SpecHash,SpecIndex,Type,Name,StartLine,EndLine,MethodsInRule,RuleContent,RelatedFunctions,FunctionBodies,FilePath,Project,ContractCode,StateVarAssignment,RuleContentNL,Funcitonality 1adad19c8ea189e760a3480b59dc3d24,142 | 143,rule,integrityOfExecuteEmergencyAction,235,246,areAllReservesBacked | executeEmergencyAction,"// rule integrityOfExecuteEmergencyAction(bool rand) { // require _disableBorrowingCalled() == false; // aggregator.initFlags(rand); // bool allReservesBacked = areAllReservesBacked(); // executeEmergencyAction(); // bool disableBorrowingCalled = _disableBorrowingCalled(); // assert !allReservesBacked => disableBorrowingCalled; // assert allReservesBacked => !disableBorrowingCalled; // } ","areAllReservesBacked (Lines 87-97), | executeEmergencyAction (Lines 70-92), "," function areAllReservesBacked() external view returns (bool) { if (_assets.length == 0) { return true; } (bool areReservesBacked, ) = _proofOfReserveAggregator.areAllReservesBacked( _assets ); return areReservesBacked; } | function executeEmergencyAction() external override { ( bool areReservesBacked, bool[] memory unbackedAssetsFlags ) = _proofOfReserveAggregator.areAllReservesBacked(_assets); if (!areReservesBacked) { _disableBorrowing(); uint256 assetsLength = _assets.length; for (uint256 i = 0; i < assetsLength; ++i) { if (unbackedAssetsFlags[i]) { // freeze reserve _configurator.freezeReserve(_assets[i]); emit AssetIsNotBacked(_assets[i]); } } emit EmergencyActionExecuted(); } } ",./aave_proof_of_reserve/specs/executorV2.spec,aave_proof_of_reserve,,Yes,,"Functionality: The provided code checks if all assets in a portfolio are backed by reserves through a proof of reserve aggregator. If any asset is unbacked, it disables borrowing, freezes the reserves of those specific unbacked assets, and emits events to indicate an emergency action and the assets not backed by reserves." 1a45f8a5d1331c57ccb23e4ab81cf513,144 | 145,rule,integrityOfExecuteEmergencyAction,244,255,areAllReservesBacked | executeEmergencyAction,"// rule integrityOfExecuteEmergencyAction(bool rand) { // aggregator.initFlags(rand); // require configurator.freezeWasCalled() == false; // bool allReservesBacked = areAllReservesBacked(); // executeEmergencyAction(); // bool freezeReserveWasCalled = configurator.freezeWasCalled(); // assert !allReservesBacked => freezeReserveWasCalled; // assert allReservesBacked => !freezeReserveWasCalled; // } ","areAllReservesBacked (Lines 87-97), | executeEmergencyAction (Lines 70-92), "," function areAllReservesBacked() external view returns (bool) { if (_assets.length == 0) { return true; } (bool areReservesBacked, ) = _proofOfReserveAggregator.areAllReservesBacked( _assets ); return areReservesBacked; } | function executeEmergencyAction() external override { ( bool areReservesBacked, bool[] memory unbackedAssetsFlags ) = _proofOfReserveAggregator.areAllReservesBacked(_assets); if (!areReservesBacked) { _disableBorrowing(); uint256 assetsLength = _assets.length; for (uint256 i = 0; i < assetsLength; ++i) { if (unbackedAssetsFlags[i]) { // freeze reserve _configurator.freezeReserve(_assets[i]); emit AssetIsNotBacked(_assets[i]); } } emit EmergencyActionExecuted(); } } ",./aave_proof_of_reserve/specs/executorV3.spec,aave_proof_of_reserve,,Yes,,"Functionality: Verify if all reserves are backed by assets, returning a boolean value. In case of unbacked reserves, disable borrowing, freeze unbacked asset reserves, and emit relevant events to indicate assets that are not backed and that an emergency action has been executed." 1f13d0617b40090d6b6dc2a0541c72d8,148 | 149,rule,PoRFeedChange,35,54,enableProofOfReserveFeed | enableProofOfReserveFeedWithBridgeWrapper,"rule PoRFeedChange(address asset, address PoRFeed, address wrapper){ address feedBefore = getProofOfReserveFeedForAsset(asset); address bridgeWrapperBefore = getBridgeWrapperForAsset(asset); method f; env e; call_f_with_params(f, e, asset, PoRFeed, wrapper); address feedAfter = getProofOfReserveFeedForAsset(asset); address bridgeWrapperAfter = getBridgeWrapperForAsset(asset); assert f.selector == enableProofOfReserveFeed(address, address).selector => (feedAfter != 0 && feedAfter == PoRFeed); assert f.selector == enableProofOfReserveFeedWithBridgeWrapper(address, address, address).selector => (feedAfter != 0 && feedAfter == PoRFeed && bridgeWrapperAfter != 0 && bridgeWrapperAfter == wrapper); assert f.selector == disableProofOfReserveFeed(address).selector => feedAfter == 0 && bridgeWrapperAfter == 0; assert (f.selector != enableProofOfReserveFeed(address, address).selector && f.selector != disableProofOfReserveFeed(address).selector && f.selector != enableProofOfReserveFeedWithBridgeWrapper(address, address, address).selector) => feedBefore == feedAfter && bridgeWrapperBefore == bridgeWrapperAfter; } ","enableProofOfReserveFeed (Lines 42-57), | enableProofOfReserveFeedWithBridgeWrapper (Lines 60-79), "," function enableProofOfReserveFeed(address asset, address proofOfReserveFeed) external onlyOwner { require(asset != address(0), 'INVALID_ASSET'); require(proofOfReserveFeed != address(0), 'INVALID_PROOF_OF_RESERVE_FEED'); require(_proofOfReserveList[asset] == address(0), 'FEED_ALREADY_ENABLED'); _proofOfReserveList[asset] = proofOfReserveFeed; emit ProofOfReserveFeedStateChanged( asset, proofOfReserveFeed, address(0), true ); } | function enableProofOfReserveFeedWithBridgeWrapper( address asset, address proofOfReserveFeed, address bridgeWrapper ) external onlyOwner { require(asset != address(0), 'INVALID_ASSET'); require(proofOfReserveFeed != address(0), 'INVALID_PROOF_OF_RESERVE_FEED'); require(bridgeWrapper != address(0), 'INVALID_BRIDGE_WRAPPER'); require(_proofOfReserveList[asset] == address(0), 'FEED_ALREADY_ENABLED'); _proofOfReserveList[asset] = proofOfReserveFeed; _bridgeWrapperList[asset] = bridgeWrapper; emit ProofOfReserveFeedStateChanged( asset, proofOfReserveFeed, bridgeWrapper, true ); } ",./aave_proof_of_reserve/specs/aggregator.spec,aave_proof_of_reserve,,Yes,,"Functionality: Enable or update the proof of reserve feed for a specified asset, optionally setting a bridge wrapper. Ensure the asset and feed addresses are valid and not already configured. Emit events to log changes in the state of proof of reserve feeds." 6f0a788c11d1de327d863a034e8efbe5,611 | 612,rule,additiveBurn,141,156,balanceOf | burn,"rule additiveBurn(address user1, address user2, uint256 x, uint256 y) { env e; uint256 index = gRNVB(); require (user1 != user2 && balanceOf(user1) == balanceOf(user2)); require user1 != currentContract && user2 != currentContract; burn(e, user1, x, index); burn(e, user1, y, index); uint256 balanceScenario1 = balanceOf(user1); burn(e, user2, x+y, index); uint256 balanceScenario2 = balanceOf(user2); assert bounded_error_eq(balanceScenario1, balanceScenario2, 3), ""burn is not additive""; // assert balanceScenario1 == balanceScenario2, ""burn is not additive""; } ","balanceOf (Lines 101-112), | burn (Lines 178-245), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/VariableDebtToken.spec,aave_v3,,Yes,,"Functionality: Adjust a user's balance and the total supply of a financial instrument by calculating interest accrued based on a stable rate, and either mint or burn tokens to reflect changes resulting from a transaction, while also updating average stable rates and timestamps accordingly." 5ef46523db01c063e88661dc1f6ac3eb,412,rule,additiveTransfer,178,201,balanceOf,"rule additiveTransfer(address from1, address from2, address to1, address to2, uint256 x, uint256 y) { env e1; env e2; uint256 indexRay = gRNI(); require (from1 != from2 && to1 != to2 && from1 != to2 && from2 != to1 && (from1 == to1 <=> from2 == to2) && balanceOf(from1) == balanceOf(from2) && balanceOf(to1) == balanceOf(to2)); require e1.msg.sender == from1; require e2.msg.sender == from2; transfer(e1, to1, x); transfer(e1, to1, y); uint256 balanceFromScenario1 = balanceOf(from1); uint256 balanceToScenario1 = balanceOf(to1); transfer(e2, to2, x+y); uint256 balanceFromScenario2 = balanceOf(from2); uint256 balanceToScenario2 = balanceOf(to2); assert bounded_error_eq(balanceFromScenario1, balanceFromScenario2, 3) && bounded_error_eq(balanceToScenario1, balanceToScenario2, 3), ""transfer is not additive""; } ","balanceOf (Lines 101-112), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } ",./aave_v3/specs/AToken.spec,aave_v3,,Yes,,"Functionality: Calculate and return the adjusted balance of a given account by applying the compounded interest rate to the account's existing balance, incorporating a stable rate and time factor, with the calculation ceasing and returning zero if the account balance is initially zero." a17ca2e8fe7297047b2c0484f1988b46,532,rule,getReserveNormalizedVariableDebtCheck,197,209,supply,"rule getReserveNormalizedVariableDebtCheck() { env e1; calldataarg args; calldataarg args2; address asset; uint256 amount; address onBehalfOf; uint16 referralCode; require asset != _aToken; uint256 oldIndex = getReserveNormalizedVariableDebt(e1, args); uint256 totalDebtBefore = getCurrScaledVariableDebt(asset); supply(e1, asset, amount, onBehalfOf, referralCode); uint256 newIndex = getReserveNormalizedVariableDebt(e1, args); assert totalDebtBefore != 0 => newIndex >= oldIndex; } ","supply (Lines 143-160), "," function supply( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) public virtual override { SupplyLogic.executeSupply( _reserves, _reservesList, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyParams({ asset: asset, amount: amount, onBehalfOf: onBehalfOf, referralCode: referralCode }) ); } ",./aave_v3/specs/pool.spec,aave_v3,,Yes,,"Functionality: Execute a supply operation on behalf of a specified user by calling the SupplyLogic's `executeSupply` method with the specified asset, amount, beneficiary address, and referral code using the user's configuration and reserves data." efa0872e2bd5bc7b8586d20edeed3133,403 | 404,rule,integrityMint,101,119,mint | balanceOf,"rule integrityMint(address a, address b, uint256 x) { env e; uint256 indexRay = gRNI(); uint256 underlyingBalanceBefore = balanceOf(a); uint256 atokenBlanceBefore = scaledBalanceOf(e, a); uint256 totalATokenSupplyBefore = scaledTotalSupply(e); mint(e,b,a,x,indexRay); uint256 underlyingBalanceAfter = balanceOf(a); uint256 atokenBlanceAfter = scaledBalanceOf(e, a); uint256 totalATokenSupplyAfter = scaledTotalSupply(e); assert atokenBlanceAfter - atokenBlanceBefore == totalATokenSupplyAfter - totalATokenSupplyBefore; assert totalATokenSupplyAfter > totalATokenSupplyBefore; assert bounded_error_eq(underlyingBalanceAfter, underlyingBalanceBefore+x, 1); } ","mint (Lines 124-175), | balanceOf (Lines 101-112), "," function mint( address user, address onBehalfOf, uint256 amount, uint256 rate ) external virtual override onlyPool returns (bool, uint256, uint256) { MintLocalVars memory vars; if (user != onBehalfOf) { _decreaseBorrowAllowance(onBehalfOf, user, amount); } (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf); vars.previousSupply = totalSupply(); vars.currentAvgStableRate = _avgStableRate; vars.nextSupply = _totalSupply = vars.previousSupply + amount; vars.amountInRay = amount.wadToRay(); vars.currentStableRate = _userState[onBehalfOf].additionalData; vars.nextStableRate = (vars.currentStableRate.rayMul(currentBalance.wadToRay()) + vars.amountInRay.rayMul(rate)).rayDiv((currentBalance + amount).wadToRay()); _userState[onBehalfOf].additionalData = vars.nextStableRate.toUint128(); //solium-disable-next-line _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp); // Calculates the updated average stable rate vars.currentAvgStableRate = _avgStableRate = ( (vars.currentAvgStableRate.rayMul(vars.previousSupply.wadToRay()) + rate.rayMul(vars.amountInRay)).rayDiv(vars.nextSupply.wadToRay()) ).toUint128(); uint256 amountToMint = amount + balanceIncrease; _mint(onBehalfOf, amountToMint, vars.previousSupply); emit Transfer(address(0), onBehalfOf, amountToMint); emit Mint( user, onBehalfOf, amountToMint, currentBalance, balanceIncrease, vars.nextStableRate, vars.currentAvgStableRate, vars.nextSupply ); return (currentBalance == 0, vars.nextSupply, vars.currentAvgStableRate); } | function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } ",./aave_v3/specs/AToken.spec,aave_v3,,Yes,,"Functionality: The provided code defines a function for minting new tokens on behalf of a user, adjusting their balance based on an updated average stable rate and compounded interest, and emits events reflecting these changes. It also includes a method to calculate a user's current balance, factoring in accrued interest." d006d522c332ba3ba477c7e5c892b16a,568,rule,setLiquidationProtocolFeeIntegrity,151,154,setLiquidationProtocolFee,"rule setLiquidationProtocolFeeIntegrity(uint256 liquidationProtocolFee) { setLiquidationProtocolFee(liquidationProtocolFee); assert getLiquidationProtocolFee() == liquidationProtocolFee; } ","setLiquidationProtocolFee (Lines 311-321), "," function setLiquidationProtocolFee( address asset, uint256 newFee ) external override onlyRiskOrPoolAdmins { require(newFee <= PercentageMath.PERCENTAGE_FACTOR, Errors.INVALID_LIQUIDATION_PROTOCOL_FEE); DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); uint256 oldFee = currentConfig.getLiquidationProtocolFee(); currentConfig.setLiquidationProtocolFee(newFee); _pool.setConfiguration(asset, currentConfig); emit LiquidationProtocolFeeChanged(asset, oldFee, newFee); } ",./aave_v3/specs/ReserveConfiguration.spec,aave_v3,,Yes,,"Functionality: Update the liquidation protocol fee for a specified asset to a new value, ensuring it does not exceed a predefined maximum percentage. The function validates the new fee, updates the configuration in the pool, and emits an event documenting the change." de9b73e2d0acb6c986c9cda6f3cedfea,490 | 491,rule,setBorrowing,20,24,setBorrowing | isBorrowing,"rule setBorrowing(uint256 reserveIndex, bool borrowing) { setBorrowing(reserveIndex, borrowing); assert isBorrowing(reserveIndex) == borrowing, ""unexpected result""; } ","setBorrowing (Lines 27-41), | isBorrowing (Lines 87-95), "," function setBorrowing( DataTypes.UserConfigurationMap storage self, uint256 reserveIndex, bool borrowing ) internal { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); uint256 bit = 1 << (reserveIndex << 1); if (borrowing) { self.data |= bit; } else { self.data &= ~bit; } } } | function isBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 1 != 0; } } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,"Functionality: Enable or disable a user's borrowing status for a specific reserve index within a decentralized finance protocol. It verifies the reserve index's validity, modifies the user's configuration map to reflect the borrowing status, and checks if a user is currently borrowing from a specific reserve." 8aa368715d4def33d5d14e9a16af7032,577 | 578,rule,integrityBurn,138,147,balanceOf | burn,"rule integrityBurn(address a, uint256 x) { env e; require getIncentivesController(e) == 0; uint256 index; uint256 balancebefore = balanceOf(e, a); burn(e,a,x); uint256 balanceAfter = balanceOf(e, a); assert balanceAfter == balancebefore - x; } ","balanceOf (Lines 101-112), | burn (Lines 178-245), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/StableDebtToken.spec,aave_v3,,Yes,,"Functionality: Adjust user and total supply balances on a stablecoin contract, using time-dependent interest calculations for accurate accounting. Account for potential accumulation errors by adjusting the average stable rate as necessary, and handle burns or mints based on the comparison of user balance increases to requested burn amounts." cb81952394ea33aa5b1e095725df8d51,584 | 585,rule,additiveBurn,187,198,balanceOf | burn,"rule additiveBurn(address a, uint256 x, uint256 y) { env e; storage initialStorage = lastStorage; burn(e, a, x); burn(e, a, y); uint256 balanceScenario1 = balanceOf(e, a); uint256 t = x + y; burn(e, a, t) at initialStorage; uint256 balanceScenario2 = balanceOf(e, a); assert balanceScenario1 == balanceScenario2, ""burn is not additive""; } ","balanceOf (Lines 101-112), | burn (Lines 178-245), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/StableDebtToken.spec,aave_v3,,Yes,,"Functionality: Adjust a user's balance and update the average stable rate and total supply in a decentralized finance protocol, handling balance increase due to interest, and minting or burning tokens accordingly while ensuring the last borrower repaying doesn't repay more than the available debt supply." de9b73e2d0acb6c986c9cda6f3cedfea,490 | 491,rule,setBorrowing,20,24,setBorrowing | isBorrowing,"rule setBorrowing(uint256 reserveIndex, bool borrowing) { setBorrowing(reserveIndex, borrowing); assert isBorrowing(reserveIndex) == borrowing, ""unexpected result""; } ","setBorrowing (Lines 27-41), | isBorrowing (Lines 87-95), "," function setBorrowing( DataTypes.UserConfigurationMap storage self, uint256 reserveIndex, bool borrowing ) internal { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); uint256 bit = 1 << (reserveIndex << 1); if (borrowing) { self.data |= bit; } else { self.data &= ~bit; } } } | function isBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 1 != 0; } } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,"Functionality: Manage and check user borrowing status for specific reserves in a financial protocol. ""setBorrowing"" updates a user's borrowing status for a given reserve index, and ""isBorrowing"" checks if a user is currently borrowing from a specified reserve." 568f1c053d910601ac7880d98abeb0b3,510,rule,integrityOfisUsingAsCollateralOne,85,89,isUsingAsCollateral,"// rule integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther){ // bool reserveCollateral = isUsingAsCollateral(reserveIndex); // bool reserveCollateralOther = isUsingAsCollateral(reserveIndexOther); // assert reserveCollateral && isUsingAsCollateralOne() => !reserveCollateralOther || reserveIndexOther == reserveIndex; // } ","isUsingAsCollateral (Lines 103-111), "," function isUsingAsCollateral( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; } } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,Functionality: Check if a specific reserve is being used as collateral by a user within a decentralized finance platform. This is done by shifting the user's configuration data to extract and evaluate a flag indicating collateral usage at the specified reserve index. 128f7992885aeaf61914ff90925752e5,765,rule,cantGoFromNoAuthorizationToCompletedInOneStepUnlessRoleIsLegacy,290,294,authorizeSignerWithSignature,"rule cantGoFromNoAuthorizationToCompletedInOneStepUnlessRoleIsLegacy(method f) filtered { f -> !f.isView // the authorizeSignerWithSignature allows the signer to provide a signature to the account, so we exclude this option && f.selector != authorizeSignerWithSignature(address,bytes32,uint8,bytes32,bytes32).selector } { ","authorizeSignerWithSignature (Lines 407-417), "," function authorizeSignerWithSignature(address signer, bytes32 role, uint8 v, bytes32 r, bytes32 s) public { authorizeAddressWithRole(signer, role, v, r, s); signerAuthorizations[msg.sender][role][signer] = SignerAuthorization({ started: true, completed: true }); emit SignerAuthorized(msg.sender, signer, role); } ",./celo_governance/specs/accounts.spec,celo_governance,,Yes,,"Functionality: Use `authorizeSignerWithSignature` to authorize a signer for a specific role using EIP-712 signature, by internally calling `authorizeAddressWithRole` with the signer's details and marking the authorization process as both started and completed in the contract's state, then emit a `SignerAuthorized` event." ea4ba5e30a4c062b8d537ef09b36af6b,766,rule,cantMakeASignerForNonLegacyRoleWithoutApprovalOfSigner,309,313,authorizeSignerWithSignature,"rule cantMakeASignerForNonLegacyRoleWithoutApprovalOfSigner(method f) filtered { f -> !f.isView // the authorizeSignerWithSignature allows the signer to provide a signature to the account, so we exclude this option && f.selector != authorizeSignerWithSignature(address,bytes32,uint8,bytes32,bytes32).selector } { ","authorizeSignerWithSignature (Lines 407-417), "," function authorizeSignerWithSignature(address signer, bytes32 role, uint8 v, bytes32 r, bytes32 s) public { authorizeAddressWithRole(signer, role, v, r, s); signerAuthorizations[msg.sender][role][signer] = SignerAuthorization({ started: true, completed: true }); emit SignerAuthorized(msg.sender, signer, role); } ",./celo_governance/specs/accounts.spec,celo_governance,,Yes,,"Functionality: Authorize a specified signer with a particular role after verifying the sender's signature, and mark the authorization process as both started and completed within the contract's storage. Then, emit an event to record the authorization of the signer for the specified role." f4502f38f9ab63a1c3e3807bdfab9353,836 | 838 | 840,rule,no_double_vote_referendum_all_but_vote,164,164,getUpvotedProposal | upvote | getUpvoteRecord,"rule no_double_vote_referendum_all_but_vote(method f, address account, uint256 deqIndex) filtered { f -> !f.isView } { // the invariant is upvoteInv, defined here } definition upvoteInv(uint p, uint w) returns bool = (p != 0 && w > 0) || (p == 0 && w == 0) ; rule no_double_upvote(uint256 p, address u) { uint256 _upvotes = getUpvotes(p); uint256 _usersUpvotedProposal = getUpvotedProposal(u); env e; require e.msg.sender == u && u != 0; // note that the sender is the signer here require accounts.voteSignerToAccount(u) == u; uint256 lesser; uint256 greater; upvote(e, p, lesser, greater); uint256 upvotes_ = getUpvotes(p); uint voteRecordP; uint voteRecordW; voteRecordP, voteRecordW = getUpvoteRecord(u); require upvoteInv(voteRecordP, voteRecordW); assert _usersUpvotedProposal == p => upvotes_ <= _upvotes, ""Upvotes increased from ${_upvotes} to ${upvotes_} even though upvoted by user $u who already upvoted $p""; } ","getUpvotedProposal (Lines 43-48), | upvote (Lines 530-558), | getUpvoteRecord (Lines 1161-1164), "," function getUpvotedProposal(address account) public view returns (uint256) { uint256 proposalId; uint256 weight; (proposalId, weight) = this.getUpvoteRecord(account); return proposalId; } | function upvote(uint256 proposalId, uint256 lesser, uint256 greater) external nonReentrant returns (bool) { dequeueProposalsIfReady(); // If acting on an expired proposal, expire the proposal and take no action. if (removeIfQueuedAndExpired(proposalId)) { return false; } address account = getAccounts().voteSignerToAccount(msg.sender); Voter storage voter = voters[account]; removeIfQueuedAndExpired(voter.upvote.proposalId); // We can upvote a proposal in the queue if we're not already upvoting a proposal in the queue. uint256 weight = getLockedGold().getAccountTotalLockedGold(account); require(weight > 0, ""cannot upvote without locking gold""); require(queue.contains(proposalId), ""cannot upvote a proposal not in the queue""); require( voter.upvote.proposalId == 0 || !queue.contains(voter.upvote.proposalId), ""cannot upvote more than one queued proposal"" ); uint256 upvotes = queue.getValue(proposalId).add(weight); queue.update(proposalId, upvotes, lesser, greater); voter.upvote = UpvoteRecord(proposalId, weight); emit ProposalUpvoted(proposalId, account, weight); return true; } | function getUpvoteRecord(address account) external view returns (uint256, uint256) { UpvoteRecord memory upvoteRecord = voters[account].upvote; return (upvoteRecord.proposalId, upvoteRecord.weight); } ",./celo_governance/specs/governance.spec,celo_governance,,Yes,,"Functionality: Manage upvotes for proposals within a governance framework, allowing users to upvote proposals in a queue by locking a certain amount of gold. It prevents upvoting multiple queued proposals simultaneously and ensures only active, non-expired proposals with sufficient locked gold can be upvoted, reflecting each vote's weight accurately." e438d9fd1e89cf34c68ae2275f73ae7a,822 | 824 | 826,rule,no_referendum_votes_unless_approved,118,118,getUpvotedProposal | upvote | getUpvoteRecord,"rule no_referendum_votes_unless_approved(method f, uint256 p) filtered { f -> !f.isView } { // the invariant is upvoteInv, defined here } definition upvoteInv(uint p, uint w) returns bool = (p != 0 && w > 0) || (p == 0 && w == 0) ; rule no_double_upvote(uint256 p, address u) { uint256 _upvotes = getUpvotes(p); uint256 _usersUpvotedProposal = getUpvotedProposal(u); env e; require e.msg.sender == u && u != 0; // note that the sender is the signer here require accounts.voteSignerToAccount(u) == u; uint256 lesser; uint256 greater; upvote(e, p, lesser, greater); uint256 upvotes_ = getUpvotes(p); uint voteRecordP; uint voteRecordW; voteRecordP, voteRecordW = getUpvoteRecord(u); require upvoteInv(voteRecordP, voteRecordW); assert _usersUpvotedProposal == p => upvotes_ <= _upvotes, ""Upvotes increased from ${_upvotes} to ${upvotes_} even though upvoted by user $u who already upvoted $p""; } ","getUpvotedProposal (Lines 43-48), | upvote (Lines 530-558), | getUpvoteRecord (Lines 1161-1164), "," function getUpvotedProposal(address account) public view returns (uint256) { uint256 proposalId; uint256 weight; (proposalId, weight) = this.getUpvoteRecord(account); return proposalId; } | function upvote(uint256 proposalId, uint256 lesser, uint256 greater) external nonReentrant returns (bool) { dequeueProposalsIfReady(); // If acting on an expired proposal, expire the proposal and take no action. if (removeIfQueuedAndExpired(proposalId)) { return false; } address account = getAccounts().voteSignerToAccount(msg.sender); Voter storage voter = voters[account]; removeIfQueuedAndExpired(voter.upvote.proposalId); // We can upvote a proposal in the queue if we're not already upvoting a proposal in the queue. uint256 weight = getLockedGold().getAccountTotalLockedGold(account); require(weight > 0, ""cannot upvote without locking gold""); require(queue.contains(proposalId), ""cannot upvote a proposal not in the queue""); require( voter.upvote.proposalId == 0 || !queue.contains(voter.upvote.proposalId), ""cannot upvote more than one queued proposal"" ); uint256 upvotes = queue.getValue(proposalId).add(weight); queue.update(proposalId, upvotes, lesser, greater); voter.upvote = UpvoteRecord(proposalId, weight); emit ProposalUpvoted(proposalId, account, weight); return true; } | function getUpvoteRecord(address account) external view returns (uint256, uint256) { UpvoteRecord memory upvoteRecord = voters[account].upvote; return (upvoteRecord.proposalId, upvoteRecord.weight); } ",./celo_governance/specs/governance.spec,celo_governance,,Yes,,"Functionality: Enable users to upvote governance proposals using their locked gold as voting power. It validates proposal eligibility, prevents users from supporting multiple active proposals, and updates the upvoted proposal's total upvotes accordingly while maintaining a record of each voter's current upvote." 2b7cc4ca0087210de350b81670679aae,899,rule,no_weight_changing_when_voting,44,61,isVoting,"rule no_weight_changing_when_voting(method f, address account) { env _e; uint256 _accountWeight = sinvoke _weight(_e,account); bool isAccountVoting = sinvoke isVoting(_e,account); env eF; calldataarg arg; invoke f(eF,arg); env e_; uint256 accountWeight_ = sinvoke _weight(e_,account); assert( isAccountVoting => _accountWeight == accountWeight_, ""Method changed weight of account if voting"" ); }","isVoting (Lines 986-995), "," function isVoting(address account) external view returns (bool) { Voter storage voter = voters[account]; uint256 upvotedProposal = voter.upvote.proposalId; bool isVotingQueue = upvotedProposal != 0 && isQueued(upvotedProposal) && !isQueuedProposalExpired(upvotedProposal); Proposals.Proposal storage proposal = proposals[voter.mostRecentReferendumProposal]; bool isVotingReferendum = (getProposalDequeuedStage(proposal) == Proposals.Stage.Referendum); return isVotingQueue || isVotingReferendum; } ",./celo_governance/specs/locked_gold_linked.spec,celo_governance,,Yes,,"Functionality: Determine if an account is currently voting by checking if they've upvoted a proposal that is queued (but not expired) or if their most recent referendum proposal is in the referendum stage, and return true if either condition is met." 29e34d2da68f8eb527ac2d29a4ac129f,812 | 814 | 816,rule,no_double_upvote,80,97,getUpvotedProposal | upvote | getUpvoteRecord,"rule no_double_upvote(uint256 p, address u) { uint256 _upvotes = getUpvotes(p); uint256 _usersUpvotedProposal = getUpvotedProposal(u); env e; require e.msg.sender == u && u != 0; // note that the sender is the signer here // the invariant is upvoteInv, defined here } definition upvoteInv(uint p, uint w) returns bool = (p != 0 && w > 0) || (p == 0 && w == 0) ; require accounts.voteSignerToAccount(u) == u; uint256 lesser; uint256 greater; upvote(e, p, lesser, greater); uint256 upvotes_ = getUpvotes(p); uint voteRecordP; uint voteRecordW; voteRecordP, voteRecordW = getUpvoteRecord(u); require upvoteInv(voteRecordP, voteRecordW); assert _usersUpvotedProposal == p => upvotes_ <= _upvotes, ""Upvotes increased from ${_upvotes} to ${upvotes_} even though upvoted by user $u who already upvoted $p""; } ","getUpvotedProposal (Lines 43-48), | upvote (Lines 530-558), | getUpvoteRecord (Lines 1161-1164), "," function getUpvotedProposal(address account) public view returns (uint256) { uint256 proposalId; uint256 weight; (proposalId, weight) = this.getUpvoteRecord(account); return proposalId; } | function upvote(uint256 proposalId, uint256 lesser, uint256 greater) external nonReentrant returns (bool) { dequeueProposalsIfReady(); // If acting on an expired proposal, expire the proposal and take no action. if (removeIfQueuedAndExpired(proposalId)) { return false; } address account = getAccounts().voteSignerToAccount(msg.sender); Voter storage voter = voters[account]; removeIfQueuedAndExpired(voter.upvote.proposalId); // We can upvote a proposal in the queue if we're not already upvoting a proposal in the queue. uint256 weight = getLockedGold().getAccountTotalLockedGold(account); require(weight > 0, ""cannot upvote without locking gold""); require(queue.contains(proposalId), ""cannot upvote a proposal not in the queue""); require( voter.upvote.proposalId == 0 || !queue.contains(voter.upvote.proposalId), ""cannot upvote more than one queued proposal"" ); uint256 upvotes = queue.getValue(proposalId).add(weight); queue.update(proposalId, upvotes, lesser, greater); voter.upvote = UpvoteRecord(proposalId, weight); emit ProposalUpvoted(proposalId, account, weight); return true; } | function getUpvoteRecord(address account) external view returns (uint256, uint256) { UpvoteRecord memory upvoteRecord = voters[account].upvote; return (upvoteRecord.proposalId, upvoteRecord.weight); } ",./celo_governance/specs/governance.spec,celo_governance,,Yes,,"Functionality: Allow users to upvote proposals for consideration by locking their gold. This process includes validating user vote weight, checking proposal status, ensuring a single queued proposal upvote per user, updating the proposal queue with increased upvotes, and recording the user's upvote with their locked gold weight." e0e25c2eff6e77e4aa64ef1b150b6797,719 | 720,rule,noWithdrawBeforeUnlocking,128,150,pendingWithdrawalsNotFull | unlock,"rule noWithdrawBeforeUnlocking(address account, uint256 value, method f) { // We must make sure the length of pending withdrawals is not MAX_UINT, since then the `push` will make the length 0. // (this should have been checked by the solidity compiler) require(sinvoke pendingWithdrawalsNotFull(account), ""Pending withdrawals are full""); // Unlock a value and add it to pending withdrawals env _e; require(_e.msg.sender == account); sinvoke unlock(_e,value); // Try to run any function - adversary's goal is to succeed in unlocking before time env eF; require(eF.block.timestamp > _e.block.timestamp); calldataarg arg; sinvoke f(eF,arg); // We check if adversary succeeded uint256 totalPendingWithdrawals_ = sinvoke getTotalPendingWithdrawals(account); assert( eF.block.timestamp < _e.block.timestamp + sinvoke getunlockingPeriod() => sinvoke getAccountNonvotingLockedGold(account) + totalPendingWithdrawals_ >= value, ""If we are before the unlock period passed, we cannot transfer the value outside the locked balance or pending balance"" ); } ","pendingWithdrawalsNotFull (Lines 47-49), | unlock (Lines 62-64), "," function pendingWithdrawalsNotFull(address account) public view returns (bool) { return balances[account].pendingWithdrawals.length.add(2) >= 2; // we can add 2 more additional elements } | function unlock(uint256 value) external { accountTotalLockedGold[msg.sender] = accountTotalLockedGold[msg.sender].sub(value); } ",./celo_governance/specs/lockedGold.spec,celo_governance,,Yes,,"Functionality: Check if an account can add at least two more pending withdrawals, returning `true` if the sum of the current number of pending withdrawals and 2 is at least 2. Subtract a specified `value` from the sender's total locked gold balance in the contract." 30c9824cebc813125783c38049e611c2,757,rule,address_can_authorize_two_addresses,125,149,authorizeSignerWithSignature,"rule address_can_authorize_two_addresses(address x, address d1, address d2, bytes32 role1, bytes32 role2) { require x != 0 && d1 != 0 && d2 != 0 && x != d1 && x != d2 && d1 != d2 && isAccount(x); env e; require e.msg.sender == x; storage init = lastStorage; // first, authorizing d2 as a validation signer should succeed uint8 v2; bytes32 r2; bytes32 s2; authorizeSignerWithSignature(e, d2, role2, v2, r2, s2); // Authorize d1 as a Vote signer (alternative execution path - start from the state where validator authorization started) uint8 v1; bytes32 r1; bytes32 s1; authorizeSignerWithSignature(e, d1, role1, v1, r1, s1) at init; // Even after authorizing d1, the authorization of d2 as a Validation signer should succeed // AuthorizedBy(d1) and AuthorizedBy(d2) should still be x assert _getAuthorizedBy(d1) == x && _getAuthorizedBy(d2) == x, ""Authorizedby should both be x""; } ","authorizeSignerWithSignature (Lines 407-417), "," function authorizeSignerWithSignature(address signer, bytes32 role, uint8 v, bytes32 r, bytes32 s) public { authorizeAddressWithRole(signer, role, v, r, s); signerAuthorizations[msg.sender][role][signer] = SignerAuthorization({ started: true, completed: true }); emit SignerAuthorized(msg.sender, signer, role); } ",./celo_governance/specs/accounts.spec,celo_governance,,Yes,,"Functionality: Authorize a signer with a specific role using their signature. This is achieved by first authorizing the address with the desired role using the signature components (v, r, s), and then marking the authorization as started and completed for the signer in the contract's state." ea4ba5e30a4c062b8d537ef09b36af6b,766,rule,cantMakeASignerForNonLegacyRoleWithoutApprovalOfSigner,309,313,authorizeSignerWithSignature,"rule cantMakeASignerForNonLegacyRoleWithoutApprovalOfSigner(method f) filtered { f -> !f.isView // the authorizeSignerWithSignature allows the signer to provide a signature to the account, so we exclude this option && f.selector != authorizeSignerWithSignature(address,bytes32,uint8,bytes32,bytes32).selector } { ","authorizeSignerWithSignature (Lines 407-417), "," function authorizeSignerWithSignature(address signer, bytes32 role, uint8 v, bytes32 r, bytes32 s) public { authorizeAddressWithRole(signer, role, v, r, s); signerAuthorizations[msg.sender][role][signer] = SignerAuthorization({ started: true, completed: true }); emit SignerAuthorized(msg.sender, signer, role); } ",./celo_governance/specs/accounts.spec,celo_governance,,Yes,,"Functionality: Authorize a signer with a specific role by utilizing a digital signature (comprised of `v`, `r`, and `s` components) via the `authorizeAddressWithRole` function. It then marks the authorization as both started and completed within a mapping, and emits a `SignerAuthorized` event." 9228f7b8b1622d2108c894af4bd773de,870 | 872 | 874,rule,constitution_change,325,328,getUpvotedProposal | upvote | getUpvoteRecord,"rule constitution_change(method f) filtered { f -> !f.isView // the invariant is upvoteInv, defined here } definition upvoteInv(uint p, uint w) returns bool = (p != 0 && w > 0) || (p == 0 && w == 0) ; rule no_double_upvote(uint256 p, address u) { uint256 _upvotes = getUpvotes(p); uint256 _usersUpvotedProposal = getUpvotedProposal(u); env e; require e.msg.sender == u && u != 0; // note that the sender is the signer here require accounts.voteSignerToAccount(u) == u; uint256 lesser; uint256 greater; upvote(e, p, lesser, greater); uint256 upvotes_ = getUpvotes(p); uint voteRecordP; uint voteRecordW; voteRecordP, voteRecordW = getUpvoteRecord(u); require upvoteInv(voteRecordP, voteRecordW); assert _usersUpvotedProposal == p => upvotes_ <= _upvotes, ""Upvotes increased from ${_upvotes} to ${upvotes_} even though upvoted by user $u who already upvoted $p""; } && f.selector != setConstitution(address,bytes4,uint256).selector } { ","getUpvotedProposal (Lines 43-48), | upvote (Lines 530-558), | getUpvoteRecord (Lines 1161-1164), "," function getUpvotedProposal(address account) public view returns (uint256) { uint256 proposalId; uint256 weight; (proposalId, weight) = this.getUpvoteRecord(account); return proposalId; } | function upvote(uint256 proposalId, uint256 lesser, uint256 greater) external nonReentrant returns (bool) { dequeueProposalsIfReady(); // If acting on an expired proposal, expire the proposal and take no action. if (removeIfQueuedAndExpired(proposalId)) { return false; } address account = getAccounts().voteSignerToAccount(msg.sender); Voter storage voter = voters[account]; removeIfQueuedAndExpired(voter.upvote.proposalId); // We can upvote a proposal in the queue if we're not already upvoting a proposal in the queue. uint256 weight = getLockedGold().getAccountTotalLockedGold(account); require(weight > 0, ""cannot upvote without locking gold""); require(queue.contains(proposalId), ""cannot upvote a proposal not in the queue""); require( voter.upvote.proposalId == 0 || !queue.contains(voter.upvote.proposalId), ""cannot upvote more than one queued proposal"" ); uint256 upvotes = queue.getValue(proposalId).add(weight); queue.update(proposalId, upvotes, lesser, greater); voter.upvote = UpvoteRecord(proposalId, weight); emit ProposalUpvoted(proposalId, account, weight); return true; } | function getUpvoteRecord(address account) external view returns (uint256, uint256) { UpvoteRecord memory upvoteRecord = voters[account].upvote; return (upvoteRecord.proposalId, upvoteRecord.weight); } ",./celo_governance/specs/governance.spec,celo_governance,,Yes,,"Functionality: Allow users to upvote governance proposals by locking gold, ensuring they can only upvote a proposal not already queued or expired. Users retrieve their current upvoted proposal's ID and weight, ensuring they cannot upvote more than one proposal in the queue simultaneously." 102ab15f7889b3b711c724608fba9f17,945,rule,executeDelegatesOnlyToAllowedAddresses,59,67,summaryInstance.checkDelegated,"rule executeDelegatesOnlyToAllowedAddresses(method f) { require summaryInstance.lenDelegated() == 0; arbitrary(f); uint pushed = summaryInstance.lenDelegated(); assert pushed <= 3, ""not expected to call execute more than 3 times""; assert summaryInstance.checkDelegated(getProxyActions()), ""not all delegated are allowed""; } ","checkDelegated (Lines 80-88), "," function checkDelegated(address allowed) external returns (bool) { for (uint i = 0 ; i < delegated.length; i++) { if (delegated[i] != allowed) { return false; } } return true; } ",./furucombo/specs/hmaker.spec,furucombo,,Yes,,"Functionality: Check if a specified address (allowed) is the only address in a predefined list (delegated). It iterates through the list and returns false at the first instance of a different address, otherwise, it returns true, indicating all addresses in the list match the specified address." fa776b5c4182a8ae091f55e1af54298c,947 | 948,rule,changesHandler,22,28,register | unregister,"rule changesHandler(method f, address handler) { bytes32 _regState; bytes32 regState_; handlerTransition(handler, _regState, regState_, f); assert (f.selector != register(address,bytes32).selector && f.selector != unregister(address).selector) => _regState == regState_, ""method unexpectedly changes handler info""; } ","register (Lines 28-32), | unregister (Lines 38-43), "," function register(address registration, bytes32 info) external onlyOwner { require(registration != address(0), ""zero address""); require(adapters[registration] == bytes32(0), ""registered""); adapters[registration] = info; } | function unregister(address registration) external onlyOwner { require(registration != address(0), ""zero address""); require(adapters[registration] != bytes32(0), ""no registration""); require(adapters[registration] != DEPRECATED, ""unregistered""); adapters[registration] = DEPRECATED; } ",./furucombo/specs/registry.spec,furucombo,,Yes,,"Functionality: Register an address with specific information if it hasn't been registered yet and is not zero. Unregister a previously registered address by marking it as deprecated, ensuring the address is neither zero, unregistered, nor already marked as deprecated." 7957cf7a0c367b6d664077d0b8c77797,935,rule,transferredTokensMeanThatStackIsUpdated,476,491,summaryInstance.getEthAddress,"rule transferredTokensMeanThatStackIsUpdated(method f) { require summaryInstance.getEthAddress(currentContract) != someToken; // not an eth transfer require someToken != 0; // not an eth transfer require someToken.allowance(currentContract, summaryInstance) == 0; // to make sure we're starting clean as implied by approvedTokensAreTemporary rule approvedTokensAreTemporary(method f, address someAllowed) { require someAllowed == summaryInstance; // narrowing down uint256 allowanceBefore = someToken.allowance(currentContract, someAllowed); arbitrary(f); uint256 allowanceAfter = someToken.allowance(currentContract, someAllowed); assert allowanceBefore == 0 => allowanceAfter == 0, ""Allowances must be nullified""; } uint256 balanceBefore = someToken.balanceOf(currentContract); uint256 stackLengthBefore = getStackLength(); require stackLengthBefore < MAX_UINT256() - STACK_INCREASE_BOUND(); // see stackLengthIncreaseIsBounded rule stackLengthIncreaseIsBounded(method f) { uint256 stackLengthAfter = getStackLength(); assert stackLengthAfter <= stackLengthBefore + STACK_INCREASE_BOUND(), ""Found a way to increase stack length by more than 1 million""; uint256 balanceAfter = someToken.balanceOf(currentContract); assert (balanceAfter > balanceBefore) => stackLengthAfter > stackLengthBefore, ""must push an entry to postprocess stack if transferring funds into proxy which are not eth""; ","getEthAddress (Lines 30-39), "," function getEthAddress(address handler) external returns (address) { // either the handler defines a ETH_ADDRESS function or it does not. If it does not then just return address(0) address eth = address(0); try WithEthAddress(handler).ETH_ADDRESS() returns (address x) { eth = x; } catch { eth = address(0); } return eth; } ",./furucombo/specs/proxy.spec,furucombo,,Yes,,"Functionality: Attempt to retrieve an Ethereum (ETH) address from a specified contract (`handler`). If the `handler` contract implements a function `ETH_ADDRESS` that returns an address, return this address. Otherwise, return the zero address (address(0)) to indicate failure or non-existence of such a function." 3c7675cafe2f8abdcb3651dc5a393e8a,943,rule,is,568,577,registry.isValidCaller,"// the rule is usually expected to fail, because handler functions do not check who calls them (it's usually the sender via the proxy delegatecall). rule onlyValidCaller(method f) { env e; bool isGoodCaller = registry.isValidCaller(e.msg.sender); calldataarg arg; f@withrevert(e, arg); bool succeeded = !lastReverted; assert !isGoodCaller => !succeeded, ""function can be called even if the sender is not an allowed caller""; } ","isValidCaller (Lines 133-137), "," function isValidCaller( address caller ) external view override returns (bool) { return callers[caller] != 0 && callers[caller] != DEPRECATED; } ",./furucombo/specs/proxy.spec,furucombo,,Yes,,"Functionality: Check whether the provided address (caller) is authorized by verifying if its associated value in the `callers` mapping is neither zero nor marked as deprecated (indicated by the `DEPRECATED` constant), and return a boolean indication of its validity." 02c2786803dc8418e6fad9709e745c2f,931,rule,nonExecutableWithInitializedSender,461,473,registry.isValidCaller,"rule nonExecutableWithInitializedSender(method f) { require !isHandler(); // the rule is usually expected to fail, because handler functions are payable. rule holdNoEth(method f) { require ethBalance(currentContract) == 0; arbitrary(f); assert ethBalance(currentContract) == 0; } require !f.isView; // only non-view functions, that may modify the state, are interesting. require getSender() != 0; // sender is initialized env e; bool isGoodCaller = registry.isValidCaller(e, e.msg.sender); calldataarg arg; f@withrevert(e, arg); assert (f.isFallback && !isGoodCaller) => lastReverted; // only valid callers should execute in fallback successfully (would violate since certora prover cannot distinguish between receive and fallback for now) assert (f.selector == execs(address[],bytes32[],bytes[]).selector && e.msg.sender != currentContract) => lastReverted; // `execs` is allowed to be called by proxy itself after initialized assert (!f.isFallback && f.selector != execs(address[],bytes32[],bytes[]).selector) => lastReverted; // all non-view functions other than above ones should revert if sender is already initialized ","isValidCaller (Lines 133-137), "," function isValidCaller( address caller ) external view override returns (bool) { return callers[caller] != 0 && callers[caller] != DEPRECATED; } ",./furucombo/specs/proxy.spec,furucombo,,Yes,,"Functionality: Check if a caller address is valid by verifying that its corresponding value in the 'callers' mapping is neither zero nor marked as 'DEPRECATED'. Return true if the caller is valid, otherwise, return false. This ensures only authorized or non-deprecated callers can proceed." 466147d681a7443c369915dd2914ed64,915,rule,noOverwrite,370,381,postProcess,"rule noOverwrite(method f, uint slot) { require getStackLength() < MAX_UINT256() - STACK_INCREASE_BOUND(); // see stackLengthIncreaseIsBounded rule stackLengthIncreaseIsBounded(method f) { uint256 stackLengthBefore = getStackLength(); arbitrary(f); uint256 stackLengthAfter = getStackLength(); assert stackLengthAfter <= stackLengthBefore + STACK_INCREASE_BOUND(), ""Found a way to increase stack length by more than 1 million""; } uint oldValue = getSlot(slot); uint newValue = getSlot(slot); // slot 0 is stack length, postProcess() may nullify it and it's fine, and execs() can increase it // the rule is usually expected to fail, because handler functions are payable. rule holdNoEth(method f) { require ethBalance(currentContract) == 0; assert ethBalance(currentContract) == 0; assert oldValue != 0 => (newValue == oldValue || slot == getStackLengthSlot()), ""Slot $slot changd during this execution""; ","postProcess (Lines 334-344), "," function postProcess() external payable override { bytes4 sig = stack.getSig(); // selector of openLockETHAndDraw(uint256,address,address,bytes32,uint256) // and openLockGemAndDraw(address,address,bytes32,uint256,uint256) if (sig == 0x5481e4a4 || sig == 0x73af24e7) { _transferCdp(uint256(stack.get())); uint256 amount = IERC20(DAI_TOKEN).balanceOf(address(this)); if (amount > 0) IERC20(DAI_TOKEN).safeTransfer(_getSender(), amount); } else revert(""Invalid post process""); } ",./furucombo/specs/proxy.spec,furucombo,,Yes,,"Functionality: Check if the invoked function matches specific signatures related to financial operations. If so, transfer a collateralized debt position (CDP) and, if any DAI tokens are available, safely transfer these tokens to the transaction initiator's address. Otherwise, revert the transaction for invalid operations." 0567294d237aab42e4368f373490459b,954,rule,unregisterHandlerIsPermanent2,72,78,unregister,"rule unregisterHandlerIsPermanent2(method f, address handler) { rule unregisterHandlerIsPermanent(method f, address handler) { env e; unregister(e, handler); callArbitrary(f); assert handlers(handler) == deprecated(), ""unexpected handler info after unregister""; } require handlers(handler) == deprecated(); ","unregister (Lines 38-43), "," function unregister(address registration) external onlyOwner { require(registration != address(0), ""zero address""); require(adapters[registration] != bytes32(0), ""no registration""); require(adapters[registration] != DEPRECATED, ""unregistered""); adapters[registration] = DEPRECATED; } ",./furucombo/specs/registry.spec,furucombo,,Yes,,"Functionality: Mark a specific registration as deprecated if it exists and is not already marked as such, ensuring that the registration address is not the zero address and has been previously registered. Only the contract owner is allowed to perform this action." 33c932d0e1757f2a8595ccd730e08402,950,rule,changesCaller,38,44,unregisterCaller,"rule changesCaller(method f, address caller) { bytes32 _regState; bytes32 regState_; callerTransition(caller, _regState, regState_, f); assert (f.selector != registerCaller(address,bytes32).selector && f.selector != unregisterCaller(address).selector) => _regState == regState_, ""method unexpectedly changes caller info""; } ","unregisterCaller (Lines 94-100), "," function unregisterCaller(address registration) external onlyOwner { require(registration != address(0), ""zero address""); require(callers[registration] != bytes32(0), ""no registration""); require(callers[registration] != DEPRECATED, ""unregistered""); callers[registration] = DEPRECATED; emit CallerUnregistered(registration); } ",./furucombo/specs/registry.spec,furucombo,,Yes,,"Functionality: Invalidate a previously registered caller's address, ensuring it's neither a zero address nor already unregistered. This action, only executable by the contract owner, marks the caller's address as deprecated within the contract's state and emits an event indicating the caller's unregistration." 32d202a5d42940d3ddd3ea1a426af50b,905,rule,executeDelegatesOnlyToAllowedAddresses,59,67,summaryInstance.checkDelegated,"rule executeDelegatesOnlyToAllowedAddresses(method f) { require summaryInstance.lenDelegated() == 0; arbitrary(f); uint pushed = summaryInstance.lenDelegated(); assert pushed <= 3, ""not expected to call execute more than 3 times""; assert summaryInstance.checkDelegated(FCOMPOUND_ACTIONS()), ""not all delegated are allowed""; } ","checkDelegated (Lines 80-88), "," function checkDelegated(address allowed) external returns (bool) { for (uint i = 0 ; i < delegated.length; i++) { if (delegated[i] != allowed) { return false; } } return true; } ",./furucombo/specs/hscompound.spec,furucombo,,Yes,,"Functionality: The given code checks if the address provided as 'allowed' is the only address recorded in the 'delegated' array. It returns true only if the 'allowed' address matches all entries in the 'delegated' array, otherwise, it returns false." fe8c987fb27105b67dce90aa69e6d968,953,rule,unregisterCallerIsPermanent,63,70,unregisterCaller,"rule unregisterCallerIsPermanent(method f, address caller) { env e; unregisterCaller(e, caller); callArbitrary(f); assert callers(caller) == deprecated(), ""unexpected caller info after unregister""; } ","unregisterCaller (Lines 94-100), "," function unregisterCaller(address registration) external onlyOwner { require(registration != address(0), ""zero address""); require(callers[registration] != bytes32(0), ""no registration""); require(callers[registration] != DEPRECATED, ""unregistered""); callers[registration] = DEPRECATED; emit CallerUnregistered(registration); } ",./furucombo/specs/registry.spec,furucombo,,Yes,,"Functionality: Mark a caller as unregistered in a smart contract, ensuring it's not the zero address, already registered, and not previously unregistered. It then sets the caller's status to 'DEPRECATED' and emits a 'CallerUnregistered' event with the caller's address." ec94157365af0a8d8ad4ed57ec684886,1485 | 1486,rule,unexpectedBalanceChange,87,93,safeTransferFrom | safeBatchTransferFrom,"rule unexpectedBalanceChange(method f, env e) filtered { f -> f.selector != safeTransferFrom(address, address, uint256, uint256, bytes).selector && f.selector != safeBatchTransferFrom(address, address, uint256[], uint256[], bytes).selector && f.selector != mint(address, uint256, uint256, bytes).selector && f.selector != mintBatch(address, uint256[], uint256[], bytes).selector && f.selector != burn(address, uint256, uint256).selector && f.selector != burnBatch(address, uint256[], uint256[]).selector } { ","safeTransferFrom (Lines 117-129), | safeBatchTransferFrom (Lines 134-146), "," function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeTransferFrom(from, to, id, amount, data); } | function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: Enable the secure transfer of single or multiple token types from one address to another, contingent on the initiator being the token holder or an approved delegate, through checks ensuring authorization before leveraging internal functions that actually facilitate the transfer process." f756903d59f2aed80775848b57232223,1533 | 1534 | 1535,rule,singleTokenSafeTransferFromSafeBatchTransferFromEquivalence,779,806,balanceOf | safeTransferFrom | safeBatchTransferFrom,"rule singleTokenSafeTransferFromSafeBatchTransferFromEquivalence { storage beforeTransfer = lastStorage; env e; address holder; address recipient; uint256 token; uint256 transferAmount; bytes data; uint256[] tokens; uint256[] transferAmounts; mathint holderStartingBalance = balanceOf(holder, token); mathint recipientStartingBalance = balanceOf(recipient, token); require tokens.length == 1; require transferAmounts.length == 1; require tokens[0] == token; require transferAmounts[0] == transferAmount; // transferring via safeTransferFrom safeTransferFrom(e, holder, recipient, token, transferAmount, data) at beforeTransfer; mathint holderSafeTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); mathint recipientSafeTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; // transferring via safeBatchTransferFrom safeBatchTransferFrom(e, holder, recipient, tokens, transferAmounts, data) at beforeTransfer; mathint holderSafeBatchTransferFromBalanceChange = holderStartingBalance - balanceOf(holder, token); mathint recipientSafeBatchTransferFromBalanceChange = balanceOf(recipient, token) - recipientStartingBalance; assert holderSafeTransferFromBalanceChange == holderSafeBatchTransferFromBalanceChange && recipientSafeTransferFromBalanceChange == recipientSafeBatchTransferFromBalanceChange, ""Transferring a single token via safeTransferFrom or safeBatchTransferFrom must be equivalent""; } ","balanceOf (Lines 70-73), | safeTransferFrom (Lines 117-129), | safeBatchTransferFrom (Lines 134-146), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } | function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeTransferFrom(from, to, id, amount, data); } | function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: Validate token ownership or approval before executing token transfers in the ERC1155 standard. Specifically, it checks for valid non-zero addresses, owner or approved caller status, then performs either a single or batch transfer of tokens, including accompanying data." d37c0dd25e371ea38fa91d5158d92698,1520,rule,cantMintBatchOtherBalances,574,595,balanceOf,"rule cantMintBatchOtherBalances(env e){ address to; uint256 id1; uint256 id2; uint256 id3; uint256[] ids; uint256[] amounts; address other; bytes data; uint256 otherBalanceBefore1 = balanceOf(other, id1); uint256 otherBalanceBefore2 = balanceOf(other, id2); uint256 otherBalanceBefore3 = balanceOf(other, id3); mintBatch(e, to, ids, amounts, data); uint256 otherBalanceAfter1 = balanceOf(other, id1); uint256 otherBalanceAfter2 = balanceOf(other, id2); uint256 otherBalanceAfter3 = balanceOf(other, id3); assert other != to => (otherBalanceBefore1 == otherBalanceAfter1 && otherBalanceBefore2 == otherBalanceAfter2 && otherBalanceBefore3 == otherBalanceAfter3) , ""I like to see your money disappearing""; } ","balanceOf (Lines 70-73), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: Verify the validity of an account address, ensuring it is not a zero address, to prevent operations involving invalid ownership. Subsequently, retrieve and return the balance of a specific token, identified by `id`, owned by the specified `account`." ec94157365af0a8d8ad4ed57ec684886,1485 | 1486,rule,unexpectedBalanceChange,87,93,safeTransferFrom | safeBatchTransferFrom,"rule unexpectedBalanceChange(method f, env e) filtered { f -> f.selector != safeTransferFrom(address, address, uint256, uint256, bytes).selector && f.selector != safeBatchTransferFrom(address, address, uint256[], uint256[], bytes).selector && f.selector != mint(address, uint256, uint256, bytes).selector && f.selector != mintBatch(address, uint256[], uint256[], bytes).selector && f.selector != burn(address, uint256, uint256).selector && f.selector != burnBatch(address, uint256[], uint256[]).selector } { ","safeTransferFrom (Lines 117-129), | safeBatchTransferFrom (Lines 134-146), "," function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeTransferFrom(from, to, id, amount, data); } | function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: The code defines two functions for transferring ERC1155 tokens. The `safeTransferFrom` function allows for the transfer of a single token type, while the `safeBatchTransferFrom` function enables the transfer of multiple token types in one transaction, enforcing ownership or approval verification before proceeding." e5c1003fa926635a164cb5ca90a76138,1647 | 1651,rule,delegate_no_frontrunning,236,271,balanceOf | _delegate,"rule delegate_no_frontrunning(method f) { env e; calldataarg args; address delegator; address delegatee; address third; address other; // converted from an invariant to a rule to slightly change the logic // if the fromBlock is the same as before, then the number of checkpoints stays the same // however if the fromBlock is new than the number of checkpoints increases // passes, fails rule sanity because tautology check seems to be bugged // passes + rule sanity invariant fromBlock_greaterThanEq_pos(address account, uint32 pos) ckptFromBlock(account, pos) >= pos filtered { f -> !f.isView } rule unique_checkpoints_rule(method f) { address account; uint32 num_ckpts_ = numCheckpoints(account); uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); f(e, args); uint32 _num_ckpts = numCheckpoints(account); uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, ""same fromBlock, new checkpoint""; } require numCheckpoints(delegatee) < 1000000; require numCheckpoints(third) < 1000000; uint256 delegator_bal = balanceOf(e, delegator); uint256 delegatee_votes_ = getVotes(delegatee); uint256 third_votes_ = getVotes(third); uint256 other_votes_ = getVotes(other); require delegates(delegator) == third; require third != delegatee; require other != third; require other != delegatee; require delegatee != 0x0; _delegate(e, delegator, delegatee); uint256 _delegatee_votes = getVotes(delegatee); uint256 _third_votes = getVotes(third); uint256 _other_votes = getVotes(other); // previous delegatee loses all of their votes // delegatee gains that many votes // third loses any votes delegated to them assert _delegatee_votes == delegatee_votes_ + delegator_bal, ""delegatee did not receive votes""; assert third != 0 => _third_votes == third_votes_ - delegator_bal, ""votes not removed from third""; assert other_votes_ == _other_votes, ""delegate not contained""; ","balanceOf (Lines 70-73), | _delegate (Lines 127-133), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } | function _delegate(address account, address delegatee) internal virtual { address oldDelegate = delegates(account); _delegation[account] = delegatee; emit DelegateChanged(account, oldDelegate, delegatee); _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); } ",./openzepplin/specs/ERC20Votes.spec,openzepplin,,Yes,,"Functionality: Check the balance of a specific token (identified by `id`) for a given account in an ERC1155 contract while ensuring the account isn't a zero address. Additionally, manage the delegation of voting power from one account to another, updating their delegation status and redistributing voting units accordingly." 41259f74d90666460170f6c3f7cde3b8,1497 | 1498,rule,transferBalanceIncreaseEffect,281,295,balanceOf | safeTransferFrom,"rule transferBalanceIncreaseEffect(env e){ address from; address to; address other; uint256 id; uint256 amount; bytes data; require from != other; uint256 otherBalanceBefore = balanceOf(other, id); safeTransferFrom(e, from, to, id, amount, data); uint256 otherBalanceAfter = balanceOf(other, id); assert other != to => otherBalanceBefore == otherBalanceAfter, ""Don't touch my money!""; } ","balanceOf (Lines 70-73), | safeTransferFrom (Lines 117-129), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } | function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), ""ERC1155: caller is not token owner nor approved"" ); _safeTransferFrom(from, to, id, amount, data); } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: Check an account's balance for a specific token ID and safely transfer a specified amount of a token from one account to another, ensuring the sender is authorized through ownership or approval, and validating address integrity to prevent transfers to the zero address." 7a0e8380f35b696c56504691cf006d5a,1494,rule,cannotTransferMoreBatch,238,257,balanceOf,"rule cannotTransferMoreBatch(env e){ address from; address to; uint256[] ids; uint256[] amounts; bytes data; uint256 idToCheck1; uint256 amountToCheck1; uint256 idToCheck2; uint256 amountToCheck2; uint256 idToCheck3; uint256 amountToCheck3; uint256 balanceBefore1 = balanceOf(from, idToCheck1); uint256 balanceBefore2 = balanceOf(from, idToCheck2); uint256 balanceBefore3 = balanceOf(from, idToCheck3); require ids.length == 3; require amounts.length == 3; require ids[0] == idToCheck1; require amounts[0] == amountToCheck1; require ids[1] == idToCheck2; require amounts[1] == amountToCheck2; require ids[2] == idToCheck3; require amounts[2] == amountToCheck3; safeBatchTransferFrom@withrevert(e, from, to, ids, amounts, data); assert (amountToCheck1 > balanceBefore1 || amountToCheck2 > balanceBefore2 || amountToCheck3 > balanceBefore3) => lastReverted, ""Achtung! Scammer!""; } ","balanceOf (Lines 70-73), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } ",./openzepplin/specs/ERC1155.spec,openzepplin,,Yes,,"Functionality: Verify that the specified account address is not a zero address to ensure it's a valid owner, then retrieve and return the balance of a specific token (identified by `id`) that is held by the given account from the internal `_balances` mapping." a9ad2fb52b9f699a195da63275d122d9,1688,rule,scheduleCheck,169,179,schedule,"rule scheduleCheck(method f, env e){ bytes32 id; address target; uint256 value; bytes data ;bytes32 predecessor; bytes32 salt; uint256 delay; hashIdCorrelation(id, target, value, data, predecessor, salt); schedule(e, target, value, data, predecessor, salt, delay); assert getTimestamp(id) == to_uint256(e.block.timestamp + delay), ""Time doesn't obey to mortal souls""; } ","schedule (Lines 214-225), "," function schedule( address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay ) public virtual onlyRole(PROPOSER_ROLE) { bytes32 id = hashOperation(target, value, data, predecessor, salt); _schedule(id, delay); emit CallScheduled(id, 0, target, value, data, predecessor, delay); } ",./openzepplin/specs/TimelockController.spec,openzepplin,,Yes,,"Functionality: Schedule a deferred transaction with a specific delay, targeting a certain address with data and value, only if the caller has the required proposer role. It calculates a unique operation ID based on the target, value, data, predecessor, and salt, then schedules the operation and emits a corresponding event." 5482195dc811971ad08ec18f89a78ecd,1599 | 1603,rule,sanity,181,198,balanceOf | _delegate,"// passes + rule sanity rule delegatee_receives_votes() { env e; address delegator; address delegatee; // converted from an invariant to a rule to slightly change the logic // if the fromBlock is the same as before, then the number of checkpoints stays the same // however if the fromBlock is new than the number of checkpoints increases // passes, fails rule sanity because tautology check seems to be bugged // passes but fails rule sanity from hash on delegate by sig invariant timestamp_constrains_fromBlock(address account, uint32 index, env e) ckptFromBlock(account, index) < e.block.number filtered { f -> !f.isView } rule unique_checkpoints_rule(method f) { env e; calldataarg args; address account; uint32 num_ckpts_ = numCheckpoints(account); uint32 fromBlock_ = num_ckpts_ == 0 ? 0 : ckptFromBlock(account, num_ckpts_ - 1); f(e, args); uint32 _num_ckpts = numCheckpoints(account); uint32 _fromBlock = _num_ckpts == 0 ? 0 : ckptFromBlock(account, _num_ckpts - 1); assert fromBlock_ == _fromBlock => num_ckpts_ == _num_ckpts || _num_ckpts == 1, ""same fromBlock, new checkpoint""; // this assert fails consistently // assert !doubleFromBlock(account) => ckpts_ != _ckpts, ""new fromBlock but total checkpoints not being increased""; } require numCheckpoints(delegatee) < 1000000; require delegates(delegator) != delegatee; require delegatee != 0x0; uint256 delegator_bal = balanceOf(e, delegator); uint256 votes_= getVotes(delegatee); _delegate(e, delegator, delegatee); uint256 _votes = getVotes(delegatee); assert _votes == votes_ + delegator_bal, ""delegatee did not receive votes""; ","balanceOf (Lines 70-73), | _delegate (Lines 127-133), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } | function _delegate(address account, address delegatee) internal virtual { address oldDelegate = delegates(account); _delegation[account] = delegatee; emit DelegateChanged(account, oldDelegate, delegatee); _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); } ",./openzepplin/specs/ERC721Votes.spec,openzepplin,,Yes,,"Functionality: Retrieve the balance of a specific token ID for a given account, ensuring the account address is not zero, and internally assign a delegate to an account, recording the delegation change and adjusting the vote counts of the old and new delegates based on the delegator's voting power." 3b46de187a09f7f67a6debd54638f799,1474,rule,burnBatchOnEmptyArraysChangesNothing,142,160,balanceOf,"rule burnBatchOnEmptyArraysChangesNothing { uint256 token; address nonHolderA; address nonHolderB; uint256 startingBalance = balanceOf(nonHolderA, token); bool startingPermission = isApprovedForAll(nonHolderA, nonHolderB); env e; address holder; uint256[] noTokens; uint256[] noBurnAmounts; require noTokens.length == 0; require noBurnAmounts.length == 0; burnBatch(e, holder, noTokens, noBurnAmounts); uint256 endingBalance = balanceOf(nonHolderA, token); bool endingPermission = isApprovedForAll(nonHolderA, nonHolderB); assert startingBalance == endingBalance, ""burnBatch must not change token balances if passed empty arrays""; assert startingPermission == endingPermission, ""burnBatch must not change account permissions if passed empty arrays""; } ","balanceOf (Lines 70-73), "," function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), ""ERC1155: address zero is not a valid owner""); return _balances[id][account]; } ",./openzepplin/specs/ERC1155Burnable.spec,openzepplin,,Yes,,"Functionality: Retrieve the balance of a specific token (identified by `id`) owned by a given account. Ensure the account address is not the zero address, indicating it must be a valid owner, before returning the account's token balance from the internal balances mapping." 650a8ad637770c08c9dff6bd1ba25058,1930 | 1931 | 1932,rule,validBalanceTotalShort,190,215,mintOtokenA | redeemA | settleVault,"rule validBalanceTotalShort(address owner, uint256 vaultId, uint256 index, address secondAddress, address oToken, method f, uint256 amount) description ""$f breaks the validity of stored balance of short asset"" { links(); env e; calldataarg arg; require oToken == shortOtoken; require !isVaultExpired(e, owner, vaultId); require getVaultShortOtoken(owner, vaultId, index) == oToken; uint256 shortVaultBefore = getVaultShortAmount(owner, vaultId, index); uint256 supplyBefore = shortOtoken.totalSupply(); // only test the cases before expiry if (f.selector == settleVault(address,uint256,address).selector) || (f.selector == redeemA(address,uint256).selector) { assert true; } else if (f.selector == mintOtokenA(address,uint256,address,uint256,uint256).selector) { // ignore the case where you can mint otokens directly to the margin pool require (secondAddress != pool); sinvoke mintOtokenA(e, owner, vaultId, secondAddress, index, amount); } else { callFunctionWithParameters(f, owner, vaultId, index); } uint256 shortVaultAfter = getVaultShortAmount(owner, vaultId, index); uint256 supplyAfter = shortOtoken.totalSupply(); assert shortVaultBefore != shortVaultAfter => (supplyAfter - supplyBefore == shortVaultAfter - shortVaultBefore); assert supplyAfter != supplyBefore => ( supplyAfter - supplyBefore == shortVaultAfter - shortVaultBefore); } ","mintOtokenA (Lines 268-285), | redeemA (Lines 344-347), | settleVault (Lines 360-367), "," function mintOtokenA( address owner, uint256 vaultId, address to, uint256 index, uint256 amount ) external { require(smallVault(owner, vaultId, 1)); Actions.MintArgs memory args = Actions.MintArgs({ owner: owner, vaultId: vaultId, to: to, otoken: anOtokenA, index: index, amount: amount }); _mintOtoken(args); } | function redeemA(address receiver, uint256 amount) external { Actions.RedeemArgs memory args = Actions.RedeemArgs({receiver: receiver, otoken: anOtokenA, amount: amount}); _redeem(args); } | function settleVault( address owner, uint256 vaultId, address to ) external { Actions.SettleVaultArgs memory args = Actions.SettleVaultArgs({owner: owner, vaultId: vaultId, to: to}); _settleVault(args); } ",./opyn_gamma_protocol/specs/ValidBalances.spec,opyn_gamma_protocol,,Yes,,"Functionality: The provided code implements three operations related to managing tokens and vaults within a finance-oriented smart contract: minting a specific type of oToken (`anOtokenA`) into a designated account, redeeming said oToken for an underlying asset, and settling a vault by transferring its contents or obligations to another account." bde417a50b4ad4f79fb7d8b4809fca07,1801 | 1802 | 1803 | 1808,rule,integrityOfAddShort,188,196,addShort | addLong | addCollateral | totalShortAmount,"rule integrityOfAddShort(address shortOtoken, uint256 x, uint256 index) { requireInvariant validVault(); invariant validVault() longAmountLength() == longOtokensLength() && shortAmountLength() == shortOtokensLength() && collateralAmountLength() == collateralAssetsLength() invariant validEntityIndex(uint256 index) (getLongAmount(index) > 0 <=> getLongOtoken(index) != 0) && (getShortAmount(index) > 0 <=> getShortOtoken(index) != 0) && (getCollateralAmount(index) > 0 <=> getCollateralAsset(index) != 0) preserved addLong(address asset, uint256 amt, uint256 _index) { requireInvariant validEntityIndex(_index); requireInvariant validEntityIndex(index); require asset != ADDRESSZERO(); } preserved addShort(address asset, uint256 amt, uint256 _index) preserved addCollateral(address asset, uint256 amt, uint256 _index) } require shortOtoken != ADDRESSZERO(); uint256 shortAmountBefore = totalShortAmount(); sinvoke addShort(shortOtoken, x, index); assert totalShortAmount() == shortAmountBefore + x && getShortOtoken(index) == shortOtoken, ""integrity break of addShort""; ","addShort (Lines 57-78), | addLong (Lines 112-133), | addCollateral (Lines 167-188), | totalShortAmount (Lines 91-97), "," function addShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V1""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.shortOtokens.length) && (_index == _vault.shortAmounts.length)) { _vault.shortOtokens.push(_shortOtoken); _vault.shortAmounts.push(_amount); } else { require((_index < _vault.shortOtokens.length) && (_index < _vault.shortAmounts.length), ""V2""); address existingShort = _vault.shortOtokens[_index]; require((existingShort == _shortOtoken) || (existingShort == address(0)), ""V3""); _vault.shortAmounts[_index] = _vault.shortAmounts[_index].add(_amount); _vault.shortOtokens[_index] = _shortOtoken; } } | function addLong( Vault storage _vault, address _longOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V4""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.longOtokens.length) && (_index == _vault.longAmounts.length)) { _vault.longOtokens.push(_longOtoken); _vault.longAmounts.push(_amount); } else { require((_index < _vault.longOtokens.length) && (_index < _vault.longAmounts.length), ""V5""); address existingLong = _vault.longOtokens[_index]; require((existingLong == _longOtoken) || (existingLong == address(0)), ""V6""); _vault.longAmounts[_index] = _vault.longAmounts[_index].add(_amount); _vault.longOtokens[_index] = _longOtoken; } } | function addCollateral( Vault storage _vault, address _collateralAsset, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V7""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.collateralAssets.length) && (_index == _vault.collateralAmounts.length)) { _vault.collateralAssets.push(_collateralAsset); _vault.collateralAmounts.push(_amount); } else { require((_index < _vault.collateralAssets.length) && (_index < _vault.collateralAmounts.length), ""V8""); address existingCollateral = _vault.collateralAssets[_index]; require((existingCollateral == _collateralAsset) || (existingCollateral == address(0)), ""V9""); _vault.collateralAmounts[_index] = _vault.collateralAmounts[_index].add(_amount); _vault.collateralAssets[_index] = _collateralAsset; } } | function totalShortAmount() external returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < vault.shortAmounts.length; i++) { total = total.add(vault.shortAmounts[i]); } return total; } ",./opyn_gamma_protocol/specs/MarginVault.spec,opyn_gamma_protocol,,Yes,,"Functionality: Add assets (long, short, or collateral) to a vault at a specified index, ensuring valid amounts and indices are provided, then potentially update existing entries or append new ones. Also, calculate the total amount of short positions in the vault." 3ad2a8378261e379ed62392d65de0601,1817 | 1818 | 1819 | 1824,rule,additiveAddShort,201,220,addShort | addLong | addCollateral | totalShortAmount,"rule additiveAddShort(address shortOtoken, uint256 x, uint256 y, uint256 index) { requireInvariant validVault(); invariant validVault() longAmountLength() == longOtokensLength() && shortAmountLength() == shortOtokensLength() && collateralAmountLength() == collateralAssetsLength() invariant validEntityIndex(uint256 index) (getLongAmount(index) > 0 <=> getLongOtoken(index) != 0) && (getShortAmount(index) > 0 <=> getShortOtoken(index) != 0) && (getCollateralAmount(index) > 0 <=> getCollateralAsset(index) != 0) preserved addLong(address asset, uint256 amt, uint256 _index) { requireInvariant validEntityIndex(_index); requireInvariant validEntityIndex(index); require asset != ADDRESSZERO(); } preserved addShort(address asset, uint256 amt, uint256 _index) preserved addCollateral(address asset, uint256 amt, uint256 _index) } require shortOtoken != ADDRESSZERO(); require index < MAXINT(); // no violation when limiting index require x > 0 && y > 0 ; uint256 t = x + y ; require( t >= x && t >= y); //no overflow storage initialStorage = lastStorage; invoke addShort(shortOtoken, x, index); bool call1 = !lastReverted; invoke addShort(shortOtoken, y, index); bool call2 = !lastReverted; uint256 shortAmountScenario1 = totalShortAmount(); invoke addShort(shortOtoken, t, index) at initialStorage; bool call3 = !lastReverted; uint256 shortAmountScenario2 = totalShortAmount(); assert (call1 && call2) <=> call3, ""addShort is not additive, one scenario reverts"" ; assert call3 => shortAmountScenario1 == shortAmountScenario2, ""addShort is not additive"" ; ","addShort (Lines 57-78), | addLong (Lines 112-133), | addCollateral (Lines 167-188), | totalShortAmount (Lines 91-97), "," function addShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V1""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.shortOtokens.length) && (_index == _vault.shortAmounts.length)) { _vault.shortOtokens.push(_shortOtoken); _vault.shortAmounts.push(_amount); } else { require((_index < _vault.shortOtokens.length) && (_index < _vault.shortAmounts.length), ""V2""); address existingShort = _vault.shortOtokens[_index]; require((existingShort == _shortOtoken) || (existingShort == address(0)), ""V3""); _vault.shortAmounts[_index] = _vault.shortAmounts[_index].add(_amount); _vault.shortOtokens[_index] = _shortOtoken; } } | function addLong( Vault storage _vault, address _longOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V4""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.longOtokens.length) && (_index == _vault.longAmounts.length)) { _vault.longOtokens.push(_longOtoken); _vault.longAmounts.push(_amount); } else { require((_index < _vault.longOtokens.length) && (_index < _vault.longAmounts.length), ""V5""); address existingLong = _vault.longOtokens[_index]; require((existingLong == _longOtoken) || (existingLong == address(0)), ""V6""); _vault.longAmounts[_index] = _vault.longAmounts[_index].add(_amount); _vault.longOtokens[_index] = _longOtoken; } } | function addCollateral( Vault storage _vault, address _collateralAsset, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V7""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.collateralAssets.length) && (_index == _vault.collateralAmounts.length)) { _vault.collateralAssets.push(_collateralAsset); _vault.collateralAmounts.push(_amount); } else { require((_index < _vault.collateralAssets.length) && (_index < _vault.collateralAmounts.length), ""V8""); address existingCollateral = _vault.collateralAssets[_index]; require((existingCollateral == _collateralAsset) || (existingCollateral == address(0)), ""V9""); _vault.collateralAmounts[_index] = _vault.collateralAmounts[_index].add(_amount); _vault.collateralAssets[_index] = _collateralAsset; } } | function totalShortAmount() external returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < vault.shortAmounts.length; i++) { total = total.add(vault.shortAmounts[i]); } return total; } ",./opyn_gamma_protocol/specs/MarginVault.spec,opyn_gamma_protocol,,Yes,,"Functionality: Manage the composition of a financial vault by adding short, long positions, and collateral. It ensures positions and collateral are validly indexed and amounts are non-zero before updating or adding them. Additionally, it calculates the total amount of short positions within the vault." bac57955ecb800bfe75fc177616b4b39,1967 | 1968,rule,settleVault,52,72,settleVault | getProceed,"rule settleVault (address owner, uint256 vaultId, uint256 index, address oToken, address to, address collateral) { env e; require oToken == shortOtoken; require collateral == collateralToken; require getVaultShortOtoken(owner, vaultId, 0) == oToken; require getVaultCollateralAsset(owner, vaultId, 0) == collateral; require to != pool; uint256 supplyBefore = shortOtoken.totalSupply(); uint256 amountRemoved = getProceed(owner, vaultId); uint256 poolBalanceBefore = collateralToken.balanceOf(pool); sinvoke settleVault(e, owner, vaultId, to); uint256 supplyAfter = shortOtoken.totalSupply(); uint256 poolBalanceAfter = collateralToken.balanceOf(pool); assert supplyAfter == supplyBefore; assert poolBalanceAfter != poolBalanceBefore => (poolBalanceBefore - poolBalanceAfter == amountRemoved); } ","settleVault (Lines 360-367), | getProceed (Lines 467-475), "," function settleVault( address owner, uint256 vaultId, address to ) external { Actions.SettleVaultArgs memory args = Actions.SettleVaultArgs({owner: owner, vaultId: vaultId, to: to}); _settleVault(args); } | function getProceed(address _owner, uint256 _vaultId) external view returns (uint256) { (MarginVault.Vault memory vault, uint256 typeVault, ) = getVaultWithDetails(_owner, _vaultId); (uint256 netValue, bool isExcess) = calculator.getExcessCollateral(vault, typeVault); if (!isExcess) return 0; return netValue; } ",./opyn_gamma_protocol/specs/controller.spec,opyn_gamma_protocol,,Yes,,"Functionality: Enable the settlement of a user's vault by specifying the vault owner, the vault's ID, and the destination address for the settlement process. Additionally, calculate and return the net value of excess collateral in a vault, if any, for a given vault owned by a user." f10873924b8c60246813e7ff872ad5fd,1722 | 1723 | 1724,rule,where,71,89,addShort | addLong | addCollateral,"This is a parametic rule where f is any of the external/public functions of the contract */ rule changeToOneEntity(uint256 shortIndex, uint256 longIndex, uint256 collateralIndex, method f ) { requireInvariant validVault(); invariant validVault() longAmountLength() == longOtokensLength() && shortAmountLength() == shortOtokensLength() && collateralAmountLength() == collateralAssetsLength() invariant validEntityIndex(uint256 index) (getLongAmount(index) > 0 <=> getLongOtoken(index) != 0) && (getShortAmount(index) > 0 <=> getShortOtoken(index) != 0) && (getCollateralAmount(index) > 0 <=> getCollateralAsset(index) != 0) { preserved addLong(address asset, uint256 amt, uint256 _index) { requireInvariant validEntityIndex(_index); requireInvariant validEntityIndex(index); require asset != ADDRESSZERO(); } preserved addShort(address asset, uint256 amt, uint256 _index) preserved addCollateral(address asset, uint256 amt, uint256 _index) } uint256 longAmountBefore = getLongAmount(longIndex); uint256 shortAmountBefore = getShortAmount(shortIndex); uint256 collateralAmountBefore = getCollateralAmount(collateralIndex); calldataarg args; env e; sinvoke f(e,args); uint256 longAmountAfter = getLongAmount(longIndex); uint256 shortAmountAfter = getShortAmount(shortIndex); uint256 collateralAmountAfter = getCollateralAmount(collateralIndex); assert (longAmountBefore != longAmountAfter => (shortAmountBefore == shortAmountAfter && collateralAmountBefore == collateralAmountAfter)) && (shortAmountBefore != shortAmountAfter => (longAmountBefore == longAmountAfter && collateralAmountBefore == collateralAmountAfter)) && (collateralAmountBefore != collateralAmountAfter => (longAmountBefore == longAmountAfter && shortAmountBefore == shortAmountAfter)); ","addShort (Lines 57-78), | addLong (Lines 112-133), | addCollateral (Lines 167-188), "," function addShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V1""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.shortOtokens.length) && (_index == _vault.shortAmounts.length)) { _vault.shortOtokens.push(_shortOtoken); _vault.shortAmounts.push(_amount); } else { require((_index < _vault.shortOtokens.length) && (_index < _vault.shortAmounts.length), ""V2""); address existingShort = _vault.shortOtokens[_index]; require((existingShort == _shortOtoken) || (existingShort == address(0)), ""V3""); _vault.shortAmounts[_index] = _vault.shortAmounts[_index].add(_amount); _vault.shortOtokens[_index] = _shortOtoken; } } | function addLong( Vault storage _vault, address _longOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V4""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.longOtokens.length) && (_index == _vault.longAmounts.length)) { _vault.longOtokens.push(_longOtoken); _vault.longAmounts.push(_amount); } else { require((_index < _vault.longOtokens.length) && (_index < _vault.longAmounts.length), ""V5""); address existingLong = _vault.longOtokens[_index]; require((existingLong == _longOtoken) || (existingLong == address(0)), ""V6""); _vault.longAmounts[_index] = _vault.longAmounts[_index].add(_amount); _vault.longOtokens[_index] = _longOtoken; } } | function addCollateral( Vault storage _vault, address _collateralAsset, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V7""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.collateralAssets.length) && (_index == _vault.collateralAmounts.length)) { _vault.collateralAssets.push(_collateralAsset); _vault.collateralAmounts.push(_amount); } else { require((_index < _vault.collateralAssets.length) && (_index < _vault.collateralAmounts.length), ""V8""); address existingCollateral = _vault.collateralAssets[_index]; require((existingCollateral == _collateralAsset) || (existingCollateral == address(0)), ""V9""); _vault.collateralAmounts[_index] = _vault.collateralAmounts[_index].add(_amount); _vault.collateralAssets[_index] = _collateralAsset; } } ",./opyn_gamma_protocol/specs/MarginVault.spec,opyn_gamma_protocol,,Yes,,"Functionality: The code defines three functions to add short positions, long positions, and collateral to a structured data type called a vault. Each function validates input, checks if it's adding to an existing position or introducing a new one, and then updates the vault with the specified amount and asset type." f0f9f49ffb6aedfa59f3920098039328,1833 | 1834 | 1835 | 1836 | 1841,rule,inverseAddRemoveShort,225,232,addShort | removeShort | addLong | addCollateral | totalShortAmount,"rule inverseAddRemoveShort(address shortOtoken, uint256 x, uint256 index) { requireInvariant validVault(); invariant validVault() longAmountLength() == longOtokensLength() && shortAmountLength() == shortOtokensLength() && collateralAmountLength() == collateralAssetsLength() invariant validEntityIndex(uint256 index) (getLongAmount(index) > 0 <=> getLongOtoken(index) != 0) && (getShortAmount(index) > 0 <=> getShortOtoken(index) != 0) && (getCollateralAmount(index) > 0 <=> getCollateralAsset(index) != 0) preserved addLong(address asset, uint256 amt, uint256 _index) { requireInvariant validEntityIndex(_index); requireInvariant validEntityIndex(index); require asset != ADDRESSZERO(); } preserved addShort(address asset, uint256 amt, uint256 _index) preserved addCollateral(address asset, uint256 amt, uint256 _index) } uint256 shortAmountBefore = totalShortAmount(); sinvoke addShort(shortOtoken, x, index); invoke removeShort(shortOtoken, x, index); assert !lastReverted && totalShortAmount() == shortAmountBefore, ""removeShort is not inverse of addShort""; ","addShort (Lines 57-78), | removeShort (Lines 87-103), | addLong (Lines 112-133), | addCollateral (Lines 167-188), | totalShortAmount (Lines 91-97), "," function addShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V1""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.shortOtokens.length) && (_index == _vault.shortAmounts.length)) { _vault.shortOtokens.push(_shortOtoken); _vault.shortAmounts.push(_amount); } else { require((_index < _vault.shortOtokens.length) && (_index < _vault.shortAmounts.length), ""V2""); address existingShort = _vault.shortOtokens[_index]; require((existingShort == _shortOtoken) || (existingShort == address(0)), ""V3""); _vault.shortAmounts[_index] = _vault.shortAmounts[_index].add(_amount); _vault.shortOtokens[_index] = _shortOtoken; } } | function removeShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { // check that the removed short oToken exists in the vault at the specified index require(_index < _vault.shortOtokens.length, ""V2""); require(_vault.shortOtokens[_index] == _shortOtoken, ""V3""); uint256 newShortAmount = _vault.shortAmounts[_index].sub(_amount); if (newShortAmount == 0) { delete _vault.shortOtokens[_index]; } _vault.shortAmounts[_index] = newShortAmount; } | function addLong( Vault storage _vault, address _longOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V4""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.longOtokens.length) && (_index == _vault.longAmounts.length)) { _vault.longOtokens.push(_longOtoken); _vault.longAmounts.push(_amount); } else { require((_index < _vault.longOtokens.length) && (_index < _vault.longAmounts.length), ""V5""); address existingLong = _vault.longOtokens[_index]; require((existingLong == _longOtoken) || (existingLong == address(0)), ""V6""); _vault.longAmounts[_index] = _vault.longAmounts[_index].add(_amount); _vault.longOtokens[_index] = _longOtoken; } } | function addCollateral( Vault storage _vault, address _collateralAsset, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V7""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.collateralAssets.length) && (_index == _vault.collateralAmounts.length)) { _vault.collateralAssets.push(_collateralAsset); _vault.collateralAmounts.push(_amount); } else { require((_index < _vault.collateralAssets.length) && (_index < _vault.collateralAmounts.length), ""V8""); address existingCollateral = _vault.collateralAssets[_index]; require((existingCollateral == _collateralAsset) || (existingCollateral == address(0)), ""V9""); _vault.collateralAmounts[_index] = _vault.collateralAmounts[_index].add(_amount); _vault.collateralAssets[_index] = _collateralAsset; } } | function totalShortAmount() external returns (uint256) { uint256 total = 0; for (uint256 i = 0; i < vault.shortAmounts.length; i++) { total = total.add(vault.shortAmounts[i]); } return total; } ",./opyn_gamma_protocol/specs/MarginVault.spec,opyn_gamma_protocol,,Yes,,"Functionality: Manage the composition of a financial Vault by adding or removing short positions, long positions, and collateral assets through specific operations that validate the transaction's index and amount, ensuring adherence to the vault's current state and structure constraints." ea3d83ed976940e0ddc7cfb8b3596b17,2092 | 2093,rule,callOptionsPreExpiry,449,595,shortOtoken.havocTotalSupply | havocVault,"rule callOptionsPreExpiry( address owner, uint256 vaultId, uint256 index, address asset, uint256 _shortAmount, uint256 _longAmount, uint256 _collateral, uint256 _poolAssetBalance, uint256 _poolShortOtokenBalance, uint256 _poolLongOtokenBalance, uint256 _totalSupplyShortOtoken, uint256 _totalSupplyLongOtoken, uint256 shortPrice, uint256 longPrice, uint256 shortAmount_, uint256 longAmount_, uint256 collateral_, uint256 poolAssetBalance_, uint256 poolShortOtokenBalance_, uint256 poolLongOtokenBalance_, uint256 totalSupplyShortOtoken_, uint256 totalSupplyLongOtoken_ ) { links(); env e; require shortOtoken == getVaultShortOtoken(owner, vaultId, index); require longOtoken == getVaultLongOtoken(owner, vaultId, index); require collateralToken == getVaultCollateralAsset(owner, vaultId, index); require asset == collateralToken; require _shortAmount == getVaultShortAmount(owner, vaultId, index); require _longAmount == getVaultLongAmount(owner, vaultId, index); require _collateral == getVaultCollateralAmount(owner, vaultId, index); require _poolAssetBalance == pool.getStoredBalance(asset); require _poolShortOtokenBalance == pool.getStoredBalance(shortOtoken); require _poolLongOtokenBalance == pool.getStoredBalance(longOtoken); require _totalSupplyShortOtoken == assetTotalSupply(shortOtoken); require _totalSupplyLongOtoken == assetTotalSupply(longOtoken); require shortPrice == shortOtoken.strikePrice(); require longPrice == longOtoken.strikePrice(); // assume before expiry require !isVaultExpired(e, owner, vaultId); This rule is proven relaying on: 1. a proof with Mathemtic minValue to compute the debt a vault 2. a rule that shows that one vault is changed at a time 3. rules that show valid balances of the system with respect to vault changes Due to complexity for the SMT solver the rules is split to three cases of a valid vault: if( longAmount <= shortAmount && longAmount * longPrice <= shortAmount * shortPrice ) //case 1 debt = shortAmount * shortPrice - longAmount * longPrice ; else if ( longAmount > shortAmount_ && longPrice < shortPrice) //case 2 debt = shortAmount * (shortPrice - longPrice) ; else //case 3 debt = 0; */ rule putOptionsPreExpiryCase1StartingWithAllCases( // assume before expiry // assume no bankruptcy mathint _obligation = (_totalSupplyShortOtoken - _poolShortOtokenBalance ) * shortPrice + (_totalSupplyLongOtoken - _poolLongOtokenBalance ) * longPrice; require _poolAssetBalance >= _obligation ; // based on Mathematica proof: mathint _debt = 0; if( _longAmount <= _shortAmount && ( _longAmount * longPrice <= _shortAmount * shortPrice )) { _debt = _shortAmount * shortPrice - _longAmount * longPrice ; } else if ( _longAmount > _shortAmount && longPrice < shortPrice) { _debt = _shortAmount * (shortPrice - longPrice) ; } //assume vault is in a valid state require _collateral >= _debt ; //assume valid state based on valid balances rules require ( _totalSupplyShortOtoken >= _poolShortOtokenBalance + _shortAmount) && _totalSupplyLongOtoken >= _poolLongOtokenBalance && _poolLongOtokenBalance >= _longAmount && _poolAssetBalance >= _collateral ; //compute excess collateral of other vaults mathint _obligationWithoutThisVault = (_totalSupplyShortOtoken - _poolShortOtokenBalance - _shortAmount) * shortPrice + (_totalSupplyLongOtoken - _poolLongOtokenBalance +_longAmount ) * longPrice ; // assume excess collateral of other vaults is not cover by this vault's collateral require ( _poolAssetBalance - _collateral >= _obligationWithoutThisVault ); // simulate many operations in one step uint256 newShortAmount; uint256 newLongAmount; uint256 newcollateralAmount; uint256 newTotalSupply; sinvoke havocVault(owner, vaultId, index, newShortAmount, newLongAmount, newcollateralAmount); sinvoke pool.havocSystemBalance(longOtoken); sinvoke pool.havocSystemBalance(asset); sinvoke shortOtoken.havocTotalSupply(newTotalSupply); require shortAmount_ == getVaultShortAmount(owner, vaultId, index); require longAmount_ == getVaultLongAmount(owner, vaultId, index); require collateral_ == getVaultCollateralAmount(owner, vaultId, index); require poolAssetBalance_ == pool.getStoredBalance(asset); require poolShortOtokenBalance_ == pool.getStoredBalance(shortOtoken); require poolLongOtokenBalance_ == pool.getStoredBalance(longOtoken); require totalSupplyShortOtoken_ == assetTotalSupply(shortOtoken); require totalSupplyLongOtoken_ == assetTotalSupply(longOtoken); mathint obligation_ = (totalSupplyShortOtoken_ - poolShortOtokenBalance_ ) * shortPrice + (totalSupplyLongOtoken_ - poolLongOtokenBalance_ ) * longPrice ; //assume isValid vault case 1 require longAmount_ <= shortAmount_ && longAmount_ * longPrice <= shortAmount_ * shortPrice ; mathint debt_ = shortAmount_ * shortPrice - longAmount_ * longPrice ; require collateral_ >= debt_ ; // valid state - need to prove this as in invariant also when the vault is not valid should hold require (totalSupplyShortOtoken_ >= poolShortOtokenBalance_ + shortAmount_) && totalSupplyLongOtoken_ >= poolLongOtokenBalance_ && poolLongOtokenBalance_ >= longAmount_ && poolAssetBalance_ >= collateral_ ; // assume changes are coherent based on total required rules require collateral_ - _collateral == poolAssetBalance_- _poolAssetBalance; require longAmount_ - _longAmount == poolLongOtokenBalance_ - _poolLongOtokenBalance; require shortAmount_ - _shortAmount == totalSupplyShortOtoken_ - _totalSupplyShortOtoken; //verify no bankruptchy assert poolAssetBalance_ >= obligation_ ; } rule putOptionsPreExpiryCase2StartingWithAllCases( //assume isValid vault case 2 require longAmount_ > shortAmount_ && longPrice < shortPrice ; mathint _obligation = (_totalSupplyShortOtoken - _poolShortOtokenBalance ) + (_totalSupplyLongOtoken - _poolLongOtokenBalance ) ; require (_poolAssetBalance >= _obligation) ; mathint temp = _shortAmount - _longAmount; if temp > 0 { _debt = temp; /** * (long strike - short strike) * short amount * calculate ---------------------------------------------- * long strike */ mathint temp2 = (longPrice - shortPrice) * _shortAmount / longPrice; mathint _debt2; if temp2 > _debt { _debt2 = temp2; } else { _debt2 = _debt; require _collateral >= _debt2 ; ////assume valid state based on valid balances rules _totalSupplyLongOtoken >= _poolLongOtokenBalance && _poolAssetBalance >= _collateral; //compute excess collateral of other vaults. mathint _obligationWithoutThisVault = (_totalSupplyShortOtoken - _poolShortOtokenBalance - _shortAmount) + (_totalSupplyLongOtoken - _poolLongOtokenBalance +_longAmount ) ; //assume vault is in a vaild state mathint obligation_ = (totalSupplyShortOtoken_ - poolShortOtokenBalance_ ) + (totalSupplyLongOtoken_ - poolLongOtokenBalance_ ) ; mathint debt_ = 0; mathint temp3 = shortAmount_ - longAmount_; if temp3 > 0 { debt_ = temp3; /** * (long strike - short strike) * short amount * calculate ---------------------------------------------- * long strike */ mathint temp4 = (longPrice - shortPrice) * shortAmount_ / longPrice; mathint debt_2; if temp4 > debt_ { debt_2 = temp4; debt_2 = debt_; require ( collateral_ >= debt_2 ); // valid state totalSupplyLongOtoken_ >= poolLongOtokenBalance_ && poolLongOtokenBalance_ >= longAmount_ && poolAssetBalance_ >= collateral_ ; ","havocTotalSupply (Lines 5-7), | havocVault (Lines 24-36), "," function havocTotalSupply(uint256 newVal) public { _totalSupply = newVal; } | function havocVault( address owner, uint256 vaultId, uint256 i, uint256 newShortAmount, uint256 newLongAmount, uint256 newcollateralAmount ) external { MarginVault.Vault storage vault = cheapGetVault(owner, vaultId); vault.shortAmounts[i] = newShortAmount; vault.longAmounts[i] = newLongAmount; vault.collateralAmounts[i] = newcollateralAmount; } ",./opyn_gamma_protocol/specs/NoBankruptcy.spec,opyn_gamma_protocol,,Yes,,"Functionality: Update the total supply of a smart contract's token and modify specific properties (short amounts, long amounts, and collateral amounts) of a particular vault identified by its owner and ID within a decentralized finance (DeFi) platform or protocol." f10873924b8c60246813e7ff872ad5fd,1722 | 1723 | 1724,rule,where,71,89,addShort | addLong | addCollateral,"This is a parametic rule where f is any of the external/public functions of the contract */ rule changeToOneEntity(uint256 shortIndex, uint256 longIndex, uint256 collateralIndex, method f ) { requireInvariant validVault(); invariant validVault() longAmountLength() == longOtokensLength() && shortAmountLength() == shortOtokensLength() && collateralAmountLength() == collateralAssetsLength() invariant validEntityIndex(uint256 index) (getLongAmount(index) > 0 <=> getLongOtoken(index) != 0) && (getShortAmount(index) > 0 <=> getShortOtoken(index) != 0) && (getCollateralAmount(index) > 0 <=> getCollateralAsset(index) != 0) { preserved addLong(address asset, uint256 amt, uint256 _index) { requireInvariant validEntityIndex(_index); requireInvariant validEntityIndex(index); require asset != ADDRESSZERO(); } preserved addShort(address asset, uint256 amt, uint256 _index) preserved addCollateral(address asset, uint256 amt, uint256 _index) } uint256 longAmountBefore = getLongAmount(longIndex); uint256 shortAmountBefore = getShortAmount(shortIndex); uint256 collateralAmountBefore = getCollateralAmount(collateralIndex); calldataarg args; env e; sinvoke f(e,args); uint256 longAmountAfter = getLongAmount(longIndex); uint256 shortAmountAfter = getShortAmount(shortIndex); uint256 collateralAmountAfter = getCollateralAmount(collateralIndex); assert (longAmountBefore != longAmountAfter => (shortAmountBefore == shortAmountAfter && collateralAmountBefore == collateralAmountAfter)) && (shortAmountBefore != shortAmountAfter => (longAmountBefore == longAmountAfter && collateralAmountBefore == collateralAmountAfter)) && (collateralAmountBefore != collateralAmountAfter => (longAmountBefore == longAmountAfter && shortAmountBefore == shortAmountAfter)); ","addShort (Lines 57-78), | addLong (Lines 112-133), | addCollateral (Lines 167-188), "," function addShort( Vault storage _vault, address _shortOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V1""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.shortOtokens.length) && (_index == _vault.shortAmounts.length)) { _vault.shortOtokens.push(_shortOtoken); _vault.shortAmounts.push(_amount); } else { require((_index < _vault.shortOtokens.length) && (_index < _vault.shortAmounts.length), ""V2""); address existingShort = _vault.shortOtokens[_index]; require((existingShort == _shortOtoken) || (existingShort == address(0)), ""V3""); _vault.shortAmounts[_index] = _vault.shortAmounts[_index].add(_amount); _vault.shortOtokens[_index] = _shortOtoken; } } | function addLong( Vault storage _vault, address _longOtoken, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V4""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.longOtokens.length) && (_index == _vault.longAmounts.length)) { _vault.longOtokens.push(_longOtoken); _vault.longAmounts.push(_amount); } else { require((_index < _vault.longOtokens.length) && (_index < _vault.longAmounts.length), ""V5""); address existingLong = _vault.longOtokens[_index]; require((existingLong == _longOtoken) || (existingLong == address(0)), ""V6""); _vault.longAmounts[_index] = _vault.longAmounts[_index].add(_amount); _vault.longOtokens[_index] = _longOtoken; } } | function addCollateral( Vault storage _vault, address _collateralAsset, uint256 _amount, uint256 _index ) external { require(_amount > 0, ""V7""); // valid indexes in any array are between 0 and array.length - 1. // if adding an amount to an preexisting short oToken, check that _index is in the range of 0->length-1 if ((_index == _vault.collateralAssets.length) && (_index == _vault.collateralAmounts.length)) { _vault.collateralAssets.push(_collateralAsset); _vault.collateralAmounts.push(_amount); } else { require((_index < _vault.collateralAssets.length) && (_index < _vault.collateralAmounts.length), ""V8""); address existingCollateral = _vault.collateralAssets[_index]; require((existingCollateral == _collateralAsset) || (existingCollateral == address(0)), ""V9""); _vault.collateralAmounts[_index] = _vault.collateralAmounts[_index].add(_amount); _vault.collateralAssets[_index] = _collateralAsset; } } ",./opyn_gamma_protocol/specs/MarginVault.spec,opyn_gamma_protocol,,Yes,,"Functionality: The code extends the functionality of a cryptocurrency vault by allowing the addition of short positions, long positions, and collateral, ensuring validity through checks on amounts, indices, and asset consistency, while leveraging existing positions if applicable." ab45c728ea5bd7fbe18def86689e2164,2031 | 2032,invariant,also,428,595,shortOtoken.havocTotalSupply | havocVault," // valid state - need to prove this as in invariant also when the vault is not valid should hold This rule is proven relaying on: 1. a proof with Mathemtic minValue to compute the debt a vault 2. a rule that shows that one vault is changed at a time 3. rules that show valid balances of the system with respect to vault changes Due to complexity for the SMT solver the rules is split to three cases of a valid vault: if( longAmount <= shortAmount && longAmount * longPrice <= shortAmount * shortPrice ) //case 1 debt = shortAmount * shortPrice - longAmount * longPrice ; else if ( longAmount > shortAmount_ && longPrice < shortPrice) //case 2 debt = shortAmount * (shortPrice - longPrice) ; else //case 3 debt = 0; */ rule putOptionsPreExpiryCase1StartingWithAllCases( address owner, uint256 vaultId, uint256 index, address asset, uint256 _shortAmount, uint256 _longAmount, uint256 _collateral, uint256 _poolAssetBalance, uint256 _poolShortOtokenBalance, uint256 _poolLongOtokenBalance, uint256 _totalSupplyShortOtoken, uint256 _totalSupplyLongOtoken, uint256 shortPrice, uint256 longPrice, uint256 shortAmount_, uint256 longAmount_, uint256 collateral_, uint256 poolAssetBalance_, uint256 poolShortOtokenBalance_, uint256 poolLongOtokenBalance_, uint256 totalSupplyShortOtoken_, uint256 totalSupplyLongOtoken_ ) { links(); env e; require shortOtoken == getVaultShortOtoken(owner, vaultId, index); require longOtoken == getVaultLongOtoken(owner, vaultId, index); require collateralToken == getVaultCollateralAsset(owner, vaultId, index); require asset == collateralToken; require _shortAmount == getVaultShortAmount(owner, vaultId, index); require _longAmount == getVaultLongAmount(owner, vaultId, index); require _collateral == getVaultCollateralAmount(owner, vaultId, index); require _poolAssetBalance == pool.getStoredBalance(asset); require _poolShortOtokenBalance == pool.getStoredBalance(shortOtoken); require _poolLongOtokenBalance == pool.getStoredBalance(longOtoken); require _totalSupplyShortOtoken == assetTotalSupply(shortOtoken); require _totalSupplyLongOtoken == assetTotalSupply(longOtoken); require shortPrice == shortOtoken.strikePrice(); require longPrice == longOtoken.strikePrice(); // assume before expiry require !isVaultExpired(e, owner, vaultId); // assume no bankruptcy mathint _obligation = (_totalSupplyShortOtoken - _poolShortOtokenBalance ) * shortPrice + (_totalSupplyLongOtoken - _poolLongOtokenBalance ) * longPrice; require _poolAssetBalance >= _obligation ; // based on Mathematica proof: mathint _debt = 0; if( _longAmount <= _shortAmount && ( _longAmount * longPrice <= _shortAmount * shortPrice )) { _debt = _shortAmount * shortPrice - _longAmount * longPrice ; } else if ( _longAmount > _shortAmount && longPrice < shortPrice) { _debt = _shortAmount * (shortPrice - longPrice) ; } //assume vault is in a valid state require _collateral >= _debt ; //assume valid state based on valid balances rules require ( _totalSupplyShortOtoken >= _poolShortOtokenBalance + _shortAmount) && _totalSupplyLongOtoken >= _poolLongOtokenBalance && _poolLongOtokenBalance >= _longAmount && _poolAssetBalance >= _collateral ; //compute excess collateral of other vaults mathint _obligationWithoutThisVault = (_totalSupplyShortOtoken - _poolShortOtokenBalance - _shortAmount) * shortPrice + (_totalSupplyLongOtoken - _poolLongOtokenBalance +_longAmount ) * longPrice ; // assume excess collateral of other vaults is not cover by this vault's collateral require ( _poolAssetBalance - _collateral >= _obligationWithoutThisVault ); // simulate many operations in one step uint256 newShortAmount; uint256 newLongAmount; uint256 newcollateralAmount; uint256 newTotalSupply; sinvoke havocVault(owner, vaultId, index, newShortAmount, newLongAmount, newcollateralAmount); sinvoke pool.havocSystemBalance(longOtoken); sinvoke pool.havocSystemBalance(asset); sinvoke shortOtoken.havocTotalSupply(newTotalSupply); require shortAmount_ == getVaultShortAmount(owner, vaultId, index); require longAmount_ == getVaultLongAmount(owner, vaultId, index); require collateral_ == getVaultCollateralAmount(owner, vaultId, index); require poolAssetBalance_ == pool.getStoredBalance(asset); require poolShortOtokenBalance_ == pool.getStoredBalance(shortOtoken); require poolLongOtokenBalance_ == pool.getStoredBalance(longOtoken); require totalSupplyShortOtoken_ == assetTotalSupply(shortOtoken); require totalSupplyLongOtoken_ == assetTotalSupply(longOtoken); mathint obligation_ = (totalSupplyShortOtoken_ - poolShortOtokenBalance_ ) * shortPrice + (totalSupplyLongOtoken_ - poolLongOtokenBalance_ ) * longPrice ; //assume isValid vault case 1 require longAmount_ <= shortAmount_ && longAmount_ * longPrice <= shortAmount_ * shortPrice ; mathint debt_ = shortAmount_ * shortPrice - longAmount_ * longPrice ; require collateral_ >= debt_ ; require (totalSupplyShortOtoken_ >= poolShortOtokenBalance_ + shortAmount_) && totalSupplyLongOtoken_ >= poolLongOtokenBalance_ && poolLongOtokenBalance_ >= longAmount_ && poolAssetBalance_ >= collateral_ ; // assume changes are coherent based on total required rules require collateral_ - _collateral == poolAssetBalance_- _poolAssetBalance; require longAmount_ - _longAmount == poolLongOtokenBalance_ - _poolLongOtokenBalance; require shortAmount_ - _shortAmount == totalSupplyShortOtoken_ - _totalSupplyShortOtoken; //verify no bankruptchy assert poolAssetBalance_ >= obligation_ ; } /** For put options before expiry (strong bound): obligation(o) 闁 (o.totalSupply() - storedBalance(o)) The debt computation is also different rule callOptionsPreExpiry( // assume before expiry mathint _obligation = (_totalSupplyShortOtoken - _poolShortOtokenBalance ) + (_totalSupplyLongOtoken - _poolLongOtokenBalance ) ; require (_poolAssetBalance >= _obligation) ; mathint temp = _shortAmount - _longAmount; if temp > 0 { _debt = temp; /** * (long strike - short strike) * short amount * calculate ---------------------------------------------- * long strike */ mathint temp2 = (longPrice - shortPrice) * _shortAmount / longPrice; mathint _debt2; if temp2 > _debt { _debt2 = temp2; } else { _debt2 = _debt; require _collateral >= _debt2 ; ////assume valid state based on valid balances rules _totalSupplyLongOtoken >= _poolLongOtokenBalance && _poolAssetBalance >= _collateral; //compute excess collateral of other vaults. mathint _obligationWithoutThisVault = (_totalSupplyShortOtoken - _poolShortOtokenBalance - _shortAmount) + (_totalSupplyLongOtoken - _poolLongOtokenBalance +_longAmount ) ; //assume vault is in a vaild state mathint obligation_ = (totalSupplyShortOtoken_ - poolShortOtokenBalance_ ) + (totalSupplyLongOtoken_ - poolLongOtokenBalance_ ) ; mathint debt_ = 0; mathint temp3 = shortAmount_ - longAmount_; if temp3 > 0 { debt_ = temp3; /** * (long strike - short strike) * short amount * calculate ---------------------------------------------- * long strike */ mathint temp4 = (longPrice - shortPrice) * shortAmount_ / longPrice; mathint debt_2; if temp4 > debt_ { debt_2 = temp4; debt_2 = debt_; require ( collateral_ >= debt_2 ); // valid state totalSupplyLongOtoken_ >= poolLongOtokenBalance_ && poolLongOtokenBalance_ >= longAmount_ && poolAssetBalance_ >= collateral_ ; ","havocTotalSupply (Lines 5-7), | havocVault (Lines 24-36), "," function havocTotalSupply(uint256 newVal) public { _totalSupply = newVal; } | function havocVault( address owner, uint256 vaultId, uint256 i, uint256 newShortAmount, uint256 newLongAmount, uint256 newcollateralAmount ) external { MarginVault.Vault storage vault = cheapGetVault(owner, vaultId); vault.shortAmounts[i] = newShortAmount; vault.longAmounts[i] = newLongAmount; vault.collateralAmounts[i] = newcollateralAmount; } ",./opyn_gamma_protocol/specs/NoBankruptcy.spec,opyn_gamma_protocol,,Yes,,"Functionality: Update the total supply of a currency and modify the amounts within a specific trader's margin vault, adjusting their short positions, long positions, and collateral amounts at a designated index, thereby allowing dynamic management of trading strategies and margin requirements." 85ea85426e0fbbed91f41eef0e3e9ef8,1988 | 1989 | 1990 | 1991,rule,optionWithdrawsRestricted,135,152,withdrawLongB | burnOtokenB | settleVault | getProceed,"rule optionWithdrawsRestricted(address owner, uint256 vaultId, uint256 index, address from, address amount, method f) { env e; // The pool cannot really call any of these functions require (e.msg.sender != pool); require (!whitelist.isWhitelistedCollateral(longOtoken)); uint256 otokenBalanceBefore = longOtoken.balanceOf(pool); if (f.selector == burnOtokenB(address,uint256,address,uint256,uint256).selector) { require(owner != pool); sinvoke burnOtokenB(e, owner, vaultId, from, index, amount); } else { calldataarg arg; sinvoke f(e, arg); } uint256 otokenBalanceAfter = longOtoken.balanceOf(pool); // or settle vault assert otokenBalanceAfter < otokenBalanceBefore => (f.selector == withdrawLongB(address, uint256, address, uint256, uint256).selector) || (f.selector == settleVault(address,uint256,address).selector); rule settleVault (address owner, uint256 vaultId, uint256 index, address oToken, address to, address collateral) { require oToken == shortOtoken; require collateral == collateralToken; require getVaultShortOtoken(owner, vaultId, 0) == oToken; require getVaultCollateralAsset(owner, vaultId, 0) == collateral; require to != pool; uint256 supplyBefore = shortOtoken.totalSupply(); uint256 amountRemoved = getProceed(owner, vaultId); uint256 poolBalanceBefore = collateralToken.balanceOf(pool); sinvoke settleVault(e, owner, vaultId, to); uint256 supplyAfter = shortOtoken.totalSupply(); uint256 poolBalanceAfter = collateralToken.balanceOf(pool); assert supplyAfter == supplyBefore; assert poolBalanceAfter != poolBalanceBefore => (poolBalanceBefore - poolBalanceAfter == amountRemoved); } ","withdrawLongB (Lines 212-228), | burnOtokenB (Lines 325-342), | settleVault (Lines 360-367), | getProceed (Lines 467-475), "," function withdrawLongB( address owner, uint256 vaultId, address to, uint256 index, uint256 amount ) external { Actions.WithdrawArgs memory args = Actions.WithdrawArgs({ owner: owner, vaultId: vaultId, to: to, asset: anOtokenB, index: index, amount: amount }); _withdrawLong(args); } | function burnOtokenB( address owner, uint256 vaultId, address from, uint256 index, uint256 amount ) external { require(smallVault(owner, vaultId, 1)); Actions.BurnArgs memory args = Actions.BurnArgs({ owner: owner, vaultId: vaultId, from: from, otoken: anOtokenB, index: index, amount: amount }); _burnOtoken(args); } | function settleVault( address owner, uint256 vaultId, address to ) external { Actions.SettleVaultArgs memory args = Actions.SettleVaultArgs({owner: owner, vaultId: vaultId, to: to}); _settleVault(args); } | function getProceed(address _owner, uint256 _vaultId) external view returns (uint256) { (MarginVault.Vault memory vault, uint256 typeVault, ) = getVaultWithDetails(_owner, _vaultId); (uint256 netValue, bool isExcess) = calculator.getExcessCollateral(vault, typeVault); if (!isExcess) return 0; return netValue; } ",./opyn_gamma_protocol/specs/controller.spec,opyn_gamma_protocol,,Yes,,"Functionality: Enable the manipulation and settlement of vaults containing options, specifically for withdrawing long positions, burning options, and settling vaults post-expiry. It also allows users to query the net value of a vault's excess collateral, ensuring operations respect the underlying collateralization and ownership rules." f42822566974f4f8ba598551edb94bb1,2155 | 2158,rule,zeroMintDoesNotIncreaseBalance,154,163,mint | changeSupply,"rule zeroMintDoesNotIncreaseBalance(address user) { env e; requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); uint before = balanceOf(user); mint(e, user, 0); uint after = balanceOf(user); assert after == before; ","mint (Lines 42-87), | changeSupply (Lines 472-507), "," function mint( address _asset, uint256 _amount, uint256 _minimumOusdAmount ) external whenNotCapitalPaused nonReentrant { require(assets[_asset].isSupported, ""Asset is not supported""); require(_amount > 0, ""Amount must be greater than 0""); uint256 price = IMinMaxOracle(priceProvider).priceMin( Helpers.getSymbol(_asset) ); if (price > 1e8) { price = 1e8; } uint256 assetDecimals = Helpers.getDecimals(_asset); uint256 unitAdjustedDeposit = _amount.scaleBy(int8(18 - assetDecimals)); uint256 priceAdjustedDeposit = _amount.mulTruncateScale( price.scaleBy(int8(10)), // 18-8 because oracles have 8 decimals precision 10**assetDecimals ); if (_minimumOusdAmount > 0) { require( priceAdjustedDeposit >= _minimumOusdAmount, ""Mint amount lower than minimum"" ); } emit Mint(msg.sender, priceAdjustedDeposit); // Rebase must happen before any transfers occur. if (unitAdjustedDeposit >= rebaseThreshold && !rebasePaused) { _rebase(); } // Mint matching OUSD oUSD.mint(msg.sender, priceAdjustedDeposit); // Transfer the deposited coins to the vault IERC20 asset = IERC20(_asset); asset.safeTransferFrom(msg.sender, address(this), _amount); if (unitAdjustedDeposit >= autoAllocateThreshold) { _allocate(); } } | function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: The code allows users to mint a new asset (OUSD) by depositing a supported asset, ensuring the deposited amount meets a minimum OUSD value threshold. It optionally triggers a rebase and allocation of funds based on thresholds, after adjusting for price and decimals precision. Additionally, it permits authorized entities to adjust the total supply of a system's internal credits, ensuring the new supply does not invalidate the credits-to-token ratio." 172b126d74fd4b7f46a10cdaadc8bd21,2140 | 2141,rule,changesRebaseState,79,90,rebaseOptIn | rebaseOptOut,"rule changesRebaseState(method f) { address who; uint8 _rebaseState = rebaseState(who); executeAFunction(f); uint8 rebaseState_ = rebaseState(who); assert _rebaseState == rebaseState_ || f.selector == rebaseOptIn().selector || f.selector == rebaseOptOut().selector; } ","rebaseOptIn (Lines 424-445), | rebaseOptOut (Lines 450-464), "," function rebaseOptIn() public nonReentrant { require(_isNonRebasingAccount(msg.sender), ""Account has not opted out""); // Convert balance into the same amount at the current exchange rate uint256 newCreditBalance = _creditBalances[msg.sender] .mul(rebasingCreditsPerToken) .div(_creditsPerToken(msg.sender)); // Decreasing non rebasing supply nonRebasingSupply = nonRebasingSupply.sub(balanceOf(msg.sender)); _creditBalances[msg.sender] = newCreditBalance; // Increase rebasing credits, totalSupply remains unchanged so no // adjustment necessary rebasingCredits = rebasingCredits.add(_creditBalances[msg.sender]); rebaseState[msg.sender] = RebaseOptions.OptIn; // Delete any fixed credits per token delete nonRebasingCreditsPerToken[msg.sender]; } | function rebaseOptOut() public nonReentrant { require(!_isNonRebasingAccount(msg.sender), ""Account has not opted in""); // Increase non rebasing supply nonRebasingSupply = nonRebasingSupply.add(balanceOf(msg.sender)); // Set fixed credits per token nonRebasingCreditsPerToken[msg.sender] = rebasingCreditsPerToken; // Decrease rebasing credits, total supply remains unchanged so no // adjustment necessary rebasingCredits = rebasingCredits.sub(_creditBalances[msg.sender]); // Mark explicitly opted out of rebasing rebaseState[msg.sender] = RebaseOptions.OptOut; } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Provide users the ability to opt in or out of a rebasing mechanism. When opting in, convert user's balance to the current exchange rate, adjusting the non-rebasing and rebasing supplies accordingly. For opting out, increase the non-rebasing supply and set a fixed credit per token rate, while adjusting the rebasing credits." 0ab50333e9e0f234a3fc8d7851508941,2150 | 2153,rule,mintDoesNotDecreaseBalance,143,152,mint | changeSupply,"rule mintDoesNotDecreaseBalance(address burned, uint256 amount){ env e; requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); uint before = balanceOf(burned); mint(e, burned, amount); uint after = balanceOf(burned); assert after >= before; ","mint (Lines 42-87), | changeSupply (Lines 472-507), "," function mint( address _asset, uint256 _amount, uint256 _minimumOusdAmount ) external whenNotCapitalPaused nonReentrant { require(assets[_asset].isSupported, ""Asset is not supported""); require(_amount > 0, ""Amount must be greater than 0""); uint256 price = IMinMaxOracle(priceProvider).priceMin( Helpers.getSymbol(_asset) ); if (price > 1e8) { price = 1e8; } uint256 assetDecimals = Helpers.getDecimals(_asset); uint256 unitAdjustedDeposit = _amount.scaleBy(int8(18 - assetDecimals)); uint256 priceAdjustedDeposit = _amount.mulTruncateScale( price.scaleBy(int8(10)), // 18-8 because oracles have 8 decimals precision 10**assetDecimals ); if (_minimumOusdAmount > 0) { require( priceAdjustedDeposit >= _minimumOusdAmount, ""Mint amount lower than minimum"" ); } emit Mint(msg.sender, priceAdjustedDeposit); // Rebase must happen before any transfers occur. if (unitAdjustedDeposit >= rebaseThreshold && !rebasePaused) { _rebase(); } // Mint matching OUSD oUSD.mint(msg.sender, priceAdjustedDeposit); // Transfer the deposited coins to the vault IERC20 asset = IERC20(_asset); asset.safeTransferFrom(msg.sender, address(this), _amount); if (unitAdjustedDeposit >= autoAllocateThreshold) { _allocate(); } } | function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Allow users to mint a stablecoin (OUSD) by depositing supported assets, ensuring the mint amount is not less than a minimum requirement based on asset price. It also handles supply adjustments, maintaining a stable value through rebasing while ensuring supply changes respect a maximum limit." f4466f865ca52d7ffa25b07318044952,2171,rule,zeroBurnDoesNotDecreaseBalance,199,208,changeSupply,"rule zeroBurnDoesNotDecreaseBalance(address burned){ env e; requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); uint before = balanceOf(burned); burn(e, burned, 0); uint after = balanceOf(burned); assert before == after; ","changeSupply (Lines 472-507), "," function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Adjust the total supply of a token, not exceeding a maximum limit, and recalibrate the per-token value based on rebasing credits. Emit an event to document the updated total supply, ensuring changes reflect the correct proportion of non-rebasing supply to maintain integrity and prevent invalid supply adjustments." 4b64abcbac1abba4a89abd58adb082d2,2136,rule,totalSupplyIntegrity,54,62,changeSupply,"rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); ","changeSupply (Lines 472-507), "," function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Adjusts the total supply of a token to a specified value, with limitations set by a maximum supply threshold, ensuring no adjustment to zero or negative values. It recalculates a proportionate credit per token, ensuring the change is valid and emitting an event to signal the update." f42822566974f4f8ba598551edb94bb1,2155 | 2158,rule,zeroMintDoesNotIncreaseBalance,154,163,mint | changeSupply,"rule zeroMintDoesNotIncreaseBalance(address user) { env e; requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); uint before = balanceOf(user); mint(e, user, 0); uint after = balanceOf(user); assert after == before; ","mint (Lines 42-87), | changeSupply (Lines 472-507), "," function mint( address _asset, uint256 _amount, uint256 _minimumOusdAmount ) external whenNotCapitalPaused nonReentrant { require(assets[_asset].isSupported, ""Asset is not supported""); require(_amount > 0, ""Amount must be greater than 0""); uint256 price = IMinMaxOracle(priceProvider).priceMin( Helpers.getSymbol(_asset) ); if (price > 1e8) { price = 1e8; } uint256 assetDecimals = Helpers.getDecimals(_asset); uint256 unitAdjustedDeposit = _amount.scaleBy(int8(18 - assetDecimals)); uint256 priceAdjustedDeposit = _amount.mulTruncateScale( price.scaleBy(int8(10)), // 18-8 because oracles have 8 decimals precision 10**assetDecimals ); if (_minimumOusdAmount > 0) { require( priceAdjustedDeposit >= _minimumOusdAmount, ""Mint amount lower than minimum"" ); } emit Mint(msg.sender, priceAdjustedDeposit); // Rebase must happen before any transfers occur. if (unitAdjustedDeposit >= rebaseThreshold && !rebasePaused) { _rebase(); } // Mint matching OUSD oUSD.mint(msg.sender, priceAdjustedDeposit); // Transfer the deposited coins to the vault IERC20 asset = IERC20(_asset); asset.safeTransferFrom(msg.sender, address(this), _amount); if (unitAdjustedDeposit >= autoAllocateThreshold) { _allocate(); } } | function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Enable users to mint a new digital asset (OUSD) by depositing a supported asset, after calculating its value based on current market prices, ensuring the deposit meets a minimum value, and potentially triggering a rebase to adjust the supply if certain thresholds are met." 73689d9fc34026be926eb7e27b25c843,2148,rule,additiveMint,125,141,mint,"rule additiveMint(address minter, uint256 x, uint256 y) { env e; storage init = lastStorage; mint(e, minter, x); mint(e, minter, y); uint b1 = balanceOf(minter); require x+y <= MAX_UINT256(); uint sumXY = x+y; mint(e, minter, sumXY) at init; uint b2 = balanceOf(minter); assert b1 == b2, ""mint is not additive in balance of burned""; } ","mint (Lines 42-87), "," function mint( address _asset, uint256 _amount, uint256 _minimumOusdAmount ) external whenNotCapitalPaused nonReentrant { require(assets[_asset].isSupported, ""Asset is not supported""); require(_amount > 0, ""Amount must be greater than 0""); uint256 price = IMinMaxOracle(priceProvider).priceMin( Helpers.getSymbol(_asset) ); if (price > 1e8) { price = 1e8; } uint256 assetDecimals = Helpers.getDecimals(_asset); uint256 unitAdjustedDeposit = _amount.scaleBy(int8(18 - assetDecimals)); uint256 priceAdjustedDeposit = _amount.mulTruncateScale( price.scaleBy(int8(10)), // 18-8 because oracles have 8 decimals precision 10**assetDecimals ); if (_minimumOusdAmount > 0) { require( priceAdjustedDeposit >= _minimumOusdAmount, ""Mint amount lower than minimum"" ); } emit Mint(msg.sender, priceAdjustedDeposit); // Rebase must happen before any transfers occur. if (unitAdjustedDeposit >= rebaseThreshold && !rebasePaused) { _rebase(); } // Mint matching OUSD oUSD.mint(msg.sender, priceAdjustedDeposit); // Transfer the deposited coins to the vault IERC20 asset = IERC20(_asset); asset.safeTransferFrom(msg.sender, address(this), _amount); if (unitAdjustedDeposit >= autoAllocateThreshold) { _allocate(); } } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Validate the asset and amount for minting, calculating its price and adjusting for decimals to ensure it meets the minimum required OUSD amount. Emit a Mint event, potentially trigger a rebase if above threshold, mint OUSD to the sender, transfer deposited assets to the contract, and possibly initiate allocation if above a defined threshold." 8971fb1874e7a89ff1c97e87fa8b565f,2127,invariant,optingInAndOutSyncdWithNonRebasingState,344,366,changeSupply,"invariant optingInAndOutSyncdWithNonRebasingState(address a) (rebaseState(a) == OPT_IN() => nonRebasingCreditsPerToken(a) == 0) && (rebaseState(a) == OPT_OUT() => nonRebasingCreditsPerToken(a) > 0) // otherwise - no need to check rule isRebasingPredicateSynchronized(address a) { requireInvariant optingInAndOutSyncdWithNonRebasingState; requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); uint256 _previousNonRebasingCreditsPerToken = nonRebasingCreditsPerToken(a); bool becomesNonRebasing = Certora_isNonRebasingAccount(a); // the only thing we can say for sure here is that if it was non rebasing, then it remains non rebasing if (_previousNonRebasingCreditsPerToken > 0) { assert becomesNonRebasing; } // can't say anything else because a contract will be migrated. // after we call _isNonRebasingAccount, and it returns true, it must be the case that nonRebasingCreditsPerToken is positive. if (becomesNonRebasing) { assert nonRebasingCreditsPerToken(a) > 0; } else { assert nonRebasingCreditsPerToken(a) == 0; } ","changeSupply (Lines 472-507), "," function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Adjust the total supply of a token within predefined limits, ensuring it cannot exceed a maximum value or drop to zero. It recalculates a per-token credit value, ensuring it remains positive, and emits an event detailing the adjusted supply, total credits, and the calculated per-token credit value." edfff2a81ec7a1cafbc532a35123aaa0,2132,rule,neverRevert_BalanceOf,33,39,changeSupply,"rule neverRevert_BalanceOf { // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); ","changeSupply (Lines 472-507), "," function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Safely adjust the total supply of a token, ensuring it does not exceed a maximum limit, and update its rebasing credits per token proportionally. It emits an event upon successful supply adjustment or in case the new supply matches the current, without making changes." edfff2a81ec7a1cafbc532a35123aaa0,2132,rule,neverRevert_BalanceOf,33,39,changeSupply,"rule neverRevert_BalanceOf { // probably must assume invariant rebasingCreditsPerTokenMustBeGreaterThan0 requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0(); address who; invoke balanceOf(who); assert !lastReverted; } // Requireing rebasingCreditsPerToken to be greater than zero is important // since much of the rebasing/nonRebasing accounting will fail if it is zero. // // Once the contract is initialized, the rebasingCreditsPerToken should never // again be zero, since a positive value is set in the initializer and there is a // require statement guarding against it going back down to zero in the only // function that updates it. invariant rebasingCreditsPerTokenMustBeGreaterThan0() rebasingCreditsPerToken() > 0 // a condition for changeSupply() not to revert: // TODO: Check in init_state. probably wrong in constructor/initialization? rule totalSupplyIntegrity(method f) { require totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); requireInvariant rebasingCreditsPerTokenMustBeGreaterThan0; // need to require that nonRebasingCreditsPerToken is < 1e18? executeAFunction(f); assert totalSupply() >= (rebasingCredits()*ONE()) / rebasingCreditsPerToken() + nonRebasingSupply(); ","changeSupply (Lines 472-507), "," function changeSupply(uint256 _newTotalSupply) external onlyVault nonReentrant { require(_totalSupply > 0, ""Cannot increase 0 supply""); if (_totalSupply == _newTotalSupply) { emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); return; } _totalSupply = _newTotalSupply > MAX_SUPPLY ? MAX_SUPPLY : _newTotalSupply; rebasingCreditsPerToken = rebasingCredits.divPrecisely( _totalSupply.sub(nonRebasingSupply) ); require(rebasingCreditsPerToken > 0, ""Invalid change in supply""); _totalSupply = rebasingCredits .divPrecisely(rebasingCreditsPerToken) .add(nonRebasingSupply); emit TotalSupplyUpdated( _totalSupply, rebasingCredits, rebasingCreditsPerToken ); } ",./ousd/spec/ousd.spec,ousd,,Yes,,"Functionality: Adjust the total token supply to a new value, within the bounds of a maximum supply limit, recalculating a per-token credit ratio. This adjustment restricts changes to a non-zero supply and ensures the recalculated credits per token remain positive, emitting an updated supply event upon change." 8817c3e3f94e6cb1fef12918c6792b78,2255,rule,unrelatedUserBalanceNotChangingParametric,929,978,_dripsState,"// rule unrelatedUserBalanceNotChangingParametric( // method f, uint256 senderId, uint256 receiverId, uint256 assetId) { // env e; env eB; env eF; // calldataarg args; // // step 1 - balance before of receiverId // bytes32 dripsHashBefore; bytes32 dripsHistoryHashBefore; // uint32 updateTimeBefore; uint128 balanceBefore; uint32 maxEndBefore; // dripsHashBefore, dripsHistoryHashBefore, updateTimeBefore, // balanceBefore, maxEndBefore = _dripsState(eB, receiverId, assetId); // uint256 userId1; uint256 config1; // uint256 userId2; uint256 config2; // uint256 userId3; uint256 config3; // userId1, config1, userId2, config2, userId3, config3 = unpackArgs(e, args); // DH.DripsReceiver argsReceiver1; // require argsReceiver1.userId == userId1; // require argsReceiver1.config == config1; // DH.DripsReceiver argsReceiver2; // require argsReceiver2.userId == userId2; // require argsReceiver2.config == config2; // DH.DripsReceiver argsReceiver3; // require argsReceiver3.userId == userId3; // require argsReceiver3.config == config3; // require argsReceiver1.userId != receiverId; // require argsReceiver2.userId != receiverId; // require argsReceiver3.userId != receiverId; // f(e, args); // //assert false; // false 2 // // step 3 - balance after of user2 // bytes32 dripsHashAfter; bytes32 dripsHistoryHashAfter; // uint32 updateTimeAfter; uint128 balanceAfter; uint32 maxEndAfter; // dripsHashAfter, dripsHistoryHashAfter, updateTimeAfter, // balanceAfter, maxEndAfter = _dripsState(eF, receiverId, assetId); // // check that balance of user2 was not modified // assert balanceBefore == balanceAfter, ""balanceOf receiverId changed""; // //assert false; // } ","_dripsState (Lines 509-529), "," function _dripsState(uint256 userId, uint256 assetId) //internal public view returns ( bytes32 dripsHash, bytes32 dripsHistoryHash, uint32 updateTime, uint128 balance, uint32 maxEnd ) { DripsState storage state = _dripsStorage().states[assetId][userId]; return ( state.dripsHash, state.dripsHistoryHash, state.updateTime, state.balance, state.maxEnd ); } ",./radicle_drips/specs/DripsHub.spec,radicle_drips,,Yes,,"Functionality: Retrieve the state of drips for a specific user and asset, returning the current drips configuration hash, drips history hash, the last update time, the user's balance in the drips mechanism, and the maximum end time of any ongoing drips." 82c40e04f3e556bbdf4feac170959366,2266,rule,correctnessOfSplit,38,55,split,"rule correctnessOfSplit() { env e; uint256 userId; uint256 assetId; uint128 collectableAmt; uint128 splitAmt; uint128 splittableBefore; uint128 collectableBefore; uint128 splittableAfter; uint128 collectableAfter; splittableBefore = splittable(e, userId, assetId); collectableBefore = collectable(e, userId, assetId); collectableAmt, splitAmt = split(e, userId, assetId, true); splittableAfter = splittable(e, userId, assetId); collectableAfter = collectable(e, userId, assetId); assert splittableBefore >= splittableAfter; assert collectableBefore + collectableAmt == collectableAfter; assert splittableBefore + collectableBefore >= splittableAfter + collectableAfter; } ","split (Lines 343-349), "," function split( uint256 userId, IERC20 erc20, SplitsReceiver[] memory currReceivers ) public whenNotPaused returns (uint128 collectableAmt, uint128 splitAmt) { return Splits._split(userId, _assetId(erc20), currReceivers); } ",./radicle_drips/specs/Splits.spec,radicle_drips,,Yes,,"Functionality: Perform a funds split for a specific user and ERC20 token, distributing amounts to specified receivers. This method calculates the collectable and split amounts based on current receiver configurations, using the internal `_split` function and pausing execution if the contract is paused." a02f160902ea2b8f5f1fd31d36969688,2350,rule,whoChangedBalanceOfUserId,326,362,_dripsState,"rule whoChangedBalanceOfUserId(method f, uint256 userId) { env eB; env eF; calldataarg args; uint256 assetId; bytes32 dripsHashBefore; bytes32 dripsHistoryHashBefore; uint32 updateTimeBefore; uint128 balanceBefore; uint32 maxEndBefore; dripsHashBefore, dripsHistoryHashBefore, updateTimeBefore, balanceBefore, maxEndBefore = _dripsState(eB, userId, assetId); f(eF,args); // call any function bytes32 dripsHashAfter; bytes32 dripsHistoryHashAfter; uint32 updateTimeAfter; uint128 balanceAfter; uint32 maxEndAfter; dripsHashAfter, dripsHistoryHashAfter, updateTimeAfter, balanceAfter, maxEndAfter = _dripsState(eF, userId, assetId); assert balanceBefore == balanceAfter, ""balanceOfUser changed""; } ","_dripsState (Lines 509-529), "," function _dripsState(uint256 userId, uint256 assetId) //internal public view returns ( bytes32 dripsHash, bytes32 dripsHistoryHash, uint32 updateTime, uint128 balance, uint32 maxEnd ) { DripsState storage state = _dripsStorage().states[assetId][userId]; return ( state.dripsHash, state.dripsHistoryHash, state.updateTime, state.balance, state.maxEnd ); } ",./radicle_drips/specs/DripsHubBackup.spec,radicle_drips,,Yes,,"Functionality: Retrieve and return the state of drips for a specific user and asset, including the drips hash, drips history hash, the last update time, current balance, and the maximum end time, by accessing and querying data from a storage structure." a33253c68f2bf1192bc92020f9ca8780,2336 | 2338 | 2339,rule,cannotFrontRunSplitTwoSameUsers,116,160,split | _setSplits | _assertSplitsValid,"rule cannotFrontRunSplitTwoSameUsers() { env e; env e2; uint256 assetId; uint256 userA_Id; uint256 userB_Id; uint256 userC_Id; uint128 userA_collectableAmt; uint128 userA_splitAmt; uint128 userA_collectableAmt2; uint128 userA_splitAmt2; uint128 userB_collectableAmt; uint128 userB_splitAmt; uint128 userA_splittableBefore = splittable(e, userA_Id, assetId); uint128 userB_splittableBefore = splittable(e, userB_Id, assetId); uint128 userC_splittableBefore = splittable(e, userC_Id, assetId); uint128 userA_collectableBefore = collectable(e, userA_Id, assetId); uint128 userB_collectableBefore = collectable(e, userB_Id, assetId); uint128 userC_collectableBefore = collectable(e, userC_Id, assetId); require ( (userA_Id != userB_Id) && ((userC_Id == userA_Id) || (userC_Id == userB_Id)) ); // prevents overflow in the edge cases of (userC == userA) or (userC == userB): require userA_collectableBefore + userA_splittableBefore + userB_splittableBefore < 2^128; require userB_collectableBefore + userB_splittableBefore + userA_splittableBefore < 2^128; // setting up the currReceivers[] of userA and userB to be the same - singe receiver userC uint256 length1 = getCurrSplitsReceiverLocaLength(e, true); require length1 == 1; // only one receiver uint256 index1; uint256 userId1; uint32 weight1; userId1, weight1 = getCurrSplitsReceiverLocalArr(e, true, index1); require userId1 == userC_Id; require weight1 <= 1000000; // safe assumption require weight1 > 0; // safe assumption // safe assumptions since the function _assertSplitsValid() // verified that totalWeight <= _TOTAL_SPLITS_WEIGHT upon _setSplits() // we required that there is only one receiver, therefore totalWeight = weight1 // _TOTAL_SPLITS_WEIGHT == 1000000, hence the assumption above is safe // also _assertSplitsValid() verified that weight != 0 storage initStorage = lastStorage; userA_collectableAmt, userA_splitAmt = split(e, userA_Id, assetId, true); uint128 userC_splittableAfterSplitA = splittable(e, userC_Id, assetId); userB_collectableAmt, userB_splitAmt = split(e, userB_Id, assetId, true) at initStorage; uint128 userC_splittableAfterSplitB = splittable(e, userC_Id, assetId); userA_collectableAmt2, userA_splitAmt2 = split@withrevert(e, userA_Id, assetId, true); assert !lastReverted; } ","split (Lines 343-349), | _setSplits (Lines 215-223), | _assertSplitsValid (Lines 229-247), "," function split( uint256 userId, IERC20 erc20, SplitsReceiver[] memory currReceivers ) public whenNotPaused returns (uint128 collectableAmt, uint128 splitAmt) { return Splits._split(userId, _assetId(erc20), currReceivers); } | function _setSplits(uint256 userId, SplitsReceiver[] memory receivers) internal { SplitsState storage state = _splitsStorage().splitsStates[userId]; bytes32 newSplitsHash = _hashSplits(receivers); emit SplitsSet(userId, newSplitsHash); if (newSplitsHash != state.splitsHash) { _assertSplitsValid(receivers, newSplitsHash); state.splitsHash = newSplitsHash; } } | function _assertSplitsValid(SplitsReceiver[] memory receivers, bytes32 receiversHash) internal { require(receivers.length <= _MAX_SPLITS_RECEIVERS, ""Too many splits receivers""); uint64 totalWeight = 0; uint256 prevUserId; for (uint256 i = 0; i < receivers.length; i++) { SplitsReceiver memory receiver = receivers[i]; uint32 weight = receiver.weight; require(weight != 0, ""Splits receiver weight is zero""); totalWeight += weight; uint256 userId = receiver.userId; if (i > 0) { require(prevUserId != userId, ""Duplicate splits receivers""); require(prevUserId < userId, ""Splits receivers not sorted by user ID""); } prevUserId = userId; emit SplitsReceiverSeen(receiversHash, userId, weight); } require(totalWeight <= _TOTAL_SPLITS_WEIGHT, ""Splits weights sum too high""); } ",./radicle_drips/specs/Splits2.spec,radicle_drips,,Yes,,"Functionality: Facilitate the distribution of ERC20 tokens to multiple recipients based on predefined splits, validate distribution parameters including the maximum number of recipients, ensure uniqueness and order of recipients, and manage the total allocation weight within an allowed range." bb50a156cf13a8276faff05171a8c44f,2352 | 2353 | 2354,rule,singleUserTimeUpdateNotChangingOtherUserBalance,392,490,"DH.create, create | _dripsState | _updateReceiverStates","rule singleUserTimeUpdateNotChangingOtherUserBalance(method f, uint256 userId) { env e; env eB; env eF; calldataarg args; // uint8 i; // userId1 and userId2 - receivers Id uint256 assetId; uint256 userId1; uint256 userId2; require userId != userId1; // != userId2; require userId1 < userId2; // sorted require userId != userId2; // step 1 - balance before of user2 bytes32 dripsHashBefore; bytes32 dripsHistoryHashBefore; uint32 updateTimeBefore; uint128 balanceBefore; uint32 maxEndBefore; dripsHashBefore, dripsHistoryHashBefore, updateTimeBefore, balanceBefore, maxEndBefore = _dripsState(eB, userId2, assetId); // assert false; // false 0 // step 2 - setup user1 changes and then call _updateReceiverStates() /* //setting values to config by create: uint192 _amtPerSec; uint32 _start; uint32 _duration; require _amtPerSec != 0; */ DH.DripsConfig configOld1;// = create(_amtPerSec, _start, _duration); DH.DripsConfig configOld2;// = DH.create(_amtPerSec+1, _start+1, _duration+1); DH.DripsConfig configNew1;// = DH.create(_amtPerSec+2, _start+2, _duration+2); // DH.DripsConfig configNew2; require configOld1 != configNew1; // require configOld2 == configNew2; DH.DripsReceiver receiverOld1; require receiverOld1.userId == userId1; require receiverOld1.config == configOld1; DH.DripsReceiver receiverOld2; require receiverOld2.userId == userId2; require receiverOld2.config == configOld2; DH.DripsReceiver receiverNew1; require receiverNew1.userId == userId1; require receiverNew1.config == configNew1; // DripsReceiver[] memory currReceivers; // DripsReceiver[] memory newReceivers; // currReceivers[i].userId = userId1; // currReceivers[i].config = configCurr; // require sorted // require no duplicate // require amtPerSec != 0 // require(i < _MAX_DRIPS_RECEIVERS,""""); // require currReceivers == newReceivers; // newReceivers[i].config = configNew; // the only change in newReceivers is configNew of userId2 // DripsState storage state = _dripsStorage().states[assetId][userId]; // uint32 lastUpdate = state.updateTime; // uint32 currMaxEnd = state.maxEnd; // uint32 newMaxEnd = sizeof(uint32); // assert false; // false 1 //assert configOld2 != configNew2; //returned 0 _helperUpdateReceiverStates( e, receiverOld1, receiverOld2, receiverNew1, assetId, userId ); //assert false; // false 2 // step 3 - balance after of user2 bytes32 dripsHashAfter; bytes32 dripsHistoryHashAfter; uint32 updateTimeAfter; uint128 balanceAfter; uint32 maxEndAfter; dripsHashAfter, dripsHistoryHashAfter, updateTimeAfter, balanceAfter, maxEndAfter = _dripsState(eF, userId2, assetId); // check that balance of user2 was not modified assert balanceBefore == balanceAfter, ""balanceOfUser2 changed""; assert false; } ","create (Lines 59-68), | _dripsState (Lines 509-529), | _updateReceiverStates (Lines 818-1089), "," function create( uint192 _amtPerSec, uint32 _start, uint32 _duration ) internal pure returns (DripsConfig) { uint256 config = _amtPerSec; config = (config << 32) | _start; config = (config << 32) | _duration; return DripsConfig.wrap(config); } | function _dripsState(uint256 userId, uint256 assetId) //internal public view returns ( bytes32 dripsHash, bytes32 dripsHistoryHash, uint32 updateTime, uint128 balance, uint32 maxEnd ) { DripsState storage state = _dripsStorage().states[assetId][userId]; return ( state.dripsHash, state.dripsHistoryHash, state.updateTime, state.balance, state.maxEnd ); } | function _updateReceiverStates( mapping(uint256 => DripsState) storage states, DripsReceiver[] memory currReceivers, uint32 lastUpdate, uint32 currMaxEnd, DripsReceiver[] memory newReceivers, uint32 newMaxEnd //) private { ) internal virtual { //return; //require(currReceivers.length == 1, ""Attempt to reduce computation""); //require(newReceivers.length == 1, ""Attempt to reduce computation""); uint256 currIdx = 0; uint256 newIdx = 0; while (true) { bool pickCurr = currIdx < currReceivers.length; DripsReceiver memory currRecv; if (pickCurr) currRecv = currReceivers[currIdx]; bool pickNew = newIdx < newReceivers.length; DripsReceiver memory newRecv; if (pickNew) newRecv = newReceivers[newIdx]; // if-1 // Limit picking both curr and new to situations when they differ only by start/end time if ( pickCurr && pickNew && (currRecv.userId != newRecv.userId || currRecv.config.amtPerSec() != newRecv.config.amtPerSec()) ) { pickCurr = _isOrdered(currRecv, newRecv); pickNew = !pickCurr; } if (pickCurr && pickNew) { // if-2: same userId, same amtPerSec // Shift the existing drip to fulfil the new configuration // states[currRecv.userId].amtDeltas[_currTimestamp()].thisCycle = thisCycleMapping[currRecv.userId][_currTimestamp()]; // states[currRecv.userId].amtDeltas[_currTimestamp()].nextCycle = nextCycleMapping[currRecv.userId][_currTimestamp()]; // states[currRecv.userId].nextReceivableCycle = nextReceivableCycleMapping[currRecv.userId]; DripsState storage state = states[currRecv.userId]; (uint32 currStart, uint32 currEnd) = _dripsRangeInFuture( currRecv, lastUpdate, currMaxEnd ); (uint32 newStart, uint32 newEnd) = _dripsRangeInFuture( newRecv, _currTimestamp(), newMaxEnd ); { int256 amtPerSec = int256(uint256(currRecv.config.amtPerSec())); // Move the start and end times if updated _addDeltaRange(state, currStart, newStart, -amtPerSec); _addDeltaRange(state, currEnd, newEnd, amtPerSec); } // Ensure that the user receives the updated cycles uint32 currStartCycle = _cycleOf(currStart); uint32 newStartCycle = _cycleOf(newStart); if (currStartCycle > newStartCycle && state.nextReceivableCycle > newStartCycle) { state.nextReceivableCycle = newStartCycle; } } else if (pickCurr) { // if-3 // Remove an existing drip DripsState storage state = states[currRecv.userId]; (uint32 start, uint32 end) = _dripsRangeInFuture(currRecv, lastUpdate, currMaxEnd); //require (end - start == 10); int256 amtPerSec = int256(uint256(currRecv.config.amtPerSec())); _addDeltaRange(state, start, end, -amtPerSec); // } else if (pickNew) { // if-4 // Create a new drip DripsState storage state = states[newRecv.userId]; (uint32 start, uint32 end) = _dripsRangeInFuture( newRecv, _currTimestamp(), newMaxEnd ); int256 amtPerSec = int256(uint256(newRecv.config.amtPerSec())); _addDeltaRange(state, start, end, amtPerSec); // Ensure that the user receives the updated cycles uint32 startCycle = _cycleOf(start); if (state.nextReceivableCycle == 0 || state.nextReceivableCycle > startCycle) { state.nextReceivableCycle = startCycle; } // } else { break; } if (pickCurr) currIdx++; if (pickNew) newIdx++; } } /// @notice Calculates the time range in the future in which a receiver will be dripped to. /// @param receiver The drips receiver /// @param maxEnd The maximum end time of drips function _dripsRangeInFuture( DripsReceiver memory receiver, uint32 updateTime, uint32 maxEnd //) private view returns (uint32 start, uint32 end) { ) internal view returns (uint32 start, uint32 end) { return _dripsRange(receiver, updateTime, maxEnd, _currTimestamp(), type(uint32).max); } /// @notice Calculates the time range in which a receiver is to be dripped to. /// This range is capped to provide a view on drips through a specific time window. /// @param receiver The drips receiver /// @param updateTime The time when drips are configured /// @param maxEnd The maximum end time of drips /// @param startCap The timestamp the drips range start should be capped to /// @param endCap The timestamp the drips range end should be capped to function _dripsRange( DripsReceiver memory receiver, uint32 updateTime, uint32 maxEnd, uint32 startCap, uint32 endCap //) private pure returns (uint32 start, uint32 end_) { ) internal pure returns (uint32 start, uint32 end_) { start = receiver.config.start(); if (start == 0) start = updateTime; uint40 end = uint40(start) + receiver.config.duration(); if (end == start || end > maxEnd) end = maxEnd; if (start < startCap) start = startCap; if (end > endCap) end = endCap; if (end < start) end = start; return (start, uint32(end)); } /// @notice Adds funds received by a user in a given time range /// @param state The user state /// @param start The timestamp from which the delta takes effect /// @param end The timestamp until which the delta takes effect /// @param amtPerSec The dripping rate function _addDeltaRange( DripsState storage state, uint32 start, uint32 end, int256 amtPerSec //) private { ) internal { if (start == end) return; mapping(uint32 => AmtDelta) storage amtDeltas = state.amtDeltas; _addDelta(amtDeltas, start, amtPerSec); _addDelta(amtDeltas, end, -amtPerSec); } /// @notice Adds delta of funds received by a user at a given time /// @param amtDeltas The user amount deltas /// @param timestamp The timestamp when the deltas need to be added /// @param amtPerSec The dripping rate function _addDelta( mapping(uint32 => AmtDelta) storage amtDeltas, uint256 timestamp, int256 amtPerSec //) private { ) internal virtual { unchecked { // In order to set a delta on a specific timestamp it must be introduced in two cycles. // These formulas follow the logic from `_drippedAmt`, see it for more details. int256 amtPerSecMultiplier = int256(_AMT_PER_SEC_MULTIPLIER); int256 fullCycle = (int256(uint256(_cycleSecs)) * amtPerSec) / amtPerSecMultiplier; int256 nextCycle = (int256(timestamp % _cycleSecs) * amtPerSec) / amtPerSecMultiplier; AmtDelta storage amtDelta = amtDeltas[_cycleOf(uint32(timestamp))]; // Any over- or under-flows are fine, they're guaranteed to be fixed by a matching // under- or over-flow from the other call to `_addDelta` made by `_addDeltaRange`. // This is because the total balance of `Drips` can never exceed `type(int128).max`, // so in the end no amtDelta can have delta higher than `type(int128).max`. amtDelta.thisCycle += int128(fullCycle - nextCycle); amtDelta.nextCycle += int128(nextCycle); } } /// @notice Checks if two receivers fulfil the sortedness requirement of the receivers list. /// @param prev The previous receiver /// @param prev The next receiver function _isOrdered(DripsReceiver memory prev, DripsReceiver memory next) //private internal pure returns (bool) { if (prev.userId != next.userId) return prev.userId < next.userId; return prev.config.lt(next.config); } /// @notice Calculates the amount dripped over a time range. /// The amount dripped in the `N`th second of each cycle is: /// `(N + 1) * amtPerSec / AMT_PER_SEC_MULTIPLIER - N * amtPerSec / AMT_PER_SEC_MULTIPLIER`. /// For a range of `N`s from `0` to `M` the sum of the dripped amounts is calculated as: /// `M * amtPerSec / AMT_PER_SEC_MULTIPLIER` assuming that `M <= cycleSecs`. /// For an arbitrary time range across multiple cycles the amount is calculated as the sum of /// the amount dripped in the start cycle, each of the full cycles in between and the end cycle. /// This algorithm has the following properties: /// - During every second full units are dripped, there are no partially dripped units. /// - Undripped fractions are dripped when they add up into full units. /// - Undripped fractions don't add up across cycle end boundaries. /// - Some seconds drip more units and some less. /// - Every `N`th second of each cycle drips the same amount. /// - Every full cycle drips the same amount. /// - The amount dripped in a given second is independent from the dripping start and end. /// - Dripping over time ranges `A:B` and then `B:C` is equivalent to dripping over `A:C`. /// - Different drips existing in the system don't interfere with each other. /// @param amtPerSec The dripping rate /// @param start The dripping start time /// @param end The dripping end time /// @param amt The dripped amount function _drippedAmt( uint256 amtPerSec, uint256 start, uint256 end //) private view returns (uint256 amt) { ) internal view returns (uint256 amt) { // This function is written in Yul because it can be called thousands of times // per transaction and it needs to be optimized as much as possible. // As of Solidity 0.8.13, rewriting it in unchecked Solidity triples its gas cost. uint256 cycleSecs = _cycleSecs; // solhint-disable-next-line no-inline-assembly assembly { let endedCycles := sub(div(end, cycleSecs), div(start, cycleSecs)) let amtPerCycle := div(mul(cycleSecs, amtPerSec), _AMT_PER_SEC_MULTIPLIER) amt := mul(endedCycles, amtPerCycle) let amtEnd := div(mul(mod(end, cycleSecs), amtPerSec), _AMT_PER_SEC_MULTIPLIER) amt := add(amt, amtEnd) let amtStart := div(mul(mod(start, cycleSecs), amtPerSec), _AMT_PER_SEC_MULTIPLIER) amt := sub(amt, amtStart) } } /// @notice Calculates the cycle containing the given timestamp. /// @param timestamp The timestamp. /// @return cycle The cycle containing the timestamp. //function _cycleOf(uint32 timestamp) private view returns (uint32 cycle) { function _cycleOf(uint32 timestamp) internal view returns (uint32 cycle) { unchecked { return timestamp / _cycleSecs + 1; //return timestamp + 1; // attempt to simplify } } /// @notice The current timestamp, casted to the library's internal representation. /// @return timestamp The current timestamp //function _currTimestamp() private view returns (uint32 timestamp) { function _currTimestamp() internal view returns (uint32 timestamp) { return uint32(block.timestamp); } /// @notice Returns the Drips storage. /// @return dripsStorage The storage. //function _dripsStorage() private view returns (DripsStorage storage dripsStorage) { function _dripsStorage() internal view returns (DripsStorage storage dripsStorage) { bytes32 slot = _dripsStorageSlot; // solhint-disable-next-line no-inline-assembly assembly { dripsStorage.slot := slot } } } ",./radicle_drips/specs/DripsHubBackup.spec,radicle_drips,,Yes,,"Functionality: Manage and update the states of drips receivers with added complexity involving timing and funds distribution. It includes functionalities to create drips configurations, update receiver states based on new configurations, calculate the amount dripped over a time range, and manage funds received by users within specified timeframes." 21d9c3b44533dd7b2f54ea2f42689a71,2244,rule,ifTheOnlyOneDripperStopsReceivableDripsCanNotIncrease,661,669,_receivableDrips,"// rule ifTheOnlyOneDripperStopsReceivableDripsCanNotIncrease() // { // // make sure there is only one sender and one receiver // // make sure the sender is dripping to the receiver // // calculate the _receivableDrips(receiver) before dripping stops // // stop the dripping // // calculate the _receivableDrips(receiver) after dripping stops // // after == before // } ","_receivableDrips (Lines 224-236), "," function _receivableDrips( uint256 userId, uint256 assetId, uint32 maxCycles ) public // internal view returns (uint128 receivableAmt, uint32 receivableCycles) { (receivableAmt, receivableCycles, , , ) = _receivableDripsVerbose( userId, assetId, maxCycles ); } ",./radicle_drips/specs/DripsHub.spec,radicle_drips,,Yes,,"Functionality: Calculate and return the amount receivable (`receivableAmt`) and the number of receivable cycles (`receivableCycles`) for a given user's asset, based on a maximum number of cycles (`maxCycles`), by calling another function `_receivableDripsVerbose`." 90a6b394d17ec347fccba99e4b390a9f,2276 | 2278 | 2279,rule,integrityOfSplit,143,209,split | _setSplits | _assertSplitsValid,"rule integrityOfSplit() { env e; uint256 assetId; uint256 userA_Id; uint256 userB_Id; uint256 userC_Id; uint256 userD_Id; require userA_Id != userB_Id; require userA_Id != userD_Id; require userA_Id != userC_Id; require userB_Id != userD_Id; require userB_Id != userC_Id; require userC_Id != userD_Id; // obtaining splittable/collectable states before calling split() uint128 userA_splittableBefore = splittable(e, userA_Id, assetId); require userA_splittableBefore > 0; uint128 userA_collectableBefore = collectable(e, userA_Id, assetId); uint128 userB_splittableBefore = splittable(e, userB_Id, assetId); uint128 userB_collectableBefore = collectable(e, userB_Id, assetId); uint128 userC_splittableBefore = splittable(e, userC_Id, assetId); uint128 userC_collectableBefore = collectable(e, userC_Id, assetId); uint128 userD_splittableBefore = splittable(e, userD_Id, assetId); uint128 userD_collectableBefore = collectable(e, userD_Id, assetId); // setting up the currReceivers[] of userA /// are invariant of the split rule moneyNotLostOrCreatedDuringSplit() { uint256 userA_Id; uint256 userB_Id; uint256 length; uint256 index1; uint256 userId1; uint32 weight1; userId1, weight1 = getCurrSplitsReceiverLocalArr(e, true, index1); length = getCurrSplitsReceiverLocaLength(e, true); require length == 1; // only one splitsReceiver require userId1 == userB_Id; // making sure it is userB (not limiting it to be userA) require weight1 <= 1000000; // safe assumptions since the function _assertSplitsValid() // verified that totalWeight <= _TOTAL_SPLITS_WEIGHT upon _setSplits() // we required that there is only one receiver, therefore totalWeight = weight1 // _TOTAL_SPLITS_WEIGHT == 1000000, hence the assumption above is safe // recording the state before split() // calling the split() on userA uint128 userA_collectableAmt; uint128 userA_splitAmt; userA_collectableAmt, userA_splitAmt = split(e, userA_Id, assetId, true); // recording the state after split() uint128 userA_splittableAfter = splittable(e, userA_Id, assetId); uint128 userA_collectableAfter = collectable(e, userA_Id, assetId); uint128 userB_splittableAfter = splittable(e, userB_Id, assetId); uint128 userB_collectableAfter = collectable(e, userB_Id, assetId); // the expectation: uint128 moneyBefore = userA_splittableBefore + userA_collectableBefore + userB_splittableBefore + userB_collectableBefore; uint128 moneyAfter = userA_splittableAfter + userA_collectableAfter + userB_splittableAfter + userB_collectableAfter; assert moneyBefore == moneyAfter; } uint256 index1; uint256 userId1; uint32 weight1; uint256 index2; uint256 userId2; uint32 weight2; userId2, weight2 = getCurrSplitsReceiverLocalArr(e, true, index2); require index1 != index2; // different indexes sample different splitReceivers require userId1 == userB_Id; // userB is on the list of splitReceivers of userA require userId2 != userC_Id; // the second splitReceiver is not userC require userId2 != userA_Id; // the second splitReceiver is not the splitter itself require userId2 == userD_Id; // since we run with loop_iter 2, there are max 2 receivers // obtaining splittable/collectable states after calling split() uint128 userC_splittableAfter = splittable(e, userC_Id, assetId); uint128 userC_collectableAfter = collectable(e, userC_Id, assetId); uint128 userD_splittableAfter = splittable(e, userD_Id, assetId); uint128 userD_collectableAfter = collectable(e, userD_Id, assetId); // UserA's splittable should NOT increase /// @notice The sanity rule should always fail. rule sanity { method f; env e; calldataarg args; f(e, args); assert false, ""This rule should always fail""; assert userA_splittableAfter <= userA_splittableBefore; assert userA_splittableAfter == 0; // it should be zero // UserB is on the list of UserA's splitters, therefore ""userB's splittable should NOT decrease"" assert userB_splittableAfter >= userB_splittableBefore; // UserC is NOT on the list of UserA's splitters, therefore ""userC's splittable should NOT change"" assert userC_splittableAfter == userC_splittableBefore; // UserA's collectable should NOT decrease, UserB and UserC's collectable should NOT change assert userA_collectableAfter >= userA_collectableBefore; assert userB_collectableAfter == userB_collectableBefore; assert userC_collectableAfter == userC_collectableBefore; // The increase of the splittable of the receivers userB and userD should be equal the splittable of userA assert (userB_splittableAfter - userB_splittableBefore) + (userD_splittableAfter - userD_splittableBefore) + (userA_collectableAfter - userA_collectableBefore) == userA_splittableBefore; ","split (Lines 343-349), | _setSplits (Lines 215-223), | _assertSplitsValid (Lines 229-247), "," function split( uint256 userId, IERC20 erc20, SplitsReceiver[] memory currReceivers ) public whenNotPaused returns (uint128 collectableAmt, uint128 splitAmt) { return Splits._split(userId, _assetId(erc20), currReceivers); } | function _setSplits(uint256 userId, SplitsReceiver[] memory receivers) internal { SplitsState storage state = _splitsStorage().splitsStates[userId]; bytes32 newSplitsHash = _hashSplits(receivers); emit SplitsSet(userId, newSplitsHash); if (newSplitsHash != state.splitsHash) { _assertSplitsValid(receivers, newSplitsHash); state.splitsHash = newSplitsHash; } } | function _assertSplitsValid(SplitsReceiver[] memory receivers, bytes32 receiversHash) internal { require(receivers.length <= _MAX_SPLITS_RECEIVERS, ""Too many splits receivers""); uint64 totalWeight = 0; uint256 prevUserId; for (uint256 i = 0; i < receivers.length; i++) { SplitsReceiver memory receiver = receivers[i]; uint32 weight = receiver.weight; require(weight != 0, ""Splits receiver weight is zero""); totalWeight += weight; uint256 userId = receiver.userId; if (i > 0) { require(prevUserId != userId, ""Duplicate splits receivers""); require(prevUserId < userId, ""Splits receivers not sorted by user ID""); } prevUserId = userId; emit SplitsReceiverSeen(receiversHash, userId, weight); } require(totalWeight <= _TOTAL_SPLITS_WEIGHT, ""Splits weights sum too high""); } ",./radicle_drips/specs/Splits.spec,radicle_drips,,Yes,,"Functionality: Validate and update a user's split configurations, ensuring the total weights do not exceed a set limit, receivers have non-zero weights, are unique, properly sorted, and do not exceed a maximum number. Emit relevant events for updated splits and observed receivers for tracking purposes." f96340bb5b2edebb42346d3192f88b3e,2248,rule,integrityOfPast,688,766,setDrips,"rule integrityOfPast(method f) { require requireValidSlots(); env e0; address erc20; calldataarg args; uint256 dripperId; uint256 receiverId; require erc20 == 0x100; require dripperId == 1; require receiverId == 2; // setup one dripper and one receiver with start dripping timestamp of now uint192 amtPerSec; uint32 start; uint32 duration; require amtPerSec == 1; require start == 5; require duration == 100; DH.DripsConfig configBefore = _helperCreateConfig(amtPerSec, start, duration); require e0.block.timestamp == start; int128 balanceDelta; DH.DripsReceiver currReceiverBefore; require currReceiverBefore.userId == 0; // this will force passing empty currReceivers DH.DripsReceiver newReceiverBefore; require newReceiverBefore.userId == receiverId; require receiverId != 0; require newReceiverBefore.config == configBefore; //_helperSetDrips01(e0, dripperId, erc20, currReceiverBefore, balanceDelta, newReceiverBefore); //helperSetDrips01(e0, dripperId, erc20, currReceiverBefore, balanceDelta, newReceiverBefore); // let at least one cycle pass uint32 cycleSecs = getCycleSecs(); require cycleSecs == 2; env e1; require e1.block.timestamp > e0.block.timestamp + cycleSecs; // calculate the ReceivableDripsBefore of the receiver // collectableAll() can be used if the user has also set splits uint128 ReceivableDripsBefore; uint32 receivableCyclesBefore; // type(uint32).max = 2^32 - 1 = 4294967295 ReceivableDripsBefore, receivableCyclesBefore = receivableDrips(e1, receiverId, erc20, 4294967295); // change the dripper configuration to start dripping to the receiver in the future // i.e. try to alter the past, as if the past dripping did not occur // use the same amtPerSec and duration, only change the start time to the future uint32 newStart; require newStart > e1.block.timestamp + 10 * cycleSecs; DH.DripsConfig configAfter = _helperCreateConfig(amtPerSec, newStart, duration); DH.DripsReceiver newReceiverAfter; require newReceiverAfter.userId == receiverId; require newReceiverAfter.config == configAfter; //_helperSetDrips11(e1, dripperId, erc20, newReceiverBefore, balanceDelta, newReceiverAfter); //setDrips(e1, dripperId, erc20, _helperArrOfStruct(e1, newReceiverBefore), balanceDelta, _helperArrOfStruct(e1, newReceiverAfter)); //helperSetDrips11(e1, dripperId, erc20, newReceiverBefore, balanceDelta, newReceiverAfter); // calculate again the ReceivableDripsAfter of the receiver // at a time before the newStart begins env e2; require e2.block.timestamp > e1.block.timestamp; require e2.block.timestamp < newStart; uint128 ReceivableDripsAfter; uint32 receivableCyclesAfter; ReceivableDripsAfter, receivableCyclesAfter = receivableDrips(e2, receiverId, erc20, 4294967295); // validate that the past dripping stays, i.e. what was already dripped is still receivable assert ReceivableDripsBefore == ReceivableDripsAfter; assert false; // sanity rule sanity(method f){ //uint32 cycleSecs = getCycleSecs(); //require cycleSecs == 2; //setupState(); env e; calldataarg args; f(e,args); assert false; } ","setDrips (Lines 87-102), "," function setDrips( IERC20 erc20, DripsReceiver[] calldata currReceivers, int128 balanceDelta, DripsReceiver[] calldata newReceivers ) public returns (uint128 newBalance, int128 realBalanceDelta) { if (balanceDelta > 0) _transferFromCaller(erc20, uint128(balanceDelta)); (newBalance, realBalanceDelta) = dripsHub.setDrips( calcUserId(msg.sender), erc20, currReceivers, balanceDelta, newReceivers ); if (realBalanceDelta < 0) erc20.safeTransfer(msg.sender, uint128(-realBalanceDelta)); } ",./radicle_drips/specs/DripsHub.spec,radicle_drips,,Yes,,"Functionality: This function adjusts a user's contributions to a DripsHub by transferring ERC20 tokens to or from the calling user, based on the desired balance change. It updates the user's drips configuration, transferring additional funds if increasing their balance or returning excess funds if decreasing." f40242dd01fb5bcfc6a3fadde0825f66,2362 | 2363,rule,tokensNonInterference,120,170,deposit | withdraw,"rule tokensNonInterference() { env e; calldataarg args; bool depositOrWithdraw; address user; uint256 amtDeposited; uint256 amtWithdrawn; uint256 tokenABalanceOfUserBefore; uint256 tokenABalanceOfUserAfter; uint256 tokenBBalanceOfUserBefore; uint256 tokenBBalanceOfUserAfter; uint256 tokenABalanceOfReserveBefore; uint256 tokenABalanceOfReserveAfter; uint256 tokenBBalanceOfReserveBefore; uint256 tokenBBalanceOfReserveAfter; uint256 tokenADepositedBefore; uint256 tokenADepositedAfter; uint256 tokenBDepositedBefore; uint256 tokenBDepositedAfter; tokenABalanceOfUserBefore = tokenA.balanceOf(user); tokenBBalanceOfUserBefore = tokenB.balanceOf(user); tokenABalanceOfReserveBefore = tokenA.balanceOf(reserveH); tokenBBalanceOfReserveBefore = tokenB.balanceOf(reserveH); tokenADepositedBefore = getDeposited(tokenA); tokenBDepositedBefore = getDeposited(tokenB); require tokenADepositedBefore == tokenABalanceOfReserveBefore; require tokenBDepositedBefore == tokenBBalanceOfReserveBefore; //require e.msg.sender != 0; require reserveH.getPlugins(tokenA) == 0; require reserveH.getPlugins(tokenB) == 0; //deposit(e, tokenA, user, amtDeposited); //f(e,args); //withdraw(e, tokenA, user, amtWithdrawn); if (depositOrWithdraw) { deposit(e, tokenA, user, amtDeposited); } else { withdraw(e, tokenA, user, amtWithdrawn); } require amtDeposited == amtWithdrawn; tokenABalanceOfUserAfter = tokenA.balanceOf(user); tokenBBalanceOfUserAfter = tokenB.balanceOf(user); tokenABalanceOfReserveAfter = tokenA.balanceOf(reserveH); tokenBBalanceOfReserveAfter = tokenB.balanceOf(reserveH); tokenADepositedAfter = getDeposited(tokenA); tokenBDepositedAfter = getDeposited(tokenB); assert tokenBBalanceOfUserBefore == tokenBBalanceOfUserAfter; assert tokenBBalanceOfReserveBefore == tokenBBalanceOfReserveAfter; assert tokenBDepositedBefore == tokenBDepositedAfter; } ","deposit (Lines 171-182), | withdraw (Lines 191-203), "," function deposit( IERC20 token, address from, uint256 amt ) public override onlyUser { IReservePlugin plugin = plugins[token]; require(from != address(plugin) && from != address(this), ""Reserve: deposition from self""); deposited[token] += amt; _transfer(token, from, _pluginAddr(plugin), amt); if (plugin != NO_PLUGIN) plugin.afterDeposition(token, amt); emit Deposited(msg.sender, token, from, amt); } | function withdraw( IERC20 token, address to, uint256 amt ) public override onlyUser { uint256 balance = deposited[token]; require(balance >= amt, ""Reserve: withdrawal over balance""); deposited[token] = balance - amt; IReservePlugin plugin = plugins[token]; if (plugin != NO_PLUGIN) plugin.beforeWithdrawal(token, amt); _transfer(token, _pluginAddr(plugin), to, amt); emit Withdrawn(msg.sender, token, to, amt); } ",./radicle_drips/specs/Reserve.spec,radicle_drips,,Yes,,"Functionality: Enable users to deposit and withdraw ERC20 tokens into a reserve, updating balances accordingly. It involves checking for self-depositions, handling plugin interactions before withdrawal and after deposition, and ensuring transactions do not exceed available balances. Events are emitted for both deposit and withdrawal actions." efff4092bc3b8f8ce76091de03bbe3cb,2306 | 2308 | 2309,rule,should,466,523,split | _setSplits | _assertSplitsValid," ""This rule should always fail""; } */ /// @notice front running split() does not affect receiver /// userA has a single splitReceiver userC /// userB also has the same single splitReceiver UserC /// we want to verify split() can be called on userA successfully /// even if someone front runs it and calls splits() first on userB /// no assumptions about userA, userB, userC rule cannotFrontRunSplitGeneralCase() { env e; env e2; uint256 assetId; uint256 userA_Id; uint256 userB_Id; uint256 userC_Id; uint128 userA_collectableAmt; uint128 userA_splitAmt; uint128 userA_collectableAmt2; uint128 userA_splitAmt2; uint128 userB_collectableAmt; uint128 userB_splitAmt; uint128 userA_splittableBefore = splittable(e, userA_Id, assetId); uint128 userB_splittableBefore = splittable(e, userB_Id, assetId); uint128 userC_splittableBefore = splittable(e, userC_Id, assetId); uint128 userA_collectableBefore = collectable(e, userA_Id, assetId); uint128 userB_collectableBefore = collectable(e, userB_Id, assetId); uint128 userC_collectableBefore = collectable(e, userC_Id, assetId); // prevents overflow of the splittable of the receiver userC: /// are invariant of the split rule moneyNotLostOrCreatedDuringSplit() { env e; uint256 assetId; uint256 userA_Id; uint256 userB_Id; // setting up the currReceivers[] of userA uint256 length; uint256 index1; uint256 userId1; uint32 weight1; userId1, weight1 = getCurrSplitsReceiverLocalArr(e, true, index1); length = getCurrSplitsReceiverLocaLength(e, true); require length == 1; // only one splitsReceiver require userId1 == userB_Id; // making sure it is userB (not limiting it to be userA) require weight1 <= 1000000; // safe assumptions since the function _assertSplitsValid() // verified that totalWeight <= _TOTAL_SPLITS_WEIGHT upon _setSplits() // we required that there is only one receiver, therefore totalWeight = weight1 // _TOTAL_SPLITS_WEIGHT == 1000000, hence the assumption above is safe // recording the state before split() // calling the split() on userA uint128 userA_collectableAmt; uint128 userA_splitAmt; userA_collectableAmt, userA_splitAmt = split(e, userA_Id, assetId, true); // recording the state after split() uint128 userA_splittableAfter = splittable(e, userA_Id, assetId); uint128 userA_collectableAfter = collectable(e, userA_Id, assetId); uint128 userB_splittableAfter = splittable(e, userB_Id, assetId); uint128 userB_collectableAfter = collectable(e, userB_Id, assetId); // the expectation: uint128 moneyBefore = userA_splittableBefore + userA_collectableBefore + userB_splittableBefore + userB_collectableBefore; uint128 moneyAfter = userA_splittableAfter + userA_collectableAfter + userB_splittableAfter + userB_collectableAfter; assert moneyBefore == moneyAfter; require userA_splittableBefore + userB_splittableBefore + userC_splittableBefore < 2^128; // prevents overflow of the collectable of the splitters userA and userB: require userA_collectableBefore + userA_splittableBefore < 2^128; require userB_collectableBefore + userB_splittableBefore < 2^128; // prevents overflow in the edge cases of (userC == userA) or (userC == userB): require userA_collectableBefore + userA_splittableBefore + userB_splittableBefore < 2^128; require userB_collectableBefore + userB_splittableBefore + userA_splittableBefore < 2^128; // setting up the currReceivers[] of userA and userB to be the same - singe receiver userC uint256 length1 = getCurrSplitsReceiverLocaLength(e, true); require length1 == 1; // only one receiver uint256 index1; uint256 userId1; uint32 weight1; require userId1 == userC_Id; require weight1 <= 1000000; // safe assumption require weight1 > 0; // safe assumption // safe assumptions since the function _assertSplitsValid() // verified that totalWeight <= _TOTAL_SPLITS_WEIGHT upon _setSplits() // we required that there is only one receiver, therefore totalWeight = weight1 // _TOTAL_SPLITS_WEIGHT == 1000000, hence the assumption above is safe // also _assertSplitsValid() verified that weight != 0 storage initStorage = lastStorage; uint128 userC_splittableAfterSplitA = splittable(e, userC_Id, assetId); userB_collectableAmt, userB_splitAmt = split(e, userB_Id, assetId, true) at initStorage; uint128 userC_splittableAfterSplitB = splittable(e, userC_Id, assetId); userA_collectableAmt2, userA_splitAmt2 = split@withrevert(e, userA_Id, assetId, true); assert !lastReverted; ","split (Lines 343-349), | _setSplits (Lines 215-223), | _assertSplitsValid (Lines 229-247), "," function split( uint256 userId, IERC20 erc20, SplitsReceiver[] memory currReceivers ) public whenNotPaused returns (uint128 collectableAmt, uint128 splitAmt) { return Splits._split(userId, _assetId(erc20), currReceivers); } | function _setSplits(uint256 userId, SplitsReceiver[] memory receivers) internal { SplitsState storage state = _splitsStorage().splitsStates[userId]; bytes32 newSplitsHash = _hashSplits(receivers); emit SplitsSet(userId, newSplitsHash); if (newSplitsHash != state.splitsHash) { _assertSplitsValid(receivers, newSplitsHash); state.splitsHash = newSplitsHash; } } | function _assertSplitsValid(SplitsReceiver[] memory receivers, bytes32 receiversHash) internal { require(receivers.length <= _MAX_SPLITS_RECEIVERS, ""Too many splits receivers""); uint64 totalWeight = 0; uint256 prevUserId; for (uint256 i = 0; i < receivers.length; i++) { SplitsReceiver memory receiver = receivers[i]; uint32 weight = receiver.weight; require(weight != 0, ""Splits receiver weight is zero""); totalWeight += weight; uint256 userId = receiver.userId; if (i > 0) { require(prevUserId != userId, ""Duplicate splits receivers""); require(prevUserId < userId, ""Splits receivers not sorted by user ID""); } prevUserId = userId; emit SplitsReceiverSeen(receiversHash, userId, weight); } require(totalWeight <= _TOTAL_SPLITS_WEIGHT, ""Splits weights sum too high""); } ",./radicle_drips/specs/Splits.spec,radicle_drips,,Yes,,"Functionality: Validate and record splitting configurations for users, ensuring that each split configuration has unique, non-zero-weighted receivers sorted by user ID, does not exceed the maximum number of receivers or the total permissible weight, and emits events for changes in splits settings and validations." 9597955c8e2bbe3631118f5f7c3e7f61,2397,rule,solvency,109,146,_tokenBalanceOf,"rule solvency(address token, address from, address to, uint256 amount, uint256 share, method f, uint256 _strategy, uint256 strategy_, address straToken, uint256 _systemBalance, uint256 systemBalance_, uint256 _strategyBalance, uint256 strategyBalance_ ) { //link the strategy to the current token require strategyInstance.token() == token; // link the strategy owner to the bentobox require strategyInstance.owner() == currentContract; require harnessBorrower() == borrower; require harnessToken() == token; mathint _actual = tokenBalanceOf(token); //casting form uint256 require _systemBalance == tokenBalanceOfUser(token, currentContract); mathint _asElastic = totalTokenAmount(token); //casting from uint128 require _actual == _asElastic; require _strategy == getStrategyTokenBalance(token); require straToken == strategy(token); //proven in zeroStrategy invariant zeroStrategy(address token) strategy(token) == 0 => getStrategyTokenBalance(token) == 0 invariant integrityOfTotalShare(address token) totalTokenShare(token) == shareSum(token) // Rules /** * solvency: require straToken == 0 => _strategy == 0; require _strategyBalance == tokenBalanceOfUser(token, straToken); env e; calldataarg args; callFunctionWithParams(token, from, to, amount, share, f); mathint actual_ = tokenBalanceOf(token); //casting form uint256 require systemBalance_ == tokenBalanceOfUser(token, currentContract); mathint asElastic_ = totalTokenAmount(token); //casting from uint128 require strategy_ == getStrategyTokenBalance(token); assert actual_ >= asElastic_, ""system solvency is broken""; } * internal representation of total assets: * _tokenBalanceOf(token) >= totals[token].elastics * checking if the the total assets within the BentoBox and outside * the BentoBox are preserved */ ","_tokenBalanceOf (Lines 762-764), "," function _tokenBalanceOf(IERC20 token) internal view returns (uint256 amount) { amount = token.balanceOf(address(this)).add(strategyData[token].balance); } ",./sushi_benttobox/spec/bentoBoxCompoundStrategy.spec,sushi_benttobox,,Yes,,"Functionality: Retrieve and sum the current balance of a specified ERC20 token held by the contract with any additional balance recorded in the contract's strategy data, then return the total amount as a uint256 variable." 7e9aac58bbbccc00c71b63c64ab4325f,2412,rule,validDecreaseToBalanceOf,198,213,transferMultiple,"rule validDecreaseToBalanceOf(address token, address a, address from, address to, address other, method f) { uint256 amount; uint256 share; require from == harnessFrom(); uint256 vBefore = balanceOf(token, a); callFunctionWithParams(token, from, to, amount, share, f); uint256 vAfter = balanceOf(token, a); assert (vBefore > vAfter => ( from == a && ( f.selector == transfer(address, address, address, uint256).selector || f.selector == withdraw(address, address, address, uint256, uint256).selector || f.selector == transferMultiple(address, address, address[], uint256[]).selector))); } ","transferMultiple (Lines 42-51), "," function transferMultiple(IERC20 token, address from, address[] calldata tos, uint256[] calldata shares) public override { require (tos.length <= 3); // this would not constraint tos for any other rule except noChangeToOthersBalances, // because harnessOther is only constraint in noChangeToOthersBalances assumeTosNotOther(tos, harnessOther); require(from == harnessFrom); super.transferMultiple(token, from, tos, shares); } ",./sushi_benttobox/spec/bentoBoxCompoundStrategy.spec,sushi_benttobox,,Yes,,"Functionality: Enforce restrictions on bulk token transfers by verifying that the number of recipient addresses (`tos`) does not exceed three, ensuring these addresses are not a specific unauthorized address (`harnessOther`), and confirming the sender (`from`) matches a predetermined address (`harnessFrom`) before executing the transfer." f6e4ce1109082cda4c2c1c2473673d39,2642,rule,named,135,151,toBase," Contributes to proof of rule named ""Identity"" in GDoc. */ rule toElasticAndToBaseAreInverse2down { bool roundUp = false; uint128 base1 = getBase(); uint128 elastic1 = getElastic(); require base1 != 0 && elastic1 != 0; uint256 amount; uint256 amountToBase = toBase(amount, roundUp); uint256 amountToBaseToElastic = toElastic(amountToBase, roundUp); uint256 error_margin2 = elastic1 / base1 + 1; assert only_slightly_larger_than(amount, amountToBaseToElastic, error_margin2); } ","toBase (Lines 21-23), "," function toBase(uint256 elastic, bool roundUp) public view returns (uint256 base) { base = rebase.toBase(elastic, roundUp); } ",./sushi_benttobox/spec/rebase.spec,sushi_benttobox,,Yes,,"Functionality: Convert a specified amount (elastic) into its base equivalent by calling the `toBase` method from the `rebase` object. Additionally, the conversion adheres to a rounding direction specified by the `roundUp` boolean, determining whether to round the resulting base value up or not." e1b2e43f16e886fc37949cddfb10f341,2441,rule,validDecreaseToBalanceOf,182,197,transferMultiple,"rule validDecreaseToBalanceOf(address token, address a, address from, address to, address other, method f) { uint256 amount; uint256 share; require from == harnessFrom(); uint256 vBefore = balanceOf(token, a); callFunctionWithParams(token, from, to, amount, share, f); uint256 vAfter = balanceOf(token, a); assert (vBefore > vAfter => ( from == a && ( f.selector == transfer(address, address, address, uint256).selector || f.selector == withdraw(address, address, address, uint256, uint256).selector || f.selector == transferMultiple(address, address, address[], uint256[]).selector))); } ","transferMultiple (Lines 42-51), "," function transferMultiple(IERC20 token, address from, address[] calldata tos, uint256[] calldata shares) public override { require (tos.length <= 3); // this would not constraint tos for any other rule except noChangeToOthersBalances, // because harnessOther is only constraint in noChangeToOthersBalances assumeTosNotOther(tos, harnessOther); require(from == harnessFrom); super.transferMultiple(token, from, tos, shares); } ",./sushi_benttobox/spec/bentobox.spec,sushi_benttobox,,Yes,,"Functionality: Enforce a rule where only a maximum of three recipients can be specified for a token transfer operation from a single source. Additionally, ensure that the transaction originates only from a specified address and excludes any transfers that might affect a predefined set of balances negatively." d296507cc1113d76649bdf4021822e7c,2388 | 2389,rule,integrityExit,152,165,exit | checkAplusBeqC,"rule integrityExit(uint256 balance) { require receiver() == receiverInstance; env e; uint256 balanceBefore = tokenBalanceOf(tokenInstance,receiverInstance); int256 amountAdded = exit(e, balance); uint256 balanceAfter = tokenBalanceOf(tokenInstance,receiverInstance); mathint t = balanceBefore + balance; require t <= MAX_UNSIGNED_INT(); uint256 expectedBalance = balanceBefore + balance; assert checkAplusBeqC(expectedBalance, amountAdded, balanceAfter), ""wrong balance transfered to receiver""; }","exit (Lines 46-50), | checkAplusBeqC (Lines 28-36), "," function exit(uint256 balance) external override returns (int256 amountAdded) { uint256 b = getCurrentBalance(balance); token.transfer(receiver, b); return safeSub(b, balance); } | function checkAplusBeqC(uint256 a, int256 b, uint256 c) public returns (bool) { if (b >= 0) { uint256 b_ = uint256(b); return a.add(b_) == c; } else if (b < 0) { uint256 b_ = uint256(-b); return a.sub(b_) == c; } } ",./sushi_benttobox/spec/compoundStrategy.spec,sushi_benttobox,,Yes,,"Functionality: Transfer a specific balance amount from the token to the receiver and return the difference between the final balance and the initial balance. Additionally, evaluate whether the sum of the first number and a signed second number equals the third number, handling both positive and negative cases for the second number." 28cad6aafbbf352327b1ba4df3d94fdb,2392,invariant,zeroStrategy,94,146,_tokenBalanceOf,"invariant zeroStrategy(address token) strategy(token) == 0 => getStrategyTokenBalance(token) == 0 invariant integrityOfTotalShare(address token) totalTokenShare(token) == shareSum(token) // Rules /** * solvency: rule solvency(address token, address from, address to, uint256 amount, uint256 share, method f, uint256 _strategy, uint256 strategy_, address straToken, uint256 _systemBalance, uint256 systemBalance_, uint256 _strategyBalance, uint256 strategyBalance_ ) { //link the strategy to the current token require strategyInstance.token() == token; // link the strategy owner to the bentobox require strategyInstance.owner() == currentContract; require harnessBorrower() == borrower; require harnessToken() == token; mathint _actual = tokenBalanceOf(token); //casting form uint256 require _systemBalance == tokenBalanceOfUser(token, currentContract); mathint _asElastic = totalTokenAmount(token); //casting from uint128 require _actual == _asElastic; require _strategy == getStrategyTokenBalance(token); require straToken == strategy(token); //proven in zeroStrategy require straToken == 0 => _strategy == 0; require _strategyBalance == tokenBalanceOfUser(token, straToken); env e; calldataarg args; callFunctionWithParams(token, from, to, amount, share, f); mathint actual_ = tokenBalanceOf(token); //casting form uint256 require systemBalance_ == tokenBalanceOfUser(token, currentContract); mathint asElastic_ = totalTokenAmount(token); //casting from uint128 require strategy_ == getStrategyTokenBalance(token); assert actual_ >= asElastic_, ""system solvency is broken""; } * internal representation of total assets: * _tokenBalanceOf(token) >= totals[token].elastics * checking if the the total assets within the BentoBox and outside * the BentoBox are preserved */ ","_tokenBalanceOf (Lines 762-764), "," function _tokenBalanceOf(IERC20 token) internal view returns (uint256 amount) { amount = token.balanceOf(address(this)).add(strategyData[token].balance); } ",./sushi_benttobox/spec/bentoBoxCompoundStrategy.spec,sushi_benttobox,,Yes,,Functionality: Calculate the total balance of a specific ERC20 token associated with the contract by summing the token's balance directly held by the contract and the balance recorded in a separate strategy data structure for that token. 79c9aec6f0686cbc25a0242e49b76a72,2379,rule,ifExitedIsTrueThenMethodsRevertExceptOwner,101,113,claimOwnership,"rule ifExitedIsTrueThenMethodsRevertExceptOwner() { env e; require exited() == true; method f; calldataarg args; require !f.isView && f.selector != claimOwnership().selector; require e.msg.sender != owner(); f@withrevert(e, args); assert(lastReverted, ""Methods didn't revert""); } ","claimOwnership (Lines 386-396), "," function claimOwnership() public { address _pendingOwner = pendingOwner; // Checks require(msg.sender == _pendingOwner, ""Ownable: caller != pending owner""); // Effects emit OwnershipTransferred(owner, _pendingOwner); owner = _pendingOwner; pendingOwner = address(0); } ",./sushi_benttobox/spec/compoundStrategy.spec,sushi_benttobox,,Yes,,"Functionality: Transfer the contract's ownership to a pre-designated pending owner. It verifies that the caller of this function is the current pending owner, then emits an event signaling the transfer of ownership, updates the contract's owner to the new owner, and resets the pending owner to a null address." 09961ed97c1b3fd554fc854fbe72872a,2450,rule,toBaseIsMonotone,48,56,toBase,"rule toBaseIsMonotone { bool roundUp; uint x; uint y; require x < y; uint xToBase = toBase(x, roundUp); uint yToBase = toBase(y, roundUp); assert xToBase <= yToBase; } ","toBase (Lines 21-23), "," function toBase(uint256 elastic, bool roundUp) public view returns (uint256 base) { base = rebase.toBase(elastic, roundUp); } ",./sushi_benttobox/spec/rebase.spec,sushi_benttobox,,Yes,,"Functionality: Convert an amount specified in the elastic unit to the base unit using the `rebase` object's `toBase` method, optionally rounding up the result. The conversion is defined by the rebase mechanism's underlying logic, which typically involves adjusting for accumulated interest or contract-specific scaling factors." 8f1ff28b8387f64d1e0676b313659db6,2364 | 2366,rule,integrityHarvest,46,67,harvest | checkAplusBeqC,"rule integrityHarvest(uint256 balance, uint256 strategyBalanceBefore) { require receiver() == receiverInstance; require strategyBalanceBefore == tokenInstance.balanceOf(currentContract); uint256 balanceBefore = tokenInstance.balanceOf(receiverInstance); env e; int256 amountAdded = harvest(e, balance,_); require amountAdded < MIN_INT(); uint256 strategyBalanceAfter = tokenInstance.balanceOf(currentContract); uint256 balanceAfter = tokenInstance.balanceOf(receiverInstance); if (compareGTzero(amountAdded)) { // strategy made profit assert checkAplusBeqC(balanceBefore, amountAdded, balanceAfter), ""wrong balance transfered to receiver""; } else { // strategy made loss assert balanceBefore == balanceAfter, ""balance should not change if profit is negative""; } } ","harvest (Lines 26-33), | checkAplusBeqC (Lines 28-36), "," function harvest(uint256 balance, address sender) external override returns (int256 amountAdded) { uint256 b = getCurrentBalance(balance); int256 gain = safeSub(b, balance); if (gain > 0) { token.transfer(receiver, uint256(gain)); } return gain; } | function checkAplusBeqC(uint256 a, int256 b, uint256 c) public returns (bool) { if (b >= 0) { uint256 b_ = uint256(b); return a.add(b_) == c; } else if (b < 0) { uint256 b_ = uint256(-b); return a.sub(b_) == c; } } ",./sushi_benttobox/spec/strategy.spec,sushi_benttobox,,Yes,,"Functionality: Calculate and transfer the positive gains of a balance to a receiver by subtracting the initial balance from the current balance. Also, verify if the sum or difference of two numbers (a plus/minus b) equals a third number (c), handling both positive and negative cases for b." 9a553dd57f62d2198f955fd7453a7dc5,2421,invariant,zeroStrategy,78,130,_tokenBalanceOf,"invariant zeroStrategy(address token) strategy(token) == 0 => getStrategyTokenBalance(token) == 0 invariant integrityOfTotalShare(address token) totalTokenShare(token) == shareSum(token) // Rules /** * solvency: rule solvency(address token, address from, address to, uint256 amount, uint256 share, method f, uint256 _strategy, uint256 strategy_, address straToken, uint256 _systemBalance, uint256 systemBalance_, uint256 _strategyBalance, uint256 strategyBalance_ ) { //link the strategy to the current token require strategyInstance.token() == token; // link the strategy owner to the bentobox require strategyInstance.owner() == currentContract; require harnessBorrower() == borrower; require harnessToken() == token; mathint _actual = tokenBalanceOf(token); //casting form uint256 require _systemBalance == tokenBalanceOfUser(token, currentContract); mathint _asElastic = totalTokenAmount(token); //casting from uint128 require _actual == _asElastic; require _strategy == getStrategyTokenBalance(token); require straToken == strategy(token); //proven in zeroStrategy require straToken == 0 => _strategy == 0; require _strategyBalance == tokenBalanceOfUser(token, straToken); env e; calldataarg args; callFunctionWithParams(token, from, to, amount, share, f); mathint actual_ = tokenBalanceOf(token); //casting form uint256 require systemBalance_ == tokenBalanceOfUser(token, currentContract); mathint asElastic_ = totalTokenAmount(token); //casting from uint128 require strategy_ == getStrategyTokenBalance(token); assert actual_ >= asElastic_, ""system solvency is broken""; } * internal representation of total assets: * _tokenBalanceOf(token) >= totals[token].elastics * checking if the the total assets within the BentoBox and outside * the BentoBox are preserved */ ","_tokenBalanceOf (Lines 762-764), "," function _tokenBalanceOf(IERC20 token) internal view returns (uint256 amount) { amount = token.balanceOf(address(this)).add(strategyData[token].balance); } ",./sushi_benttobox/spec/bentobox.spec,sushi_benttobox,,Yes,,"Functionality: Calculate and return the combined total balance of a specified ERC20 token, considering both the balance held directly by the contract and an additional amount potentially managed by a strategy, as indicated in `strategyData` for that token." 5b6710c4341e78f1615e26805e65fcc4,518 | 519 | 520 | 522 | 524,rule,integrityOfEmpty,128,132,isUsingAsCollateralOrBorrowing | isBorrowing | isUsingAsCollateral | isUsingAsCollateralAny | isBorrowingAny,"// rule integrityOfEmpty(uint256 reserveIndex){ // bool borrowingOrCollateral = isUsingAsCollateralOrBorrowing(reserveIndex); invariant isUsingAsCollateralOrBorrowing(uint256 reserveIndex ) (isUsingAsCollateral(reserveIndex) || isBorrowing(reserveIndex)) <=> isUsingAsCollateralOrBorrowing(reserveIndex) // // if at 1 asset is used as collateral and isUsingAsCollateralOne, then any other asset is not used as collateral // rule integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther){ invariant integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther) isUsingAsCollateral(reserveIndex) && isUsingAsCollateralOne() => !isUsingAsCollateral(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is used as collateral, isUsingAsCollateralAny is true // // ** not implmented yet - if isUsingAsCollateralAny is true then there exist at least 1 asset that is being used as collateral // rule integrityOfisUsingAsCollateralAny(uint256 reserveIndex){ invariant integrityOfisUsingAsCollateralAny(uint256 reserveIndex) isUsingAsCollateral(reserveIndex) => isUsingAsCollateralAny() // // if at 1 asset is used for borrowing and isBorrowingOne, then any other asset is not used for borrowing // rule integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther){ invariant integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther) isBorrowing(reserveIndex) && isBorrowingOne() => !isBorrowing(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is borrowed, isBorrowing is true // // ** not implmented yet - if isBorrowingAny is true then there exist at least 1 asset that is being used for borrowing // rule integrityOfisBorrowingAny(uint256 reserveIndex){ invariant integrityOfisBorrowingAny(uint256 reserveIndex) isBorrowing(reserveIndex) => isBorrowingAny() // // if user data is empty then for any index neither borrowing nor collateral is set invariant integrityOfEmpty(uint256 reserveIndex) isEmpty() => !isBorrowingAny() && !isUsingAsCollateralOrBorrowing(reserveIndex) // invariant notEmpty(uint256 reserveIndex) // (isBorrowingAny() || isUsingAsCollateral(reserveIndex)) => !isEmpty() // if IsolationModeState is active then there must be exactly one asset register as collateral. // note that this is a necessary requirement, but it is not sufficient. rule integrityOfIsolationModeState(){ // invariant integrityOfIsolationModeState(calldataarg args) // !isUsingAsCollateralOne() => !isIsolated() rule integrityOfSiloedBorrowingState(){ bool existExactlyOneBorrow = isBorrowingOne(); bool answer; address asset; answer, asset = getSiloedBorrowingState(); assert answer => existExactlyOneBorrow; } bool existExactlyOneCollateral = isUsingAsCollateralOne(); bool answer; address asset; uint256 ceiling; answer, asset, ceiling = getIsolationModeState(); assert answer => existExactlyOneCollateral; // bool anyBorrowed = isBorrowingAny(); // assert isEmpty() => !borrowingOrCollateral; // } // bool reserveBorrowing = isBorrowing(reserveIndex); // assert reserveBorrowing => isBorrowingAny(); // // assert exists uint256 index. isBorrowingAny() => isBorrowing(index); // bool reserveBorrowingOther = isBorrowing(reserveIndexOther); // assert reserveBorrowing && isBorrowingOne() => !reserveBorrowingOther || reserveIndexOther == reserveIndex; // bool reserveCollateral = isUsingAsCollateral(reserveIndex); // assert reserveCollateral => isUsingAsCollateralAny(); // // assert exists uint256 index. isUsingAsCollateralAny() => isUsingAsCollateral(index); // bool reserveCollateralOther = isUsingAsCollateral(reserveIndexOther); // assert reserveCollateral && isUsingAsCollateralOne() => !reserveCollateralOther || reserveIndexOther == reserveIndex; ","isUsingAsCollateralOrBorrowing (Lines 71-79), | isBorrowing (Lines 87-95), | isUsingAsCollateral (Lines 103-111), | isUsingAsCollateralAny (Lines 131-135), | isBorrowingAny (Lines 153-155), "," function isUsingAsCollateralOrBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 3 != 0; } } | function isBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 1 != 0; } } | function isUsingAsCollateral( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; } } | function isUsingAsCollateralAny( DataTypes.UserConfigurationMap memory self ) internal pure returns (bool) { return self.data & COLLATERAL_MASK != 0; } | function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data & BORROWING_MASK != 0; } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,"Functionality: Determine the borrowing and collateral usage status of a user within a financial protocol. The code checks if a user is using assets as collateral, borrowing, or engaging in any of these activities, leveraging bitwise operations to query the user's configuration stored as a compact data structure." 0b98e96bc072889dd6362ca32821031f,418 | 419,rule,burnNoChangeToOther,271,287,balanceOf | burn,"rule burnNoChangeToOther(address user, address recieverOfUnderlying, uint256 amount, uint256 index, address other) { require other != user && other != recieverOfUnderlying; env e; uint256 otherDataBefore = additionalData(other); uint256 otherBalanceBefore = balanceOf(other); burn(e, user, recieverOfUnderlying, amount, index); uint256 otherDataAfter = additionalData(other); uint256 otherBalanceAfter = balanceOf(other); assert otherDataBefore == otherDataAfter && otherBalanceBefore == otherBalanceAfter; } ","balanceOf (Lines 101-112), | burn (Lines 178-245), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/AToken.spec,aave_v3,,Yes,,"Functionality: Adjust user and total balances within a lending protocol by calculating compounded interest for account balances, and manage token supply through burn or mint operations based on the repayment or accrual of interest, ensuring accounting integrity through dynamic total supply and rate adjustments." 34abe06e6ad3fdef0c1c59516bf3ec68,437 | 438 | 439 | 441 | 443,invariant,integrityOfisUsingAsCollateralOne,91,101,isUsingAsCollateralOrBorrowing | isBorrowing | isUsingAsCollateral | isUsingAsCollateralAny | isBorrowingAny,"invariant integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther) isUsingAsCollateral(reserveIndex) && isUsingAsCollateralOne() => !isUsingAsCollateral(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is used as collateral, isUsingAsCollateralAny is true // // ** not implmented yet - if isUsingAsCollateralAny is true then there exist at least 1 asset that is being used as collateral // rule integrityOfisUsingAsCollateralAny(uint256 reserveIndex){ invariant integrityOfisUsingAsCollateralAny(uint256 reserveIndex) isUsingAsCollateral(reserveIndex) => isUsingAsCollateralAny() // // if at 1 asset is used for borrowing and isBorrowingOne, then any other asset is not used for borrowing // rule integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther){ invariant integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther) isBorrowing(reserveIndex) && isBorrowingOne() => !isBorrowing(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is borrowed, isBorrowing is true // // ** not implmented yet - if isBorrowingAny is true then there exist at least 1 asset that is being used for borrowing // rule integrityOfisBorrowingAny(uint256 reserveIndex){ invariant integrityOfisBorrowingAny(uint256 reserveIndex) isBorrowing(reserveIndex) => isBorrowingAny() // // if user data is empty then for any index neither borrowing nor collateral is set // rule integrityOfEmpty(uint256 reserveIndex){ invariant integrityOfEmpty(uint256 reserveIndex) isEmpty() => !isBorrowingAny() && !isUsingAsCollateralOrBorrowing(reserveIndex) invariant isUsingAsCollateralOrBorrowing(uint256 reserveIndex ) (isUsingAsCollateral(reserveIndex) || isBorrowing(reserveIndex)) <=> isUsingAsCollateralOrBorrowing(reserveIndex) // // if at 1 asset is used as collateral and isUsingAsCollateralOne, then any other asset is not used as collateral // rule integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther){ // invariant notEmpty(uint256 reserveIndex) // (isBorrowingAny() || isUsingAsCollateral(reserveIndex)) => !isEmpty() // if IsolationModeState is active then there must be exactly one asset register as collateral. // note that this is a necessary requirement, but it is not sufficient. rule integrityOfIsolationModeState(){ // invariant integrityOfIsolationModeState(calldataarg args) // !isUsingAsCollateralOne() => !isIsolated() rule integrityOfSiloedBorrowingState(){ bool existExactlyOneBorrow = isBorrowingOne(); bool answer; address asset; answer, asset = getSiloedBorrowingState(); assert answer => existExactlyOneBorrow; } bool existExactlyOneCollateral = isUsingAsCollateralOne(); bool answer; address asset; uint256 ceiling; answer, asset, ceiling = getIsolationModeState(); assert answer => existExactlyOneCollateral; // bool borrowingOrCollateral = isUsingAsCollateralOrBorrowing(reserveIndex); // bool anyBorrowed = isBorrowingAny(); // assert isEmpty() => !borrowingOrCollateral; // } // bool reserveBorrowing = isBorrowing(reserveIndex); // assert reserveBorrowing => isBorrowingAny(); // // assert exists uint256 index. isBorrowingAny() => isBorrowing(index); // bool reserveBorrowingOther = isBorrowing(reserveIndexOther); // assert reserveBorrowing && isBorrowingOne() => !reserveBorrowingOther || reserveIndexOther == reserveIndex; // bool reserveCollateral = isUsingAsCollateral(reserveIndex); // assert reserveCollateral => isUsingAsCollateralAny(); // // assert exists uint256 index. isUsingAsCollateralAny() => isUsingAsCollateral(index); // bool reserveCollateralOther = isUsingAsCollateral(reserveIndexOther); // assert reserveCollateral && isUsingAsCollateralOne() => !reserveCollateralOther || reserveIndexOther == reserveIndex; ","isUsingAsCollateralOrBorrowing (Lines 71-79), | isBorrowing (Lines 87-95), | isUsingAsCollateral (Lines 103-111), | isUsingAsCollateralAny (Lines 131-135), | isBorrowingAny (Lines 153-155), "," function isUsingAsCollateralOrBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 3 != 0; } } | function isBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 1 != 0; } } | function isUsingAsCollateral( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; } } | function isUsingAsCollateralAny( DataTypes.UserConfigurationMap memory self ) internal pure returns (bool) { return self.data & COLLATERAL_MASK != 0; } | function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data & BORROWING_MASK != 0; } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,"Functionality: Determine the user's interaction with reserves in a decentralized finance protocol by checking if assets are being used as collateral or if there is any borrowing activity. These functions facilitate querying whether specific reserves are utilized for borrowing, used as collateral, or if any such activities exist across all reserves." d286347bed34bbf33e877c5571387424,400,rule,permitIntegrity,58,65,permit,"rule permitIntegrity(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) { env e; uint256 nonceBefore = nonces(owner); permit(e, owner, spender, value, deadline, v, r, s); assert allowance(owner, spender) == value; assert nonces(owner) == nonceBefore + 1; } ","permit (Lines 170-193), "," function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external override { require(owner != address(0), Errors.ZERO_ADDRESS_NOT_VALID); //solium-disable-next-line require(block.timestamp <= deadline, Errors.INVALID_EXPIRATION); uint256 currentValidNonce = _nonces[owner]; bytes32 digest = keccak256( abi.encodePacked( '\x19\x01', DOMAIN_SEPARATOR(), keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) ) ); require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE); _nonces[owner] = currentValidNonce + 1; _approve(owner, spender, value); } ",./aave_v3/specs/AToken.spec,aave_v3,,Yes,,"Functionality: Validate the signature for a permit to approve a spender by an owner within a deadline, using EIP-2612's permit method. It increases the nonce for the owner after authenticating the permit and approves the spender to spend a specified value of the owner's tokens." 9834b93a34f460bef753f707bca5e47f,594 | 595,rule,burnNoChangeToOther,292,307,balanceOf | burn,"rule burnNoChangeToOther(address user, uint256 amount, address other) { require other != user; env e; uint256 otherDataBefore = additionalData(other); uint256 otherBalanceBefore = balanceOf(e, other); burn(e, user, amount); uint256 otherDataAfter = additionalData(other); uint256 otherBalanceAfter = balanceOf(e, other); assert otherDataBefore == otherDataAfter && otherBalanceBefore == otherBalanceAfter; } ","balanceOf (Lines 101-112), | burn (Lines 178-245), "," function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/StableDebtToken.spec,aave_v3,,Yes,,"Functionality: Adjust a user's account balance by calculating compounded interest based on their stable rate and timestamp, and manage the burning of tokens. This involves updating total supply, user stable rates, and average stable rates, while handling cases where the total supply becomes fully repaid or overpaid." cd74f6164fe5b2edd58a2c20ba1ccdc0,586 | 587 | 588,rule,inverseMintBurn,201,208,mint | balanceOf | burn,"rule inverseMintBurn(address a, address delegatedUser, uint256 amount, uint256 rate) { env e; uint256 balancebefore = balanceOf(e, a); mint(e, delegatedUser, a, amount, rate); burn(e, a, amount); uint256 balanceAfter = balanceOf(e, a); assert balancebefore == balanceAfter, ""burn is not the inverse of mint""; } ","mint (Lines 124-175), | balanceOf (Lines 101-112), | burn (Lines 178-245), "," function mint( address user, address onBehalfOf, uint256 amount, uint256 rate ) external virtual override onlyPool returns (bool, uint256, uint256) { MintLocalVars memory vars; if (user != onBehalfOf) { _decreaseBorrowAllowance(onBehalfOf, user, amount); } (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf); vars.previousSupply = totalSupply(); vars.currentAvgStableRate = _avgStableRate; vars.nextSupply = _totalSupply = vars.previousSupply + amount; vars.amountInRay = amount.wadToRay(); vars.currentStableRate = _userState[onBehalfOf].additionalData; vars.nextStableRate = (vars.currentStableRate.rayMul(currentBalance.wadToRay()) + vars.amountInRay.rayMul(rate)).rayDiv((currentBalance + amount).wadToRay()); _userState[onBehalfOf].additionalData = vars.nextStableRate.toUint128(); //solium-disable-next-line _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp); // Calculates the updated average stable rate vars.currentAvgStableRate = _avgStableRate = ( (vars.currentAvgStableRate.rayMul(vars.previousSupply.wadToRay()) + rate.rayMul(vars.amountInRay)).rayDiv(vars.nextSupply.wadToRay()) ).toUint128(); uint256 amountToMint = amount + balanceIncrease; _mint(onBehalfOf, amountToMint, vars.previousSupply); emit Transfer(address(0), onBehalfOf, amountToMint); emit Mint( user, onBehalfOf, amountToMint, currentBalance, balanceIncrease, vars.nextStableRate, vars.currentAvgStableRate, vars.nextSupply ); return (currentBalance == 0, vars.nextSupply, vars.currentAvgStableRate); } | function balanceOf(address account) public view virtual override returns (uint256) { uint256 accountBalance = super.balanceOf(account); uint256 stableRate = _userState[account].additionalData; if (accountBalance == 0) { return 0; } uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( stableRate, _timestamps[account] ); return accountBalance.rayMul(cumulatedInterest); } | function burn( address from, uint256 amount ) external virtual override onlyPool returns (uint256, uint256) { (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(from); uint256 previousSupply = totalSupply(); uint256 nextAvgStableRate = 0; uint256 nextSupply = 0; uint256 userStableRate = _userState[from].additionalData; // Since the total supply and each single user debt accrue separately, // there might be accumulation errors so that the last borrower repaying // might actually try to repay more than the available debt supply. // In this case we simply set the total supply and the avg stable rate to 0 if (previousSupply <= amount) { _avgStableRate = 0; _totalSupply = 0; } else { nextSupply = _totalSupply = previousSupply - amount; uint256 firstTerm = uint256(_avgStableRate).rayMul(previousSupply.wadToRay()); uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); // For the same reason described above, when the last user is repaying it might // happen that user rate * user balance > avg rate * total supply. In that case, // we simply set the avg rate to 0 if (secondTerm >= firstTerm) { nextAvgStableRate = _totalSupply = _avgStableRate = 0; } else { nextAvgStableRate = _avgStableRate = ( (firstTerm - secondTerm).rayDiv(nextSupply.wadToRay()) ).toUint128(); } } if (amount == currentBalance) { _userState[from].additionalData = 0; _timestamps[from] = 0; } else { //solium-disable-next-line _timestamps[from] = uint40(block.timestamp); } //solium-disable-next-line _totalSupplyTimestamp = uint40(block.timestamp); if (balanceIncrease > amount) { uint256 amountToMint = balanceIncrease - amount; _mint(from, amountToMint, previousSupply); emit Transfer(address(0), from, amountToMint); emit Mint( from, from, amountToMint, currentBalance, balanceIncrease, userStableRate, nextAvgStableRate, nextSupply ); } else { uint256 amountToBurn = amount - balanceIncrease; _burn(from, amountToBurn, previousSupply); emit Transfer(from, address(0), amountToBurn); emit Burn(from, amountToBurn, currentBalance, balanceIncrease, nextAvgStableRate, nextSupply); } return (nextSupply, nextAvgStableRate); } ",./aave_v3/specs/StableDebtToken.spec,aave_v3,,Yes,,"Functionality: Adjust users' balances and the system's total supply based on mint and burn actions. The code calculates new user and system-wide interest rates when tokens are minted or burned, updates ownership and timestamps, and emits events to log these actions." 6cba815f971eb840c0cfa029125938b9,427 | 428 | 429 | 431 | 433,invariant,isUsingAsCollateralOrBorrowing,81,89,isUsingAsCollateralOrBorrowing | isBorrowing | isUsingAsCollateral | isUsingAsCollateralAny | isBorrowingAny,"invariant isUsingAsCollateralOrBorrowing(uint256 reserveIndex ) (isUsingAsCollateral(reserveIndex) || isBorrowing(reserveIndex)) <=> isUsingAsCollateralOrBorrowing(reserveIndex) // // if at 1 asset is used as collateral and isUsingAsCollateralOne, then any other asset is not used as collateral // rule integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther){ invariant integrityOfisUsingAsCollateralOne(uint256 reserveIndex, uint256 reserveIndexOther) isUsingAsCollateral(reserveIndex) && isUsingAsCollateralOne() => !isUsingAsCollateral(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is used as collateral, isUsingAsCollateralAny is true // // ** not implmented yet - if isUsingAsCollateralAny is true then there exist at least 1 asset that is being used as collateral // rule integrityOfisUsingAsCollateralAny(uint256 reserveIndex){ invariant integrityOfisUsingAsCollateralAny(uint256 reserveIndex) isUsingAsCollateral(reserveIndex) => isUsingAsCollateralAny() // // if at 1 asset is used for borrowing and isBorrowingOne, then any other asset is not used for borrowing // rule integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther){ invariant integrityOfisBorrowingOne(uint256 reserveIndex, uint256 reserveIndexOther) isBorrowing(reserveIndex) && isBorrowingOne() => !isBorrowing(reserveIndexOther) || reserveIndexOther == reserveIndex // // if at least 1 asset is borrowed, isBorrowing is true // // ** not implmented yet - if isBorrowingAny is true then there exist at least 1 asset that is being used for borrowing // rule integrityOfisBorrowingAny(uint256 reserveIndex){ invariant integrityOfisBorrowingAny(uint256 reserveIndex) isBorrowing(reserveIndex) => isBorrowingAny() // // if user data is empty then for any index neither borrowing nor collateral is set // rule integrityOfEmpty(uint256 reserveIndex){ invariant integrityOfEmpty(uint256 reserveIndex) isEmpty() => !isBorrowingAny() && !isUsingAsCollateralOrBorrowing(reserveIndex) // invariant notEmpty(uint256 reserveIndex) // (isBorrowingAny() || isUsingAsCollateral(reserveIndex)) => !isEmpty() // if IsolationModeState is active then there must be exactly one asset register as collateral. // note that this is a necessary requirement, but it is not sufficient. rule integrityOfIsolationModeState(){ // invariant integrityOfIsolationModeState(calldataarg args) // !isUsingAsCollateralOne() => !isIsolated() rule integrityOfSiloedBorrowingState(){ bool existExactlyOneBorrow = isBorrowingOne(); bool answer; address asset; answer, asset = getSiloedBorrowingState(); assert answer => existExactlyOneBorrow; } bool existExactlyOneCollateral = isUsingAsCollateralOne(); bool answer; address asset; uint256 ceiling; answer, asset, ceiling = getIsolationModeState(); assert answer => existExactlyOneCollateral; // bool borrowingOrCollateral = isUsingAsCollateralOrBorrowing(reserveIndex); // bool anyBorrowed = isBorrowingAny(); // assert isEmpty() => !borrowingOrCollateral; // } // bool reserveBorrowing = isBorrowing(reserveIndex); // assert reserveBorrowing => isBorrowingAny(); // // assert exists uint256 index. isBorrowingAny() => isBorrowing(index); // bool reserveBorrowingOther = isBorrowing(reserveIndexOther); // assert reserveBorrowing && isBorrowingOne() => !reserveBorrowingOther || reserveIndexOther == reserveIndex; // bool reserveCollateral = isUsingAsCollateral(reserveIndex); // assert reserveCollateral => isUsingAsCollateralAny(); // // assert exists uint256 index. isUsingAsCollateralAny() => isUsingAsCollateral(index); // bool reserveCollateralOther = isUsingAsCollateral(reserveIndexOther); // assert reserveCollateral && isUsingAsCollateralOne() => !reserveCollateralOther || reserveIndexOther == reserveIndex; ","isUsingAsCollateralOrBorrowing (Lines 71-79), | isBorrowing (Lines 87-95), | isUsingAsCollateral (Lines 103-111), | isUsingAsCollateralAny (Lines 131-135), | isBorrowingAny (Lines 153-155), "," function isUsingAsCollateralOrBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 3 != 0; } } | function isBorrowing( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> (reserveIndex << 1)) & 1 != 0; } } | function isUsingAsCollateral( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.INVALID_RESERVE_INDEX); return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; } } | function isUsingAsCollateralAny( DataTypes.UserConfigurationMap memory self ) internal pure returns (bool) { return self.data & COLLATERAL_MASK != 0; } | function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { return self.data & BORROWING_MASK != 0; } ",./aave_v3/specs/UserConfiguration.spec,aave_v3,,Yes,,"Functionality: Determine user interactions with reserves by verifying if they are using assets as collateral, borrowing, or engaging in either activity for any reserve. It manipulates bits within a user configuration map to check user status per reserve, utilizing bitwise operations for efficiency."