The Mint
Two immutable Ethereum contracts: ProofOfTime (tWARP) and TimeVault. At deploy, token supply is zero until gatherTWARP mints 1,000,000 tokens once. Only the TimeVault address may call mintTWARP, and only on the fixed 365-day stepped schedule—up to 500,000 additional tokens, hard-capped on-chain.
This is not an exploitable mint
There is no public mint function. No admin withdraw from the vault. Scheduled inflation runs in _accrueGlobal: the vault calls mintTWARP(address(this), …) and credits holders via rewardPerTokenStored (wallet balance weight). claim() only transfers your accrued share—it does not mint at claim time.
Supply model
ProofOfTime: GENESIS_SUPPLY = 1,000,000, MAX_SUPPLY = 1,500,000. TimeVault: VAULT_MINT_CAP = 500,000 over 365 days. Per-block rates are (405 ÷ 7,200), (809 ÷ 7,200), and (1,618 ÷ 7,200) tokens per block in phases 1–3 (~405 / ~809 / ~1,618 tokens per day). Integer math lands just under 500k; the final accrual when relBlock ≥ 2,628,000 tops up to exactly 500,000.
Who can mint?
- Wallets / traders: transfer and approve only—cannot call mintTWARP.
- Deployer (once): gatherTWARP, setMintManager(vault), then vault admin runs setNativeToken, optional setExcludedPool, enableTWARP.
- TimeVault only: mintTWARP inside _accrueGlobal while totalMinted < VAULT_MINT_CAP and block ≤ startBlock + 2,628,000.
- Uniswap LP pool (optional setExcludedPool): LP balance is excluded from eligibleSupply; the pool address earns zero. Regular wallets accrue by holding tWARP—no deposit.
On-chain source (excerpts)
Copied from contracts/src/ProofOfTime.sol and TimeVault.sol (Solidity 0.8.24). Verify on Etherscan after deploy or read the repo files directly.
function gatherTWARP() external {
require(!genesisMinted, "gathered");
genesisMinted = true;
_mint(msg.sender, GENESIS_SUPPLY);
}function mintTWARP(address to, uint256 amount) external {
require(msg.sender == mintManager, "manager");
require(totalSupply() + amount <= MAX_SUPPLY, "cap");
_mint(to, amount);
}function _rateAtRelative(uint256 rel) internal pure returns (uint256) {
if (rel >= PHASE2_BLOCKS) return RATE_P3_PER_BLOCK;
if (rel >= PHASE1_BLOCKS) return RATE_P2_PER_BLOCK;
return RATE_P1_PER_BLOCK;
}function _eligibleSupply() internal view returns (uint256) {
uint256 supply = nativeToken.totalSupply();
if (excludedPool != address(0)) {
uint256 poolBal = nativeToken.balanceOf(excludedPool);
if (poolBal >= supply) return 0;
return supply - poolBal;
}
return supply;
}
// inside _accrueGlobal:
uint256 eligible = _eligibleSupply();
if (amount > 0 && eligible > 0) {
nativeToken.mintTWARP(address(this), amount);
rewardPerTokenStored += (amount * PRECISION) / eligible;
}function _settle(address account) internal {
uint256 bal = _rewardBalance(account); // 0 for excludedPool
uint256 paid = userRewardPerTokenPaid[account];
uint256 rpt = rewardPerTokenStored;
if (bal > 0 && rpt > paid) {
rewards[account] += (bal * (rpt - paid)) / PRECISION;
}
userRewardPerTokenPaid[account] = rpt;
}
function claim() external {
_accrueGlobal();
_settle(msg.sender);
uint256 amount = rewards[msg.sender];
require(amount > 0, "nothing");
rewards[msg.sender] = 0;
nativeToken.transfer(msg.sender, amount);
}What cannot happen
- mintTWARP from any address other than the configured TimeVault.
- totalSupply above MAX_SUPPLY (1.5M) or vault mint above VAULT_MINT_CAP (500k).
- Restart, pause, or retarget the schedule after enableTWARP.
- Admin pull of vault token balance (no withdraw function).
- Mint rewards to the excluded LP pool address.
Rates & blocks (verifiable constants)
In TimeVault.sol: BLOCKS_PER_DAY = 7_200, PHASE1_BLOCKS = 100_800, PHASE2_BLOCKS = 756_000, SCHEDULE_BLOCKS = 2_628_000. View functions: getCurrentPhase(), getTokensPerBlock(), getTotalMinted(), getRemainingTokens(), getBlocksUntilHalvening(1|2).
Audit surface
Inflation is limited to ProofOfTime.mintTWARP (caller = vault, cap checks) and TimeVault._accrueGlobal / _mintAmountForWindow / _tokensForElapsed, plus claim for holders.