Skip to content

Commit

Permalink
GasPriceMinimum Foundry Migration (#10811)
Browse files Browse the repository at this point in the history
* created test contract

* add openzeppelin-contracts < 0.8.20

* added latest forge test submodule

* pragma 0.8 support celo in foundry test

* first passing test

* fully migrated GasPriceMinimumTest

* added Registry8 test

* Using Registry interface instead.

Avoids upgrading the registry contract to pragma ^0.8.0 in the PR.

* removed extra setUp

* Pinned submodule to specific tag/commit

* ++ celo-foundry with pragma 0.8

* fixed import mappings

* removed unused submodule

* added celo foundry contracts v8

* PR feedback

---------

Co-authored-by: Martín Volpe <[email protected]>
  • Loading branch information
soloseng and martinvol authored Dec 18, 2023
1 parent 16c6263 commit 6217f99
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 278 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@
[submodule "packages/protocol/lib/memview.sol"]
path = packages/protocol/lib/memview.sol
url = https://github.com/summa-tx/memview.sol
[submodule "packages/protocol/lib/openzeppelin-contracts8"]
path = packages/protocol/lib/openzeppelin-contracts8
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "packages/protocol/lib/celo-foundry-8"]
path = packages/protocol/lib/celo-foundry-8
url = https://github.com/celo-org/celo-foundry
9 changes: 6 additions & 3 deletions packages/protocol/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ out = 'out'
test = 'test-sol'
libs = ['lib', 'node_modules']
remappings = [
'openzeppelin-solidity/=lib/openzeppelin-contracts/',
'solidity-bytes-utils/=lib/solidity-bytes-utils/',
'forge-std/=lib/celo-foundry/lib/forge-std/src/',
'ds-test/=lib/celo-foundry/lib/forge-std/lib/ds-test/src/',
'forge-std/=lib/celo-foundry/lib/forge-std/src/',
'forge-std-8/=lib/celo-foundry-8/lib/forge-std/src/',
'openzeppelin-solidity/=lib/openzeppelin-contracts/',
'@openzeppelin/contracts8/=lib/openzeppelin-contracts8/contracts/',
'celo-foundry/=lib/celo-foundry/src/',
'celo-foundry-8/=lib/celo-foundry-8/src/',
'solidity-bytes-utils/=lib/solidity-bytes-utils/',
'@summa-tx/memview.sol/=lib/memview.sol',
]

Expand Down
1 change: 1 addition & 0 deletions packages/protocol/lib/celo-foundry-8
Submodule celo-foundry-8 added at 1bef01
1 change: 1 addition & 0 deletions packages/protocol/lib/openzeppelin-contracts8
1 change: 1 addition & 0 deletions packages/protocol/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@celo-contracts=contracts/
@celo-contracts-8=contracts-0.8/
@test-sol=test-sol
@lib=lib
272 changes: 272 additions & 0 deletions packages/protocol/test-sol/common/GasPriceMinimum.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.7 <0.8.20;

import "celo-foundry-8/Test.sol";

import "@celo-contracts/common/FixidityLib.sol";

import "@celo-contracts/common/interfaces/IRegistry.sol";

// Contract to test
import "@celo-contracts-8/common/GasPriceMinimum.sol";

contract GasPriceMinimumTest is Test {
using FixidityLib for FixidityLib.Fraction;

IRegistry registry;
GasPriceMinimum public gasPriceMinimum;
address owner;
address nonOwner;

uint256 gasPriceMinimumFloor = 100;
uint256 initialGasPriceMinimum = gasPriceMinimumFloor;
uint256 targetDensity = FixidityLib.newFixedFraction(5, 10).unwrap();
FixidityLib.Fraction targetDensityFraction = FixidityLib.newFixedFraction(5, 10);
uint256 adjustmentSpeed = FixidityLib.newFixedFraction(5, 10).unwrap();
FixidityLib.Fraction adjustmentSpeedFraction = FixidityLib.newFixedFraction(5, 10);
address registryAddress = 0x000000000000000000000000000000000000ce10;

event TargetDensitySet(uint256 targetDensity);
event GasPriceMinimumFloorSet(uint256 gasPriceMinimumFloor);
event AdjustmentSpeedSet(uint256 adjustmentSpeed);
event GasPriceMinimumUpdated(uint256 gasPriceMinimum);
event BaseFeeOpCodeActivationBlockSet(uint256 baseFeeOpCodeActivationBlock);

function setUp() public virtual {
owner = address(this);
nonOwner = actor("nonOwner");

deployCodeTo("Registry.sol", abi.encode(false), registryAddress);
gasPriceMinimum = new GasPriceMinimum(true);

registry = IRegistry(registryAddress);

registry.setAddressFor("GasPriceMinimum", address(gasPriceMinimum));

gasPriceMinimum.initialize(
registryAddress,
gasPriceMinimumFloor,
targetDensity,
adjustmentSpeed,
0
);
}
}

