Omniscia Evergon Labs Audit
FractionFactory Manual Review Findings
FractionFactory Manual Review Findings
FFY-01M: Manual Configuration of Name & Symbol
| Type | Severity | Location |
|---|---|---|
| Input Sanitization | ![]() | FractionFactory.sol:L152, L153 |
Description:
The FractionFactory::deploy function accepts dynamic name_ and symbol_ arguments which may overlap with existingly deployed fraction instances.
Impact:
The name_ and symbol_ configurability of the FractionFactory::deploy function permits poisoning and similar social engineering attacks to be executed.
Example:
149/// @inheritdoc IFractionFactory150function deploy(151 uint32[] calldata chainIds,152 string memory name_,153 string memory symbol_,154 uint256 nftId_,155 uint256 amountToBeMinted_,156 address payable refundAddress157) external payable onlyOwner returns (address dm, bytes32 datapoint) {158 // Allocate DataPoiint159 DataPoint dp = IDataPointRegistry(_registry).allocate(address(this));160 datapoint = DataPoint.unwrap(dp);161
162 // Deploy & approve local DM163 bytes32 salt = keccak256(abi.encodePacked(_msgSender(), nftId_));164 dm = computeDeployAddress(_msgSender(), nftId_);165
166 IAccessManagerOmnichain(_dataIndex).approveDataManager(dp, dm, true);167 _deployOnLocalChain(datapoint, salt, name_, symbol_, nftId_, amountToBeMinted_, true);168
169 // Prepare deployment data for secondary chains170 bytes memory message = _buildLzSendMessage(datapoint, salt, name_, symbol_, nftId_, amountToBeMinted_);171
172 (uint256[] memory deployFees, uint256 totalNativeFee) = _estimateAndValidateFeesAndApproveDms(chainIds, dp, dm, message, refundAddress);173
174 _deployOnOtherChains(chainIds, message, refundAddress, deployFees);175
176 // Transfer DataPoint ownership to the caller177 IDataPointRegistry(_registry).transferOwnership(dp, msg.sender);178
179 // Refund180 if (msg.value > totalNativeFee) {181 unchecked {182 refundAddress.sendValue(msg.value - totalNativeFee);183 }184 }185}Recommendation:
We advise the name_ and symbol_ arguments to be replaced by automatically generated ones based on the wrapper's own metadata that is being fractionalized, ensuring that they cannot be spoofed as being of a different NFT ID than they actually are.
Alleviation (c6b23c23d8bcd8cce85049ad959cbd711a37126b):
A manual name_ and symbol_ can no longer be specified during the deployment of a FractionFactory::deploy invocation, ensuring that poisoning and spoofing attacks cannot be executed via this avenue.
FFY-02M: Improper Refund Addresses
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | FractionFactory.sol:L156, L172, L174, L182 |
Description:
The FractionFactory::deploy function will assume that the refundAddress specified is equivalent across chains which is incorrect as many L2s employ address masking, especially in the context of smart contract refund recipients (i.e. multi-signature wallets).
Impact:
A cross-chain message's surplus or failure may result in unspent native funds being erroneously sent to an incorrect / inaccessible refund recipient, resulting in minor fund loss.
Example:
149/// @inheritdoc IFractionFactory150function deploy(151 uint32[] calldata chainIds,152 string memory name_,153 string memory symbol_,154 uint256 nftId_,155 uint256 amountToBeMinted_,156 address payable refundAddress157) external payable onlyOwner returns (address dm, bytes32 datapoint) {158 // Allocate DataPoiint159 DataPoint dp = IDataPointRegistry(_registry).allocate(address(this));160 datapoint = DataPoint.unwrap(dp);161
162 // Deploy & approve local DM163 bytes32 salt = keccak256(abi.encodePacked(_msgSender(), nftId_));164 dm = computeDeployAddress(_msgSender(), nftId_);165
166 IAccessManagerOmnichain(_dataIndex).approveDataManager(dp, dm, true);167 _deployOnLocalChain(datapoint, salt, name_, symbol_, nftId_, amountToBeMinted_, true);168
169 // Prepare deployment data for secondary chains170 bytes memory message = _buildLzSendMessage(datapoint, salt, name_, symbol_, nftId_, amountToBeMinted_);171
172 (uint256[] memory deployFees, uint256 totalNativeFee) = _estimateAndValidateFeesAndApproveDms(chainIds, dp, dm, message, refundAddress);173
174 _deployOnOtherChains(chainIds, message, refundAddress, deployFees);175
176 // Transfer DataPoint ownership to the caller177 IDataPointRegistry(_registry).transferOwnership(dp, msg.sender);178
179 // Refund180 if (msg.value > totalNativeFee) {181 unchecked {182 refundAddress.sendValue(msg.value - totalNativeFee);183 }184 }185}Recommendation:
We advise the system to permit a different refund address to be specified per chain and to utilize the caller address as the intended refund recipient for any locally-unspent funds, ensuring that message refund operations are executed correctly.
Alleviation (c6b23c23d8bcd8cce85049ad959cbd711a37126b):
After discussions with the Evergon Labs team, we concluded that this particular refundAddress does not require to be updated as it is meant to represent a local refund address rather than a refund address at an L2 level.
As such, we consider this exhibit to be nullified.

