
Loading...
Loading
Loading...
LoadingLoading audit report...

WalletGuard.ai, powered by Gestalt Labs
Forge fork-validation ran but no findings met the threshold for PoC inclusion. See the per-finding "Forge validated" badges in the report below for individual results.
The analyzed contract is a multi-component NFT and social credentialing protocol (Phi Protocol) comprising bonding curve share trading, ERC-1155 NFT minting, reward distribution, and curator tracking logic across several upgradeable contracts. The analysis identified 2 critical, 7 high, 12 medium, 8 low, and 5 informational findings after deduplication and severity calibration. The single most dangerous pattern is the public visibility of internal accounting functions, specifically `_addCredIdPerAddress` and `_removeCredIdPerAddress`, combined with reward redirection via unguarded claim entry points in `Claimable.sol`, which together allow any attacker to corrupt curator state and divert protocol rewards. The contract system is not safe for deployment in its current form; multiple high-severity access control and reentrancy deficiencies must be remediated before mainnet launch.
Anyone can call the NFT contract's claim functions directly and supply arbitrary wallet addresses for referral and verifier rewards, redirecting those ETH payments to attacker-controlled addresses. The NFT is also minted to any address the attacker specifies, bypassing the intended flow that routes all claims through PhiFactory's access controls.
Two functions that manage internal bookkeeping for which NFT credentials a wallet holds are mistakenly public, meaning any outsider can add fake entries to any wallet or delete real ones. This corrupts position tracking, can break the sell mechanism for legitimate holders, and can be used to permanently lock users out of selling their shares.
When a new artwork is created for an existing credential, the Merkle root that proves who is eligible to claim prior artworks is silently overwritten. This means a cred creator can invalidate all outstanding claim proofs for existing artworks simply by creating a second artwork, permanently freezing NFT claims for everyone who had not yet claimed.
4 centralization points identified
The upgrade path is controlled by PhiFactory with no passthrough function, making upgrades to NFT instances impossible in practice without design change; this is an architectural property buyers should know.
_authorizeUpgrade()Exploitation requires the owner to set a malicious fee value; this is an admin risk property buyers should know about rather than a direct vulnerability.
setProtocolFeePercent()The PhiFactory-as-owner design means all NFT instances are controlled by PhiFactory; this is a centralization property buyers should know, not a direct exploit path.
initialize()The use of immutable instead of constant in an upgradeable contract is an upgrade footgun that buyers should know about; no immediate exploit path exists with the current correct values.
An attacker first calls the public `_removeCredIdPerAddress` to delete a victim curator's legitimate cred tracking entries. When the victim later attempts to call `sellShareCred`, the internal `_updateCuratorShareBalance` function attempts to remove the credId from the same corrupted index structure, reverting with `WrongCredId` or `IndexOutofBounds`. The victim's shares are permanently unsellable even though their on-chain share balance remains positive. The attacker can simultaneously call `_addCredIdPerAddress` with phantom credIds to further corrupt the array structure and maximize the disruption.
A valid phiSigner signature for a legitimate minter is intercepted or obtained (e.g., from the mempool during a legitimate claim transaction). The attacker calls `signatureClaim` directly on the PhiNFT1155 contract (not through PhiFactory), supplying the legitimate minter's signature but substituting attacker-controlled addresses for `ref_` and `verifier_` fields. PhiFactory's `_validateAndUpdateClaimState` accepts this because `msg.sender` is the NFT's `art.artAddress`, bypassing the tx.origin guard. The NFT is minted to the legitimate minter (signature is valid for that field), but all referral and verifier ETH rewards are diverted to the attacker. This is compounded by the tx.origin bypass finding which confirms the validation path is exploitable.
`buyShareCred` and `sellShareCred` lack `nonReentrant` guards. During a buy, the excess ETH refund is sent to `_msgSender()` before `lastTradeTimestamp` is updated. A malicious contract receiving the refund can reenter `buyShareCred` again at the same bonding curve price (since supply was already incremented), obtaining shares at an earlier price point and manipulating the curve state. During a sell, the payout is sent before the reward deposit call to `IPhiRewards`, enabling reentrancy into sell or buy during the ETH transfer window. The `ContributeRewards` nonReentrant guard is also permanently broken (locked starts at 0, not 1), meaning if reentrancy protection were ever applied to reward claim functions, it would always revert instead of protecting.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 8 | 1H2M | 79% | 1.8m | Cross-function reentrancy in Cred._handleTrade, buyShareCred, sellShareCred, ERC-1155 callback reentrancy in PhiNFT1155.claimFromFactory via _mint -> onERC1155Received, Classic reentrancy patterns in RewardControl._withdraw, nonReentrant modifier initialization and usage across all contracts, CEI pattern compliance in all ETH-transferring functions, Access control on _addCredIdPerAddress and _removeCredIdPerAddress, Self-call patterns in PhiFactory.claim and batchClaim, Read-only reentrancy via view functions, ERC-777 callback risks (no ERC-777 tokens used, standard ERC-20 only in ContributeRewards), Flash loan callback patterns, Integer overflow/underflow in bonding curve calculations, Batch trade validation and execution logic, Merkle proof verification correctness, Signature validation and expiry checks, UUPS upgrade authorization, ContributeRewards nonReentrant lock initialization |
| access control | success | 9 | 1H2M2L | 81% | 1.5m | Access control on all public/external functions, Ownership patterns and two-step ownership transfer, UUPS initializer protection and _disableInitializers in constructors, Signature verification (ECDSA.recover, ecrecover, EIP-712), Signature replay attacks (nonce, expiry, chainId), tx.origin usage for authorization, Reentrancy patterns and nonReentrant guard coverage, ERC-1155 safe transfer callbacks (CEI pattern), delegatecall usage, selfdestruct presence, ETH withdrawal access controls, Merkle proof verification, Array bounds and pagination logic, Internal vs public function visibility, Custom reentrancy guard initialization, Cross-chain replay protection, ContributeRewards access control, CuratorRewardsDistributor reward distribution logic |
| economic | success | 11 | 2H3M1L | 86% | 1.8m | Flash loan attack surface on bonding curve pricing and share balance checks, Oracle manipulation – no external price oracles found, bonding curve uses pure math, Reentrancy in Cred._handleTrade, PhiFactory claim flow, CuratorRewardsDistributor.distribute, ContributeRewards.claimReward, Access control on all external/public functions across all contracts, Signature verification in Cred.createCred, Cred.updateCred, PhiFactory.signatureClaim, Merkle proof verification in PhiFactory.merkleClaim and ContributeRewards.claimReward, EIP-712 typed data signing in RewardControl.withdrawWithSig, Integer overflow/underflow checks (Solidity 0.8.25, checked by default), Precision loss in fee/reward calculations, UUPS upgrade authorization in PhiNFT1155 and Cred, Soulbound token transfer enforcement, Reward distribution logic in CuratorRewardsDistributor and PhiRewards, Pagination logic in getPositionsForCurator and getCuratorAddresses, nonReentrant modifier initialization in Cred and ContributeRewards, credMerkleRoot overwrite on new art creation for existing creds, MEV exposure in batchBuyShareCred/batchSellShareCred price calculation vs execution gap, withdrawFor permissionless withdrawal triggering, BondingCurve._getCreatorFee supply==0 edge case |
| logic validation | success | 14 | 1H4M4L | 81% | 2.5m | Input validation and parameter bounds on all public/external functions, Integer overflow/underflow in arithmetic operations including unchecked blocks, Access control on state-modifying functions, Reentrancy patterns across PhiFactory, Cred, PhiNFT1155, RewardControl, CuratorRewardsDistributor, EIP-712 domain separator construction in RewardControl/solady EIP712, Signature verification in Cred.createCred, PhiFactory.signatureClaim, Merkle proof verification in PhiFactory.merkleClaim and ContributeRewards.claimReward, State machine integrity in art creation, claiming, and cred trading, Bonding curve arithmetic and precision loss, Array bounds and pagination logic in getPositionsForCurator and _getCuratorData, ETH handling, refund logic, and fee calculations, Timestamp dependence in share lock period and signature expiry, Unbounded loops in distribute() and batch operations, UUPS upgrade authorization, Soul-bound token transfer restrictions, ContributeRewards reward claim logic and merkle proof indexing |
| code quality | success | 13 | 84% | 1.8m | ERC-1155 standard compliance (PhiNFT1155), ERC-2981 royalty compliance, EIP-712 domain separator and signature verification, UUPS upgrade safety and authorization, Reentrancy in Cred buy/sell trade flow, Reentrancy in PhiFactory claim flow, Access control on internal/public functions, Merkle proof verification correctness, Bonding curve price calculation logic, Reward distribution math and rounding, RewardControl withdraw functions, ContributeRewards claim flow, Array bounds and pagination logic in getPositionsForCurator, Signature replay protection (nonces, deadlines), Integer overflow/underflow in Solidity 0.8, Creator parameter validation in createCred, Gas token minting abuse patterns, Storage layout for upgradeable contracts | |
| compiler bugs | success | 6 | 1M | 86% | 1.3m | Access control on all public/external functions across all contracts, Reentrancy patterns in PhiFactory, Cred, CuratorRewardsDistributor, ContributeRewards, RewardControl, Signature validation in PhiFactory.signatureClaim, Cred.createCred, Cred.updateCred, Merkle proof validation in PhiFactory.merkleClaim and ContributeRewards.claimReward, Integer overflow/underflow in bonding curve calculations and fee arithmetic, ERC1155 transfer restrictions (soulBounded) in PhiNFT1155, UUPS upgrade authorization in PhiNFT1155 and Cred, Royalty configuration bounds checking, Pagination logic in Cred.getPositionsForCurator and getCuratorAddresses, ETH handling and refund logic in PhiFactory._processClaim and Cred._handleTrade, RewardControl.withdrawFor authorization, credMerkleRoot overwrite vulnerability, Compiler bug patterns for solc 0.8.25 (not in any known affected range for listed bugs), Storage variable initialization (locked in ContributeRewards) |
| assembly safety | success | 12 | 2H3M1L | 85% | 2.3m | Full codepoint-by-codepoint scan for non-ASCII characters (RTLO, zero-width, homoglyphs) across all files — none found, All assembly{} blocks — only memory array resize patterns (mstore) found, no sload/sstore/selfdestruct/delegatecall, Reentrancy patterns in PhiFactory claim/batchClaim/signatureClaim/merkleClaim, Access control on Cred._addCredIdPerAddress and _removeCredIdPerAddress (public visibility bug), Claimable.signatureClaim and merkleClaim — unguarded external entry points on PhiNFT1155, RewardControl.withdrawFor — forced withdrawal without authorization, Cred.getPositionsForCurator — array index bug when start_ > 0, BondingCurve._getCreatorFee — missing return at supply_ == 0, ContributeRewards.setRewardInfo — missing access control, PhiFactory.createArt — signature validation and creator_ parameter validation, Cred.createCred — creator_ parameter not validated against signed data, UUPS upgrade safety for PhiNFT1155, EIP-712 signature replay protection in RewardControl.withdrawWithSig, MerkleProof library implementation, PhiFactory._validateAndUpdateClaimState — tx.origin check logic, Integer overflow/underflow in bonding curve arithmetic, Timestamp dependence in SHARE_LOCK_PERIOD, Share lock period bypass in batch operations, Flash loan attack surface on bonding curve price manipulation, Storage collision risks in UUPS upgradeable contracts |
| l2 specific | success | 13 | 1H1L | 81% | 2.6m | Access control on all public/external functions in Cred.sol, PhiFactory.sol, PhiNFT1155.sol, Reentrancy patterns in Cred._handleTrade, PhiFactory._processClaim, ContributeRewards.claimReward, Signature validation in Cred.createCred, Cred.updateCred, PhiFactory.signatureClaim, Merkle proof validation in PhiFactory.merkleClaim and ContributeRewards.claimReward, UUPS upgrade authorization in PhiNFT1155 and Cred, Array index logic in Cred.getPositionsForCurator and _removeCredIdPerAddress, Bonding curve fee calculation correctness in BondingCurve._getCreatorFee, ETH accounting and refund logic in PhiFactory._processClaim and Cred._handleTrade, nonReentrant mutex initialization in Cred, PhiFactory, ContributeRewards, ERC1155 transfer restrictions (soulBounded) in PhiNFT1155, RewardControl withdraw functions and EIP-712 signature verification, Merkle root overwrite behavior in PhiFactory._initializePhiArt, Claimable entry points and caller validation, CuratorRewardsDistributor distribute() rounding and balance accounting, Timestamp dependence for SHARE_LOCK_PERIOD, Integer overflow/underflow risks (Solidity 0.8.25 built-in protection), L2-specific patterns - no L2-specific imports or predeploy addresses found; contract is chain ID 1 (Ethereum mainnet) |
| upgrade | success | 11 | 2L | 88% | 1.8m | UUPS upgrade authorization in PhiNFT1155 and Cred (onlyOwner _authorizeUpgrade), disableInitializers() in constructors of upgradeable contracts, Storage layout of PhiNFT1155 and Cred for upgrade safety, CreatorRoyaltiesControl storage gap absence, Proxy/clone interaction: PhiFactory uses cloneDeterministic for ERC-1167 clones of UUPS contract, Initialization: initialize() functions guarded by initializer modifier, Access control on public/external functions in Cred.sol, RewardControl withdraw, withdrawFor, withdrawWithSig authorization, EIP-712 signature verification in RewardControl.withdrawWithSig, Reentrancy guards in PhiFactory, Cred, and ContributeRewards, Merkle proof validation in PhiFactory.merkleClaim and ContributeRewards.claimReward, Signature recovery and replay protection in PhiFactory and Cred, BondingCurve pricing math and fee calculation edge cases, getPositionsForCurator array index logic, credMerkleRoot overwrite on multiple art creation for same cred, Claimable.signatureClaim and merkleClaim access control, selfdestruct presence (none found), delegatecall to untrusted addresses (none found), Integer overflow in Solidity 0.8.25 (protected by default), ContributeRewards reward claim logic and bitmap tracking |
This automated audit has inherent limitations. The following areas are not covered.
This report is an automated point-in-time assessment and does not guarantee protection against all possible attacks. It does not cover off-chain components, economic modeling, or business logic correctness unless explicitly noted. Changes to the contract after the audit commit are not reviewed. This is not financial or legal advice. WalletGuard, powered by Gestalt Labs, provides this analysis as-is with no warranty of completeness.
[](https://walletguard.ai/audit/8149a711-81de-4128-bd7b-0990d2baaa5e)
<a href="https://walletguard.ai/audit/8149a711-81de-4128-bd7b-0990d2baaa5e"> <img src="https://walletguard.ai/api/badge/8149a711-81de-4128-bd7b-0990d2baaa5e" alt="WalletGuard Audit Badge" /> </a>