Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

BEATLAND FESTIVAL BeatToken.sol CONTRACT AUDIT VULNERABILITY FINDINGS

There is no emission of events for important actions , therefore making it harder to trace and audit.

Description

  • Emmission of events is one of the best practices in solidity programming ,especially in the recording of crucial activities and actions on-chain. This is very essential for operations which change the state of the blockchain ,and the data is used by off-chain listeners such as Decentralised Apps (dApps) , smart contract auditors , businesses , UI/UX dashboards e.t.c

  • In BeatToken.sol cobntract , there are various functions which change/modify the state and also token balances but they do not make any emmision of events which would result in a change of state on the blockchain.Some of these functions include mint ,setFestivalContract and burnFrom functions.Due to this ,it becomes quite hard to verify /authorize operations and to trace any changes made externally resulting in a more challenging audit process.

//SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract BeatToken is ERC20, Ownable2Step {
address public festivalContract;
constructor() ERC20("BeatDrop Token", "BEAT") Ownable(msg.sender){
}
function setFestivalContract(address _festival) external onlyOwner {
require(festivalContract == address(0), "Festival contract already set");
festivalContract = _festival;
//@no event emmisions making it hard to trace state changes and audit
}
function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
_mint(to, amount);
//@no event emmisions making it hard to trace state changes and audit
}
function burnFrom(address from, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Burn");
_burn(from, amount);
//@no event emmisions making it hard to trace state changes and audit
}
}
//For the above highlighted actions ,no event is emmitted

Risk

Likelihood:

  • The likelihood of this will occur every time when the festivalContract is set or also when the tokens are minted and/or burned ,therefore making it a high likelihood since many people may attend the festival and more tokens may need to be generated / minted.

  • Another likelihood is when off-chain systems connected to the festival such as dApps ,which may be promoting the festival through online sale of tickets or various exploreres and other tools of analysis may not necessarily pick up on the state changes made and this leads to a lack of transparency and traceability that may lead people to question the authenticity of the festival as well as provide a leeway for fraudulent activity

Impact:

  • The non emmission of events for important events does not relay important changes made to state ,thus making it harder to trace and audit for various authorities as well as smart contract auditors .

  • The hardness of tracing and auditing the state changes mayy lead to a lack of transparency that may put the funds associated with the function at risk of theft , fraud and /or manipultion and such malicious activity might go unnoticed by various bodies such as monitoring systems .

Proof of Concept

//SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract BeatToken is ERC20, Ownable2Step {
address public festivalContract;
constructor() ERC20("BeatDrop Token", "BEAT") Ownable(msg.sender){
}
function setFestivalContract(address _festival) external onlyOwner {
require(festivalContract == address(0), "Festival contract already set"); //@audit cannot be reused for other festivals
festivalContract = _festival;
// owner sets a festivalContract
}
function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
_mint(to, amount);
// festivalContract mints tokens
}
function burnFrom(address from, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Burn");
_burn(from, amount);
//festivalContract burns tokens
}
}
//For the above highlighted actions ,no event is emmitted ,so no change is made to state
//Makes it quite hard for various off chain applications connected to the festival such as dApps to detect freshly minted or burnt tokens
//In the event of fraudulent / malicious activity , it makes it hard to prove accountabilitty

Recommended Mitigation

- //SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract BeatToken is ERC20, Ownable2Step {
address public festivalContract;
constructor() ERC20("BeatDrop Token", "BEAT") Ownable(msg.sender){
}
function setFestivalContract(address _festival) external onlyOwner {
require(festivalContract == address(0), "Festival contract already set"); //@audit cannot be reused for other festivals
festivalContract = _festival;
}
function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
_mint(to, amount);
}
function burnFrom(address from, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Burn");
_burn(from, amount);
changes and audit
}
}
//The above code does not contain any event emmissions
+// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract BeatToken is ERC20, Ownable2Step {
address public festivalContract;
// Event declarations
event FestivalContractSet(address indexed newFestivalContract);
event TokensMinted(address indexed to, uint256 amount);
event TokensBurned(address indexed from, uint256 amount);
constructor() ERC20("BeatDrop Token", "BEAT") Ownable(msg.sender) {}
function setFestivalContract(address _festival) external onlyOwner {
require(festivalContract == address(0), "Festival contract already set"); // 🔐 One-time lock-in
festivalContract = _festival;
// Emit event
emit FestivalContractSet(_festival);
}
function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
_mint(to, amount);
// Emit event
emit TokensMinted(to, amount);
}
function burnFrom(address from, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Burn");
_burn(from, amount);
// Emit event
emit TokensBurned(from, amount);
}
}
//The above code contains event emmisions
Updates

Lead Judging Commences

inallhonesty Lead Judge 27 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Missing events / Events not properly configured

Informational. This protocol doesn't rely on events to function, they are just nice to have, but not mandatory.

Support

FAQs

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