diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a02e71fa..f247a9d3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,6 +17,16 @@ jobs: with: submodules: recursive + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install eth-abi + run: | + python3 -m pip install --upgrade pip + python3 -m pip install eth-abi + - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -30,7 +40,7 @@ jobs: - name: Run Forge tests run: | - forge test -vvv + forge test -vvv --ffi id: test - name: Check formatting diff --git a/py/compute_premium.py b/py/compute_premium.py new file mode 100644 index 00000000..dab29915 --- /dev/null +++ b/py/compute_premium.py @@ -0,0 +1,32 @@ +import sys +from eth_abi import encode + +PRECISION = 10 ** 18 +SECONDS_PER_DAY = 86400 +PER_PERIOD_DECAY_PERCENT = 50 +PER_PERIOD_DECAY_PERCENT_WAD = int(PER_PERIOD_DECAY_PERCENT * PRECISION / 100) + +class DecayedPriceCalculator: + @staticmethod + def decayed_premium(start_premium, elapsed_seconds): + ratio = elapsed_seconds / SECONDS_PER_DAY + percent_wad_remaining_per_period = (PRECISION - PER_PERIOD_DECAY_PERCENT_WAD) / PRECISION + multiplier = (percent_wad_remaining_per_period ** ratio) + price = (start_premium * multiplier) + return int(price) + + @classmethod + def calculate_from_cli(cls): + if len(sys.argv) != 3: + print("Usage: python3 price.py ") + sys.exit(1) + + start_premium = int(sys.argv[1]) + elapsed_seconds = int(sys.argv[2]) + + result = cls.decayed_premium(start_premium, elapsed_seconds) + enc = encode(['uint256'], [result]) + print("0x" + enc.hex()) + +if __name__ == "__main__": + DecayedPriceCalculator.calculate_from_cli() \ No newline at end of file diff --git a/test/ExponentialPremiumPriceOracle/ExponentialPremiumFuzzTest.t.sol b/test/ExponentialPremiumPriceOracle/ExponentialPremiumFuzzTest.t.sol index 4b099d71..3e0dcd12 100644 --- a/test/ExponentialPremiumPriceOracle/ExponentialPremiumFuzzTest.t.sol +++ b/test/ExponentialPremiumPriceOracle/ExponentialPremiumFuzzTest.t.sol @@ -34,4 +34,21 @@ contract ExponentialPremiumFuzzTest is ExponentialPremiumOracleBase { assert(premium1 >= premium2); } + + function test_decayedPremium_accuracy(uint256 elapsed) public { + uint256 bound = 400 * 1 days; + vm.assume(elapsed <= bound); + string[] memory input = new string[](4); + input[0] = "python3"; + input[1] = "py/compute_premium.py"; + input[2] = vm.toString(startPremium); + input[3] = vm.toString(elapsed); + uint256 result = oracle.decayedPremium(elapsed); + bytes memory res = vm.ffi(input); + uint256 expected = abi.decode(res, (uint256)); + uint256 leftBound = (expected * (999)) / 1000; + uint256 rightBound = (expected * (1001)) / 1000; + bool withinBounds = (leftBound <= result && result <= rightBound); // Checking accuracy within 0.1 percent of the expected result + assertTrue(withinBounds); + } }