Kerne Logo
← Back to Insights
May 8, 20267 min readUpdated June 25, 2026

What the Resolv Exploit Tells Us About Synthetic-Dollar Mint Paths

On March 22, an attacker minted $80M of unbacked USR against about $100K of USDC by compromising a single off-chain key. The architectural class of failure that made it possible is more common than the post-mortems suggest. Here is the mechanism, the three on-chain questions worth asking before depositing into any synthetic dollar, and how Kerne's mint path answers them.

What the Resolv Exploit Tells Us About Synthetic-Dollar Mint Paths

On March 22, 2026, an attacker minted approximately 80 million USR against about $100,000 of USDC, then extracted approximately 25 million dollars of ETH before the team could pause the contracts. USR's price depegged from $1 to a low of roughly $0.025 (about 2.5 cents) on Curve before partial recovery. Both Halborn and QuillAudits published detailed post-mortems within the week.

This was not a smart-contract bug in the traditional sense. The Solidity compiled cleanly. The on-chain logic did exactly what it was told. The exploit lived in the line where on-chain logic accepted instructions from off-chain code that it should not have trusted to issue them.

Six weeks later, the relevant question is no longer "what did Resolv get wrong." It is: "what is the same class of failure called in every other synthetic dollar that ships, and how do you check whether a protocol you are about to deposit into has it." This post is a forensic read of the Resolv mint path, the architectural class of failure it represents, and a reading of Kerne's own mint path under the same questions.

The mechanism, decomposed

The Resolv mint mechanism worked like this in normal operation. A user wanted USR. They sent USDC to a contract entry point. An off-chain Resolv backend observed the deposit, computed the USR amount, signed an instruction with a backend-held key (the SERVICE_ROLE in their AWS KMS configuration), and called completeSwap on the on-chain contract. completeSwap accepted the instruction if the signature matched the configured SERVICE_ROLE address, and minted the requested amount of USR to the user.

The exploit: an attacker compromised the SERVICE_ROLE signing key. They sent about $100,000 of USDC into the deposit entry, the first of several such deposits (roughly $300,000 in total across three transactions, per QuillAudits). They then signed and submitted completeSwap instructions calling for 80 million USR in total, minted as 50 million and then 30 million, far in excess of the collateral provided. The on-chain contract verified the signature, observed it matched the configured SERVICE_ROLE, and minted. There was no on-chain check that the requested mint amount was bounded by the deposit. There was no per-block, per-day, or per-call cap. There was no second signer. The off-chain key was sufficient.

How the attacker obtained that key is documented in Resolv's own post-mortem: a GitHub credential belonging to a contractor, reused without that person's knowledge from a prior third-party engagement, let a malicious CI/CD build workflow exfiltrate the SERVICE_ROLE signing key, and Resolv reported no evidence of insider involvement, so the contractor was an unwitting credential-reuse vector rather than a culpable party.

That is the entire exploit, end to end. The approximately $25 million extracted afterward was the attacker swapping the unbacked USR through secondary market liquidity across decentralized exchanges, before the team could pause the contracts.

The class of failure

The architectural class of failure here is not "Resolv had a bad key-management process." Plenty of protocols have had keys compromised. The class of failure is: a single off-chain key is the sole gate on an unbounded on-chain state change.

Three properties combined to make the Resolv vector exploitable:

  1. The on-chain mint amount was set by the off-chain caller, not derived from on-chain state. The contract did not verify the relationship between USDC deposited and USR minted; it took the requested amount as authoritative.
  2. There was no per-call or per-block ceiling on the mint quantity. A single transaction could mint any number, including amounts orders of magnitude larger than the protocol's total backing.
  3. The privileged role required exactly one signature, and the signer was an automated off-chain process whose key lived in a cloud KMS rather than in cold-stored multisig hardware.

Any one of these three would have been recoverable. Two of them combined would have been damaging but bounded. All three together made the protocol's entire collateral pool extractable in a single uninterrupted run by anyone who got the one key.

This is not a Resolv-specific failure mode. Several active synthetic-dollar protocols ship with at least one of the three properties present. Two of them is more common than the public discourse suggests. The Resolv exploit is the canonical case study because the third property closed the loop and made it visible at scale.

Three questions for any synthetic dollar

The lesson the Resolv post-mortem teaches is not "do not trust off-chain code." Off-chain code is unavoidable in any protocol with a CEX hedge, an oracle pipeline, or a queue-based withdrawal mechanism. The lesson is: when off-chain code controls an on-chain state change that mints supply, the on-chain side must constrain what off-chain can request.

When evaluating a synthetic dollar, three on-chain questions get you most of the way to a verdict.

  1. What is the on-chain authority required to mint the supply token, and how is it gated? AccessControl with a named role is better than Ownable with a single key. A multi-signature requirement is better than a single. A timelock on the role-grant is better than no timelock.
  2. Is the mint amount derived from on-chain state, or is it set by the caller? An on-chain price oracle plus a deposit balance check is verifiable. A "completeSwap with off-chain calculated amount" is a Resolv vector.
  3. Is there a per-call, per-block, or per-day cap on mint quantity? A protocol that can mint arbitrarily many supply tokens in a single transaction is one bug away from a Resolv outcome. A protocol that caps mint at a fraction of TVL per day, even if every other gate fails, has a containment perimeter.

Each of these is one cast call, against the relevant chain, against the relevant contract. Anyone who reads the answers correctly can answer the question for any protocol they are considering.

Kerne's mint path under the same three questions

Kerne's kUSD mint path runs the same three checks as follows.

