Skip to content
Contract reference View as Markdown

SqueekCoreLink to this section

TL;DRLink to this section

  • SqueekCore records settled work per ten-minute round and earns progress toward Squeek blocks at an adaptive difficulty.
  • Read state() for height and progress and roundPage for history; only the Reporter's signed settlements change them.
  • Count blocks from block units, never from physical work divided by difficulty.
  • Call it through the proxy at 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f on Sepolia.

OverviewLink to this section

In active development

The Squeekchain contracts are in active development and are likely to change. Functions, events, errors, limits and behavior described here may change before a mainnet release, and the Sepolia deployment may be upgraded or replaced. Pin the ABI you integrate against, and re-verify the deployment before relying on it.

SqueekCore is the onchain work ledger. Each settlement certifies a span of rounds: active rounds carry their work and data hash, and every omitted round in the span is certified as zero work. Each active round is priced at the current difficulty into block units (billionths of a Squeek block), then steers the difficulty. SqueekCore mints nothing and never calls other contracts; SqueekMining reads it.

Network Address Kind
Sepolia 0x74665dAecA35A2303e767E76a2FF1FB6F93e119f Transparent proxy; implementation version 3, upgradeable through the governance timelock

Launch values and identities are under Deployments.

FunctionsLink to this section

TypesLink to this section

struct CoreState {
    uint64 settledThroughRound;    // certified frontier: every round ≤ this is settled
    uint64 cumulativeWork;         // physical work units, all time
    uint96 cumulativeBlockUnits;   // earned progress; 10^9 units = one Squeek block
    uint64 blockHeight;            // cumulativeBlockUnits / 10^9
    uint96 blockProgress;          // cumulativeBlockUnits % 10^9
    uint32 workPerThousandBlocks;  // difficulty for the next active round
    uint256 authorizationNonce;    // nonce the next settlement signature must use
    bool paused;
}

struct CoreConfiguration {
    bytes32 streamId;
    uint64 genesisTimestamp;           // seconds; round 1 starts here
    uint64 roundDuration;              // 600 seconds
    uint64 maxWorkPerRound;            // work units
    uint16 maxRoundsPerBatch;          // 16
    address guardian;
    address reporter;
    uint32 initialWorkPerThousandBlocks;
    uint64 targetBlocksPerActiveRound; // 40
    uint64 difficultySmoothingRounds;  // 30
    uint96 blockUnitsPerBlock;         // 1_000_000_000
}

struct Round { uint64 roundId; uint64 work; bytes32 dataHash; }

struct LedgerPosition { uint64 cumulativeWork; uint96 cumulativeBlockUnits; uint32 workPerThousandBlocks; }

struct RoundCheckpoint {
    uint64 roundId;
    uint64 cumulativeWork;
    uint96 cumulativeBlockUnits;
    uint32 workPerThousandBlocks;   // difficulty after this round steered
    bytes32 dataHash;
}

struct RoundPage {
    uint64 certifiedThroughRound;   // last round this answer covers completely
    LedgerPosition start;           // position after afterRound
    LedgerPosition end;             // position after certifiedThroughRound
    RoundCheckpoint[] rounds;       // active rounds only, oldest first
}

struct BlockInfo { uint64 roundId; uint64 workThreshold; }

struct SqueekLaunchConfig {
    bytes32 streamId;
    address guardian;
    address initialReporter;
    uint64 genesisTimestamp;
    uint32 initialWorkPerThousandBlocks;
    uint64 maxWorkPerRound;
}

uint32 difficulty decodes to number in viem; every other numeric field here decodes to bigint.

ViewsLink to this section

stateLink to this section

function state() external view returns (CoreState result);

The current ledger position, derived height and progress, the certified frontier, the authorization nonce and the pause flag. Rounds after settledThroughRound are unknown, not zero.

roundPageLink to this section

function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords) external view returns (RoundPage result);

Up to maxRecords active rounds after afterRound, with the ledger positions at both ends.

Name Type Unit Meaning
afterRound uint64 round Exclusive start
throughRound uint64 round Inclusive requested end; at most settledThroughRound
maxRecords uint256 records 0–16 active rounds to return

If the budget would leave out an active round, the page stops just before it: resume from the returned certifiedThroughRound, not throughRound. For a point read at certified round r, use roundPage(r, r, 0).end; at round 0 it returns the launch position. Reverts with InvalidRoundPageBounds for a reversed range, an end past the frontier, or more than 16 records.

blockInfoLink to this section

function blockInfo(uint64 squeekBlock) external view returns (BlockInfo result);

The round that completed Squeek block squeekBlock (from 1 to blockHeight) and the cumulative physical work at which it completed, computed at that round's pre-steering difficulty. Reverts with BlockNotSettled for block 0 or a block not yet completed.

configurationLink to this section

function configuration() external view returns (CoreConfiguration result);

Launch configuration, the current Guardian and Reporter, and the difficulty rule's constants.

pausedLink to this section

function paused() external view returns (bool);

Whether settlement is paused. Reads keep working while paused.

hasEverBeenReporterLink to this section

function hasEverBeenReporter(address candidate) external view returns (bool);

