feat: Push3 → Solidity transpiler + OptimizerV3 port
This commit is contained in:
parent
9809e5e640
commit
5e8a94b7a9
9 changed files with 1364 additions and 0 deletions
221
onchain/src/OptimizerV3Push3.sol
Normal file
221
onchain/src/OptimizerV3Push3.sol
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
/**
|
||||
* @title OptimizerV3Push3
|
||||
* @notice Auto-generated from optimizer_v3.push3 via Push3→Solidity transpiler.
|
||||
* Implements the same isBullMarket logic as OptimizerV3.
|
||||
*/
|
||||
contract OptimizerV3Push3 {
|
||||
/**
|
||||
* @notice Determines if the market is in bull configuration.
|
||||
* @param percentageStaked Percentage of authorized stake in use (0 to 1e18).
|
||||
* @param averageTaxRate Normalized average tax rate from Stake contract (0 to 1e18).
|
||||
* @return bull True if bull config, false if bear.
|
||||
*/
|
||||
function isBullMarket(
|
||||
uint256 percentageStaked,
|
||||
uint256 averageTaxRate
|
||||
) public pure returns (bool bull) {
|
||||
require(percentageStaked <= 1e18, "Invalid percentage staked");
|
||||
uint256 taxrate = uint256(averageTaxRate);
|
||||
uint256 staked = uint256(((percentageStaked * 100) / 1000000000000000000));
|
||||
bool b33;
|
||||
if ((staked > 91)) {
|
||||
uint256 deltas = uint256((100 - staked));
|
||||
uint256 r28;
|
||||
if ((taxrate <= 206185567010309)) {
|
||||
r28 = uint256(0);
|
||||
} else {
|
||||
uint256 r27;
|
||||
if ((taxrate <= 412371134020618)) {
|
||||
r27 = uint256(1);
|
||||
} else {
|
||||
uint256 r26;
|
||||
if ((taxrate <= 618556701030927)) {
|
||||
r26 = uint256(2);
|
||||
} else {
|
||||
uint256 r25;
|
||||
if ((taxrate <= 1030927835051546)) {
|
||||
r25 = uint256(3);
|
||||
} else {
|
||||
uint256 r24;
|
||||
if ((taxrate <= 1546391752577319)) {
|
||||
r24 = uint256(4);
|
||||
} else {
|
||||
uint256 r23;
|
||||
if ((taxrate <= 2164948453608247)) {
|
||||
r23 = uint256(5);
|
||||
} else {
|
||||
uint256 r22;
|
||||
if ((taxrate <= 2783505154639175)) {
|
||||
r22 = uint256(6);
|
||||
} else {
|
||||
uint256 r21;
|
||||
if ((taxrate <= 3608247422680412)) {
|
||||
r21 = uint256(7);
|
||||
} else {
|
||||
uint256 r20;
|
||||
if ((taxrate <= 4639175257731958)) {
|
||||
r20 = uint256(8);
|
||||
} else {
|
||||
uint256 r19;
|
||||
if ((taxrate <= 5670103092783505)) {
|
||||
r19 = uint256(9);
|
||||
} else {
|
||||
uint256 r18;
|
||||
if ((taxrate <= 7216494845360824)) {
|
||||
r18 = uint256(10);
|
||||
} else {
|
||||
uint256 r17;
|
||||
if ((taxrate <= 9278350515463917)) {
|
||||
r17 = uint256(11);
|
||||
} else {
|
||||
uint256 r16;
|
||||
if ((taxrate <= 11855670103092783)) {
|
||||
r16 = uint256(12);
|
||||
} else {
|
||||
uint256 r15;
|
||||
if ((taxrate <= 15979381443298969)) {
|
||||
r15 = uint256(13);
|
||||
} else {
|
||||
uint256 r14;
|
||||
if ((taxrate <= 22164948453608247)) {
|
||||
r14 = uint256(14);
|
||||
} else {
|
||||
uint256 r13;
|
||||
if ((taxrate <= 29381443298969072)) {
|
||||
r13 = uint256(15);
|
||||
} else {
|
||||
uint256 r12;
|
||||
if ((taxrate <= 38144329896907216)) {
|
||||
r12 = uint256(16);
|
||||
} else {
|
||||
uint256 r11;
|
||||
if ((taxrate <= 49484536082474226)) {
|
||||
r11 = uint256(17);
|
||||
} else {
|
||||
uint256 r10;
|
||||
if ((taxrate <= 63917525773195876)) {
|
||||
r10 = uint256(18);
|
||||
} else {
|
||||
uint256 r9;
|
||||
if ((taxrate <= 83505154639175257)) {
|
||||
r9 = uint256(19);
|
||||
} else {
|
||||
uint256 r8;
|
||||
if ((taxrate <= 109278350515463917)) {
|
||||
r8 = uint256(20);
|
||||
} else {
|
||||
uint256 r7;
|
||||
if ((taxrate <= 144329896907216494)) {
|
||||
r7 = uint256(21);
|
||||
} else {
|
||||
uint256 r6;
|
||||
if ((taxrate <= 185567010309278350)) {
|
||||
r6 = uint256(22);
|
||||
} else {
|
||||
uint256 r5;
|
||||
if ((taxrate <= 237113402061855670)) {
|
||||
r5 = uint256(23);
|
||||
} else {
|
||||
uint256 r4;
|
||||
if ((taxrate <= 309278350515463917)) {
|
||||
r4 = uint256(24);
|
||||
} else {
|
||||
uint256 r3;
|
||||
if ((taxrate <= 402061855670103092)) {
|
||||
r3 = uint256(25);
|
||||
} else {
|
||||
uint256 r2;
|
||||
if ((taxrate <= 520618556701030927)) {
|
||||
r2 = uint256(26);
|
||||
} else {
|
||||
uint256 r1;
|
||||
if ((taxrate <= 680412371134020618)) {
|
||||
r1 = uint256(27);
|
||||
} else {
|
||||
uint256 r0;
|
||||
if ((taxrate <= 886597938144329896)) {
|
||||
r0 = uint256(28);
|
||||
} else {
|
||||
r0 = uint256(29);
|
||||
}
|
||||
r1 = uint256(r0);
|
||||
}
|
||||
r2 = uint256(r1);
|
||||
}
|
||||
r3 = uint256(r2);
|
||||
}
|
||||
r4 = uint256(r3);
|
||||
}
|
||||
r5 = uint256(r4);
|
||||
}
|
||||
r6 = uint256(r5);
|
||||
}
|
||||
r7 = uint256(r6);
|
||||
}
|
||||
r8 = uint256(r7);
|
||||
}
|
||||
r9 = uint256(r8);
|
||||
}
|
||||
r10 = uint256(r9);
|
||||
}
|
||||
r11 = uint256(r10);
|
||||
}
|
||||
r12 = uint256(r11);
|
||||
}
|
||||
r13 = uint256(r12);
|
||||
}
|
||||
r14 = uint256(r13);
|
||||
}
|
||||
r15 = uint256(r14);
|
||||
}
|
||||
r16 = uint256(r15);
|
||||
}
|
||||
r17 = uint256(r16);
|
||||
}
|
||||
r18 = uint256(r17);
|
||||
}
|
||||
r19 = uint256(r18);
|
||||
}
|
||||
r20 = uint256(r19);
|
||||
}
|
||||
r21 = uint256(r20);
|
||||
}
|
||||
r22 = uint256(r21);
|
||||
}
|
||||
r23 = uint256(r22);
|
||||
}
|
||||
r24 = uint256(r23);
|
||||
}
|
||||
r25 = uint256(r24);
|
||||
}
|
||||
r26 = uint256(r25);
|
||||
}
|
||||
r27 = uint256(r26);
|
||||
}
|
||||
r28 = uint256(r27);
|
||||
}
|
||||
uint256 dup29 = uint256(r28);
|
||||
uint256 r32;
|
||||
if ((dup29 >= 14)) {
|
||||
uint256 dup30 = uint256((dup29 + 1));
|
||||
uint256 r31;
|
||||
if ((dup30 > 29)) {
|
||||
r31 = uint256(29);
|
||||
} else {
|
||||
r31 = uint256(dup30);
|
||||
}
|
||||
r32 = uint256(r31);
|
||||
} else {
|
||||
r32 = uint256(dup29);
|
||||
}
|
||||
uint256 effidx = uint256(r32);
|
||||
b33 = (((((deltas * deltas) * deltas) * effidx) / 20) < 50);
|
||||
} else {
|
||||
b33 = false;
|
||||
}
|
||||
bull = b33;
|
||||
}
|
||||
}
|
||||
144
onchain/test/OptimizerV3Push3.t.sol
Normal file
144
onchain/test/OptimizerV3Push3.t.sol
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import { OptimizerV3 } from "../src/OptimizerV3.sol";
|
||||
import { OptimizerV3Push3 } from "../src/OptimizerV3Push3.sol";
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
/**
|
||||
* @title OptimizerV3Push3Test
|
||||
* @notice Verifies that the Push3-transpiled OptimizerV3Push3 produces
|
||||
* identical results to the hand-written OptimizerV3 for all test cases.
|
||||
*
|
||||
* Tests mirror OptimizerV3.t.sol to ensure equivalence.
|
||||
*/
|
||||
contract OptimizerV3Push3Test is Test {
|
||||
OptimizerV3 ref; // reference (hand-written)
|
||||
OptimizerV3Push3 push3; // transpiled from Push3
|
||||
|
||||
uint256[30] TAX_RATES =
|
||||
[uint256(1), 3, 5, 8, 12, 18, 24, 30, 40, 50, 60, 80, 100, 130, 180, 250, 320, 420, 540, 700, 920, 1200, 1600, 2000, 2600, 3400, 4400, 5700, 7500, 9700];
|
||||
uint256 constant MAX_TAX = 9700;
|
||||
|
||||
function setUp() public {
|
||||
ref = new OptimizerV3();
|
||||
push3 = new OptimizerV3Push3();
|
||||
}
|
||||
|
||||
function _norm(uint256 taxIdx) internal view returns (uint256) {
|
||||
return TAX_RATES[taxIdx] * 1e18 / MAX_TAX;
|
||||
}
|
||||
|
||||
function _pct(uint256 pct) internal pure returns (uint256) {
|
||||
return pct * 1e18 / 100;
|
||||
}
|
||||
|
||||
// ---- Equivalence against reference ----
|
||||
|
||||
function testEquivalenceAllTaxRates() public view {
|
||||
uint256[7] memory staked = [uint256(0), 50, 91, 92, 95, 96, 100];
|
||||
for (uint256 s = 0; s < staked.length; s++) {
|
||||
for (uint256 t = 0; t < 30; t++) {
|
||||
bool r = ref.isBullMarket(_pct(staked[s]), _norm(t));
|
||||
bool p = push3.isBullMarket(_pct(staked[s]), _norm(t));
|
||||
assertEq(p, r, string.concat(
|
||||
"Mismatch at staked=", vm.toString(staked[s]),
|
||||
"% taxIdx=", vm.toString(t)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function testEquivalence93to99Percent() public view {
|
||||
for (uint256 s = 93; s <= 99; s++) {
|
||||
for (uint256 t = 0; t < 30; t++) {
|
||||
bool r = ref.isBullMarket(_pct(s), _norm(t));
|
||||
bool p = push3.isBullMarket(_pct(s), _norm(t));
|
||||
assertEq(p, r, string.concat(
|
||||
"Mismatch at staked=", vm.toString(s),
|
||||
"% taxIdx=", vm.toString(t)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Direct correctness tests (mirror OptimizerV3.t.sol) ----
|
||||
|
||||
function testAlwaysBearAt0Percent() public view {
|
||||
for (uint256 t = 0; t < 30; t++) {
|
||||
assertFalse(push3.isBullMarket(0, _norm(t)));
|
||||
}
|
||||
}
|
||||
|
||||
function testAlwaysBearAt91Percent() public view {
|
||||
for (uint256 t = 0; t < 30; t++) {
|
||||
assertFalse(push3.isBullMarket(_pct(91), _norm(t)));
|
||||
}
|
||||
}
|
||||
|
||||
function testBoundary92PercentLowestTax() public view {
|
||||
// deltaS=8, effIdx=0 → penalty=0 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(92), _norm(0)));
|
||||
}
|
||||
|
||||
function testBoundary92PercentTaxIdx1() public view {
|
||||
// deltaS=8, effIdx=1 → penalty=512*1/20=25 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(92), _norm(1)));
|
||||
}
|
||||
|
||||
function testBoundary92PercentTaxIdx2() public view {
|
||||
// deltaS=8, effIdx=2 → penalty=512*2/20=51 >= 50 → BEAR
|
||||
assertFalse(push3.isBullMarket(_pct(92), _norm(2)));
|
||||
}
|
||||
|
||||
function testAt95PercentTaxIdx7() public view {
|
||||
// deltaS=5, effIdx=7 → penalty=125*7/20=43 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(95), _norm(7)));
|
||||
}
|
||||
|
||||
function testAt95PercentTaxIdx8() public view {
|
||||
// deltaS=5, effIdx=8 → penalty=125*8/20=50 NOT < 50 → BEAR
|
||||
assertFalse(push3.isBullMarket(_pct(95), _norm(8)));
|
||||
}
|
||||
|
||||
function testAt97PercentHighTax() public view {
|
||||
// deltaS=3, effIdx=29 → penalty=27*29/20=39 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(97), _norm(29)));
|
||||
}
|
||||
|
||||
function testAt100PercentAlwaysBull() public view {
|
||||
for (uint256 t = 0; t < 30; t++) {
|
||||
assertTrue(push3.isBullMarket(1e18, _norm(t)));
|
||||
}
|
||||
}
|
||||
|
||||
function testEffIdxShiftAtBoundary() public view {
|
||||
// taxIdx=13: effIdx=13, penalty=64*13/20=41 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(96), _norm(13)));
|
||||
// taxIdx=14: effIdx=15 (shift!), penalty=64*15/20=48 < 50 → BULL
|
||||
assertTrue(push3.isBullMarket(_pct(96), _norm(14)));
|
||||
// taxIdx=15: effIdx=16, penalty=64*16/20=51 >= 50 → BEAR
|
||||
assertFalse(push3.isBullMarket(_pct(96), _norm(15)));
|
||||
}
|
||||
|
||||
function testRevertsAbove100Percent() public {
|
||||
vm.expectRevert("Invalid percentage staked");
|
||||
push3.isBullMarket(1e18 + 1, 0);
|
||||
}
|
||||
|
||||
// ---- Fuzz ----
|
||||
|
||||
function testFuzzEquivalence(uint256 percentageStaked, uint256 averageTaxRate) public view {
|
||||
percentageStaked = bound(percentageStaked, 0, 1e18);
|
||||
averageTaxRate = bound(averageTaxRate, 0, 1e18);
|
||||
bool r = ref.isBullMarket(percentageStaked, averageTaxRate);
|
||||
bool p = push3.isBullMarket(percentageStaked, averageTaxRate);
|
||||
assertEq(p, r, "Push3 result must match reference for all inputs");
|
||||
}
|
||||
|
||||
function testFuzzNeverReverts(uint256 percentageStaked, uint256 averageTaxRate) public view {
|
||||
percentageStaked = bound(percentageStaked, 0, 1e18);
|
||||
averageTaxRate = bound(averageTaxRate, 0, 1e18);
|
||||
push3.isBullMarket(percentageStaked, averageTaxRate);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue