Guardian's review of Vault Wrapper for Fun.xyz, published October 2025. The report records 12 findings, including 5 low and 7 informational.
- Published
- Review window
- October 10 to 13, 2025
- Language
- Solidity
- Sector
- Payments
- 0 Critical
- 0 High
- 0 Medium
- 5 Low
- 7 Informational
Scope
9 files in scope · 252 nSLOC
| File | nSLOC | Lines |
|---|---|---|
src/VaultWrapper.sol | 48 | 155 |
src/Utils/Chains.sol | 88 | 140 |
src/Utils/Replacer.sol | 26 | 66 |
src/Utils/VaultDepositManager.sol | 39 | 127 |
src/Utils/VaultWrapperErrors.sol | 14 | 45 |
src/Utils/VaultWrapperEvents.sol | 6 | 27 |
src/Utils/WETHHelper.sol | 15 | 43 |
src/CREATE3/CREATE3Factory.sol | 13 | 26 |
src/CREATE3/ICREATE3Factory.sol | 3 | 14 |
Findings 12
-
L-01 Low Lacking VaultABI Restriction Validation Acknowledged
Description
In the
registerVaultfunction, thevaultABIparameter is provided to represent the target function that is callable on the vault.However this
vaultABIparameter is only emitted in theVaultRegisteredevent and is not used in the logic or storage resulting from theregisterVaultfunction.When a vault is configured to be used with the
VaultWrappersystem any function on it may be invoked through theVaultWrapper, even those that are not intending to be called through the expected execution flow.Given that the
VaultWrapperis intended to work with arbitrary vault implementations, it would be prudent to limit the callable function signatures to the expected set for a vault.Recommendation
It may be expected that the
vaultABIstring is only emitted in theVaultRegisteredevent. However it would be prudent to consider storing a set of whitelistedbytes4function selectors for each vault to explicitly define the expected interaction with a supported vault. -
L-02 Low Functions May Not Be Native Compatible Warning Acknowledged
Description
The
VaultDepositManagerassigns aFLAG_ALLOWS_NATIVEflag for the entire vault, which may have multiple functions that are immediately supported when a vault is registered.However not all of these functions that are now callable on a vault may support native payment. For example, often vaults have a
depositand adepositNativetype function, where the deposit function would not be compatible with native Ether.Recommendation
Be aware of this generalization that the configuration makes. The native support could be indicated for each supported selector for a vault, however this may introduce more complexity than such a validation is worth.
-
L-03 Low Native Funds Trapped If allowFailure True Warning Acknowledged
Description
The expected flow for any vault functions that accept native payment is to include a direct Ether transfer to the
VaultWrappercontract in the multicall before the relevant deposit action.Relay protocol allows calls to be configured with a allowFailure value, where when true, if the call fails as a part of the multicall, it continues with execution of the batch.
However in the case of the
VaultWrapper’s native use-case this could result in native funds being trapped ifallowFailureis used for the invocation of theVaultWrapper.depositfunction which then fails for any reason at the Fun wrapper level or at the underlying vault level.Recommendation
In the offchain UI configuration of these actions do not allow
allowFailureto be configured as true. Additionally, consider documenting this risk for integrators. -
L-04 Low Mint Actions Do Not Refund The User Warning Acknowledged
Description
The mint function of the ERC4626 standard will not always transfer the entire input amount from the depositor, but instead the amount that is necessary to mint the specified shares.
The
VaultWrappersystem should be considered incompatible with anymintormint-likefunctions which do not take an explicit deposit input amount since they will leave input tokens in theVaultWrappercontract which are susceptible to being stolen by the next depositor.Recommendation
Be aware and clearly document that any
mintfunction or function that does not accept and use an explicit input token amount is strictly incompatible with theVaultWrappersystem. -
L-05 Low Replacer Incompatible With Dynamic Types Logical Error Acknowledged
Description
The replacer always assumes that values are left padded to fit a 32 byte increment and that their value will be interpreted using the whole 32 byte word (as is the case with static types such as
addressuintetc…).However this is often not the case with dynamic types which will not be left padded and are instead interpreted using their length entry.
For example, consider the following function calldata for a function
“deposit(bytes amount)”, where the amount is encoded in a bytes object and those bytes are expected to be a uint64 (less than a full word).bytes memory amount = hex"FF"; // Assume amount is represented by the 1 byte 0xFF for demonstration ├ Hex (Memory): ├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000001 ├─ Contents ([0x20:..]): 0xff00000000000000000000000000000000000000000000000000000000000000 ➜ abi.encodeWithSignature("deposit(bytes)", amount) Type: dynamic bytes ├ Hex (Memory): ├─ Length ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000064 ├─ Contents ([0x20:..]): 0x98b1e06a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 The calldata is: 0x98b1e06a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001ff00000000000000000000000000000000000000000000000000000000000000 As you can see the ff value is left aligned rather than right aligned. If this amount value is replaced with the placeholder, the replace function will replace the bytes assuming that the value is right aligned and uses the entire word. Which will provide an invalid amount of 0 given the length value will not reach a right aligned value.Recommendation
Be aware that the replacer is not compatible with any dynamic type encodings. These should be rare in any vault implementation and therefore are not worth supporting given the complexity. The
VaultWrappershould only be used with vaults that use a static type encoding for fields that ought to be replaced. -
I-01 Informational Unnecessary Ownership SSTORE Gas Optimization Acknowledged
Description
Upon production deployment of the
VaultWrapper, the owner is transferred twice in the same deployment transaction when the initial owner is a multisig and will not be the deployer.This is to support the logic where if the
initialOwneraddress is zero then the owner can default to the msg.sender. However a more efficient way to achieve this is to simply use a ternary case in the constructor invocation of theOwnableconstructor.Recommendation
In the constructor of the
VaultDepositManagercontract, consider removing theinitialOwner != address(0)case and instead passing the initial owner address as such to theOwnableconstructor:constructor(address initialOwner) Ownable(initialOwner == address(0) ? msg.sender : initialOwner)This way avoiding duplicating
_transferOwnershipcalls. -
I-02 Informational Missing Update Configuration Configuration Acknowledged
Description
In the
VaultDepositManagercontract the only function which exposes configuration for thevaultConfigsmapping is theregisterVaultfunction.However this function can only be called once, when the vault is initially being configured and not again until the vault has been removed.
It may however be useful to update vault configuration if for example later it is desired to support native functions with a vault.
Recommendation
Be aware that a vault will have to be removed to be reconfigured. Consider adding an update configuration function in case it is needed.
-
I-03 Informational Missing Event Data Events Acknowledged
Description
The
VaultRegisteredevent includes all of the details around the configuration that was made for the corresponding vault except theallowsNativevalue that was configured.Recommendation
Consider including the
allowsNativevalue in theVaultRegisteredevent that is emitted. -
I-04 Informational Native Functionality Can Be Incompatible Warning Acknowledged
Description
For native value deposits the
VaultWrappersystem expects that the Relay router has transferred the native funds to theVaultWrappercontract ahead of thedepositinvocation.The only compatible way to do this for e.g. a swap is if the swap uses the
VaultWrapperas the output address. Otherwise if the relay router receives the output tokens, like in the example transaction provided where USDC is swapped for USDT, then there is no way to be sure you can transfer all Ether to theVaultWrappercontract since the ether amount would have to be encoded in one of the call objects ahead of time.For some esoteric systems, it may not be possible to specify a receiver and instead the native output may be forced to go to the Relay router address thus breaking the use-case of the fun
VaultWrappercontract.Recommendation
Simply be aware of this limitation of support for Native deposits and consider it when evaluating which swap venues to use.
-
I-05 Informational Tokens Exceeding 18 Decimals Incompatible Warning Acknowledged
Description
When a vault is configured with the
FLAG_CONVERT_TO_18_DECIMALSflag, the tokens are converted to 18 decimals for the deposit calldata. However only tokens that have less than 18 decimals are converted up to 18.Tokens with larger than 18 decimals, such as YAM v2 which have 24 decimals will not be converted to 18 decimals. For any vault that requires 18 decimal representations this will break the integration.
Recommendation
Consider explicitly disallowing tokens that have larger than 18 decimals in the
FLAG_CONVERT_TO_18_DECIMALShandling. -
I-06 Informational Lacking Vault Code Existence Check Validation Acknowledged
Description
In the
registerVaultfunction there is no validation that the vault contract houses bytecode. As a result, when a deposit is executed, the.callwill return true even though no deposit was made. And the transaction will silently complete while leaving user tokens in theVaultWrappercontract.Recommendation
Consider validating that the byte code of the vault address is nonzero when registering to avoid any mistakes that can cause loss of funds.
-
I-07 Informational Fee On Transfer Tokens Unsupported Warning Acknowledged
Description
The contract compares
minAmountOuttoIERC20(token).balanceOf(msg.sender)before transfer. For fee-on-transfer or negatively rebasing tokens, the wrapper will receive fewer tokens than the sender’s reported balance, but the deposit still proceeds, undermining slippage protection. The vault may then deposit a smaller net amount than the user intended.Recommendation
Be aware that fee on transfer tokens are not supported with the
VaultWrapper.
No findings match.
More from Fun.xyz
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.