Whether candidate has ever held the Reporter role. A retired Reporter address can never be reused.

eip712DomainLink to this section

function eip712Domain() external view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions);

The EIP-712 domain settlement signatures are made for: name SqueekCore, version 1, this chain and this proxy. The domain version is not the implementation version.

IdentityLink to this section

function proxyAdmin() external view returns (address);
function implementationCodeAddress() external view returns (address);
function implementationCodeVersion() external pure returns (uint64);
function implementationStorageLayoutDigest() external pure returns (bytes32);

The ProxyAdmin from the proxy's admin slot, and the running implementation's address, version (3) and storage-layout digest. Verify the deployment checks them.

SettlementLink to this section

submitRoundsLink to this section

function submitRounds(Round[] rounds, uint64 settledThroughRound, uint256 expectedNonce, uint256 deadline, bytes signature) external;

Settles the listed active rounds and certifies every omitted round up to settledThroughRound as zero work. Any account can submit the transaction, but only a valid Reporter signature authorizes it; on Sepolia a relayer submits and pays gas.

Name Type Unit Meaning
rounds Round[] — 0–16 active rounds with strictly increasing IDs, each with nonzero work and a nonzero data hash, all after the current frontier
settledThroughRound uint64 round New frontier; must advance and must have ended by wall-clock time
expectedNonce uint256 — The current authorizationNonce
deadline uint256 Unix seconds Last time the signature is accepted
signature bytes — The Reporter's EIP-712 signature over these fields, this proxy, chain and implementation

Reverts with EnforcedPause, BatchTooLarge, InvalidActiveRound, NonIncreasingRoundId, InvalidRoundId, WorkExceeded, SettledThroughRoundNotAdvanced, RoundNotElapsed, ExpiredAuthorization, AuthorizationNonceMismatch, InvalidReporterSignature or a signature-format error. Over-cap work is rejected, never truncated.

AdminLink to this section

pauseLink to this section

function pause() external;

Guardian only. Stops settlement and voids every outstanding signature by advancing the nonce. Reverts with UnauthorizedGuardian or EnforcedPause.

unpauseLink to this section

function unpause() external;

Guardian only. Resumes settlement and voids signatures made while paused. Reverts with UnauthorizedGuardian or ExpectedPause.

setReporterLink to this section

function setReporter(address newReporter) external;

Guardian only, while paused: replaces the Reporter with an address that has never held the role. Reverts with UnauthorizedGuardian, ExpectedPause, InvalidReporter or ReporterAlreadyUsed.

initializeLink to this section

function initialize(SqueekLaunchConfig config) external;

Sets the launch configuration and starts the ledger paused at zero, during proxy construction only. Reverts with InvalidInitialization, NotInitializing, InvalidProxyAdmin, InvalidImplementationCodeAddress, InvalidStreamId, InvalidGuardian, InvalidReporter, InvalidGenesisTimestamp, InvalidWorkCap or InvalidWorkPerThousandBlocks.

EventsLink to this section

event CoreInitialized(bytes32 indexed streamId, address indexed guardian, address indexed reporter, uint64 genesisTimestamp, uint32 initialWorkPerThousandBlocks, uint64 maxWorkPerRound);
event RoundRecorded(uint64 indexed roundId, uint64 work, uint64 cumulativeWork, uint96 cumulativeBlockUnits, uint32 workPerThousandBlocks, bytes32 dataHash);
event BatchSettled(bytes32 indexed digest, address indexed reporter, uint64 indexed settledThroughRound, uint256 nonce, uint64 previousSettledThroughRound, uint16 activeRoundCount, uint64 cumulativeWork, uint96 cumulativeBlockUnits);
event ReporterUpdated(address indexed previousReporter, address indexed newReporter);
event Paused(address account);
event Unpaused(address account);
event Initialized(uint64 version);
event EIP712DomainChanged();
Event Emitted when
CoreInitialized, ReporterUpdated, Initialized Launch; ReporterUpdated also on every Reporter change, with previousReporter zero at launch
RoundRecorded Once per active round in a settlement, in round order; positions are after the round, and workPerThousandBlocks is the difficulty after its steering
BatchSettled Once per settlement, after its RoundRecorded events, including settlements with no active rounds; nonce is the one consumed
Paused, Unpaused The Guardian pauses or resumes settlement
EIP712DomainChanged Declared by the inherited EIP-712 interface; not emitted by a SqueekCore action

There is no per-block event: derive completed blocks from cumulativeBlockUnits.

ErrorsLink to this section

