Commit a0ff1cc9 authored by Andreas Bigger's avatar Andreas Bigger

Port all remaining test files to triple slash natspec styling.

parent 3c0e8a98
...@@ -5,9 +5,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -5,9 +5,7 @@ import { Test } from "forge-std/Test.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
contract AddressAliasHelper_applyAndUndo_Test is Test { contract AddressAliasHelper_applyAndUndo_Test is Test {
/** /// @notice Tests that applying and then undoing an alias results in the original address.
* @notice Tests that applying and then undoing an alias results in the original address.
*/
function testFuzz_applyAndUndo_succeeds(address _address) external { function testFuzz_applyAndUndo_succeeds(address _address) external {
address aliased = AddressAliasHelper.applyL1ToL2Alias(_address); address aliased = AddressAliasHelper.applyL1ToL2Alias(_address);
address unaliased = AddressAliasHelper.undoL1ToL2Alias(aliased); address unaliased = AddressAliasHelper.undoL1ToL2Alias(aliased);
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -6,41 +6,32 @@ import { AdminFaucetAuthModule } from "../periphery/faucet/authmodules/AdminFauc ...@@ -6,41 +6,32 @@ import { AdminFaucetAuthModule } from "../periphery/faucet/authmodules/AdminFauc
import { Faucet } from "../periphery/faucet/Faucet.sol"; import { Faucet } from "../periphery/faucet/Faucet.sol";
import { FaucetHelper } from "./Helpers.sol"; import { FaucetHelper } from "./Helpers.sol";
/** /// @title AdminFaucetAuthModuleTest
* @title AdminFaucetAuthModuleTest /// @notice Tests the AdminFaucetAuthModule contract.
* @notice Tests the AdminFaucetAuthModule contract.
*/
contract AdminFaucetAuthModuleTest is Test { contract AdminFaucetAuthModuleTest is Test {
/** /// @notice The admin of the `AdminFaucetAuthModule` contract.
* @notice The admin of the `AdminFaucetAuthModule` contract.
*/
address internal admin; address internal admin;
/**
* @notice Private key of the `admin`. /// @notice Private key of the `admin`.
*/
uint256 internal adminKey; uint256 internal adminKey;
/**
* @notice Not an admin of the `AdminFaucetAuthModule` contract. /// @notice Not an admin of the `AdminFaucetAuthModule` contract.
*/
address internal nonAdmin; address internal nonAdmin;
/**
* @notice Private key of the `nonAdmin`. /// @notice Private key of the `nonAdmin`.
*/
uint256 internal nonAdminKey; uint256 internal nonAdminKey;
/**
* @notice An instance of the `AdminFaucetAuthModule` contract. /// @notice An instance of the `AdminFaucetAuthModule` contract.
*/
AdminFaucetAuthModule internal adminFam; AdminFaucetAuthModule internal adminFam;
/**
* @notice An instance of the `FaucetHelper` contract. /// @notice An instance of the `FaucetHelper` contract.
*/
FaucetHelper internal faucetHelper; FaucetHelper internal faucetHelper;
string internal adminFamName = "AdminFAM"; string internal adminFamName = "AdminFAM";
string internal adminFamVersion = "1"; string internal adminFamVersion = "1";
/** /// @notice Deploy the `AdminFaucetAuthModule` contract.
* @notice Deploy the `AdminFaucetAuthModule` contract.
*/
function setUp() external { function setUp() external {
adminKey = 0xB0B0B0B0; adminKey = 0xB0B0B0B0;
admin = vm.addr(adminKey); admin = vm.addr(adminKey);
...@@ -53,10 +44,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -53,10 +44,7 @@ contract AdminFaucetAuthModuleTest is Test {
faucetHelper = new FaucetHelper(); faucetHelper = new FaucetHelper();
} }
/** /// @notice Get signature as a bytes blob.
* @notice Get signature as a bytes blob.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -68,11 +56,9 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -68,11 +56,9 @@ contract AdminFaucetAuthModuleTest is Test {
return signature; return signature;
} }
/** /// @notice Signs a proof with the given private key and returns the signature using
* @notice Signs a proof with the given private key and returns the signature using /// the given EIP712 domain separator. This assumes that the issuer's address is the
* the given EIP712 domain separator. This assumes that the issuer's address is the /// corresponding public key to _issuerPrivateKey.
* corresponding public key to _issuerPrivateKey.
*/
function issueProofWithEIP712Domain( function issueProofWithEIP712Domain(
uint256 _issuerPrivateKey, uint256 _issuerPrivateKey,
bytes memory _eip712Name, bytes memory _eip712Name,
...@@ -101,9 +87,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -101,9 +87,7 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns true for valid proofs signed by admins.
* @notice assert that verify returns true for valid proofs signed by admins.
*/
function test_adminProof_verify_succeeds() external { function test_adminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
...@@ -129,9 +113,7 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -129,9 +113,7 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns false for proofs signed by nonadmins.
* @notice assert that verify returns false for proofs signed by nonadmins.
*/
function test_nonAdminProof_verify_succeeds() external { function test_nonAdminProof_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
...@@ -157,10 +139,8 @@ contract AdminFaucetAuthModuleTest is Test { ...@@ -157,10 +139,8 @@ contract AdminFaucetAuthModuleTest is Test {
); );
} }
/** /// @notice Assert that verify returns false for proofs where the id in the proof is different
* @notice assert that verify returns false for proofs where the id in the proof is different /// than the id in the call to verify.
* than the id in the call to verify.
*/
function test_proofWithWrongId_verify_succeeds() external { function test_proofWithWrongId_verify_succeeds() external {
bytes32 nonce = faucetHelper.consumeNonce(); bytes32 nonce = faucetHelper.consumeNonce();
address fundsReceiver = makeAddr("fundsReceiver"); address fundsReceiver = makeAddr("fundsReceiver");
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { TestERC20 } from "./Helpers.sol"; import { TestERC20 } from "./Helpers.sol";
import { TestERC721 } from "./Helpers.sol"; import { TestERC721 } from "./Helpers.sol";
...@@ -53,12 +53,12 @@ contract AssetReceiver_Initializer is Test { ...@@ -53,12 +53,12 @@ contract AssetReceiver_Initializer is Test {
} }
contract AssetReceiverTest is AssetReceiver_Initializer { contract AssetReceiverTest is AssetReceiver_Initializer {
// Tests if the owner was set correctly during deploy /// @notice Tests if the owner was set correctly during deploy.
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(address(alice), assetReceiver.owner()); assertEq(address(alice), assetReceiver.owner());
} }
// Tests that receive works as inteded /// @notice Tests that receive works as inteded.
function test_receive_succeeds() external { function test_receive_succeeds() external {
// Check that contract balance is 0 initially // Check that contract balance is 0 initially
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -74,7 +74,8 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -74,7 +74,8 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(assetReceiver).balance, 100); assertEq(address(assetReceiver).balance, 100);
} }
// Tests withdrawETH function with only an address as argument, called by owner /// @notice Tests withdrawETH function with only an address
/// as an argument, called by owner.
function test_withdrawETH_succeeds() external { function test_withdrawETH_succeeds() external {
// Check contract initial balance // Check contract initial balance
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -96,14 +97,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -96,14 +97,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(alice).balance, 2 ether); assertEq(address(alice).balance, 2 ether);
} }
// withdrawETH should fail if called by non-owner /// @notice withdrawETH should fail if called by non-owner.
function test_withdrawETH_unauthorized_reverts() external { function test_withdrawETH_unauthorized_reverts() external {
vm.deal(address(assetReceiver), 1 ether); vm.deal(address(assetReceiver), 1 ether);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawETH(payable(alice)); assetReceiver.withdrawETH(payable(alice));
} }
// Similar as withdrawETH but specify amount to withdraw /// @notice Similar as withdrawETH but specify amount to withdraw.
function test_withdrawETHwithAmount_succeeds() external { function test_withdrawETHwithAmount_succeeds() external {
assertEq(address(assetReceiver).balance, 0); assertEq(address(assetReceiver).balance, 0);
...@@ -124,14 +125,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -124,14 +125,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(address(alice).balance, 1.5 ether); assertEq(address(alice).balance, 1.5 ether);
} }
// withdrawETH with address and amount as arguments called by non-owner /// @notice withdrawETH with address and amount as arguments called by non-owner.
function test_withdrawETHwithAmount_unauthorized_reverts() external { function test_withdrawETHwithAmount_unauthorized_reverts() external {
vm.deal(address(assetReceiver), 1 ether); vm.deal(address(assetReceiver), 1 ether);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawETH(payable(alice), 0.5 ether); assetReceiver.withdrawETH(payable(alice), 0.5 ether);
} }
// Test withdrawERC20 with token and address arguments, from owner /// @notice Test withdrawERC20 with token and address arguments, from owner.
function test_withdrawERC20_succeeds() external { function test_withdrawERC20_succeeds() external {
// check balances before the call // check balances before the call
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
...@@ -152,14 +153,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -152,14 +153,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
} }
// Same as withdrawERC20 but call from non-owner /// @notice Same as withdrawERC20 but call from non-owner.
function test_withdrawERC20_unauthorized_reverts() external { function test_withdrawERC20_unauthorized_reverts() external {
deal(address(testERC20), address(assetReceiver), 100_000); deal(address(testERC20), address(assetReceiver), 100_000);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawERC20(testERC20, alice); assetReceiver.withdrawERC20(testERC20, alice);
} }
// Similar as withdrawERC20 but specify amount to withdraw /// @notice Similar as withdrawERC20 but specify amount to withdraw.
function test_withdrawERC20withAmount_succeeds() external { function test_withdrawERC20withAmount_succeeds() external {
// check balances before the call // check balances before the call
assertEq(testERC20.balanceOf(address(assetReceiver)), 0); assertEq(testERC20.balanceOf(address(assetReceiver)), 0);
...@@ -180,14 +181,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -180,14 +181,14 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC20.balanceOf(address(assetReceiver)), 50_000); assertEq(testERC20.balanceOf(address(assetReceiver)), 50_000);
} }
// Similar as withdrawERC20 with amount but call from non-owner /// @notice Similar as withdrawERC20 with amount but call from non-owner.
function test_withdrawERC20withAmount_unauthorized_reverts() external { function test_withdrawERC20withAmount_unauthorized_reverts() external {
deal(address(testERC20), address(assetReceiver), 100_000); deal(address(testERC20), address(assetReceiver), 100_000);
vm.expectRevert("UNAUTHORIZED"); vm.expectRevert("UNAUTHORIZED");
assetReceiver.withdrawERC20(testERC20, alice, 50_000); assetReceiver.withdrawERC20(testERC20, alice, 50_000);
} }
// Test withdrawERC721 from owner /// @notice Test withdrawERC721 from owner.
function test_withdrawERC721_succeeds() external { function test_withdrawERC721_succeeds() external {
// Check owner of the token before calling withdrawERC721 // Check owner of the token before calling withdrawERC721
assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice); assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
...@@ -208,7 +209,7 @@ contract AssetReceiverTest is AssetReceiver_Initializer { ...@@ -208,7 +209,7 @@ contract AssetReceiverTest is AssetReceiver_Initializer {
assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice); assertEq(testERC721.ownerOf(DEFAULT_TOKEN_ID), alice);
} }
// Similar as withdrawERC721 but call from non-owner /// @notice Similar as withdrawERC721 but call from non-owner.
function test_withdrawERC721_unauthorized_reverts() external { function test_withdrawERC721_unauthorized_reverts() external {
vm.prank(alice); vm.prank(alice);
testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID); testERC721.transferFrom(alice, address(assetReceiver), DEFAULT_TOKEN_ID);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { Vm } from "forge-std/Vm.sol"; import { Vm } from "forge-std/Vm.sol";
import "./CommonTest.t.sol"; import "./CommonTest.t.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities // Testing utilities
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckBalanceHigh } from "../periphery/drippie/dripchecks/CheckBalanceHigh.sol"; import { CheckBalanceHigh } from "../periphery/drippie/dripchecks/CheckBalanceHigh.sol";
/** /// @title CheckBalanceHighTest
* @title CheckBalanceHighTest /// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case
* @notice Tests the CheckBalanceHigh contract via fuzzing both the success case /// and the failure case.
* and the failure case.
*/
contract CheckBalanceHighTest is Test { contract CheckBalanceHighTest is Test {
/** /// @notice An instance of the CheckBalanceHigh contract.
* @notice An instance of the CheckBalanceHigh contract.
*/
CheckBalanceHigh c; CheckBalanceHigh c;
/** /// @notice Deploy the `CheckTrue` contract.
* @notice Deploy the `CheckTrue` contract.
*/
function setUp() external { function setUp() external {
c = new CheckBalanceHigh(); c = new CheckBalanceHigh();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the target's balance is larger than the threshold.
* when the target's balance is larger than the threshold.
*/
function testFuzz_check_succeeds(address _target, uint256 _threshold) external { function testFuzz_check_succeeds(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({
target: _target, target: _target,
...@@ -39,10 +31,8 @@ contract CheckBalanceHighTest is Test { ...@@ -39,10 +31,8 @@ contract CheckBalanceHighTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the target's balance is smaller than the threshold.
* when the target's balance is smaller than the threshold.
*/
function testFuzz_check_lowBalance_fails(address _target, uint256 _threshold) external { function testFuzz_check_lowBalance_fails(address _target, uint256 _threshold) external {
CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({ CheckBalanceHigh.Params memory p = CheckBalanceHigh.Params({
target: _target, target: _target,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckBalanceLow } from "../periphery/drippie/dripchecks/CheckBalanceLow.sol"; import { CheckBalanceLow } from "../periphery/drippie/dripchecks/CheckBalanceLow.sol";
/** /// @title CheckBalanceLowTest
* @title CheckBalanceLowTest /// @notice Tests the CheckBalanceLow contract via fuzzing both the success case
* @notice Tests the CheckBalanceLow contract via fuzzing both the success case /// and the failure case.
* and the failure case.
*/
contract CheckBalanceLowTest is Test { contract CheckBalanceLowTest is Test {
/** /// @notice An instance of the CheckBalanceLow contract.
* @notice An instance of the CheckBalanceLow contract.
*/
CheckBalanceLow c; CheckBalanceLow c;
/** /// @notice Deploy the `CheckBalanceLow` contract.
* @notice Deploy the `CheckBalanceLow` contract.
*/
function setUp() external { function setUp() external {
c = new CheckBalanceLow(); c = new CheckBalanceLow();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the target's balance is smaller than the threshold.
* when the target's balance is smaller than the threshold.
*/
function testFuzz_check_succeeds(address _target, uint256 _threshold) external { function testFuzz_check_succeeds(address _target, uint256 _threshold) external {
CheckBalanceLow.Params memory p = CheckBalanceLow.Params({ CheckBalanceLow.Params memory p = CheckBalanceLow.Params({
target: _target, target: _target,
...@@ -37,10 +29,8 @@ contract CheckBalanceLowTest is Test { ...@@ -37,10 +29,8 @@ contract CheckBalanceLowTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the target's balance is larger than the threshold.
* when the target's balance is larger than the threshold.
*/
function testFuzz_check_highBalance_fails(address _target, uint256 _threshold) external { function testFuzz_check_highBalance_fails(address _target, uint256 _threshold) external {
CheckBalanceLow.Params memory p = CheckBalanceLow.Params({ CheckBalanceLow.Params memory p = CheckBalanceLow.Params({
target: _target, target: _target,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -7,11 +7,9 @@ import { ...@@ -7,11 +7,9 @@ import {
IGelatoTreasury IGelatoTreasury
} from "../periphery/drippie/dripchecks/CheckGelatoLow.sol"; } from "../periphery/drippie/dripchecks/CheckGelatoLow.sol";
/** /// @title MockGelatoTreasury
* @title MockGelatoTreasury /// @notice Mocks the Gelato treasury for testing purposes. Allows arbitrary
* @notice Mocks the Gelato treasury for testing purposes. Allows arbitrary /// setting of user balances.
* setting of user balances.
*/
contract MockGelatoTreasury is IGelatoTreasury { contract MockGelatoTreasury is IGelatoTreasury {
mapping(address => mapping(address => uint256)) private tokenBalances; mapping(address => mapping(address => uint256)) private tokenBalances;
...@@ -28,39 +26,27 @@ contract MockGelatoTreasury is IGelatoTreasury { ...@@ -28,39 +26,27 @@ contract MockGelatoTreasury is IGelatoTreasury {
} }
} }
/** /// @title CheckGelatoLowTest
* @title CheckGelatoLowTest /// @notice Tests the CheckBalanceHigh contract via fuzzing both the success case
* @notice Tests the CheckBalanceHigh contract via fuzzing both the success case /// and the failure case.
* and the failure case.
*/
contract CheckGelatoLowTest is Test { contract CheckGelatoLowTest is Test {
/** /// @notice An instance of the CheckGelatoLow contract.
* @notice An instance of the CheckGelatoLow contract.
*/
CheckGelatoLow c; CheckGelatoLow c;
/** /// @notice An instance of the MockGelatoTreasury contract.
* @notice An instance of the MockGelatoTreasury contract.
*/
MockGelatoTreasury gelato; MockGelatoTreasury gelato;
/** /// @notice The account Gelato uses to represent ether
* @notice The account Gelato uses to represent ether
*/
address internal constant eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address internal constant eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/** /// @notice Deploy the `CheckGelatoLow` and `MockGelatoTreasury` contracts.
* @notice Deploy the `CheckGelatoLow` and `MockGelatoTreasury` contracts.
*/
function setUp() external { function setUp() external {
c = new CheckGelatoLow(); c = new CheckGelatoLow();
gelato = new MockGelatoTreasury(); gelato = new MockGelatoTreasury();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true
* @notice Fuzz the `check` function and assert that it always returns true /// when the user's balance in the treasury is less than the threshold.
* when the user's balance in the treasury is less than the threshold.
*/
function testFuzz_check_succeeds(uint256 _threshold, address _recipient) external { function testFuzz_check_succeeds(uint256 _threshold, address _recipient) external {
CheckGelatoLow.Params memory p = CheckGelatoLow.Params({ CheckGelatoLow.Params memory p = CheckGelatoLow.Params({
treasury: address(gelato), treasury: address(gelato),
...@@ -73,11 +59,9 @@ contract CheckGelatoLowTest is Test { ...@@ -73,11 +59,9 @@ contract CheckGelatoLowTest is Test {
assertEq(c.check(abi.encode(p)), true); assertEq(c.check(abi.encode(p)), true);
} }
/** /// @notice Fuzz the `check` function and assert that it always returns false
* @notice Fuzz the `check` function and assert that it always returns false /// when the user's balance in the treasury is greater than or equal
* when the user's balance in the treasury is greater than or equal /// to the threshold.
* to the threshold.
*/
function testFuzz_check_highBalance_fails(uint256 _threshold, address _recipient) external { function testFuzz_check_highBalance_fails(uint256 _threshold, address _recipient) external {
CheckGelatoLow.Params memory p = CheckGelatoLow.Params({ CheckGelatoLow.Params memory p = CheckGelatoLow.Params({
treasury: address(gelato), treasury: address(gelato),
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol";
/** /// @title CheckTrueTest
* @title CheckTrueTest /// @notice Ensures that the CheckTrue DripCheck contract always returns true.
* @notice Ensures that the CheckTrue DripCheck contract always returns true.
*/
contract CheckTrueTest is Test { contract CheckTrueTest is Test {
/** /// @notice An instance of the CheckTrue contract.
* @notice An instance of the CheckTrue contract.
*/
CheckTrue c; CheckTrue c;
/** /// @notice Deploy the `CheckTrue` contract.
* @notice Deploy the `CheckTrue` contract.
*/
function setUp() external { function setUp() external {
c = new CheckTrue(); c = new CheckTrue();
} }
/** /// @notice Fuzz the `check` function and assert that it always returns true.
* @notice Fuzz the `check` function and assert that it always returns true.
*/
function testFuzz_always_true_succeeds(bytes memory input) external { function testFuzz_always_true_succeeds(bytes memory input) external {
assertEq(c.check(input), true); assertEq(c.check(input), true);
} }
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test, StdUtils } from "forge-std/Test.sol"; import { Test, StdUtils } from "forge-std/Test.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
...@@ -766,9 +766,7 @@ contract ConfigurableCaller { ...@@ -766,9 +766,7 @@ contract ConfigurableCaller {
event WhatHappened(bool success, bytes returndata); event WhatHappened(bool success, bytes returndata);
/** /// @notice Call the configured target with the configured payload OR revert.
* @notice Call the configured target with the configured payload OR revert.
*/
function call() external { function call() external {
if (doRevert) { if (doRevert) {
revert("ConfigurableCaller: revert"); revert("ConfigurableCaller: revert");
...@@ -787,31 +785,23 @@ contract ConfigurableCaller { ...@@ -787,31 +785,23 @@ contract ConfigurableCaller {
} }
} }
/** /// @notice Set whether or not to have `call` revert.
* @notice Set whether or not to have `call` revert.
*/
function setDoRevert(bool _doRevert) external { function setDoRevert(bool _doRevert) external {
doRevert = _doRevert; doRevert = _doRevert;
} }
/** /// @notice Set the target for the call made in `call`.
* @notice Set the target for the call made in `call`.
*/
function setTarget(address _target) external { function setTarget(address _target) external {
target = _target; target = _target;
} }
/** /// @notice Set the payload for the call made in `call`.
* @notice Set the payload for the call made in `call`.
*/
function setPayload(bytes calldata _payload) external { function setPayload(bytes calldata _payload) external {
payload = _payload; payload = _payload;
} }
/** /// @notice Fallback function that reverts if `doRevert` is true.
* @notice Fallback function that reverts if `doRevert` is true. /// Otherwise, it does nothing.
* Otherwise, it does nothing.
*/
fallback() external { fallback() external {
if (doRevert) { if (doRevert) {
revert("ConfigurableCaller: revert"); revert("ConfigurableCaller: revert");
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -7,14 +7,12 @@ import { IDripCheck } from "../periphery/drippie/IDripCheck.sol"; ...@@ -7,14 +7,12 @@ import { IDripCheck } from "../periphery/drippie/IDripCheck.sol";
import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "../periphery/drippie/dripchecks/CheckTrue.sol";
import { SimpleStorage } from "./Helpers.sol"; import { SimpleStorage } from "./Helpers.sol";
/** /// @title TestDrippie
* @title TestDrippie /// @notice This is a wrapper contract around Drippie used for testing.
* @notice This is a wrapper contract around Drippie used for testing. /// Returning an entire `Drippie.DripState` causes stack too
* Returning an entire `Drippie.DripState` causes stack too /// deep errors without `--vir-ir` which causes the compile time
* deep errors without `--vir-ir` which causes the compile time /// to go up by ~4x. Each of the methods is a simple getter around
* to go up by ~4x. Each of the methods is a simple getter around /// parts of the `DripState`.
* parts of the `DripState`.
*/
contract TestDrippie is Drippie { contract TestDrippie is Drippie {
constructor(address owner) Drippie(owner) {} constructor(address owner) Drippie(owner) {}
...@@ -47,58 +45,38 @@ contract TestDrippie is Drippie { ...@@ -47,58 +45,38 @@ contract TestDrippie is Drippie {
} }
} }
/** /// @title Drippie_Test
* @title Drippie_Test /// @notice Test coverage of the Drippie contract.
* @notice Test coverage of the Drippie contract.
*/
contract Drippie_Test is Test { contract Drippie_Test is Test {
/** /// @notice Emitted when a drip is executed.
* @notice Emitted when a drip is executed.
*/
event DripExecuted(string indexed nameref, string name, address executor, uint256 timestamp); event DripExecuted(string indexed nameref, string name, address executor, uint256 timestamp);
/** /// @notice Emitted when a drip's status is updated.
* @notice Emitted when a drip's status is updated.
*/
event DripStatusUpdated(string indexed nameref, string name, Drippie.DripStatus status); event DripStatusUpdated(string indexed nameref, string name, Drippie.DripStatus status);
/** /// @notice Emitted when a drip is created.
* @notice Emitted when a drip is created.
*/
event DripCreated(string indexed nameref, string name, Drippie.DripConfig config); event DripCreated(string indexed nameref, string name, Drippie.DripConfig config);
/** /// @notice Address of the test DripCheck. CheckTrue is deployed
* @notice Address of the test DripCheck. CheckTrue is deployed /// here so it always returns true.
* here so it always returns true.
*/
IDripCheck check; IDripCheck check;
/** /// @notice Address of a SimpleStorage contract. Used to test that
* @notice Address of a SimpleStorage contract. Used to test that /// calls are actually made by Drippie.
* calls are actually made by Drippie.
*/
SimpleStorage simpleStorage; SimpleStorage simpleStorage;
/** /// @notice Address of the Drippie contract.
* @notice Address of the Drippie contract.
*/
TestDrippie drippie; TestDrippie drippie;
/** /// @notice Address of the Drippie owner
* @notice Address of the Drippie owner
*/
address constant alice = address(0x42); address constant alice = address(0x42);
/** /// @notice The name of the default drip
* @notice The name of the default drip
*/
string constant dripName = "foo"; string constant dripName = "foo";
/** /// @notice Set up the test suite by deploying a CheckTrue DripCheck.
* @notice Set up the test suite by deploying a CheckTrue DripCheck. /// This is a mock that always returns true. Alice is the owner
* This is a mock that always returns true. Alice is the owner /// and also give drippie 1 ether so that it can balance transfer.
* and also give drippie 1 ether so that it can balance transfer.
*/
function setUp() external { function setUp() external {
check = IDripCheck(new CheckTrue()); check = IDripCheck(new CheckTrue());
simpleStorage = new SimpleStorage(); simpleStorage = new SimpleStorage();
...@@ -107,12 +85,10 @@ contract Drippie_Test is Test { ...@@ -107,12 +85,10 @@ contract Drippie_Test is Test {
vm.deal(address(drippie), 1 ether); vm.deal(address(drippie), 1 ether);
} }
/** /// @notice Builds a default Drippie.DripConfig. Uses CheckTrue as the
* @notice Builds a default Drippie.DripConfig. Uses CheckTrue as the /// dripcheck so it will always be able to do its DripActions.
* dripcheck so it will always be able to do its DripActions. /// Gives a dummy DripAction that does a simple transfer to
* Gives a dummy DripAction that does a simple transfer to /// a dummy address.
* a dummy address.
*/
function _defaultConfig() internal view returns (Drippie.DripConfig memory) { function _defaultConfig() internal view returns (Drippie.DripConfig memory) {
Drippie.DripAction[] memory actions = new Drippie.DripAction[](1); Drippie.DripAction[] memory actions = new Drippie.DripAction[](1);
actions[0] = Drippie.DripAction({ target: payable(address(0x44)), data: hex"", value: 1 }); actions[0] = Drippie.DripAction({ target: payable(address(0x44)), data: hex"", value: 1 });
...@@ -127,9 +103,7 @@ contract Drippie_Test is Test { ...@@ -127,9 +103,7 @@ contract Drippie_Test is Test {
}); });
} }
/** /// @notice Creates a default drip using the default drip config.
* @notice Creates a default drip using the default drip config.
*/
function _createDefaultDrip(string memory name) internal { function _createDefaultDrip(string memory name) internal {
address owner = drippie.owner(); address owner = drippie.owner();
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
...@@ -137,18 +111,14 @@ contract Drippie_Test is Test { ...@@ -137,18 +111,14 @@ contract Drippie_Test is Test {
drippie.create(name, cfg); drippie.create(name, cfg);
} }
/** /// @notice Moves the block's timestamp forward so that it is
* @notice Moves the block's timestamp forward so that it is /// possible to execute a drip.
* possible to execute a drip.
*/
function _warpToExecutable(string memory name) internal { function _warpToExecutable(string memory name) internal {
Drippie.DripConfig memory config = drippie.dripConfig(name); Drippie.DripConfig memory config = drippie.dripConfig(name);
vm.warp(config.interval + drippie.dripStateLast(name)); vm.warp(config.interval + drippie.dripStateLast(name));
} }
/** /// @notice Creates a drip and asserts that it was configured as expected.
* @notice Creates a drip and asserts that it was configured as expected.
*/
function test_create_succeeds() external { function test_create_succeeds() external {
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
...@@ -185,9 +155,7 @@ contract Drippie_Test is Test { ...@@ -185,9 +155,7 @@ contract Drippie_Test is Test {
} }
} }
/** /// @notice Ensures that the same drip cannot be created two times.
* @notice Ensures that the same drip cannot be created two times.
*/
function test_create_calledTwice_reverts() external { function test_create_calledTwice_reverts() external {
vm.startPrank(drippie.owner()); vm.startPrank(drippie.owner());
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
...@@ -197,9 +165,7 @@ contract Drippie_Test is Test { ...@@ -197,9 +165,7 @@ contract Drippie_Test is Test {
vm.stopPrank(); vm.stopPrank();
} }
/** /// @notice Ensures that only the owner of Drippie can create a drip.
* @notice Ensures that only the owner of Drippie can create a drip.
*/
function testFuzz_owner_unauthorized_reverts(address caller) external { function testFuzz_owner_unauthorized_reverts(address caller) external {
vm.assume(caller != drippie.owner()); vm.assume(caller != drippie.owner());
vm.prank(caller); vm.prank(caller);
...@@ -207,9 +173,7 @@ contract Drippie_Test is Test { ...@@ -207,9 +173,7 @@ contract Drippie_Test is Test {
drippie.create(dripName, _defaultConfig()); drippie.create(dripName, _defaultConfig());
} }
/** /// @notice The owner should be able to set the status of the drip.
* @notice The owner should be able to set the status of the drip.
*/
function test_set_status_succeeds() external { function test_set_status_succeeds() external {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
emit DripCreated(dripName, dripName, _defaultConfig()); emit DripCreated(dripName, dripName, _defaultConfig());
...@@ -255,9 +219,7 @@ contract Drippie_Test is Test { ...@@ -255,9 +219,7 @@ contract Drippie_Test is Test {
} }
} }
/** /// @notice The drip status cannot be set back to NONE after it is created.
* @notice The drip status cannot be set back to NONE after it is created.
*/
function test_set_statusNone_reverts() external { function test_set_statusNone_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -268,10 +230,8 @@ contract Drippie_Test is Test { ...@@ -268,10 +230,8 @@ contract Drippie_Test is Test {
drippie.status(dripName, Drippie.DripStatus.NONE); drippie.status(dripName, Drippie.DripStatus.NONE);
} }
/** /// @notice The owner cannot set the status of the drip to the status that
* @notice The owner cannot set the status of the drip to the status that /// it is already set as.
* it is already set as.
*/
function test_set_statusSame_reverts() external { function test_set_statusSame_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -282,10 +242,8 @@ contract Drippie_Test is Test { ...@@ -282,10 +242,8 @@ contract Drippie_Test is Test {
drippie.status(dripName, Drippie.DripStatus.PAUSED); drippie.status(dripName, Drippie.DripStatus.PAUSED);
} }
/** /// @notice The owner should be able to archive the drip if it is in the
* @notice The owner should be able to archive the drip if it is in the /// paused state.
* paused state.
*/
function test_shouldArchive_ifPaused_succeeds() external { function test_shouldArchive_ifPaused_succeeds() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -306,10 +264,8 @@ contract Drippie_Test is Test { ...@@ -306,10 +264,8 @@ contract Drippie_Test is Test {
assertEq(uint256(status), uint256(Drippie.DripStatus.ARCHIVED)); assertEq(uint256(status), uint256(Drippie.DripStatus.ARCHIVED));
} }
/** /// @notice The owner should not be able to archive the drip if it is in the
* @notice The owner should not be able to archive the drip if it is in the /// active state.
* active state.
*/
function test_shouldNotArchive_ifActive_reverts() external { function test_shouldNotArchive_ifActive_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -323,30 +279,24 @@ contract Drippie_Test is Test { ...@@ -323,30 +279,24 @@ contract Drippie_Test is Test {
drippie.status(dripName, Drippie.DripStatus.ARCHIVED); drippie.status(dripName, Drippie.DripStatus.ARCHIVED);
} }
/** /// @notice The owner should not be allowed to pause the drip if it
* @notice The owner should not be allowed to pause the drip if it /// has already been archived.
* has already been archived.
*/
function test_shouldNotAllowPaused_ifArchived_reverts() external { function test_shouldNotAllowPaused_ifArchived_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
_notAllowFromArchive(dripName, Drippie.DripStatus.PAUSED); _notAllowFromArchive(dripName, Drippie.DripStatus.PAUSED);
} }
/** /// @notice The owner should not be allowed to make the drip active again if
* @notice The owner should not be allowed to make the drip active again if /// it has already been archived.
* it has already been archived.
*/
function test_shouldNotAllowActive_ifArchived_reverts() external { function test_shouldNotAllowActive_ifArchived_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
_notAllowFromArchive(dripName, Drippie.DripStatus.ACTIVE); _notAllowFromArchive(dripName, Drippie.DripStatus.ACTIVE);
} }
/** /// @notice Archive the drip and then attempt to set the status to the passed
* @notice Archive the drip and then attempt to set the status to the passed /// in status.
* in status.
*/
function _notAllowFromArchive(string memory name, Drippie.DripStatus status) internal { function _notAllowFromArchive(string memory name, Drippie.DripStatus status) internal {
address owner = drippie.owner(); address owner = drippie.owner();
vm.prank(owner); vm.prank(owner);
...@@ -358,9 +308,7 @@ contract Drippie_Test is Test { ...@@ -358,9 +308,7 @@ contract Drippie_Test is Test {
drippie.status(name, status); drippie.status(name, status);
} }
/** /// @notice Attempt to update a drip that does not exist.
* @notice Attempt to update a drip that does not exist.
*/
function test_name_notExist_reverts() external { function test_name_notExist_reverts() external {
string memory otherName = "bar"; string memory otherName = "bar";
...@@ -373,9 +321,7 @@ contract Drippie_Test is Test { ...@@ -373,9 +321,7 @@ contract Drippie_Test is Test {
drippie.status(otherName, Drippie.DripStatus.ARCHIVED); drippie.status(otherName, Drippie.DripStatus.ARCHIVED);
} }
/** /// @notice Expect a revert when attempting to set the status when not the owner.
* @notice Expect a revert when attempting to set the status when not the owner.
*/
function test_status_unauthorized_reverts() external { function test_status_unauthorized_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -383,9 +329,7 @@ contract Drippie_Test is Test { ...@@ -383,9 +329,7 @@ contract Drippie_Test is Test {
drippie.status(dripName, Drippie.DripStatus.ACTIVE); drippie.status(dripName, Drippie.DripStatus.ACTIVE);
} }
/** /// @notice The drip should execute and be able to transfer value.
* @notice The drip should execute and be able to transfer value.
*/
function test_drip_amount_succeeds() external { function test_drip_amount_succeeds() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -415,9 +359,7 @@ contract Drippie_Test is Test { ...@@ -415,9 +359,7 @@ contract Drippie_Test is Test {
drippie.drip(dripName); drippie.drip(dripName);
} }
/** /// @notice A single DripAction should be able to make a state modifying call.
* @notice A single DripAction should be able to make a state modifying call.
*/
function test_trigger_oneFunction_succeeds() external { function test_trigger_oneFunction_succeeds() external {
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
...@@ -452,9 +394,7 @@ contract Drippie_Test is Test { ...@@ -452,9 +394,7 @@ contract Drippie_Test is Test {
assertEq(simpleStorage.get(key), value); assertEq(simpleStorage.get(key), value);
} }
/** /// @notice Multiple drip actions should be able to be triggered with the same check.
* @notice Multiple drip actions should be able to be triggered with the same check.
*/
function test_trigger_twoFunctions_succeeds() external { function test_trigger_twoFunctions_succeeds() external {
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
Drippie.DripAction[] memory actions = new Drippie.DripAction[](2); Drippie.DripAction[] memory actions = new Drippie.DripAction[](2);
...@@ -511,11 +451,9 @@ contract Drippie_Test is Test { ...@@ -511,11 +451,9 @@ contract Drippie_Test is Test {
assertEq(simpleStorage.get(keyTwo), valueTwo); assertEq(simpleStorage.get(keyTwo), valueTwo);
} }
/** /// @notice The drips can only be triggered once per interval. Attempt to
* @notice The drips can only be triggered once per interval. Attempt to /// trigger the same drip multiple times in the same interval. Then
* trigger the same drip multiple times in the same interval. Then /// move forward to the next interval and it should trigger.
* move forward to the next interval and it should trigger.
*/
function test_twice_inOneInterval_reverts() external { function test_twice_inOneInterval_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -547,10 +485,8 @@ contract Drippie_Test is Test { ...@@ -547,10 +485,8 @@ contract Drippie_Test is Test {
drippie.drip(dripName); drippie.drip(dripName);
} }
/** /// @notice It should revert if attempting to trigger a drip that does not exist.
* @notice It should revert if attempting to trigger a drip that does not exist. /// Note that the drip was never created at the beginning of the test.
* Note that the drip was never created at the beginning of the test.
*/
function test_drip_notExist_reverts() external { function test_drip_notExist_reverts() external {
vm.prank(drippie.owner()); vm.prank(drippie.owner());
...@@ -559,9 +495,7 @@ contract Drippie_Test is Test { ...@@ -559,9 +495,7 @@ contract Drippie_Test is Test {
drippie.drip(dripName); drippie.drip(dripName);
} }
/** /// @notice The owner cannot trigger the drip when it is paused.
* @notice The owner cannot trigger the drip when it is paused.
*/
function test_not_active_reverts() external { function test_not_active_reverts() external {
_createDefaultDrip(dripName); _createDefaultDrip(dripName);
...@@ -575,9 +509,7 @@ contract Drippie_Test is Test { ...@@ -575,9 +509,7 @@ contract Drippie_Test is Test {
drippie.drip(dripName); drippie.drip(dripName);
} }
/** /// @notice The interval must be 0 if reentrant is set on the config.
* @notice The interval must be 0 if reentrant is set on the config.
*/
function test_reentrant_succeeds() external { function test_reentrant_succeeds() external {
address owner = drippie.owner(); address owner = drippie.owner();
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
...@@ -594,10 +526,8 @@ contract Drippie_Test is Test { ...@@ -594,10 +526,8 @@ contract Drippie_Test is Test {
assertEq(_cfg.interval, 0); assertEq(_cfg.interval, 0);
} }
/** /// @notice A non zero interval when reentrant is true will cause a revert
* @notice A non zero interval when reentrant is true will cause a revert /// when creating a drip.
* when creating a drip.
*/
function test_drip_reentrant_reverts() external { function test_drip_reentrant_reverts() external {
address owner = drippie.owner(); address owner = drippie.owner();
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
...@@ -610,10 +540,8 @@ contract Drippie_Test is Test { ...@@ -610,10 +540,8 @@ contract Drippie_Test is Test {
drippie.create(dripName, cfg); drippie.create(dripName, cfg);
} }
/** /// @notice If reentrant is false and the interval is 0 then it should
* @notice If reentrant is false and the interval is 0 then it should /// revert when the drip is created.
* revert when the drip is created.
*/
function test_notReentrant_zeroInterval_reverts() external { function test_notReentrant_zeroInterval_reverts() external {
address owner = drippie.owner(); address owner = drippie.owner();
Drippie.DripConfig memory cfg = _defaultConfig(); Drippie.DripConfig memory cfg = _defaultConfig();
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -45,9 +45,7 @@ contract Faucet_Initializer is Test { ...@@ -45,9 +45,7 @@ contract Faucet_Initializer is Test {
_initializeContracts(); _initializeContracts();
} }
/** /// @notice Instantiates a Faucet.
* @notice Instantiates a Faucet.
*/
function _initializeContracts() internal { function _initializeContracts() internal {
faucet = new Faucet(faucetContractAdmin); faucet = new Faucet(faucetContractAdmin);
...@@ -76,10 +74,7 @@ contract Faucet_Initializer is Test { ...@@ -76,10 +74,7 @@ contract Faucet_Initializer is Test {
vm.stopPrank(); vm.stopPrank();
} }
/** /// @notice Get signature as a bytes blob.
* @notice Get signature as a bytes blob.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -91,11 +86,9 @@ contract Faucet_Initializer is Test { ...@@ -91,11 +86,9 @@ contract Faucet_Initializer is Test {
return signature; return signature;
} }
/** /// @notice Signs a proof with the given private key and returns the signature using
* @notice Signs a proof with the given private key and returns the signature using /// the given EIP712 domain separator. This assumes that the issuer's address is the
* the given EIP712 domain separator. This assumes that the issuer's address is the /// corresponding public key to _issuerPrivateKey.
* corresponding public key to _issuerPrivateKey.
*/
function issueProofWithEIP712Domain( function issueProofWithEIP712Domain(
uint256 _issuerPrivateKey, uint256 _issuerPrivateKey,
bytes memory _eip712Name, bytes memory _eip712Name,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
...@@ -66,38 +66,27 @@ contract SimpleStorage { ...@@ -66,38 +66,27 @@ contract SimpleStorage {
} }
} }
/** /// @notice Simple helper contract that helps with testing flow and signature for
* Simple helper contract that helps with testing flow and signature for OptimistInviter contract. /// OptimistInviter contract. Made this a separate contract instead of including
* Made this a separate contract instead of including in OptimistInviter.t.sol for reusability. /// in OptimistInviter.t.sol for reusability.
*/
contract OptimistInviterHelper { contract OptimistInviterHelper {
/** /// @notice EIP712 typehash for the ClaimableInvite type.
* @notice EIP712 typehash for the ClaimableInvite type.
*/
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH = bytes32 public constant CLAIMABLE_INVITE_TYPEHASH =
keccak256("ClaimableInvite(address issuer,bytes32 nonce)"); keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
/** /// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
* @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
*/
bytes32 public constant EIP712_DOMAIN_TYPEHASH = bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256( keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
); );
/** /// @notice Address of OptimistInviter contract we are testing.
* @notice Address of OptimistInviter contract we are testing.
*/
OptimistInviter public optimistInviter; OptimistInviter public optimistInviter;
/** /// @notice OptimistInviter contract name. Used to construct the EIP-712 domain.
* @notice OptimistInviter contract name. Used to construct the EIP-712 domain.
*/
string public name; string public name;
/** /// @notice Keeps track of current nonce to generate new nonces for each invite.
* @notice Keeps track of current nonce to generate new nonces for each invite.
*/
uint256 public currentNonce; uint256 public currentNonce;
constructor(OptimistInviter _optimistInviter, string memory _name) { constructor(OptimistInviter _optimistInviter, string memory _name) {
...@@ -105,13 +94,9 @@ contract OptimistInviterHelper { ...@@ -105,13 +94,9 @@ contract OptimistInviterHelper {
name = _name; name = _name;
} }
/** /// @notice Returns the hash of the struct ClaimableInvite.
* @notice Returns the hash of the struct ClaimableInvite. /// @param _claimableInvite ClaimableInvite struct to hash.
* /// @return EIP-712 typed struct hash.
* @param _claimableInvite ClaimableInvite struct to hash.
*
* @return EIP-712 typed struct hash.
*/
function getClaimableInviteStructHash(OptimistInviter.ClaimableInvite memory _claimableInvite) function getClaimableInviteStructHash(OptimistInviter.ClaimableInvite memory _claimableInvite)
public public
pure pure
...@@ -127,23 +112,16 @@ contract OptimistInviterHelper { ...@@ -127,23 +112,16 @@ contract OptimistInviterHelper {
); );
} }
/** /// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
* @notice Returns a bytes32 nonce that should change everytime. In practice, people should use /// pseudorandom nonces.
* pseudorandom nonces. /// @return Nonce that should be used as part of ClaimableInvite.
*
* @return Nonce that should be used as part of ClaimableInvite.
*/
function consumeNonce() public returns (bytes32) { function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++))); return bytes32(keccak256(abi.encode(currentNonce++)));
} }
/** /// @notice Returns a ClaimableInvite with the issuer and current nonce.
* @notice Returns a ClaimableInvite with the issuer and current nonce. /// @param _issuer Issuer to include in the ClaimableInvite.
* /// @return ClaimableInvite that can be hashed & signed.
* @param _issuer Issuer to include in the ClaimableInvite.
*
* @return ClaimableInvite that can be hashed & signed.
*/
function getClaimableInviteWithNewNonce(address _issuer) function getClaimableInviteWithNewNonce(address _issuer)
public public
returns (OptimistInviter.ClaimableInvite memory) returns (OptimistInviter.ClaimableInvite memory)
...@@ -151,13 +129,9 @@ contract OptimistInviterHelper { ...@@ -151,13 +129,9 @@ contract OptimistInviterHelper {
return OptimistInviter.ClaimableInvite(_issuer, consumeNonce()); return OptimistInviter.ClaimableInvite(_issuer, consumeNonce());
} }
/** /// @notice Computes the EIP712 digest with default correct parameters.
* @notice Computes the EIP712 digest with default correct parameters. /// @param _claimableInvite ClaimableInvite struct to hash.
* /// @return EIP-712 compatible digest.
* @param _claimableInvite ClaimableInvite struct to hash.
*
* @return EIP-712 compatible digest.
*/
function getDigest(OptimistInviter.ClaimableInvite calldata _claimableInvite) function getDigest(OptimistInviter.ClaimableInvite calldata _claimableInvite)
public public
view view
...@@ -173,18 +147,14 @@ contract OptimistInviterHelper { ...@@ -173,18 +147,14 @@ contract OptimistInviterHelper {
); );
} }
/** /// @notice Computes the EIP712 digest with the given domain parameters.
* @notice Computes the EIP712 digest with the given domain parameters. /// Used for testing that different domain parameters fail.
* Used for testing that different domain parameters fail. /// @param _claimableInvite ClaimableInvite struct to hash.
* /// @param _name Contract name to use in the EIP712 domain.
* @param _claimableInvite ClaimableInvite struct to hash. /// @param _version Contract version to use in the EIP712 domain.
* @param _name Contract name to use in the EIP712 domain. /// @param _chainid Chain ID to use in the EIP712 domain.
* @param _version Contract version to use in the EIP712 domain. /// @param _verifyingContract Address to use in the EIP712 domain.
* @param _chainid Chain ID to use in the EIP712 domain. /// @return EIP-712 compatible digest.
* @param _verifyingContract Address to use in the EIP712 domain.
*
* @return EIP-712 compatible digest.
*/
function getDigestWithEIP712Domain( function getDigestWithEIP712Domain(
OptimistInviter.ClaimableInvite calldata _claimableInvite, OptimistInviter.ClaimableInvite calldata _claimableInvite,
bytes memory _name, bytes memory _name,
...@@ -206,11 +176,9 @@ contract OptimistInviterHelper { ...@@ -206,11 +176,9 @@ contract OptimistInviterHelper {
} }
} }
// solhint-disable max-line-length /// @notice Simple ERC1271 wallet that can be used to test the ERC1271 signature checker.
/** /// @notice https://github.com/OpenZeppelin/openzeppelin-contracts/
* Simple ERC1271 wallet that can be used to test the ERC1271 signature checker. /// blob/master/contracts/mocks/ERC1271WalletMock.sol
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/mocks/ERC1271WalletMock.sol
*/
contract TestERC1271Wallet is Ownable, IERC1271 { contract TestERC1271Wallet is Ownable, IERC1271 {
constructor(address originalOwner) { constructor(address originalOwner) {
transferOwnership(originalOwner); transferOwnership(originalOwner);
...@@ -227,46 +195,31 @@ contract TestERC1271Wallet is Ownable, IERC1271 { ...@@ -227,46 +195,31 @@ contract TestERC1271Wallet is Ownable, IERC1271 {
} }
} }
/** /// @notice Simple helper contract that helps with testing the Faucet contract.
* Simple helper contract that helps with testing the Faucet contract.
*/
contract FaucetHelper { contract FaucetHelper {
/** /// @notice EIP712 typehash for the Proof type.
* @notice EIP712 typehash for the Proof type.
*/
bytes32 public constant PROOF_TYPEHASH = bytes32 public constant PROOF_TYPEHASH =
keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)"); keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)");
/** /// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
* @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
*/
bytes32 public constant EIP712_DOMAIN_TYPEHASH = bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256( keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
); );
/** /// @notice Keeps track of current nonce to generate new nonces for each drip.
* @notice Keeps track of current nonce to generate new nonces for each drip.
*/
uint256 public currentNonce; uint256 public currentNonce;
/** /// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
* @notice Returns a bytes32 nonce that should change everytime. In practice, people should use /// pseudorandom nonces.
* pseudorandom nonces. /// @return Nonce that should be used as part of drip parameters.
*
* @return Nonce that should be used as part of drip parameters.
*/
function consumeNonce() public returns (bytes32) { function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++))); return bytes32(keccak256(abi.encode(currentNonce++)));
} }
/** /// @notice Returns the hash of the struct Proof.
* @notice Returns the hash of the struct Proof. /// @param _proof Proof struct to hash.
* /// @return EIP-712 typed struct hash.
* @param _proof Proof struct to hash.
*
* @return EIP-712 typed struct hash.
*/
function getProofStructHash(AdminFaucetAuthModule.Proof memory _proof) function getProofStructHash(AdminFaucetAuthModule.Proof memory _proof)
public public
pure pure
...@@ -275,20 +228,16 @@ contract FaucetHelper { ...@@ -275,20 +228,16 @@ contract FaucetHelper {
return keccak256(abi.encode(PROOF_TYPEHASH, _proof.recipient, _proof.nonce, _proof.id)); return keccak256(abi.encode(PROOF_TYPEHASH, _proof.recipient, _proof.nonce, _proof.id));
} }
/** /// @notice Computes the EIP712 digest with the given domain parameters.
* @notice Computes the EIP712 digest with the given domain parameters. /// Used for testing that different domain parameters fail.
* Used for testing that different domain parameters fail. /// @param _proof Proof struct to hash.
* /// @param _name Contract name to use in the EIP712 domain.
* @param _proof Proof struct to hash. /// @param _version Contract version to use in the EIP712 domain.
* @param _name Contract name to use in the EIP712 domain. /// @param _chainid Chain ID to use in the EIP712 domain.
* @param _version Contract version to use in the EIP712 domain. /// @param _verifyingContract Address to use in the EIP712 domain.
* @param _chainid Chain ID to use in the EIP712 domain. /// @param _verifyingContract Address to use in the EIP712 domain.
* @param _verifyingContract Address to use in the EIP712 domain. /// @param _verifyingContract Address to use in the EIP712 domain.
* @param _verifyingContract Address to use in the EIP712 domain. /// @return EIP-712 compatible digest.
* @param _verifyingContract Address to use in the EIP712 domain.
*
* @return EIP-712 compatible digest.
*/
function getDigestWithEIP712Domain( function getDigestWithEIP712Domain(
AdminFaucetAuthModule.Proof memory _proof, AdminFaucetAuthModule.Proof memory _proof,
bytes memory _name, bytes memory _name,
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0; pragma solidity >=0.6.2 <0.9.0;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol";
import { Optimist } from "../periphery/op-nft/Optimist.sol"; import { Optimist } from "../periphery/op-nft/Optimist.sol";
...@@ -67,9 +67,7 @@ contract Optimist_Initializer is Test { ...@@ -67,9 +67,7 @@ contract Optimist_Initializer is Test {
address internal bob; address internal bob;
address internal sally; address internal sally;
/** /// @notice BaseURI attestor sets the baseURI of the Optimist NFT.
* @notice BaseURI attestor sets the baseURI of the Optimist NFT.
*/
function _attestBaseURI(string memory _baseUri) internal { function _attestBaseURI(string memory _baseUri) internal {
bytes32 baseURIAttestationKey = optimist.BASE_URI_ATTESTATION_KEY(); bytes32 baseURIAttestationKey = optimist.BASE_URI_ATTESTATION_KEY();
AttestationStation.AttestationData[] AttestationStation.AttestationData[]
...@@ -91,9 +89,7 @@ contract Optimist_Initializer is Test { ...@@ -91,9 +89,7 @@ contract Optimist_Initializer is Test {
attestationStation.attest(attestationData); attestationStation.attest(attestationData);
} }
/** /// @notice Allowlist attestor creates an attestation for an address.
* @notice Allowlist attestor creates an attestation for an address.
*/
function _attestAllowlist(address _about) internal { function _attestAllowlist(address _about) internal {
bytes32 attestationKey = optimistAllowlist.OPTIMIST_CAN_MINT_ATTESTATION_KEY(); bytes32 attestationKey = optimistAllowlist.OPTIMIST_CAN_MINT_ATTESTATION_KEY();
AttestationStation.AttestationData[] AttestationStation.AttestationData[]
...@@ -114,9 +110,7 @@ contract Optimist_Initializer is Test { ...@@ -114,9 +110,7 @@ contract Optimist_Initializer is Test {
assertTrue(optimist.isOnAllowList(_about)); assertTrue(optimist.isOnAllowList(_about));
} }
/** /// @notice Coinbase Quest attestor creates an attestation for an address.
* @notice Coinbase Quest attestor creates an attestation for an address.
*/
function _attestCoinbaseQuest(address _about) internal { function _attestCoinbaseQuest(address _about) internal {
bytes32 attestationKey = optimistAllowlist.COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY(); bytes32 attestationKey = optimistAllowlist.COINBASE_QUEST_ELIGIBLE_ATTESTATION_KEY();
AttestationStation.AttestationData[] AttestationStation.AttestationData[]
...@@ -137,9 +131,7 @@ contract Optimist_Initializer is Test { ...@@ -137,9 +131,7 @@ contract Optimist_Initializer is Test {
assertTrue(optimist.isOnAllowList(_about)); assertTrue(optimist.isOnAllowList(_about));
} }
/** /// @notice Issues invite, then claims it using the claimer's address.
* @notice Issues invite, then claims it using the claimer's address.
*/
function _inviteAndClaim(address _about) internal { function _inviteAndClaim(address _about) internal {
uint256 inviterPrivateKey = 0xbeefbeef; uint256 inviterPrivateKey = 0xbeefbeef;
address inviter = vm.addr(inviterPrivateKey); address inviter = vm.addr(inviterPrivateKey);
...@@ -179,9 +171,7 @@ contract Optimist_Initializer is Test { ...@@ -179,9 +171,7 @@ contract Optimist_Initializer is Test {
assertTrue(optimist.isOnAllowList(_about)); assertTrue(optimist.isOnAllowList(_about));
} }
/** /// @notice Mocks the allowlistAttestor to always return true for a given address.
* @notice Mocks the allowlistAttestor to always return true for a given address.
*/
function _mockAllowlistTrueFor(address _claimer) internal { function _mockAllowlistTrueFor(address _claimer) internal {
vm.mockCall( vm.mockCall(
address(optimistAllowlist), address(optimistAllowlist),
...@@ -192,9 +182,7 @@ contract Optimist_Initializer is Test { ...@@ -192,9 +182,7 @@ contract Optimist_Initializer is Test {
assertTrue(optimist.isOnAllowList(_claimer)); assertTrue(optimist.isOnAllowList(_claimer));
} }
/** /// @notice Returns address as uint256.
* @notice Returns address as uint256.
*/
function _getTokenId(address _owner) internal pure returns (uint256) { function _getTokenId(address _owner) internal pure returns (uint256) {
return uint256(uint160(address(_owner))); return uint256(uint160(address(_owner)));
} }
...@@ -245,9 +233,7 @@ contract Optimist_Initializer is Test { ...@@ -245,9 +233,7 @@ contract Optimist_Initializer is Test {
} }
contract OptimistTest is Optimist_Initializer { contract OptimistTest is Optimist_Initializer {
/** /// @notice Check that constructor and initializer parameters are correctly set.
* @notice Check that constructor and initializer parameters are correctly set.
*/
function test_initialize_succeeds() external { function test_initialize_succeeds() external {
// expect name to be set // expect name to be set
assertEq(optimist.name(), name); assertEq(optimist.name(), name);
...@@ -259,10 +245,8 @@ contract OptimistTest is Optimist_Initializer { ...@@ -259,10 +245,8 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.version(), "2.0.0"); assertEq(optimist.version(), "2.0.0");
} }
/** /// @notice Bob should be able to mint an NFT if he is allowlisted
* @notice Bob should be able to mint an NFT if he is allowlisted /// by the allowlistAttestor and has a balance of 0.
* by the allowlistAttestor and has a balance of 0.
*/
function test_mint_afterAllowlistAttestation_succeeds() external { function test_mint_afterAllowlistAttestation_succeeds() external {
// bob should start with 0 balance // bob should start with 0 balance
assertEq(optimist.balanceOf(bob), 0); assertEq(optimist.balanceOf(bob), 0);
...@@ -287,10 +271,8 @@ contract OptimistTest is Optimist_Initializer { ...@@ -287,10 +271,8 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Bob should be able to mint an NFT if he claimed an invite through OptimistInviter
* @notice Bob should be able to mint an NFT if he claimed an invite through OptimistInviter /// and has a balance of 0.
* and has a balance of 0.
*/
function test_mint_afterInviteClaimed_succeeds() external { function test_mint_afterInviteClaimed_succeeds() external {
// bob should start with 0 balance // bob should start with 0 balance
assertEq(optimist.balanceOf(bob), 0); assertEq(optimist.balanceOf(bob), 0);
...@@ -315,10 +297,8 @@ contract OptimistTest is Optimist_Initializer { ...@@ -315,10 +297,8 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Bob should be able to mint an NFT if he has an attestation from Coinbase Quest
* @notice Bob should be able to mint an NFT if he has an attestation from Coinbase Quest /// attestor and has a balance of 0.
* attestor and has a balance of 0.
*/
function test_mint_afterCoinbaseQuestAttestation_succeeds() external { function test_mint_afterCoinbaseQuestAttestation_succeeds() external {
// bob should start with 0 balance // bob should start with 0 balance
assertEq(optimist.balanceOf(bob), 0); assertEq(optimist.balanceOf(bob), 0);
...@@ -343,9 +323,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -343,9 +323,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Multiple valid attestations should allow Bob to mint.
* @notice Multiple valid attestations should allow Bob to mint.
*/
function test_mint_afterMultipleAttestations_succeeds() external { function test_mint_afterMultipleAttestations_succeeds() external {
// bob should start with 0 balance // bob should start with 0 balance
assertEq(optimist.balanceOf(bob), 0); assertEq(optimist.balanceOf(bob), 0);
...@@ -376,9 +354,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -376,9 +354,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Sally should be able to mint a token on behalf of bob.
* @notice Sally should be able to mint a token on behalf of bob.
*/
function test_mint_secondaryMinter_succeeds() external { function test_mint_secondaryMinter_succeeds() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -394,18 +370,14 @@ contract OptimistTest is Optimist_Initializer { ...@@ -394,18 +370,14 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Bob should not be able to mint an NFT if he is not allowlisted.
* @notice Bob should not be able to mint an NFT if he is not allowlisted.
*/
function test_mint_forNonAllowlistedClaimer_reverts() external { function test_mint_forNonAllowlistedClaimer_reverts() external {
vm.prank(bob); vm.prank(bob);
vm.expectRevert("Optimist: address is not on allowList"); vm.expectRevert("Optimist: address is not on allowList");
optimist.mint(bob); optimist.mint(bob);
} }
/** /// @notice Bob's tx should revert if he already minted.
* @notice Bob's tx should revert if he already minted.
*/
function test_mint_forAlreadyMintedClaimer_reverts() external { function test_mint_forAlreadyMintedClaimer_reverts() external {
_attestAllowlist(bob); _attestAllowlist(bob);
...@@ -421,9 +393,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -421,9 +393,7 @@ contract OptimistTest is Optimist_Initializer {
optimist.mint(bob); optimist.mint(bob);
} }
/** /// @notice The baseURI should be set by attestation station by the baseURIAttestor.
* @notice The baseURI should be set by attestation station by the baseURIAttestor.
*/
function test_baseURI_returnsCorrectBaseURI_succeeds() external { function test_baseURI_returnsCorrectBaseURI_succeeds() external {
_attestBaseURI(base_uri); _attestBaseURI(base_uri);
...@@ -440,9 +410,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -440,9 +410,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.baseURI(), base_uri); assertEq(optimist.baseURI(), base_uri);
} }
/** /// @notice tokenURI should return the token uri for a minted token.
* @notice tokenURI should return the token uri for a minted token.
*/
function test_tokenURI_returnsCorrectTokenURI_succeeds() external { function test_tokenURI_returnsCorrectTokenURI_succeeds() external {
// we are using true but it can be any non empty value // we are using true but it can be any non empty value
_attestBaseURI(base_uri); _attestBaseURI(base_uri);
...@@ -460,9 +428,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -460,9 +428,7 @@ contract OptimistTest is Optimist_Initializer {
); );
} }
/** /// @notice Should return the token id of the owner.
* @notice Should return the token id of the owner.
*/
function test_tokenIdOfAddress_returnsOwnerID_succeeds() external { function test_tokenIdOfAddress_returnsOwnerID_succeeds() external {
uint256 willTokenId = 1024; uint256 willTokenId = 1024;
address will = address(1024); address will = address(1024);
...@@ -474,9 +440,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -474,9 +440,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.tokenIdOfAddress(will), willTokenId); assertEq(optimist.tokenIdOfAddress(will), willTokenId);
} }
/** /// @notice transferFrom should revert since Optimist is a SBT.
* @notice transferFrom should revert since Optimist is a SBT.
*/
function test_transferFrom_soulbound_reverts() external { function test_transferFrom_soulbound_reverts() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -499,9 +463,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -499,9 +463,7 @@ contract OptimistTest is Optimist_Initializer {
optimist.safeTransferFrom(bob, sally, _getTokenId(bob), bytes("0x")); optimist.safeTransferFrom(bob, sally, _getTokenId(bob), bytes("0x"));
} }
/** /// @notice approve should revert since Optimist is a SBT.
* @notice approve should revert since Optimist is a SBT.
*/
function test_approve_soulbound_reverts() external { function test_approve_soulbound_reverts() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -517,9 +479,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -517,9 +479,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.getApproved(_getTokenId(bob)), address(0)); assertEq(optimist.getApproved(_getTokenId(bob)), address(0));
} }
/** /// @notice setApprovalForAll should revert since Optimist is a SBT.
* @notice setApprovalForAll should revert since Optimist is a SBT.
*/
function test_setApprovalForAll_soulbound_reverts() external { function test_setApprovalForAll_soulbound_reverts() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -539,9 +499,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -539,9 +499,7 @@ contract OptimistTest is Optimist_Initializer {
); );
} }
/** /// @notice Only owner should be able to burn token.
* @notice Only owner should be able to burn token.
*/
function test_burn_byOwner_succeeds() external { function test_burn_byOwner_succeeds() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -557,9 +515,7 @@ contract OptimistTest is Optimist_Initializer { ...@@ -557,9 +515,7 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 0); assertEq(optimist.balanceOf(bob), 0);
} }
/** /// @notice Non-owner attempting to burn token should revert.
* @notice Non-owner attempting to burn token should revert.
*/
function test_burn_byNonOwner_reverts() external { function test_burn_byNonOwner_reverts() external {
_mockAllowlistTrueFor(bob); _mockAllowlistTrueFor(bob);
...@@ -576,20 +532,16 @@ contract OptimistTest is Optimist_Initializer { ...@@ -576,20 +532,16 @@ contract OptimistTest is Optimist_Initializer {
assertEq(optimist.balanceOf(bob), 1); assertEq(optimist.balanceOf(bob), 1);
} }
/** /// @notice Should support ERC-721 interface.
* @notice Should support ERC-721 interface.
*/
function test_supportsInterface_returnsCorrectInterfaceForERC721_succeeds() external { function test_supportsInterface_returnsCorrectInterfaceForERC721_succeeds() external {
bytes4 iface721 = type(IERC721).interfaceId; bytes4 iface721 = type(IERC721).interfaceId;
// check that it supports ERC-721 interface // check that it supports ERC-721 interface
assertEq(optimist.supportsInterface(iface721), true); assertEq(optimist.supportsInterface(iface721), true);
} }
/** /// @notice Checking that multi-call using the invite & claim flow works correctly, since the
* @notice Checking that multi-call using the invite & claim flow works correctly, since the /// frontend will be making multicalls to improve UX. The OptimistInviter.claimInvite
* frontend will be making multicalls to improve UX. The OptimistInviter.claimInvite /// and Optimist.mint will be batched
* and Optimist.mint will be batched
*/
function test_multicall_batchingClaimAndMint_succeeds() external { function test_multicall_batchingClaimAndMint_succeeds() external {
uint256 inviterPrivateKey = 0xbeefbeef; uint256 inviterPrivateKey = 0xbeefbeef;
address inviter = vm.addr(inviterPrivateKey); address inviter = vm.addr(inviterPrivateKey);
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol";
import { OptimistAllowlist } from "../periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "../periphery/op-nft/OptimistAllowlist.sol";
...@@ -106,10 +106,7 @@ contract OptimistAllowlist_Initializer is Test { ...@@ -106,10 +106,7 @@ contract OptimistAllowlist_Initializer is Test {
optimistInviter.claimInvite(claimer, claimableInvite, signature); optimistInviter.claimInvite(claimer, claimableInvite, signature);
} }
/** /// @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
* @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -149,42 +146,32 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -149,42 +146,32 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertEq(optimistAllowlist.version(), "1.0.0"); assertEq(optimistAllowlist.version(), "1.0.0");
} }
/** /// @notice Base case, a account without any relevant attestations should not be able to mint.
* @notice Base case, a account without any relevant attestations should not be able to mint.
*/
function test_isAllowedToMint_withoutAnyAttestations_fails() external { function test_isAllowedToMint_withoutAnyAttestations_fails() external {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice After receiving a valid allowlist attestation, the account should be able to mint.
* @notice After receiving a valid allowlist attestation, the account should be able to mint.
*/
function test_isAllowedToMint_fromAllowlistAttestor_succeeds() external { function test_isAllowedToMint_fromAllowlistAttestor_succeeds() external {
attestAllowlist(bob); attestAllowlist(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice After receiving a valid attestation from the Coinbase Quest attestor,
* @notice After receiving a valid attestation from the Coinbase Quest attestor, /// the account should be able to mint.
* the account should be able to mint.
*/
function test_isAllowedToMint_fromCoinbaseQuestAttestor_succeeds() external { function test_isAllowedToMint_fromCoinbaseQuestAttestor_succeeds() external {
attestCoinbaseQuest(bob); attestCoinbaseQuest(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Account that received an attestation from the OptimistInviter contract by going
* @notice Account that received an attestation from the OptimistInviter contract by going /// through the claim invite flow should be able to mint.
* through the claim invite flow should be able to mint.
*/
function test_isAllowedToMint_fromInvite_succeeds() external { function test_isAllowedToMint_fromInvite_succeeds() external {
inviteAndClaim(bob); inviteAndClaim(bob);
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Attestation from the wrong allowlist attestor should not allow minting.
* @notice Attestation from the wrong allowlist attestor should not allow minting.
*/
function test_isAllowedToMint_fromWrongAllowlistAttestor_fails() external { function test_isAllowedToMint_fromWrongAllowlistAttestor_fails() external {
// Ted is not the allowlist attestor // Ted is not the allowlist attestor
vm.prank(ted); vm.prank(ted);
...@@ -196,9 +183,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -196,9 +183,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Coinbase quest attestation from wrong attestor should not allow minting.
* @notice Coinbase quest attestation from wrong attestor should not allow minting.
*/
function test_isAllowedToMint_fromWrongCoinbaseQuestAttestor_fails() external { function test_isAllowedToMint_fromWrongCoinbaseQuestAttestor_fails() external {
// Ted is not the coinbase quest attestor // Ted is not the coinbase quest attestor
vm.prank(ted); vm.prank(ted);
...@@ -210,10 +195,8 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -210,10 +195,8 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Claiming an invite on the non-official OptimistInviter contract should not allow
* @notice Claiming an invite on the non-official OptimistInviter contract should not allow /// minting.
* minting.
*/
function test_isAllowedToMint_fromWrongOptimistInviter_fails() external { function test_isAllowedToMint_fromWrongOptimistInviter_fails() external {
vm.prank(ted); vm.prank(ted);
attestationStation.attest( attestationStation.attest(
...@@ -224,9 +207,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -224,9 +207,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having multiple signals, even if one is invalid, should still allow minting.
* @notice Having multiple signals, even if one is invalid, should still allow minting.
*/
function test_isAllowedToMint_withMultipleAttestations_succeeds() external { function test_isAllowedToMint_withMultipleAttestations_succeeds() external {
attestAllowlist(bob); attestAllowlist(bob);
attestCoinbaseQuest(bob); attestCoinbaseQuest(bob);
...@@ -246,9 +227,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -246,9 +227,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertTrue(optimistAllowlist.isAllowedToMint(bob)); assertTrue(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having falsy attestation value should not allow minting.
* @notice Having falsy attestation value should not allow minting.
*/
function test_isAllowedToMint_fromAllowlistAttestorWithFalsyValue_fails() external { function test_isAllowedToMint_fromAllowlistAttestorWithFalsyValue_fails() external {
// First sends correct attestation // First sends correct attestation
attestAllowlist(bob); attestAllowlist(bob);
...@@ -264,9 +243,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer { ...@@ -264,9 +243,7 @@ contract OptimistAllowlistTest is OptimistAllowlist_Initializer {
assertFalse(optimistAllowlist.isAllowedToMint(bob)); assertFalse(optimistAllowlist.isAllowedToMint(bob));
} }
/** /// @notice Having falsy attestation value from Coinbase attestor should not allow minting.
* @notice Having falsy attestation value from Coinbase attestor should not allow minting.
*/
function test_isAllowedToMint_fromCoinbaseQuestAttestorWithFalsyValue_fails() external { function test_isAllowedToMint_fromCoinbaseQuestAttestorWithFalsyValue_fails() external {
// First sends correct attestation // First sends correct attestation
attestAllowlist(bob); attestAllowlist(bob);
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "../periphery/op-nft/AttestationStation.sol";
import { OptimistInviter } from "../periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "../periphery/op-nft/OptimistInviter.sol";
...@@ -69,9 +69,7 @@ contract OptimistInviter_Initializer is Test { ...@@ -69,9 +69,7 @@ contract OptimistInviter_Initializer is Test {
_initializeContracts(); _initializeContracts();
} }
/** /// @notice Instantiates an AttestationStation, and an OptimistInviter.
* @notice Instantiates an AttestationStation, and an OptimistInviter.
*/
function _initializeContracts() internal { function _initializeContracts() internal {
attestationStation = new AttestationStation(); attestationStation = new AttestationStation();
...@@ -88,16 +86,12 @@ contract OptimistInviter_Initializer is Test { ...@@ -88,16 +86,12 @@ contract OptimistInviter_Initializer is Test {
vm.warp(optimistInviter.MIN_COMMITMENT_PERIOD() + block.timestamp); vm.warp(optimistInviter.MIN_COMMITMENT_PERIOD() + block.timestamp);
} }
/** /// @notice Returns a user's current invite count, as stored in the AttestationStation.
* @notice Returns a user's current invite count, as stored in the AttestationStation.
*/
function _getInviteCount(address _issuer) internal view returns (uint256) { function _getInviteCount(address _issuer) internal view returns (uint256) {
return optimistInviter.inviteCounts(_issuer); return optimistInviter.inviteCounts(_issuer);
} }
/** /// @notice Returns true if claimer has the proper attestation from OptimistInviter to mint.
* @notice Returns true if claimer has the proper attestation from OptimistInviter to mint.
*/
function _hasMintAttestation(address _claimer) internal view returns (bool) { function _hasMintAttestation(address _claimer) internal view returns (bool) {
bytes memory attestation = attestationStation.attestations( bytes memory attestation = attestationStation.attestations(
address(optimistInviter), address(optimistInviter),
...@@ -107,10 +101,7 @@ contract OptimistInviter_Initializer is Test { ...@@ -107,10 +101,7 @@ contract OptimistInviter_Initializer is Test {
return attestation.length > 0; return attestation.length > 0;
} }
/** /// @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
* @notice Get signature as a bytes blob, since SignatureChecker takes arbitrary signature blobs.
*
*/
function _getSignature(uint256 _signingPrivateKey, bytes32 _digest) function _getSignature(uint256 _signingPrivateKey, bytes32 _digest)
internal internal
pure pure
...@@ -122,10 +113,8 @@ contract OptimistInviter_Initializer is Test { ...@@ -122,10 +113,8 @@ contract OptimistInviter_Initializer is Test {
return signature; return signature;
} }
/** /// @notice Signs a claimable invite with the given private key and returns the signature using
* @notice Signs a claimable invite with the given private key and returns the signature using /// correct EIP712 domain separator.
* correct EIP712 domain separator.
*/
function _issueInviteAs(uint256 _privateKey) function _issueInviteAs(uint256 _privateKey)
internal internal
returns (OptimistInviter.ClaimableInvite memory, bytes memory) returns (OptimistInviter.ClaimableInvite memory, bytes memory)
...@@ -140,11 +129,9 @@ contract OptimistInviter_Initializer is Test { ...@@ -140,11 +129,9 @@ contract OptimistInviter_Initializer is Test {
); );
} }
/** /// @notice Signs a claimable invite with the given private key and returns the signature using
* @notice Signs a claimable invite with the given private key and returns the signature using /// the given EIP712 domain separator. This assumes that the issuer's address is the
* the given EIP712 domain separator. This assumes that the issuer's address is the /// corresponding public key to _issuerPrivateKey.
* corresponding public key to _issuerPrivateKey.
*/
function _issueInviteWithEIP712Domain( function _issueInviteWithEIP712Domain(
uint256 _issuerPrivateKey, uint256 _issuerPrivateKey,
bytes memory _eip712Name, bytes memory _eip712Name,
...@@ -170,9 +157,7 @@ contract OptimistInviter_Initializer is Test { ...@@ -170,9 +157,7 @@ contract OptimistInviter_Initializer is Test {
); );
} }
/** /// @notice Commits a signature and claimer address to the OptimistInviter contract.
* @notice Commits a signature and claimer address to the OptimistInviter contract.
*/
function _commitInviteAs(address _as, bytes memory _signature) internal { function _commitInviteAs(address _as, bytes memory _signature) internal {
vm.prank(_as); vm.prank(_as);
bytes32 hashedSignature = keccak256(abi.encode(_as, _signature)); bytes32 hashedSignature = keccak256(abi.encode(_as, _signature));
...@@ -182,11 +167,9 @@ contract OptimistInviter_Initializer is Test { ...@@ -182,11 +167,9 @@ contract OptimistInviter_Initializer is Test {
assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp); assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp);
} }
/** /// @notice Signs a claimable invite with the given private key. The claimer commits then claims
* @notice Signs a claimable invite with the given private key. The claimer commits then claims /// the invite. Checks that all expected events are emitted and that state is updated
* the invite. Checks that all expected events are emitted and that state is updated /// correctly. Returns the signature and invite for use in tests.
* correctly. Returns the signature and invite for use in tests.
*/
function _issueThenClaimShouldSucceed(uint256 _issuerPrivateKey, address _claimer) function _issueThenClaimShouldSucceed(uint256 _issuerPrivateKey, address _claimer)
internal internal
returns (OptimistInviter.ClaimableInvite memory, bytes memory) returns (OptimistInviter.ClaimableInvite memory, bytes memory)
...@@ -236,10 +219,8 @@ contract OptimistInviter_Initializer is Test { ...@@ -236,10 +219,8 @@ contract OptimistInviter_Initializer is Test {
return (claimableInvite, signature); return (claimableInvite, signature);
} }
/** /// @notice Issues 3 invites to the given address. Checks that all expected events are emitted
* @notice Issues 3 invites to the given address. Checks that all expected events are emitted /// and that state is updated correctly.
* and that state is updated correctly.
*/
function _grantInvitesTo(address _to) internal { function _grantInvitesTo(address _to) internal {
address[] memory addresses = new address[](1); address[] memory addresses = new address[](1);
addresses[0] = _to; addresses[0] = _to;
...@@ -267,11 +248,9 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -267,11 +248,9 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
assertEq(optimistInviter.version(), "1.0.0"); assertEq(optimistInviter.version(), "1.0.0");
} }
/** /// @notice Alice the admin should be able to give Bob, Sally, and Carol 3 invites, and the
* @notice Alice the admin should be able to give Bob, Sally, and Carol 3 invites, and the /// OptimistInviter contract should increment invite counts on inviteCounts and issue
* OptimistInviter contract should increment invite counts on inviteCounts and issue /// 'optimist.can-invite' attestations.
* 'optimist.can-invite' attestations.
*/
function test_grantInvites_adminAddingInvites_succeeds() external { function test_grantInvites_adminAddingInvites_succeeds() external {
address[] memory addresses = new address[](3); address[] memory addresses = new address[](3);
addresses[0] = bob; addresses[0] = bob;
...@@ -310,9 +289,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -310,9 +289,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
assertEq(_getInviteCount(address(carolERC1271Wallet)), 3); assertEq(_getInviteCount(address(carolERC1271Wallet)), 3);
} }
/** /// @notice Bob, who is not the invite granter, should not be able to issue invites.
* @notice Bob, who is not the invite granter, should not be able to issue invites.
*/
function test_grantInvites_nonAdminAddingInvites_reverts() external { function test_grantInvites_nonAdminAddingInvites_reverts() external {
address[] memory addresses = new address[](2); address[] memory addresses = new address[](2);
addresses[0] = bob; addresses[0] = bob;
...@@ -323,9 +300,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -323,9 +300,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.setInviteCounts(addresses, 3); optimistInviter.setInviteCounts(addresses, 3);
} }
/** /// @notice Sally should be able to commit an invite given by by Bob.
* @notice Sally should be able to commit an invite given by by Bob.
*/
function test_commitInvite_committingForYourself_succeeds() external { function test_commitInvite_committingForYourself_succeeds() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
(, bytes memory signature) = _issueInviteAs(bobPrivateKey); (, bytes memory signature) = _issueInviteAs(bobPrivateKey);
...@@ -337,9 +312,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -337,9 +312,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp); assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp);
} }
/** /// @notice Sally should be able to Bob's for a different claimer, Eve.
* @notice Sally should be able to Bob's for a different claimer, Eve.
*/
function test_commitInvite_committingForSomeoneElse_succeeds() external { function test_commitInvite_committingForSomeoneElse_succeeds() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
(, bytes memory signature) = _issueInviteAs(bobPrivateKey); (, bytes memory signature) = _issueInviteAs(bobPrivateKey);
...@@ -351,9 +324,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -351,9 +324,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp); assertEq(optimistInviter.commitmentTimestamps(hashedSignature), block.timestamp);
} }
/** /// @notice Attempting to commit the same hash twice should revert. This prevents griefing.
* @notice Attempting to commit the same hash twice should revert. This prevents griefing.
*/
function test_commitInvite_committingSameHashTwice_reverts() external { function test_commitInvite_committingSameHashTwice_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
(, bytes memory signature) = _issueInviteAs(bobPrivateKey); (, bytes memory signature) = _issueInviteAs(bobPrivateKey);
...@@ -368,18 +339,14 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -368,18 +339,14 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.commitInvite(hashedSignature); optimistInviter.commitInvite(hashedSignature);
} }
/** /// @notice Bob issues signature, and Sally claims the invite. Bob's invite count should be
* @notice Bob issues signature, and Sally claims the invite. Bob's invite count should be /// decremented, and Sally should be able to mint.
* decremented, and Sally should be able to mint.
*/
function test_claimInvite_succeeds() external { function test_claimInvite_succeeds() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
_issueThenClaimShouldSucceed(bobPrivateKey, sally); _issueThenClaimShouldSucceed(bobPrivateKey, sally);
} }
/** /// @notice Bob issues signature, and Ted commits the invite for Sally. Eve claims for Sally.
* @notice Bob issues signature, and Ted commits the invite for Sally. Eve claims for Sally.
*/
function test_claimInvite_claimForSomeoneElse_succeeds() external { function test_claimInvite_claimForSomeoneElse_succeeds() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
( (
...@@ -428,9 +395,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -428,9 +395,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Signature issued for previous versions of the contract should fail.
* @notice Signature issued for previous versions of the contract should fail.
*/
function test_claimInvite_usingSignatureIssuedForDifferentVersion_reverts() external { function test_claimInvite_usingSignatureIssuedForDifferentVersion_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
( (
...@@ -452,10 +417,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -452,10 +417,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Replay attack for signature issued for contract on different chain (ie. mainnet)
* @notice Replay attack for signature issued for contract on different chain (ie. mainnet) /// should fail.
* should fail.
*/
function test_claimInvite_usingSignatureIssuedForDifferentChain_reverts() external { function test_claimInvite_usingSignatureIssuedForDifferentChain_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
( (
...@@ -477,10 +440,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -477,10 +440,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Replay attack for signature issued for instantiation of the OptimistInviter contract
* @notice Replay attack for signature issued for instantiation of the OptimistInviter contract /// on a different address should fail.
* on a different address should fail.
*/
function test_claimInvite_usingSignatureIssuedForDifferentContract_reverts() external { function test_claimInvite_usingSignatureIssuedForDifferentContract_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
( (
...@@ -502,9 +463,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -502,9 +463,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Attempting to claim again using the same signature again should fail.
* @notice Attempting to claim again using the same signature again should fail.
*/
function test_claimInvite_replayingUsedNonce_reverts() external { function test_claimInvite_replayingUsedNonce_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
...@@ -527,11 +486,9 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -527,11 +486,9 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(carol, claimableInvite, signature); optimistInviter.claimInvite(carol, claimableInvite, signature);
} }
/** /// @notice Issuing signatures through a contract that implements ERC1271 should succeed (ie.
* @notice Issuing signatures through a contract that implements ERC1271 should succeed (ie. /// Gnosis Safe or other smart contract wallets). Carol is using a ERC1271 contract
* Gnosis Safe or other smart contract wallets). Carol is using a ERC1271 contract /// wallet that is simply backed by her private key.
* wallet that is simply backed by her private key.
*/
function test_claimInvite_usingERC1271Wallet_succeeds() external { function test_claimInvite_usingERC1271Wallet_succeeds() external {
_grantInvitesTo(address(carolERC1271Wallet)); _grantInvitesTo(address(carolERC1271Wallet));
...@@ -560,10 +517,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -560,10 +517,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
assertEq(_getInviteCount(address(carolERC1271Wallet)), 2); assertEq(_getInviteCount(address(carolERC1271Wallet)), 2);
} }
/** /// @notice Claimer must commit the signature before claiming the invite. Sally attempts to
* @notice Claimer must commit the signature before claiming the invite. Sally attempts to /// claim the Bob's invite without committing the signature first.
* claim the Bob's invite without committing the signature first.
*/
function test_claimInvite_withoutCommittingHash_reverts() external { function test_claimInvite_withoutCommittingHash_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
( (
...@@ -576,9 +531,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -576,9 +531,7 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Using a signature that doesn't correspond to the claimable invite should fail.
* @notice Using a signature that doesn't correspond to the claimable invite should fail.
*/
function test_claimInvite_withIncorrectSignature_reverts() external { function test_claimInvite_withIncorrectSignature_reverts() external {
_grantInvitesTo(carol); _grantInvitesTo(carol);
_grantInvitesTo(bob); _grantInvitesTo(bob);
...@@ -598,10 +551,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -598,10 +551,8 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, bobClaimableInvite, carolSignature); optimistInviter.claimInvite(sally, bobClaimableInvite, carolSignature);
} }
/** /// @notice Attempting to use a signature from a issuer who never was granted invites should
* @notice Attempting to use a signature from a issuer who never was granted invites should /// fail.
* fail.
*/
function test_claimInvite_whenIssuerNeverReceivedInvites_reverts() external { function test_claimInvite_whenIssuerNeverReceivedInvites_reverts() external {
// Bob was never granted any invites, but issues an invite for Eve // Bob was never granted any invites, but issues an invite for Eve
( (
...@@ -617,13 +568,10 @@ contract OptimistInviterTest is OptimistInviter_Initializer { ...@@ -617,13 +568,10 @@ contract OptimistInviterTest is OptimistInviter_Initializer {
optimistInviter.claimInvite(sally, claimableInvite, signature); optimistInviter.claimInvite(sally, claimableInvite, signature);
} }
/** /// @notice Attempting to use a signature from a issuer who has no more invites should fail.
* @notice Attempting to use a signature from a issuer who has no more invites should fail. /// Bob has 3 invites, but issues 4 invites for Sally, Carol, Ted, and Eve. Only the
* Bob has 3 invites, but issues 4 invites for Sally, Carol, Ted, and Eve. Only the /// first 3 invites should be claimable. The last claimer, Eve, should not be able to
* first 3 invites should be claimable. The last claimer, Eve, should not be able to /// claim the invite.
* claim the invite.
*
*/
function test_claimInvite_whenIssuerHasNoInvitesLeft_reverts() external { function test_claimInvite_whenIssuerHasNoInvitesLeft_reverts() external {
_grantInvitesTo(bob); _grantInvitesTo(bob);
......
...@@ -3,10 +3,8 @@ pragma solidity ^0.8.0; ...@@ -3,10 +3,8 @@ pragma solidity ^0.8.0;
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol";
/** /// @title LibRLP
* @title LibRLP /// @notice Via https://github.com/Rari-Capital/solmate/issues/207.
* @notice Via https://github.com/Rari-Capital/solmate/issues/207.
*/
library LibRLP { library LibRLP {
using Bytes32AddressLib for bytes32; using Bytes32AddressLib for bytes32;
......
//SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
/* Testing utilities */ // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CallRecorder } from "./Helpers.sol"; import { CallRecorder } from "./Helpers.sol";
import { Reverter } from "./Helpers.sol"; import { Reverter } from "./Helpers.sol";
...@@ -34,12 +34,12 @@ contract Transactor_Initializer is Test { ...@@ -34,12 +34,12 @@ contract Transactor_Initializer is Test {
} }
contract TransactorTest is Transactor_Initializer { contract TransactorTest is Transactor_Initializer {
// Tests if the owner was set correctly during deploy /// @notice Tests if the owner was set correctly during deploy
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(address(alice), transactor.owner()); assertEq(address(alice), transactor.owner());
} }
// Tests CALL, should do a call to target /// @notice Tests CALL, should do a call to target
function test_call_succeeds() external { function test_call_succeeds() external {
// Initialize call data // Initialize call data
bytes memory data = abi.encodeWithSelector(callRecorded.record.selector); bytes memory data = abi.encodeWithSelector(callRecorded.record.selector);
...@@ -49,7 +49,7 @@ contract TransactorTest is Transactor_Initializer { ...@@ -49,7 +49,7 @@ contract TransactorTest is Transactor_Initializer {
transactor.CALL(address(callRecorded), data, 200_000 wei); transactor.CALL(address(callRecorded), data, 200_000 wei);
} }
// It should revert if called by non-owner /// @notice It should revert if called by non-owner
function test_call_unauthorized_reverts() external { function test_call_unauthorized_reverts() external {
// Initialize call data // Initialize call data
bytes memory data = abi.encodeWithSelector(callRecorded.record.selector); bytes memory data = abi.encodeWithSelector(callRecorded.record.selector);
...@@ -59,6 +59,7 @@ contract TransactorTest is Transactor_Initializer { ...@@ -59,6 +59,7 @@ contract TransactorTest is Transactor_Initializer {
transactor.CALL(address(callRecorded), data, 200_000 wei); transactor.CALL(address(callRecorded), data, 200_000 wei);
} }
/// @notice Deletate call succeeds.
function test_delegateCall_succeeds() external { function test_delegateCall_succeeds() external {
// Initialize call data // Initialize call data
bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector); bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector);
...@@ -68,7 +69,7 @@ contract TransactorTest is Transactor_Initializer { ...@@ -68,7 +69,7 @@ contract TransactorTest is Transactor_Initializer {
transactor.DELEGATECALL(address(reverter), data); transactor.DELEGATECALL(address(reverter), data);
} }
// It should revert if called by non-owner /// @notice It should revert if called by non-owner
function test_delegateCall_unauthorized_reverts() external { function test_delegateCall_unauthorized_reverts() external {
// Initialize call data // Initialize call data
bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector); bytes memory data = abi.encodeWithSelector(reverter.doRevert.selector);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -7,9 +8,7 @@ import { AddressAliasHelper } from "../../vendor/AddressAliasHelper.sol"; ...@@ -7,9 +8,7 @@ import { AddressAliasHelper } from "../../vendor/AddressAliasHelper.sol";
contract AddressAliasHelper_Converter { contract AddressAliasHelper_Converter {
bool public failedRoundtrip; bool public failedRoundtrip;
/** /// @dev Allows the actor to convert L1 to L2 addresses and vice versa.
* @dev Allows the actor to convert L1 to L2 addresses and vice versa.
*/
function convertRoundTrip(address addr) external { function convertRoundTrip(address addr) external {
// Alias our address // Alias our address
address aliasedAddr = AddressAliasHelper.applyL1ToL2Alias(addr); address aliasedAddr = AddressAliasHelper.applyL1ToL2Alias(addr);
...@@ -39,12 +38,11 @@ contract AddressAliasHelper_AddressAliasing_Invariant is StdInvariant, Test { ...@@ -39,12 +38,11 @@ contract AddressAliasHelper_AddressAliasing_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant Address aliases are always able to be undone.
* @custom:invariant Address aliases are always able to be undone. ///
* /// Asserts that an address that has been aliased with
* Asserts that an address that has been aliased with `applyL1ToL2Alias` can always /// `applyL1ToL2Alias` can always be unaliased with
* be unaliased with `undoL1ToL2Alias`. /// `undoL1ToL2Alias`.
*/
function invariant_round_trip_aliasing() external { function invariant_round_trip_aliasing() external {
// ASSERTION: The round trip aliasing done in testRoundTrip(...) should never fail. // ASSERTION: The round trip aliasing done in testRoundTrip(...) should never fail.
assertEq(actor.failedRoundtrip(), false); assertEq(actor.failedRoundtrip(), false);
......
...@@ -16,10 +16,8 @@ contract Burn_EthBurner is StdUtils { ...@@ -16,10 +16,8 @@ contract Burn_EthBurner is StdUtils {
vm = _vm; vm = _vm;
} }
/** /// @notice Takes an integer amount of eth to burn through the Burn library and
* @notice Takes an integer amount of eth to burn through the Burn library and /// updates the contract state if an incorrect amount of eth moved from the contract
* updates the contract state if an incorrect amount of eth moved from the contract
*/
function burnEth(uint256 _value) external { function burnEth(uint256 _value) external {
uint256 preBurnvalue = bound(_value, 0, type(uint128).max); uint256 preBurnvalue = bound(_value, 0, type(uint128).max);
...@@ -59,12 +57,10 @@ contract Burn_BurnEth_Invariant is StdInvariant, Test { ...@@ -59,12 +57,10 @@ contract Burn_BurnEth_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant `eth(uint256)` always burns the exact amount of eth passed.
* @custom:invariant `eth(uint256)` always burns the exact amount of eth passed. ///
* /// Asserts that when `Burn.eth(uint256)` is called, it always
* Asserts that when `Burn.eth(uint256)` is called, it always burns the exact amount /// burns the exact amount of ETH passed to the function.
* of ETH passed to the function.
*/
function invariant_burn_eth() external { function invariant_burn_eth() external {
// ASSERTION: The amount burned should always match the amount passed exactly // ASSERTION: The amount burned should always match the amount passed exactly
assertEq(actor.failedEthBurn(), false); assertEq(actor.failedEthBurn(), false);
......
...@@ -16,11 +16,9 @@ contract Burn_GasBurner is StdUtils { ...@@ -16,11 +16,9 @@ contract Burn_GasBurner is StdUtils {
vm = _vm; vm = _vm;
} }
/** /// @notice Takes an integer amount of gas to burn through the Burn library and
* @notice Takes an integer amount of gas to burn through the Burn library and /// updates the contract state if at least that amount of gas was not burned
* updates the contract state if at least that amount of gas was not burned /// by the library
* by the library
*/
function burnGas(uint256 _value) external { function burnGas(uint256 _value) external {
// cap the value to the max resource limit // cap the value to the max resource limit
uint256 MAX_RESOURCE_LIMIT = 8_000_000; uint256 MAX_RESOURCE_LIMIT = 8_000_000;
...@@ -59,12 +57,10 @@ contract Burn_BurnGas_Invariant is StdInvariant, Test { ...@@ -59,12 +57,10 @@ contract Burn_BurnGas_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant `gas(uint256)` always burns at least the amount of gas passed.
* @custom:invariant `gas(uint256)` always burns at least the amount of gas passed. ///
* /// Asserts that when `Burn.gas(uint256)` is called, it always burns
* Asserts that when `Burn.gas(uint256)` is called, it always burns at least the amount /// at least the amount of gas passed to the function.
* of gas passed to the function.
*/
function invariant_burn_gas() external { function invariant_burn_gas() external {
// ASSERTION: The amount burned should always match the amount passed exactly // ASSERTION: The amount burned should always match the amount passed exactly
assertEq(actor.failedGasBurn(), false); assertEq(actor.failedGasBurn(), false);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { StdUtils } from "forge-std/StdUtils.sol"; import { StdUtils } from "forge-std/StdUtils.sol";
...@@ -36,9 +37,8 @@ contract RelayActor is StdUtils { ...@@ -36,9 +37,8 @@ contract RelayActor is StdUtils {
doFail = _doFail; doFail = _doFail;
} }
/** /// @notice Relays a message to the `L1CrossDomainMessenger` with a random `version`,
* Relays a message to the `L1CrossDomainMessenger` with a random `version`, and `_message`. /// and `_message`.
*/
function relay( function relay(
uint8 _version, uint8 _version,
uint8 _value, uint8 _value,
...@@ -143,21 +143,19 @@ contract XDM_MinGasLimits_Succeeds is XDM_MinGasLimits { ...@@ -143,21 +143,19 @@ contract XDM_MinGasLimits_Succeeds is XDM_MinGasLimits {
super.init(false); super.init(false);
} }
/** /// @custom:invariant A call to `relayMessage` should succeed if at least the minimum gas limit
* @custom:invariant A call to `relayMessage` should succeed if at least the minimum gas limit /// can be supplied to the target context, there is enough gas to complete
* can be supplied to the target context, there is enough gas to complete /// execution of `relayMessage` after the target context's execution is
* execution of `relayMessage` after the target context's execution is /// finished, and the target context did not revert.
* finished, and the target context did not revert. ///
* /// There are two minimum gas limits here:
* There are two minimum gas limits here: ///
* /// - The outer min gas limit is for the call from the `OptimismPortal` to the
* - The outer min gas limit is for the call from the `OptimismPortal` to the /// `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's
* `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function /// `baseGas` function with the `message` and inner limit.
* with the `message` and inner limit. ///
* /// - The inner min gas limit is for the call from the
* - The inner min gas limit is for the call from the `L1CrossDomainMessenger` to the target /// `L1CrossDomainMessenger` to the target contract.
* contract.
*/
function invariant_minGasLimits() external { function invariant_minGasLimits() external {
uint256 length = actor.numHashes(); uint256 length = actor.numHashes();
for (uint256 i = 0; i < length; ++i) { for (uint256 i = 0; i < length; ++i) {
...@@ -177,22 +175,20 @@ contract XDM_MinGasLimits_Reverts is XDM_MinGasLimits { ...@@ -177,22 +175,20 @@ contract XDM_MinGasLimits_Reverts is XDM_MinGasLimits {
super.init(true); super.init(true);
} }
/** /// @custom:invariant A call to `relayMessage` should assign the message hash to the
* @custom:invariant A call to `relayMessage` should assign the message hash to the /// `failedMessages` mapping if not enough gas is supplied to forward
* `failedMessages` mapping if not enough gas is supplied to forward /// `minGasLimit` to the target context or if there is not enough gas to
* `minGasLimit` to the target context or if there is not enough gas to /// complete execution of `relayMessage` after the target context's execution
* complete execution of `relayMessage` after the target context's execution /// is finished.
* is finished. ///
* /// There are two minimum gas limits here:
* There are two minimum gas limits here: ///
* /// - The outer min gas limit is for the call from the `OptimismPortal` to the
* - The outer min gas limit is for the call from the `OptimismPortal` to the /// `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's
* `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function /// `baseGas` function with the `message` and inner limit.
* with the `message` and inner limit. ///
* /// - The inner min gas limit is for the call from the
* - The inner min gas limit is for the call from the `L1CrossDomainMessenger` to the target /// `L1CrossDomainMessenger` to the target contract.
* contract.
*/
function invariant_minGasLimits() external { function invariant_minGasLimits() external {
uint256 length = actor.numHashes(); uint256 length = actor.numHashes();
for (uint256 i = 0; i < length; ++i) { for (uint256 i = 0; i < length; ++i) {
......
...@@ -9,11 +9,9 @@ contract Encoding_Converter { ...@@ -9,11 +9,9 @@ contract Encoding_Converter {
bool public failedRoundtripAToB; bool public failedRoundtripAToB;
bool public failedRoundtripBToA; bool public failedRoundtripBToA;
/** /// @notice Takes a pair of integers to be encoded into a versioned nonce with the
* @notice Takes a pair of integers to be encoded into a versioned nonce with the /// Encoding library and then decoded and updates the test contract's state
* Encoding library and then decoded and updates the test contract's state /// indicating if the round trip encoding failed.
* indicating if the round trip encoding failed.
*/
function convertRoundTripAToB(uint240 _nonce, uint16 _version) external { function convertRoundTripAToB(uint240 _nonce, uint16 _version) external {
// Encode the nonce and version // Encode the nonce and version
uint256 encodedVersionedNonce = Encoding.encodeVersionedNonce(_nonce, _version); uint256 encodedVersionedNonce = Encoding.encodeVersionedNonce(_nonce, _version);
...@@ -30,11 +28,9 @@ contract Encoding_Converter { ...@@ -30,11 +28,9 @@ contract Encoding_Converter {
} }
} }
/** /// @notice Takes an integer representing a packed version and nonce and attempts
* @notice Takes an integer representing a packed version and nonce and attempts /// to decode them using the Encoding library before re-encoding and updates
* to decode them using the Encoding library before re-encoding and updates /// the test contract's state indicating if the round trip encoding failed.
* the test contract's state indicating if the round trip encoding failed.
*/
function convertRoundTripBToA(uint256 _versionedNonce) external { function convertRoundTripBToA(uint256 _versionedNonce) external {
// Decode the nonce and version // Decode the nonce and version
uint240 decodedNonce; uint240 decodedNonce;
...@@ -68,22 +64,19 @@ contract Encoding_Invariant is StdInvariant, Test { ...@@ -68,22 +64,19 @@ contract Encoding_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant `convertRoundTripAToB` never fails.
* @custom:invariant `convertRoundTripAToB` never fails. ///
* /// Asserts that a raw versioned nonce can be encoded / decoded
* Asserts that a raw versioned nonce can be encoded / decoded to reach the same raw value. /// to reach the same raw value.
*/
function invariant_round_trip_encoding_AToB() external { function invariant_round_trip_encoding_AToB() external {
// ASSERTION: The round trip encoding done in testRoundTripAToB(...) // ASSERTION: The round trip encoding done in testRoundTripAToB(...)
assertEq(actor.failedRoundtripAToB(), false); assertEq(actor.failedRoundtripAToB(), false);
} }
/** /// @custom:invariant `convertRoundTripBToA` never fails.
* @custom:invariant `convertRoundTripBToA` never fails. ///
* /// Asserts that an encoded versioned nonce can always be decoded /
* Asserts that an encoded versioned nonce can always be decoded / re-encoded to reach /// re-encoded to reach the same encoded value.
* the same encoded value.
*/
function invariant_round_trip_encoding_BToA() external { function invariant_round_trip_encoding_BToA() external {
// ASSERTION: The round trip encoding done in testRoundTripBToA should never // ASSERTION: The round trip encoding done in testRoundTripBToA should never
// fail. // fail.
......
...@@ -11,10 +11,9 @@ contract Hash_CrossDomainHasher { ...@@ -11,10 +11,9 @@ contract Hash_CrossDomainHasher {
bool public failedCrossDomainHashV0; bool public failedCrossDomainHashV0;
bool public failedCrossDomainHashV1; bool public failedCrossDomainHashV1;
/** /// @notice Takes the necessary parameters to perform a cross domain hash with a randomly
* @notice Takes the necessary parameters to perform a cross domain hash with a randomly /// generated version. Only schema versions 0 and 1 are supported and all others
* generated version. Only schema versions 0 and 1 are supported and all others should revert. /// should revert.
*/
function hashCrossDomainMessageHighVersion( function hashCrossDomainMessageHighVersion(
uint16 _version, uint16 _version,
uint240 _nonce, uint240 _nonce,
...@@ -37,10 +36,9 @@ contract Hash_CrossDomainHasher { ...@@ -37,10 +36,9 @@ contract Hash_CrossDomainHasher {
} }
} }
/** /// @notice Takes the necessary parameters to perform a cross domain hash using the v0 schema
* @notice Takes the necessary parameters to perform a cross domain hash using the v0 schema /// and compares the output of a call to the unversioned function to the v0 function
* and compares the output of a call to the unversioned function to the v0 function directly /// directly.
*/
function hashCrossDomainMessageV0( function hashCrossDomainMessageV0(
uint240 _nonce, uint240 _nonce,
address _sender, address _sender,
...@@ -75,10 +73,9 @@ contract Hash_CrossDomainHasher { ...@@ -75,10 +73,9 @@ contract Hash_CrossDomainHasher {
} }
} }
/** /// @notice Takes the necessary parameters to perform a cross domain hash using the v1 schema
* @notice Takes the necessary parameters to perform a cross domain hash using the v1 schema /// and compares the output of a call to the unversioned function to the v1 function
* and compares the output of a call to the unversioned function to the v1 function directly /// directly.
*/
function hashCrossDomainMessageV1( function hashCrossDomainMessageV1(
uint240 _nonce, uint240 _nonce,
address _sender, address _sender,
...@@ -133,36 +130,31 @@ contract Hashing_Invariant is StdInvariant, Test { ...@@ -133,36 +130,31 @@ contract Hashing_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant `hashCrossDomainMessage` reverts if `version` is > `1`.
* @custom:invariant `hashCrossDomainMessage` reverts if `version` is > `1`. ///
* /// The `hashCrossDomainMessage` function should always revert if
* The `hashCrossDomainMessage` function should always revert if the `version` passed is > `1`. /// the `version` passed is > `1`.
*/
function invariant_hash_xdomain_msg_high_version() external { function invariant_hash_xdomain_msg_high_version() external {
// ASSERTION: The round trip aliasing done in testRoundTrip(...) should never fail. // ASSERTION: The round trip aliasing done in testRoundTrip(...) should never fail.
assertFalse(actor.failedCrossDomainHashHighVersion()); assertFalse(actor.failedCrossDomainHashHighVersion());
} }
/** /// @custom:invariant `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0`
* @custom:invariant `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0` /// are equivalent.
* are equivalent. ///
* /// If the version passed is 0, `hashCrossDomainMessage` and
* If the version passed is 0, `hashCrossDomainMessage` and `hashCrossDomainMessageV0` should be /// `hashCrossDomainMessageV0` should be equivalent.
* equivalent.
*/
function invariant_hash_xdomain_msg_0() external { function invariant_hash_xdomain_msg_0() external {
// ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV0 // ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV0
// should always match when the version passed is 0 // should always match when the version passed is 0
assertFalse(actor.failedCrossDomainHashV0()); assertFalse(actor.failedCrossDomainHashV0());
} }
/** /// @custom:invariant `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1`
* @custom:invariant `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1` /// are equivalent.
* are equivalent. ///
* /// If the version passed is 1, `hashCrossDomainMessage` and
* If the version passed is 1, `hashCrossDomainMessage` and `hashCrossDomainMessageV1` should be /// `hashCrossDomainMessageV1` should be equivalent.
* equivalent.
*/
function invariant_hash_xdomain_msg_1() external { function invariant_hash_xdomain_msg_1() external {
// ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV1 // ASSERTION: A call to hashCrossDomainMessage and hashCrossDomainMessageV1
// should always match when the version passed is 1 // should always match when the version passed is 1
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { L2OutputOracle_Initializer } from "../CommonTest.t.sol"; import { L2OutputOracle_Initializer } from "../CommonTest.t.sol";
...@@ -13,9 +14,7 @@ contract L2OutputOracle_Proposer { ...@@ -13,9 +14,7 @@ contract L2OutputOracle_Proposer {
vm = _vm; vm = _vm;
} }
/** /// @dev Allows the actor to propose an L2 output to the `L2OutputOracle`
* @dev Allows the actor to propose an L2 output to the `L2OutputOracle`
*/
function proposeL2Output( function proposeL2Output(
bytes32 _outputRoot, bytes32 _outputRoot,
uint256 _l2BlockNumber, uint256 _l2BlockNumber,
...@@ -49,13 +48,11 @@ contract L2OutputOracle_MonotonicBlockNumIncrease_Invariant is L2OutputOracle_In ...@@ -49,13 +48,11 @@ contract L2OutputOracle_MonotonicBlockNumIncrease_Invariant is L2OutputOracle_In
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant The block number of the output root proposals should monotonically
* @custom:invariant The block number of the output root proposals should monotonically /// increase.
* increase. ///
* /// When a new output is submitted, it should never be allowed to
* When a new output is submitted, it should never be allowed to correspond to a block /// correspond to a block number that is less than the current output.
* number that is less than the current output.
*/
function invariant_monotonicBlockNumIncrease() external { function invariant_monotonicBlockNumIncrease() external {
// Assert that the block number of proposals must monotonically increase. // Assert that the block number of proposals must monotonically increase.
assertTrue(oracle.nextBlockNumber() >= oracle.latestBlockNumber()); assertTrue(oracle.nextBlockNumber() >= oracle.latestBlockNumber());
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { StdUtils } from "forge-std/Test.sol"; import { StdUtils } from "forge-std/Test.sol";
...@@ -148,13 +149,11 @@ contract OptimismPortal_Deposit_Invariant is Portal_Initializer { ...@@ -148,13 +149,11 @@ contract OptimismPortal_Deposit_Invariant is Portal_Initializer {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant Deposits of any value should always succeed unless
* @custom:invariant Deposits of any value should always succeed unless /// `_to` = `address(0)` or `_isCreation` = `true`.
* `_to` = `address(0)` or `_isCreation` = `true`. ///
* /// All deposits, barring creation transactions and transactions
* All deposits, barring creation transactions and transactions sent to `address(0)`, /// sent to `address(0)`, should always succeed.
* should always succeed.
*/
function invariant_deposit_completes() external { function invariant_deposit_completes() external {
assertEq(actor.failedToComplete(), false); assertEq(actor.failedToComplete(), false);
} }
...@@ -178,13 +177,11 @@ contract OptimismPortal_CannotTimeTravel is OptimismPortal_Invariant_Harness { ...@@ -178,13 +177,11 @@ contract OptimismPortal_CannotTimeTravel is OptimismPortal_Invariant_Harness {
excludeSender(address(multisig)); excludeSender(address(multisig));
} }
/** /// @custom:invariant `finalizeWithdrawalTransaction` should revert if the finalization
* @custom:invariant `finalizeWithdrawalTransaction` should revert if the finalization /// period has not elapsed.
* period has not elapsed. ///
* /// A withdrawal that has been proven should not be able to be finalized
* A withdrawal that has been proven should not be able to be finalized until after /// until after the finalization period has elapsed.
* the finalization period has elapsed.
*/
function invariant_cannotFinalizeBeforePeriodHasPassed() external { function invariant_cannotFinalizeBeforePeriodHasPassed() external {
vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed"); vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed");
op.finalizeWithdrawalTransaction(_defaultTx); op.finalizeWithdrawalTransaction(_defaultTx);
...@@ -215,13 +212,11 @@ contract OptimismPortal_CannotFinalizeTwice is OptimismPortal_Invariant_Harness ...@@ -215,13 +212,11 @@ contract OptimismPortal_CannotFinalizeTwice is OptimismPortal_Invariant_Harness
excludeSender(address(multisig)); excludeSender(address(multisig));
} }
/** /// @custom:invariant `finalizeWithdrawalTransaction` should revert if the withdrawal
* @custom:invariant `finalizeWithdrawalTransaction` should revert if the withdrawal /// has already been finalized.
* has already been finalized. ///
* /// Ensures that there is no chain of calls that can be made that
* Ensures that there is no chain of calls that can be made that allows a withdrawal /// allows a withdrawal to be finalized twice.
* to be finalized twice.
*/
function invariant_cannotFinalizeTwice() external { function invariant_cannotFinalizeTwice() external {
vm.expectRevert("OptimismPortal: withdrawal has already been finalized"); vm.expectRevert("OptimismPortal: withdrawal has already been finalized");
op.finalizeWithdrawalTransaction(_defaultTx); op.finalizeWithdrawalTransaction(_defaultTx);
...@@ -249,14 +244,13 @@ contract OptimismPortal_CanAlwaysFinalizeAfterWindow is OptimismPortal_Invariant ...@@ -249,14 +244,13 @@ contract OptimismPortal_CanAlwaysFinalizeAfterWindow is OptimismPortal_Invariant
excludeSender(address(multisig)); excludeSender(address(multisig));
} }
/** /// @custom:invariant A withdrawal should **always** be able to be finalized
* @custom:invariant A withdrawal should **always** be able to be finalized /// `FINALIZATION_PERIOD_SECONDS` after it was successfully proven.
* `FINALIZATION_PERIOD_SECONDS` after it was successfully proven. ///
* /// This invariant asserts that there is no chain of calls that can
* This invariant asserts that there is no chain of calls that can be made that /// be made that will prevent a withdrawal from being finalized
* will prevent a withdrawal from being finalized exactly `FINALIZATION_PERIOD_SECONDS` /// exactly `FINALIZATION_PERIOD_SECONDS` after it was successfully
* after it was successfully proven. /// proven.
*/
function invariant_canAlwaysFinalize() external { function invariant_canAlwaysFinalize() external {
uint256 bobBalanceBefore = address(bob).balance; uint256 bobBalanceBefore = address(bob).balance;
......
...@@ -45,10 +45,8 @@ contract ResourceMetering_User is StdUtils, ResourceMetering { ...@@ -45,10 +45,8 @@ contract ResourceMetering_User is StdUtils, ResourceMetering {
return rcfg; return rcfg;
} }
/** /// @notice Takes the necessary parameters to allow us to burn arbitrary amounts of gas to test
* @notice Takes the necessary parameters to allow us to burn arbitrary amounts of gas to test /// the underlying resource metering/gas market logic
* the underlying resource metering/gas market logic
*/
function burn(uint256 _gasToBurn, bool _raiseBaseFee) public { function burn(uint256 _gasToBurn, bool _raiseBaseFee) public {
// Part 1: we cache the current param values and do some basic checks on them. // Part 1: we cache the current param values and do some basic checks on them.
uint256 cachedPrevBaseFee = uint256(params.prevBaseFee); uint256 cachedPrevBaseFee = uint256(params.prevBaseFee);
...@@ -169,78 +167,67 @@ contract ResourceMetering_Invariant is StdInvariant, Test { ...@@ -169,78 +167,67 @@ contract ResourceMetering_Invariant is StdInvariant, Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant The base fee should increase if the last block used more
* @custom:invariant The base fee should increase if the last block used more /// than the target amount of gas.
* than the target amount of gas ///
* /// If the last block used more than the target amount of gas
* If the last block used more than the target amount of gas (and there were no /// (and there were no empty blocks in between), ensure this
* empty blocks in between), ensure this block's baseFee increased, but not by /// block's baseFee increased, but not by more than the max amount
* more than the max amount per block. /// per block.
*/
function invariant_high_usage_raise_baseFee() external { function invariant_high_usage_raise_baseFee() external {
assertFalse(actor.failedRaiseBaseFee()); assertFalse(actor.failedRaiseBaseFee());
} }
/** /// @custom:invariant The base fee should decrease if the last block used less
* @custom:invariant The base fee should decrease if the last block used less /// than the target amount of gas.
* than the target amount of gas ///
* /// If the previous block used less than the target amount of gas,
* If the previous block used less than the target amount of gas, the base fee should decrease, /// the base fee should decrease, but not more than the max amount.
* but not more than the max amount.
*/
function invariant_low_usage_lower_baseFee() external { function invariant_low_usage_lower_baseFee() external {
assertFalse(actor.failedLowerBaseFee()); assertFalse(actor.failedLowerBaseFee());
} }
/** /// @custom:invariant A block's base fee should never be below `MINIMUM_BASE_FEE`.
* @custom:invariant A block's base fee should never be below `MINIMUM_BASE_FEE` ///
* /// This test asserts that a block's base fee can never drop
* This test asserts that a block's base fee can never drop below the /// below the `MINIMUM_BASE_FEE` threshold.
* `MINIMUM_BASE_FEE` threshold.
*/
function invariant_never_below_min_baseFee() external { function invariant_never_below_min_baseFee() external {
assertFalse(actor.failedNeverBelowMinBaseFee()); assertFalse(actor.failedNeverBelowMinBaseFee());
} }
/** /// @custom:invariant A block can never consume more than `MAX_RESOURCE_LIMIT` gas.
* @custom:invariant A block can never consume more than `MAX_RESOURCE_LIMIT` gas. ///
* /// This test asserts that a block can never consume more than
* This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT` /// the `MAX_RESOURCE_LIMIT` gas threshold.
* gas threshold.
*/
function invariant_never_above_max_gas_limit() external { function invariant_never_above_max_gas_limit() external {
assertFalse(actor.failedMaxGasPerBlock()); assertFalse(actor.failedMaxGasPerBlock());
} }
/** /// @custom:invariant The base fee can never be raised more than the max base fee change.
* @custom:invariant The base fee can never be raised more than the max base fee change. ///
* /// After a block consumes more gas than the target gas, the base fee
* After a block consumes more gas than the target gas, the base fee cannot be raised /// cannot be raised more than the maximum amount allowed. The max base
* more than the maximum amount allowed. The max base fee change (per-block) is derived /// fee change (per-block) is derived as follows:
* as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR` /// `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
*/
function invariant_never_exceed_max_increase() external { function invariant_never_exceed_max_increase() external {
assertFalse(actor.failedMaxRaiseBaseFeePerBlock()); assertFalse(actor.failedMaxRaiseBaseFeePerBlock());
} }
/** /// @custom:invariant The base fee can never be lowered more than the max base fee change.
* @custom:invariant The base fee can never be lowered more than the max base fee change. ///
* /// After a block consumes less than the target gas, the base fee cannot
* After a block consumes less than the target gas, the base fee cannot be lowered more /// be lowered more than the maximum amount allowed. The max base fee
* than the maximum amount allowed. The max base fee change (per-block) is derived as /// change (per-block) is derived as follows:
*follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR` /// `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
*/
function invariant_never_exceed_max_decrease() external { function invariant_never_exceed_max_decrease() external {
assertFalse(actor.failedMaxLowerBaseFeePerBlock()); assertFalse(actor.failedMaxLowerBaseFeePerBlock());
} }
/** /// @custom:invariant The `maxBaseFeeChange` calculation over multiple blocks can never
* @custom:invariant The `maxBaseFeeChange` calculation over multiple blocks can never /// underflow.
* underflow. ///
* /// When calculating the `maxBaseFeeChange` after multiple empty blocks,
* When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation /// the calculation should never be allowed to underflow.
* should never be allowed to underflow.
*/
function invariant_never_underflow() external { function invariant_never_underflow() external {
assertFalse(actor.underflow()); assertFalse(actor.underflow());
} }
......
...@@ -23,13 +23,11 @@ contract SafeCall_Succeeds_Invariants is Test { ...@@ -23,13 +23,11 @@ contract SafeCall_Succeeds_Invariants is Test {
vm.deal(address(actor), type(uint128).max); vm.deal(address(actor), type(uint128).max);
} }
/** /// @custom:invariant If `callWithMinGas` performs a call, then it must always
* @custom:invariant If `callWithMinGas` performs a call, then it must always /// provide at least the specified minimum gas limit to the subcontext.
* provide at least the specified minimum gas limit to the subcontext. ///
* /// If the check for remaining gas in `SafeCall.callWithMinGas` passes, the
* If the check for remaining gas in `SafeCall.callWithMinGas` passes, the /// subcontext of the call below it must be provided at least `minGas` gas.
* subcontext of the call below it must be provided at least `minGas` gas.
*/
function invariant_callWithMinGas_alwaysForwardsMinGas_succeeds() public { function invariant_callWithMinGas_alwaysForwardsMinGas_succeeds() public {
assertEq(actor.numCalls(), 0, "no failed calls allowed"); assertEq(actor.numCalls(), 0, "no failed calls allowed");
} }
...@@ -56,14 +54,12 @@ contract SafeCall_Fails_Invariants is Test { ...@@ -56,14 +54,12 @@ contract SafeCall_Fails_Invariants is Test {
vm.deal(address(actor), type(uint128).max); vm.deal(address(actor), type(uint128).max);
} }
/** /// @custom:invariant `callWithMinGas` reverts if there is not enough gas to pass
* @custom:invariant `callWithMinGas` reverts if there is not enough gas to pass /// to the subcontext.
* to the subcontext. ///
* /// If there is not enough gas in the callframe to ensure that
* If there is not enough gas in the callframe to ensure that `callWithMinGas` /// `callWithMinGas` can provide the specified minimum gas limit
* can provide the specified minimum gas limit to the subcontext of the call, /// to the subcontext of the call, then `callWithMinGas` must revert.
* then `callWithMinGas` must revert.
*/
function invariant_callWithMinGas_neverForwardsMinGas_reverts() public { function invariant_callWithMinGas_neverForwardsMinGas_reverts() public {
assertEq(actor.numCalls(), 0, "no successful calls allowed"); assertEq(actor.numCalls(), 0, "no successful calls allowed");
} }
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
...@@ -37,10 +38,8 @@ contract SystemConfig_GasLimitLowerBound_Invariant is Test { ...@@ -37,10 +38,8 @@ contract SystemConfig_GasLimitLowerBound_Invariant is Test {
targetSelector(selector); targetSelector(selector);
} }
/** /// @custom:invariant The gas limit of the `SystemConfig` contract can never be lower
* @custom:invariant The gas limit of the `SystemConfig` contract can never be lower /// than the hard-coded lower bound.
* than the hard-coded lower bound.
*/
function invariant_gasLimitLowerBound() external { function invariant_gasLimitLowerBound() external {
assertTrue(config.gasLimit() >= config.minimumGasLimit()); assertTrue(config.gasLimit() >= config.minimumGasLimit());
} }
......
# `AddressAliasHelper` Invariants # `AddressAliasHelper` Invariants
## Address aliases are always able to be undone. ## Address aliases are always able to be undone.
**Test:** [`AddressAliasHelper.t.sol#L48`](../contracts/test/invariants/AddressAliasHelper.t.sol#L48) **Test:** [`AddressAliasHelper.t.sol#L46`](../contracts/test/invariants/AddressAliasHelper.t.sol#L46)
Asserts that an address that has been aliased with `applyL1ToL2Alias` can always be unaliased with `undoL1ToL2Alias`. Asserts that an address that has been aliased with `applyL1ToL2Alias` can always be unaliased with `undoL1ToL2Alias`.
\ No newline at end of file
# `Burn.Eth` Invariants # `Burn.Eth` Invariants
## `eth(uint256)` always burns the exact amount of eth passed. ## `eth(uint256)` always burns the exact amount of eth passed.
**Test:** [`Burn.Eth.t.sol#L68`](../contracts/test/invariants/Burn.Eth.t.sol#L68) **Test:** [`Burn.Eth.t.sol#L64`](../contracts/test/invariants/Burn.Eth.t.sol#L64)
Asserts that when `Burn.eth(uint256)` is called, it always burns the exact amount of ETH passed to the function. Asserts that when `Burn.eth(uint256)` is called, it always burns the exact amount of ETH passed to the function.
\ No newline at end of file
# `Burn.Gas` Invariants # `Burn.Gas` Invariants
## `gas(uint256)` always burns at least the amount of gas passed. ## `gas(uint256)` always burns at least the amount of gas passed.
**Test:** [`Burn.Gas.t.sol#L68`](../contracts/test/invariants/Burn.Gas.t.sol#L68) **Test:** [`Burn.Gas.t.sol#L64`](../contracts/test/invariants/Burn.Gas.t.sol#L64)
Asserts that when `Burn.gas(uint256)` is called, it always burns at least the amount of gas passed to the function. Asserts that when `Burn.gas(uint256)` is called, it always burns at least the amount of gas passed to the function.
\ No newline at end of file
# `CrossDomainMessenger` Invariants # `CrossDomainMessenger` Invariants
## A call to `relayMessage` should succeed if at least the minimum gas limit can be supplied to the target context, there is enough gas to complete execution of `relayMessage` after the target context's execution is finished, and the target context did not revert. ## A call to `relayMessage` should succeed if at least the minimum gas limit can be supplied to the target context, there is enough gas to complete execution of `relayMessage` after the target context's execution is finished, and the target context did not revert.
**Test:** [`CrossDomainMessenger.t.sol#L161`](../contracts/test/invariants/CrossDomainMessenger.t.sol#L161) **Test:** [`CrossDomainMessenger.t.sol#L159`](../contracts/test/invariants/CrossDomainMessenger.t.sol#L159)
There are two minimum gas limits here: There are two minimum gas limits here:
- The outer min gas limit is for the call from the `OptimismPortal` to the `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function with the `message` and inner limit. - The outer min gas limit is for the call from the `OptimismPortal` to the `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function with the `message` and inner limit.
- The inner min gas limit is for the call from the `L1CrossDomainMessenger` to the target contract. - The inner min gas limit is for the call from the `L1CrossDomainMessenger` to the target contract.
## A call to `relayMessage` should assign the message hash to the `failedMessages` mapping if not enough gas is supplied to forward `minGasLimit` to the target context or if there is not enough gas to complete execution of `relayMessage` after the target context's execution is finished. ## A call to `relayMessage` should assign the message hash to the `failedMessages` mapping if not enough gas is supplied to forward `minGasLimit` to the target context or if there is not enough gas to complete execution of `relayMessage` after the target context's execution is finished.
**Test:** [`CrossDomainMessenger.t.sol#L196`](../contracts/test/invariants/CrossDomainMessenger.t.sol#L196) **Test:** [`CrossDomainMessenger.t.sol#L192`](../contracts/test/invariants/CrossDomainMessenger.t.sol#L192)
There are two minimum gas limits here: There are two minimum gas limits here:
- The outer min gas limit is for the call from the `OptimismPortal` to the `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function with the `message` and inner limit. - The outer min gas limit is for the call from the `OptimismPortal` to the `L1CrossDomainMessenger`, and it can be retrieved by calling the xdm's `baseGas` function with the `message` and inner limit.
......
# `Encoding` Invariants # `Encoding` Invariants
## `convertRoundTripAToB` never fails. ## `convertRoundTripAToB` never fails.
**Test:** [`Encoding.t.sol#L76`](../contracts/test/invariants/Encoding.t.sol#L76) **Test:** [`Encoding.t.sol#L71`](../contracts/test/invariants/Encoding.t.sol#L71)
Asserts that a raw versioned nonce can be encoded / decoded to reach the same raw value. Asserts that a raw versioned nonce can be encoded / decoded to reach the same raw value.
## `convertRoundTripBToA` never fails. ## `convertRoundTripBToA` never fails.
**Test:** [`Encoding.t.sol#L87`](../contracts/test/invariants/Encoding.t.sol#L87) **Test:** [`Encoding.t.sol#L80`](../contracts/test/invariants/Encoding.t.sol#L80)
Asserts that an encoded versioned nonce can always be decoded / re-encoded to reach the same encoded value. Asserts that an encoded versioned nonce can always be decoded / re-encoded to reach the same encoded value.
\ No newline at end of file
# `Hashing` Invariants # `Hashing` Invariants
## `hashCrossDomainMessage` reverts if `version` is > `1`. ## `hashCrossDomainMessage` reverts if `version` is > `1`.
**Test:** [`Hashing.t.sol#L141`](../contracts/test/invariants/Hashing.t.sol#L141) **Test:** [`Hashing.t.sol#L137`](../contracts/test/invariants/Hashing.t.sol#L137)
The `hashCrossDomainMessage` function should always revert if the `version` passed is > `1`. The `hashCrossDomainMessage` function should always revert if the `version` passed is > `1`.
## `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0` are equivalent. ## `version` = `0`: `hashCrossDomainMessage` and `hashCrossDomainMessageV0` are equivalent.
**Test:** [`Hashing.t.sol#L153`](../contracts/test/invariants/Hashing.t.sol#L153) **Test:** [`Hashing.t.sol#L147`](../contracts/test/invariants/Hashing.t.sol#L147)
If the version passed is 0, `hashCrossDomainMessage` and `hashCrossDomainMessageV0` should be equivalent. If the version passed is 0, `hashCrossDomainMessage` and `hashCrossDomainMessageV0` should be equivalent.
## `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1` are equivalent. ## `version` = `1`: `hashCrossDomainMessage` and `hashCrossDomainMessageV1` are equivalent.
**Test:** [`Hashing.t.sol#L166`](../contracts/test/invariants/Hashing.t.sol#L166) **Test:** [`Hashing.t.sol#L158`](../contracts/test/invariants/Hashing.t.sol#L158)
If the version passed is 1, `hashCrossDomainMessage` and `hashCrossDomainMessageV1` should be equivalent. If the version passed is 1, `hashCrossDomainMessage` and `hashCrossDomainMessageV1` should be equivalent.
\ No newline at end of file
# `L2OutputOracle` Invariants # `L2OutputOracle` Invariants
## The block number of the output root proposals should monotonically increase. ## The block number of the output root proposals should monotonically increase.
**Test:** [`L2OutputOracle.t.sol#L59`](../contracts/test/invariants/L2OutputOracle.t.sol#L59) **Test:** [`L2OutputOracle.t.sol#L56`](../contracts/test/invariants/L2OutputOracle.t.sol#L56)
When a new output is submitted, it should never be allowed to correspond to a block number that is less than the current output. When a new output is submitted, it should never be allowed to correspond to a block number that is less than the current output.
\ No newline at end of file
# `OptimismPortal` Invariants # `OptimismPortal` Invariants
## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`. ## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`.
**Test:** [`OptimismPortal.t.sol#L158`](../contracts/test/invariants/OptimismPortal.t.sol#L158) **Test:** [`OptimismPortal.t.sol#L157`](../contracts/test/invariants/OptimismPortal.t.sol#L157)
All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed. All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed.
## `finalizeWithdrawalTransaction` should revert if the finalization period has not elapsed. ## `finalizeWithdrawalTransaction` should revert if the finalization period has not elapsed.
**Test:** [`OptimismPortal.t.sol#L188`](../contracts/test/invariants/OptimismPortal.t.sol#L188) **Test:** [`OptimismPortal.t.sol#L185`](../contracts/test/invariants/OptimismPortal.t.sol#L185)
A withdrawal that has been proven should not be able to be finalized until after the finalization period has elapsed. A withdrawal that has been proven should not be able to be finalized until after the finalization period has elapsed.
## `finalizeWithdrawalTransaction` should revert if the withdrawal has already been finalized. ## `finalizeWithdrawalTransaction` should revert if the withdrawal has already been finalized.
**Test:** [`OptimismPortal.t.sol#L225`](../contracts/test/invariants/OptimismPortal.t.sol#L225) **Test:** [`OptimismPortal.t.sol#L220`](../contracts/test/invariants/OptimismPortal.t.sol#L220)
Ensures that there is no chain of calls that can be made that allows a withdrawal to be finalized twice. Ensures that there is no chain of calls that can be made that allows a withdrawal to be finalized twice.
## A withdrawal should **always** be able to be finalized `FINALIZATION_PERIOD_SECONDS` after it was successfully proven. ## A withdrawal should **always** be able to be finalized `FINALIZATION_PERIOD_SECONDS` after it was successfully proven.
**Test:** [`OptimismPortal.t.sol#L260`](../contracts/test/invariants/OptimismPortal.t.sol#L260) **Test:** [`OptimismPortal.t.sol#L254`](../contracts/test/invariants/OptimismPortal.t.sol#L254)
This invariant asserts that there is no chain of calls that can be made that will prevent a withdrawal from being finalized exactly `FINALIZATION_PERIOD_SECONDS` after it was successfully proven. This invariant asserts that there is no chain of calls that can be made that will prevent a withdrawal from being finalized exactly `FINALIZATION_PERIOD_SECONDS` after it was successfully proven.
\ No newline at end of file
# `ResourceMetering` Invariants # `ResourceMetering` Invariants
## The base fee should increase if the last block used more than the target amount of gas ## The base fee should increase if the last block used more than the target amount of gas.
**Test:** [`ResourceMetering.t.sol#L180`](../contracts/test/invariants/ResourceMetering.t.sol#L180) **Test:** [`ResourceMetering.t.sol#L177`](../contracts/test/invariants/ResourceMetering.t.sol#L177)
If the last block used more than the target amount of gas (and there were no empty blocks in between), ensure this block's baseFee increased, but not by more than the max amount per block. If the last block used more than the target amount of gas (and there were no empty blocks in between), ensure this block's baseFee increased, but not by more than the max amount per block.
## The base fee should decrease if the last block used less than the target amount of gas.
## The base fee should decrease if the last block used less than the target amount of gas **Test:** [`ResourceMetering.t.sol#L186`](../contracts/test/invariants/ResourceMetering.t.sol#L186)
**Test:** [`ResourceMetering.t.sol#L191`](../contracts/test/invariants/ResourceMetering.t.sol#L191)
If the previous block used less than the target amount of gas, the base fee should decrease, but not more than the max amount. If the previous block used less than the target amount of gas, the base fee should decrease, but not more than the max amount.
## A block's base fee should never be below `MINIMUM_BASE_FEE`.
## A block's base fee should never be below `MINIMUM_BASE_FEE` **Test:** [`ResourceMetering.t.sol#L194`](../contracts/test/invariants/ResourceMetering.t.sol#L194)
**Test:** [`ResourceMetering.t.sol#L201`](../contracts/test/invariants/ResourceMetering.t.sol#L201)
This test asserts that a block's base fee can never drop below the `MINIMUM_BASE_FEE` threshold. This test asserts that a block's base fee can never drop below the `MINIMUM_BASE_FEE` threshold.
## A block can never consume more than `MAX_RESOURCE_LIMIT` gas. ## A block can never consume more than `MAX_RESOURCE_LIMIT` gas.
**Test:** [`ResourceMetering.t.sol#L211`](../contracts/test/invariants/ResourceMetering.t.sol#L211) **Test:** [`ResourceMetering.t.sol#L202`](../contracts/test/invariants/ResourceMetering.t.sol#L202)
This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT` gas threshold. This test asserts that a block can never consume more than the `MAX_RESOURCE_LIMIT` gas threshold.
## The base fee can never be raised more than the max base fee change. ## The base fee can never be raised more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L222`](../contracts/test/invariants/ResourceMetering.t.sol#L222) **Test:** [`ResourceMetering.t.sol#L212`](../contracts/test/invariants/ResourceMetering.t.sol#L212)
After a block consumes more gas than the target gas, the base fee cannot be raised more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR` After a block consumes more gas than the target gas, the base fee cannot be raised more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The base fee can never be lowered more than the max base fee change. ## The base fee can never be lowered more than the max base fee change.
**Test:** [`ResourceMetering.t.sol#L233`](../contracts/test/invariants/ResourceMetering.t.sol#L233) **Test:** [`ResourceMetering.t.sol#L222`](../contracts/test/invariants/ResourceMetering.t.sol#L222)
After a block consumes less than the target gas, the base fee cannot be lowered more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR` After a block consumes less than the target gas, the base fee cannot be lowered more than the maximum amount allowed. The max base fee change (per-block) is derived as follows: `prevBaseFee / BASE_FEE_MAX_CHANGE_DENOMINATOR`
## The `maxBaseFeeChange` calculation over multiple blocks can never underflow. ## The `maxBaseFeeChange` calculation over multiple blocks can never underflow.
**Test:** [`ResourceMetering.t.sol#L244`](../contracts/test/invariants/ResourceMetering.t.sol#L244) **Test:** [`ResourceMetering.t.sol#L231`](../contracts/test/invariants/ResourceMetering.t.sol#L231)
When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation should never be allowed to underflow. When calculating the `maxBaseFeeChange` after multiple empty blocks, the calculation should never be allowed to underflow.
\ No newline at end of file
# `SafeCall` Invariants # `SafeCall` Invariants
## If `callWithMinGas` performs a call, then it must always provide at least the specified minimum gas limit to the subcontext. ## If `callWithMinGas` performs a call, then it must always provide at least the specified minimum gas limit to the subcontext.
**Test:** [`SafeCall.t.sol#L33`](../contracts/test/invariants/SafeCall.t.sol#L33) **Test:** [`SafeCall.t.sol#L31`](../contracts/test/invariants/SafeCall.t.sol#L31)
If the check for remaining gas in `SafeCall.callWithMinGas` passes, the subcontext of the call below it must be provided at least `minGas` gas. If the check for remaining gas in `SafeCall.callWithMinGas` passes, the subcontext of the call below it must be provided at least `minGas` gas.
## `callWithMinGas` reverts if there is not enough gas to pass to the subcontext. ## `callWithMinGas` reverts if there is not enough gas to pass to the subcontext.
**Test:** [`SafeCall.t.sol#L67`](../contracts/test/invariants/SafeCall.t.sol#L67) **Test:** [`SafeCall.t.sol#L63`](../contracts/test/invariants/SafeCall.t.sol#L63)
If there is not enough gas in the callframe to ensure that `callWithMinGas` can provide the specified minimum gas limit to the subcontext of the call, then `callWithMinGas` must revert. If there is not enough gas in the callframe to ensure that `callWithMinGas` can provide the specified minimum gas limit to the subcontext of the call, then `callWithMinGas` must revert.
\ No newline at end of file
...@@ -11,8 +11,6 @@ const BASE_INVARIANTS_DIR = path.join( ...@@ -11,8 +11,6 @@ const BASE_INVARIANTS_DIR = path.join(
const BASE_DOCS_DIR = path.join(__dirname, '..', 'invariant-docs') const BASE_DOCS_DIR = path.join(__dirname, '..', 'invariant-docs')
const BASE_INVARIANT_GH_URL = '../contracts/test/invariants/' const BASE_INVARIANT_GH_URL = '../contracts/test/invariants/'
const NATSPEC_INV = '@custom:invariant' const NATSPEC_INV = '@custom:invariant'
const BLOCK_COMMENT_PREFIX_REGEX = /\*(\/)?/
const BLOCK_COMMENT_HEADER_REGEX = /\*\s(.)+/
// Represents an invariant test contract // Represents an invariant test contract
type Contract = { type Contract = {
...@@ -30,10 +28,8 @@ type InvariantDoc = { ...@@ -30,10 +28,8 @@ type InvariantDoc = {
const writtenFiles = [] const writtenFiles = []
/** // Lazy-parses all test files in the `contracts/test/invariants` directory
* Lazy-parses all test files in the `contracts/test/invariants` directory to generate documentation // to generate documentation on all invariant tests.
* on all invariant tests.
*/
const docGen = (dir: string): void => { const docGen = (dir: string): void => {
// Grab all files within the invariants test dir // Grab all files within the invariants test dir
const files = fs.readdirSync(dir) const files = fs.readdirSync(dir)
...@@ -58,33 +54,25 @@ const docGen = (dir: string): void => { ...@@ -58,33 +54,25 @@ const docGen = (dir: string): void => {
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
let line = lines[i] let line = lines[i]
if (line.startsWith('/**')) {
// We are at the beginning of a new doc comment. Reset the `currentDoc`.
currentDoc = {}
// Move on to the next line
line = lines[++i]
// We have an invariant doc // We have an invariant doc
if (line.startsWith(`* ${NATSPEC_INV}`)) { if (line.startsWith(`/// ${NATSPEC_INV}`)) {
// Assign the header of the invariant doc. // Assign the header of the invariant doc.
// TODO: Handle ambiguous case for `INVARIANT: ` prefix. // TODO: Handle ambiguous case for `INVARIANT: ` prefix.
// TODO: Handle multi-line headers.
currentDoc = { currentDoc = {
header: line.replace(`* ${NATSPEC_INV}`, '').trim(), header: line.replace(`/// ${NATSPEC_INV}`, '').trim(),
desc: '', desc: '',
} }
// If the header is multi-line, continue appending to the `currentDoc`'s header. // If the header is multi-line, continue appending to the `currentDoc`'s header.
while (BLOCK_COMMENT_HEADER_REGEX.test((line = lines[++i]))) { line = lines[++i]
currentDoc.header += ` ${line while (line.startsWith(`///`) && line.trim() !== '///') {
.replace(BLOCK_COMMENT_PREFIX_REGEX, '') currentDoc.header += ` ${line.replace(`///`, '').trim()}`
.trim()}` line = lines[++i]
} }
// Process the description // Process the description
while ((line = lines[++i]).startsWith('*')) { while ((line = lines[++i]).startsWith('///')) {
line = line.replace(BLOCK_COMMENT_PREFIX_REGEX, '').trim() line = line.replace('///', '').trim()
// If the line has any contents, insert it into the desc. // If the line has any contents, insert it into the desc.
// Otherwise, consider it a linebreak. // Otherwise, consider it a linebreak.
...@@ -98,7 +86,6 @@ const docGen = (dir: string): void => { ...@@ -98,7 +86,6 @@ const docGen = (dir: string): void => {
contract.docs.push(currentDoc) contract.docs.push(currentDoc)
} }
} }
}
// Add the contract to the array of docs // Add the contract to the array of docs
docs.push(contract) docs.push(contract)
...@@ -133,9 +120,7 @@ const docGen = (dir: string): void => { ...@@ -133,9 +120,7 @@ const docGen = (dir: string): void => {
) )
} }
/** // Generate a table of contents for all invariant docs and place it in the README.
* Generate a table of contents for all invariant docs and place it in the README.
*/
const tocGen = (): void => { const tocGen = (): void => {
const autoTOCPrefix = '<!-- START autoTOC -->\n' const autoTOCPrefix = '<!-- START autoTOC -->\n'
const autoTOCPostfix = '<!-- END autoTOC -->\n' const autoTOCPostfix = '<!-- END autoTOC -->\n'
...@@ -165,9 +150,7 @@ const tocGen = (): void => { ...@@ -165,9 +150,7 @@ const tocGen = (): void => {
) )
} }
/** // Render a `Contract` object into valid markdown.
* Render a `Contract` object into valid markdown.
*/
const renderContractDoc = (contract: Contract, header: boolean): string => { const renderContractDoc = (contract: Contract, header: boolean): string => {
const _header = header ? `# \`${contract.name}\` Invariants\n` : '' const _header = header ? `# \`${contract.name}\` Invariants\n` : ''
const docs = contract.docs const docs = contract.docs
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment