diff --git a/.gitmodules b/.gitmodules index 7eaacd704b8..b26686ad5b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/packages/protocol/foundry.toml b/packages/protocol/foundry.toml index 4ea48658d7e..6347022e32a 100644 --- a/packages/protocol/foundry.toml +++ b/packages/protocol/foundry.toml @@ -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', ] diff --git a/packages/protocol/lib/celo-foundry-8 b/packages/protocol/lib/celo-foundry-8 new file mode 160000 index 00000000000..1bef01f5658 --- /dev/null +++ b/packages/protocol/lib/celo-foundry-8 @@ -0,0 +1 @@ +Subproject commit 1bef01f56581acc0b25501bfd4f0add1b5daecd3 diff --git a/packages/protocol/lib/openzeppelin-contracts8 b/packages/protocol/lib/openzeppelin-contracts8 new file mode 160000 index 00000000000..b53c43242fc --- /dev/null +++ b/packages/protocol/lib/openzeppelin-contracts8 @@ -0,0 +1 @@ +Subproject commit b53c43242fc9c0e435b66178c3847c4a1b417cc1 diff --git a/packages/protocol/remappings.txt b/packages/protocol/remappings.txt index ade04c498d7..8722ca0c8b0 100644 --- a/packages/protocol/remappings.txt +++ b/packages/protocol/remappings.txt @@ -1,3 +1,4 @@ @celo-contracts=contracts/ +@celo-contracts-8=contracts-0.8/ @test-sol=test-sol @lib=lib \ No newline at end of file diff --git a/packages/protocol/test-sol/common/GasPriceMinimum.t.sol b/packages/protocol/test-sol/common/GasPriceMinimum.t.sol new file mode 100644 index 00000000000..ec3c67f6526 --- /dev/null +++ b/packages/protocol/test-sol/common/GasPriceMinimum.t.sol @@ -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); + } + } +} diff --git a/packages/protocol/test/common/gaspriceminimum.ts b/packages/protocol/test/common/gaspriceminimum.ts deleted file mode 100644 index 990e39607ce..00000000000 --- a/packages/protocol/test/common/gaspriceminimum.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { NULL_ADDRESS } from '@celo/base/lib/address' -import { - assertEqualBN, - assertLogMatches2, - assertTransactionRevertWithReason, -} from '@celo/protocol/lib/test-utils' -import { fromFixed, toFixed } from '@celo/utils/lib/fixidity' -import BigNumber from 'bignumber.js' -import { RegistryContract, RegistryInstance } from 'types' -import { GasPriceMinimumContract, GasPriceMinimumInstance } from 'types/08' -import { SOLIDITY_08_PACKAGE } from '../../contractPackages' -import { ArtifactsSingleton } from '../../lib/artifactsSingleton' - -const Registry: RegistryContract = artifacts.require('Registry') -const GasPriceMinimum: GasPriceMinimumContract = - ArtifactsSingleton.getInstance(SOLIDITY_08_PACKAGE).require('GasPriceMinimum') - -// @ts-ignore -// TODO(mcortesi): Use BN -GasPriceMinimum.numberFormat = 'BigNumber' - -contract('GasPriceMinimum', (accounts: string[]) => { - let gasPriceMinimum: GasPriceMinimumInstance - let registry: RegistryInstance - const nonOwner = accounts[1] - const gasPriceMinimumFloor = new BigNumber(100) - const initialGasPriceMinimum = gasPriceMinimumFloor - const targetDensity = toFixed(1 / 2) - const adjustmentSpeed = toFixed(1 / 2) - - beforeEach(async () => { - registry = await Registry.new(true) - gasPriceMinimum = await GasPriceMinimum.new(true) - - await gasPriceMinimum.initialize( - registry.address, - gasPriceMinimumFloor, - targetDensity, - adjustmentSpeed, - 0 - ) - }) - - describe('#initialize()', () => { - it('should have set the owner', async () => { - const owner: string = await gasPriceMinimum.owner() - assert.equal(owner, accounts[0]) - }) - - it('should set the gas price minimum', async () => { - const actualGasPriceMinimum = await gasPriceMinimum.getGasPriceMinimum(NULL_ADDRESS) - assertEqualBN(actualGasPriceMinimum, initialGasPriceMinimum) - }) - - it('should set the target density', async () => { - const actualTargetDensity = await gasPriceMinimum.targetDensity() - assertEqualBN(actualTargetDensity, targetDensity) - }) - - it('should set the adjustment speed', async () => { - const actualAdjustmentSpeed = await gasPriceMinimum.adjustmentSpeed() - assertEqualBN(actualAdjustmentSpeed, adjustmentSpeed) - }) - - it('should set the gas price minimum floor', async () => { - const actualGasPriceMinimumFloor = await gasPriceMinimum.gasPriceMinimumFloor() - assertEqualBN(actualGasPriceMinimumFloor, gasPriceMinimumFloor) - }) - - it('should not be callable again', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.initialize( - registry.address, - gasPriceMinimumFloor, - targetDensity, - adjustmentSpeed, - 0 - ), - 'contract already initialized' - ) - }) - }) - - describe('#setAdjustmentSpeed', () => { - const newAdjustmentSpeed = toFixed(1 / 3) - - it('should set the adjustment speed', async () => { - await gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed) - const actualAdjustmentSpeed = await gasPriceMinimum.adjustmentSpeed() - assertEqualBN(actualAdjustmentSpeed, newAdjustmentSpeed) - }) - - it('should emit the AdjustmentSpeedSet event', async () => { - const resp = await gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed) - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'AdjustmentSpeedSet', - args: { - adjustmentSpeed: newAdjustmentSpeed, - }, - }) - }) - - it('should revert when the provided fraction is greater than one', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setAdjustmentSpeed(toFixed(3 / 2)), - 'adjustment speed must be smaller than 1' - ) - }) - - it('should revert when called by anyone other than the owner', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setAdjustmentSpeed(newAdjustmentSpeed, { from: nonOwner }), - 'Ownable: caller is not the owner' - ) - }) - }) - - describe('#setTargetDensity', () => { - const newTargetDensity = toFixed(1 / 3) - - it('should set the adjustment speed', async () => { - await gasPriceMinimum.setTargetDensity(newTargetDensity) - const actualTargetDensity = await gasPriceMinimum.targetDensity() - assertEqualBN(actualTargetDensity, newTargetDensity) - }) - - it('should emit the TargetDensitySet event', async () => { - const resp = await gasPriceMinimum.setTargetDensity(newTargetDensity) - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'TargetDensitySet', - args: { - targetDensity: newTargetDensity, - }, - }) - }) - - it('should revert when the provided fraction is greater than one', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setTargetDensity(toFixed(3 / 2)), - 'target density must be smaller than 1' - ) - }) - - it('should revert when called by anyone other than the owner', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setTargetDensity(newTargetDensity, { - from: nonOwner, - }), - 'Ownable: caller is not the owner' - ) - }) - }) - - describe('#setGasPriceMinimumFloor', () => { - const newGasPriceMinFloor = new BigNumber(150) - - it('should set the minimum gas price floor', async () => { - await gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinFloor) - const actualFloor = await gasPriceMinimum.gasPriceMinimumFloor() - assertEqualBN(actualFloor, newGasPriceMinFloor) - }) - - it('should emit the MinimumGasPriceFloorSet event', async () => { - const resp = await gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinFloor) - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'GasPriceMinimumFloorSet', - args: { - gasPriceMinimumFloor: newGasPriceMinFloor, - }, - }) - }) - - it('should revert when the provided floor is zero', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setGasPriceMinimumFloor(0), - 'gas price minimum floor must be greater than zero' - ) - }) - - it('should revert when called by anyone other than the owner', async () => { - await assertTransactionRevertWithReason( - gasPriceMinimum.setGasPriceMinimumFloor(newGasPriceMinFloor, { - from: nonOwner, - }), - 'Ownable: caller is not the owner' - ) - }) - }) - - describe('#getUpdatedGasPriceMinimum', () => { - describe('when the block is full', () => { - it('should return 25% more than the initial minimum and should not be limited by the gas price minimum floor as a whole', async () => { - const currentGasPriceMinimum = await gasPriceMinimum.gasPriceMinimum() - await gasPriceMinimum.setGasPriceMinimumFloor(currentGasPriceMinimum) - const actualUpdatedGasPriceMinimum = await gasPriceMinimum.getUpdatedGasPriceMinimum(1, 1) - const expectedUpdatedGasPriceMinimum = currentGasPriceMinimum.times(5).div(4).plus(1) - assertEqualBN(actualUpdatedGasPriceMinimum, expectedUpdatedGasPriceMinimum) - }) - }) - - describe('when the block is empty', () => { - it('should return 25% less than the initial minimum, but be limited by the gas price minimum floor if new gas price lies below minimum', async () => { - const currentGasPriceMinimum = await gasPriceMinimum.gasPriceMinimum() - await gasPriceMinimum.setGasPriceMinimumFloor(currentGasPriceMinimum) - const actualUpdatedGasPriceMinimum = await gasPriceMinimum.getUpdatedGasPriceMinimum(0, 1) - const expectedCappedUpdatedGasPriceMinimum = await gasPriceMinimum.gasPriceMinimumFloor() - assertEqualBN(actualUpdatedGasPriceMinimum, expectedCappedUpdatedGasPriceMinimum) - }) - - it('should return 25% less than the initial minimum, but not be limited by the gas price minimum floor if new gas price lies above minimum', async () => { - const currentGasPriceMinimum = await gasPriceMinimum.gasPriceMinimum() - await gasPriceMinimum.setGasPriceMinimumFloor(1) - const actualUpdatedGasPriceMinimum = await gasPriceMinimum.getUpdatedGasPriceMinimum(0, 1) - const expectedUpdatedGasPriceMinimum = currentGasPriceMinimum.times(3).div(4).plus(1) - assertEqualBN(actualUpdatedGasPriceMinimum, expectedUpdatedGasPriceMinimum) - }) - }) - - describe('when the fullness of the block is random', () => { - const getUpdatedGasPriceMinimum = ( - gasPriceMinFloor, - previousGasPriceMinimum, - density, - _targetDensity, - _adjustmentSpeed - ) => { - const one = new BigNumber(1) - const newGasPriceMinimum = previousGasPriceMinimum - .times( - one.plus( - fromFixed(_adjustmentSpeed).times(fromFixed(density).minus(fromFixed(_targetDensity))) - ) - ) - .plus(one) - .integerValue(BigNumber.ROUND_DOWN) - - return newGasPriceMinimum.lt(gasPriceMinFloor) ? gasPriceMinFloor : newGasPriceMinimum - } - - it('should return an updated gas price minimum that matches a typescript implementation', async () => { - const numIterations = 100 - const currentGasPriceMinimum = await gasPriceMinimum.gasPriceMinimum() - const gasPriceMinFloor = currentGasPriceMinimum - await gasPriceMinimum.setGasPriceMinimumFloor(gasPriceMinFloor) - - for (let i = 0; i < numIterations; i++) { - const curGas = await gasPriceMinimum.gasPriceMinimum() - - const blockGasLimit = new BigNumber(web3.utils.randomHex(4)) - const gasUsed = BigNumber.random().times(blockGasLimit).integerValue() - const actualUpdatedGasPriceMinimum = await gasPriceMinimum.getUpdatedGasPriceMinimum( - gasUsed, - blockGasLimit - ) - - const expectedUpdatedGasPriceMinimum = getUpdatedGasPriceMinimum( - gasPriceMinFloor, - curGas, - toFixed(gasUsed.div(blockGasLimit)), - targetDensity, - adjustmentSpeed - ) - - assertEqualBN(actualUpdatedGasPriceMinimum, expectedUpdatedGasPriceMinimum) - } - }) - }) - }) -})