contract GasPriceMinimumInitialize is GasPriceMinimumTest {
function test_shouldHaveSetOwner() public {
assertEq(gasPriceMinimum.owner(), owner);
}

function test_shouldSetTheGasPriceMinimum() public {
assertEq(gasPriceMinimum.getGasPriceMinimum(address(0)), initialGasPriceMinimum);
}

function test_shouldHaveTargetDensity() public {
assertEq(gasPriceMinimum.targetDensity(), targetDensity);
}

function test_shouldHaveAdjustmentSpeed() public {
assertEq(gasPriceMinimum.adjustmentSpeed(), adjustmentSpeed);
}

function test_shouldHaveGasPriceMinimumFloor() public {
assertEq(gasPriceMinimum.gasPriceMinimumFloor(), gasPriceMinimumFloor);
}

function test_shouldRevertWhenCalledAgain() public {
vm.expectRevert("contract already initialized");
gasPriceMinimum.initialize(
registryAddress,
gasPriceMinimumFloor,
targetDensity,
adjustmentSpeed,
0
);
}
}

contract GasPriceMinimumSetAdjustmentSpeed is GasPriceMinimumTest {
using FixidityLib for FixidityLib.Fraction;

uint256 newAdjustmentSpeed = FixidityLib.newFixedFraction(1, 3).unwrap();

function test_shouldSetTheAdjustmentSpeed() public {
gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed);

assertEq(gasPriceMinimum.adjustmentSpeed(), newAdjustmentSpeed);
}

function test_shouldEmitAdjustmentSpeedSetEvent() public {
vm.expectEmit(true, false, false, false);
emit AdjustmentSpeedSet(newAdjustmentSpeed);
gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed);
}

function test_shouldRevertWhenTheProvidedFractionIsGreaterThanOne() public {
vm.expectRevert("adjustment speed must be smaller than 1");
gasPriceMinimum.setAdjustmentSpeed(FixidityLib.newFixedFraction(3, 2).unwrap());
}

function test_shouldRevertWhenCalledByNonOwner() public {
vm.prank(nonOwner);
vm.expectRevert("Ownable: caller is not the owner");
gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed);
}
}

contract GasPriceMinimumSetTargetDensity is GasPriceMinimumTest {
using FixidityLib for FixidityLib.Fraction;

uint256 newTargetDensity = FixidityLib.newFixedFraction(1, 3).unwrap();

function test_shouldSetTargetDensity() public {
gasPriceMinimum.setTargetDensity(newTargetDensity);
assertEq(gasPriceMinimum.targetDensity(), newTargetDensity);
}

function test_ShouldEmitTargetDensitySetEvent() public {
vm.expectEmit(true, true, true, true);
emit TargetDensitySet(newTargetDensity);
gasPriceMinimum.setTargetDensity(newTargetDensity);
}

function test_ShouldRevertWhenProvidedFractionIsGreaterThanOne() public {
vm.expectRevert("target density must be smaller than 1");
gasPriceMinimum.setTargetDensity(FixidityLib.newFixedFraction(3, 2).unwrap());
}

function test_ShouldRevertWhenCalledByNonOwner() public {
vm.prank(nonOwner);
vm.expectRevert("Ownable: caller is not the owner");
gasPriceMinimum.setTargetDensity(newTargetDensity);
}
}

contract GasPriceMinimumSetGasPriceMinimumFloor is GasPriceMinimumTest {
uint256 newGasPriceMinimumFloor = 150;

function test_ShouldSetGasPriceMinimumFloor() public {
gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinimumFloor);

assertEq(gasPriceMinimum.gasPriceMinimumFloor(), newGasPriceMinimumFloor);
}

function test_emitsGasPriceMinimumFloorSet() public {
vm.expectEmit(true, true, true, true);
emit GasPriceMinimumFloorSet(newGasPriceMinimumFloor);
gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinimumFloor);
}

function test_shouldRevertWhenProvidedFloorIsZero() public {
vm.expectRevert("gas price minimum floor must be greater than zero");
gasPriceMinimum.setGasPriceMinimumFloor(0);
}

function test_shouldRevertWhenCalledByNonOwner() public {
vm.prank(nonOwner);
vm.expectRevert("Ownable: caller is not the owner");
gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinimumFloor);
}
}

