fix: Shift field silently ignored — dyadic rational inputs effectively unsupported (#606)
Add require(shift == 0) guards to Optimizer.calculateParams and OptimizerV3.calculateParams so non-zero shifts revert instead of being silently discarded. OptimizerV3Push3 already had this guard. Update IOptimizer.sol NatSpec to document that shift is reserved for future use and must be 0 in all current implementations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
26957dae88
commit
42b4bf4149
5 changed files with 35 additions and 2 deletions
|
|
@ -3,7 +3,10 @@ pragma solidity ^0.8.19;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Dyadic rational input: mantissa × 2^(-shift).
|
* @notice Dyadic rational input: mantissa × 2^(-shift).
|
||||||
* For shift == 0 (current usage via _toDyadic), value == mantissa.
|
* shift is reserved for future use and MUST be 0. All current
|
||||||
|
* Optimizer implementations (Optimizer, OptimizerV3, OptimizerV3Push3)
|
||||||
|
* require shift == 0 and revert otherwise. When shift == 0 (as
|
||||||
|
* produced by _toDyadic), value == mantissa.
|
||||||
*/
|
*/
|
||||||
struct OptimizerInput {
|
struct OptimizerInput {
|
||||||
int256 mantissa;
|
int256 mantissa;
|
||||||
|
|
|
||||||
|
|
@ -356,8 +356,10 @@ contract Optimizer is Initializable, UUPSUpgradeable, IOptimizer {
|
||||||
virtual
|
virtual
|
||||||
returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth)
|
returns (uint256 capitalInefficiency, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth)
|
||||||
{
|
{
|
||||||
// Guard against negative mantissa — uint256() cast silently wraps negatives.
|
// Guard against non-zero shift and negative mantissa.
|
||||||
|
// shift is reserved for future use; uint256() cast silently wraps negatives.
|
||||||
for (uint256 k; k < 8; k++) {
|
for (uint256 k; k < 8; k++) {
|
||||||
|
require(inputs[k].shift == 0, "shift not yet supported");
|
||||||
require(inputs[k].mantissa >= 0, "negative mantissa");
|
require(inputs[k].mantissa >= 0, "negative mantissa");
|
||||||
}
|
}
|
||||||
// Extract slots 0 and 1 (shift=0 assumed — mantissa IS the value)
|
// Extract slots 0 and 1 (shift=0 assumed — mantissa IS the value)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,11 @@ contract OptimizerV3 is Optimizer {
|
||||||
override
|
override
|
||||||
returns (uint256 ci, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth)
|
returns (uint256 ci, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth)
|
||||||
{
|
{
|
||||||
|
// Guard against non-zero shift (reserved for future use).
|
||||||
|
for (uint256 k; k < 8; k++) {
|
||||||
|
require(inputs[k].shift == 0, "shift not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
// ── BEGIN TRANSPILER OUTPUT (optimizer_v3.push3) ──
|
// ── BEGIN TRANSPILER OUTPUT (optimizer_v3.push3) ──
|
||||||
// Do NOT edit by hand — regenerate via: npx tsx tools/push3-transpiler/transpile-cli.ts
|
// Do NOT edit by hand — regenerate via: npx tsx tools/push3-transpiler/transpile-cli.ts
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -359,6 +359,18 @@ contract OptimizerTest is Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice calculateParams reverts when any slot has shift != 0
|
||||||
|
*/
|
||||||
|
function testCalculateParamsRevertsOnNonZeroShift() public {
|
||||||
|
for (uint256 k = 0; k < 8; k++) {
|
||||||
|
OptimizerInput[8] memory inputs;
|
||||||
|
inputs[k] = OptimizerInput({ mantissa: 0, shift: 1 });
|
||||||
|
vm.expectRevert("shift not yet supported");
|
||||||
|
optimizer.calculateParams(inputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Non-admin calling upgradeTo should revert with UnauthorizedAccount
|
* @notice Non-admin calling upgradeTo should revert with UnauthorizedAccount
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,17 @@ contract OptimizerV3Push3Test is Test {
|
||||||
push3.calculateParams(inp);
|
push3.calculateParams(inp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Shift guard ----
|
||||||
|
|
||||||
|
function testNonZeroShiftReverts() public {
|
||||||
|
for (uint256 k = 0; k < 8; k++) {
|
||||||
|
OptimizerInput[8] memory inp;
|
||||||
|
inp[k] = OptimizerInput({mantissa: 0, shift: 1});
|
||||||
|
vm.expectRevert("shift not yet supported");
|
||||||
|
push3.calculateParams(inp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Fuzz ----
|
// ---- Fuzz ----
|
||||||
|
|
||||||
function testFuzzNeverReverts(uint256 percentageStaked, uint256 averageTaxRate) public view {
|
function testFuzzNeverReverts(uint256 percentageStaked, uint256 averageTaxRate) public view {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue