Deployed vs Source
Last updated: August 14, 2026
On any young protocol the repository moves faster than the chain. This page is the canonical table of every place where Kerne's deployed bytecode behaves differently from the current source, so a reviewer reading the code finds context here rather than surprises there. It was first published before any external review began, and it is where the gap between the audited commit and the deployed contracts is stated.
The reading rule: when an internal security document marks a finding FIXED, that means fixed in source. Whether the fix is live on chain is a separate fact, and this page is where that fact lives.
How to read the protocol's state
Every security finding Kerne has published falls into one of four buckets:
- Fixed and deployed. The fix is live bytecode. The findings tracker cites the closing commit and date per finding.
- Fixed in source, not yet on chain. The deployment ceremony this page once described as pending has since executed, in two parts: the vault on 2026-06-16 and the mint PSM on 2026-07-10. The PSM was deployed from the frozen audit commit and matches it. The vault was deployed from source that predates the vault fixes, so those fixes did not close on chain when the ceremony ran, and the eight Hexens remediations written afterwards are not on chain either. That is the KerneVault row in the table below, stated rather than left for a reader to infer from the word FIXED. The same bucket now holds a second contract: the skUSD fix tagged KRN-26-SKUSD-SQUAT is in source and not on chain, and it is the first row below.
- Open on chain, with a mitigation and an operating rule. The four standing divergences in the table below. These are the gaps that persist today, each with a stated rule that bounds its exposure.
- Source only, never deployed. The repository contains contracts that have never been deployed (including kUSDMinter and the entire cross-chain bridge stack, the latter under an explicit do-not-deploy quarantine). Findings against them affect no live funds. The auditor-facing scope document draws this boundary precisely.
The four standing divergences
Each row states what the live bytecode does, what the current source does instead, the practical exposure, and the operating rule that bounds it. All four contracts are source-verified on BaseScan and Sourcify, so everything described here is independently checkable against the deployed code itself.
skUSD
Deployed 2026-07-03Deployed bytecode
The live skUSD is byte-identical to commit 0912c870, the commit the Hexens review covers, and Sourcify reports both a runtime match and a creation match against it. What the bytecode does not carry is a fix written after that freeze. In the deployed _withdraw, the KRN-26-SKUSD-ORPHAN reset that collapses an in-flight yield vest fires only when totalSupply() reaches zero, and one wei of shares holds that trigger open indefinitely. So an address that keeps a dust position outstanding while the staked capital exits during a vest ends up owning the entire still-unvested distribution, which would otherwise have gone to the stakers who stayed. Tagged KRN-26-SKUSD-SQUAT. skUSD was in the Hexens core scope and the final report carries no findings against it; this one was found in Kerne's own adversarial sweep of the live money surfaces on July 30, 2026.
Current source
Current source caps the amount still vesting after an exit at the principal still staked, so one distribution can at most double the remaining stake over its vest and a dust position inherits nothing. The excess leaves the tracked ledger and becomes an untracked donation the strategist can sweep and redistribute, which is the same destination the zero-supply reset already uses. An early exit still forfeits its slice of the vest to the remaining stakers, because that is the locked-profit behaviour the flash-deposit defence rests on, and it is deliberately unchanged. The fix went into src/skUSD.sol on July 30, 2026, the day it was found, and both the exploit and the fixed behaviour are pinned by test/security/SkUSDOrphanResetSquatter.t.sol.
Practical exposure
Measured, not estimated. Compiled against the exact source that is deployed, the regression test stakes 1,000 kUSD, distributes 1,000 kUSD, exits the staker while the distribution is still fully locked, and the one-wei position then redeems 500.000000000000000001 kUSD. The 1e6 virtual-share offset absorbs the other half, and a larger squat takes proportionally more. Compiled against current source the same position redeems 0.000000000000000001 kUSD, exactly the wei it deposited. Two conditions have to hold at the same time for that to pay anything on chain, and neither holds today. The first is a distribution in flight: lockedYield() returns 0 at Base block 49986207, and it is non-zero only inside the vesting window that follows a distributeYield() call, which is gated on STRATEGIST_ROLE and held by a single address, so the window is opened by Kerne rather than by an attacker. The second is that the staked principal has to collapse below the still-unvested amount while that window is open. skUSD holds 1,011.582169134048985696 kUSD of staked principal, and exactly one distribution has ever run on this contract: 0.1 kUSD at block 48383014 on July 9, 2026, the smoke test that proved the bot strategist path end to end, fully vested a day later. At those magnitudes more than 99.99 percent of the stake would have to leave inside a single vesting window. What changes that is a distribution large relative to the stake, or a distribution made into a vault that is close to empty.
Operating rule
Standing operating rule: no distributeYield() call is made into a vault whose staked principal is not large relative to the distribution, and if lockedYield() and totalAssets() ever approach each other, distributions stop until the fix is on chain rather than being sized around the defect. Both numbers are readable on chain by anyone at any time, so the rule is checkable rather than promised. yieldVestingPeriod is 86,400 seconds against a floor of 3,600, and the 2-of-3 Safe can shorten it in a single call, which narrows the window without closing the gap. The gap itself closes only on a redeploy: skUSD is not a proxy, and this is the contract where the protocol's staked kUSD actually sits, so shipping the fix means migrating the staked balance across every holder rather than patching an empty vault. No date is claimed for that here, because a date this page cannot keep is worth less than the plain statement that the fix is written, tested, and waiting on a migration.
KerneVault v2
Deployed 2026-06-16Deployed bytecode
The verified source of the live vault is byte-identical to repository commit ecc95cf7 (2026-06-15), the day before it was deployed. That is roughly three weeks earlier than commit 0912c870, the commit the Hexens review covers. All ten findings in the Hexens final report, published July 31, 2026, are live on this bytecode, along with three defects we found and fixed ourselves after the deploy and before the audit freeze: two NAV accounting gaps on the sweep, L1 and prime-allocation paths, and the withdrawal-side esKERNE forfeiture dust bypass reported by a whitehat. The on-chain public-deposits gate is also absent, so calling depositsEnabled() on the live contract reverts. The equivalent control is now enforced through whitelistEnabled instead, set on July 30, 2026.
Current source
Current source carries the two NAV reclassification fixes, the forfeiture high-water fix, and the depositsEnabled gate. The remediation branch flip/hexens-remediation-jul20 at commit 98f29e55, a child of the audited commit, additionally carries eight of the ten Hexens fixes. The other two findings are acknowledged by design, with the reasoning published in full rather than patched under time pressure.
Practical exposure
The vault holds no user funds. totalSupply() is 0, its WETH balance is 0, and no third party has ever held a share. Both High findings concern esKERNE forfeiture, and esKERNE totalSupply() and totalEmitted() are both 0, so nothing has ever been emitted for the mechanism to act on. There is no open drain. The real exposure was forward-looking and specific: because the on-chain deposits gate was never deployed, maxDeposit() returned the maximum uint256 for any address, so a depositor could have put funds into pre-audit bytecode before the remediated build went live. That door was closed on July 30, 2026 in transaction 0x0be06e9a4a4ce3545fdea6af8e83ed1663e8d0ce35cb28a211836ab83a57579e. whitelistEnabled() is now true, no address is whitelisted, and maxDeposit() returns 0 for every address. No deposit was ever made while it was open: totalSupply() was 0 the whole time and still is. Closing it also closed a second defect we found the same day and had not previously published. Because totalSupply() is 0 while totalAssets() reads 0.013714087110463148 WETH, all of it the strategist-written l1Assets bucket rather than held tokens, the ERC-4626 share math was distorted by a factor of about 1.4e16. At Base block 49317861 previewDeposit() returned 0 shares for any WETH deposit at or below 13,720,947,584,255 wei, about 2.6 cents, which would have taken the asset and minted nothing. The same distortion made a full exit fragile: a depositor of 1 WETH had only 48,961,198,891 wei of headroom before claimWithdrawal() reverts with InsufficientLiquidBuffer, while a single routine strategist write is permitted to move l1Assets by up to 20 percent, and one of the two writes observed in the preceding 22 hours moved it up by 30,196,271,320,664 wei. We reproduced both on an anvil fork of Base against the live bytecode. Nobody was exposed to either, because nobody ever deposited.
Operating rule
Standing operating rule, and the sequencing this protocol commits to: remediated bytecode before capital, not before announcement. No deposit is opened into this address until the remediated build is deployed and verified. The door was closed on chain on July 30, 2026 by a single 2-of-3 Safe call to setWhitelistEnabled(true), transaction 0x0be06e9a4a4ce3545fdea6af8e83ed1663e8d0ce35cb28a211836ab83a57579e, which reproduces the missing gate exactly and leaves withdrawals untouched. Two conditions must both hold before it reopens: the remediated build is deployed and verified, and the share math reads correctly, meaning totalAssets() is backed by assets the vault can actually pay out rather than by an off-chain bucket asserted against a zero share supply. Reopening is a further 2-of-3 Safe call and will be published before it happens, not after. One thing worth stating plainly while it is true and provable: at Base block 49318881 the strategist hot key 0x09a2780a holds STRATEGIST_ROLE and PAUSER_ROLE on this vault and does not hold DEFAULT_ADMIN_ROLE, and every function that can move the underlying asset out of the vault, sweepToExchange, requestL1Deposit, transferToPrime, returnFromPrime and emergencyExit, is gated on DEFAULT_ADMIN_ROLE, which only the 2-of-3 Safe 0x52d3E450 holds. The single strategist function that touches assets is captureFounderWealth, bounded by maxFounderCaptureBps, currently 500, against tracked on-chain assets, currently 0. The full finding-by-finding on-chain map, with commands to reproduce every claim, is in the repository at docs/security/DEPLOYED_BYTECODE_VS_AUDITED_SOURCE_2026-07-25.md.
kUSD
Deployed 2026-04-08Deployed bytecode
Standard OpenZeppelin ERC20Burnable: burnFrom is callable by any address holding an allowance from the token owner. No role gate on burning.
Current source
Burning is gated behind BURNER_ROLE in src/kUSD.sol; only role holders can burn third-party balances, allowance or not.
Practical exposure
An attacker must first obtain an allowance from the holder (a malicious or compromised approval target). With one, they can destroy the holder's kUSD rather than transfer it. There is no path to burn without an allowance, so the practical surface is the same approval hygiene every ERC-20 demands.
Operating rule
Permanent disclosure item. No kUSD redeploy is planned: a token migration would cost holders more than the role gate is worth at current scale. Treat kUSD approvals with the same care as any token approval.
KerneYieldDistributor
Deployed 2026-04 (pre-timelock source)Deployed bytecode
ROOT_UPDATER_ROLE can set a new Merkle distribution root with immediate effect. The role is held by the operational hot wallet 0x09a2780ac8Be6D5d2d1F85A8D92b09D40C9CA37e (the same EOA that signs the hourly Proof of Reserves).
Current source
Root updates go through proposeMerkleRoot and executeMerkleRoot with a 24-hour ROOT_UPDATE_TIMELOCK, so a compromised updater key cannot route funds instantly.
Practical exposure
If the deployed contract held funds and the hot key were compromised, an arbitrary root could claim them. Today the exposure is zero in practice because the contract is deliberately unfunded: there is nothing on it to route.
Operating rule
Standing operating rule: never fund the deployed distributor. Redeploy from current source (with the timelock) before any real yield routes through it. The unfunded state is verifiable on chain at any time.
Recently closed on chain
Verified August 14, 2026skUSD immediate distribution. This was a standing divergence until the 2026-07-03 redeploy to 0x96F5102C shipped the streaming distribution code. Each yield distribution now vests over a 24-hour window (yieldVestingPeriod = 86,400 seconds, re-read on chain at Base block 49986207), so a depositor can no longer bracket a distribution with transient capital. That closure is intact and the value is readable on chain at any time.
This entry used to end by saying deployed matched source on this contract. That was accurate from 2026-07-03 until 2026-07-30, when the adversarial sweep of the live money surfaces found a second defect in the same withdrawal path, KRN-26-SKUSD-SQUAT. It was fixed in source the same day and it is not on chain, so skUSD is now the first row of the divergence table above rather than a closed entry here. The two are separate findings against the same contract: the streaming fix that closed in July is live, and the one-wei squat on the orphan reset is not.
Not a divergence, but read it before you file one
Updated July 29, 2026KerneTreasury v3 0x5343C41d is the live treasury and the address the live mint PSM returns from treasury(). It had no published source on BaseScan, Sourcify or Blockscout as of July 28, 2026, so it cannot be diffed on an explorer the way the rows above can be, which is why it is not one of them. That is a statement about verifiability, not a claim that its bytecode matches current source. It holds no protocol assets, 0 ETH, 0 WETH, 0 USDC and 0 KERNE, and its owner is the 2-of-3 Safe.
Where a gap does exist, as of July 29, 2026. Current source requires the caller of executeBuyback to supply an output floor drawn from a price that caller cannot move inside the execution block, and rejects a zero floor outright with no owner exemption. The deployed bytecode still accepts a zero floor and, in that case, derives the entire floor from a quote read against the same Aerodrome pool the swap executes on, inside the same transaction. That floor is not weak, it is inert: it moves by the same factor an attacker moves the pool by, so it clears at every manipulation size. Two independent researchers demonstrated it 13 days apart. The fix is in source only and reaches chain in a v4 redeploy, which stays behind the external audit.
It is not reachable on the deployed system. This address holds no inventory, previewBuyback returns zero so the call reverts before any swap, no buyback keeper is granted, and no KERNE venue deep enough to sandwich exists. The buyback flywheel stays disarmed until the floor is drawn from a manipulation resistant price source, and arming is a 2-of-3 Safe action rather than a bot action. That gate is now enforced in code rather than prose.
Why we publish this
A reviewer who finds a source-versus-chain gap on their own has every reason to read it as concealment. The same facts, stated by the team first with addresses and operating rules, are evidence the team knows its own system. Kerne has found and triaged over two hundred findings against its own code, and publishes the named ones with live statuses at /security/findings-tracker; this page is where the honest residue of that work lives. If a future deployment changes any row above, this page changes in the same commit.
Continue the audit trail: the audit and security posture page, the per-finding status tracker, the public contract registry, and the one-link diligence surface at kerne.fi/dataroom.
Where to go from here. If you would rather check the reserve figure without trusting this site, DefiLlama tracks Kerne independently and states its own methodology at defillama.com/protocol/kerne. If you run a protocol and this is the kind of disclosure you want out of your own review, we will introduce you to Hexens, who pay us if that introduction becomes a paying engagement. You pay us nothing for it and we add nothing to what Hexens quotes you, and we say so at /security/audit-referrals before the recommendation rather than after it. If you are weighing an allocation instead, the queue at /commit holds your multiplier while you finish reading and moves no funds. None of that changes the table above: every row is open on the deployed bytecode today, and vault deposits stay closed until that is no longer true.