Skip to main content

Gas and limits

This page collects the numbers: what each operation costs in gas, how the project stops those costs creeping up, how large each contract is allowed to be, and every fixed limit written into the contracts. All figures come from files in the repository; none are estimates.

The gas snapshot​

blockchain/gas-snapshot.json holds the gas used by each tracked operation, measured on Hardhat's in-process network by blockchain/test/gas/measure-operations.ts. The figures are transaction gas (receipt.gasUsed), including the 21,000 base cost and calldata.

OperationGasWhat is measured
addIssuer75,343Admin registers an issuer with a short name
registerType150,590Issuer registers its first custom type ("Workshop", "A short workshop")
issue238,790Issuer issues a credential with no expiry, built-in type 1
issueExpiring238,917The same with a validUntil
suspend44,519Issuer suspends an active credential (reason 5)
reinstate44,682Issuer reinstates it
supersede76,397Issuer supersedes it with another active credential
revoke42,265Issuer revokes an active credential
adminHold66,189Admin suspends a credential (sets the admin hold)
issueReplacing284,333Issue and supersede the old credential in one transaction
supersedeToRegistry69,555Issuer points a credential at a replacement in another set
redeem137,402Student wallet redeems an invitation
rebind103,590A new wallet takes over the enrolment
revokeInvitations34,767Issuer cancels outstanding invitations
unbind36,203Issuer removes the binding
setTypeActive32,070Issuer switches its custom type off
pause46,965Guardian pauses the set
unpause27,101Admin unpauses
suspendIssuer52,190Guardian suspends an issuer (setIssuerSuspended)
removeIssuer29,502Admin removes an issuer
accessAdmitFirst279,791Holder admits the first member (creates the Semaphore group)
accessAdmitLater201,669Holder admits the second member
accessProve330,178A member proves access (group of three, tree depth 2)
accessRemove138,405Holder removes the third member

retireIssuer, setIssuerName, bindCredentialRegistry, role changes and the timelock are not in the snapshot. Gas depends on input sizes (names, signatures), so other inputs give other figures. Nothing in v0.3 grows with stored data: blockchain/test/v03/BoundedWork.test.ts measures every write with 1, 100 and 1,000 items already stored and requires the gas to stay flat.

The same replay also records calldata size and zero bytes per operation (calldataStats in blockchain/scripts/lib/network-cost.ts), which the network evaluation tooling (npm run network:measure, npm run network:report) uses to compare chains.

How regressions are caught​

RuleSource
Tolerance is 10 percent either way (GAS_TOLERANCE = 0.1)blockchain/scripts/lib/gas-snapshot.ts
A regression above +10 percent or a missing operation always failsblockchain/test/gas/GasSnapshot.test.ts
An improvement beyond 10 percent or a new operation fails in CI ("out of date") so a later regression cannot hide behind the gain; locally it is only loggedSame
The snapshot is refreshed locally with npm run gas:snapshot (GAS_SNAPSHOT_WRITE=true) and committed; writing it in CI is refusedSame
The check is skipped under coverage, where instrumented code costs far moreSame

npm run gas:check runs this test alone. npm run gas runs the whole suite with hardhat-gas-reporter (REPORT_GAS=true) and writes gas-report.txt, which CI uploads as an artifact. Details are on Invariants and gas.

Contract size budgets​

Ethereum refuses to deploy runtime code above 24,576 bytes (EIP-170). The project sets a tighter budget for new contracts to keep headroom for later revisions.

GroupBudgetChecked by
Contracts compiled directly in blockchain/contracts/ (that is AcademicRecords)24,576 bytes (EIP_170_LIMIT)npm run sizes (blockchain/scripts/check-sizes.ts), in CI
Contracts compiled directly in blockchain/contracts/v03/ and blockchain/contracts/zk/20,000 bytes (V03_BUDGET)Same; also ContractSize.test.ts and AccessGate.test.ts

Runtime sizes from the local Hardhat build of the current sources (optimizer on, 200 runs, viaIR):

ContractRuntime bytes
AcademicRecords15,732
RecordStorage (library)5,588
StudentManagement2,861
IssuerRegistry6,484
CredentialRegistry4,761
StatusRegistry7,696
HolderRegistry4,762
RecordTypeRegistry3,510
CertichainTimelock5,448
VersionAnnouncer4,875
CertichainAccessGate4,719

npm run sizes prints the same figures after a compile.

Every hard limit​

v0.3 registries​

LimitValueConstantContract
Issuer name length1 to 128 bytesMAX_NAME_BYTESIssuerRegistry
Key lineage depth8 hopsMAX_LINEAGE_HOPSIssuerRegistry
issuedAt ahead of block time5 minutesMAX_FUTURE_DRIFTCredentialRegistry
Anchor layout version1SCHEMA_VERSIONCredentialRegistry
Nonce reuseOnce per issuer_usedNoncesCredentialRegistry
Reserved status reasons8 to 999 refused in issuer callsRESERVED_FROM, ISSUER_DEFINED_FROMStatusRegistry
Invitation lifetimeExpiry at most 30 days after the redeeming blockMAX_INVITATION_TTLHolderRegistry
Enrolments per walletOne per issuer_enrolmentsHolderRegistry
Built-in record typesIds 0 to 31LAST_BUILT_IN_TYPERecordTypeRegistry
Reserved record typesIds 32 to 999(gap before FIRST_CUSTOM_TYPE)RecordTypeRegistry
First custom record type1000FIRST_CUSTOM_TYPERecordTypeRegistry
Record type name1 to 64 bytesMAX_NAME_BYTESRecordTypeRegistry
Record type descriptionAt most 256 bytesMAX_DESCRIPTION_BYTESRecordTypeRegistry
Set label1 to 32 bytesMAX_LABEL_BYTESVersionAnnouncer
Admin transfer delayChosen at deployment; scripts default to 3 daysDEFAULT_ADMIN_DELAY_SEC (script)IssuerRegistry, StatusRegistry

Access gate​

LimitValueConstant
Members per credential, over its whole life64MAX_MEMBERS
Tree depth served6MAX_DEPTH_SERVED
Epoch length1 dayEPOCH_SECONDS
Accepted epochsCurrent, previous and next_checkProof
Old group root still accepted10 minutes after it changesGROUP_ROOT_VALIDITY
Receipts per memberOne per credential per epoch (nullifier)_spent

Timelock (deploy script)​

LimitValueConstant
Minimum delay on a public chain1 hourMIN_DELAY_FLOOR_SEC
Maximum delay30 daysMAX_DELAY_SEC

Legacy v0.2​

LimitValueWhere
Sharees per record32MAX_SHAREES_PER_RECORD in RecordStorage
Contract sizeEIP-170check-sizes.ts

The v0.2 lookups by student wallet and the per-student delete loop over stored records, so their cost grows with data; the 32 sharee cap bounds the work per record.

Source files​

  • blockchain/gas-snapshot.json
  • blockchain/test/gas/measure-operations.ts, blockchain/test/gas/GasSnapshot.test.ts
  • blockchain/scripts/lib/gas-snapshot.ts, blockchain/scripts/lib/network-cost.ts
  • blockchain/scripts/check-sizes.ts, blockchain/scripts/lib/contract-size.ts
  • blockchain/hardhat.config.ts, blockchain/scripts/lib/solidity-profiles.ts

Tests​

  • blockchain/test/gas/GasSnapshot.test.ts, OperationRecords.test.ts, gas-snapshot-lib.test.ts, network-cost-lib.test.ts, network-fees-file.test.ts
  • blockchain/test/v03/ContractSize.test.ts, blockchain/test/v03/BoundedWork.test.ts