contract GasPriceMinimumGetUpdatedGasPriceMinimum is GasPriceMinimumTest {
using FixidityLib for FixidityLib.Fraction;
uint256 nonce = 0;

function getExpectedUpdatedGasPriceMinimum(
uint256 gasPriceMinFloor,
uint256 previousGasPriceMinimum,
FixidityLib.Fraction memory density,
FixidityLib.Fraction memory _targetDensity,
FixidityLib.Fraction memory _adjustmentSpeed
) public pure returns (uint256) {
uint256 one = 1;
uint256 newGasPriceMin = previousGasPriceMinimum *
one +
FixidityLib.fromFixed(_adjustmentSpeed) *
FixidityLib.fromFixed(density) -
FixidityLib.fromFixed(_targetDensity);

return newGasPriceMin < gasPriceMinFloor ? gasPriceMinFloor : newGasPriceMin;
}

function random(uint256 minNumber, uint256 maxNumber) public returns (uint256) {
nonce += 1;
if (minNumber > 0) {
return
(uint256(keccak256(abi.encodePacked(nonce, msg.sender, blockhash(block.number - 1)))) %
(maxNumber - 1)) +
1;
}
return (uint256(keccak256(abi.encodePacked(nonce, msg.sender, blockhash(block.number - 1)))) %
maxNumber);
}

function test_shouldReturn25PercentMoreThanInitialMinimumAndShouldNotBeLimitedByGasPriceMinimumFloorAsAWhole_WhenTheBlockIsFull()
public
{
uint256 currentGasPriceMinimum = gasPriceMinimum.gasPriceMinimum();

gasPriceMinimum.setGasPriceMinimumFloor(currentGasPriceMinimum);

uint256 expectedUpdatedGasPriceMinimum = (currentGasPriceMinimum * 5) / 4 + 1;

assertEq(gasPriceMinimum.getUpdatedGasPriceMinimum(1, 1), expectedUpdatedGasPriceMinimum);
}

function test_shouldReturn25PercentLessThanInitialMinimumButShouldBeLimitedByGasPriceMinimumFloorIfNewGasLiesBelowMinimum_WhenTheBlockIsEmtpy()
public
{
uint256 currentGasPriceMinimum = gasPriceMinimum.gasPriceMinimum();

gasPriceMinimum.setGasPriceMinimumFloor(currentGasPriceMinimum);

uint256 expectedCappedUpdatedGasPriceMinimum = gasPriceMinimum.gasPriceMinimumFloor();

assertEq(gasPriceMinimum.getUpdatedGasPriceMinimum(0, 1), expectedCappedUpdatedGasPriceMinimum);
}

function test_shouldReturn25PercentLessThanInitialMinimumAndShouldNotBeLimitedByGasPriceMinimumFloorIfNewGasPriceLiesAboveMinimum_WhenTheBlockIsEmtpy()
public
{
uint256 currentGasPriceMinimum = gasPriceMinimum.gasPriceMinimum();

gasPriceMinimum.setGasPriceMinimumFloor(1);

uint256 expectedUpdatedGasPriceMinimum = (currentGasPriceMinimum * 3) / 4 + 1;

assertEq(gasPriceMinimum.getUpdatedGasPriceMinimum(0, 1), expectedUpdatedGasPriceMinimum);
}

function test_shouldReturnAnUpdatedGasPriceMinimumThatMatchesARandomNumber_WhenTheFullnessOfTheBlockIsRandom()
public
{
uint256 numIterations = 100;
uint256 currentGasPriceMinimum = gasPriceMinimum.gasPriceMinimum();
uint256 gasPriceMinFloor = currentGasPriceMinimum;
gasPriceMinimum.setGasPriceMinimumFloor(gasPriceMinFloor);

for (uint256 i = 0; i < numIterations; i++) {
uint256 currGas = gasPriceMinimum.gasPriceMinimum();

uint256 blockGasLimit = random(1, 105);
uint256 gasUsed = random(0, 1) * blockGasLimit;

uint256 actualUpdatedGasPriceMinimum = gasPriceMinimum.getUpdatedGasPriceMinimum(
gasUsed,
blockGasLimit
);

uint256 expectedUpdatedGasPriceMinimum = getExpectedUpdatedGasPriceMinimum(
gasPriceMinFloor,
currGas,
FixidityLib.newFixedFraction(gasUsed, blockGasLimit),
targetDensityFraction,
adjustmentSpeedFraction
);

assertEq(actualUpdatedGasPriceMinimum, expectedUpdatedGasPriceMinimum);
}
}
}
Loading

0 comments on commit 6217f99

Please sign in to comment.