Guardian's review of Hook for Story Protocol, published December 2025. The report records 11 findings across 2 review rounds, including 2 medium and 4 low.
- Published
- Review window
- November 3 to 25, 2025
- Rounds
- Main Review, Remediation Review
- Language
- Solidity
- Chains
- Story
- Sector
- Infrastructure
- 0 Critical
- 0 High
- 2 Medium
- 4 Low
- 5 Informational
Scope
Findings 11
Main Review
10 findings · November 3 to 5, 2025-
M-01 Medium maxMintingFee Broken For registerDerivative Logical Error Resolved
Description
In the
registerDerivativefunction amaxMintingFeeparameter exists to limit the maximum fees that a user must pay when invoking the function.However the derivative registration can pull from multiple
licenseTermsIdswhich may have differing fee currencies, making the shared maximum entirely ineffective forlicenseTermsIdswhich would use several currencies which may have different decimals or wildly different prices.Recommendation
Consider allowing the user to specify a list of
maxMintingFeesperlicenseTermsIdto match whatever unique currency eachlicenseTermsIdcould have. -
M-02 Medium Whitelist Can Be Opened By EIP 7702 Access Control Acknowledged
Description
Story protocol supports EIP 7702 which allows the whitelist logic to be opened up to non-whitelisted callers in the
LicenseCallerWhitelistHook.EIP-7702 allows EOAs to delegate their execution capabilities to smart contracts. For example, if Bob is an approved caller, he can delegate his EOA to a simple proxy contract that exposes the
registerDerivativeandmintLicenseTokensfunctionality to any caller:contract BobEOA { LicensingModule licensingModule; function registerDerivative(...) external { licensingModule.registerDerivative(…); } function mintLicenseTokens(...) external { licensingModule.mintLicenseTokens(…); } }The whitelist validation key is keyed off of the
ipOwner,licensorIpId,licenseTemplate,licenseTermsId, andminter(caller), thus a caller who is whitelisted for some combination of those can open up the whitelist to any caller for that same combination.Recommendation
Monitor whitelisted EOAs and consider removing them if they delegate to proxy contracts. Stay up to date with EIP-7702 developments, particularly proposals for delegate runtime introspection (discussion link)[https://ethereum-magicians.org/t/transient-eip-7702-delegate-runtime-introspection-enabling-safer-smarter-wallet-innovation/24050], which may allow safer detection and handling of such cases.
-
L-01 Low calculateMintingFee Does Not Check Whitelist Validation Resolved
Description
The documentation for the
calculateMintingFeefunction suggests thatThe hook should revert if the minting fee calculation is not allowed. However there is no whitelist validation performed, and thus thecalculateMintingFeeimplementation does not abide by this documented behavior.Recommendation
Consider if
_checkWhitelistshould be added to the beginning of thecalculateMintingFeefunction, or if the documentation should be updated. -
L-02 Low Derivatives Registry Bypassing The Whitelist Access Control Acknowledged
Description
In the
LicensingModulethere are two functions which allow for the registry of derivative child ip's, the first beingregisterDerivativeand the second beingregisterDerivativeWithLicenseTokens.registerDerivativewill check the whitelist status by invoking thebeforeRegisterDerivativefunction in it's flow. However theregisterDerivativeWithLicenseTokensdoes not invoke thebeforeRegisterDerivativefunction on the hook.As a result, the whitelist status is not verified at the time of derivative registration in this second function. An account may have been whitelisted to initially mint these
licenseTokenIds, however the whitelist status could have been revoked fro the account with theremoveFromWhitelistfunction before the derivatives are registered with those tokens using theregisterDerivativeWithLicenseTokensfunction.Furthermore, the whitelist entries are invalidated every time the owner of the
licensorIpIdchanges, the new owner may not expect anyone to be able to mint or register derivatives until they explicitly whitelist them. However as described, existinglicenseTokenIdsholders can still register derivatives.Recommendation
Consider introducing a new hook to be called at the time of
registerDerivativeWithLicenseTokensinvocation, or invoke the samebeforeRegisterDerivativehook in this secondary derivative registration function. -
L-03 Low Whitelist Does Not Prevent Transfers Validation Acknowledged
Description
In case the LicenseToken is transferable the whitelist can be bypassed in the following way:
- Whitelisted user mints tokens
- Whitelisted user sells / transfers them to not whitelisted users
Recommendation
Consider to prevent transfers of LicenseTokens if they use the whitelist hook or to add the whitelist hook to the transfer flow too.
Otherwise consider to document this behavior so that IP owners are aware.
-
L-04 Low Inaccurate predictMintingLicenseFee Result Logical Error Resolved
Description
In the
_payMintingFeeflow if themintingFeeByHookvalue is less than theroyaltyInfo.mintingFeeByLicense * amountresult then the transaction is reverted. However in thepredictMintingLicenseFeefunction there is no accounting for such a minimum, and if the hook fee result is present it is always returned with no indication of if it would be less than themintingFeeByLicenserequirement leading to a revert.Recommendation
Consider if the minimum
mintingFeeByLicense * amountshould be reflected in thepredictMintingLicenseFeein some way. Either by a revert, a sentinel failure value, or reporting back themintingFeeByLicense * amountvalue if it is greater than the hook fee. -
I-01 Informational Redundant Code Superfluous Code Resolved
Description
The same two lines of code to calculate the whitelist key are written out four times in the contract:
address ipOwner = IIPAccount(payable(licensorIpId)).owner();bytes32 key = keccak256(abi.encodePacked(ipOwner, licensorIpId, licenseTemplate, licenseTermsId, minter));Recommendation
Consider to move the code into a internal function.
-
I-02 Informational Missing Zero Address Checks Validation Resolved
Description
The constructor of the
LicenseCallerWhitelistHookdoes not perform a zero address check for the givenlicenseRegistry.There are also no zero address checks for the parameters of the
addToWhitelistfunction.Recommendation
Consider to add a zero address checks to follow best practices.
-
I-03 Informational Incorrect Whitelist Key Comment Documentation Resolved
Description
In the LicenseCallerWhitelistHook documentation for the whitelist mapping, the comment purports that the keys are based on:
keccak256(licensorIpId, licenseTemplate, licenseTermsId, minterAddress).However this is not the case, instead the key is based on five entries instead of four, like so:
keccak256(abi.encodePacked(ipOwner, licensorIpId, licenseTemplate, licenseTermsId, minter)).Recommendation
Correct the comment to reflect that the licensor
ipOwneraddress is the first item in the key. -
I-04 Informational Whitelist Behavior On Owner Change Warning Resolved
Description
If the owner of an IP changes than all whitelist entries are invalided. But if the old owner receives the IP back later on the same whitelist entries are valid again.
This behavior may be unexpected for users.
Recommendation
Be aware and consider to document this behavior.
Remediation Review
1 finding · November 25, 2025-
I-01 Informational Inconsistent NatSpec Documentation Acknowledged
Description
The
_payMintingFeedoes now return thecurrencyTokenbut does not include this return value in it's NatSpec.Recommendation
Consider to add the
currencyTokento the NatSpec.
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.
