Guardian's review of WHUF Transfer Lock for Ethos Network, published September 2026. The report records 5 findings, including 5 informational.
- Published
- Language
- Solidity
- Chains
- Base
- Sector
- Infrastructure
- 0 Critical
- 0 High
- 0 Medium
- 0 Low
- 5 Informational
Scope
4 files in scope · 35 nSLOC
| File | nSLOC | Lines |
|---|---|---|
src/errors/WhuffieErrors.sol | 0 | 19 |
src/EthosWhuffie.sol | 18 | 174 |
src/interfaces/IWhuffieLockList.sol | 3 | 10 |
src/WhuffieLockList.sol | 14 | 44 |
Findings 5
-
I-01 Informational Deployment tooling omits the new lock-list constructor argument Configuration Acknowledged
Description
The change makes every EthosWhuffie implementation deployment require an IWhuffieLockList constructor argument. The supplied deployUpgradeable() path still uses deployViaCreate2(signer, factory, [], salt) for deterministic deployment and factory.deploy() for the legacy path, so neither provides that argument. The ethosWhuffie configuration supplies only the three proxy initializer arguments, and the market-stack deployment sequence does not deploy a WhuffieLockList first. The compiled EthosWhuffie artifact confirms one mandatory constructor input. Fresh proxy deployments and implementation upgrades through the supplied flow cannot encode or deploy this implementation. Solidity fixtures pass the list directly and miss the mismatch. This audit mirror omits package.json, deploy.ts, and abi/index.ts, so the TypeScript path could not be executed end to end here; the code and compiled ABI establish the argument mismatch. Downstream code: scripts/deploy-contract.ts:944-951 and scripts/ops/deploy-market-stack.ts:22-46.
Recommendation
Be aware.
-
I-02 Informational Scheduled unlock observability is lazy and its timestamp depends on the trigger Events Acknowledged
Description
At UNLOCK_AT, the launch lock no longer prevents a balance change, but transfersUnlocked remains false and no TransfersUnlocked event is emitted until a successful balance change lazily updates the flag. That automatic path emits UNLOCK_AT even if the triggering transaction occurs later. The owner may instead call unlockTransfers() after UNLOCK_AT before any balance change; that path emits the later block.timestamp. Thus the same scheduled release can produce either the deadline or a later transaction time depending on which post-deadline transaction arrives first. During the gap, the public flag is false even though a transfer would succeed. The event NatSpec calls its field the block timestamp when transfers were unlocked, leaving its meaning inconsistent for frontends and monitors. On-chain transfer enforcement remains correct. Prior I-30 concerned a missing manual-unlock event; that was fixed before this scheduled path was introduced.
Recommendation
Either acknowledge the finding or apply one of the proposed fixes:
- emit
block.timestampat both places - emit
min(block.timestamp, UNLOCK_AT)inunlockTransfers()
- emit
-
I-03 Informational Market and Rewards documentation does not respond to actual behavior Documentation Acknowledged
Description
The documentation states that Market trading and Rewards claims are not expected to work for locked accounts because
EthosMarketandEthosRewardsare included inWhuffieLockList. However,_update()applies the restriction based only on whether the WHUF sender is locked, making the actual behavior dependent on the direction of each transfer.```solidity
Which WHUF flows should work before transfers unlock?
The lock is per account, not global. Accounts on
WhuffieLockList(pre-launch mint recipients other than Treasury, and Ethos proxies other thanEthosVouchV2andEthosReview) cannot send WHUF beforeUNLOCK_ATexcept into or out ofEthosVouchV2, or as anEthosReviewfee pull. Accounts not on the list, including market makers funded by Treasury, transfer freely. Market trading and Rewards claims are not expected to work for locked accounts beforeUNLOCK_ATbecauseEthosMarketandEthosRewardsare themselves on the list. ```When buying from the Market, the buyer is the WHUF sender. Purchases therefore fail for locked buyers but remain available to buyers who are not included in
WhuffieLockList, regardless of the Market’s locked status. When selling,EthosMarketis the WHUF sender, so the payout fails while the Market remains locked even if the seller is not locked. Similarly,EthosRewardsis the WHUF sender duringclaim(). Consequently, Rewards claims fail whileEthosRewardsremains locked even when the claimant is not included inWhuffieLockList.I-Market and Rewards documentation does not respond to actual C O N T I N U E D03behaviorTherefore, before the global transfer unlock, an unlocked user can buy a Market position but cannot sell it or claim accrued rewards.
Recommendation
Revisit whether this is the intended behavior and if yes, fix the documentation. Otherwise, apply the needed changes
-
I-04 Informational TransfersUnlocked owner parameter documentation is ambiguous Documentation Acknowledged
Description
The
ownerparameter is described as “the token owner,” which could mean either a WHUF holder or theEthosWhuffiecontract owner. The emitted value represents the contract owner.Recommendation
Describe
owneras “theEthosWhuffiecontract owner.” -
I-05 Informational UNLOCK_AT documentation incorrectly requires a balance change Documentation Acknowledged
Description
The comment states that the first balance change after
UNLOCK_ATtriggers unlocking. However, zero-value transfers, mints, and burns also invoke_update()and unlock transfers without changing any balance.Recommendation
Replace “balance change” with “transfer, mint, or burn operation.”
No findings match.
More from Ethos Network
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.
