Ponzio halving follows a deployment-based supply schedule above a fixed floor
Ponzio halving sets a declining supply target for the Ethereum token Ponzio The Cat, subject to a fixed supply floor. The implementation defines each cycle as four days plus 144 seconds. Within a cycle, 168 intervals of 34 minutes and 18 seconds divide the reduction into steps. A cycle boundary marks half the previous cycle's starting target; it doesn't suddenly halve the immediately preceding reading. Stored supply changes only when a successful update records the calculated state. The contract's floor and maximum-share condition limit the schedule, so elapsed time alone can't establish the recorded supply.
Key takeaway: At the supply floor, a successful update can still issue collector shares without reducing the recorded token supply.
Cycle boundaries and debase steps follow the same clock
Deployment supplies the starting timestamp
The cycle lasts 345 744 seconds, measured from the timestamp the constructor records as
DEPLOYED_TIME
. The contract divides elapsed blockchain time by
HALVING_EVERY
to count completed cycles. It uses the remaining time to locate the debase step within the current cycle. Both calculations share the same origin, tying the smaller reductions to the longer schedule.
A cycle contains 168 equal time slots
Dividing 345 744 seconds by the 2 058-second debase interval gives 168 slots. The implementation starts with an
INITIAL_SUPPLY
of 21 000 000 tokens and halves the cycle baseline after each complete cycle. Subsequent cycles use smaller baselines, so equal-length slots remove fewer whole tokens as time passes.
Within a cycle, let
B
mean its baseline supply in base units and
k
the completed slot count. The reduction equals
floor(B × k / 336)
, and subtracting it from
B
gives the unclamped target. The floor overrides lower results. The slot count runs from 0 through 167 before the next cycle resets it. Integer division can make decimal approximations differ in the final base unit.
Does the supply halve all at once?
Stored supply can halve in one successful delayed update, although the target falls in smaller steps throughout the cycle, including across its boundary. The next cycle begins at half the previous cycle's baseline. That value is only slightly below the target in the final slot of the preceding cycle. Treating the boundary as another 50% cut would count the cycle's reduction twice. The relevant comparison spans cycle starts, with the fixed floor taking priority near the end of contraction.
When does the halving schedule stop reducing supply?
The target stops falling at the minimum supply, while a maximum-share flag can independently stop further supply-and-fee calculations.
The fixed supply floor
The implementation sets the minimum supply to 0.000001 Ponzio tokens.
Once the time-based target falls below that level,
computeSupply()
clamps it to the minimum. The first update reaching the floor still calculates its fee from the contraction since the previous stored supply. Later calculations enter the floor branch only after an update has recorded the minimum.
At that point, token supply stays at the floor, while collector fees can continue. That branch uses elapsed time since the previous recorded update, with 13.37% of the minimum supply as its full-cycle fee amount. It prorates shorter gaps and caps longer gaps at that amount for one calculation. A successful update can therefore change internal shares without reducing total token supply.
The separate share-capacity limit
If collector minting would overflow the internal share counter, the implementation mints only the remaining share capacity and sets its share-capacity stop flag. Afterward,
computeSupply()
returns the stored supply and zero fees. That stop takes priority over its ordinary time-based calculation.
The limits use different units
The share counter uses
uint256
, whereas the minimum supply uses token base units. Reaching that counter's capacity is distinct from reaching the token floor.
Calculated targets and recorded supply can diverge
A read-only calculation can advance with the block timestamp even while
totalSupply()
continues to return the last recorded value.
computeSupply()
calculates a prospective supply and fee without writing either into storage. For an update to write a changed state, its computed fee must be nonzero and the configured collector must be nonzero. Successful execution replaces the stored supply and update timestamp. It also processes the collector share allocation and synchronizes the configured Uniswap v2 pair. A reverting pair synchronization reverses the transaction, including its earlier storage changes.
TotalSupplyUpdated
records the old and new supply alongside share counts and the fee amount. That event belongs to a completed supply update when its transaction succeeds. A clock display describes elapsed time, and a read-only preview describes a computed state. Neither alone establishes a new stored value. Actual recorded supply can lag the calculated target, including across more than one cycle boundary.
A halving target doesn't determine a holder's return
A halved supply baseline doesn't promise a doubled position value, because balances and market prices describe different quantities. Above the floor, the ordinary collector fee uses 13.37% of the supply decrease, rounded down to base units. Issuing collector shares can further reduce a passive holder's fraction. Providing liquidity and staking liquidity pool (LP) tokens introduce separate pool and reward conditions. Their returns don't follow from the number of elapsed cycles. The schedule describes token accounting; demand, executable prices, and the remaining balance determine economic value.
Readings for stored and projected contract state
Different read-only functions answer different timing questions, from a fixed clock origin to a prospective token balance.
| Reading option | Information returned | Time basis | Relevant state |
|---|---|---|---|
DEPLOYED_TIME()
|
Deployment timestamp | Fixed at deployment | Schedule origin |
totalSupply()
|
Stored token supply | Last recorded supply | Aggregate token supply |
computeSupply()
|
Target supply and computed fee | Query block timestamp, subject to stopping branches | Supply and collector fee |
computeNewState()
|
Projected shares, supply, and adjusted fee | Query block timestamp, subject to stopping branches | Projected accounting state |
balanceOf(account)
|
Token balance from stored ratios | Stored supply and shares | Selected account |
realBalanceOf(account)
|
Token balance from projected ratios | Computed supply and shares | Selected account |
maxSharesReached()
|
Share-capacity stop flag | Stored condition | Supply-and-fee calculation stop |
Comparing readings at one block keeps their time context consistent. Otherwise, elapsed time between queries can change projected values.
computeNewState()
also accounts for the large-fee and share-capacity branches. Its fee output can therefore differ from the unadjusted fee
computeSupply()
returns.
Base units explain small supply readings
Eighteen decimal places separate a whole Ponzio token from the raw integers contract functions return. Token-supply and fee values require division by 10 18 for display in whole tokens. Internal shares use their own accounting scale, so the same conversion doesn't turn shares into token balances. A display omitting fractional digits can show zero despite a nonzero supply. The fixed floor, expressed in whole tokens, is 0.000001 Ponzio.
Ponzio halving: reader questions
Will a four-day countdown drift away from Ponzio's cycle boundaries?
A countdown using exactly four days per cycle accumulates an error of 144 seconds per cycle against this implementation. The extra time belongs to every halving interval, not just the first. Its displayed boundary must also use the contract's deployment timestamp rather than an unrelated calendar starting point.
Can one successful update incorporate multiple missed debase intervals?
An eligible successful update can apply the target for several elapsed intervals without replaying each interval separately. During the contraction branch, computeSupply() derives the target directly from elapsed deployment time. A later update still needs a nonzero computed fee, a configured collector, and successful pair synchronization.
Does changing the fee collector restart the halving clock?
Changing the fee collector leaves DEPLOYED_TIME and the schedule constants unchanged. The setter calls the supply update function before replacing the recipient, so the change can coincide with an accounting update. It doesn't create a new timing origin or defer later cycle boundaries.
Why can another supply update call in the same block produce no event?
If the previous recorded update has the current block's timestamp, updateTotalSupply() returns immediately. It emits no new supply-update event on that path. The guard compares timestamps rather than counting calls, so invoking the function again doesn't itself create another scheduled reduction.