Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GasPriceMinimum Foundry Migration #10811

Merged
merged 20 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
soloseng marked this conversation as resolved.
Show resolved Hide resolved

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
Loading