
castr.fun Token Launchpad Security Review
Review of a Base token launchpad using Bancor-style bonding curves and migration to Aerodrome Slipstream liquidity.
Kann Audits / Security Review
IZKONENReview of the Base ERC-20 token and its replaceable Fibonacci-schedule emission module.
Executive summary
Review of the Base ERC-20 token and its replaceable Fibonacci-schedule emission module.
This page reflects only the scope and review context disclosed in the published report. Fields the report does not provide are omitted rather than inferred; the PDF remains the source of record for issue detail and limitations.
Security is contextual. This report does not guarantee that the protocol is free from vulnerabilities. It applies to the review context documented in the report.
Scope & record
src/IZKONEN.solsrc/IZKONENMinter.solFindings overview
The counts below are transcribed from the published report. Status and issue detail remain subject to that report’s exact terminology.
Published findings
Findings below are reproduced from the complete Kann Audits security review. View the full PDF for complete scope, methodology, assumptions, and audit context.
L-01
MAX_WALLET is a constant equal to 3% of MAX_SUPPLY, or 1,160,700,000,000 IZK. The _update function enforces the limit after a transfer by checking whether balanceOf(to) exceeds MAX_WALLET. However, the public token documentation advertises the limit as “3% of current supply.” Because the early-year circulating supply is small relative to the fixed maximumsupply-derived limit, the concentration protection is effectively inactive until approximately 2041. During the early emission years, a single wallet can therefore hold a share of the circulating supply many times larger than the advertised 3%.
The advertised “maximum 3% per wallet” property is not enforced during the early years of the emission schedule. There is no direct loss of funds, but the implementation does not match the stated tokenomics and provides substantially weaker concentration protection than users may expect.
Calculate the wallet limit dynamically as 3% of totalSupply()
I-01
The tranche comments and whitepaper language describe a Fibonacci-style emission schedule. The implemented whole-token tranche values mostly follow tranche[n] = tranche[n - 1] + tranche[n - 2], but three entries differ from a strict recurrence by exactly one whole token. The 2030 tranche is one token above the strict sum, the 2037 tranche is one token below the strict sum, and the 2043 tranche is one token above the strict sum. The discrepancy is economically immaterial at the scale of the schedule and appears to be a rounding or documentation-consistency issue rather than a technical defect. The total schedule plus initial liquidity still reaches the hard cap as tested.
The implementation does not exactly match a strict Fibonacci recurrence for every listed year. This may confuse reviewers or users comparing the implementation with the whitepaper, although the discrepancy is limited to one whole token in three tranches.
Update the whitepaper or code comments to state that the tranche table is Fibonacci-derived with three one-token rounding adjustments rather than a strict recurrence for every listed year.
I-02
If the Minter contract is replaced, the new instance must be initialized with a startingYearIndex_ that exactly matches the previous minter’s yearsMinted. An incorrect or stale index can cause already-minted tranches to be repeated or future tranches to be skipped.
Incorrect migration configuration can disrupt the intended emission schedule and may result in extra tokens being minted or scheduled tranches being skipped.
During migration, verify that the replacement minter’s startingYearIndex_ exactly matches the current emission progress before granting it MINTER_ROLE.
I-03
The Minter can be initialized with a startTime_ that is significantly earlier than the current block timestamp. Without a lower-bound validation, an incorrectly configured deployment can begin with an emission start time that is already far in the past, affecting the intended timing of the schedule.
For a first deployment, add a bounded validation such as: require( startTime_ <= block.timestamp && startTime_ >= block.timestamp - GRACE_PERIOD ); For a replacement minter, derive startTime_ directly from the retired minter’s immutable startTime on-chain rather than accepting it as a new external input.
I-04
The initialLiquidityMinted flag is stored in the Minter contract rather than globally in the token. A replacement minter therefore starts with initialLiquidityMinted = false, allowing mintInitialLiquidity() to be executed again and mint an additional 38.5 billion IZK.
A replacement minter can mint the 38.5 billion IZK initial-liquidity allocation a second time, consuming supply reserved for later emissions. When the emission schedule reaches the final tranches, minting can revert because the hard cap has already been consumed. Execution Cycle Minter1 executes mintInitialLiquidity() legitimately → Minter1 is replaced by Minter2 with initialLiquidityMinted = false → Minter2 executes mintInitialLiquidity() again → the normal emission schedule continues → at index 22, token.mint reverts with CapExceeded.
Store the “initial liquidity already minted” state globally in the token, pass the flag explicitly into the replacement minter’s constructor, or otherwise track the already-minted initial-liquidity allocation at the token level.
I-05
The token does not maintain a single active minter. If a replacement minter is deployed with the correct startingYearIndex_ but the previous minter is not deauthorized, both contracts can remain valid minters with the same emission progress. Since mint() is permissionless, each open emission window can be executed independently through both modules, causing the same tranche to be emitted twice.
Overlapping minters can cause double payouts to the configured market and treasury emission addresses, resulting in over-emission. The hard cap is then reached prematurely, preventing the largest late-stage tranches from being minted. The issue does not allow a third party to redirect the emission to an arbitrary address; the duplicated payouts still go to the configured emission recipients. Execution Cycle Minter A is operating → Minter B is added with the correct index → Minter A remains authorized → mint() is called on both A and B during each open window → each tranche is emitted twice → in a later year, token.mint reverts with CapExceeded.
Enforce a single active minter at the token level. For example, store an activeMinter address in the token and allow minting only from that address, with replacement performed atomically. Alternatively, track tranche execution directly in the token so the same tranche cannot be minted by more than one module.
ACKNOWLEDGED
Disclaimers A security audit can never guarantee the complete absence of vulnerabilities. Audits are a time, resource, and expertise-bound effort in which we aim to identify as many issues as possible. About Kann Audits Kann Audits is a top-tier Web3 security audit company, trusted by leading projects and providing comphrensive audits and expert guidance to secure the most critical blockchain protocols. Kann Audits provides services such as manual auditing, AI audits using the Kann AI Model, and incident response. To learn more, visit https://kannaudits.com To view our audit portfolio, visit https://github.com/KannAudits To book an audit, message https://t.me/KannAudits
Methodology
Kann Audits reports describe independent researcher review followed by collaborative analysis of findings and attack paths. The standard review foundation includes:
Audit team
Final assessment
The low finding and one informational finding were fixed; four informational findings were acknowledged.
The assessment applies only to the review context and limitations documented in the published report. Missing details are not inferred, and later changes require separate analysis.
Start a conversation
Share the system, fixed scope, and target date. Build enough time into the plan for review, remediation, and verification.