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 androundPagefor 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
0x74665dAecA35A2303e767E76a2FF1FB6F93e119fon 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
settledThroughRoundas unknown. Act only on certified rounds, not onblock.timestampalone. - 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
RoundRecordeddifficulty 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 timestampt ≥ genesisTimestamp, the round isfloor((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(),roundPageandblockInfowithout 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.