Missing Critical Event Emissions When State Variables Change (Poor Observability + Reduced Off-chain Integration)
Description
-
Events should be emitted whenever important state variables are modified to provide transparency and enable off-chain monitoring of contract activities.
-
The contract fails to emit events for several critical state changes including contract initialization, bid clearing, listing status updates, and failed credit withdrawals, making it difficult for external systems and users to track these important activities.
In src/BidBeastsNFTMarketPlace.sol:
contract BidBeastsNFTMarket is Ownable(msg.sender) {
constructor(address _BidBeastsNFT) {
@> BBERC721 = BidBeasts(_BidBeastsNFT);
}
function listNFT(uint256 tokenId, uint256 _minPrice, uint256 _buyNowPrice) external{
if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
@> bids[tokenId] = Bid(msg.sender, salePrice);
@> listing.listed = false;
}
}
function _executeSale(uint256 tokenId) internal {
@> listing.listed = false;
@> delete bids[tokenId];
}
function withdrawAllFailedCredits(address _receiver) external {
@> failedTransferCredits[msg.sender] = 0;
}
}
Risk
Likelihood:
-
These state changes occur during normal contract operations - contract deployment, buy-now purchases, auction completions, and failed credit withdrawals.
-
Off-chain systems and dApps commonly rely on events to track contract state changes and update their interfaces accordingly.
Impact:
-
Reduced transparency and auditability of contract operations.
-
Difficulty for off-chain systems, indexers, and dApps to track important state changes.
Proof of Concept
The following code demonstrates the missing event emissions that prevent proper off-chain monitoring and integration:
contract BidBeastsNFTMarket is Ownable(msg.sender) {
constructor(address _BidBeastsNFT) {
BBERC721 = BidBeasts(_BidBeastsNFT);
}
function listNFT(uint256 tokenId, uint256 _minPrice, uint256 _buyNowPrice) external{
if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
bids[tokenId] = Bid(msg.sender, salePrice);
listing.listed = false;
}
}
function _executeSale(uint256 tokenId) internal {
listing.listed = false;
delete bids[tokenId];
}
function withdrawAllFailedCredits(address _receiver) external {
failedTransferCredits[msg.sender] = 0;
}
}
Recommended Mitigation
Add appropriate event emissions for all critical state changes to improve transparency and enable proper off-chain integration.
contract BidBeastsNFTMarket is Ownable(msg.sender) {
//existing code
+ event BidBeastsContractSet(address indexed newContract);
+ event BidCleared(uint256 indexed tokenId);
+ event FailedCreditsWithdrawn(address indexed user, uint256 amount);
constructor(address _BidBeastsNFT) {
BBERC721 = BidBeasts(_BidBeastsNFT);//Line#57
+ emit BidBeastsContractSet(_BidBeastsNFT);
}
//existing code
function listNFT(uint256 tokenId, uint256 _minPrice, uint256 _buyNowPrice) external{
//existing code
if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
//existing code
bids[tokenId] = Bid(msg.sender, salePrice);//Line#124
+ emit BidPlaced(tokenId, msg.sender, salePrice);
listing.listed = false;//Line#125
+ emit NftUnlisted(tokenId);
//existing code
}
//existing code
}
//existing code
function _executeSale(uint256 tokenId) internal {
//existing code
listing.listed = false;//Line#210
+ emit NftUnlisted(tokenId);
delete bids[tokenId];//Line#211
+ emit BidCleared(tokenId);
//existing code
}
function withdrawAllFailedCredits(address _receiver) external {
//existing code
failedTransferCredits[msg.sender] = 0;//Line#242
+ emit FailedCreditsWithdrawn(msg.sender, amount);
//existing code
}
//existing code
}