Error Cause Fix
InvalidRoundPageBounds Reversed range, end past the frontier, or more than 16 records Read at most settledThroughRound with 0–16 records
BlockNotSettled Block 0 or a block not yet completed Query 1..blockHeight
EnforcedPause, ExpectedPause Settling while paused, or an unpause or Reporter change while not paused Check paused()
UnauthorizedGuardian A Guardian action from another address Guardian only
BatchTooLarge More than 16 active rounds Split the span
InvalidActiveRound A listed round has zero work or a zero data hash Omit zero-work rounds; they are certified implicitly
NonIncreasingRoundId, InvalidRoundId Round IDs out of order, already settled, or past the new frontier Rebuild the batch from the current frontier
WorkExceeded A round's work is above maxWorkPerRound The round cannot settle; the measurement pipeline must reject it
SettledThroughRoundNotAdvanced, RoundNotElapsed The frontier does not advance, or targets a round that has not ended Wait for the round to end
ExpiredAuthorization deadline has passed Sign again
AuthorizationNonceMismatch The signature used a stale nonce, for example after a pause Re-read authorizationNonce and sign again
InvalidReporterSignature, ECDSAInvalidSignature, ECDSAInvalidSignatureLength(length), ECDSAInvalidSignatureS(s) Not the current Reporter's valid signature over these exact fields Sign with the current Reporter key
InvalidReporter, ReporterAlreadyUsed Reporter is zero, a privileged address, or previously used Choose a fresh address
InvalidStreamId, InvalidGuardian, InvalidGenesisTimestamp, InvalidWorkCap, InvalidWorkPerThousandBlocks Invalid launch configuration Setup only
InvalidInitialization, NotInitializing, InvalidProxyAdmin, InvalidImplementationCodeAddress Initialization outside proxy construction Setup only
SafeCastOverflowedUintDowncast(bits, value) Inherited overflow guard The call rolled back; investigate

Integrator patternsLink to this section

  • Count blocks from block units. blockHeight = cumulativeBlockUnits / 10^9. Over an interval, subtract endpoint heights: floor(end / 10^9) − floor(start / 10^9). Never divide physical work by difficulty.
  • Respect the certified frontier. Treat rounds after settledThroughRound as unknown. Act only on certified rounds, not on block.timestamp alone.
  • Page by the returned frontier. Advance your cursor to certifiedThroughRound; a page may end before the round you requested.
  • Price with the previous difficulty. A RoundRecorded difficulty is the one after its steering; the round itself was priced at the preceding position's difficulty.
  • Derive the current round yourself. V3 has no currentRound(): at Ethereum timestamp t ≥ genesisTimestamp, the round is floor((t − genesisTimestamp) / 600) + 1.
  • Expect several blocks at once. One settlement can complete several Squeek blocks; there is no per-block callback or event.
  • Consume from a contract. Another contract can read state(), roundPage and blockInfo without any role. It owns its own cursor, eligibility rules and replay protection.

Price first, then steerLink to this section

Difficulty D = workPerThousandBlocks is the work 1,000 Squeek blocks cost. For each active round with w work, in round order:

U_next = U + floor(w * 10^12 / D)
W_next = W + w
D_next = D                                            if w < 40
D_next = floor((29 * D + floor(w * 1000 / 40)) / 30)  otherwise

W is cumulative physical work and U cumulative block units. A round with 1–39 work earns progress but does not steer. A round with at least 40 work moves difficulty toward the price at which it would have earned 40 blocks; 40 is a steering target, not a guaranteed block count. Zero-work gaps change nothing. Port the integer expressions exactly; floating-point or reordered arithmetic rounds differently.

Usage exampleLink to this section

The Quickstart reads configuration(), state() and a point endpoint and checks them against each other. This example counts the Squeek blocks completed in a span of certified rounds.

// blocks-in-span.ts
import { parseAbi } from "viem";
import { blockNumber, client, required } from "./setup";
import { verifyGraph } from "./verify";

const { core } = await verifyGraph();
const abi = parseAbi([
  "function state() view returns ((uint64 settledThroughRound, uint64 cumulativeWork, uint96 cumulativeBlockUnits, uint64 blockHeight, uint96 blockProgress, uint32 workPerThousandBlocks, uint256 authorizationNonce, bool paused))",
  "function roundPage(uint64 afterRound, uint64 throughRound, uint256 maxRecords) view returns ((uint64 certifiedThroughRound, (uint64 cumulativeWork, uint96 cumulativeBlockUnits, uint32 workPerThousandBlocks) start, (uint64 cumulativeWork, uint96 cumulativeBlockUnits, uint32 workPerThousandBlocks) end, (uint64 roundId, uint64 cumulativeWork, uint96 cumulativeBlockUnits, uint32 workPerThousandBlocks, bytes32 dataHash)[] rounds))",
]);
const read = { address: core, abi, blockNumber } as const;
const { settledThroughRound } = await client.readContract({ ...read, functionName: "state" });
const through = settledThroughRound;
const after = through > 144n ? through - 144n : 0n; // the last 144 rounds, about one day
const startPoint = await client.readContract({ ...read, functionName: "roundPage", args: [after, after, 0n] });
const endPoint = await client.readContract({ ...read, functionName: "roundPage", args: [through, through, 0n] });
const Q = 1_000_000_000n;
const completedBlocks = endPoint.end.cumulativeBlockUnits / Q - startPoint.end.cumulativeBlockUnits / Q;
const work = endPoint.end.cumulativeWork - startPoint.end.cumulativeWork;
console.log({ afterRound: after, throughRound: through, work, completedBlocks });

ABILink to this section

SqueekCore.json. Call it on the proxy address, not the implementation.