Compute-unit consumption
Every SithBit instruction costs compute units (CU) when it executes on-chain. The table below records what each user-facing flow consumed when it was last measured, and the ceiling the integration suite fences it under — a tripwire that catches a program change silently making an instruction materially more expensive.
Values are per transaction as the CLI builds it, so bundled
instructions ride along: mail send carries a ComputeBudget limit
instruction, and mailbox create bundles the wallet’s self-CreateAlias.
| Instruction / flow | CLI command | Measured CU | Fenced ceiling |
|---|---|---|---|
| SendMail (plain wallet-to-wallet) | mail send | 23,618 | 47,000 |
| SendMail (relay from-address) | mail send -f | 29,876 | 53,000 |
| SendMail (with bounty) | mail send --bounty | 23,592 | 47,000 |
| SendMail (reply linkage) | mail send --reply-to | 25,075 | 48,000 |
| ClaimBounty (domained operator-share path) | mail claim-bounty | 24,847 | 48,000 |
| RefundBounty (expiry-gated sender reclaim) | mail refund-bounty | 7,579 | 31,000 |
| DeleteMail | mail delete | 15,111 | 38,000 |
| RefundMail | mail refund | 16,211 | 39,000 |
| CreateMailbox (+ bundled CreateAlias) | mailbox create | 32,085 | 55,000 |
| SetEncryptionKey | mailbox key set | 10,811 | 34,000 |
| RequestCloseMailbox (opens the close timelock) | mailbox close | 13,334 | 36,000 |
| CancelCloseMailbox (request aborted) | mailbox close --cancel | 12,156 | 35,000 |
| FinalizeCloseMailbox (past the timelock; both rents refunded) | mailbox close --finalize | 12,858 | 36,000 |
| CreateFrombox (third-party first purchase, default 9-account reputation tail — reputation-scaled pricing plus the lazy first-use mint of the payer’s sender-reputation PDA) | frombox stamp / frombox update (create-if-absent) | 35,256 | 58,000 |
| CreateAlias | alias create | 14,888 | 38,000 |
| OfferTransferAlias | alias transfer init | 18,894 | 42,000 |
| AcceptTransferAlias (zero-fee, flat fee paid) | alias transfer accept | 13,838 | 37,000 |
| CancelTransferAlias (offer retracted past binding window) | alias transfer cancel | 24,828 | 48,000 |
| ListAliasForSale | alias sell | 20,308 | 43,000 |
| BuyAlias | alias buy | 20,747 | 44,000 |
| CancelAliasListing (listing withdrawn past binding window) | alias sell --cancel | 19,183 | 42,000 |
| SellAlias (open auction) | alias sell --auction | 26,798 | 50,000 |
| BidAlias | alias bid | 19,715 | 43,000 |
| SettleAuction | alias settle-auction | 26,674 | 50,000 |
| AuthorizeDomain | domain create | 15,242 | 38,000 |
| ListDomainForSale | domain sell | 32,553 | 56,000 |
| BuyDomain | domain buy | 19,807 | 43,000 |
| CancelDomainListing (listing withdrawn past binding window) | domain sell --cancel | 16,001 | 39,000 |
| AttestSender (staged DNSSEC-proof verified sender) | domain attest-sender | 322,474 | 345,000 |
| RevokeSenderAttestation (holder-signed close) | domain revoke-attestation | 11,224 | 34,000 |
| SetSenderAttestationFee | postmaster fee attestation | 6,546 | 30,000 |
| SetReputationFloor | postmaster fee reputation-floor | 6,756 | 30,000 |
| CreatePinLease (per-CID storage deposit) | mail lease create | 34,221 | 57,000 |
| ClosePinLease (holder-signed drain) | mail lease close | 7,316 | 30,000 |
| SetPinLeaseFee | postmaster fee pin-lease | 6,962 | 30,000 |
For scale: Solana’s default per-instruction budget is 200,000 CU and the
per-transaction maximum is 1,400,000 CU — every SithBit flow fits
comfortably inside the default budget except the DNSSEC-proof-verified
ones (domain attest-sender above, and the untabled domain authorize /
domain reclaim it mirrors), whose on-chain RSA chain walk is why the
CLI prepends an explicit ComputeBudget limit sized to the proof’s zone
depth — still well under the transaction maximum.
Methodology
The measurements come from the CLI integration suite
(mail_client/tests/api/cu.rs), which runs against a local
surfpool validator with the exact program
binaries from target/deploy/:
- Each flow is driven end-to-end through the compiled
sithbitCLI; the transaction signature is captured from the explorer URL the CLI prints. - The landed transaction is fetched with
getTransactionand the consumption read frommeta.computeUnitsConsumed. - The recorded ceiling is the max measured value plus a 22,500-CU bump-grind allowance, rounded up to the next 1,000 CU. The test asserts every future run stays at or under the ceiling.
The “measured” column is the highest of several samples, and the
allowance exists because consumption is not a constant: it swings in
exact multiples of ~1,500 CU between runs of the same command, because
PDA bump-seed grinding (finding the off-curve address for each fresh
alias, message, or listing) costs one syscall per failed candidate and
the number of candidates depends on the seeds involved. The same
domain sell flow was observed at both 14,553 and 32,553 CU across
runs, and alias transfer cancel swung from 9,816 to 24,828 CU — the
widest spread recorded. The allowance covers a 15-iteration unlucky grind streak, which
keeps the fence effectively flake-free while still tripping on any
program change that adds real work.
To re-measure — after a program change trips a fence, or to refresh the table — build the programs and run the suite with output visible:
cargo build-sbf --manifest-path mail_program/Cargo.toml
cargo build-sbf --manifest-path alias_program/Cargo.toml
cargo build-sbf --manifest-path domain_program/Cargo.toml
cargo test -p mail-client --features postmaster cu -- --nocapture
Each measurement prints as CU measured | <flow> | <units>. Update the
constants in cu.rs and this table together, deliberately — the fence
exists so cost regressions are a recorded decision, never an accident.
The docs gate enforces the pairing: mail_docs/check_cu_rows.py diffs
this table’s measured and ceiling values against the suite’s constants,
both ways, so a re-measured constant cannot leave a stale row behind.
Caveat: all numbers were measured on the current program build in this repository. Different program versions (or a cluster running a different feature set) will consume different amounts; treat the table as a snapshot fenced by the tests, not a protocol constant.