{"attester":"0xef635a06669a5c7e40b8F80edc6A80022F3fFdC5","cappedByCritical":false,"chunks":[{"contracts":["Rubixi"],"endLine":155,"file":"Rubixi.sol","findingsKept":3,"findingsReturned":3,"id":"Rubixi.sol","receipts":[0],"startLine":1,"status":"ok"}],"compiler":{"version":"0.4.26"},"counts":{"critical":1,"high":3,"info":4,"low":8,"medium":5},"eip712":{"chainId":84532,"name":"Sentinel","version":"1"},"findings":[{"category":"access_control","confidence":0.99,"confirmedByStatic":true,"description":"`DynamicPyramid` is not a constructor because its name does not match the contract name `Rubixi`. It is therefore a publicly callable function that assigns `creator = msg.sender`. Any address can call it at any time, become the owner, and exercise all `onlyowner` functions, including changing the owner, payout multiplier, and fee percentage and withdrawing accounted fees.","detectors":["controlled-array-length"],"endLine":17,"file":"Rubixi.sol","id":"F-01","models":["cx/gpt-5.6-terra"],"origin":"both","originalSeverity":"critical","quorum":{"outcome":"confirmed","requestedFor":"critical","votes":[{"model":"cx/gpt-5.6-terra","reason":"original finding","receipt":0,"verdict":"confirmed"},{"model":"ag/claude-sonnet-4-6","reason":"In Solidity pre-0.4.22 style, constructors must share the contract name. Here the contract is `Rubixi` but the 'constructor' is named `DynamicPyramid`, making it a regular public function. Anyone can call `DynamicPyramid()` to set `creator = msg.sender` and gain full owner privileges, including fee withdrawal and parameter manipulation.","receipt":1,"verdict":"confirmed"},{"model":"ds/deepseek-v4-pro","reason":"reviewer call failed: all models failed: ds/deepseek-v4-pro (HTTP 503: [deepseek/deepseek-v4-pro] [402]: {\"error\":{\"message\":\"Insufficient Balance (request_id: f7384160-4907-4aff-b999-2ddfcbed7807)\",\"type\":\"unknown_error\",\"param\":null,\"code\":\"invalid_request_error\"}} (reset after 2m)); xpl/deepseek-v4-pro (HTTP 400: [400","verdict":"uncertain"}]},"receipts":[0,1],"recommendation":"Replace this function with a proper constructor (`constructor() public` for this compiler version, or a function named `Rubixi`), and ensure ownership is initialized only during deployment.","severity":"critical","snippet":"        function DynamicPyramid() {\n                creator = msg.sender;\n        }","startLine":15,"title":"Misnamed constructor leaves ownership claimable by anyone"},{"category":"arithmetic","confidence":null,"confirmedByStatic":true,"description":"Rubixi.addPayout(uint256) (Rubixi.sol#51-71) uses msg.value but is not reachable from any payable function\n\tNon-payable entry points that can reach this function:\n\t- Rubixi.fallback() (Rubixi.sol#31-33)","detectors":["msg-value-in-nonpayable"],"endLine":71,"file":"Rubixi.sol","id":"F-02","models":[],"origin":"static","originalSeverity":"high","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#msg-value-in-nonpayable for the detector's explanation and the usual fix.","severity":"high","snippet":"        function() {\n                init();\n        }\n\n        //init function run on fallback\n        function init() private {\n                //Ensures only tx with value of 1 ether or greater are processed and added to pyramid\n                if (msg.value < 1 ether) {\n                        collectedFees += msg.value;\n                        return;\n                }\n","startLine":31,"title":"Msg value in nonpayable (Slither)"},{"category":"arithmetic","confidence":null,"confirmedByStatic":true,"description":"Rubixi.init() (Rubixi.sol#36-48) uses msg.value but is not reachable from any payable function\n\tNon-payable entry points that can reach this function:\n\t- Rubixi.fallback() (Rubixi.sol#31-33)","detectors":["msg-value-in-nonpayable"],"endLine":48,"file":"Rubixi.sol","id":"F-03","models":[],"origin":"static","originalSeverity":"high","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#msg-value-in-nonpayable for the detector's explanation and the usual fix.","severity":"high","snippet":"        function() {\n                init();\n        }\n\n        //init function run on fallback\n        function init() private {\n                //Ensures only tx with value of 1 ether or greater are processed and added to pyramid\n                if (msg.value < 1 ether) {\n                        collectedFees += msg.value;\n                        return;\n                }\n","startLine":31,"title":"Msg value in nonpayable (Slither)"},{"category":"denial_of_service","confidence":0.99,"confirmedByStatic":false,"description":"The fallback is the only external entry point that invokes `init`, which relies on `msg.value` to register deposits. However, the fallback is not marked `payable`. Solidity rejects Ether-bearing calls to non-payable functions before executing their body, so users cannot make deposits and the pyramid's core functionality cannot operate.","detectors":[],"endLine":33,"file":"Rubixi.sol","id":"F-04","models":["cx/gpt-5.6-terra"],"origin":"ai","originalSeverity":"high","receipts":[0],"recommendation":"Declare the fallback as payable, e.g. `function() public payable { init(); }`, and make the intended payment flow explicitly payable.","severity":"high","snippet":"        function() {\n                init();\n        }","startLine":31,"title":"Non-payable fallback prevents all intended deposits"},{"category":"unchecked_call","confidence":0.96,"confirmedByStatic":true,"description":"The return value of `send` is ignored. If a participant is a contract whose fallback rejects the 2,300-gas stipend or otherwise reverts, the Ether transfer fails, but the contract still deducts the payout from internal `balance` and advances `payoutOrder`. That participant is permanently skipped, while the failed payout becomes unaccounted Ether that cannot be retried through the payout mechanism.","detectors":["unchecked-send"],"endLine":69,"file":"Rubixi.sol","id":"F-05","models":["cx/gpt-5.6-terra"],"origin":"both","originalSeverity":"medium","receipts":[0],"recommendation":"Check the result of the transfer and only reduce `balance` and advance `payoutOrder` after a successful payment. Prefer a pull-payment withdrawal mechanism so recipients can claim funds with an appropriate gas limit.","severity":"medium","snippet":"                        participants[payoutOrder].etherAddress.send(payoutToSend);\n\n                        balance -= participants[payoutOrder].payout;\n                        payoutOrder += 1;","startLine":66,"title":"Failed participant payouts are accounted as successful"},{"category":"unchecked_call","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectAllFees() (Rubixi.sol#74-79) ignores return value by creator.send(collectedFees) (Rubixi.sol#77)","detectors":["unchecked-send"],"endLine":79,"file":"Rubixi.sol","id":"F-06","models":[],"origin":"static","originalSeverity":"medium","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send for the detector's explanation and the usual fix.","severity":"medium","snippet":"        function collectAllFees() onlyowner {\n                if (collectedFees == 0) throw;\n\n                creator.send(collectedFees);\n                collectedFees = 0;\n        }","startLine":74,"title":"Unchecked send (Slither)"},{"category":"unchecked_call","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectFeesInEther(uint256) (Rubixi.sol#81-89) ignores return value by creator.send(_amt) (Rubixi.sol#87)","detectors":["unchecked-send"],"endLine":89,"file":"Rubixi.sol","id":"F-07","models":[],"origin":"static","originalSeverity":"medium","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send for the detector's explanation and the usual fix.","severity":"medium","snippet":"        function collectFeesInEther(uint _amt) onlyowner {\n                _amt *= 1 ether;\n                if (_amt > collectedFees) collectAllFees();\n\n                if (collectedFees == 0) throw;\n\n                creator.send(_amt);\n                collectedFees -= _amt;\n        }","startLine":81,"title":"Unchecked send (Slither)"},{"category":"arithmetic","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectPercentOfFees(uint256) (Rubixi.sol#91-97) performs a multiplication on the result of a division:\n\t- feesToCollect = collectedFees / 100 * _pcent (Rubixi.sol#94)","detectors":["divide-before-multiply"],"endLine":97,"file":"Rubixi.sol","id":"F-08","models":[],"origin":"static","originalSeverity":"medium","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply for the detector's explanation and the usual fix.","severity":"medium","snippet":"        function collectPercentOfFees(uint _pcent) onlyowner {\n                if (collectedFees == 0 || _pcent > 100) throw;\n\n                uint feesToCollect = collectedFees / 100 * _pcent;\n                creator.send(feesToCollect);\n                collectedFees -= feesToCollect;\n        }","startLine":91,"title":"Divide before multiply (Slither)"},{"category":"unchecked_call","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectPercentOfFees(uint256) (Rubixi.sol#91-97) ignores return value by creator.send(feesToCollect) (Rubixi.sol#95)","detectors":["unchecked-send"],"endLine":97,"file":"Rubixi.sol","id":"F-09","models":[],"origin":"static","originalSeverity":"medium","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send for the detector's explanation and the usual fix.","severity":"medium","snippet":"        function collectPercentOfFees(uint _pcent) onlyowner {\n                if (collectedFees == 0 || _pcent > 100) throw;\n\n                uint feesToCollect = collectedFees / 100 * _pcent;\n                creator.send(feesToCollect);\n                collectedFees -= feesToCollect;\n        }","startLine":91,"title":"Unchecked send (Slither)"},{"category":"access_control","confidence":null,"confirmedByStatic":true,"description":"Modifier Rubixi.onlyowner() (Rubixi.sol#19-21) does not always execute _; or revert","detectors":["incorrect-modifier"],"endLine":21,"file":"Rubixi.sol","id":"F-10","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-modifier for the detector's explanation and the usual fix.","severity":"low","snippet":"        modifier onlyowner {\n                if (msg.sender == creator) _;\n        }","startLine":19,"title":"Incorrect modifier (Slither)"},{"category":"denial_of_service","confidence":null,"confirmedByStatic":true,"description":"Rubixi.addPayout(uint256) (Rubixi.sol#51-71) has external calls inside a loop: participants[payoutOrder].etherAddress.send(payoutToSend) (Rubixi.sol#66)\n\tCalls stack containing the loop:\n\t\tRubixi.fallback()\n\t\tRubixi.init()","detectors":["calls-loop"],"endLine":71,"file":"Rubixi.sol","id":"F-11","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#calls-loop for the detector's explanation and the usual fix.","severity":"low","snippet":"        function addPayout(uint _fee) private {\n                //Adds new address to participant array\n                participants.push(Participant(msg.sender, (msg.value * pyramidMultiplier) / 100));\n\n                //These statements ensure a quicker payout system to later pyramid entrants, so the pyramid has a longer lifespan\n                if (participants.length == 10) pyramidMultiplier = 200;\n                else if (participants.length == 25) pyramidMultiplier = 150;\n\n                // collect fees and update contract balance\n                balance += (msg.value * (100 - _fee)) / 100;\n                collectedFees += (msg.value * _fee) / 100;\n","startLine":51,"title":"Calls loop (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectFeesInEther(uint256) (Rubixi.sol#81-89) should emit an event for: \n\t- collectedFees -= _amt (Rubixi.sol#88)","detectors":["events-maths"],"endLine":89,"file":"Rubixi.sol","id":"F-12","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#events-maths for the detector's explanation and the usual fix.","severity":"low","snippet":"        function collectFeesInEther(uint _amt) onlyowner {\n                _amt *= 1 ether;\n                if (_amt > collectedFees) collectAllFees();\n\n                if (collectedFees == 0) throw;\n\n                creator.send(_amt);\n                collectedFees -= _amt;\n        }","startLine":81,"title":"Events maths (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.collectPercentOfFees(uint256) (Rubixi.sol#91-97) should emit an event for: \n\t- collectedFees -= feesToCollect (Rubixi.sol#96)","detectors":["events-maths"],"endLine":97,"file":"Rubixi.sol","id":"F-13","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#events-maths for the detector's explanation and the usual fix.","severity":"low","snippet":"        function collectPercentOfFees(uint _pcent) onlyowner {\n                if (collectedFees == 0 || _pcent > 100) throw;\n\n                uint feesToCollect = collectedFees / 100 * _pcent;\n                creator.send(feesToCollect);\n                collectedFees -= feesToCollect;\n        }","startLine":91,"title":"Events maths (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.changeOwner(address) (Rubixi.sol#100-102) should emit an event for: \n\t- creator = _owner (Rubixi.sol#101)","detectors":["events-access"],"endLine":102,"file":"Rubixi.sol","id":"F-14","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#events-access for the detector's explanation and the usual fix.","severity":"low","snippet":"        function changeOwner(address _owner) onlyowner {\n                creator = _owner;\n        }","startLine":100,"title":"Events access (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.changeOwner(address)._owner (Rubixi.sol#100) lacks a zero-check on :\n\t\t- creator = _owner (Rubixi.sol#101)","detectors":["missing-zero-check"],"endLine":101,"file":"Rubixi.sol","id":"F-15","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-check for the detector's explanation and the usual fix.","severity":"low","snippet":"        function changeOwner(address _owner) onlyowner {\n                creator = _owner;","startLine":100,"title":"Missing zero check (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.changeMultiplier(uint256) (Rubixi.sol#104-108) should emit an event for: \n\t- pyramidMultiplier = _mult (Rubixi.sol#107)","detectors":["events-maths"],"endLine":108,"file":"Rubixi.sol","id":"F-16","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#events-maths for the detector's explanation and the usual fix.","severity":"low","snippet":"        function changeMultiplier(uint _mult) onlyowner {\n                if (_mult > 300 || _mult < 120) throw;\n\n                pyramidMultiplier = _mult;\n        }","startLine":104,"title":"Events maths (Slither)"},{"category":"other","confidence":null,"confirmedByStatic":true,"description":"Rubixi.changeFeePercentage(uint256) (Rubixi.sol#110-114) should emit an event for: \n\t- feePercent = _fee (Rubixi.sol#113)","detectors":["events-maths"],"endLine":114,"file":"Rubixi.sol","id":"F-17","models":[],"origin":"static","originalSeverity":"low","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#events-maths for the detector's explanation and the usual fix.","severity":"low","snippet":"        function changeFeePercentage(uint _fee) onlyowner {\n                if (_fee > 10) throw;\n\n                feePercent = _fee;\n        }","startLine":110,"title":"Events maths (Slither)"},{"category":"reentrancy","confidence":null,"confirmedByStatic":true,"description":"Reentrancy in Rubixi.addPayout(uint256) (Rubixi.sol#51-71):\n\tExternal calls:\n\t- participants[payoutOrder].etherAddress.send(payoutToSend) (Rubixi.sol#66)\n\tState variables written after the call(s):\n\t- balance -= participants[payoutOrder].payout (Rubixi.sol#68)\n\t- payoutOrder += 1 (Rubixi.sol#69)","detectors":["reentrancy-unlimited-gas"],"endLine":71,"file":"Rubixi.sol","id":"F-18","models":[],"origin":"static","originalSeverity":"info","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-unlimited-gas for the detector's explanation and the usual fix.","severity":"info","snippet":"        function addPayout(uint _fee) private {\n                //Adds new address to participant array\n                participants.push(Participant(msg.sender, (msg.value * pyramidMultiplier) / 100));\n\n                //These statements ensure a quicker payout system to later pyramid entrants, so the pyramid has a longer lifespan\n                if (participants.length == 10) pyramidMultiplier = 200;\n                else if (participants.length == 25) pyramidMultiplier = 150;\n\n                // collect fees and update contract balance\n                balance += (msg.value * (100 - _fee)) / 100;\n                collectedFees += (msg.value * _fee) / 100;\n","startLine":51,"title":"Reentrancy unlimited gas (Slither)"},{"category":"reentrancy","confidence":null,"confirmedByStatic":true,"description":"Reentrancy in Rubixi.collectAllFees() (Rubixi.sol#74-79):\n\tExternal calls:\n\t- creator.send(collectedFees) (Rubixi.sol#77)\n\tState variables written after the call(s):\n\t- collectedFees = 0 (Rubixi.sol#78)","detectors":["reentrancy-unlimited-gas"],"endLine":79,"file":"Rubixi.sol","id":"F-19","models":[],"origin":"static","originalSeverity":"info","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-unlimited-gas for the detector's explanation and the usual fix.","severity":"info","snippet":"        function collectAllFees() onlyowner {\n                if (collectedFees == 0) throw;\n\n                creator.send(collectedFees);\n                collectedFees = 0;\n        }","startLine":74,"title":"Reentrancy unlimited gas (Slither)"},{"category":"reentrancy","confidence":null,"confirmedByStatic":true,"description":"Reentrancy in Rubixi.collectFeesInEther(uint256) (Rubixi.sol#81-89):\n\tExternal calls:\n\t- collectAllFees() (Rubixi.sol#83)\n\t\t- creator.send(collectedFees) (Rubixi.sol#77)\n\t- creator.send(_amt) (Rubixi.sol#87)\n\tState variables written after the call(s):\n\t- collectedFees -= _amt (Rubixi.sol#88)","detectors":["reentrancy-unlimited-gas"],"endLine":89,"file":"Rubixi.sol","id":"F-20","models":[],"origin":"static","originalSeverity":"info","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-unlimited-gas for the detector's explanation and the usual fix.","severity":"info","snippet":"                creator.send(collectedFees);\n                collectedFees = 0;\n        }\n\n        function collectFeesInEther(uint _amt) onlyowner {\n                _amt *= 1 ether;\n                if (_amt > collectedFees) collectAllFees();\n\n                if (collectedFees == 0) throw;\n\n                creator.send(_amt);\n                collectedFees -= _amt;","startLine":77,"title":"Reentrancy unlimited gas (Slither)"},{"category":"reentrancy","confidence":null,"confirmedByStatic":true,"description":"Reentrancy in Rubixi.collectPercentOfFees(uint256) (Rubixi.sol#91-97):\n\tExternal calls:\n\t- creator.send(feesToCollect) (Rubixi.sol#95)\n\tState variables written after the call(s):\n\t- collectedFees -= feesToCollect (Rubixi.sol#96)","detectors":["reentrancy-unlimited-gas"],"endLine":97,"file":"Rubixi.sol","id":"F-21","models":[],"origin":"static","originalSeverity":"info","receipts":[],"recommendation":"See https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-unlimited-gas for the detector's explanation and the usual fix.","severity":"info","snippet":"        function collectPercentOfFees(uint _pcent) onlyowner {\n                if (collectedFees == 0 || _pcent > 100) throw;\n\n                uint feesToCollect = collectedFees / 100 * _pcent;\n                creator.send(feesToCollect);\n                collectedFees -= feesToCollect;\n        }","startLine":91,"title":"Reentrancy unlimited gas (Slither)"}],"grade":"D","models":{"primary":["cx/gpt-5.6-terra"],"quorum":["ag/claude-sonnet-4-6"]},"needsHumanReview":false,"receipts":[{"purpose":"analysis:Rubixi.sol","receipt":{"finishedAt":1792411217122,"model":"cx/gpt-5.6-terra","promptHash":"0xdb33eee9c47022cecd3cf64b537efa15b5392e8f6b0b0d166b0cbfc651c9e11c","provider":"9router.dea.web.id","requestId":"resp_01dd8ffe5acf47c0016ad60618d5488193957534b68a7cb6c0","responseHash":"0xddb94baf150d6aafe972eca0e5f0717a36536863031fdffe507d5bc2baf684e0","startedAt":1792411158863,"usage":{"completionTokens":1514,"costMicroUsd":0,"promptTokens":4095}},"signature":"0x9918690ca63baade7d543a63b793019d1ba44afe984af7b2d1e2b16f2996fa703a61cd921a49e525d94b2aac27f63600cec10b1bb4c0565940f784b4934e6a8b1c","structHash":"0x77efc5cff8ffe34bdc011aad96c015865b27b654104efd3d9c1f8d140fdd7370"},{"purpose":"quorum:F-01","receipt":{"finishedAt":1792411226167,"model":"ag/claude-sonnet-4-6","promptHash":"0x33c3679cbbf06cc726802fa43e5007a95ebcc1668205e2418813ef542a656734","provider":"9router.dea.web.id","requestId":"chatcmpl-msg_vrtx_011CgBZNnqyMaH1F1Yhhh25J","responseHash":"0x590b8905d45df6de75d4a85895bdb3ce17748cc9388ac898a129d6dae489d63e","startedAt":1792411218571,"usage":{"completionTokens":102,"costMicroUsd":0,"promptTokens":2690}},"signature":"0x5ca9c4103ddb796bb5a25a4967d6f92591495badf2a790d35caa1cc9458dd9fd7f61657895ab89ffc27eaf291ba38fcafae49a5806a1490694023d718245f6f21b","structHash":"0xabd758e3ed75a658d7d9436a59de2e8bd45019ee8613ec7ce225d8d4e68868ce"}],"receiptsRoot":"0xff978c10f8494306140a36e98bae81c4f9b046d647399fa97152e7d6c358e2e4","schema":"sentinel.report/v1","score":57,"slither":{"findings":34,"mediumOrAbove":8,"suppressed":14,"version":"0.11.6"},"sourceHash":"0x7a16efab354d87c218c2792eedba2cfd1c7397da8391f1e4ba0d1095929e0d90","stats":{"aiOnlyPct":4.8,"staticConfirmedPct":95.2,"staticOnlyPct":85.7,"total":21},"target":{"address":null,"chainId":0,"files":[{"lines":154,"path":"Rubixi.sol","sloc":104}],"kind":"upload","mainContract":null},"tier":"deep","timings":{"finishedAt":1792411226174,"stagesMs":{"ai":58268,"ingest":0,"quorum":9033,"score":0,"static":794},"startedAt":1792411158066}}