This Proof of Concept will describe how to add code to enable the feature to allow a portion of the claim fee to be given to the previous king (if a previous king exists).
The code below shows the source the new variable, for the constructor, and claimThrone function to make the new feature of paying out kings work properly, as well as an optional new event that is triggered when a payout is assigned to a previous king.
* work as described in the specification. An optional event is also added to show that the
* previous king is paid out.
*/
* @dev Initializes the game contract.
* @param _initialClaimFee The starting fee to claim the throne.
* @param _gracePeriod The initial grace period in seconds (e.g., 86400 for 24 hours).
* @param _feeIncreasePercentage The percentage increase for the claim fee (0-100).
* @param _platformFeePercentage The percentage of claim fee for the owner (0-100).
* @param _previousKingFeePercentage The percentage of claim fee to give to the previous king (0-100).
*/
constructor(
uint256 _initialClaimFee,
uint256 _gracePeriod,
uint256 _feeIncreasePercentage,
uint256 _platformFeePercentage,
uint256 _previousKingFeePercentage
) Ownable(msg.sender) {
require(_initialClaimFee > 0, "Game: Initial claim fee must be greater than zero.");
require(_gracePeriod > 0, "Game: Grace period must be greater than zero.");
require(_feeIncreasePercentage <= 100, "Game: Fee increase percentage must be 0-100.");
require(_platformFeePercentage <= 100, "Game: Platform fee percentage must be 0-100.");
require(_previousKingFeePercentage <= 100, "Game: Previous king fee percentage must be 0-100.");
initialClaimFee = _initialClaimFee;
initialGracePeriod = _gracePeriod;
feeIncreasePercentage = _feeIncreasePercentage;
platformFeePercentage = _platformFeePercentage;
previousKingFeePercentage = _previousKingFeePercentage;
claimFee = initialClaimFee;
gracePeriod = initialGracePeriod;
lastClaimTime = block.timestamp;
gameRound = 1;
gameEnded = false;
}
* OPTIONAL - Event to show that a previous king was rewarded
* @dev Emitted when there was a previous king and they get rewarded part of the claim amount.
* @param previousKing The address of the previous king to be rewarded.
* @param claimAmount The ETH amount sent by the new king.
* @param previousKingPayout The ETH amount rewarded to the previous king
* @param timestamp The block timestamp when the claim occurred.
*/
event RewardPreviousKing(
address indexed previousKing,
uint256 claimAmount,
uint256 previousKingPayout,
uint256 timestamp
);
function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
if (currentKing != address(0))
{
previousKingPayout = (sentAmount * previousKingFeePercentage) / 100;
pendingWinnings[currentKing] = pendingWinnings[currentKing] + previousKingPayout;
emit RewardPreviousKing(
currentKing,
sentAmount,
previousKingPayout,
block.timestamp
);
}
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
amountToPot = sentAmount - currentPlatformFee - previousKingPayout;
pot = pot + amountToPot;
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
claimFee = sentAmount + (sentAmount * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}
The Mitigation involves changing the code to allow a previous king to receive a portion of the claim fee from a new king.
+ uint256 public previousKingFeePercentage; // Percentage of the claimFee that the new king will pay previous king
constructor(
uint256 _initialClaimFee,
uint256 _gracePeriod,
uint256 _feeIncreasePercentage,
- uint256 _platformFeePercentage
+ uint256 _platformFeePercentage,
+ uint256 _previousKingFeePercentage
) Ownable(msg.sender) { // Set deployer as owner
require(_initialClaimFee > 0, "Game: Initial claim fee must be greater than zero.");
require(_gracePeriod > 0, "Game: Grace period must be greater than zero.");
require(_feeIncreasePercentage <= 100, "Game: Fee increase percentage must be 0-100.");
require(_platformFeePercentage <= 100, "Game: Platform fee percentage must be 0-100.");
+ require(_previousKingFeePercentage <= 100, "Game: Previous king fee percentage must be 0-100.");
initialClaimFee = _initialClaimFee;
initialGracePeriod = _gracePeriod;
feeIncreasePercentage = _feeIncreasePercentage;
platformFeePercentage = _platformFeePercentage;
+ previousKingFeePercentage = _previousKingFeePercentage;
// Initialize game state for the first round
claimFee = initialClaimFee;
gracePeriod = initialGracePeriod;
lastClaimTime = block.timestamp; // Game starts immediately upon deployment
gameRound = 1;
gameEnded = false;
// currentKing starts as address(0) until first claim
}
+ /*
+ * @dev Emitted when there was a previous king and they get rewarded part of the claim amount.
+ * @param previousKing The address of the previous king to be rewarded.
+ * @param claimAmount The ETH amount sent by the new king.
+ * @param previousKingPayout The ETH amount rewarded to the previous king
+ * @param timestamp The block timestamp when the claim occurred.
+ */
+ event RewardPreviousKing(
+ address indexed previousKing,
+ uint256 claimAmount,
+ uint256 previousKingPayout,
+ uint256 timestamp
+ );
/**
* @dev Allows a player to claim the throne by sending the required claim fee.
* If there's a previous king, a small portion of the new claim fee is sent to them.
* A portion also goes to the platform owner, and the rest adds to the pot.
*/
function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
+ //If there is a previous king we must pay them a portion of the claim (sentAmount)
+ if (currentKing != address(0))
+ {
+ // Calculate amount to pay previous king based on a previousKingFeePercentage set in the constructor
+ previousKingPayout = (sentAmount * previousKingFeePercentage) / 100; //R-Fix-Added
+
+ // Payout the previous king by adding to their winnings
+ pendingWinnings[currentKing] = pendingWinnings[currentKing] + previousKingPayout;
+ // Optional: Emit a event which is useful to see that the correct payout is paid to the previous king
+ emit RewardPreviousKing(
+ currentKing, // address of soon to be previous king
+ sentAmount, // claim amount,
+ previousKingPayout, //previous king payout
+ block.timestamp // time stamp
+ );
+ }
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
- amountToPot = sentAmount - currentPlatformFee;
+ /* Both the current platform fee and the previous king payout have been used from the sent amount.
+ * The amount that can be added to the pot is the sent amount after subtracting
+ * the platform fee and previous king payout, which have been assigned to others.
+ */
+ amountToPot = sentAmount - currentPlatformFee - previousKingPayout;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
- claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
+ /*
+ * sentAmount is the true current current claim amount and should be used to calculate
+ * the next claim fee minimum amount that must be paid when claiming the throne
+ */
+
+ claimFee = sentAmount + (sentAmount * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}
- remove this code
+ add this code