Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Asset Token Tracking Vulnerability in Estate NFT Buyout System

Finding description and impact

The InheritanceManager contract contains a vulnerability in how it tracks the payment asset for estate NFTs. The core issue stems from using a single state variable assetToPay instead of mapping payment assets to individual NFT IDs. This creates a scenario where only the most recently set payment asset is used for all NFT buyouts, regardless of which asset was specified when each NFT was created.

The impact is that:

  • Payment assets for previously created NFTs are lost and overwritten

  • All NFT buyouts use the most recently set asset, not the asset originally intended for each NFT

  • Beneficiaries may be unable to buy out older NFTs if they don't have the newly required asset

  • This creates confusion and potential financial loss if token values differ significantly

Proof of Concept

The following code segments demonstrate the vulnerability:

// State variable for asset to pay
address assetToPay; // Not tracked per NFT
function createEstateNFT(string memory _description, uint256 _value, address _asset) external onlyOwner {
uint256 nftID = nft.createEstate(_description);
nftValue[nftID] = _value;
assetToPay = _asset; // Overwrites the previous asset for all NFTs
}
function buyOutEstateNFT(uint256 _nftID) external onlyBeneficiaryWithIsInherited {
uint256 value = nftValue[_nftID];
uint256 divisor = beneficiaries.length;
uint256 multiplier = beneficiaries.length - 1;
uint256 finalAmount = (value / divisor) * multiplier;
IERC20(assetToPay).safeTransferFrom(msg.sender, address(this), finalAmount);
// ... rest of function
}

Consider this scenario:

  1. Owner creates NFT #1 for a property valued at 100,000 USDC with USDC as the payment asset

  2. Later, owner creates NFT #2 for a property valued at 50 WETH with WETH as the payment asset

  3. The assetToPay is now set to WETH, overwriting the previous USDC setting

  4. When a beneficiary tries to buy out NFT #1, they must pay in WETH instead of the originally intended USDC

  5. This mismatch could make the buyout impossible or financially unfair if the beneficiary doesn't have WETH

Recommended mitigation steps

To fix this issue, implement a mapping to track payment assets per NFT ID:

// Change single address to mapping
mapping(uint256 nftID => address asset) public nftPaymentAsset;
function createEstateNFT(string memory _description, uint256 _value, address _asset) external onlyOwner {
uint256 nftID = nft.createEstate(_description);
nftValue[nftID] = _value;
nftPaymentAsset[nftID] = _asset; // Track asset per NFT
}
function buyOutEstateNFT(uint256 _nftID) external onlyBeneficiaryWithIsInherited {
uint256 value = nftValue[_nftID];
uint256 divisor = beneficiaries.length;
uint256 multiplier = beneficiaries.length - 1;
uint256 finalAmount = (value / divisor) * multiplier;
address paymentAsset = nftPaymentAsset[_nftID]; // Get correct asset for this NFT
IERC20(paymentAsset).safeTransferFrom(msg.sender, address(this), finalAmount);
// ... rest of function
}

Tools Used

  • Manual Code Review

  • Solidity Static Analysis

Updates

Lead Judging Commences

0xtimefliez Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

global asset in NFT values

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.