Beginner FriendlyFoundryGameFi
100 EXP
View results
Submission Details
Severity: medium
Valid

`MartenitsaEvent` inherits `MartenitsaToken` instead of using the instance of the dedicated `MartenitsaToken` contract, leads to user not being actual producers

Summary

The MartenitsaEvent contract allows users to become producers during the special event by redeeming their 1 HealthToken.
But the MartenitsaEvent contract inherits the MartenitsaToken contract instead of using the instance of the dedicated MartenitsaToken contract which results into MartenitsaEvent contract having its own producers information that will be isolated from the actual MartenitsaToken contract and as a result of which user is actually not a producer even though they participate in the event and will not be able to participate in the marketplace.

The MartenitsaToken is a dedicated contract which stores all the information regarding producers and allows them to produce token and list it on marketplace, and marketplace contract allows only this martenitsa token contract to allow the listing, and as MartenitsaEvent contract has their own token, which will have no relation with that dedicated token contract.

Vulnerability Details

The vulnerability is present in the MartenitsaEvent contract where it inherits the MartenitsaToken contract and as a result of which all the producers related information and producers making Martenitsa will be associated with the MartenitsaEvent contract.

The MartenitsaToken contract is a dedicated token contract for Martenitsa and Marketplace contract utilizes the dedicated MartenitsaToken contract for facilitating the Martenitsa transfers.

But, MartenitsaEvent by inheriting MartenitsaToken acts as a Token contract and all the producers being added by participating in the event and making Martenitsa only happens inside of MartenitsaEvent contract and is thus independent of the dedicated MartenitsaToken contract.

function joinEvent() external {
require(block.timestamp < eventEndTime, "Event has ended");
require(!_participants[msg.sender], "You have already joined the event");
require(!isProducer[msg.sender], "Producers are not allowed to participate");
require(_healthToken.balanceOf(msg.sender) >= healthTokenRequirement, "Insufficient HealthToken balance");
_participants[msg.sender] = true;
participants.push(msg.sender);
emit ParticipantJoined(msg.sender);
(bool success) = _healthToken.transferFrom(msg.sender, address(this), healthTokenRequirement);
require(success, "The transfer is not successful");
@> _addProducer(msg.sender);
}

Here, isProducer is a mapping which is a separate variable of MartenitsaEvent contract which it is inherited by inheriting the MartenitsaToken contract and is actually not the variable associated with MartenitsaToken contract.

Therefore, all the users participating in the event are thus producers inside the MartenitsaEvent contract and not in actual MartenitsaToken contract and thus they are not actually producers as they are not associated with the dedicated MartenitsaToken contract.

function _addProducer(address _producer) internal {
@> isProducer[_producer] = true;
@> producers.push(_producer);
}

Impact

  • The users become producers on MartenitsaEvent contract and not in the actual MartenitsaToken contract.

  • The Martenitsa they produce is of no significance as it is in no way related to the actual MartenitsaToken contract and thus it can't be listed on MartenitsaMarketplace as it allows listings only from the actual MartenitsaToken contract.

PoC

Add the test in the file: test/MartenitsaEvent.t.sol

Run the test:

forge test --mt test_UsersParticipatingInEvents_DoesNotActuallyBecomeProducers_AndCantList
function test_UsersParticipatingInEvents_DoesNotActuallyBecomeProducers_AndCantList() public {
// initially a non-producer user
address kit = makeAddr("kit");
// giving health tokens to kit
uint256 healthTokenAmount = martenitsaEvent.healthTokenRequirement();
deal(address(healthToken), kit, healthTokenAmount);
// owner starts the Martenitsa Event
martenitsaEvent.startEvent(1 days);
// kit joins the event
// approves the health token to voting contract
vm.startPrank(kit);
healthToken.approve(address(martenitsaEvent), healthTokenAmount);
martenitsaEvent.joinEvent();
vm.stopPrank();
// it is expected that the user becomes producer in the `MartenitsaToken` contract
// but due the inheritance of `MartenitsaToken` in `MartenitsaEvent` contract
// the user will only be able to produce tokens in this contract and not in the actual `MartenitsaToken` contract
// and thus will not be able to list the token in the marketplace
// kit is a producer in the event contract which is insignificant and is not actually a producer on the `MartenitsaToken` contract
assert(martenitsaEvent.isProducer(kit) == true);
assert(martenitsaToken.isProducer(kit) == false);
}

Tools Used

Manual Review, Foundry Unit Test

Recommendations

Instead of inheriting the MartenitsaToken inside MartenitsaEvent contract, use the instance of the dedicated MartenitsaToken contract.
Perform changes as below:

  • MartenitsaEvent.sol

