You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Introduces new EscalationGame contracts and TypeScript test utilities implementing an attrition-based escalation mechanism, but contains critical logic errors in deposit excess calculations, improper tie-handling in market resolution, and a hardcoded CREATE2 salt preventing multiple deployments.
⚠️ 6 issues found across 6 files
#1 Incorrect deposit excess calculation and condition ordering in claimDepositForWinning
The function claimDepositForWinning contains two logic errors in handling deposits that straddle the binding capital threshold:
Line 181 uses if (deposit.cumulativeAmount > maxWithdrawableBalance) which incorrectly captures deposits where only the portion exceeds the threshold (straddling deposits), treating them as fully excess deposits. This prevents such deposits from receiving the proportional rewards calculated in the else-if branch.
Line 184 checks deposit.cumulativeAmount + deposit.amount > maxWithdrawableBalance but cumulativeAmount already includes deposit.amount, causing double counting. Line 185 then calculates excess = deposit.cumulativeAmount + deposit.amount - maxWithdrawableBalance which results in an incorrect excess value (larger than the actual excess by exactly deposit.amount).
In the straddling case, if the calculated excess exceeds deposit.amount (which occurs due to the double counting), the subtraction deposit.amount - excess on line 187 will underflow. While Solidity 0.8.x will revert on underflow, this renders the straddling logic completely broken.
The correct logic should check if the previous cumulative (cumulativeAmount - deposit.amount) was already above the max to identify fully excess deposits, then check for straddling, and calculate excess as deposit.cumulativeAmount - maxWithdrawableBalance. Tags: bug, security Affected code:
The getMarketResolution function uses a series of comparative checks (lines 127-129) that do not properly handle tie scenarios between outcome balances. If two or more outcomes have equal highest balances (e.g., balances[1] == balances[2] > balances[0]), the function arbitrarily returns the outcome with the higher index (No in this example) rather than correctly identifying the tie and returning Outcome.None.
This violates the intended game logic where ties should result in an undecided/None state, potentially causing incorrect market resolution and unfair distribution of stakes. The TODO comment on line 126 acknowledges this issue but it represents a critical flaw in the dispute resolution mechanism. Tags: bug, logic Affected code:
119: function getMarketResolution() public view returns (YesNoMarkets.Outcome outcome){
120: uint256 currentTotalCost = totalCost();
121: uint8 invalidOver = balances[0] >= currentTotalCost ? 1 : 0;
122: uint8 yesOver = balances[1] >= currentTotalCost ? 1 : 0;
123: uint8 noOver = balances[2] >= currentTotalCost ? 1 : 0;
124: if (invalidOver + yesOver + noOver >= 2) return YesNoMarkets.Outcome.None; // if two or more outcomes are over the total cost, the game is still going
125: // the game has ended due to timeout
126: // TODO, doesn't handle ties well logically. We could avoid it by checking if tie is happening before deposit and break it there
127: if (balances[0] > balances[1] && balances[0] > balances[2]) return YesNoMarkets.Outcome.Invalid;
128: if (balances[1] > balances[0] && balances[1] > balances[2]) return YesNoMarkets.Outcome.Yes;
129: return YesNoMarkets.Outcome.No;
130: }
The CREATE2 salt is hardcoded to bytes32(0). Since the deployment address is deterministic based on salt and init code (which includes msg.sender via the constructor argument), any subsequent call from the same msg.sender will attempt to deploy to the same address, causing the transaction to revert if a contract already exists there. This prevents creating multiple games over time or replacing a destroyed game. The salt should be unique per deployment (e.g., derived from a factory nonce or passed as a parameter). Tags: bug, architecture Affected code:
10: EscalationGame game = new EscalationGame{ salt: bytes32(uint256(0x0)) }(securityPool);
The code imports assert from node:test, but Node.js's test runner module does not export an assert object. The assertion library is provided by the separate node:assert module. This will cause a runtime error or undefined behavior when the test attempts to use assertion methods. Tags: bug, compatibility Affected code:
The assertion message 'game was started' describes a positive state that should be true, but when the assertion fails (startingTime is 0n), the error message implies the game was successfully started when it was not. This creates confusion during debugging. The message should describe the expectation, such as 'starting time should not be zero' or 'game should be started'. Tags: language, maintainability Affected code:
35: assert.strictEqual(startingTime!==0n,true,'game was started')
Proposed change:
assert.notStrictEqual(startingTime,0n,'starting time should not be zero')
#6 Incorrect salt encoding for CREATE2 address calculation
The deployEscalationGame function uses numberToBytes(0) to generate the salt for getCreate2Address. By default, numberToBytes returns the minimum byte size required to represent the number (1 byte for 0), whereas CREATE2 salts must be 32 bytes (bytes32). This causes an address mismatch between the calculated address and the actual deployed contract address, as the factory likely uses a 32-byte salt. Tags: bug, security Affected code:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.