Guardian's review of Loan Protocol Vault for Loan Meme, published October 2026. The report records 3 findings, including 3 informational.
- Published
- Review window
- September 24 to 28, 2026
- Language
- Solidity
- 0 Critical
- 0 High
- 0 Medium
- 0 Low
- 3 Informational
Scope
1 file in scope · 218 nSLOC
| File | nSLOC | Lines |
|---|---|---|
contracts/Treasury.sol | 218 | 300 |
Findings 3
-
I-01 Informational Loan asset identity is inconsistent Documentation Resolved
Description
The Treasury is intended to hold an ERC-20 loan asset whose address and caps in raw token units are supplied at deployment.
docs/AUDIT-SCOPE.mdcalls that asset USDG in its overview but USDC in its trust model.docs/REQUIREMENTS.mdthen describes USDG as Robinhood's native currency, although the scope excludes native currency andpayout()transfers throughIERC20.safeTransfer. These labels leave the token to configure and fund unclear. If USDG is the intended loan asset, an operator following the USDC label can configure or fund the wrong token, leaving the intended payouts unavailable until the configuration and funding are corrected.Recommendation
Identify the intended ERC-20 loan token consistently in the scope and requirements, including its target chain contract address and decimals. Correct the native currency entry.
-
I-02 Informational Runtime bytecode differs across instances Documentation Acknowledged
Description
The deployment specification says Treasury instances have identical bytecode. The
Treasuryconstructor initializes OpenZeppelinEIP712, which caches the instance address and its domain separator in immutable variables. Solidity embeds these values in deployed runtime code, so instances deployed from the same build with identical constructor arguments have different runtime bytecode and code hashes. The stated identical bytecode guarantee therefore cannot be used as a literal identity check on chain for shard instances.Recommendation
State that instances use the same source and compiler artifact while their deployed runtime code contains instance specific immutable values. Compare deployed code with those immutable positions accounted for when verifying shards.
-
I-03 Informational Cap invariant omits later cap reductions Documentation Resolved
Description
The audit invariant says total payouts for an asset and epoch never exceed
dailyCapand each payout never exceedsmaxPerPayout, without specifying when those cap values apply. The owner can lower either cap after a successful payout throughsetCaps(), which leaves historicalspentunchanged. For example, a 10,000-unit payout followed by a reduction ofdailyCapto 9,999 leavesspentat 10,000, above the current cap. That earlier payout can likewise exceed a subsequently reducedmaxPerPayout.This is accepted configuration behavior. When historical spend reaches or exceeds the current daily cap, the contract reports zero remaining capacity and rejects further positive payouts. The unqualified written invariant therefore overstates the guarantee that an audit report or integrator can make; the cap checks do not permit an excess payout at execution. The baseline test in
test/Treasury.ts:265-271exercises a cap reduction below prior spend, zero remaining capacity, and rejection of another payout.Recommendation
Qualify both audit invariant lists so each successful payout is bounded by
maxPerPayoutand leaves epoch spend withindailyCapunder the caps in force when it executes. Explain that later authorized reductions can leave historical epoch spend above the currentdailyCapor an earlier payout above the currentmaxPerPayout. Keep the existing spend accounting.Resolution
Guardian: Invariant 3 in both documents still says that lowering either cap blocks further positive payouts until the epoch changes or a cap is raised. Lowering only maxPerPayout still permits a smaller payout in the same epoch when daily capacity remains.
No findings match.
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
