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
This substantial protocol refactoring introduces critical security vulnerabilities including ETH drainage and double-spending vectors, severe logic errors such as operator precedence bugs and division-by-zero risks, and multiple syntax issues across the Solidity and TypeScript codebase.
The FinalizedAuction event is defined at line 15 but is never emitted in the finalizeAuction function. This prevents off-chain services from tracking auction finalization and accessing the final auction state (recipient, total rep purchased, eth amount). The event should be emitted after the successful ETH transfer with the appropriate parameters. Tags: bug, architecture Affected code:
The function requestPriceIfNeededAndQueueOperation refunds excess ETH using address(this).balance, which transfers the entire contract balance to the caller instead of only the specific excess amount sent by the caller in the current transaction. This allows malicious or accidental drainage of all ETH held by the contract, including funds from previous transactions or direct transfers. The refund should calculate the exact excess amount: msg.value if requestPrice was not called, or msg.value - getRequestPriceEthCost() if requestPrice was invoked. Tags: security, bug Affected code:
128: // send rest of the eth back
129: (bool sent, ) = payable(msg.sender).call{ value: address(this).balance }('');
130: require(sent, 'Failed to return eth');
Proposed change:
// send rest of the eth back
uint256 refundAmount = msg.value;
if (!isPriceValid() && queuedPendingOperationId == previousQueuedOperationId) {
refundAmount = msg.value - getRequestPriceEthCost();
}
(bool sent, ) = payable(msg.sender).call{ value: refundAmount }('');
require(sent, 'Failed to return eth');
#3 lockedRepInEscalationGame not decremented on withdrawal
In the withdrawFromEscalationGame function, when withdrawing deposits from the escalation game, the contract increases the depositor's poolOwnership but never decreases their lockedRepInEscalationGame. This causes the lockedRepInEscalationGame to accumulate indefinitely, leading to an underflow in redeemRep when calculating repAmount (poolOwnershipToRep minus lockedRepInEscalationGame), permanently bricking withdrawals for affected vaults. Tags: bug, security Affected code:
321: for (uint256 index = 0; index < depositIndexes.length; index++) {
322: (address depositor, uint256 amountToWithdraw) = escalationGame.withdrawDeposit(depositIndexes[index]);
323: securityVaults[depositor].poolOwnership += repToPoolOwnership(amountToWithdraw);
324: }
325: }
Proposed change:
for (uint256 index = 0; index < depositIndexes.length; index++) {
(address depositor, uint256 amountToWithdraw) = escalationGame.withdrawDeposit(depositIndexes[index]);
securityVaults[depositor].poolOwnership += repToPoolOwnership(amountToWithdraw);
securityVaults[depositor].lockedRepInEscalationGame -= amountToWithdraw;
}
#4 Unrestricted ETH reception allows fund contamination
The receive() function accepts ETH from any address without validating the sender. This violates the intended security model where only specific contracts (e.g., the truth auction or escalation game) should be able to send ETH to the contract (as noted in the TODO comment). This allows malicious actors to send ETH that could be mistaken for legitimate proceeds, potentially breaking accounting invariants between address(this).balance, completeSetCollateralAmount, and totalFeesOwedToVaults. Tags: security, bug Affected code:
385: receive() external payable {
386: // needed for Truth Auction to send ETH back
387: // TODO, add check that its truth auction sending
388: }
Proposed change:
receive() external payable {
require(msg.sender == address(escalationGame) || msg.sender == securityPoolForker, 'Unauthorized sender');
// needed for Truth Auction to send ETH back
}
#5 Inappropriate function naming suggesting malicious behavior
The function stealAllRep and the error message 'Failed to steal ETH' in migrateEth use terminology suggesting theft and malicious intent. Even if these are intended for emergency migration or admin functions, such naming is inappropriate for production code, creates confusion for auditors and users, and suggests potential backdoors. Tags: language, security, naming Affected code:
The onlyForker modifier grants a single address (securityPoolForker) unrestricted power to modify all critical state variables including systemState, totalSecurityBondAllowance, vault ownership, and complete access to all funds via stealAllRep and migrateEth. This represents a critical centralization risk and potential single point of failure/compromise. Tags: security, architecture Affected code:
Line 177 contains a critical operator precedence error. The expression forkData[parent].repAtFork - forkData[securityPool].migratedRep / SecurityPoolUtils.MAX_AUCTION_VAULT_HAIRCUT_DIVISOR performs division before subtraction due to Solidity operator precedence rules. This results in calculating repAtFork - (migratedRep / divisor) instead of the intended (repAtFork - migratedRep) / divisor or repAtFork - ((repAtFork - migratedRep) / divisor). This causes the contract to attempt to auction significantly more REP than intended (nearly the entire repAtFork amount minus a tiny fraction, rather than the non-migrated remainder minus a small buffer). Since migrated REP has already been transferred to child pools at this stage, this will likely cause the transaction to revert due to insufficient balance or sell incorrect amounts. Tags: bug, security Affected code:
Line 192 performs a division (repAvailable - repPurchased) without checking if repPurchased equals repAvailable. When the auction purchases all available REP (i.e., totalRepPurchased() == repAtFork), this results in a division by zero, causing the transaction to revert and permanently preventing the auction from being finalized. The condition if (repAvailable > 0) does not protect against this case. Tags: bug, security Affected code:
The function getMarketOutcome at line 230 reads from state but does not modify it. It should be declared as external view instead of external to allow off-chain calls without broadcasting a transaction and to save gas. Tags: bug, performance Affected code:
231: function getMarketOutcome(ISecurityPool securityPool) external returns (YesNoMarkets.Outcome outcome){
Proposed change:
function getMarketOutcome(ISecurityPool securityPool) external view returns (YesNoMarkets.Outcome outcome) {
#10 Incomplete and incorrect outcomes array initialization
The getForkingData function returns a string[4] array intended to represent market outcomes, but only initializes indices 0 and 1 with 'Yes' and 'No' respectively, leaving indices 2 and 3 as empty strings (default values). Furthermore, the Outcome enum defines 4 values (Invalid=0, Yes=1, No=2, None=3), suggesting the array should map each enum value to its corresponding label. Currently, the initialization appears misaligned (setting 'Yes' at index 0 instead of 1, 'No' at index 1 instead of 2) and incomplete (missing 'Invalid' and 'None'). Tags: bug, maintainability Affected code:
The comment on line 64 is unclear and grammatically incorrect: 'sharetoken has different salt as sharetoken address does not change in forks'. It should clarify what the ShareToken's salt is different from (presumably the SecurityPool's salt) and use proper grammar. Suggested revision: 'The ShareToken uses a different salt because its address must remain consistent across forks'. Additionally, 'sharetoken' should be capitalized as 'ShareToken' to match the contract name. Tags: language, readability Affected code:
64: // sharetoken has different salt as sharetoken address does not change in forks
Proposed change:
// The ShareToken uses a different salt because its address does not change in forks
The function stealAllRep uses terminology ('steal') that implies unauthorized, malicious, or illegal action. In a production smart contract protocol, function names must clearly describe legitimate protocol operations (e.g., migrateAllRep, transferAllRep, or seizeAllRep if punitive). The current naming is semantically incorrect, unprofessional, and potentially indicates placeholder code or security concerns. It should be renamed to align with the existing migrateEth function and proper naming conventions. Tags: language, naming, maintainability Affected code:
96: function stealAllRep() external;
Proposed change:
function migrateAllRep() external;
#13 Inconsistent parameter naming convention in setter functions
The interface uses inconsistent naming conventions for function parameters. Functions like setRetentionRate (line 82) and setVaultFeeIndex (line 89) consistently use the 'new' prefix (e.g., newRetention, newFeeIndex) when the parameter name matches a state variable name. However, setVaultOwnership (line 83) and setVaultSecurityBondAllowance (line 85) use underscore prefixes (_poolOwnership, _securityBondAllowance), and setVaultPoolOwnership (line 88) uses no prefix at all (poolOwnership). This inconsistency reduces code readability and maintainability. All setter parameters should follow the same 'new' prefix convention used elsewhere in the interface. Tags: naming, code-style, maintainability Affected code:
83: function setVaultOwnership(address vault, uint256 _poolOwnership, uint256 _securityBondAllowance) external;
Proposed change:
function setVaultOwnership(address vault, uint256 newPoolOwnership, uint256 newSecurityBondAllowance) external;
Affected code:
85: function setVaultSecurityBondAllowance(address vault, uint256 _securityBondAllowance) external;
Proposed change:
function setVaultSecurityBondAllowance(address vault, uint256 newSecurityBondAllowance) external;
Affected code:
88: function setVaultPoolOwnership(address vault, uint256 poolOwnership) external;
Proposed change:
function setVaultPoolOwnership(address vault, uint256 newPoolOwnership) external;
#14 Syntax error: Extra closing parenthesis in require statements
Multiple require statements contain extra closing parentheses that will cause compilation to fail. Line 36 has the extra parenthesis before the comma separating the condition and error message. Lines 62, 86, 129, 186, 222, 237, 238, 255, 269, and 270 have extra parentheses at the end of the statement. Tags: bug Affected code:
36: require(account != address(0), "ERC1155: balance query for the zero address");
Proposed change:
require(account != address(0), "ERC1155: balance query for the zero address");
Affected code:
62: require(accounts.length == ids.length, "ERC1155: accounts and IDs must have same lengths");
Proposed change:
require(accounts.length == ids.length, "ERC1155: accounts and IDs must have same lengths");
Affected code:
67: require(accounts[i] != address(0), "ERC1155: some address in batch balance query is zero");
Proposed change:
require(accounts[i] != address(0), "ERC1155: some address in batch balance query is zero");
Affected code:
86: require(msg.sender != operator, "ERC1155: cannot set approval status for self");
Proposed change:
require(msg.sender != operator, "ERC1155: cannot set approval status for self");
Affected code:
129: require(to != address(0), "ERC1155: target address must be non-zero");
Proposed change:
require(to != address(0), "ERC1155: target address must be non-zero");
Affected code:
186: require(to != address(0), "ERC1155: target address must be non-zero");
Proposed change:
require(to != address(0), "ERC1155: target address must be non-zero");
Affected code:
222: require(to != address(0), "ERC1155: mint to the zero address");
Proposed change:
require(to != address(0), "ERC1155: mint to the zero address");
Affected code:
237: require(to != address(0), "ERC1155: batch mint to the zero address");
Proposed change:
require(to != address(0), "ERC1155: batch mint to the zero address");
Affected code:
238: require(ids.length == values.length, "ERC1155: minted IDs and values must have same lengths");
Proposed change:
require(ids.length == values.length, "ERC1155: minted IDs and values must have same lengths");
Affected code:
255: require(account != address(0), "ERC1155: attempting to burn tokens on zero account");
Proposed change:
require(account != address(0), "ERC1155: attempting to burn tokens on zero account");
Affected code:
269: require(account != address(0), "ERC1155: attempting to burn batch of tokens on zero account");
Proposed change:
require(account != address(0), "ERC1155: attempting to burn batch of tokens on zero account");
Affected code:
270: require(ids.length == values.length, "ERC1155: burnt IDs and values must have same lengths");
Proposed change:
require(ids.length == values.length, "ERC1155: burnt IDs and values must have same lengths");
#15 Missing empty array check causes permanent token loss
The migrate function sets the sender's balance for fromId to zero before iterating through the outcomes array. If an empty array is passed, the loop body never executes, resulting in the user's tokens being burned (balance permanently set to zero) without receiving any corresponding child universe tokens. This constitutes a permanent loss of funds. Tags: bug, security Affected code:
22:
Proposed change:
require(outcomes.length > 0, 'Must specify at least one outcome');
#16 Duplicate outcomes allow double spending and supply inflation
The function does not validate that elements in the outcomes array are unique. If the same outcome index appears multiple times (e.g., [1, 1]), the loop credits the full fromIdBalance to the same child universe multiple times. This allows users to mint arbitrary amounts of tokens, inflating the supply beyond the intended total. The existing TODO comment on line 27 acknowledges this check is missing but the implementation remains vulnerable. Tags: bug, security Affected code:
27: // TODO, check that outcomes are unique
28: // TODO, do we allow people to migrate later to different universes?
Proposed change:
// Ensure outcomes are unique to prevent double spending
uint256 outcomeMask;
for (uint256 i = 0; i < outcomes.length; i++) {
uint256 bit = 1 << outcomes[i];
require((outcomeMask & bit) == 0, 'Duplicate outcome');
outcomeMask |= bit;
}
#17 Syntax Error: Mismatched Parentheses in Assembly
In the getTokenId function's assembly block, the expression contains an extra closing parenthesis after the bitmask literal. The code shows ...FFFFFFFFFFFFFF))), (three closing parentheses) which incorrectly closes the or operation before the second argument and(_outcome, 0xFF) is provided. This results in a parenthesis mismatch (4 opens vs 5 closes) that will cause a compilation failure. The correct syntax requires only two closing parentheses after the mask to close the and and shl operations respectively. Tags: bug, syntax-error Affected code:
Two function names violate camelCase conventions: getnonDecisionThreshold should be getNonDecisionThreshold (capitalize 'N' after 'get'), and getZoltarforkThreshold should be getZoltarForkThreshold (capitalize 'F' in Fork). These appear in imports and usage throughout the file. Additionally, several lines contain extra spaces. Tags: naming, language, code-style Affected code:
Test assertion messages contain typographical and grammatical errors. Line 81: 'treshold' should be 'threshold' and 'need' should be 'needs'. Line 88: 'cumulator' should be 'cumulative amount'. Tags: language Affected code:
81: assert.ok(awaitgetnonDecisionThreshold(client,securityPoolAddresses.escalationGame)>10n*reportBond,'fork treshold need to be big enough')
Proposed change:
assert.ok(awaitgetnonDecisionThreshold(client,securityPoolAddresses.escalationGame)>10n*reportBond,'fork threshold needs to be big enough')
Affected code:
88: strictEqualTypeSafe(yesDeposits[0].amount,reportBond,'amount should be report bond')
Proposed change:
strictEqualTypeSafe(yesDeposits[0].cumulativeAmount,reportBond,'cumulative amount should be report bond')
Line 140 divides repDepositShare by PRICE_PRECISION but compares it to repDeposit + (repDeposit * 10n) which is not divided by PRICE_PRECISION. This compares values in different units (normalized value vs wei value), causing the assertion to fail. Should use strictEqual18Decimal to compare raw 18-decimal values, or divide the right side by PRICE_PRECISION. Tags: bug Affected code:
140: strictEqualTypeSafe(liquidatorVault.repDepositShare/PRICE_PRECISION,repDeposit+(repDeposit*10n),'liquidator should have all the rep in the pool')
Proposed change:
strictEqual18Decimal(liquidatorVault.repDepositShare,repDeposit+(repDeposit*10n),'liquidator should have all the rep in the pool')
The timestamp comparison at line 46 multiplies timestamp.getTime() (milliseconds) by 1000, resulting in a value 1000x larger than Date.now(). This causes the difference to always be negative, so the cache invalidation condition always evaluates to false and expired blocks are never cleared from cache. The calculation should compare milliseconds to milliseconds. Tags: bug, logic, performance Affected code:
The jsonRpcRequest calls at lines 134 and 147 (when fullObjects is false) omit the requestAbortController parameter, preventing request cancellation for these specific code paths. This is inconsistent with the other branches in these methods which correctly pass the abort controller. Tags: bug, maintainability Affected code:
The eth_simulateV1 method accepts a requestAbortController parameter but fails to pass it to the underlying jsonRpcRequest call at line 194, preventing callers from aborting these potentially long-running simulation requests. Tags: bug, maintainability Affected code:
The word 'transactions' is misspelled as 'transatons' in the comment on line 18. Per guidelines, language issues are treated as critical defects. Tags: language Affected code:
18: constMOCK_SIMULATION_PRIVATE_KEY=0x2n// key used to sign simulated transatons
Proposed change:
constMOCK_SIMULATION_PRIVATE_KEY=0x2n// key used to sign simulated transactions
The word 'transaction' is misspelled as 'transction' in the error message on line 214. Error messages are user-facing strings and must be correct. Tags: language Affected code:
214: if(lastTransaction===undefined)thrownewError('last transction did not exist')
Proposed change:
if(lastTransaction===undefined)thrownewError('last transaction did not exist')
#26 Missing await for async ethereumClientService.call
Line 617 calls ethereumClientService.call() which returns a Promise, but the result is not awaited before being passed to EthereumData.parse(). This causes the parser to receive a Promise object instead of the actual data, leading to incorrect behavior or runtime errors. Tags: bug Affected code:
Line 381 decodes a uint256 balance value (which can exceed Number.MAX_SAFE_INTEGER) and casts it to 'number' before converting to BigInt. This can cause precision loss for large balances. The viem library returns bigint for uint256 types, so the cast should be to bigint or omitted entirely. Tags: bug, security Affected code:
Line 190 uses BigInt(parent) to check if the parent address is zero. The BigInt constructor in JavaScript throws a SyntaxError when given hex strings containing mixed-case letters (e.g., checksummed Ethereum addresses like '0xAbC...'). Since the parent parameter accepts any 0x${string} and may receive checksummed addresses from viem utilities or other sources, this code will crash at runtime when processing valid checksummed addresses. Use a direct comparison against zeroAddress instead. Tags: bug, compatibility Affected code:
Line 16 uses toLocaleLowerCase() to normalize a hex address string for bytecode replacement. This is risky because toLocaleLowerCase() is locale-sensitive (e.g., in Turkish locale, 'I' becomes 'ı'). While Ethereum addresses currently only contain 0-9 and A-F, using locale-sensitive methods for programmatic string transformation is an anti-pattern that could lead to subtle bugs if the character set changes or if the code is reused elsewhere. Use toLowerCase() instead. Tags: anti-pattern, maintainability Affected code:
The Escalation Game deployment entry is added to the universe-specific deployments array (getDeploymentsForUniverse) but lacks the universe identifier in its deployment name, unlike all other universe-specific contracts (RepV2, PriceOracleManagerAndOperatorQueuer, ETH SecurityPool, ShareToken, Truth Auction). Since a unique escalation game address is generated for each universe (passed via the escalationGame parameter from universe-specific address generation), omitting the universe name will cause naming collisions and make it impossible to distinguish between escalation games of different universes in the deployment list. Tags: bug, naming, consistency Affected code:
The parameter seucurityPoolAddress in the balanceOfSharesInCash function contains a typographical error. It should be securityPoolAddress to match the terminology used throughout the codebase (as seen in the import from './securityPool.js'). This typo reduces code readability and may cause confusion when referencing the variable. Tags: naming, language Affected code:
The function getOpenOracleReportMeta returns a ReportMeta interface where several fields (settlementTime, feePercentage, protocolFee, multiplier, disputeDelay) are typed as number. However, the contract likely returns these as uint256/bigint (consistent with the getOpenOracleExtraData function which explicitly converts bigints to numbers). Returning unconverted bigints violates the type contract and may cause runtime errors (e.g., JSON serialization failures or arithmetic issues) in consuming code. The values should be converted using Number() similar to lines 92-93 in getOpenOracleExtraData. Tags: bug, maintainability Affected code:
The assertion on line 65 checks that the WETH balance equals amount2, assuming the account starts with zero WETH. This will fail if the account already holds WETH or if the function is invoked multiple times in a test sequence (accumulating balance). Should record the balance before wrapping and verify the delta. Tags: bug Affected code:
63: awaitwrapWeth(client,amount2)64: constwethBalance=awaitgetERC20Balance(client,WETH_ADDRESS,client.account.address)65: assert.strictEqual(wethBalance,amount2,'Did not wrap weth')
Proposed change:
constwethBalanceBefore=awaitgetERC20Balance(client,WETH_ADDRESS,client.account.address)awaitwrapWeth(client,amount2)constwethBalance=awaitgetERC20Balance(client,WETH_ADDRESS,client.account.address)assert.strictEqual(wethBalance,wethBalanceBefore+amount2,'Did not wrap WETH')
#34 Parameter incorrectly passed as transaction value instead of argument
In createCompleteSet, the parameter completeSetsToCreate is passed as value (ETH) instead of in args. Based on the naming convention (consistent with completeSetsToRedeem which is passed as an argument) and the parameter name suggesting a quantity rather than an ETH amount, this appears to be a bug. The function should pass the parameter in args array to match the pattern used in redeemCompleteSet and other similar functions. Tags: bug Affected code:
Function name uses plural 'Pools' instead of singular 'Pool', which is inconsistent with other functions in the file (getSecurityVault, getPoolOwnershipDenominator, etc.) and the fact that the function operates on a single security pool address. Tags: naming, language Affected code:
The inline ABI fragment for getMarketOutcome specifies 'stateMutability': 'nonpayable' (line 81), which indicates a function that can modify state. However, this function is being called via client.readContract (line 72), which is intended for view/pure functions. This mismatch suggests the function should likely be marked as 'view' or 'pure'. Nonpayable functions invoked via eth_call (readContract) will not persist state changes, and if the function actually requires state transitions or has side effects, this call pattern is incorrect. If it is genuinely a getter function, the ABI metadata is wrong. Tags: bug Affected code:
81: "stateMutability": "nonpayable",
Proposed change:
"stateMutability": "view",
#37 objectEntries type assertion fails for objects with numeric keys
The type assertion T[keyof T & string] evaluates to never when T has numeric keys (e.g., {0: string, 1: number} or arrays), because keyof T returns number literals like 0 | 1, and number & string is never. This causes the return type to be [string, never][] instead of the actual value types, leading to compile-time type unsoundness where valid entries are incorrectly typed. Tags: bug Affected code:
The regex pattern (:<hex>[a-fA-F0-9])+ on line 31 is syntactically incorrect for a named capture group. It will literal match the string (:<hex> followed by hex characters instead of capturing hex digits into a group named 'hex'. The correct syntax should be (?<hex>[a-fA-F0-9]+). As a result, the bytesMatch.groups object will never contain the expected 'hex' property, causing the Uint8Array parsing logic to fail silently. Tags: bug, logic Affected code:
The contractExists function checks for !== undefined to determine if a contract exists. However, viem's getCode (or getBytecode) returns '0x' for addresses with no deployed bytecode, not undefined. This causes the function to incorrectly return true for non-existent contracts. It should check for !== '0x' (and optionally !== undefined for defensive programming). Tags: bug, logic 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.