Authority. AccessControl with two named roles: DEFAULT_ADMIN_ROLE and MINTER_ROLE. DEFAULT_ADMIN_ROLE is held by a 2-of-3 Gnosis Safe multisig at 0x52d3E450bA6c299B1B07298F1E87DD74732D4877. MINTER_ROLE is held by exactly two contracts: KerneVault v2 at 0x8ccc56B5624e2FDB592F6609d81F4c3798e3292B and KUSDPSM v3 at 0x07eBb486e11BD217e6085eb5ab663e4517595993. Both are on-chain contracts, not off-chain keys. The earlier KUSDPSM at 0xFf3025ec18e301855aB0f36Ec6ECa115a29A5Fbc had MINTER_ROLE revoked in the June 16, 2026 ceremony and now serves only as the kUSD-to-USDC redeem reserve.

Mint amount. The user-facing mint path is KUSDPSM.swapStableForKUSD. It reads the USDC the user deposited from the contract's own balance, applies a 10 basis point fee defined as a contract constant, and mints the resulting kUSD. There is no caller-supplied mint quantity, no off-chain "this is what you are getting" signal, no SERVICE_ROLE equivalent. The function is a pure on-chain calculation.

Caps. KUSDPSM enforces an on-chain mintingEnabled flag that the multisig can flip in one transaction if anything looks off. The Phase 2 kUSDMinter contract, which is in source but not yet deployed, additionally caps mint at a configurable per-day amount as a defense-in-depth ceiling. For Genesis Window scale this is intentionally conservative.

These three are checkable in three commands against Base mainnet:

# 1. Is the mint privilege held only by on-chain contracts?
cast call 0x5C2E...0AA3 "hasRole(bytes32,address)(bool)" \
  $(cast keccak "MINTER_ROLE") 0x07eB...5993
# expected: true
# 2. Is the mint surface currently active?
cast call 0x07eB...5993 "mintingEnabled()(bool)"
# expected: true
# 3. Is role-administration gated by the multisig?
cast call 0x5C2E...0AA3 "hasRole(bytes32,address)(bool)" \
  $(cast keccak "DEFAULT_ADMIN_ROLE") 0x52d3...4877
# expected: true

The first command verifies KUSDPSM is the only non-vault holder of the mint privilege. The second verifies the mint surface is currently active. The third verifies role-administration is gated by the multisig, not a single EOA. We assert all three values continuously via /api/risk-status (rendered in human-readable form at /risk) and an hourly CI workflow at .github/workflows/onchain-role-smoke.yml; if any flips, the workflow fails and the homepage banner reflects it.

What we still owe

We are not implying Kerne has nothing left to harden. The 201-finding internal adversarial audit we ran on May 8 catalogues 36 findings rated critical, 51 high, 51 medium, 37 low, and 26 informational across the full contract set. The kUSDMinter contract referenced above is in source but not yet deployed. The KerneVerificationNode contract for cross-chain attestation is also in source and not yet deployed. Several access-control hardening items from the audit's section on default thresholds, role-admin self-loops, and single-key STRATEGIST_ROLE on adapter contracts remain on the remediation queue.

The point of this post is not to claim immunity. It is to demonstrate a posture difference: every claim about how Kerne's mint path works is verifiable in three commands; every gap is named with the remediation status visible. The Resolv exploit was a class of failure that would have been detectable to any reader running those three commands beforehand. Synthetic-dollar protocols that survive long-term will be the ones whose readers do.

Conclusion

Resolv was a profitable basis-trade product before March 22. The mechanism worked. The yield was real. The team was competent. The thing that ended it was not a market move or a wrong bet. It was an architectural choice that placed an unbounded mint authority behind a single off-chain key, and the absence of any on-chain ceiling that would have limited the blast radius when that key was inevitably compromised.

Every synthetic-dollar protocol designer reads that post-mortem and decides what to do about it. The right answer is not "we have better key management than Resolv did." The right answer is to make the mint path checkable from the chain, with no off-chain authority that can mint more than a verifiable function of on-chain deposit balance, and with caps that contain blast radius even if every other defense fails.

That is the bar Kerne is built around. The full open-finding list at /security names what we still need to do to fully meet it. The verification surface at /api/risk-status reports continuously on whether we are meeting it today. Three commands, no claims.

If you hold or allocate to a synthetic dollar and want this same check run independently on its reserves rather than your own, that is what kerne.fi/counterparty-verification provides: a point-in-time, independently signed read of a counterparty's public on-chain reserves, checkable the same way.

The Resolv exploit recap in this piece is drawn from Resolv's own post-mortem (published June 2, 2026) and the public analyses by Halborn and QuillAudits. Reported figures vary across these sources: the amount extracted is put at roughly $23 million to $25 million (Resolv's own post-mortem says approximately $25 million; QuillAudits itemizes $23.7 million in ETH plus $1.2 million in wstUSR), the attacker's USDC deposits at roughly $100,000 to $300,000, and the USR price low at roughly $0.025 (about 2.5 cents) on Curve per The Block and CoinDesk (Halborn prints $0.0025 but links that figure to The Block). Where they differ, this piece uses hedged figures. For the on-chain check commands above against Base mainnet, see Exit Triggers and Emergency Runbook. For Kerne's full open-finding list and remediation cadence, see Security and Audits.

Verify it yourself

Run the same check on any reserve, or have it run for you.

Paste any issuer's signed attestation into the free verify tool and recover the signer, rehash the figures, and check freshness in your own browser. For a machine-signed, point-in-time read of an address you name, delivered on the page in about two minutes, the instant self-serve read is $29; a human-reviewed read is $149. An independent read of a counterparty you hold or allocate to is $2,500. Attestation tooling, not an audit, and not a solvency opinion.