diff --git a/src/MartenitsaEvent.sol b/src/MartenitsaEvent.sol
index c9c0cdb..689e157 100644
--- a/src/MartenitsaEvent.sol
+++ b/src/MartenitsaEvent.sol
@@ -3,8 +3,9 @@ pragma solidity ^0.8.21;
import {HealthToken} from "./HealthToken.sol";
import {MartenitsaToken} from "./MartenitsaToken.sol";
+import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
-contract MartenitsaEvent is MartenitsaToken {
+contract MartenitsaEvent is Ownable {
HealthToken private _healthToken;
@@ -13,13 +14,14 @@ contract MartenitsaEvent is MartenitsaToken {
uint256 public eventEndTime;
uint256 public healthTokenRequirement = 10 ** 18;
address[] public participants;
+ MartenitsaToken public martenitsaToken;
mapping(address => bool) private _participants;
event EventStarted(uint256 indexed startTime, uint256 indexed eventEndTime);
event ParticipantJoined(address indexed participant);
- constructor(address healthToken) onlyOwner {
+ constructor(address healthToken) Ownable(msg.sender) {
_healthToken = HealthToken(healthToken);
}
@@ -34,6 +36,10 @@ contract MartenitsaEvent is MartenitsaToken {
emit EventStarted(eventStartTime, eventEndTime);
}
+ function setMartenitsaToken(address _mt) external onlyOwner {
+ martenitsaToken = MartenitsaToken(_mt);
+ }
+
/**
* @notice Function to join to event. Each participant is a producer during the event.
* @notice The event should be active and the caller should not be joined already.
@@ -42,7 +48,7 @@ contract MartenitsaEvent is MartenitsaToken {
function joinEvent() external {
require(block.timestamp < eventEndTime, "Event has ended");
require(!_participants[msg.sender], "You have already joined the event");
- require(!isProducer[msg.sender], "Producers are not allowed to participate");
+ require(!martenitsaToken.isProducer(msg.sender), "Producers are not allowed to participate");
require(_healthToken.balanceOf(msg.sender) >= healthTokenRequirement, "Insufficient HealthToken balance");
_participants[msg.sender] = true;
@@ -60,7 +66,7 @@ contract MartenitsaEvent is MartenitsaToken {
function stopEvent() external onlyOwner {
require(block.timestamp >= eventEndTime, "Event is not ended");
for (uint256 i = 0; i < participants.length; i++) {
- isProducer[participants[i]] = false;
+ _participants[participants[i]] = false;
}
}
@@ -71,12 +77,16 @@ contract MartenitsaEvent is MartenitsaToken {
return _participants[participant];
}
+ function isProducer(address _producer) external view returns (bool) {
+ return _participants[_producer];
+ }
+
/**
* @notice Function to add a new producer.
* @param _producer The address of the new producer.
*/
function _addProducer(address _producer) internal {
- isProducer[_producer] = true;
- producers.push(_producer);
+ _participants[_producer] = true;
+ participants.push(_producer);
}
}
  • MartenitsaToken.sol

diff --git a/src/MartenitsaToken.sol b/src/MartenitsaToken.sol
index 040ce8a..17b5032 100644
--- a/src/MartenitsaToken.sol
+++ b/src/MartenitsaToken.sol
@@ -3,6 +3,7 @@ pragma solidity ^0.8.21;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
+import "./MartenitsaEvent.sol";
contract MartenitsaToken is ERC721, Ownable {
@@ -10,8 +11,9 @@ contract MartenitsaToken is ERC721, Ownable {
address[] public producers;
mapping(address => uint256) public countMartenitsaTokensOwner;
- mapping(address => bool) public isProducer;
+ mapping(address => bool) public _isProducer;
mapping(uint256 => string) public tokenDesigns;
+ MartenitsaEvent public martenitsaEvent;
event Created(address indexed owner, uint256 indexed tokenId, string indexed design);
@@ -23,7 +25,7 @@ contract MartenitsaToken is ERC721, Ownable {
*/
function setProducers(address[] memory _producersList) public onlyOwner{
for (uint256 i = 0; i < _producersList.length; i++) {
- isProducer[_producersList[i]] = true;
+ _isProducer[_producersList[i]] = true;
producers.push(_producersList[i]);
}
}
@@ -33,7 +35,7 @@ contract MartenitsaToken is ERC721, Ownable {
* @param design The type (bracelet, necklace, Pizho and Penda and other) of martenitsa.
*/
function createMartenitsa(string memory design) external {
- require(isProducer[msg.sender], "You are not a producer!");
+ require(isProducer(msg.sender), "You are not a producer!");
require(bytes(design).length > 0, "Design cannot be empty");
uint256 tokenId = _nextTokenId++;
@@ -45,6 +47,14 @@ contract MartenitsaToken is ERC721, Ownable {
_safeMint(msg.sender, tokenId);
}
+ function setMartenitsaEvent(address _martenitsaEvent) public onlyOwner {
+ martenitsaEvent = MartenitsaEvent(_martenitsaEvent);
+ }
+
+ function isProducer(address _producer) public view returns (bool) {
+ return _isProducer[_producer] || martenitsaEvent.getParticipant(_producer);
+ }
+
/**
* @notice Function to get the design of a martenitsa.
* @param tokenId The Id of the MartenitsaToken.
Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

MartenitsaToken and MartenitsaEvent have different addresses

Support

FAQs

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