From 1de4f17622ad116899bb481413436377c3290b4b Mon Sep 17 00:00:00 2001 From: dd0sxx Date: Tue, 5 Dec 2023 16:59:11 -0500 Subject: [PATCH 1/2] test: Add tests for token voting action creator and caster (#16) **Motivation:** Needed to migrate the token voting tests to the periphery repo to have test coverage. **Modifications:** - added the test suite, and some interface contracts **Result:** - all of the token voting contracts (minus the factory) have coverage in the periphery repo --- src/interfaces/ILlamaCore.sol | 34 ++ src/interfaces/ILlamaLens.sol | 42 ++ src/interfaces/ILlamaRelativeStrategyBase.sol | 14 + .../ERC721TokenholderActionCreator.sol | 1 - src/token-voting/TokenholderActionCreator.sol | 3 +- test/PeripheryTestSetup.sol | 55 ++ test/mock/MockERC20Votes.sol | 36 ++ test/mock/MockERC721Votes.sol | 33 ++ .../ERC20TokenholderActionCreator.t.sol | 280 +++++++++ .../token-voting/ERC20TokenholderCaster.t.sol | 542 ++++++++++++++++++ .../ERC721TokenholderActionCreator.t.sol | 285 +++++++++ .../ERC721TokenholderCaster.t.sol | 542 ++++++++++++++++++ .../LlamaTokenVotingFactory.t.sol | 368 ++++++++++++ 13 files changed, 2232 insertions(+), 3 deletions(-) create mode 100644 src/interfaces/ILlamaLens.sol create mode 100644 test/PeripheryTestSetup.sol create mode 100644 test/mock/MockERC20Votes.sol create mode 100644 test/mock/MockERC721Votes.sol create mode 100644 test/token-voting/ERC20TokenholderActionCreator.t.sol create mode 100644 test/token-voting/ERC20TokenholderCaster.t.sol create mode 100644 test/token-voting/ERC721TokenholderActionCreator.t.sol create mode 100644 test/token-voting/ERC721TokenholderCaster.t.sol create mode 100644 test/token-voting/LlamaTokenVotingFactory.t.sol diff --git a/src/interfaces/ILlamaCore.sol b/src/interfaces/ILlamaCore.sol index 5aeb62f..d0eba15 100644 --- a/src/interfaces/ILlamaCore.sol +++ b/src/interfaces/ILlamaCore.sol @@ -20,16 +20,27 @@ import { /// @author Llama (devsdosomething@llama.xyz) /// @notice This is the interface for LlamaCore. interface ILlamaCore { + error PolicyholderDoesNotHavePermission(); + function actionGuard(address target, bytes4 selector) external view returns (address guard); + function actionsCount() external view returns (uint256); + function approvals(uint256 actionId, address policyholder) external view returns (bool hasApproved); + function authorizedAccountLogics(address accountLogic) external view returns (bool isAuthorized); + function authorizedScripts(address script) external view returns (bool isAuthorized); + function authorizedStrategyLogics(ILlamaStrategy strategyLogic) external view returns (bool isAuthorized); + function cancelAction(ActionInfo memory actionInfo) external; + function cancelActionBySig(address policyholder, ActionInfo memory actionInfo, uint8 v, bytes32 r, bytes32 s) external; + function castApproval(uint8 role, ActionInfo memory actionInfo, string memory reason) external returns (uint96); + function castApprovalBySig( address policyholder, uint8 role, @@ -39,7 +50,9 @@ interface ILlamaCore { bytes32 r, bytes32 s ) external returns (uint96); + function castDisapproval(uint8 role, ActionInfo memory actionInfo, string memory reason) external returns (uint96); + function castDisapprovalBySig( address policyholder, uint8 role, @@ -49,7 +62,9 @@ interface ILlamaCore { bytes32 r, bytes32 s ) external returns (uint96); + function createAccounts(address llamaAccountLogic, bytes[] memory accountConfigs) external; + function createAction( uint8 role, ILlamaStrategy strategy, @@ -58,6 +73,7 @@ interface ILlamaCore { bytes memory data, string memory description ) external returns (uint256 actionId); + function createActionBySig( address policyholder, uint8 role, @@ -70,22 +86,40 @@ interface ILlamaCore { bytes32 r, bytes32 s ) external returns (uint256 actionId); + function createStrategies(address llamaStrategyLogic, bytes[] memory strategyConfigs) external; + function disapprovals(uint256 actionId, address policyholder) external view returns (bool hasDisapproved); + function executeAction(ActionInfo memory actionInfo) external payable; + function executor() external view returns (address); + function getAction(uint256 actionId) external view returns (Action memory); + function getActionState(ActionInfo memory actionInfo) external view returns (uint8); + function incrementNonce(bytes4 selector) external; + function initialize(LlamaInstanceConfig memory config, address policyLogic, address policyMetadataLogic) external; + function name() external view returns (string memory); + function nonces(address policyholder, bytes4 selector) external view returns (uint256 currentNonce); + function policy() external view returns (ILlamaPolicy); + function queueAction(ActionInfo memory actionInfo) external; + function setAccountLogicAuthorization(address accountLogic, bool authorized) external; + function setGuard(address target, bytes4 selector, address guard) external; + function setScriptAuthorization(address script, bool authorized) external; + function setStrategyAuthorization(ILlamaStrategy strategy, bool authorized) external; + function setStrategyLogicAuthorization(ILlamaStrategy strategyLogic, bool authorized) external; + function strategies(ILlamaStrategy strategy) external view returns (bool deployed, bool authorized); } diff --git a/src/interfaces/ILlamaLens.sol b/src/interfaces/ILlamaLens.sol new file mode 100644 index 0000000..0ad9dc6 --- /dev/null +++ b/src/interfaces/ILlamaLens.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.23; + +interface ILlamaLens { + struct PermissionData { + address target; + bytes4 selector; + address strategy; + } + + function LLAMA_CORE_LOGIC() external view returns (address); + + function LLAMA_FACTORY() external view returns (address); + + function LLAMA_POLICY_LOGIC() external view returns (address); + + function computeLlamaAccountAddress(address llamaAccountLogic, bytes memory accountConfig, address llamaCore) + external + pure + returns (address); + + function computeLlamaCoreAddress(string memory name, address deployer) external view returns (address); + + function computeLlamaExecutorAddress(address llamaCore) external pure returns (address); + + function computeLlamaExecutorAddress(string memory name, address deployer) external view returns (address); + + function computeLlamaPolicyAddress(string memory name, address deployer) external view returns (address); + + function computeLlamaPolicyMetadataAddress( + address llamaPolicyMetadataLogic, + bytes memory metadataConfig, + address llamaPolicy + ) external pure returns (address); + + function computeLlamaStrategyAddress(address llamaStrategyLogic, bytes memory strategyConfig, address llamaCore) + external + pure + returns (address); + + function computePermissionId(PermissionData memory permission) external pure returns (bytes32); +} diff --git a/src/interfaces/ILlamaRelativeStrategyBase.sol b/src/interfaces/ILlamaRelativeStrategyBase.sol index 5d5b41d..6e1fe97 100644 --- a/src/interfaces/ILlamaRelativeStrategyBase.sol +++ b/src/interfaces/ILlamaRelativeStrategyBase.sol @@ -19,6 +19,20 @@ interface ILlamaRelativeStrategyBase { bytes data; } + struct Config { + uint64 approvalPeriod; // The length of time of the approval period. + uint64 queuingPeriod; // The length of time of the queuing period. The disapproval period is the queuing period when + // enabled. + uint64 expirationPeriod; // The length of time an action can be executed before it expires. + uint16 minApprovalPct; // Minimum percentage of total approval quantity / total approval supply. + uint16 minDisapprovalPct; // Minimum percentage of total disapproval quantity / total disapproval supply. + bool isFixedLengthApprovalPeriod; // Determines if an action be queued before approvalEndTime. + uint8 approvalRole; // Anyone with this role can cast approval of an action. + uint8 disapprovalRole; // Anyone with this role can cast disapproval of an action. + uint8[] forceApprovalRoles; // Anyone with this role can single-handedly approve an action. + uint8[] forceDisapprovalRoles; // Anyone with this role can single-handedly disapprove an action. + } + error CannotCancelInState(ActionState currentState); error DisapprovalDisabled(); error InvalidActionInfo(); diff --git a/src/token-voting/ERC721TokenholderActionCreator.sol b/src/token-voting/ERC721TokenholderActionCreator.sol index 0836ad8..4287696 100644 --- a/src/token-voting/ERC721TokenholderActionCreator.sol +++ b/src/token-voting/ERC721TokenholderActionCreator.sol @@ -18,7 +18,6 @@ contract ERC721TokenholderActionCreator is TokenholderActionCreator { { TOKEN = token; if (!TOKEN.supportsInterface(type(IERC721).interfaceId)) revert InvalidTokenAddress(); - uint256 totalSupply = TOKEN.getPastTotalSupply(block.timestamp - 1); if (totalSupply == 0) revert InvalidTokenAddress(); if (_creationThreshold > totalSupply) revert InvalidCreationThreshold(); diff --git a/src/token-voting/TokenholderActionCreator.sol b/src/token-voting/TokenholderActionCreator.sol index c89637a..0ca6571 100644 --- a/src/token-voting/TokenholderActionCreator.sol +++ b/src/token-voting/TokenholderActionCreator.sol @@ -215,9 +215,8 @@ abstract contract TokenholderActionCreator { uint256 balance = _getPastVotes(tokenHolder, block.timestamp - 1); if (balance < creationThreshold) revert InsufficientBalance(balance); - actionCreators[actionId] = tokenHolder; - actionId = LLAMA_CORE.createAction(role, strategy, target, value, data, description); + actionCreators[actionId] = tokenHolder; emit ActionCreated(actionId, tokenHolder, role, strategy, target, value, data, description); } diff --git a/test/PeripheryTestSetup.sol b/test/PeripheryTestSetup.sol new file mode 100644 index 0000000..fa6bdf1 --- /dev/null +++ b/test/PeripheryTestSetup.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Test, console2} from "forge-std/Test.sol"; + +import {Vm} from "forge-std/Vm.sol"; + +import {ILlamaAccount} from "src/interfaces/ILlamaAccount.sol"; +import {ILlamaPolicyMetadata} from "src/interfaces/ILlamaPolicyMetadata.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {ILlamaCore} from "src/interfaces/ILlamaCore.sol"; +import {ILlamaExecutor} from "src/interfaces/ILlamaExecutor.sol"; +import {ILlamaLens} from "src/interfaces/ILlamaLens.sol"; +import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; +import {ActionInfo, PermissionData, RoleHolderData} from "src/lib/Structs.sol"; + +contract PeripheryTestSetup is Test { + string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); // can't use constant here + + // Llama's Llama instance. + ILlamaCore constant CORE = ILlamaCore(0x688576dC6b1AbaEe1d7Ee2B7c41Ee5D81BFD293c); + ILlamaExecutor constant EXECUTOR = ILlamaExecutor(0xdAf00E9786cABB195a8a1Cf102730863aE94Dd75); + ILlamaPolicy constant POLICY = ILlamaPolicy(0x07CCDBF8bC642007D001CB1F412733A529bE3B04); + ILlamaAccount constant ACCOUNT = ILlamaAccount(0xf147B323B00bE213FBB4F5cC148FD611a02D8845); + ILlamaStrategy constant STRATEGY = ILlamaStrategy(0xeE1695FEbA09ADeC2bFb6ADd963A51cF119fF4Fb); + ILlamaLens constant LENS = ILlamaLens(0x1D74803D4939aFa3CC9fF1B8667bE4d119d925cB); + address constant RELATIVE_QUANTITY_QUORUM_LOGIC = 0x81F7D26fD7d814bFcEF78239a32c0BA5282C98Dc; + + uint8 public constant CORE_TEAM_ROLE = 1; + + // llama core team members. + address coreTeam1 = 0x2beC65F165cB63Ca2aa07CC14fE5915EAF6fc294; + address coreTeam2 = 0x475B3Ca8763e0Fa601dDC47162bD1F87dF465872; + address coreTeam3 = 0xe56f23CbD1B1071B0540E5068d699f3f071b75a4; + address coreTeam4 = 0x6b45E38c87bfCa15ee90AAe2AFe3CFC58cE08F75; + address coreTeam5 = 0xbdfcE43E5D2C7AA8599290d940c9932B8dBC94Ca; + + // Function selectors used in tests. + bytes4 public constant SET_ROLE_HOLDER_SELECTOR = 0x2524842c; // pause(bool) + + // Permission data for those selectors. + PermissionData setRoleHolderPermission = PermissionData(address(POLICY), SET_ROLE_HOLDER_SELECTOR, STRATEGY); + + // Permission IDs for the permission data. + bytes32 setRoleHolderPermissionId = keccak256(abi.encode(setRoleHolderPermission)); + + // Othes constants. + uint96 DEFAULT_ROLE_QTY = 1; + uint96 EMPTY_ROLE_QTY = 0; + uint64 DEFAULT_ROLE_EXPIRATION = type(uint64).max; + + function setUp() public virtual { + vm.createSelectFork(MAINNET_RPC_URL, 18_707_845); + } +} diff --git a/test/mock/MockERC20Votes.sol b/test/mock/MockERC20Votes.sol new file mode 100644 index 0000000..4e54696 --- /dev/null +++ b/test/mock/MockERC20Votes.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; +import {ERC20Permit} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import {ERC20Votes} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import {Time} from "lib/openzeppelin-contracts/contracts/utils/types/Time.sol"; +import {Nonces} from "lib/openzeppelin-contracts/contracts/utils/Nonces.sol"; + +contract MockERC20Votes is ERC20, ERC20Permit, ERC20Votes { + constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {} + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } + + function CLOCK_MODE() public view override returns (string memory) { + if (clock() != Time.timestamp()) revert ERC6372InconsistentClock(); + return "mode=timestamp"; + } + + function clock() public view override returns (uint48) { + return Time.timestamp(); + } + + // The following functions are overrides required by Solidity. + + function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Votes) { + super._update(from, to, value); + delegate(to); + } + + function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) { + return super.nonces(owner); + } +} diff --git a/test/mock/MockERC721Votes.sol b/test/mock/MockERC721Votes.sol new file mode 100644 index 0000000..190e895 --- /dev/null +++ b/test/mock/MockERC721Votes.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {ERC721} from "lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; +import {EIP712} from "lib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol"; +import {ERC721Votes} from "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol"; + +contract MockERC721Votes is ERC721, EIP712, ERC721Votes { + constructor() ERC721("MyToken", "MTK") EIP712("MyToken", "1") {} + + function mint(address to, uint256 tokenId) public { + _mint(to, tokenId); + } + + function CLOCK_MODE() public pure override returns (string memory) { + return "mode=timestamp"; + } + + function clock() public view override returns (uint48) { + return uint48(block.timestamp); + } + + // The following functions are overrides required by Solidity. + + function _update(address to, uint256 tokenId, address auth) internal override(ERC721, ERC721Votes) returns (address) { + delegate(to); + return super._update(to, tokenId, auth); + } + + function _increaseBalance(address account, uint128 amount) internal override(ERC721, ERC721Votes) { + super._increaseBalance(account, amount); + } +} diff --git a/test/token-voting/ERC20TokenholderActionCreator.t.sol b/test/token-voting/ERC20TokenholderActionCreator.t.sol new file mode 100644 index 0000000..4191ddc --- /dev/null +++ b/test/token-voting/ERC20TokenholderActionCreator.t.sol @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Test, console2} from "forge-std/Test.sol"; + +import {ERC20Votes} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; +import {PeripheryTestSetup} from "test/PeripheryTestSetup.sol"; + +import {Action, ActionInfo} from "src/lib/Structs.sol"; +import {RoleDescription} from "src/lib/UDVTs.sol"; +import {ILlamaCore} from "src/interfaces/ILlamaCore.sol"; +import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {ERC20TokenholderActionCreator} from "src/token-voting/ERC20TokenholderActionCreator.sol"; +import {TokenholderActionCreator} from "src/token-voting/TokenholderActionCreator.sol"; + +contract ERC20TokenholderActionCreatorTest is PeripheryTestSetup { + event ActionCreated( + uint256 id, + address indexed creator, + uint8 role, + ILlamaStrategy indexed strategy, + address indexed target, + uint256 value, + bytes data, + string description + ); + + event ActionCanceled(uint256 id, address indexed creator); + + event ActionThresholdSet(uint256 newThreshold); + + MockERC20Votes mockErc20Votes; + ERC20Votes token; + address tokenHolder1 = makeAddr("tokenHolder1"); + address tokenHolder2 = makeAddr("tokenHolder2"); + address tokenHolder3 = makeAddr("tokenHolder3"); + address notATokenHolder = makeAddr("notATokenHolder"); + + function setUp() public virtual override { + PeripheryTestSetup.setUp(); + vm.deal(address(this), 1 ether); + vm.deal(address(msg.sender), 1 ether); + + mockErc20Votes = new MockERC20Votes(); + token = ERC20Votes(address(mockErc20Votes)); + } +} + +contract Constructor is ERC20TokenholderActionCreatorTest { + function test_RevertsIf_InvalidLlamaCore() public { + // With invalid LlamaCore instance, TokenholderActionCreator.InvalidLlamaCoreAddress is unreachable + vm.expectRevert(); + new ERC20TokenholderActionCreator(token, ILlamaCore(makeAddr("invalid-llama-core")), uint256(0)); + } + + function test_RevertsIf_InvalidTokenAddress() public { + vm.expectRevert(); // will EvmError: Revert vecause totalSupply fn does not exist + new ERC20TokenholderActionCreator(ERC20Votes(makeAddr("invalid-token")), ILlamaCore(address(CORE)), uint256(0)); + } + + function test_RevertsIf_CreationThresholdExceedsTotalSupply() public { + mockErc20Votes.mint(tokenHolder1, 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + vm.expectRevert(TokenholderActionCreator.InvalidCreationThreshold.selector); + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), 17_000_000_000_000_000_000_000_000); + } + + function test_ProperlySetsConstructorArguments() public { + uint256 threshold = 500_000e18; + mockErc20Votes.mint(tokenHolder1, 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + assertEq(address(actionCreator.TOKEN()), address(token)); + assertEq(address(actionCreator.LLAMA_CORE()), address(CORE)); + assertEq(actionCreator.creationThreshold(), threshold); + } +} + +contract TokenHolderCreateAction is ERC20TokenholderActionCreatorTest { + bytes data = abi.encodeCall( + POLICY.setRoleHolder, (CORE_TEAM_ROLE, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + uint256 threshold = 500_000e18; + + function test_RevertsIf_InsufficientBalance() public { + mockErc20Votes.mint(tokenHolder1, 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.prank(notATokenHolder); + vm.expectRevert(abi.encodeWithSelector(TokenholderActionCreator.InsufficientBalance.selector, 0)); + actionCreator.createAction(CORE_TEAM_ROLE, STRATEGY, address(POLICY), 0, data, ""); + } + + function test_RevertsIf_TokenholderActionCreatorDoesNotHavePermission() public { + mockErc20Votes.mint(tokenHolder1, threshold); // we use mockErc20Votes because IVotesToken is an + // interface without the `mint` function + vm.prank(tokenHolder1); + mockErc20Votes.delegate(tokenHolder1); // we use mockErc20Votes because IVotesToken is an interface without + // the `delegate` function + + vm.warp(block.timestamp + 1); + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + token.getPastVotes(tokenHolder1, block.timestamp - 1); + + vm.expectRevert(ILlamaCore.PolicyholderDoesNotHavePermission.selector); + vm.prank(tokenHolder1); + actionCreator.createAction(CORE_TEAM_ROLE, STRATEGY, address(POLICY), 0, data, ""); + } + + function test_ProperlyCreatesAction() public { + mockErc20Votes.mint(tokenHolder1, threshold); // we use mockErc20Votes because IVotesToken is an + // interface without the `delegate` function + vm.prank(tokenHolder1); + mockErc20Votes.delegate(tokenHolder1); // we use mockErc20Votes because IVotesToken is an interface without + // the `delegate` function + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.startPrank(address(EXECUTOR)); // init role, assign policy, and assign permission to setRoleHolder to the token + // voting action creator + POLICY.initializeRole(RoleDescription.wrap("Token Voting Action Creator Role")); + uint8 actionCreatorRole = 2; + POLICY.setRoleHolder(actionCreatorRole, address(actionCreator), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION); + POLICY.setRolePermission( + actionCreatorRole, + ILlamaPolicy.PermissionData(address(POLICY), POLICY.setRoleHolder.selector, address(STRATEGY)), + true + ); + vm.stopPrank(); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + data = abi.encodeCall( + POLICY.setRoleHolder, (actionCreatorRole, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + + uint256 actionCount = CORE.actionsCount(); + + vm.expectEmit(); + emit ActionCreated(actionCount, address(tokenHolder1), actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + vm.prank(tokenHolder1); + + uint256 actionId = actionCreator.createAction(actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + Action memory action = CORE.getAction(actionId); + + assertEq(actionId, actionCount); + assertEq(action.creationTime, block.timestamp); + } +} + +contract CancelAction is ERC20TokenholderActionCreatorTest { + uint8 actionCreatorRole = 2; + bytes data = abi.encodeCall( + POLICY.setRoleHolder, (actionCreatorRole, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + uint256 threshold = 500_000e18; + uint256 actionId; + ERC20TokenholderActionCreator actionCreator; + ActionInfo actionInfo; + + function setUp() public virtual override { + ERC20TokenholderActionCreatorTest.setUp(); + mockErc20Votes.mint(tokenHolder1, threshold); // we use mockErc20Votes because IVotesToken is an + // interface without the `delegate` function + vm.prank(tokenHolder1); + mockErc20Votes.delegate(tokenHolder1); // we use mockErc20Votes because IVotesToken is an interface without + // the `delegate` function + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + actionCreator = new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.startPrank(address(EXECUTOR)); // init role, assign policy, and assign permission to setRoleHolder to the token + // voting action creator + POLICY.initializeRole(RoleDescription.wrap("Token Voting Action Creator Role")); + POLICY.setRoleHolder(actionCreatorRole, address(actionCreator), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION); + POLICY.setRolePermission( + actionCreatorRole, + ILlamaPolicy.PermissionData(address(POLICY), POLICY.setRoleHolder.selector, address(STRATEGY)), + true + ); + vm.stopPrank(); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + vm.prank(tokenHolder1); + actionId = actionCreator.createAction(actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + actionInfo = ActionInfo(actionId, address(actionCreator), actionCreatorRole, STRATEGY, address(POLICY), 0, data); + } + + function test_PassesIf_CallerIsActionCreator() public { + vm.expectEmit(); + emit ActionCanceled(actionId, tokenHolder1); + vm.prank(tokenHolder1); + actionCreator.cancelAction(actionInfo); + } + + function test_RevertsIf_CallerIsNotActionCreator(address notCreator) public { + vm.assume(notCreator != tokenHolder1); + vm.expectRevert(TokenholderActionCreator.OnlyActionCreator.selector); + vm.prank(notCreator); + actionCreator.cancelAction(actionInfo); + } +} + +contract SetActionThreshold is ERC20TokenholderActionCreatorTest { + function test_SetsCreationThreshold() public { + uint256 threshold = 500_000e18; + mockErc20Votes.mint(address(this), 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), 1_000_000e18); + + assertEq(actionCreator.creationThreshold(), 1_000_000e18); + + vm.expectEmit(); + emit ActionThresholdSet(threshold); + vm.prank(address(EXECUTOR)); + actionCreator.setActionThreshold(threshold); + } + + function test_RevertsIf_CreationThresholdExceedsTotalSupply() public { + uint256 threshold = 1_000_000e18; + mockErc20Votes.mint(address(this), 500_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), 500_000e18); + + vm.expectRevert(TokenholderActionCreator.InvalidCreationThreshold.selector); + vm.prank(address(EXECUTOR)); + actionCreator.setActionThreshold(threshold); + } + + function test_RevertsIf_CalledByNotLlamaExecutor(address notLlamaExecutor) public { + vm.assume(notLlamaExecutor != address(EXECUTOR)); + uint256 threshold = 500_000e18; + mockErc20Votes.mint(address(this), 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + ERC20TokenholderActionCreator actionCreator = + new ERC20TokenholderActionCreator(token, ILlamaCore(address(CORE)), 1_000_000e18); + + vm.expectRevert(TokenholderActionCreator.OnlyLlamaExecutor.selector); + vm.prank(notLlamaExecutor); + actionCreator.setActionThreshold(threshold); + } +} diff --git a/test/token-voting/ERC20TokenholderCaster.t.sol b/test/token-voting/ERC20TokenholderCaster.t.sol new file mode 100644 index 0000000..58dbccf --- /dev/null +++ b/test/token-voting/ERC20TokenholderCaster.t.sol @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Test, console2} from "forge-std/Test.sol"; + +import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; + +import {Action, ActionInfo, PermissionData} from "src/lib/Structs.sol"; +import {ILlamaCore} from "src/interfaces/ILlamaCore.sol"; +import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; +import {ILlamaRelativeStrategyBase} from "src/interfaces/ILlamaRelativeStrategyBase.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {RoleDescription} from "src/lib/UDVTs.sol"; +import {ERC20Votes} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import {ERC20TokenholderCaster} from "src/token-voting/ERC20TokenholderCaster.sol"; +import {TokenholderCaster} from "src/token-voting/TokenholderCaster.sol"; +import {PeripheryTestSetup} from "test/PeripheryTestSetup.sol"; + +contract ERC20TokenholderCasterTest is PeripheryTestSetup { + uint256 constant DEFAULT_APPROVAL_THRESHOLD = 1000; + uint16 constant ONE_HUNDRED_IN_BPS = 10_000; + uint256 constant ONE_THIRD_IN_BPS = 3333; + uint256 constant TWO_THIRDS_IN_BPS = 6667; + uint8 constant CASTER_ROLE = 2; + uint8 constant MADE_UP_ROLE = 3; + + MockERC20Votes mockErc20Votes; + ERC20Votes token; // this is the same token as + // mockErc20Votes, when we mint on one it will be reflected on the other + + ActionInfo actionInfo; + ERC20TokenholderCaster caster; + + ILlamaStrategy tokenVotingStrategy; + + address tokenHolder1 = makeAddr("tokenholder-1"); + address tokenHolder2 = makeAddr("tokenholder-2"); + address tokenHolder3 = makeAddr("tokenholder-3"); + + event ApprovalCast( + uint256 id, address indexed policyholder, uint8 indexed role, uint8 indexed support, uint256 quantity, string reason + ); + + event ApprovalsSubmitted(uint256 id, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain); + + event DisapprovalCast( + uint256 id, address indexed policyholder, uint8 indexed role, uint8 indexed support, uint256 quantity, string reason + ); + + event DisapprovalsSubmitted(uint256 id, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain); + + // ========================= + // ======== Helpers ======== + // ========================= + + function createAction(ILlamaStrategy strategy) public returns (ActionInfo memory _actionInfo) { + bytes memory data = abi.encodeCall(POLICY.initializeRole, (RoleDescription.wrap("Action Caaster"))); + vm.prank(coreTeam1); + uint256 actionId = CORE.createAction(CORE_TEAM_ROLE, strategy, address(POLICY), 0, data, ""); + _actionInfo = ActionInfo(actionId, coreTeam1, CORE_TEAM_ROLE, strategy, address(POLICY), 0, data); + vm.warp(block.timestamp + 1); + } + + function deployRelativeQuantityQuorumAndSetRole(address _policyHolder, uint8 role) + internal + returns (ILlamaStrategy newStrategy) + { + { + vm.prank(address(EXECUTOR)); + POLICY.setRoleHolder(role, _policyHolder, 1, type(uint64).max); + } + + uint8[] memory forceRoles = new uint8[](0); + + ILlamaRelativeStrategyBase.Config memory strategyConfig = ILlamaRelativeStrategyBase.Config({ + approvalPeriod: 1 days, + queuingPeriod: 1 days, + expirationPeriod: 1 days, + isFixedLengthApprovalPeriod: false, + minApprovalPct: ONE_HUNDRED_IN_BPS, + minDisapprovalPct: ONE_HUNDRED_IN_BPS, + approvalRole: role, + disapprovalRole: role, + forceApprovalRoles: forceRoles, + forceDisapprovalRoles: forceRoles + }); + + ILlamaRelativeStrategyBase.Config[] memory strategyConfigs = new ILlamaRelativeStrategyBase.Config[](1); + strategyConfigs[0] = strategyConfig; + + vm.prank(address(EXECUTOR)); + + CORE.createStrategies(RELATIVE_QUANTITY_QUORUM_LOGIC, encodeStrategyConfigs(strategyConfigs)); + + newStrategy = ILlamaStrategy( + LENS.computeLlamaStrategyAddress( + address(RELATIVE_QUANTITY_QUORUM_LOGIC), encodeStrategy(strategyConfig), address(CORE) + ) + ); + + { + ILlamaPolicy.PermissionData memory _permissionData = + ILlamaPolicy.PermissionData(address(POLICY), POLICY.initializeRole.selector, address(newStrategy)); + vm.prank(address(EXECUTOR)); + POLICY.setRolePermission(CORE_TEAM_ROLE, _permissionData, true); + } + } + + function setUp() public virtual override { + PeripheryTestSetup.setUp(); + vm.deal(address(this), 1 ether); + vm.deal(address(msg.sender), 1 ether); + vm.deal(address(EXECUTOR), 1 ether); + vm.deal(tokenHolder1, 1 ether); + vm.deal(tokenHolder2, 1 ether); + vm.deal(tokenHolder3, 1 ether); + + mockErc20Votes = new MockERC20Votes(); + token = ERC20Votes(address(mockErc20Votes)); + + vm.prank(address(EXECUTOR)); + POLICY.initializeRole(RoleDescription.wrap("Token Voting Caster Role")); // initializes role 2 + vm.prank(address(EXECUTOR)); + POLICY.initializeRole(RoleDescription.wrap("Made Up Role")); // initializes role 2 + + mockErc20Votes.mint(tokenHolder1, DEFAULT_APPROVAL_THRESHOLD / 2); + mockErc20Votes.mint(tokenHolder2, DEFAULT_APPROVAL_THRESHOLD / 2); + mockErc20Votes.mint(tokenHolder3, DEFAULT_APPROVAL_THRESHOLD / 2); + vm.prank(tokenHolder1); + mockErc20Votes.delegate(tokenHolder1); + vm.prank(tokenHolder2); + mockErc20Votes.delegate(tokenHolder2); + vm.prank(tokenHolder3); + mockErc20Votes.delegate(tokenHolder3); + + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + + caster = new ERC20TokenholderCaster( + token, ILlamaCore(address(CORE)), CASTER_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + + tokenVotingStrategy = deployRelativeQuantityQuorumAndSetRole(address(caster), CASTER_ROLE); + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + + actionInfo = createAction(tokenVotingStrategy); + + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + } + + function castApprovalsFor() public { + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder3); + caster.castApproval(actionInfo, 1, ""); + } + + function castDisapprovalsFor() public { + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder3); + caster.castDisapproval(actionInfo, 1, ""); + } + + function encodeStrategyConfigs(ILlamaRelativeStrategyBase.Config[] memory strategies) + internal + pure + returns (bytes[] memory encoded) + { + encoded = new bytes[](strategies.length); + for (uint256 i = 0; i < strategies.length; i++) { + encoded[i] = encodeStrategy(strategies[i]); + } + } + + function encodeStrategy(ILlamaRelativeStrategyBase.Config memory strategy) + internal + pure + returns (bytes memory encoded) + { + encoded = abi.encode(strategy); + } +} + +contract Constructor is ERC20TokenholderCasterTest { + function test_RevertsIf_InvalidLlamaCoreAddress() public { + // With invalid LlamaCore instance, TokenholderActionCreator.InvalidLlamaCoreAddress is unreachable + vm.expectRevert(); + new ERC20TokenholderCaster(token, ILlamaCore(makeAddr("invalid-llama-core")), CASTER_ROLE, uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidTokenAddress(address notAToken) public { + vm.assume(notAToken != address(0)); + vm.assume(notAToken != address(token)); + vm.expectRevert(); // will revert with EvmError: Revert because `totalSupply` is not a function + new ERC20TokenholderCaster(ERC20Votes(notAToken), ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidRole(uint8 role) public { + role = uint8(bound(role, POLICY.numRoles(), 255)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.RoleNotInitialized.selector, uint8(255))); + new ERC20TokenholderCaster(token, ILlamaCore(address(CORE)), uint8(255), uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidMinApprovalPct() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinApprovalPct.selector, uint256(0))); + new ERC20TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(0), uint256(1)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinApprovalPct.selector, uint256(10_001))); + new ERC20TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(10_001), uint256(1)); + } + + function test_RevertsIf_InvalidMinDisapprovalPct() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinDisapprovalPct.selector, uint256(0))); + new ERC20TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(0)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinDisapprovalPct.selector, uint256(10_001))); + new ERC20TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(10_001)); + } + + function test_ProperlySetsConstructorArguments() public { + mockErc20Votes.mint(address(this), 1_000_000e18); // we use mockErc20Votes because IVotesToken is an interface + // without the `mint` function + + caster = new ERC20TokenholderCaster( + token, ILlamaCore(address(CORE)), CASTER_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + + assertEq(address(caster.LLAMA_CORE()), address(CORE)); + assertEq(address(caster.TOKEN()), address(token)); + assertEq(caster.ROLE(), CASTER_ROLE); + assertEq(caster.MIN_APPROVAL_PCT(), DEFAULT_APPROVAL_THRESHOLD); + assertEq(caster.MIN_DISAPPROVAL_PCT(), DEFAULT_APPROVAL_THRESHOLD); + } +} + +contract CastApproval is ERC20TokenholderCasterTest { + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.castApproval(notActionInfo, CASTER_ROLE, ""); + } + + function test_RevertsIf_ApprovalNotEnabled() public { + TokenholderCaster casterWithWrongRole = new ERC20TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.castApproval(actionInfo, MADE_UP_ROLE, ""); + } + + function test_RevertsIf_ActionNotActive() public { + vm.warp(block.timestamp + 1 days); + vm.expectRevert(TokenholderCaster.ActionNotActive.selector); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_AlreadyCastApproval() public { + vm.startPrank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + + vm.expectRevert(TokenholderCaster.AlreadyCastApproval.selector); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InvalidSupport() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidSupport.selector, uint8(3))); + caster.castApproval(actionInfo, 3, ""); + } + + function test_RevertsIf_CastingPeriodOver() public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); // 2/3 of the approval period + vm.expectRevert(TokenholderCaster.CastingPeriodOver.selector); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InsufficientBalance() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientBalance.selector, 0)); + caster.castApproval(actionInfo, 1, ""); + } + + function test_CastsApprovalCorrectly(uint8 support) public { + support = uint8(bound(support, 0, 2)); + vm.expectEmit(); + emit ApprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, support, token.getPastVotes(tokenHolder1, block.timestamp - 1), "" + ); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, support, ""); + } + + function test_CastsApprovalCorrectly_WithReason() public { + vm.expectEmit(); + emit ApprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, 1, token.getPastVotes(tokenHolder1, token.clock() - 1), "reason" + ); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, "reason"); + } +} + +contract CastDisapproval is ERC20TokenholderCasterTest { + function setUp() public virtual override { + ERC20TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + + vm.prank(tokenHolder1); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.castDisapproval(notActionInfo, CASTER_ROLE, ""); + } + + function test_RevertsIf_DisapprovalNotEnabled() public { + TokenholderCaster casterWithWrongRole = new ERC20TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.castDisapproval(actionInfo, MADE_UP_ROLE, ""); + } + + function test_RevertsIf_AlreadyCastApproval() public { + vm.startPrank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + + vm.expectRevert(TokenholderCaster.AlreadyCastDisapproval.selector); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InvalidSupport() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidSupport.selector, uint8(3))); + caster.castDisapproval(actionInfo, 3, ""); + } + + function test_RevertsIf_CastingPeriodOver() public { + // TODO why do we need to add 2 here + vm.warp(block.timestamp + 2 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); // 2/3 of the approval period + vm.expectRevert(TokenholderCaster.CastingPeriodOver.selector); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InsufficientBalance() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientBalance.selector, 0)); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_CastsDisapprovalCorrectly(uint8 support) public { + support = uint8(bound(support, 0, 2)); + vm.expectEmit(); + emit DisapprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, support, token.getPastVotes(tokenHolder1, block.timestamp - 1), "" + ); + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, support, ""); + } + + function test_CastsDisapprovalCorrectly_WithReason() public { + vm.expectEmit(); + emit DisapprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, 1, token.getPastVotes(tokenHolder1, token.clock() - 1), "reason" + ); + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, "reason"); + } +} + +contract SubmitApprovals is ERC20TokenholderCasterTest { + function setUp() public virtual override { + ERC20TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.submitApprovals(notActionInfo); + } + + function test_RevertsIf_AlreadySubmittedApproval() public { + vm.startPrank(tokenHolder1); + caster.submitApprovals(actionInfo); + + vm.expectRevert(TokenholderCaster.AlreadySubmittedApproval.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_SubmissionPeriodOver() public { + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.SubmissionPeriodOver.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_InsufficientApprovals() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 150)); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_CastingPeriodNotOver() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.CantSubmitYet.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_InsufficientApprovalsFor() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 150)); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ForDoesNotSurpassAgainst() public { + actionInfo = createAction(tokenVotingStrategy); + + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castApproval(actionInfo, 0, ""); + vm.prank(tokenHolder3); + caster.castApproval(actionInfo, 0, ""); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.ForDoesNotSurpassAgainst.selector, 500, 1000)); + caster.submitApprovals(actionInfo); + } + + function test_SubmitsApprovalsCorrectly() public { + vm.expectEmit(); + emit ApprovalsSubmitted(actionInfo.id, 1500, 0, 0); + caster.submitApprovals(actionInfo); + } +} + +contract SubmitDisapprovals is ERC20TokenholderCasterTest { + function setUp() public virtual override { + ERC20TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.submitDisapprovals(notActionInfo); + } + + function test_RevertsIf_DisapprovalNotEnabled() public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + TokenholderCaster casterWithWrongRole = new ERC20TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_AlreadySubmittedDisapproval() public { + Action memory action = CORE.getAction(actionInfo.id); + vm.warp( + action.minExecutionTime + - (ILlamaRelativeStrategyBase(address(actionInfo.strategy)).queuingPeriod() * ONE_THIRD_IN_BPS) + / ONE_HUNDRED_IN_BPS + ); + + castDisapprovalsFor(); + + vm.startPrank(tokenHolder1); + caster.submitDisapprovals(actionInfo); + + vm.expectRevert(TokenholderCaster.AlreadySubmittedDisapproval.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_SubmissionPeriodOver() public { + castDisapprovalsFor(); + + vm.warp(block.timestamp + 1 days); + vm.expectRevert(TokenholderCaster.SubmissionPeriodOver.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_InsufficientDisapprovals() public { + actionInfo = createAction(tokenVotingStrategy); + castApprovalsFor(); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + caster.submitApprovals(actionInfo); + + //TODO why add 1 here + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 150)); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_CastingPeriodNotOver() public { + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.CantSubmitYet.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_ForDoesNotSurpassAgainst() public { + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castDisapproval(actionInfo, 0, ""); + vm.prank(tokenHolder3); + caster.castDisapproval(actionInfo, 0, ""); + // TODO why add 1 here? + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.ForDoesNotSurpassAgainst.selector, 500, 1000)); + caster.submitDisapprovals(actionInfo); + } + + function test_SubmitsDisapprovalsCorrectly() public { + castDisapprovalsFor(); + + //TODO why add 1 here? + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectEmit(); + emit DisapprovalsSubmitted(actionInfo.id, 1500, 0, 0); + caster.submitDisapprovals(actionInfo); + } +} diff --git a/test/token-voting/ERC721TokenholderActionCreator.t.sol b/test/token-voting/ERC721TokenholderActionCreator.t.sol new file mode 100644 index 0000000..0be6113 --- /dev/null +++ b/test/token-voting/ERC721TokenholderActionCreator.t.sol @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Test, console2} from "forge-std/Test.sol"; + +import {ERC721Votes} from "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol"; +import {MockERC721Votes} from "test/mock/MockERC721Votes.sol"; +import {PeripheryTestSetup} from "test/PeripheryTestSetup.sol"; + +import {Action, ActionInfo} from "src/lib/Structs.sol"; +import {RoleDescription} from "src/lib/UDVTs.sol"; +import {ILlamaCore} from "src/interfaces/ILlamaCore.sol"; +import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {ERC721TokenholderActionCreator} from "src/token-voting/ERC721TokenholderActionCreator.sol"; +import {TokenholderActionCreator} from "src/token-voting/TokenholderActionCreator.sol"; + +contract ERC721TokenholderActionCreatorTest is PeripheryTestSetup { + event ActionCreated( + uint256 id, + address indexed creator, + uint8 role, + ILlamaStrategy indexed strategy, + address indexed target, + uint256 value, + bytes data, + string description + ); + + event ActionCanceled(uint256 id, address indexed creator); + + event ActionThresholdSet(uint256 newThreshold); + + MockERC721Votes mockErc721Votes; + ERC721Votes token; + address tokenHolder1 = makeAddr("tokenHolder1"); + address tokenHolder2 = makeAddr("tokenHolder2"); + address tokenHolder3 = makeAddr("tokenHolder3"); + address notATokenHolder = makeAddr("notATokenHolder"); + + function setUp() public virtual override { + PeripheryTestSetup.setUp(); + vm.deal(address(this), 1 ether); + vm.deal(address(msg.sender), 1 ether); + + mockErc721Votes = new MockERC721Votes(); + token = ERC721Votes(address(mockErc721Votes)); + } +} + +contract Constructor is ERC721TokenholderActionCreatorTest { + function test_RevertsIf_InvalidLlamaCore() public { + // With invalid LlamaCore instance, TokenholderActionCreator.InvalidLlamaCoreAddress is unreachable + vm.expectRevert(); + new ERC721TokenholderActionCreator(token, ILlamaCore(makeAddr("invalid-llama-core")), uint256(0)); + } + + function test_RevertsIf_InvalidTokenAddress() public { + vm.expectRevert(); // will EvmError: Revert vecause totalSupply fn does not exist + new ERC721TokenholderActionCreator(ERC721Votes(makeAddr("invalid-token")), ILlamaCore(address(CORE)), uint256(0)); + } + + function test_RevertsIf_CreationThresholdExceedsTotalSupply(uint8 num) public { + vm.assume(num > 1); + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + vm.expectRevert(TokenholderActionCreator.InvalidCreationThreshold.selector); + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), num); + } + + function test_ProperlySetsConstructorArguments() public { + uint256 threshold = 1; + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + assertEq(address(actionCreator.TOKEN()), address(token)); + assertEq(address(actionCreator.LLAMA_CORE()), address(CORE)); + assertEq(actionCreator.creationThreshold(), threshold); + } +} + +contract TokenHolderCreateAction is ERC721TokenholderActionCreatorTest { + bytes data = abi.encodeCall( + POLICY.setRoleHolder, (CORE_TEAM_ROLE, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + uint256 threshold = 1; + + function test_RevertsIf_InsufficientBalance() public { + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.prank(notATokenHolder); + vm.expectRevert(abi.encodeWithSelector(TokenholderActionCreator.InsufficientBalance.selector, 0)); + actionCreator.createAction(CORE_TEAM_ROLE, STRATEGY, address(POLICY), 0, data, ""); + } + + function test_RevertsIf_TokenholderActionCreatorDoesNotHavePermission() public { + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an + // interface without the `mint` function + vm.prank(tokenHolder1); + mockErc721Votes.delegate(tokenHolder1); // we use mockErc721Votes because IVotesToken is an interface without + // the `delegate` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + token.getPastVotes(tokenHolder1, block.timestamp - 1); + + vm.expectRevert(ILlamaCore.PolicyholderDoesNotHavePermission.selector); + vm.prank(tokenHolder1); + actionCreator.createAction(CORE_TEAM_ROLE, STRATEGY, address(POLICY), 0, data, ""); + } + + function test_ProperlyCreatesAction() public { + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an + // interface without the `delegate` function + vm.prank(tokenHolder1); + mockErc721Votes.delegate(tokenHolder1); // we use mockErc721Votes because IVotesToken is an interface without + // the `delegate` function + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.startPrank(address(EXECUTOR)); // init role, assign policy, and assign permission to setRoleHolder to the token + // voting action creator + POLICY.initializeRole(RoleDescription.wrap("Token Voting Action Creator Role")); + uint8 actionCreatorRole = 2; + POLICY.setRoleHolder(actionCreatorRole, address(actionCreator), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION); + POLICY.setRolePermission( + actionCreatorRole, + ILlamaPolicy.PermissionData(address(POLICY), POLICY.setRoleHolder.selector, address(STRATEGY)), + true + ); + vm.stopPrank(); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + data = abi.encodeCall( + POLICY.setRoleHolder, (actionCreatorRole, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + + uint256 actionCount = CORE.actionsCount(); + + vm.expectEmit(); + emit ActionCreated(actionCount, address(tokenHolder1), actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + vm.prank(tokenHolder1); + + uint256 actionId = actionCreator.createAction(actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + Action memory action = CORE.getAction(actionId); + + assertEq(actionId, actionCount); + assertEq(action.creationTime, block.timestamp); + } +} + +contract CancelAction is ERC721TokenholderActionCreatorTest { + uint8 actionCreatorRole = 2; + bytes data = abi.encodeCall( + POLICY.setRoleHolder, (actionCreatorRole, address(0xdeadbeef), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION) + ); + uint256 threshold = 1; + uint256 actionId; + ERC721TokenholderActionCreator actionCreator; + ActionInfo actionInfo; + + function setUp() public virtual override { + ERC721TokenholderActionCreatorTest.setUp(); + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an + // interface without the `delegate` function + vm.prank(tokenHolder1); + mockErc721Votes.delegate(tokenHolder1); // we use mockErc721Votes because IVotesToken is an interface without + // the `delegate` function + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + actionCreator = new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.startPrank(address(EXECUTOR)); // init role, assign policy, and assign permission to setRoleHolder to the token + // voting action creator + POLICY.initializeRole(RoleDescription.wrap("Token Voting Action Creator Role")); + POLICY.setRoleHolder(actionCreatorRole, address(actionCreator), DEFAULT_ROLE_QTY, DEFAULT_ROLE_EXPIRATION); + POLICY.setRolePermission( + actionCreatorRole, + ILlamaPolicy.PermissionData(address(POLICY), POLICY.setRoleHolder.selector, address(STRATEGY)), + true + ); + vm.stopPrank(); + + vm.roll(block.number + 1); + vm.warp(block.timestamp + 1); + + vm.prank(tokenHolder1); + actionId = actionCreator.createAction(actionCreatorRole, STRATEGY, address(POLICY), 0, data, ""); + + actionInfo = ActionInfo(actionId, address(actionCreator), actionCreatorRole, STRATEGY, address(POLICY), 0, data); + } + + function test_PassesIf_CallerIsActionCreator() public { + vm.expectEmit(); + emit ActionCanceled(actionId, tokenHolder1); + vm.prank(tokenHolder1); + actionCreator.cancelAction(actionInfo); + } + + function test_RevertsIf_CallerIsNotActionCreator(address notCreator) public { + vm.assume(notCreator != tokenHolder1); + vm.expectRevert(TokenholderActionCreator.OnlyActionCreator.selector); + vm.prank(notCreator); + actionCreator.cancelAction(actionInfo); + } +} + +contract SetActionThreshold is ERC721TokenholderActionCreatorTest { + function test_SetsCreationThreshold() public { + uint256 threshold = 1; + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + assertEq(actionCreator.creationThreshold(), threshold); + + vm.expectEmit(); + emit ActionThresholdSet(threshold); + vm.prank(address(EXECUTOR)); + actionCreator.setActionThreshold(threshold); + } + + function test_RevertsIf_CreationThresholdExceedsTotalSupply(uint8 num) public { + vm.assume(num > 1); + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), 1); + + vm.expectRevert(TokenholderActionCreator.InvalidCreationThreshold.selector); + vm.prank(address(EXECUTOR)); + actionCreator.setActionThreshold(num); + } + + function test_RevertsIf_CalledByNotLlamaExecutor(address notLlamaExecutor) public { + vm.assume(notLlamaExecutor != address(EXECUTOR)); + uint256 threshold = 1; + mockErc721Votes.mint(tokenHolder1, 0); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + vm.warp(block.timestamp + 1); + + ERC721TokenholderActionCreator actionCreator = + new ERC721TokenholderActionCreator(token, ILlamaCore(address(CORE)), threshold); + + vm.expectRevert(TokenholderActionCreator.OnlyLlamaExecutor.selector); + vm.prank(notLlamaExecutor); + actionCreator.setActionThreshold(threshold); + } +} diff --git a/test/token-voting/ERC721TokenholderCaster.t.sol b/test/token-voting/ERC721TokenholderCaster.t.sol new file mode 100644 index 0000000..d345d4a --- /dev/null +++ b/test/token-voting/ERC721TokenholderCaster.t.sol @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {Test, console2} from "forge-std/Test.sol"; + +import {MockERC721Votes} from "test/mock/MockERC721Votes.sol"; + +import {Action, ActionInfo, PermissionData} from "src/lib/Structs.sol"; +import {ILlamaCore} from "src/interfaces/ILlamaCore.sol"; +import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol"; +import {ILlamaRelativeStrategyBase} from "src/interfaces/ILlamaRelativeStrategyBase.sol"; +import {ILlamaStrategy} from "src/interfaces/ILlamaStrategy.sol"; +import {RoleDescription} from "src/lib/UDVTs.sol"; +import {ERC721Votes} from "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol"; +import {ERC721TokenholderCaster} from "src/token-voting/ERC721TokenholderCaster.sol"; +import {TokenholderCaster} from "src/token-voting/TokenholderCaster.sol"; +import {PeripheryTestSetup} from "test/PeripheryTestSetup.sol"; + +contract ERC721TokenholderCasterTest is PeripheryTestSetup { + uint256 constant DEFAULT_APPROVAL_THRESHOLD = 2; + uint16 constant ONE_HUNDRED_IN_BPS = 10_000; + uint256 constant ONE_THIRD_IN_BPS = 3333; + uint256 constant TWO_THIRDS_IN_BPS = 6667; + uint8 constant CASTER_ROLE = 2; + uint8 constant MADE_UP_ROLE = 3; + + MockERC721Votes mockErc721Votes; + ERC721Votes token; // this is the same token as + // mockErc721Votes, when we mint on one it will be reflected on the other + + ActionInfo actionInfo; + ERC721TokenholderCaster caster; + + ILlamaStrategy tokenVotingStrategy; + + address tokenHolder1 = makeAddr("tokenholder-1"); + address tokenHolder2 = makeAddr("tokenholder-2"); + address tokenHolder3 = makeAddr("tokenholder-3"); + + event ApprovalCast( + uint256 id, address indexed policyholder, uint8 indexed role, uint8 indexed support, uint256 quantity, string reason + ); + + event ApprovalsSubmitted(uint256 id, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain); + + event DisapprovalCast( + uint256 id, address indexed policyholder, uint8 indexed role, uint8 indexed support, uint256 quantity, string reason + ); + + event DisapprovalsSubmitted(uint256 id, uint96 quantityFor, uint96 quantityAgainst, uint96 quantityAbstain); + + // ========================= + // ======== Helpers ======== + // ========================= + + function createAction(ILlamaStrategy strategy) public returns (ActionInfo memory _actionInfo) { + bytes memory data = abi.encodeCall(POLICY.initializeRole, (RoleDescription.wrap("Action Caster"))); + vm.prank(coreTeam1); + uint256 actionId = CORE.createAction(CORE_TEAM_ROLE, strategy, address(POLICY), 0, data, ""); + _actionInfo = ActionInfo(actionId, coreTeam1, CORE_TEAM_ROLE, strategy, address(POLICY), 0, data); + vm.warp(block.timestamp + 1); + } + + function deployRelativeQuantityQuorumAndSetRole(address _policyHolder, uint8 role) + internal + returns (ILlamaStrategy newStrategy) + { + { + vm.prank(address(EXECUTOR)); + POLICY.setRoleHolder(role, _policyHolder, 1, type(uint64).max); + } + + uint8[] memory forceRoles = new uint8[](0); + + ILlamaRelativeStrategyBase.Config memory strategyConfig = ILlamaRelativeStrategyBase.Config({ + approvalPeriod: 1 days, + queuingPeriod: 1 days, + expirationPeriod: 1 days, + isFixedLengthApprovalPeriod: false, + minApprovalPct: ONE_HUNDRED_IN_BPS, + minDisapprovalPct: ONE_HUNDRED_IN_BPS, + approvalRole: role, + disapprovalRole: role, + forceApprovalRoles: forceRoles, + forceDisapprovalRoles: forceRoles + }); + + ILlamaRelativeStrategyBase.Config[] memory strategyConfigs = new ILlamaRelativeStrategyBase.Config[](1); + strategyConfigs[0] = strategyConfig; + + vm.prank(address(EXECUTOR)); + + CORE.createStrategies(RELATIVE_QUANTITY_QUORUM_LOGIC, encodeStrategyConfigs(strategyConfigs)); + + newStrategy = ILlamaStrategy( + LENS.computeLlamaStrategyAddress( + address(RELATIVE_QUANTITY_QUORUM_LOGIC), encodeStrategy(strategyConfig), address(CORE) + ) + ); + + { + ILlamaPolicy.PermissionData memory _permissionData = + ILlamaPolicy.PermissionData(address(POLICY), POLICY.initializeRole.selector, address(newStrategy)); + vm.prank(address(EXECUTOR)); + POLICY.setRolePermission(CORE_TEAM_ROLE, _permissionData, true); + } + } + + function setUp() public virtual override { + PeripheryTestSetup.setUp(); + vm.deal(address(this), 1 ether); + vm.deal(address(msg.sender), 1 ether); + vm.deal(address(EXECUTOR), 1 ether); + vm.deal(tokenHolder1, 1 ether); + vm.deal(tokenHolder2, 1 ether); + vm.deal(tokenHolder3, 1 ether); + + mockErc721Votes = new MockERC721Votes(); + token = ERC721Votes(address(mockErc721Votes)); + + vm.prank(address(EXECUTOR)); + POLICY.initializeRole(RoleDescription.wrap("Token Voting Caster Role")); // initializes role 2 + vm.prank(address(EXECUTOR)); + POLICY.initializeRole(RoleDescription.wrap("Made Up Role")); // initializes role 2 + + mockErc721Votes.mint(tokenHolder1, 0); + mockErc721Votes.mint(tokenHolder2, 1); + mockErc721Votes.mint(tokenHolder3, 2); + vm.prank(tokenHolder1); + mockErc721Votes.delegate(tokenHolder1); + vm.prank(tokenHolder2); + mockErc721Votes.delegate(tokenHolder2); + vm.prank(tokenHolder3); + mockErc721Votes.delegate(tokenHolder3); + + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + + caster = new ERC721TokenholderCaster( + token, ILlamaCore(address(CORE)), CASTER_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + + tokenVotingStrategy = deployRelativeQuantityQuorumAndSetRole(address(caster), CASTER_ROLE); + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + + actionInfo = createAction(tokenVotingStrategy); + + vm.warp(block.timestamp + 1); + vm.roll(block.number + 1); + } + + function castApprovalsFor() public { + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder3); + caster.castApproval(actionInfo, 1, ""); + } + + function castDisapprovalsFor() public { + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder3); + caster.castDisapproval(actionInfo, 1, ""); + } + + function encodeStrategyConfigs(ILlamaRelativeStrategyBase.Config[] memory strategies) + internal + pure + returns (bytes[] memory encoded) + { + encoded = new bytes[](strategies.length); + for (uint256 i = 0; i < strategies.length; i++) { + encoded[i] = encodeStrategy(strategies[i]); + } + } + + function encodeStrategy(ILlamaRelativeStrategyBase.Config memory strategy) + internal + pure + returns (bytes memory encoded) + { + encoded = abi.encode(strategy); + } +} + +contract Constructor is ERC721TokenholderCasterTest { + function test_RevertsIf_InvalidLlamaCoreAddress() public { + // With invalid LlamaCore instance, TokenholderActionCreator.InvalidLlamaCoreAddress is unreachable + vm.expectRevert(); + new ERC721TokenholderCaster(token, ILlamaCore(makeAddr("invalid-llama-core")), CASTER_ROLE, uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidTokenAddress(address notAToken) public { + vm.assume(notAToken != address(0)); + vm.assume(notAToken != address(token)); + vm.expectRevert(); // will revert with EvmError: Revert because `totalSupply` is not a function + new ERC721TokenholderCaster(ERC721Votes(notAToken), ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidRole(uint8 role) public { + role = uint8(bound(role, POLICY.numRoles(), 255)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.RoleNotInitialized.selector, uint8(255))); + new ERC721TokenholderCaster(token, ILlamaCore(address(CORE)), uint8(255), uint256(1), uint256(1)); + } + + function test_RevertsIf_InvalidMinApprovalPct() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinApprovalPct.selector, uint256(0))); + new ERC721TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(0), uint256(1)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinApprovalPct.selector, uint256(10_001))); + new ERC721TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(10_001), uint256(1)); + } + + function test_RevertsIf_InvalidMinDisapprovalPct() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinDisapprovalPct.selector, uint256(0))); + new ERC721TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(0)); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidMinDisapprovalPct.selector, uint256(10_001))); + new ERC721TokenholderCaster(token, ILlamaCore(address(CORE)), CASTER_ROLE, uint256(1), uint256(10_001)); + } + + function test_ProperlySetsConstructorArguments() public { + mockErc721Votes.mint(address(this), 1_000_000e18); // we use mockErc721Votes because IVotesToken is an interface + // without the `mint` function + + caster = new ERC721TokenholderCaster( + token, ILlamaCore(address(CORE)), CASTER_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + + assertEq(address(caster.LLAMA_CORE()), address(CORE)); + assertEq(address(caster.TOKEN()), address(token)); + assertEq(caster.ROLE(), CASTER_ROLE); + assertEq(caster.MIN_APPROVAL_PCT(), DEFAULT_APPROVAL_THRESHOLD); + assertEq(caster.MIN_DISAPPROVAL_PCT(), DEFAULT_APPROVAL_THRESHOLD); + } +} + +contract CastApproval is ERC721TokenholderCasterTest { + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.castApproval(notActionInfo, CASTER_ROLE, ""); + } + + function test_RevertsIf_ApprovalNotEnabled() public { + TokenholderCaster casterWithWrongRole = new ERC721TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.castApproval(actionInfo, MADE_UP_ROLE, ""); + } + + function test_RevertsIf_ActionNotActive() public { + vm.warp(block.timestamp + 1 days); + vm.expectRevert(TokenholderCaster.ActionNotActive.selector); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_AlreadyCastApproval() public { + vm.startPrank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + + vm.expectRevert(TokenholderCaster.AlreadyCastApproval.selector); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InvalidSupport() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidSupport.selector, uint8(3))); + caster.castApproval(actionInfo, 3, ""); + } + + function test_RevertsIf_CastingPeriodOver() public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); // 2/3 of the approval period + vm.expectRevert(TokenholderCaster.CastingPeriodOver.selector); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InsufficientBalance() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientBalance.selector, 0)); + caster.castApproval(actionInfo, 1, ""); + } + + function test_CastsApprovalCorrectly(uint8 support) public { + support = uint8(bound(support, 0, 2)); + vm.expectEmit(); + emit ApprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, support, token.getPastVotes(tokenHolder1, block.timestamp - 1), "" + ); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, support, ""); + } + + function test_CastsApprovalCorrectly_WithReason() public { + vm.expectEmit(); + emit ApprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, 1, token.getPastVotes(tokenHolder1, token.clock() - 1), "reason" + ); + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, "reason"); + } +} + +contract CastDisapproval is ERC721TokenholderCasterTest { + function setUp() public virtual override { + ERC721TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + + vm.prank(tokenHolder1); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.castDisapproval(notActionInfo, CASTER_ROLE, ""); + } + + function test_RevertsIf_DisapprovalNotEnabled() public { + TokenholderCaster casterWithWrongRole = new ERC721TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.castDisapproval(actionInfo, MADE_UP_ROLE, ""); + } + + function test_RevertsIf_AlreadyCastApproval() public { + vm.startPrank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + + vm.expectRevert(TokenholderCaster.AlreadyCastDisapproval.selector); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InvalidSupport() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InvalidSupport.selector, uint8(3))); + caster.castDisapproval(actionInfo, 3, ""); + } + + function test_RevertsIf_CastingPeriodOver() public { + // TODO why do we need to add 2 here + vm.warp(block.timestamp + 2 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); // 2/3 of the approval period + vm.expectRevert(TokenholderCaster.CastingPeriodOver.selector); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_RevertsIf_InsufficientBalance() public { + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientBalance.selector, 0)); + caster.castDisapproval(actionInfo, 1, ""); + } + + function test_CastsDisapprovalCorrectly(uint8 support) public { + support = uint8(bound(support, 0, 2)); + vm.expectEmit(); + emit DisapprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, support, token.getPastVotes(tokenHolder1, block.timestamp - 1), "" + ); + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, support, ""); + } + + function test_CastsDisapprovalCorrectly_WithReason() public { + vm.expectEmit(); + emit DisapprovalCast( + actionInfo.id, tokenHolder1, CASTER_ROLE, 1, token.getPastVotes(tokenHolder1, token.clock() - 1), "reason" + ); + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, "reason"); + } +} + +contract SubmitApprovals is ERC721TokenholderCasterTest { + function setUp() public virtual override { + ERC721TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.submitApprovals(notActionInfo); + } + + function test_RevertsIf_AlreadySubmittedApproval() public { + vm.startPrank(tokenHolder1); + caster.submitApprovals(actionInfo); + + vm.expectRevert(TokenholderCaster.AlreadySubmittedApproval.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_SubmissionPeriodOver() public { + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.SubmissionPeriodOver.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_InsufficientApprovals() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 1)); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_CastingPeriodNotOver() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.CantSubmitYet.selector); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_InsufficientApprovalsFor() public { + actionInfo = createAction(tokenVotingStrategy); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 1)); + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ForDoesNotSurpassAgainst() public { + actionInfo = createAction(tokenVotingStrategy); + + vm.prank(tokenHolder1); + caster.castApproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castApproval(actionInfo, 0, ""); + vm.prank(tokenHolder3); + caster.castApproval(actionInfo, 0, ""); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.ForDoesNotSurpassAgainst.selector, 1, 2)); + caster.submitApprovals(actionInfo); + } + + function test_SubmitsApprovalsCorrectly() public { + vm.expectEmit(); + emit ApprovalsSubmitted(actionInfo.id, 3, 0, 0); + caster.submitApprovals(actionInfo); + } +} + +contract SubmitDisapprovals is ERC721TokenholderCasterTest { + function setUp() public virtual override { + ERC721TokenholderCasterTest.setUp(); + + castApprovalsFor(); + + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + + caster.submitApprovals(actionInfo); + } + + function test_RevertsIf_ActionInfoMismatch(ActionInfo memory notActionInfo) public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.assume(notActionInfo.id != actionInfo.id); + vm.expectRevert(); + caster.submitDisapprovals(notActionInfo); + } + + function test_RevertsIf_DisapprovalNotEnabled() public { + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + TokenholderCaster casterWithWrongRole = new ERC721TokenholderCaster( + token, ILlamaCore(address(CORE)), MADE_UP_ROLE, DEFAULT_APPROVAL_THRESHOLD, DEFAULT_APPROVAL_THRESHOLD + ); + vm.expectRevert(abi.encodeWithSelector(ILlamaRelativeStrategyBase.InvalidRole.selector, CASTER_ROLE)); + casterWithWrongRole.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_AlreadySubmittedDisapproval() public { + Action memory action = CORE.getAction(actionInfo.id); + vm.warp( + action.minExecutionTime + - (ILlamaRelativeStrategyBase(address(actionInfo.strategy)).queuingPeriod() * ONE_THIRD_IN_BPS) + / ONE_HUNDRED_IN_BPS + ); + + castDisapprovalsFor(); + + vm.startPrank(tokenHolder1); + caster.submitDisapprovals(actionInfo); + + vm.expectRevert(TokenholderCaster.AlreadySubmittedDisapproval.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_SubmissionPeriodOver() public { + castDisapprovalsFor(); + + vm.warp(block.timestamp + 1 days); + vm.expectRevert(TokenholderCaster.SubmissionPeriodOver.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_InsufficientDisapprovals() public { + actionInfo = createAction(tokenVotingStrategy); + castApprovalsFor(); + vm.warp(block.timestamp + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + caster.submitApprovals(actionInfo); + + //TODO why add 1 here + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.InsufficientApprovals.selector, 0, 1)); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_CastingPeriodNotOver() public { + vm.warp(block.timestamp + (1 days * 3333) / ONE_HUNDRED_IN_BPS); // 1/3 of the approval period + vm.expectRevert(TokenholderCaster.CantSubmitYet.selector); + caster.submitDisapprovals(actionInfo); + } + + function test_RevertsIf_ForDoesNotSurpassAgainst() public { + vm.prank(tokenHolder1); + caster.castDisapproval(actionInfo, 1, ""); + vm.prank(tokenHolder2); + caster.castDisapproval(actionInfo, 0, ""); + vm.prank(tokenHolder3); + caster.castDisapproval(actionInfo, 0, ""); + // TODO why add 1 here? + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectRevert(abi.encodeWithSelector(TokenholderCaster.ForDoesNotSurpassAgainst.selector, 1, 2)); + caster.submitDisapprovals(actionInfo); + } + + function test_SubmitsDisapprovalsCorrectly() public { + castDisapprovalsFor(); + + //TODO why add 1 here? + vm.warp(block.timestamp + 1 + (1 days * TWO_THIRDS_IN_BPS) / ONE_HUNDRED_IN_BPS); + vm.expectEmit(); + emit DisapprovalsSubmitted(actionInfo.id, 3, 0, 0); + caster.submitDisapprovals(actionInfo); + } +} diff --git a/test/token-voting/LlamaTokenVotingFactory.t.sol b/test/token-voting/LlamaTokenVotingFactory.t.sol new file mode 100644 index 0000000..d65db04 --- /dev/null +++ b/test/token-voting/LlamaTokenVotingFactory.t.sol @@ -0,0 +1,368 @@ +// // SPDX-License-Identifier: MIT +// pragma solidity 0.8.19; + +// import {Test, console2} from "forge-std/Test.sol"; + +// import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; +// import {LlamaTestSetup} from "test/utils/LlamaTestSetup.sol"; +// import {Roles} from "test/utils/LlamaTestSetup.sol"; + +// import {Action, ActionInfo, PermissionData} from "src/lib/Structs.sol"; +// import {RoleDescription} from "src/lib/UDVTs.sol"; +// import {ERC20Votes} from "src/modules/token-voting/OZ-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol"; +// import {ERC721Votes} from "src/modules/token-voting/OZ-contracts/contracts/token/ERC721/extensions/ERC721Votes.sol"; +// import {LlamaCore} from "src/LlamaCore.sol"; +// import {LlamaTokenVotingFactory} from "src/modules/token-voting/LlamaTokenVotingFactory.sol"; +// import {MockERC20Votes} from "test/mock/MockERC20Votes.sol"; +// import {MockERC721Votes} from "test/mock/MockERC721Votes.sol"; + +// contract LlamaTokenVotingFactoryTest is LlamaTestSetup { +// event ERC20TokenholderActionCreatorCreated(address actionCreator, address indexed token); +// event ERC721TokenholderActionCreatorCreated(address actionCreator, address indexed token); +// event ERC20TokenholderCasterCreated( +// address caster, address indexed token, uint256 minApprovalPct, uint256 minDisapprovalPct +// ); +// event ERC721TokenholderCasterCreated( +// address caster, address indexed token, uint256 minApprovalPct, uint256 minDisapprovalPct +// ); +// event RoleAssigned(address indexed policyholder, uint8 indexed role, uint64 expiration, uint96 quantity); +// event RoleInitialized(uint8 indexed role, RoleDescription description); + +// uint256 public constant CREATION_THRESHOLD = 100; +// uint256 public constant MIN_APPROVAL_PCT = 1000; +// uint256 public constant MIN_DISAPPROVAL_PCT = 1000; + +// LlamaTokenVotingFactory public FACTORY; +// ERC20Votes public ERC20TOKEN; +// ERC721Votes public ERC721TOKEN; + +// function setUp() public override { +// super.setUp(); + +// FACTORY = new LlamaTokenVotingFactory(); +// MockERC20Votes mockERC20Votes = new MockERC20Votes(); +// ERC20TOKEN = ERC20Votes(address(mockERC20Votes)); +// MockERC721Votes mockERC721Votes = new MockERC721Votes(); +// ERC721TOKEN = ERC721Votes(address(mockERC721Votes)); + +// mockERC20Votes.mint(approverAdam, 100); +// mockERC20Votes.mint(approverAlicia, 100); +// mockERC20Votes.mint(approverAndy, 100); +// mockERC20Votes.mint(actionCreatorAaron, 100); + +// mockERC721Votes.mint(approverAdam, 0); +// mockERC721Votes.mint(approverAlicia, 1); +// mockERC721Votes.mint(approverAndy, 2); +// mockERC721Votes.mint(actionCreatorAaron, 3); + +// PermissionData memory newPermission1 = +// PermissionData(address(FACTORY), LlamaTokenVotingFactory.deployTokenVotingModule.selector, mpStrategy1); +// PermissionData memory newPermission2 = PermissionData( +// address(FACTORY), LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, mpStrategy1 +// ); + +// vm.startPrank(address(mpExecutor)); +// mpPolicy.setRolePermission(uint8(Roles.ActionCreator), newPermission1, true); +// mpPolicy.setRolePermission(uint8(Roles.ActionCreator), newPermission2, true); +// vm.stopPrank(); + +// vm.warp(block.timestamp + 1); +// } + +// function _createApproveAndQueueAction(bytes memory data) internal returns (ActionInfo memory actionInfo) { +// vm.prank(actionCreatorAaron); +// uint256 actionId = mpCore.createAction(uint8(Roles.ActionCreator), mpStrategy1, address(FACTORY), 0, data, ""); +// actionInfo = +// ActionInfo(actionId, actionCreatorAaron, uint8(Roles.ActionCreator), mpStrategy1, address(FACTORY), 0, data); + +// vm.prank(approverAdam); +// mpCore.castApproval(uint8(Roles.Approver), actionInfo, ""); +// vm.prank(approverAlicia); +// mpCore.castApproval(uint8(Roles.Approver), actionInfo, ""); +// vm.prank(approverAndy); +// mpCore.castApproval(uint8(Roles.Approver), actionInfo, ""); + +// vm.warp(block.timestamp + 6 days); +// mpCore.queueAction(actionInfo); +// vm.warp(block.timestamp + 5 days); +// } +// } + +// contract DeployTokenVotingModule is LlamaTokenVotingFactoryTest { +// function test_CanDeployERC20TokenVotingModule() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, +// address(ERC20TOKEN), +// true, +// true, +// true, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC20TokenholderActionCreatorCreated(0x4f81992FCe2E1846dD528eC0102e6eE1f61ed3e2, address(ERC20TOKEN)); +// vm.expectEmit(); +// emit ERC20TokenholderCasterCreated( +// 0xCB6f5076b5bbae81D7643BfBf57897E8E3FB1db9, address(ERC20TOKEN), MIN_APPROVAL_PCT, MIN_DISAPPROVAL_PCT +// ); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployERC721TokenVotingModule() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(ERC721TOKEN), false, true, true, 1, 1, 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderActionCreatorCreated(0x4f81992FCe2E1846dD528eC0102e6eE1f61ed3e2, address(ERC721TOKEN)); +// vm.expectEmit(); +// emit ERC721TokenholderCasterCreated(0xCB6f5076b5bbae81D7643BfBf57897E8E3FB1db9, address(ERC721TOKEN), 1, 1); +// mpCore.executeAction(actionInfo); +// } + +// function test_RevertsIfNoModulesDeployed() public { +// vm.expectRevert(LlamaTokenVotingFactory.NoModulesDeployed.selector); +// vm.prank(address(mpExecutor)); +// FACTORY.deployTokenVotingModule(address(ERC20TOKEN), true, false, false, 1, 1, 1); +// } + +// function test_CanDeployCreatorOnly_ERC20() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, +// address(ERC20TOKEN), +// true, +// true, +// false, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC20TokenholderActionCreatorCreated(0x4f81992FCe2E1846dD528eC0102e6eE1f61ed3e2, address(ERC20TOKEN)); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCasterOnly_ERC20() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, +// address(ERC20TOKEN), +// true, +// false, +// true, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// emit ERC20TokenholderCasterCreated( +// 0xCB6f5076b5bbae81D7643BfBf57897E8E3FB1db9, address(ERC20TOKEN), MIN_APPROVAL_PCT, MIN_DISAPPROVAL_PCT +// ); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCreatorOnly_ERC721() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(ERC721TOKEN), false, true, false, 1, 1, 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderActionCreatorCreated(0x4f81992FCe2E1846dD528eC0102e6eE1f61ed3e2, address(ERC721TOKEN)); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCasterOnly_ERC721() public { +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.deployTokenVotingModule.selector, address(ERC721TOKEN), false, false, true, 1, 1, 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderCasterCreated(0x4f81992FCe2E1846dD528eC0102e6eE1f61ed3e2, address(ERC721TOKEN), 1, 1); +// mpCore.executeAction(actionInfo); +// } +// } + +// contract DelegateCallDeployTokenVotingModuleWithRoles is LlamaTokenVotingFactoryTest { +// function authorizeScript() internal { +// vm.prank(address(mpExecutor)); +// mpCore.setScriptAuthorization(address(FACTORY), true); +// } + +// function test_CanDeployERC20TokenVotingModule() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC20TOKEN), +// true, +// true, +// true, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC20TokenholderActionCreatorCreated(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC20TOKEN)); +// vm.expectEmit(); +// emit ERC20TokenholderCasterCreated( +// 0x1d2Fdf27Cbc73084b71477AA7290AAAC9715aD2c, address(ERC20TOKEN), MIN_APPROVAL_PCT, MIN_DISAPPROVAL_PCT +// ); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployERC721TokenVotingModule() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC721TOKEN), +// false, +// true, +// true, +// 1, +// 1, +// 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderActionCreatorCreated(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC721TOKEN)); +// vm.expectEmit(); +// emit ERC721TokenholderCasterCreated(0x1d2Fdf27Cbc73084b71477AA7290AAAC9715aD2c, address(ERC721TOKEN), 1, 1); +// vm.expectEmit(); +// emit RoleAssigned(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, 9, type(uint64).max, 1); +// vm.expectEmit(); +// emit RoleAssigned(0x1d2Fdf27Cbc73084b71477AA7290AAAC9715aD2c, 10, type(uint64).max, 1); + +// mpCore.executeAction(actionInfo); +// } + +// function test_RevertsIfNoModulesDeployed() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC20TOKEN), +// true, +// false, +// false, +// 1, +// 1, +// 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectRevert( +// abi.encodeWithSelector( +// LlamaCore.FailedActionExecution.selector, +// abi.encodeWithSelector(LlamaTokenVotingFactory.NoModulesDeployed.selector) +// ) +// ); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCreatorOnly_ERC20() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC20TOKEN), +// true, +// true, +// false, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC20TokenholderActionCreatorCreated(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC20TOKEN)); +// vm.expectEmit(); +// emit RoleAssigned(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, 9, type(uint64).max, 1); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCasterOnly_ERC20() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC20TOKEN), +// true, +// false, +// true, +// CREATION_THRESHOLD, +// MIN_APPROVAL_PCT, +// MIN_DISAPPROVAL_PCT +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// emit ERC20TokenholderCasterCreated( +// 0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC20TOKEN), MIN_APPROVAL_PCT, MIN_DISAPPROVAL_PCT +// ); +// vm.expectEmit(); +// emit RoleAssigned(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, 9, type(uint64).max, 1); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCreatorOnly_ERC721() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC721TOKEN), +// false, +// true, +// false, +// 1, +// 1, +// 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderActionCreatorCreated(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC721TOKEN)); +// vm.expectEmit(); +// emit RoleAssigned(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, 9, type(uint64).max, 1); +// mpCore.executeAction(actionInfo); +// } + +// function test_CanDeployCasterOnly_ERC721() public { +// authorizeScript(); +// bytes memory data = abi.encodeWithSelector( +// LlamaTokenVotingFactory.delegateCallDeployTokenVotingModuleWithRoles.selector, +// address(ERC721TOKEN), +// false, +// false, +// true, +// 1, +// 1, +// 1 +// ); + +// ActionInfo memory actionInfo = _createApproveAndQueueAction(data); + +// vm.expectEmit(); +// emit ERC721TokenholderCasterCreated(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, address(ERC721TOKEN), 1, 1); +// vm.expectEmit(); +// emit RoleAssigned(0xaE207F1b391B8D25f7645F7AB2B10F0d0dDd5A81, 9, type(uint64).max, 1); +// mpCore.executeAction(actionInfo); +// } +// } From db98fef0d3c900b17043302672ab75f7459c7468 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Tue, 5 Dec 2023 18:42:36 -0500 Subject: [PATCH 2/2] chore: update submodules (#17) **Motivation:** Removing harcoded dependencies and adding as submodules. These were copied over incorrectly during the repo migration. **Modifications:** Delete solmate and solady directories in lib directory. **Result:** We will reference our dependencies as submodules. --- .gitmodules | 2 +- lib/solady | 1 + lib/solady/.gas-snapshot | 1052 ---------- lib/solady/.github/pull_request_template.md | 24 - .../.github/workflows/ci-all-via-ir.yml | 54 - lib/solady/.github/workflows/ci-woke.yml | 40 - lib/solady/.github/workflows/ci.yml | 88 - lib/solady/.gitignore | 36 - lib/solady/.gitmodules | 4 - lib/solady/LICENSE.txt | 21 - lib/solady/README.md | 140 -- .../audits/ackee-blockchain-solady-report.pdf | Bin 1172173 -> 0 bytes lib/solady/audits/cantina-solady-report.pdf | Bin 950468 -> 0 bytes .../audits/shung-solady-erc721-audit.pdf | Bin 102720 -> 0 bytes lib/solady/ext/woke/EIP712Mock.sol | 17 - lib/solady/ext/woke/ERC1155Mock.sol | 149 -- lib/solady/ext/woke/ERC20Mock.sol | 121 -- lib/solady/ext/woke/ERC721Mock.sol | 86 - lib/solady/ext/woke/MerkleProofMock.sol | 21 - lib/solady/ext/woke/NoETHMock.sol | 44 - lib/solady/ext/woke/SignatureCheckerMock.sol | 58 - lib/solady/ext/woke/__init__.py | 0 lib/solady/ext/woke/test_eip712.py | 84 - lib/solady/ext/woke/test_eip712_fuzz.py | 65 - lib/solady/ext/woke/test_erc1155.py | 443 ---- lib/solady/ext/woke/test_erc1155_fuzz.py | 537 ----- lib/solady/ext/woke/test_erc20.py | 242 --- lib/solady/ext/woke/test_erc721_fuzz.py | 365 ---- lib/solady/ext/woke/test_merkle_proof.py | 55 - lib/solady/ext/woke/test_merkle_proof_fuzz.py | 124 -- .../ext/woke/test_signature_checker_fuzz.py | 178 -- lib/solady/ext/woke/utils.py | 89 - lib/solady/ext/woke/weird/Approval.sol | 17 - lib/solady/ext/woke/weird/ApprovalToZero.sol | 17 - lib/solady/ext/woke/weird/BlockList.sol | 29 - lib/solady/ext/woke/weird/Bytes32Metadata.sol | 62 - lib/solady/ext/woke/weird/DaiPermit.sol | 89 - lib/solady/ext/woke/weird/ERC20.sol | 62 - lib/solady/ext/woke/weird/HighDecimals.sol | 12 - lib/solady/ext/woke/weird/LowDecimals.sol | 12 - lib/solady/ext/woke/weird/MissingReturns.sol | 58 - lib/solady/ext/woke/weird/NoRevert.sol | 56 - lib/solady/ext/woke/weird/Pausable.sol | 36 - lib/solady/ext/woke/weird/Proxied.sol | 140 -- lib/solady/ext/woke/weird/Reentrant.sol | 31 - lib/solady/ext/woke/weird/ReturnsFalse.sol | 61 - lib/solady/ext/woke/weird/RevertToZero.sol | 17 - lib/solady/ext/woke/weird/RevertZero.sol | 17 - lib/solady/ext/woke/weird/TransferFee.sol | 34 - lib/solady/ext/woke/weird/Uint96.sol | 87 - lib/solady/ext/woke/weird/Upgradable.sol | 61 - lib/solady/ext/woke/woke-via-ir.toml | 28 - lib/solady/ext/woke/woke.toml | 27 - lib/solady/foundry.toml | 21 - lib/solady/js/solady.d.ts | 66 - lib/solady/js/solady.js | 255 --- lib/solady/js/solady.test.js | 123 -- lib/solady/lib/ds-test/.gitignore | 3 - lib/solady/lib/ds-test/LICENSE | 674 ------- lib/solady/lib/ds-test/Makefile | 14 - lib/solady/lib/ds-test/default.nix | 4 - lib/solady/lib/ds-test/demo/demo.sol | 222 -- lib/solady/lib/ds-test/src/test.sol | 469 ----- lib/solady/logo.svg | 1 - lib/solady/package-lock.json | 13 - lib/solady/package.json | 17 - lib/solady/src/Milady.sol | 55 - lib/solady/src/accounts/ERC1271.sol | 104 - lib/solady/src/accounts/ERC4337.sol | 386 ---- lib/solady/src/accounts/ERC4337Factory.sol | 71 - lib/solady/src/accounts/ERC6551.sol | 321 --- lib/solady/src/accounts/ERC6551Proxy.sol | 72 - lib/solady/src/accounts/Receiver.sol | 33 - lib/solady/src/auth/Ownable.sol | 278 --- lib/solady/src/auth/OwnableRoles.sol | 530 ----- lib/solady/src/tokens/ERC1155.sol | 1114 ----------- lib/solady/src/tokens/ERC20.sol | 546 ----- lib/solady/src/tokens/ERC2981.sol | 162 -- lib/solady/src/tokens/ERC4626.sol | 524 ----- lib/solady/src/tokens/ERC6909.sol | 541 ----- lib/solady/src/tokens/ERC721.sol | 890 --------- lib/solady/src/tokens/WETH.sol | 89 - lib/solady/src/utils/Base64.sol | 166 -- lib/solady/src/utils/CREATE3.sol | 153 -- lib/solady/src/utils/Clone.sol | 387 ---- lib/solady/src/utils/DateTimeLib.sol | 516 ----- lib/solady/src/utils/DynamicBufferLib.sol | 226 --- lib/solady/src/utils/ECDSA.sol | 411 ---- lib/solady/src/utils/EIP712.sol | 208 -- lib/solady/src/utils/ERC1967Factory.sol | 419 ---- .../src/utils/ERC1967FactoryConstants.sol | 45 - lib/solady/src/utils/FixedPointMathLib.sol | 993 --------- lib/solady/src/utils/GasBurnerLib.sol | 25 - lib/solady/src/utils/JSONParserLib.sol | 815 -------- lib/solady/src/utils/LibBit.sol | 184 -- lib/solady/src/utils/LibBitmap.sol | 199 -- lib/solady/src/utils/LibClone.sol | 836 -------- lib/solady/src/utils/LibMap.sol | 309 --- lib/solady/src/utils/LibPRNG.sol | 153 -- lib/solady/src/utils/LibRLP.sol | 71 - lib/solady/src/utils/LibSort.sol | 699 ------- lib/solady/src/utils/LibString.sol | 1163 ----------- lib/solady/src/utils/LibZip.sol | 279 --- lib/solady/src/utils/MerkleProofLib.sol | 309 --- lib/solady/src/utils/MetadataReaderLib.sol | 150 -- lib/solady/src/utils/MinHeapLib.sol | 204 -- lib/solady/src/utils/Multicallable.sol | 66 - lib/solady/src/utils/RedBlackTreeLib.sol | 729 ------- lib/solady/src/utils/SSTORE2.sol | 271 --- lib/solady/src/utils/SafeCastLib.sol | 390 ---- lib/solady/src/utils/SafeTransferLib.sol | 374 ---- lib/solady/src/utils/SignatureCheckerLib.sol | 545 ----- lib/solady/src/utils/UUPSUpgradeable.sol | 142 -- lib/solady/test/Base64.t.sol | 119 -- lib/solady/test/CREATE3.t.sol | 121 -- lib/solady/test/DateTimeLib.t.sol | 796 -------- lib/solady/test/DynamicBufferLib.t.sol | 356 ---- lib/solady/test/ECDSA.t.sol | 452 ----- lib/solady/test/EIP712.t.sol | 173 -- lib/solady/test/ERC1155.t.sol | 1234 ------------ lib/solady/test/ERC1967Factory.t.sol | 302 --- lib/solady/test/ERC20.t.sol | 482 ----- lib/solady/test/ERC2981.t.sol | 140 -- lib/solady/test/ERC4337.t.sol | 555 ----- lib/solady/test/ERC4337Factory.t.sol | 75 - lib/solady/test/ERC4626.t.sol | 556 ------ lib/solady/test/ERC6551.t.sol | 415 ---- lib/solady/test/ERC6909.t.sol | 535 ----- lib/solady/test/ERC721.t.sol | 976 --------- lib/solady/test/FixedPointMathLib.t.sol | 1750 ---------------- lib/solady/test/GasBurnerLib.t.sol | 31 - lib/solady/test/JSONParserLib.t.sol | 788 -------- lib/solady/test/LibBit.t.sol | 205 -- lib/solady/test/LibBitmap.t.sol | 350 ---- lib/solady/test/LibClone.t.sol | 422 ---- lib/solady/test/LibMap.t.sol | 649 ------ lib/solady/test/LibPRNG.t.sol | 171 -- lib/solady/test/LibRLP.t.sol | 383 ---- lib/solady/test/LibSort.t.sol | 1250 ------------ lib/solady/test/LibString.t.sol | 1392 ------------- lib/solady/test/LibZip.t.sol | 276 --- lib/solady/test/MerkleProofLib.t.sol | 440 ---- lib/solady/test/MetadataReaderLib.t.sol | 305 --- lib/solady/test/MinHeapLib.t.sol | 130 -- lib/solady/test/Multicallable.t.sol | 119 -- lib/solady/test/Ownable.t.sol | 164 -- lib/solady/test/OwnableRoles.t.sol | 377 ---- lib/solady/test/README.md | 5 - lib/solady/test/Receiver.t.sol | 43 - lib/solady/test/RedBlackTree.t.sol | 570 ------ lib/solady/test/SSTORE2.t.sol | 200 -- lib/solady/test/SafeCastLib.t.sol | 307 --- lib/solady/test/SafeTransferLib.t.sol | 748 ------- lib/solady/test/SignatureCheckerLib.t.sol | 341 ---- lib/solady/test/UUPSUpgradeable.t.sol | 84 - lib/solady/test/WETH.t.sol | 202 -- lib/solady/test/utils/InvariantTest.sol | 15 - lib/solady/test/utils/SoladyTest.sol | 12 - lib/solady/test/utils/TestPlus.sol | 351 ---- lib/solady/test/utils/forge-std/Script.sol | 13 - lib/solady/test/utils/forge-std/Test.sol | 165 -- lib/solady/test/utils/forge-std/Vm.sol | 525 ----- lib/solady/test/utils/forge-std/console.sol | 1581 --------------- lib/solady/test/utils/mocks/MockCd.sol | 80 - lib/solady/test/utils/mocks/MockEIP712.sol | 26 - .../test/utils/mocks/MockEIP712Dynamic.sol | 43 - lib/solady/test/utils/mocks/MockERC1155.sol | 120 -- .../test/utils/mocks/MockERC1271Malicious.sol | 14 - .../test/utils/mocks/MockERC1271Wallet.sol | 56 - lib/solady/test/utils/mocks/MockERC20.sol | 72 - .../test/utils/mocks/MockERC20LikeUSDT.sol | 16 - lib/solady/test/utils/mocks/MockERC2981.sol | 42 - lib/solady/test/utils/mocks/MockERC4337.sol | 50 - lib/solady/test/utils/mocks/MockERC4626.sol | 75 - lib/solady/test/utils/mocks/MockERC6551.sol | 55 - .../test/utils/mocks/MockERC6551Registry.sol | 115 -- lib/solady/test/utils/mocks/MockERC6909.sol | 110 - lib/solady/test/utils/mocks/MockERC721.sol | 141 -- .../test/utils/mocks/MockETHRecipient.sol | 40 - .../test/utils/mocks/MockEntryPoint.sol | 34 - .../test/utils/mocks/MockImplementation.sol | 26 - .../test/utils/mocks/MockMulticallable.sol | 68 - lib/solady/test/utils/mocks/MockOwnable.sol | 97 - .../test/utils/mocks/MockOwnableRoles.sol | 165 -- lib/solady/test/utils/mocks/MockReceiver.sol | 8 - .../utils/mocks/MockUUPSImplementation.sol | 50 - .../utils/weird-tokens/MissingReturnToken.sol | 81 - .../utils/weird-tokens/ReturnsFalseToken.sol | 57 - .../weird-tokens/ReturnsRawBytesToken.sol | 116 -- .../weird-tokens/ReturnsTooLittleToken.sol | 69 - .../weird-tokens/ReturnsTooMuchToken.sol | 99 - .../utils/weird-tokens/ReturnsTwoToken.sol | 57 - .../utils/weird-tokens/RevertingToken.sol | 57 - lib/solmate | 1 + lib/solmate/.gas-snapshot | 568 ------ lib/solmate/.gitattributes | 1 - lib/solmate/.github/pull_request_template.md | 13 - lib/solmate/.github/workflows/tests.yml | 29 - lib/solmate/.gitignore | 3 - lib/solmate/.gitmodules | 3 - lib/solmate/.prettierignore | 1 - lib/solmate/.prettierrc | 14 - lib/solmate/.vscode/settings.json | 9 - lib/solmate/LICENSE | 661 ------ lib/solmate/README.md | 70 - .../audits/v6-Fixed-Point-Solutions.pdf | Bin 170456 -> 0 bytes lib/solmate/foundry.toml | 7 - lib/solmate/lib/ds-test/.gitignore | 3 - lib/solmate/lib/ds-test/LICENSE | 674 ------- lib/solmate/lib/ds-test/Makefile | 14 - lib/solmate/lib/ds-test/default.nix | 4 - lib/solmate/lib/ds-test/demo/demo.sol | 222 -- lib/solmate/lib/ds-test/package.json | 15 - lib/solmate/lib/ds-test/src/test.sol | 469 ----- lib/solmate/package-lock.json | 125 -- lib/solmate/package.json | 20 - lib/solmate/src/auth/Auth.sol | 64 - lib/solmate/src/auth/Owned.sol | 44 - .../auth/authorities/MultiRolesAuthority.sol | 123 -- .../src/auth/authorities/RolesAuthority.sol | 108 - lib/solmate/src/mixins/ERC4626.sol | 183 -- lib/solmate/src/test/Auth.t.sol | 192 -- lib/solmate/src/test/Bytes32AddressLib.t.sol | 22 - lib/solmate/src/test/CREATE3.t.sol | 99 - lib/solmate/src/test/DSTestPlus.t.sol | 72 - lib/solmate/src/test/ERC1155.t.sol | 1777 ----------------- lib/solmate/src/test/ERC20.t.sol | 531 ----- lib/solmate/src/test/ERC4626.t.sol | 446 ----- lib/solmate/src/test/ERC6909.t.sol | 378 ---- lib/solmate/src/test/ERC721.t.sol | 727 ------- lib/solmate/src/test/FixedPointMathLib.t.sol | 360 ---- lib/solmate/src/test/LibString.t.sol | 148 -- lib/solmate/src/test/MerkleProofLib.t.sol | 50 - .../src/test/MultiRolesAuthority.t.sol | 321 --- lib/solmate/src/test/Owned.t.sol | 40 - lib/solmate/src/test/ReentrancyGuard.t.sol | 56 - lib/solmate/src/test/RolesAuthority.t.sol | 148 -- lib/solmate/src/test/SSTORE2.t.sol | 152 -- lib/solmate/src/test/SafeCastLib.t.sol | 644 ------ lib/solmate/src/test/SafeTransferLib.t.sol | 610 ------ lib/solmate/src/test/SignedWadMath.t.sol | 74 - lib/solmate/src/test/WETH.t.sol | 146 -- .../src/test/utils/DSInvariantTest.sol | 16 - lib/solmate/src/test/utils/DSTestPlus.sol | 179 -- lib/solmate/src/test/utils/Hevm.sol | 107 - .../src/test/utils/mocks/MockAuthChild.sol | 12 - .../src/test/utils/mocks/MockAuthority.sol | 20 - .../src/test/utils/mocks/MockERC1155.sol | 42 - .../src/test/utils/mocks/MockERC20.sol | 20 - .../src/test/utils/mocks/MockERC4626.sol | 28 - .../src/test/utils/mocks/MockERC6909.sol | 22 - .../src/test/utils/mocks/MockERC721.sol | 30 - .../src/test/utils/mocks/MockOwned.sol | 12 - .../utils/weird-tokens/MissingReturnToken.sol | 83 - .../utils/weird-tokens/ReturnsFalseToken.sol | 61 - .../weird-tokens/ReturnsGarbageToken.sol | 115 -- .../weird-tokens/ReturnsTooLittleToken.sol | 70 - .../weird-tokens/ReturnsTooMuchToken.sol | 98 - .../utils/weird-tokens/ReturnsTwoToken.sol | 61 - .../utils/weird-tokens/RevertingToken.sol | 61 - lib/solmate/src/tokens/ERC1155.sol | 257 --- lib/solmate/src/tokens/ERC20.sol | 206 -- lib/solmate/src/tokens/ERC6909.sol | 118 -- lib/solmate/src/tokens/ERC721.sol | 231 --- lib/solmate/src/tokens/WETH.sol | 35 - lib/solmate/src/utils/Bytes32AddressLib.sol | 14 - lib/solmate/src/utils/CREATE3.sol | 87 - lib/solmate/src/utils/FixedPointMathLib.sol | 255 --- lib/solmate/src/utils/LibString.sol | 77 - lib/solmate/src/utils/MerkleProofLib.sol | 48 - lib/solmate/src/utils/ReentrancyGuard.sol | 19 - lib/solmate/src/utils/SSTORE2.sol | 101 - lib/solmate/src/utils/SafeCastLib.sol | 193 -- lib/solmate/src/utils/SafeTransferLib.sol | 128 -- lib/solmate/src/utils/SignedWadMath.sol | 245 --- 275 files changed, 3 insertions(+), 61307 deletions(-) create mode 160000 lib/solady delete mode 100644 lib/solady/.gas-snapshot delete mode 100644 lib/solady/.github/pull_request_template.md delete mode 100644 lib/solady/.github/workflows/ci-all-via-ir.yml delete mode 100644 lib/solady/.github/workflows/ci-woke.yml delete mode 100644 lib/solady/.github/workflows/ci.yml delete mode 100644 lib/solady/.gitignore delete mode 100644 lib/solady/.gitmodules delete mode 100644 lib/solady/LICENSE.txt delete mode 100644 lib/solady/README.md delete mode 100644 lib/solady/audits/ackee-blockchain-solady-report.pdf delete mode 100644 lib/solady/audits/cantina-solady-report.pdf delete mode 100644 lib/solady/audits/shung-solady-erc721-audit.pdf delete mode 100644 lib/solady/ext/woke/EIP712Mock.sol delete mode 100644 lib/solady/ext/woke/ERC1155Mock.sol delete mode 100644 lib/solady/ext/woke/ERC20Mock.sol delete mode 100644 lib/solady/ext/woke/ERC721Mock.sol delete mode 100644 lib/solady/ext/woke/MerkleProofMock.sol delete mode 100644 lib/solady/ext/woke/NoETHMock.sol delete mode 100644 lib/solady/ext/woke/SignatureCheckerMock.sol delete mode 100644 lib/solady/ext/woke/__init__.py delete mode 100644 lib/solady/ext/woke/test_eip712.py delete mode 100644 lib/solady/ext/woke/test_eip712_fuzz.py delete mode 100644 lib/solady/ext/woke/test_erc1155.py delete mode 100644 lib/solady/ext/woke/test_erc1155_fuzz.py delete mode 100644 lib/solady/ext/woke/test_erc20.py delete mode 100644 lib/solady/ext/woke/test_erc721_fuzz.py delete mode 100644 lib/solady/ext/woke/test_merkle_proof.py delete mode 100644 lib/solady/ext/woke/test_merkle_proof_fuzz.py delete mode 100644 lib/solady/ext/woke/test_signature_checker_fuzz.py delete mode 100644 lib/solady/ext/woke/utils.py delete mode 100644 lib/solady/ext/woke/weird/Approval.sol delete mode 100644 lib/solady/ext/woke/weird/ApprovalToZero.sol delete mode 100644 lib/solady/ext/woke/weird/BlockList.sol delete mode 100644 lib/solady/ext/woke/weird/Bytes32Metadata.sol delete mode 100644 lib/solady/ext/woke/weird/DaiPermit.sol delete mode 100644 lib/solady/ext/woke/weird/ERC20.sol delete mode 100644 lib/solady/ext/woke/weird/HighDecimals.sol delete mode 100644 lib/solady/ext/woke/weird/LowDecimals.sol delete mode 100644 lib/solady/ext/woke/weird/MissingReturns.sol delete mode 100644 lib/solady/ext/woke/weird/NoRevert.sol delete mode 100644 lib/solady/ext/woke/weird/Pausable.sol delete mode 100644 lib/solady/ext/woke/weird/Proxied.sol delete mode 100644 lib/solady/ext/woke/weird/Reentrant.sol delete mode 100644 lib/solady/ext/woke/weird/ReturnsFalse.sol delete mode 100644 lib/solady/ext/woke/weird/RevertToZero.sol delete mode 100644 lib/solady/ext/woke/weird/RevertZero.sol delete mode 100644 lib/solady/ext/woke/weird/TransferFee.sol delete mode 100644 lib/solady/ext/woke/weird/Uint96.sol delete mode 100644 lib/solady/ext/woke/weird/Upgradable.sol delete mode 100644 lib/solady/ext/woke/woke-via-ir.toml delete mode 100644 lib/solady/ext/woke/woke.toml delete mode 100644 lib/solady/foundry.toml delete mode 100644 lib/solady/js/solady.d.ts delete mode 100644 lib/solady/js/solady.js delete mode 100644 lib/solady/js/solady.test.js delete mode 100644 lib/solady/lib/ds-test/.gitignore delete mode 100644 lib/solady/lib/ds-test/LICENSE delete mode 100644 lib/solady/lib/ds-test/Makefile delete mode 100644 lib/solady/lib/ds-test/default.nix delete mode 100644 lib/solady/lib/ds-test/demo/demo.sol delete mode 100644 lib/solady/lib/ds-test/src/test.sol delete mode 100644 lib/solady/logo.svg delete mode 100644 lib/solady/package-lock.json delete mode 100644 lib/solady/package.json delete mode 100644 lib/solady/src/Milady.sol delete mode 100644 lib/solady/src/accounts/ERC1271.sol delete mode 100644 lib/solady/src/accounts/ERC4337.sol delete mode 100644 lib/solady/src/accounts/ERC4337Factory.sol delete mode 100644 lib/solady/src/accounts/ERC6551.sol delete mode 100644 lib/solady/src/accounts/ERC6551Proxy.sol delete mode 100644 lib/solady/src/accounts/Receiver.sol delete mode 100644 lib/solady/src/auth/Ownable.sol delete mode 100644 lib/solady/src/auth/OwnableRoles.sol delete mode 100644 lib/solady/src/tokens/ERC1155.sol delete mode 100644 lib/solady/src/tokens/ERC20.sol delete mode 100644 lib/solady/src/tokens/ERC2981.sol delete mode 100644 lib/solady/src/tokens/ERC4626.sol delete mode 100644 lib/solady/src/tokens/ERC6909.sol delete mode 100644 lib/solady/src/tokens/ERC721.sol delete mode 100644 lib/solady/src/tokens/WETH.sol delete mode 100644 lib/solady/src/utils/Base64.sol delete mode 100644 lib/solady/src/utils/CREATE3.sol delete mode 100644 lib/solady/src/utils/Clone.sol delete mode 100644 lib/solady/src/utils/DateTimeLib.sol delete mode 100644 lib/solady/src/utils/DynamicBufferLib.sol delete mode 100644 lib/solady/src/utils/ECDSA.sol delete mode 100644 lib/solady/src/utils/EIP712.sol delete mode 100644 lib/solady/src/utils/ERC1967Factory.sol delete mode 100644 lib/solady/src/utils/ERC1967FactoryConstants.sol delete mode 100644 lib/solady/src/utils/FixedPointMathLib.sol delete mode 100644 lib/solady/src/utils/GasBurnerLib.sol delete mode 100644 lib/solady/src/utils/JSONParserLib.sol delete mode 100644 lib/solady/src/utils/LibBit.sol delete mode 100644 lib/solady/src/utils/LibBitmap.sol delete mode 100644 lib/solady/src/utils/LibClone.sol delete mode 100644 lib/solady/src/utils/LibMap.sol delete mode 100644 lib/solady/src/utils/LibPRNG.sol delete mode 100644 lib/solady/src/utils/LibRLP.sol delete mode 100644 lib/solady/src/utils/LibSort.sol delete mode 100644 lib/solady/src/utils/LibString.sol delete mode 100644 lib/solady/src/utils/LibZip.sol delete mode 100644 lib/solady/src/utils/MerkleProofLib.sol delete mode 100644 lib/solady/src/utils/MetadataReaderLib.sol delete mode 100644 lib/solady/src/utils/MinHeapLib.sol delete mode 100644 lib/solady/src/utils/Multicallable.sol delete mode 100644 lib/solady/src/utils/RedBlackTreeLib.sol delete mode 100644 lib/solady/src/utils/SSTORE2.sol delete mode 100644 lib/solady/src/utils/SafeCastLib.sol delete mode 100644 lib/solady/src/utils/SafeTransferLib.sol delete mode 100644 lib/solady/src/utils/SignatureCheckerLib.sol delete mode 100644 lib/solady/src/utils/UUPSUpgradeable.sol delete mode 100644 lib/solady/test/Base64.t.sol delete mode 100644 lib/solady/test/CREATE3.t.sol delete mode 100644 lib/solady/test/DateTimeLib.t.sol delete mode 100644 lib/solady/test/DynamicBufferLib.t.sol delete mode 100644 lib/solady/test/ECDSA.t.sol delete mode 100644 lib/solady/test/EIP712.t.sol delete mode 100644 lib/solady/test/ERC1155.t.sol delete mode 100644 lib/solady/test/ERC1967Factory.t.sol delete mode 100644 lib/solady/test/ERC20.t.sol delete mode 100644 lib/solady/test/ERC2981.t.sol delete mode 100644 lib/solady/test/ERC4337.t.sol delete mode 100644 lib/solady/test/ERC4337Factory.t.sol delete mode 100644 lib/solady/test/ERC4626.t.sol delete mode 100644 lib/solady/test/ERC6551.t.sol delete mode 100644 lib/solady/test/ERC6909.t.sol delete mode 100644 lib/solady/test/ERC721.t.sol delete mode 100644 lib/solady/test/FixedPointMathLib.t.sol delete mode 100644 lib/solady/test/GasBurnerLib.t.sol delete mode 100644 lib/solady/test/JSONParserLib.t.sol delete mode 100644 lib/solady/test/LibBit.t.sol delete mode 100644 lib/solady/test/LibBitmap.t.sol delete mode 100644 lib/solady/test/LibClone.t.sol delete mode 100644 lib/solady/test/LibMap.t.sol delete mode 100644 lib/solady/test/LibPRNG.t.sol delete mode 100644 lib/solady/test/LibRLP.t.sol delete mode 100644 lib/solady/test/LibSort.t.sol delete mode 100644 lib/solady/test/LibString.t.sol delete mode 100644 lib/solady/test/LibZip.t.sol delete mode 100644 lib/solady/test/MerkleProofLib.t.sol delete mode 100644 lib/solady/test/MetadataReaderLib.t.sol delete mode 100644 lib/solady/test/MinHeapLib.t.sol delete mode 100644 lib/solady/test/Multicallable.t.sol delete mode 100644 lib/solady/test/Ownable.t.sol delete mode 100644 lib/solady/test/OwnableRoles.t.sol delete mode 100644 lib/solady/test/README.md delete mode 100644 lib/solady/test/Receiver.t.sol delete mode 100644 lib/solady/test/RedBlackTree.t.sol delete mode 100644 lib/solady/test/SSTORE2.t.sol delete mode 100644 lib/solady/test/SafeCastLib.t.sol delete mode 100644 lib/solady/test/SafeTransferLib.t.sol delete mode 100644 lib/solady/test/SignatureCheckerLib.t.sol delete mode 100644 lib/solady/test/UUPSUpgradeable.t.sol delete mode 100644 lib/solady/test/WETH.t.sol delete mode 100644 lib/solady/test/utils/InvariantTest.sol delete mode 100644 lib/solady/test/utils/SoladyTest.sol delete mode 100644 lib/solady/test/utils/TestPlus.sol delete mode 100644 lib/solady/test/utils/forge-std/Script.sol delete mode 100644 lib/solady/test/utils/forge-std/Test.sol delete mode 100644 lib/solady/test/utils/forge-std/Vm.sol delete mode 100644 lib/solady/test/utils/forge-std/console.sol delete mode 100644 lib/solady/test/utils/mocks/MockCd.sol delete mode 100644 lib/solady/test/utils/mocks/MockEIP712.sol delete mode 100644 lib/solady/test/utils/mocks/MockEIP712Dynamic.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC1155.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC1271Malicious.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC1271Wallet.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC20.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC20LikeUSDT.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC2981.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC4337.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC4626.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC6551.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC6551Registry.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC6909.sol delete mode 100644 lib/solady/test/utils/mocks/MockERC721.sol delete mode 100644 lib/solady/test/utils/mocks/MockETHRecipient.sol delete mode 100644 lib/solady/test/utils/mocks/MockEntryPoint.sol delete mode 100644 lib/solady/test/utils/mocks/MockImplementation.sol delete mode 100644 lib/solady/test/utils/mocks/MockMulticallable.sol delete mode 100644 lib/solady/test/utils/mocks/MockOwnable.sol delete mode 100644 lib/solady/test/utils/mocks/MockOwnableRoles.sol delete mode 100644 lib/solady/test/utils/mocks/MockReceiver.sol delete mode 100644 lib/solady/test/utils/mocks/MockUUPSImplementation.sol delete mode 100644 lib/solady/test/utils/weird-tokens/MissingReturnToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/ReturnsFalseToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/ReturnsRawBytesToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/ReturnsTooLittleToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/ReturnsTooMuchToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/ReturnsTwoToken.sol delete mode 100644 lib/solady/test/utils/weird-tokens/RevertingToken.sol create mode 160000 lib/solmate delete mode 100644 lib/solmate/.gas-snapshot delete mode 100644 lib/solmate/.gitattributes delete mode 100644 lib/solmate/.github/pull_request_template.md delete mode 100644 lib/solmate/.github/workflows/tests.yml delete mode 100644 lib/solmate/.gitignore delete mode 100644 lib/solmate/.gitmodules delete mode 100644 lib/solmate/.prettierignore delete mode 100644 lib/solmate/.prettierrc delete mode 100644 lib/solmate/.vscode/settings.json delete mode 100644 lib/solmate/LICENSE delete mode 100644 lib/solmate/README.md delete mode 100644 lib/solmate/audits/v6-Fixed-Point-Solutions.pdf delete mode 100644 lib/solmate/foundry.toml delete mode 100644 lib/solmate/lib/ds-test/.gitignore delete mode 100644 lib/solmate/lib/ds-test/LICENSE delete mode 100644 lib/solmate/lib/ds-test/Makefile delete mode 100644 lib/solmate/lib/ds-test/default.nix delete mode 100644 lib/solmate/lib/ds-test/demo/demo.sol delete mode 100644 lib/solmate/lib/ds-test/package.json delete mode 100644 lib/solmate/lib/ds-test/src/test.sol delete mode 100644 lib/solmate/package-lock.json delete mode 100644 lib/solmate/package.json delete mode 100644 lib/solmate/src/auth/Auth.sol delete mode 100644 lib/solmate/src/auth/Owned.sol delete mode 100644 lib/solmate/src/auth/authorities/MultiRolesAuthority.sol delete mode 100644 lib/solmate/src/auth/authorities/RolesAuthority.sol delete mode 100644 lib/solmate/src/mixins/ERC4626.sol delete mode 100644 lib/solmate/src/test/Auth.t.sol delete mode 100644 lib/solmate/src/test/Bytes32AddressLib.t.sol delete mode 100644 lib/solmate/src/test/CREATE3.t.sol delete mode 100644 lib/solmate/src/test/DSTestPlus.t.sol delete mode 100644 lib/solmate/src/test/ERC1155.t.sol delete mode 100644 lib/solmate/src/test/ERC20.t.sol delete mode 100644 lib/solmate/src/test/ERC4626.t.sol delete mode 100644 lib/solmate/src/test/ERC6909.t.sol delete mode 100644 lib/solmate/src/test/ERC721.t.sol delete mode 100644 lib/solmate/src/test/FixedPointMathLib.t.sol delete mode 100644 lib/solmate/src/test/LibString.t.sol delete mode 100644 lib/solmate/src/test/MerkleProofLib.t.sol delete mode 100644 lib/solmate/src/test/MultiRolesAuthority.t.sol delete mode 100644 lib/solmate/src/test/Owned.t.sol delete mode 100644 lib/solmate/src/test/ReentrancyGuard.t.sol delete mode 100644 lib/solmate/src/test/RolesAuthority.t.sol delete mode 100644 lib/solmate/src/test/SSTORE2.t.sol delete mode 100644 lib/solmate/src/test/SafeCastLib.t.sol delete mode 100644 lib/solmate/src/test/SafeTransferLib.t.sol delete mode 100644 lib/solmate/src/test/SignedWadMath.t.sol delete mode 100644 lib/solmate/src/test/WETH.t.sol delete mode 100644 lib/solmate/src/test/utils/DSInvariantTest.sol delete mode 100644 lib/solmate/src/test/utils/DSTestPlus.sol delete mode 100644 lib/solmate/src/test/utils/Hevm.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockAuthChild.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockAuthority.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockERC1155.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockERC20.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockERC4626.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockERC6909.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockERC721.sol delete mode 100644 lib/solmate/src/test/utils/mocks/MockOwned.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol delete mode 100644 lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol delete mode 100644 lib/solmate/src/tokens/ERC1155.sol delete mode 100644 lib/solmate/src/tokens/ERC20.sol delete mode 100644 lib/solmate/src/tokens/ERC6909.sol delete mode 100644 lib/solmate/src/tokens/ERC721.sol delete mode 100644 lib/solmate/src/tokens/WETH.sol delete mode 100644 lib/solmate/src/utils/Bytes32AddressLib.sol delete mode 100644 lib/solmate/src/utils/CREATE3.sol delete mode 100644 lib/solmate/src/utils/FixedPointMathLib.sol delete mode 100644 lib/solmate/src/utils/LibString.sol delete mode 100644 lib/solmate/src/utils/MerkleProofLib.sol delete mode 100644 lib/solmate/src/utils/ReentrancyGuard.sol delete mode 100644 lib/solmate/src/utils/SSTORE2.sol delete mode 100644 lib/solmate/src/utils/SafeCastLib.sol delete mode 100644 lib/solmate/src/utils/SafeTransferLib.sol delete mode 100644 lib/solmate/src/utils/SignedWadMath.sol diff --git a/.gitmodules b/.gitmodules index f498e23..66a4cff 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ url = https://github.com/transmissions11/solmate [submodule "lib/solady"] path = lib/solady - url = https://github.com/vectorized/solady \ No newline at end of file + url = https://github.com/vectorized/solady diff --git a/lib/solady b/lib/solady new file mode 160000 index 0000000..fad3f67 --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit fad3f6703c2cd4cfd185f9921790d117503f54c6 diff --git a/lib/solady/.gas-snapshot b/lib/solady/.gas-snapshot deleted file mode 100644 index 427cbaa..0000000 --- a/lib/solady/.gas-snapshot +++ /dev/null @@ -1,1052 +0,0 @@ -Base64Test:testBase64DecodeSentenceGas() (gas: 3663) -Base64Test:testBase64DecodeShortStringGas() (gas: 919) -Base64Test:testBase64EncodeDecode(bytes) (runs: 256, μ: 13919, ~: 10697) -Base64Test:testBase64EncodeDecodeAltModes(bytes) (runs: 256, μ: 834983, ~: 764290) -Base64Test:testBase64EncodeEmptyString() (gas: 953) -Base64Test:testBase64EncodeFileSafeAndNoPadding(bytes,bool,bool) (runs: 256, μ: 16092, ~: 13634) -Base64Test:testBase64EncodeSentence() (gas: 4757) -Base64Test:testBase64EncodeShortStrings() (gas: 8496) -Base64Test:testBase64EncodeToStringWithDoublePadding() (gas: 1702) -Base64Test:testBase64EncodeToStringWithNoPadding() (gas: 1680) -Base64Test:testBase64EncodeToStringWithSinglePadding() (gas: 1636) -Base64Test:testBase64WordBoundary() (gas: 12511) -Base64Test:test__codesize() (gas: 7802) -CREATE3Test:testDeployERC20() (gas: 761926) -CREATE3Test:testDeployERC20(bytes32,string,string,uint8) (runs: 256, μ: 805598, ~: 808154) -CREATE3Test:testDeployedUpperBitsSafeForPlainSolidity() (gas: 657) -CREATE3Test:testDoubleDeployDifferentBytecodeReverts() (gas: 96899279) -CREATE3Test:testDoubleDeployDifferentBytecodeReverts(bytes32,bytes,bytes) (runs: 256, μ: 96880132, ~: 96880005) -CREATE3Test:testDoubleDeploySameBytecodeReverts() (gas: 96889159) -CREATE3Test:testDoubleDeploySameBytecodeReverts(bytes32,bytes) (runs: 256, μ: 96881054, ~: 96881007) -CREATE3Test:test__codesize() (gas: 15337) -DateTimeLibTest:testAddSubDiffDays(uint256,uint256) (runs: 256, μ: 4033, ~: 4074) -DateTimeLibTest:testAddSubDiffHours(uint256,uint256) (runs: 256, μ: 3976, ~: 3932) -DateTimeLibTest:testAddSubDiffMinutes(uint256,uint256) (runs: 256, μ: 3974, ~: 3939) -DateTimeLibTest:testAddSubDiffMonths(uint256,uint256) (runs: 256, μ: 7084, ~: 7075) -DateTimeLibTest:testAddSubDiffSeconds(uint256,uint256) (runs: 256, μ: 3617, ~: 3586) -DateTimeLibTest:testAddSubDiffYears(uint256,uint256) (runs: 256, μ: 6648, ~: 6640) -DateTimeLibTest:testDateTimeArithmeticReverts() (gas: 4597) -DateTimeLibTest:testDateTimeMaxSupported() (gas: 2502) -DateTimeLibTest:testDateTimeToAndFroTimestamp((uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 3971, ~: 4050) -DateTimeLibTest:testDateToAndFroEpochDay((uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2466, ~: 2440) -DateTimeLibTest:testDateToAndFroEpochDay() (gas: 865464) -DateTimeLibTest:testDateToAndFroTimestamp() (gas: 909205) -DateTimeLibTest:testDateToEpochDay() (gas: 1603) -DateTimeLibTest:testDateToEpochDayDifferential((uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2209, ~: 2178) -DateTimeLibTest:testDateToEpochDayDifferential2((uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2131, ~: 2105) -DateTimeLibTest:testDateToEpochDayGas() (gas: 756479) -DateTimeLibTest:testDateToEpochDayGas2() (gas: 759345) -DateTimeLibTest:testDayOfWeek() (gas: 175297) -DateTimeLibTest:testDaysInMonth() (gas: 1204) -DateTimeLibTest:testDaysInMonth(uint256,uint256) (runs: 256, μ: 1033, ~: 1051) -DateTimeLibTest:testDaysToDate() (gas: 1958) -DateTimeLibTest:testEpochDayToDate(uint256) (runs: 256, μ: 1019, ~: 1019) -DateTimeLibTest:testEpochDayToDateDifferential(uint256) (runs: 256, μ: 1812, ~: 1762) -DateTimeLibTest:testEpochDayToDateDifferential2(uint256) (runs: 256, μ: 1750, ~: 1683) -DateTimeLibTest:testEpochDayToDateGas() (gas: 339723) -DateTimeLibTest:testEpochDayToDateGas2() (gas: 357444) -DateTimeLibTest:testIsLeapYear() (gas: 741) -DateTimeLibTest:testIsLeapYear(uint256) (runs: 256, μ: 521, ~: 495) -DateTimeLibTest:testIsSupportedDateFalse() (gas: 1651) -DateTimeLibTest:testIsSupportedDateTime((uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2830, ~: 2842) -DateTimeLibTest:testIsSupportedDateTrue() (gas: 670) -DateTimeLibTest:testIsSupportedEpochDayFalse() (gas: 597) -DateTimeLibTest:testIsSupportedEpochDayTrue() (gas: 305) -DateTimeLibTest:testIsSupportedTimestampFalse() (gas: 599) -DateTimeLibTest:testIsSupportedTimestampTrue() (gas: 304) -DateTimeLibTest:testIsWeekEnd(uint256) (runs: 256, μ: 727, ~: 662) -DateTimeLibTest:testMondayTimestamp() (gas: 1106) -DateTimeLibTest:testMondayTimestamp(uint256) (runs: 256, μ: 733, ~: 814) -DateTimeLibTest:testNthWeekdayInMonthOfYearTimestamp() (gas: 12031) -DateTimeLibTest:testNthWeekdayInMonthOfYearTimestamp(uint256,uint256,uint256,uint256) (runs: 256, μ: 3462, ~: 3494) -DateTimeLibTest:testWeekday() (gas: 705) -DateTimeLibTest:test__codesize() (gas: 20051) -DynamicBufferLibTest:testClear(uint256) (runs: 256, μ: 171012, ~: 171193) -DynamicBufferLibTest:testDynamicBuffer(bytes[],uint256) (runs: 256, μ: 1032333, ~: 903881) -DynamicBufferLibTest:testDynamicBuffer(uint256) (runs: 256, μ: 1036837, ~: 737806) -DynamicBufferLibTest:testDynamicBufferChaining() (gas: 22178) -DynamicBufferLibTest:testDynamicBufferReserveFromEmpty() (gas: 2781) -DynamicBufferLibTest:testDynamicBufferReserveFromEmpty2() (gas: 2423) -DynamicBufferLibTest:testDynamicBufferReserveFromEmpty3(bytes,uint256) (runs: 256, μ: 4205, ~: 2068) -DynamicBufferLibTest:testJoinWithConcat() (gas: 31407) -DynamicBufferLibTest:testJoinWithDynamicBuffer() (gas: 10764) -DynamicBufferLibTest:test__codesize() (gas: 11003) -ECDSATest:testBytes32ToEthSignedMessageHash() (gas: 381) -ECDSATest:testBytesToEthSignedMessageHash() (gas: 11582079) -ECDSATest:testBytesToEthSignedMessageHashEmpty() (gas: 556) -ECDSATest:testBytesToEthSignedMessageHashExceedsMaxLengthReverts() (gas: 608392) -ECDSATest:testBytesToEthSignedMessageHashLong() (gas: 681) -ECDSATest:testBytesToEthSignedMessageHashShort() (gas: 620) -ECDSATest:testEmptyCalldataHelpers() (gas: 3253) -ECDSATest:testRecoverAndTryRecover(bytes32) (runs: 256, μ: 2788434, ~: 2507144) -ECDSATest:testRecoverWithInvalidLongSignatureReverts() (gas: 7377) -ECDSATest:testRecoverWithInvalidShortSignatureReturnsZero() (gas: 7168) -ECDSATest:testRecoverWithInvalidSignatureReverts() (gas: 7955) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4567) -ECDSATest:testRecoverWithV0SignatureWithShortEIP2098FormatAsCalldata() (gas: 8712) -ECDSATest:testRecoverWithV0SignatureWithVersion00Reverts() (gas: 7898) -ECDSATest:testRecoverWithV0SignatureWithVersion27() (gas: 8812) -ECDSATest:testRecoverWithV0SignatureWithWrongVersionReverts() (gas: 7876) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098Format() (gas: 4611) -ECDSATest:testRecoverWithV1SignatureWithShortEIP2098FormatAsCalldata() (gas: 8644) -ECDSATest:testRecoverWithV1SignatureWithVersion01Reverts() (gas: 7910) -ECDSATest:testRecoverWithV1SignatureWithVersion28() (gas: 8813) -ECDSATest:testRecoverWithV1SignatureWithWrongVersionReverts() (gas: 7877) -ECDSATest:testRecoverWithValidSignature() (gas: 8938) -ECDSATest:testRecoverWithWrongSigner() (gas: 8829) -ECDSATest:testTryRecoverWithInvalidLongSignatureReturnsZero() (gas: 5223) -ECDSATest:testTryRecoverWithInvalidShortSignatureReturnsZero() (gas: 5049) -ECDSATest:testTryRecoverWithInvalidSignature() (gas: 8924) -ECDSATest:testTryRecoverWithV0SignatureWithShortEIP2098Format() (gas: 4556) -ECDSATest:testTryRecoverWithV0SignatureWithShortEIP2098FormatAsCalldata() (gas: 8713) -ECDSATest:testTryRecoverWithV0SignatureWithVersion00ReturnsZero() (gas: 8809) -ECDSATest:testTryRecoverWithV0SignatureWithVersion27() (gas: 8832) -ECDSATest:testTryRecoverWithV0SignatureWithWrongVersionReturnsZero() (gas: 8832) -ECDSATest:testTryRecoverWithV1SignatureWithShortEIP2098Format() (gas: 4610) -ECDSATest:testTryRecoverWithV1SignatureWithShortEIP2098FormatAsCalldata() (gas: 8735) -ECDSATest:testTryRecoverWithV1SignatureWithVersion01ReturnsZero() (gas: 8853) -ECDSATest:testTryRecoverWithV1SignatureWithVersion28() (gas: 8832) -ECDSATest:testTryRecoverWithV1SignatureWithWrongVersionReturnsZero() (gas: 8831) -ECDSATest:testTryRecoverWithValidSignature() (gas: 8901) -ECDSATest:testTryRecoverWithWrongSigner() (gas: 8827) -ECDSATest:test__codesize() (gas: 15480) -EIP712Test:testDomainSeparator() (gas: 6026) -EIP712Test:testDomainSeparatorOnClone() (gas: 8842) -EIP712Test:testDomainSeparatorOnCloneDynamicWithChainIdChange() (gas: 69650) -EIP712Test:testDomainSeparatorOnCloneWithChainIdChange() (gas: 13334) -EIP712Test:testDomainSeparatorOnDynamicWithChainIdChange() (gas: 29288) -EIP712Test:testDomainSeparatorWithChainIdChange() (gas: 10391) -EIP712Test:testEIP5267() (gas: 35389) -EIP712Test:testHashTypedData() (gas: 37210) -EIP712Test:testHashTypedDataOnClone() (gas: 40216) -EIP712Test:testHashTypedDataOnCloneDynamic() (gas: 46957) -EIP712Test:testHashTypedDataOnCloneDynamicWithChaindIdChange() (gas: 58250) -EIP712Test:testHashTypedDataOnCloneWithChaindIdChange() (gas: 49558) -EIP712Test:testHashTypedDataOnDynamic() (gas: 44865) -EIP712Test:testHashTypedDataOnDynamicWithChaindIdChange() (gas: 57357) -EIP712Test:testHashTypedDataWithChaindIdChange() (gas: 45775) -EIP712Test:test__codesize() (gas: 13881) -ERC1155HooksTest:testERC1155Hooks() (gas: 4396277) -ERC1155HooksTest:test__codesize() (gas: 12073) -ERC1155Test:testApproveAll(address,bool) (runs: 256, μ: 50193, ~: 58576) -ERC1155Test:testAuthorizedEquivalence(address,address,bool) (runs: 256, μ: 715, ~: 715) -ERC1155Test:testBalanceOfBatchWithArrayMismatchReverts(uint256) (runs: 256, μ: 32318, ~: 34937) -ERC1155Test:testBatchBalanceOf(uint256) (runs: 256, μ: 114503, ~: 93582) -ERC1155Test:testBatchBurn(uint256) (runs: 256, μ: 175450, ~: 161695) -ERC1155Test:testBatchBurnInsufficientBalanceReverts(uint256) (runs: 256, μ: 168826, ~: 173551) -ERC1155Test:testBatchBurnWithArrayLengthMismatchReverts(uint256) (runs: 256, μ: 43075, ~: 42595) -ERC1155Test:testBatchMintToEOA(uint256) (runs: 256, μ: 126044, ~: 148527) -ERC1155Test:testBatchMintToERC1155Recipient(uint256) (runs: 256, μ: 776209, ~: 776871) -ERC1155Test:testBatchMintToNonERC1155RecipientReverts(uint256) (runs: 256, μ: 172369, ~: 185840) -ERC1155Test:testBatchMintToRevertingERC1155RecipientReverts(uint256) (runs: 256, μ: 321976, ~: 315851) -ERC1155Test:testBatchMintToWrongReturnDataERC1155RecipientReverts(uint256) (runs: 256, μ: 293927, ~: 318698) -ERC1155Test:testBatchMintToZeroReverts(uint256) (runs: 256, μ: 73027, ~: 64999) -ERC1155Test:testBatchMintWithArrayMismatchReverts(uint256) (runs: 256, μ: 33385, ~: 35512) -ERC1155Test:testBurn(uint256) (runs: 256, μ: 91097, ~: 82321) -ERC1155Test:testBurnInsufficientBalanceReverts(uint256) (runs: 256, μ: 97148, ~: 97538) -ERC1155Test:testDirectSetApprovalForAll(address,address,bool) (runs: 256, μ: 24436, ~: 15497) -ERC1155Test:testMintToEOA(uint256) (runs: 256, μ: 72096, ~: 71717) -ERC1155Test:testMintToERC1155Recipient(uint256) (runs: 256, μ: 672296, ~: 653975) -ERC1155Test:testMintToNonERC155RecipientReverts(uint256) (runs: 256, μ: 103261, ~: 103225) -ERC1155Test:testMintToRevertingERC155RecipientReverts(uint256) (runs: 256, μ: 281517, ~: 281563) -ERC1155Test:testMintToWrongReturnDataERC155RecipientReverts(uint256) (runs: 256, μ: 235945, ~: 235934) -ERC1155Test:testMintToZeroReverts(uint256) (runs: 256, μ: 33088, ~: 33061) -ERC1155Test:testSafeBatchTransfer() (gas: 8330001) -ERC1155Test:testSafeBatchTransferFromToEOA(uint256) (runs: 256, μ: 208325, ~: 189416) -ERC1155Test:testSafeBatchTransferFromToERC1155Recipient(uint256) (runs: 256, μ: 883187, ~: 884928) -ERC1155Test:testSafeBatchTransferFromToNonERC1155RecipientReverts(uint256) (runs: 256, μ: 254320, ~: 268256) -ERC1155Test:testSafeBatchTransferFromToRevertingERC1155RecipientReverts(uint256) (runs: 256, μ: 432345, ~: 446565) -ERC1155Test:testSafeBatchTransferFromToWrongReturnDataERC1155RecipientReverts(uint256) (runs: 256, μ: 347280, ~: 330274) -ERC1155Test:testSafeBatchTransferFromToZeroReverts(uint256) (runs: 256, μ: 140880, ~: 124791) -ERC1155Test:testSafeBatchTransferFromWithArrayLengthMismatchReverts(uint256) (runs: 256, μ: 51206, ~: 66621) -ERC1155Test:testSafeBatchTransferInsufficientBalanceReverts(uint256) (runs: 256, μ: 170775, ~: 173686) -ERC1155Test:testSafeTransferFromInsufficientBalanceReverts(uint256) (runs: 256, μ: 99216, ~: 99806) -ERC1155Test:testSafeTransferFromSelf(uint256) (runs: 256, μ: 106266, ~: 105774) -ERC1155Test:testSafeTransferFromSelfInsufficientBalanceReverts(uint256) (runs: 256, μ: 71408, ~: 72157) -ERC1155Test:testSafeTransferFromToEOA(uint256) (runs: 256, μ: 117984, ~: 111719) -ERC1155Test:testSafeTransferFromToERC1155Recipient(uint256) (runs: 256, μ: 757271, ~: 777302) -ERC1155Test:testSafeTransferFromToNonERC155RecipientReverts(uint256) (runs: 256, μ: 140466, ~: 139762) -ERC1155Test:testSafeTransferFromToRevertingERC1155RecipientReverts(uint256) (runs: 256, μ: 317084, ~: 318526) -ERC1155Test:testSafeTransferFromToWrongReturnDataERC1155RecipientReverts(uint256) (runs: 256, μ: 272852, ~: 272482) -ERC1155Test:testSafeTransferFromToZeroReverts(uint256) (runs: 256, μ: 70843, ~: 70216) -ERC1155Test:test__codesize() (gas: 43462) -ERC1967FactoryTest:testChangeAdmin() (gas: 266356) -ERC1967FactoryTest:testChangeAdminUnauthorized() (gas: 257236) -ERC1967FactoryTest:testDeploy() (gas: 257363) -ERC1967FactoryTest:testDeployAndCall(uint256) (runs: 256, μ: 339688, ~: 340911) -ERC1967FactoryTest:testDeployAndCallWithRevert() (gas: 211881) -ERC1967FactoryTest:testDeployBrutalized(uint256) (runs: 256, μ: 116015, ~: 44119) -ERC1967FactoryTest:testDeployDeterministicAndCall(uint256) (runs: 256, μ: 313913, ~: 350129) -ERC1967FactoryTest:testFactoryDeployment() (gas: 856479) -ERC1967FactoryTest:testProxyFails() (gas: 259019) -ERC1967FactoryTest:testProxySucceeds() (gas: 255707) -ERC1967FactoryTest:testUpgrade() (gas: 266792) -ERC1967FactoryTest:testUpgradeAndCall() (gas: 354415) -ERC1967FactoryTest:testUpgradeAndCallWithRevert() (gas: 265645) -ERC1967FactoryTest:testUpgradeUnauthorized() (gas: 270260) -ERC1967FactoryTest:testUpgradeWithCorruptedProxy() (gas: 263163) -ERC1967FactoryTest:test__codesize() (gas: 33920) -ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 3840, reverts: 2351) -ERC20Invariants:test__codesize() (gas: 8050) -ERC20Test:testApprove() (gas: 35730) -ERC20Test:testApprove(address,uint256) (runs: 256, μ: 29937, ~: 31181) -ERC20Test:testBurn() (gas: 61920) -ERC20Test:testBurn(address,uint256,uint256) (runs: 256, μ: 60240, ~: 62469) -ERC20Test:testBurnInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 55227, ~: 56454) -ERC20Test:testDirectSpendAllowance(uint256) (runs: 256, μ: 61647, ~: 61904) -ERC20Test:testDirectTransfer(uint256) (runs: 256, μ: 100856, ~: 111918) -ERC20Test:testInfiniteApproveTransferFrom() (gas: 89993) -ERC20Test:testMetadata() (gas: 17598) -ERC20Test:testMint() (gas: 58668) -ERC20Test:testMint(address,uint256) (runs: 256, μ: 56402, ~: 58890) -ERC20Test:testMintOverMaxUintReverts() (gas: 55753) -ERC20Test:testPermit() (gas: 89727) -ERC20Test:testPermit(uint256) (runs: 256, μ: 89575, ~: 89659) -ERC20Test:testPermitBadDeadlineReverts(uint256) (runs: 256, μ: 41403, ~: 41421) -ERC20Test:testPermitBadNonceReverts(uint256) (runs: 256, μ: 41837, ~: 41787) -ERC20Test:testPermitPastDeadlineReverts(uint256) (runs: 256, μ: 35995, ~: 35987) -ERC20Test:testPermitReplayReverts(uint256) (runs: 256, μ: 92277, ~: 92351) -ERC20Test:testTransfer() (gas: 65298) -ERC20Test:testTransfer(address,uint256) (runs: 256, μ: 63026, ~: 65514) -ERC20Test:testTransferFrom() (gas: 85633) -ERC20Test:testTransferFrom(address,address,address,uint256,uint256) (runs: 256, μ: 89459, ~: 94758) -ERC20Test:testTransferFromInsufficientAllowanceReverts() (gas: 81443) -ERC20Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256) (runs: 256, μ: 81380, ~: 81983) -ERC20Test:testTransferFromInsufficientBalanceReverts() (gas: 61836) -ERC20Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 64833, ~: 62492) -ERC20Test:testTransferInsufficientBalanceReverts() (gas: 55919) -ERC20Test:testTransferInsufficientBalanceReverts(address,uint256,uint256) (runs: 256, μ: 55201, ~: 56424) -ERC20Test:test__codesize() (gas: 24081) -ERC2981Test:testRoyaltyOverflowCheckDifferential(uint256,uint256) (runs: 256, μ: 456, ~: 458) -ERC2981Test:testSetAndGetRoyaltyInfo(uint256) (runs: 256, μ: 107958, ~: 104834) -ERC2981Test:test__codesize() (gas: 8419) -ERC4337FactoryTest:testCreateAccountRepeatedDeployment() (gas: 149757) -ERC4337FactoryTest:testCreateAccountRepeatedDeployment(uint256) (runs: 256, μ: 171753, ~: 171617) -ERC4337FactoryTest:testDeployDeterministic(uint256) (runs: 256, μ: 134818, ~: 139900) -ERC4337FactoryTest:test__codesize() (gas: 13197) -ERC4337Test:testCdFallback() (gas: 443962) -ERC4337Test:testCdFallback2() (gas: 1140793) -ERC4337Test:testDelegateExecute() (gas: 369570) -ERC4337Test:testDelegateExecute(uint256) (runs: 256, μ: 356052, ~: 344533) -ERC4337Test:testDelegateExecuteRevertsIfOwnerSlotValueChanged() (gas: 319282) -ERC4337Test:testDepositFunctions() (gas: 502928) -ERC4337Test:testDirectStorage() (gas: 70391) -ERC4337Test:testETHReceived() (gas: 16584) -ERC4337Test:testExecute() (gas: 382774) -ERC4337Test:testExecuteBatch() (gas: 692605) -ERC4337Test:testExecuteBatch(uint256) (runs: 256, μ: 528646, ~: 668616) -ERC4337Test:testInitializer() (gas: 285192) -ERC4337Test:testIsValidSignature() (gas: 111663) -ERC4337Test:testIsValidSignaturePersonalSign() (gas: 96270) -ERC4337Test:testIsValidSignatureWrapped() (gas: 406706) -ERC4337Test:testOnERC1155BatchReceived() (gas: 1393788) -ERC4337Test:testOnERC1155Received() (gas: 1391176) -ERC4337Test:testOnERC721Received() (gas: 1311273) -ERC4337Test:testOwnerRecovery() (gas: 486056) -ERC4337Test:testValidateUserOp() (gas: 491506) -ERC4337Test:test__codesize() (gas: 54000) -ERC4626Test:testDepositWithNoApprovalReverts() (gas: 16371) -ERC4626Test:testDepositWithNotEnoughApprovalReverts() (gas: 89884) -ERC4626Test:testDifferentialFullMulDiv(uint256,uint256,uint256) (runs: 256, μ: 3343, ~: 3201) -ERC4626Test:testMetadata() (gas: 15439) -ERC4626Test:testMintWithNoApprovalReverts() (gas: 16345) -ERC4626Test:testMintZero() (gas: 54317) -ERC4626Test:testMultipleMintDepositRedeemWithdraw() (gas: 425667) -ERC4626Test:testRedeemWithNoShareAmountReverts() (gas: 10918) -ERC4626Test:testRedeemWithNotEnoughShareAmountReverts() (gas: 142915) -ERC4626Test:testSingleDepositWithdraw(uint128) (runs: 256, μ: 202683, ~: 202694) -ERC4626Test:testSingleMintRedeem(uint128) (runs: 256, μ: 201604, ~: 201615) -ERC4626Test:testTryGetAssetDecimals() (gas: 30490416) -ERC4626Test:testUseVirtualShares() (gas: 2442168) -ERC4626Test:testVaultInteractionsForSomeoneElse() (gas: 296189) -ERC4626Test:testVirtualSharesMultipleMintDepositRedeemWithdraw() (gas: 1638354) -ERC4626Test:testWithdrawWithNoUnderlyingAmountReverts() (gas: 13102) -ERC4626Test:testWithdrawWithNotEnoughUnderlyingAmountReverts() (gas: 144082) -ERC4626Test:testWithdrawZero() (gas: 52807) -ERC4626Test:test__codesize() (gas: 41081) -ERC6551Test:testCdFallback() (gas: 894557) -ERC6551Test:testDeployERC6551(uint256) (runs: 256, μ: 170988, ~: 168739) -ERC6551Test:testDeployERC6551Proxy() (gas: 102497) -ERC6551Test:testExecute() (gas: 507843) -ERC6551Test:testExecuteBatch() (gas: 816977) -ERC6551Test:testExecuteBatch(uint256) (runs: 256, μ: 606828, ~: 483186) -ERC6551Test:testInitializeERC6551ProxyImplementation() (gas: 189801) -ERC6551Test:testIsValidSignature() (gas: 187612) -ERC6551Test:testOnERC1155BatchReceived() (gas: 1526542) -ERC6551Test:testOnERC1155Received() (gas: 1523898) -ERC6551Test:testOnERC721Received() (gas: 1447973) -ERC6551Test:testOnERC721ReceivedCycles() (gas: 1710837) -ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 256, μ: 449186, ~: 455552) -ERC6551Test:testSupportsInterface() (gas: 169387) -ERC6551Test:testUpgrade() (gas: 1154845) -ERC6551Test:test__codesize() (gas: 47900) -ERC6909Test:testApprove() (gas: 36738) -ERC6909Test:testApprove(address,uint256,uint256) (runs: 256, μ: 36369, ~: 37380) -ERC6909Test:testBurn() (gas: 40643) -ERC6909Test:testBurn(address,uint256,uint256,uint256) (runs: 256, μ: 39876, ~: 41154) -ERC6909Test:testBurnInsufficientBalanceReverts(address,uint256,uint256,uint256) (runs: 256, μ: 34112, ~: 34972) -ERC6909Test:testDecimals() (gas: 5632) -ERC6909Test:testDirectApprove() (gas: 36700) -ERC6909Test:testDirectFunctions(uint256) (runs: 256, μ: 199858, ~: 201362) -ERC6909Test:testDirectSetOperator() (gas: 35937) -ERC6909Test:testDirectTransfer() (gas: 65833) -ERC6909Test:testInfiniteApproveTransferFrom() (gas: 78062) -ERC6909Test:testMetadata() (gas: 9332) -ERC6909Test:testMint() (gas: 36905) -ERC6909Test:testMint(address,uint256,uint256) (runs: 256, μ: 36208, ~: 37141) -ERC6909Test:testMintOverMaxUintReverts() (gas: 34388) -ERC6909Test:testMintOverMaxUintReverts(address,uint256,uint256,uint256) (runs: 256, μ: 35296, ~: 35348) -ERC6909Test:testOperatorTransferFrom() (gas: 73755) -ERC6909Test:testSetOperator() (gas: 37020) -ERC6909Test:testSetOperator(address,address,bool) (runs: 256, μ: 25706, ~: 16767) -ERC6909Test:testTokenURI() (gas: 33117) -ERC6909Test:testTransfer() (gas: 48028) -ERC6909Test:testTransfer(address,uint256,uint256) (runs: 256, μ: 46126, ~: 47169) -ERC6909Test:testTransferFrom() (gas: 74272) -ERC6909Test:testTransferFrom(address,address,address,uint256,uint256,uint256) (runs: 256, μ: 76831, ~: 80611) -ERC6909Test:testTransferFromCallerIsNotOperator(address,uint256,uint256) (runs: 256, μ: 39318, ~: 39315) -ERC6909Test:testTransferFromInsufficientAllowanceReverts(address,uint256,uint256,uint256) (runs: 256, μ: 66020, ~: 67346) -ERC6909Test:testTransferFromInsufficientBalanceReverts() (gas: 49521) -ERC6909Test:testTransferFromInsufficientBalanceReverts(address,uint256,uint256,uint256) (runs: 256, μ: 51956, ~: 49764) -ERC6909Test:testTransferFromInsufficientPermission() (gas: 66821) -ERC6909Test:testTransferFromOverMaxUintReverts() (gas: 89920) -ERC6909Test:testTransferFromOverMaxUintReverts(address,uint256,uint256,uint256) (runs: 256, μ: 112328, ~: 112849) -ERC6909Test:testTransferInsufficientBalanceReverts() (gas: 34534) -ERC6909Test:testTransferInsufficientBalanceReverts(address,uint256,uint256,uint256) (runs: 256, μ: 34680, ~: 35076) -ERC6909Test:testTransferOverMaxUintReverts() (gas: 63438) -ERC6909Test:testTransferOverMaxUintReverts(address,uint256,uint256,uint256) (runs: 256, μ: 63907, ~: 63923) -ERC6909Test:test__codesize() (gas: 26423) -ERC721HooksTest:testERC721Hooks() (gas: 2877778) -ERC721HooksTest:test__codesize() (gas: 9707) -ERC721Test:testApprove(uint256) (runs: 256, μ: 108103, ~: 108158) -ERC721Test:testApproveAll(uint256) (runs: 256, μ: 47875, ~: 40312) -ERC721Test:testApproveBurn(uint256) (runs: 256, μ: 86754, ~: 86771) -ERC721Test:testApproveNonExistentReverts(uint256,address) (runs: 256, μ: 33662, ~: 33621) -ERC721Test:testApproveUnauthorizedReverts(uint256) (runs: 256, μ: 83185, ~: 82400) -ERC721Test:testAuthorizedEquivalence(address,bool,bool) (runs: 256, μ: 748, ~: 743) -ERC721Test:testAux(uint256) (runs: 256, μ: 191880, ~: 193043) -ERC721Test:testBurn(uint256) (runs: 256, μ: 82530, ~: 93880) -ERC721Test:testBurnNonExistentReverts(uint256) (runs: 256, μ: 10761, ~: 10761) -ERC721Test:testCannotExceedMaxBalance() (gas: 149876) -ERC721Test:testDoubleBurnReverts(uint256) (runs: 256, μ: 63486, ~: 63432) -ERC721Test:testDoubleMintReverts(uint256) (runs: 256, μ: 79131, ~: 79166) -ERC721Test:testEverything(uint256) (runs: 256, μ: 308702, ~: 302430) -ERC721Test:testExtraData(uint256) (runs: 256, μ: 99055, ~: 99093) -ERC721Test:testExtraData2(uint256,uint256) (runs: 256, μ: 54254, ~: 53903) -ERC721Test:testIsApprovedOrOwner(uint256) (runs: 256, μ: 135460, ~: 135446) -ERC721Test:testMint(uint256) (runs: 256, μ: 82886, ~: 82916) -ERC721Test:testMintToZeroReverts(uint256) (runs: 256, μ: 8686, ~: 8686) -ERC721Test:testOwnerOfNonExistent(uint256) (runs: 256, μ: 33389, ~: 33338) -ERC721Test:testSafeMintToEOA(uint256) (runs: 256, μ: 83480, ~: 83519) -ERC721Test:testSafeMintToERC721Recipient(uint256) (runs: 256, μ: 409459, ~: 410571) -ERC721Test:testSafeMintToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 470871, ~: 460001) -ERC721Test:testSafeMintToERC721RecipientWithWrongReturnData(uint256) (runs: 256, μ: 170008, ~: 170008) -ERC721Test:testSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256,bytes) (runs: 256, μ: 171224, ~: 171171) -ERC721Test:testSafeMintToNonERC721RecipientReverts(uint256) (runs: 256, μ: 100470, ~: 100470) -ERC721Test:testSafeMintToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 101718, ~: 101665) -ERC721Test:testSafeMintToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 203127, ~: 203127) -ERC721Test:testSafeMintToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 204364, ~: 204311) -ERC721Test:testSafeTransferFromToEOA(uint256) (runs: 256, μ: 121957, ~: 122057) -ERC721Test:testSafeTransferFromToERC721Recipient(uint256) (runs: 256, μ: 470968, ~: 472076) -ERC721Test:testSafeTransferFromToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 532415, ~: 521775) -ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataReverts(uint256) (runs: 256, μ: 200852, ~: 200908) -ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataWithDataReverts(uint256,bytes) (runs: 256, μ: 202121, ~: 202117) -ERC721Test:testSafeTransferFromToNonERC721RecipientReverts(uint256) (runs: 256, μ: 131260, ~: 131198) -ERC721Test:testSafeTransferFromToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 132601, ~: 132599) -ERC721Test:testSafeTransferFromToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 233927, ~: 233985) -ERC721Test:testSafeTransferFromToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 235263, ~: 235260) -ERC721Test:testSafetyOfCustomStorage(uint256,uint256) (runs: 256, μ: 1063, ~: 713) -ERC721Test:testTransferFrom() (gas: 85744) -ERC721Test:testTransferFrom(uint256) (runs: 256, μ: 114125, ~: 112511) -ERC721Test:testTransferFromApproveAll(uint256) (runs: 256, μ: 119340, ~: 119297) -ERC721Test:testTransferFromNotExistentReverts(address,address,uint256) (runs: 256, μ: 34014, ~: 33992) -ERC721Test:testTransferFromNotOwner(uint256) (runs: 256, μ: 84724, ~: 84691) -ERC721Test:testTransferFromSelf(uint256) (runs: 256, μ: 92806, ~: 92767) -ERC721Test:testTransferFromToZeroReverts(uint256) (runs: 256, μ: 79049, ~: 79023) -ERC721Test:testTransferFromWrongFromReverts(address,uint256) (runs: 256, μ: 80413, ~: 80398) -ERC721Test:test__codesize() (gas: 41923) -FixedPointMathLibTest:testAbs() (gas: 578) -FixedPointMathLibTest:testAbs(int256) (runs: 256, μ: 516, ~: 485) -FixedPointMathLibTest:testAbsEdgeCases() (gas: 432) -FixedPointMathLibTest:testAvg() (gas: 442) -FixedPointMathLibTest:testAvgEdgeCase() (gas: 425) -FixedPointMathLibTest:testAvgSigned() (gas: 897) -FixedPointMathLibTest:testCbrt() (gas: 10261) -FixedPointMathLibTest:testCbrt(uint256) (runs: 256, μ: 1424, ~: 1513) -FixedPointMathLibTest:testCbrtBack(uint256) (runs: 256, μ: 31710, ~: 40363) -FixedPointMathLibTest:testCbrtWad() (gas: 12006) -FixedPointMathLibTest:testCbrtWad(uint256) (runs: 256, μ: 1996, ~: 2005) -FixedPointMathLibTest:testClamp(uint256,uint256,uint256) (runs: 256, μ: 546, ~: 546) -FixedPointMathLibTest:testClampSigned(int256,int256,int256) (runs: 256, μ: 611, ~: 611) -FixedPointMathLibTest:testDist() (gas: 634) -FixedPointMathLibTest:testDist(int256,int256) (runs: 256, μ: 541, ~: 546) -FixedPointMathLibTest:testDistEdgeCases() (gas: 505) -FixedPointMathLibTest:testDivWad() (gas: 702) -FixedPointMathLibTest:testDivWad(uint256,uint256) (runs: 256, μ: 725, ~: 812) -FixedPointMathLibTest:testDivWadEdgeCases() (gas: 458) -FixedPointMathLibTest:testDivWadOverflowReverts(uint256,uint256) (runs: 256, μ: 3749, ~: 3749) -FixedPointMathLibTest:testDivWadUp() (gas: 3084) -FixedPointMathLibTest:testDivWadUp(uint256,uint256) (runs: 256, μ: 795, ~: 942) -FixedPointMathLibTest:testDivWadUpEdgeCases() (gas: 483) -FixedPointMathLibTest:testDivWadUpOverflowReverts(uint256,uint256) (runs: 256, μ: 3748, ~: 3748) -FixedPointMathLibTest:testDivWadUpZeroDenominatorReverts() (gas: 3239) -FixedPointMathLibTest:testDivWadUpZeroDenominatorReverts(uint256) (runs: 256, μ: 3295, ~: 3295) -FixedPointMathLibTest:testDivWadZeroDenominatorReverts() (gas: 3240) -FixedPointMathLibTest:testDivWadZeroDenominatorReverts(uint256) (runs: 256, μ: 3315, ~: 3315) -FixedPointMathLibTest:testExpWad() (gas: 7544) -FixedPointMathLibTest:testFactorial() (gas: 98825) -FixedPointMathLibTest:testFactorialOriginal() (gas: 94187) -FixedPointMathLibTest:testFullMulDiv() (gas: 1211) -FixedPointMathLibTest:testFullMulDiv(uint256,uint256,uint256) (runs: 256, μ: 1373, ~: 996) -FixedPointMathLibTest:testFullMulDivUp(uint256,uint256,uint256) (runs: 256, μ: 1733, ~: 1539) -FixedPointMathLibTest:testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase1() (gas: 3688) -FixedPointMathLibTest:testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase2() (gas: 3699) -FixedPointMathLibTest:testGcd() (gas: 4206) -FixedPointMathLibTest:testGcd(uint256,uint256) (runs: 256, μ: 5030, ~: 2223) -FixedPointMathLibTest:testLambertW0WadAccuracy() (gas: 7194) -FixedPointMathLibTest:testLambertW0WadAccuracy(uint184) (runs: 256, μ: 1763, ~: 386) -FixedPointMathLibTest:testLambertW0WadKnownValues() (gas: 1698467) -FixedPointMathLibTest:testLambertW0WadMonoDebug() (gas: 781937) -FixedPointMathLibTest:testLambertW0WadMonotonicallyIncreasing() (gas: 18627294) -FixedPointMathLibTest:testLambertW0WadMonotonicallyIncreasing(int256,int256) (runs: 256, μ: 7024, ~: 6871) -FixedPointMathLibTest:testLambertW0WadMonotonicallyIncreasing2() (gas: 4083980) -FixedPointMathLibTest:testLambertW0WadMonotonicallyIncreasingAround(int256) (runs: 256, μ: 26638, ~: 17576) -FixedPointMathLibTest:testLambertW0WadMonotonicallyIncreasingAround2(uint96) (runs: 256, μ: 41138, ~: 17636) -FixedPointMathLibTest:testLambertW0WadRevertsForOutOfDomain() (gas: 16568) -FixedPointMathLibTest:testLambertW0WadWithinBounds() (gas: 15587) -FixedPointMathLibTest:testLambertW0WadWithinBounds(int256) (runs: 256, μ: 3223, ~: 3692) -FixedPointMathLibTest:testLnWad() (gas: 2167) -FixedPointMathLibTest:testLnWadBig() (gas: 2178) -FixedPointMathLibTest:testLnWadNegativeReverts() (gas: 3198) -FixedPointMathLibTest:testLnWadOverflowReverts() (gas: 3173) -FixedPointMathLibTest:testLnWadSmall() (gas: 2780) -FixedPointMathLibTest:testLog10() (gas: 76189) -FixedPointMathLibTest:testLog10(uint256,uint256) (runs: 256, μ: 2175, ~: 2237) -FixedPointMathLibTest:testLog10Up() (gas: 4413) -FixedPointMathLibTest:testLog2() (gas: 243209) -FixedPointMathLibTest:testLog256() (gas: 22786) -FixedPointMathLibTest:testLog256(uint256,uint256) (runs: 256, μ: 2023, ~: 2082) -FixedPointMathLibTest:testLog256Up() (gas: 1249) -FixedPointMathLibTest:testLog2Differential(uint256) (runs: 256, μ: 871, ~: 864) -FixedPointMathLibTest:testLog2Up() (gas: 297368) -FixedPointMathLibTest:testMax(uint256,uint256) (runs: 256, μ: 518, ~: 518) -FixedPointMathLibTest:testMaxCasted(uint32,uint32,uint256) (runs: 256, μ: 887, ~: 892) -FixedPointMathLibTest:testMaxSigned(int256,int256) (runs: 256, μ: 475, ~: 471) -FixedPointMathLibTest:testMin(uint256,uint256) (runs: 256, μ: 499, ~: 504) -FixedPointMathLibTest:testMinBrutalized(uint256,uint256) (runs: 256, μ: 829, ~: 839) -FixedPointMathLibTest:testMinSigned(int256,int256) (runs: 256, μ: 475, ~: 480) -FixedPointMathLibTest:testMulDiv() (gas: 1890) -FixedPointMathLibTest:testMulDiv(uint256,uint256,uint256) (runs: 256, μ: 717, ~: 816) -FixedPointMathLibTest:testMulDivEdgeCases() (gas: 740) -FixedPointMathLibTest:testMulDivOverflowReverts(uint256,uint256,uint256) (runs: 256, μ: 3854, ~: 3854) -FixedPointMathLibTest:testMulDivUp() (gas: 2103) -FixedPointMathLibTest:testMulDivUp(uint256,uint256,uint256) (runs: 256, μ: 906, ~: 1121) -FixedPointMathLibTest:testMulDivUpEdgeCases() (gas: 861) -FixedPointMathLibTest:testMulDivUpOverflowReverts(uint256,uint256,uint256) (runs: 256, μ: 3877, ~: 3877) -FixedPointMathLibTest:testMulDivUpZeroDenominator() (gas: 3253) -FixedPointMathLibTest:testMulDivUpZeroDenominatorReverts(uint256,uint256) (runs: 256, μ: 3310, ~: 3310) -FixedPointMathLibTest:testMulDivZeroDenominatorReverts() (gas: 3252) -FixedPointMathLibTest:testMulDivZeroDenominatorReverts(uint256,uint256) (runs: 256, μ: 3310, ~: 3310) -FixedPointMathLibTest:testMulWad() (gas: 714) -FixedPointMathLibTest:testMulWad(uint256,uint256) (runs: 256, μ: 659, ~: 756) -FixedPointMathLibTest:testMulWadEdgeCases() (gas: 692) -FixedPointMathLibTest:testMulWadOverflowReverts(uint256,uint256) (runs: 256, μ: 3832, ~: 3832) -FixedPointMathLibTest:testMulWadUp() (gas: 815) -FixedPointMathLibTest:testMulWadUp(uint256,uint256) (runs: 256, μ: 860, ~: 1065) -FixedPointMathLibTest:testMulWadUpEdgeCases() (gas: 793) -FixedPointMathLibTest:testMulWadUpOverflowReverts(uint256,uint256) (runs: 256, μ: 3769, ~: 3769) -FixedPointMathLibTest:testPackUnpackSci() (gas: 129349) -FixedPointMathLibTest:testPackUnpackSci(uint256) (runs: 256, μ: 30147, ~: 30088) -FixedPointMathLibTest:testRPow() (gas: 3276) -FixedPointMathLibTest:testRPowOverflowReverts() (gas: 4973) -FixedPointMathLibTest:testRawAdd(int256,int256) (runs: 256, μ: 484, ~: 484) -FixedPointMathLibTest:testRawAdd(uint256,uint256) (runs: 256, μ: 485, ~: 485) -FixedPointMathLibTest:testRawAddMod(uint256,uint256,uint256) (runs: 256, μ: 577, ~: 577) -FixedPointMathLibTest:testRawDiv(uint256,uint256) (runs: 256, μ: 487, ~: 487) -FixedPointMathLibTest:testRawMod(uint256,uint256) (runs: 256, μ: 422, ~: 422) -FixedPointMathLibTest:testRawMul(int256,int256) (runs: 256, μ: 412, ~: 412) -FixedPointMathLibTest:testRawMul(uint256,uint256) (runs: 256, μ: 442, ~: 442) -FixedPointMathLibTest:testRawMulMod(uint256,uint256,uint256) (runs: 256, μ: 512, ~: 512) -FixedPointMathLibTest:testRawSDiv(int256,int256) (runs: 256, μ: 487, ~: 487) -FixedPointMathLibTest:testRawSMod(int256,int256) (runs: 256, μ: 422, ~: 422) -FixedPointMathLibTest:testRawSub(int256,int256) (runs: 256, μ: 485, ~: 485) -FixedPointMathLibTest:testRawSub(uint256,uint256) (runs: 256, μ: 440, ~: 440) -FixedPointMathLibTest:testSci() (gas: 1838635) -FixedPointMathLibTest:testSci(uint256) (runs: 256, μ: 31826, ~: 37538) -FixedPointMathLibTest:testSci2(uint256) (runs: 256, μ: 901, ~: 911) -FixedPointMathLibTest:testSqrt() (gas: 42576) -FixedPointMathLibTest:testSqrt(uint256) (runs: 256, μ: 1037, ~: 1086) -FixedPointMathLibTest:testSqrtBack(uint256) (runs: 256, μ: 14284, ~: 385) -FixedPointMathLibTest:testSqrtHashed(uint256) (runs: 256, μ: 53185, ~: 53549) -FixedPointMathLibTest:testSqrtHashedSingle() (gas: 53041) -FixedPointMathLibTest:testSqrtWad() (gas: 7470) -FixedPointMathLibTest:testSqrtWad(uint256) (runs: 256, μ: 1493, ~: 1498) -FixedPointMathLibTest:testZeroFloorSub(uint256,uint256) (runs: 256, μ: 503, ~: 503) -FixedPointMathLibTest:testZeroFloorSubCasted(uint32,uint32,uint256) (runs: 256, μ: 926, ~: 886) -FixedPointMathLibTest:test__codesize() (gas: 39696) -GasBurnerLibTest:testBurnGas() (gas: 1700805) -GasBurnerLibTest:test__codesize() (gas: 1435) -JSONParserLibTest:testDecodeEncodedStringDoesNotRevert(string) (runs: 256, μ: 58291, ~: 57099) -JSONParserLibTest:testDecodeInvalidStringReverts() (gas: 174221) -JSONParserLibTest:testDecodeString() (gas: 201120) -JSONParserLibTest:testEmptyItem() (gas: 3693) -JSONParserLibTest:testParseEmptyArrays() (gas: 591760) -JSONParserLibTest:testParseEmptyObjects() (gas: 592699) -JSONParserLibTest:testParseGas() (gas: 151934) -JSONParserLibTest:testParseInt() (gas: 181371) -JSONParserLibTest:testParseInvalidIntReverts() (gas: 211374) -JSONParserLibTest:testParseInvalidNumberReverts() (gas: 4075307) -JSONParserLibTest:testParseInvalidReverts() (gas: 8599742) -JSONParserLibTest:testParseInvalidStringReverts() (gas: 2121353) -JSONParserLibTest:testParseInvalidUintFromHexReverts() (gas: 90700) -JSONParserLibTest:testParseInvalidUintReverts() (gas: 309989) -JSONParserLibTest:testParseJWTGas() (gas: 52735) -JSONParserLibTest:testParseNumber() (gas: 787032) -JSONParserLibTest:testParseObject() (gas: 49089) -JSONParserLibTest:testParseRecursiveObject() (gas: 104550) -JSONParserLibTest:testParseSimpleArray() (gas: 27206) -JSONParserLibTest:testParseSimpleUintArray() (gas: 1227270) -JSONParserLibTest:testParseSpecials() (gas: 295420) -JSONParserLibTest:testParseString() (gas: 1435230) -JSONParserLibTest:testParseUint() (gas: 96207) -JSONParserLibTest:testParseUint(uint256) (runs: 256, μ: 36531, ~: 30330) -JSONParserLibTest:testParseUintFromHex() (gas: 1367554) -JSONParserLibTest:testParseValidObjectDoesNotRevert(string,string) (runs: 256, μ: 49906, ~: 48850) -JSONParserLibTest:test__codesize() (gas: 38289) -LibBitTest:testAnd() (gas: 185677) -LibBitTest:testAnd(bool,bool) (runs: 256, μ: 669, ~: 671) -LibBitTest:testAutoClean(uint256,uint256) (runs: 256, μ: 446, ~: 446) -LibBitTest:testBoolToUint(bool) (runs: 256, μ: 524, ~: 524) -LibBitTest:testCLZ() (gas: 306382) -LibBitTest:testFFS() (gas: 181271) -LibBitTest:testFLS() (gas: 254359) -LibBitTest:testIsPo2() (gas: 65189) -LibBitTest:testIsPo2(uint256) (runs: 256, μ: 4805, ~: 1106) -LibBitTest:testIsPo2(uint8,uint8) (runs: 256, μ: 629, ~: 631) -LibBitTest:testOr() (gas: 188517) -LibBitTest:testOr(bool,bool) (runs: 256, μ: 620, ~: 619) -LibBitTest:testPassInBool() (gas: 802) -LibBitTest:testPopCount() (gas: 63716) -LibBitTest:testPopCount(uint256) (runs: 256, μ: 4925, ~: 1226) -LibBitTest:testReturnsBool() (gas: 735) -LibBitTest:testReverseBitsDifferential(uint256) (runs: 256, μ: 19195, ~: 19195) -LibBitTest:testReverseBytesDifferential(uint256) (runs: 256, μ: 3047, ~: 3047) -LibBitTest:test__codesize() (gas: 6067) -LibBitmapTest:testBitmapClaimWithGetSet() (gas: 27089) -LibBitmapTest:testBitmapClaimWithToggle() (gas: 17479) -LibBitmapTest:testBitmapFindLastSet() (gas: 1300541) -LibBitmapTest:testBitmapFindLastSet(uint256,uint256) (runs: 256, μ: 75758, ~: 76072) -LibBitmapTest:testBitmapGet() (gas: 2565) -LibBitmapTest:testBitmapGet(uint256) (runs: 256, μ: 2593, ~: 2593) -LibBitmapTest:testBitmapPopCount() (gas: 750426) -LibBitmapTest:testBitmapPopCount(uint256,uint256,uint256) (runs: 256, μ: 201971, ~: 171380) -LibBitmapTest:testBitmapPopCountAcrossMultipleBuckets() (gas: 73611) -LibBitmapTest:testBitmapPopCountWithinSingleBucket() (gas: 34032) -LibBitmapTest:testBitmapSet() (gas: 22527) -LibBitmapTest:testBitmapSet(uint256) (runs: 256, μ: 22599, ~: 22599) -LibBitmapTest:testBitmapSetAndGet(uint256) (runs: 256, μ: 22633, ~: 22633) -LibBitmapTest:testBitmapSetBatch() (gas: 3147640) -LibBitmapTest:testBitmapSetBatchAcrossMultipleBuckets() (gas: 452545) -LibBitmapTest:testBitmapSetBatchWithinSingleBucket() (gas: 417119) -LibBitmapTest:testBitmapSetTo() (gas: 14275) -LibBitmapTest:testBitmapSetTo(uint256,bool,uint256) (runs: 256, μ: 13050, ~: 22774) -LibBitmapTest:testBitmapSetTo(uint256,uint256) (runs: 256, μ: 45097, ~: 50098) -LibBitmapTest:testBitmapToggle() (gas: 30810) -LibBitmapTest:testBitmapToggle(uint256,bool) (runs: 256, μ: 17743, ~: 14247) -LibBitmapTest:testBitmapUnset() (gas: 22572) -LibBitmapTest:testBitmapUnset(uint256) (runs: 256, μ: 14342, ~: 14363) -LibBitmapTest:testBitmapUnsetBatch() (gas: 3086142) -LibBitmapTest:testBitmapUnsetBatchAcrossMultipleBuckets() (gas: 453362) -LibBitmapTest:testBitmapUnsetBatchWithinSingleBucket() (gas: 446762) -LibBitmapTest:test__codesize() (gas: 8132) -LibCloneTest:testClone() (gas: 72901) -LibCloneTest:testClone(uint256) (runs: 256, μ: 72952, ~: 72952) -LibCloneTest:testCloneDeteministicWithImmutableArgs() (gas: 192911) -LibCloneTest:testCloneDeteministicWithImmutableArgs(address,uint256,uint256[],bytes,uint64,uint8,uint256) (runs: 256, μ: 1055001, ~: 996152) -LibCloneTest:testCloneDeterministic() (gas: 96699) -LibCloneTest:testCloneDeterministic(uint256,bytes32) (runs: 256, μ: 96769, ~: 96769) -LibCloneTest:testCloneDeterministicRevertsIfAddressAlreadyUsed() (gas: 96882831) -LibCloneTest:testCloneWithImmutableArgs() (gas: 120744) -LibCloneTest:testCloneWithImmutableArgs(uint256,address,uint256,uint256[],uint64,uint8) (runs: 256, μ: 987490, ~: 1012210) -LibCloneTest:testCloneWithImmutableArgsRevertsIfDataTooBig() (gas: 97305788) -LibCloneTest:testDeployDeterministicERC1967() (gas: 123067) -LibCloneTest:testDeployDeterministicERC1967(uint256,bytes32) (runs: 256, μ: 123072, ~: 123072) -LibCloneTest:testDeployERC1967() (gas: 99147) -LibCloneTest:testDeployERC1967(uint256) (runs: 256, μ: 99220, ~: 99220) -LibCloneTest:testInitialDeposit() (gas: 323883) -LibCloneTest:testStartsWith(uint256) (runs: 256, μ: 29015, ~: 29025) -LibCloneTest:test__codesize() (gas: 17578) -LibMapTest:testFoundStatementDifferential(uint256,uint256,uint256) (runs: 256, μ: 499, ~: 499) -LibMapTest:testGeneralMapFunctionsGas() (gas: 3304775) -LibMapTest:testGeneralMapFunctionsWithSmallBitWidths(uint256) (runs: 256, μ: 79345, ~: 85415) -LibMapTest:testGeneralMapFunctionsWithZeroBitWidth() (gas: 12521) -LibMapTest:testGeneralMapSearchSorted(uint256) (runs: 256, μ: 191920, ~: 127091) -LibMapTest:testMapGetFromBigArray() (gas: 3308) -LibMapTest:testMapGetUint8() (gas: 3335) -LibMapTest:testMapSetUint8() (gas: 23321) -LibMapTest:testMapSetUint8FromBigArray() (gas: 23264) -LibMapTest:testUint128MapSearchSorted(uint256) (runs: 256, μ: 188304, ~: 131590) -LibMapTest:testUint128MapSetAndGet() (gas: 1607245) -LibMapTest:testUint128MapSetAndGet(uint256) (runs: 256, μ: 172303, ~: 169095) -LibMapTest:testUint128MapSetAndGet2(uint256) (runs: 256, μ: 68640, ~: 69275) -LibMapTest:testUint16MapSearchSorted(uint256) (runs: 256, μ: 92917, ~: 86851) -LibMapTest:testUint16MapSetAndGet() (gas: 958193) -LibMapTest:testUint16MapSetAndGet(uint256) (runs: 256, μ: 81211, ~: 81195) -LibMapTest:testUint16MapSetAndGet2(uint256) (runs: 256, μ: 66936, ~: 69189) -LibMapTest:testUint32MapSearchSorted(uint256) (runs: 256, μ: 100991, ~: 86096) -LibMapTest:testUint32MapSetAndGet() (gas: 1067093) -LibMapTest:testUint32MapSetAndGet(uint256) (runs: 256, μ: 114350, ~: 124336) -LibMapTest:testUint32MapSetAndGet2(uint256) (runs: 256, μ: 67417, ~: 69139) -LibMapTest:testUint32Maps(uint256) (runs: 256, μ: 64099, ~: 72236) -LibMapTest:testUint40MapSearchSorted(uint256) (runs: 256, μ: 134900, ~: 106702) -LibMapTest:testUint40MapSetAndGet() (gas: 1127344) -LibMapTest:testUint40MapSetAndGet(uint256) (runs: 256, μ: 136972, ~: 145996) -LibMapTest:testUint40MapSetAndGet2(uint256) (runs: 256, μ: 67025, ~: 69222) -LibMapTest:testUint64MapSearchSorted(uint256) (runs: 256, μ: 126533, ~: 108400) -LibMapTest:testUint64MapSetAndGet() (gas: 1220437) -LibMapTest:testUint64MapSetAndGet(uint256) (runs: 256, μ: 141134, ~: 146540) -LibMapTest:testUint64MapSetAndGet2(uint256) (runs: 256, μ: 65001, ~: 69181) -LibMapTest:testUint8MapSearchSorted(uint256) (runs: 256, μ: 98669, ~: 86281) -LibMapTest:testUint8MapSetAndGet() (gas: 883454) -LibMapTest:testUint8MapSetAndGet(uint256) (runs: 256, μ: 59148, ~: 59100) -LibMapTest:testUint8MapSetAndGet2(uint256) (runs: 256, μ: 67769, ~: 69101) -LibMapTest:test__codesize() (gas: 13187) -LibPRNGTest:testLCGGas() (gas: 20736) -LibPRNGTest:testPRNGGas() (gas: 25645) -LibPRNGTest:testPRNGNext() (gas: 16184) -LibPRNGTest:testPRNGShuffle() (gas: 504485) -LibPRNGTest:testPRNGShuffleBytes() (gas: 222095) -LibPRNGTest:testPRNGShuffleBytesGas() (gas: 1322452) -LibPRNGTest:testPRNGShuffleGas() (gas: 1610949) -LibPRNGTest:testPRNGUniform() (gas: 559341) -LibPRNGTest:test__codesize() (gas: 4526) -LibRLPTest:testComputeAddressDifferential(address,uint256) (runs: 256, μ: 1948, ~: 1831) -LibRLPTest:testComputeAddressForLargeNonces() (gas: 1771) -LibRLPTest:testComputeAddressForSmallNonces() (gas: 967) -LibRLPTest:testComputeAddressOriginalForLargeNonces() (gas: 1887) -LibRLPTest:testComputeAddressOriginalForSmallNonces() (gas: 1477) -LibRLPTest:test__codesize() (gas: 8108) -LibSortTest:testInsertionSortAddressesDifferential(uint256) (runs: 256, μ: 35967, ~: 25750) -LibSortTest:testInsertionSortInts() (gas: 112135) -LibSortTest:testInsertionSortPsuedorandom() (gas: 62806) -LibSortTest:testInsertionSortPsuedorandom(uint256) (runs: 256, μ: 61996, ~: 61637) -LibSortTest:testIsSortedAddressesDifferential(uint256) (runs: 256, μ: 37530, ~: 29367) -LibSortTest:testIsSortedAndUniquifiedAddressesDifferential(uint256) (runs: 256, μ: 33725, ~: 29154) -LibSortTest:testIsSortedAndUniquifiedDifferential(uint256) (runs: 256, μ: 33577, ~: 26806) -LibSortTest:testIsSortedAndUniquifiedIntsDifferential(uint256) (runs: 256, μ: 89821, ~: 32035) -LibSortTest:testIsSortedDifferential(uint256) (runs: 256, μ: 34398, ~: 27521) -LibSortTest:testIsSortedIntsDifferential(uint256) (runs: 256, μ: 43804, ~: 30264) -LibSortTest:testReverse() (gas: 114573) -LibSortTest:testSearchSorted() (gas: 118972) -LibSortTest:testSearchSortedAddresses() (gas: 254850) -LibSortTest:testSearchSortedBasicCases() (gas: 2244) -LibSortTest:testSearchSortedDifferential(uint256) (runs: 256, μ: 46869, ~: 29238) -LibSortTest:testSearchSortedEdgeCases() (gas: 1675) -LibSortTest:testSearchSortedElementInArray(uint256) (runs: 256, μ: 52908, ~: 31626) -LibSortTest:testSearchSortedElementNotInArray() (gas: 6338) -LibSortTest:testSearchSortedElementNotInArray(uint256) (runs: 256, μ: 67613, ~: 35422) -LibSortTest:testSearchSortedInts() (gas: 120794) -LibSortTest:testSearchSortedInts(int256[],int256) (runs: 256, μ: 11887, ~: 12059) -LibSortTest:testSearchSortedWithEmptyArray() (gas: 738) -LibSortTest:testSort(uint256) (runs: 256, μ: 46941, ~: 29774) -LibSortTest:testSortAddressesDifferential(uint256) (runs: 256, μ: 106312, ~: 29625) -LibSortTest:testSortAddressesPsuedorandom() (gas: 144831) -LibSortTest:testSortAddressesPsuedorandom(uint256) (runs: 256, μ: 146511, ~: 146653) -LibSortTest:testSortAddressesPsuedorandomBrutalizeUpperBits() (gas: 195855) -LibSortTest:testSortAddressesReversed() (gas: 45400) -LibSortTest:testSortAddressesSorted() (gas: 42376) -LibSortTest:testSortBasicCase() (gas: 1210) -LibSortTest:testSortChecksumed(uint256) (runs: 256, μ: 41765, ~: 28194) -LibSortTest:testSortDifferential(uint256) (runs: 256, μ: 50383, ~: 27194) -LibSortTest:testSortInts() (gas: 612197) -LibSortTest:testSortMostlySame() (gas: 67374) -LibSortTest:testSortOriginalMostlySame() (gas: 207985) -LibSortTest:testSortOriginalPsuedorandom() (gas: 284071) -LibSortTest:testSortOriginalPsuedorandom(uint256) (runs: 256, μ: 285134, ~: 285198) -LibSortTest:testSortOriginalReversed() (gas: 156282) -LibSortTest:testSortOriginalSorted() (gas: 142263) -LibSortTest:testSortPsuedorandom() (gas: 138551) -LibSortTest:testSortPsuedorandom(uint256) (runs: 256, μ: 139957, ~: 140097) -LibSortTest:testSortPsuedorandomNonuniform() (gas: 145689) -LibSortTest:testSortPsuedorandomNonuniform(uint256) (runs: 256, μ: 145242, ~: 145503) -LibSortTest:testSortReversed() (gas: 38827) -LibSortTest:testSortSorted() (gas: 35785) -LibSortTest:testSortTestOverhead() (gas: 102190) -LibSortTest:testSortedDifferenceDifferential() (gas: 174014) -LibSortTest:testSortedDifferenceDifferential(uint256) (runs: 256, μ: 35743, ~: 35595) -LibSortTest:testSortedDifferenceDifferentialInt(uint256) (runs: 256, μ: 35099, ~: 34485) -LibSortTest:testSortedDifferenceUnionIntersection(uint256) (runs: 256, μ: 58456, ~: 53535) -LibSortTest:testSortedIntersectionDifferential() (gas: 208149) -LibSortTest:testSortedIntersectionDifferential(uint256) (runs: 256, μ: 32268, ~: 31481) -LibSortTest:testSortedIntersectionDifferentialInt(uint256) (runs: 256, μ: 35871, ~: 35215) -LibSortTest:testSortedUnionDifferential() (gas: 212603) -LibSortTest:testSortedUnionDifferential(uint256) (runs: 256, μ: 34338, ~: 34163) -LibSortTest:testSortedUnionDifferentialInt(uint256) (runs: 256, μ: 38745, ~: 39202) -LibSortTest:testTwoComplementConversionSort(int256,int256) (runs: 256, μ: 515, ~: 514) -LibSortTest:testUniquifySorted() (gas: 1710) -LibSortTest:testUniquifySorted(uint256) (runs: 256, μ: 42329, ~: 24996) -LibSortTest:testUniquifySortedAddress() (gas: 4039) -LibSortTest:testUniquifySortedAddress(uint256) (runs: 256, μ: 67632, ~: 30265) -LibSortTest:testUniquifySortedDifferential(uint256) (runs: 256, μ: 67605, ~: 33106) -LibSortTest:testUniquifySortedWithEmptyArray() (gas: 506) -LibSortTest:test__codesize() (gas: 23230) -LibStringTest:testAddressToHexStringZeroRightPadded(address) (runs: 256, μ: 3370, ~: 3370) -LibStringTest:testBytesToHexString() (gas: 6446) -LibStringTest:testBytesToHexString(bytes) (runs: 256, μ: 692470, ~: 577893) -LibStringTest:testBytesToHexStringNoPrefix() (gas: 6064) -LibStringTest:testBytesToHexStringNoPrefix(bytes) (runs: 256, μ: 757633, ~: 578238) -LibStringTest:testContains() (gas: 43922) -LibStringTest:testFromAddressToHexString() (gas: 3774) -LibStringTest:testFromAddressToHexStringChecksummed() (gas: 40426) -LibStringTest:testFromAddressToHexStringChecksummedDifferential(uint256) (runs: 256, μ: 738350, ~: 567130) -LibStringTest:testFromAddressToHexStringWithLeadingZeros() (gas: 3795) -LibStringTest:testHexStringNoPrefixVariants(uint256,uint256) (runs: 256, μ: 648819, ~: 577685) -LibStringTest:testStringConcat() (gas: 7385) -LibStringTest:testStringConcat(string,string) (runs: 256, μ: 632721, ~: 558799) -LibStringTest:testStringConcatOriginal() (gas: 8045) -LibStringTest:testStringDirectReturn() (gas: 8213) -LibStringTest:testStringDirectReturn(string) (runs: 256, μ: 3614, ~: 3455) -LibStringTest:testStringEndsWith() (gas: 2852) -LibStringTest:testStringEndsWith(uint256) (runs: 256, μ: 845913, ~: 589643) -LibStringTest:testStringEq(string,string) (runs: 256, μ: 1586, ~: 1587) -LibStringTest:testStringEqs() (gas: 1861) -LibStringTest:testStringEscapeHTML() (gas: 11903) -LibStringTest:testStringEscapeHTML(uint256) (runs: 256, μ: 724158, ~: 606340) -LibStringTest:testStringEscapeJSON() (gas: 53305) -LibStringTest:testStringEscapeJSONHexEncode() (gas: 694952) -LibStringTest:testStringIndexOf() (gas: 17420) -LibStringTest:testStringIndexOf(uint256) (runs: 256, μ: 811014, ~: 602628) -LibStringTest:testStringIndicesOf() (gas: 11658) -LibStringTest:testStringIndicesOf(uint256) (runs: 256, μ: 765072, ~: 604437) -LibStringTest:testStringIs7BitASCII() (gas: 205190) -LibStringTest:testStringIs7BitASCIIDifferential(bytes) (runs: 256, μ: 702666, ~: 557455) -LibStringTest:testStringLastIndexOf() (gas: 24020) -LibStringTest:testStringLastIndexOf(uint256) (runs: 256, μ: 647483, ~: 598426) -LibStringTest:testStringLowerDifferential() (gas: 3947004) -LibStringTest:testStringLowerDifferential(string) (runs: 256, μ: 8929, ~: 8560) -LibStringTest:testStringLowerOriginal() (gas: 1783) -LibStringTest:testStringPackAndUnpackOne() (gas: 705911) -LibStringTest:testStringPackAndUnpackOne(string) (runs: 256, μ: 631676, ~: 557252) -LibStringTest:testStringPackAndUnpackOneDifferential(string) (runs: 256, μ: 671502, ~: 556594) -LibStringTest:testStringPackAndUnpackTwo() (gas: 864247) -LibStringTest:testStringPackAndUnpackTwo(string,string) (runs: 256, μ: 639722, ~: 558976) -LibStringTest:testStringPackAndUnpackTwoDifferential(string,string) (runs: 256, μ: 665171, ~: 557636) -LibStringTest:testStringRepeat() (gas: 8998) -LibStringTest:testStringRepeat(string,uint256) (runs: 256, μ: 648566, ~: 560206) -LibStringTest:testStringRepeatOriginal() (gas: 13671) -LibStringTest:testStringReplace(uint256) (runs: 256, μ: 669171, ~: 608064) -LibStringTest:testStringReplaceLong() (gas: 9799) -LibStringTest:testStringReplaceMedium() (gas: 8541) -LibStringTest:testStringReplaceShort() (gas: 17434) -LibStringTest:testStringRuneCount() (gas: 2972587) -LibStringTest:testStringRuneCountDifferential(string) (runs: 256, μ: 6085, ~: 5862) -LibStringTest:testStringSlice() (gas: 17767) -LibStringTest:testStringSlice(uint256) (runs: 256, μ: 664210, ~: 602350) -LibStringTest:testStringSplit() (gas: 20265) -LibStringTest:testStringSplit(uint256) (runs: 256, μ: 745574, ~: 601378) -LibStringTest:testStringStartsWith() (gas: 2567) -LibStringTest:testStringStartsWith(uint256) (runs: 256, μ: 656349, ~: 589578) -LibStringTest:testStringUpperDifferential() (gas: 3492145) -LibStringTest:testStringUpperDifferential(string) (runs: 256, μ: 8929, ~: 8560) -LibStringTest:testStringUpperOriginal() (gas: 1759) -LibStringTest:testToHexStringFixedLengthInsufficientLength() (gas: 3344) -LibStringTest:testToHexStringFixedLengthPositiveNumberLong() (gas: 4454) -LibStringTest:testToHexStringFixedLengthPositiveNumberShort() (gas: 1459) -LibStringTest:testToHexStringFixedLengthUint256Max() (gas: 4478) -LibStringTest:testToHexStringFixedLengthZeroRightPadded(uint256,uint256) (runs: 256, μ: 8223, ~: 4860) -LibStringTest:testToHexStringPositiveNumber() (gas: 1407) -LibStringTest:testToHexStringUint256Max() (gas: 4219) -LibStringTest:testToHexStringZero() (gas: 1345) -LibStringTest:testToHexStringZeroRightPadded(uint256) (runs: 256, μ: 2025, ~: 1322) -LibStringTest:testToMinimalHexStringNoPrefixPositiveNumber() (gas: 6022) -LibStringTest:testToMinimalHexStringNoPrefixUint256Max() (gas: 4064) -LibStringTest:testToMinimalHexStringNoPrefixZero() (gas: 1350) -LibStringTest:testToMinimalHexStringNoPrefixZeroRightPadded(uint256) (runs: 256, μ: 2039, ~: 1343) -LibStringTest:testToMinimalHexStringPositiveNumber() (gas: 6111) -LibStringTest:testToMinimalHexStringUint256Max() (gas: 4247) -LibStringTest:testToMinimalHexStringZero() (gas: 1393) -LibStringTest:testToMinimalHexStringZeroRightPadded(uint256) (runs: 256, μ: 2072, ~: 1369) -LibStringTest:testToStringPositiveNumber() (gas: 1448) -LibStringTest:testToStringPositiveNumberBrutalized() (gas: 1056792) -LibStringTest:testToStringSignedDifferential(int256) (runs: 256, μ: 644623, ~: 557786) -LibStringTest:testToStringSignedGas() (gas: 7253) -LibStringTest:testToStringSignedMemory(int256) (runs: 256, μ: 666499, ~: 556934) -LibStringTest:testToStringSignedOriginalGas() (gas: 9861) -LibStringTest:testToStringUint256Max() (gas: 7442) -LibStringTest:testToStringUint256MaxBrutalized() (gas: 569890) -LibStringTest:testToStringZero() (gas: 1196) -LibStringTest:testToStringZeroBrutalized() (gas: 557263) -LibStringTest:testToStringZeroRightPadded(uint256) (runs: 256, μ: 682989, ~: 557533) -LibStringTest:test__codesize() (gas: 41335) -LibZipTest:testCdCompress() (gas: 156007) -LibZipTest:testCdCompressDecompress(bytes) (runs: 256, μ: 716189, ~: 645775) -LibZipTest:testCdCompressDecompress(uint256) (runs: 256, μ: 782010, ~: 694845) -LibZipTest:testCdDecompressOnInvalidInput() (gas: 26080) -LibZipTest:testCdFallback() (gas: 5674556) -LibZipTest:testCdFallback(bytes,uint256) (runs: 256, μ: 1199984, ~: 1049321) -LibZipTest:testCdFallbackDecompressor(bytes) (runs: 256, μ: 121353, ~: 117716) -LibZipTest:testCdFallbackDecompressor(uint256) (runs: 256, μ: 167254, ~: 156539) -LibZipTest:testCdFallbackMaskTrick(uint256,uint256) (runs: 256, μ: 689, ~: 663) -LibZipTest:testDecompressWontRevert(bytes) (runs: 256, μ: 725418, ~: 626106) -LibZipTest:testFlzCompressDecompress() (gas: 1920321) -LibZipTest:testFlzCompressDecompress(bytes) (runs: 256, μ: 846262, ~: 678560) -LibZipTest:testFlzCompressDecompress2() (gas: 942068) -LibZipTest:test__codesize() (gas: 21712) -MerkleProofLibTest:testEmptyCalldataHelpers() (gas: 1086) -MerkleProofLibTest:testVerifyMultiProof(bool,bool,bool,bool,bytes32) (runs: 256, μ: 757466, ~: 629117) -MerkleProofLibTest:testVerifyMultiProofForHeightOneTree(bool,bool,bool,bool,bool,bool[]) (runs: 256, μ: 37116, ~: 37668) -MerkleProofLibTest:testVerifyMultiProofForHeightTwoTree(bool,bool,bool,bool,bool,bytes32) (runs: 256, μ: 6515, ~: 6489) -MerkleProofLibTest:testVerifyMultiProofForSingleLeaf(bytes32[],uint256) (runs: 256, μ: 880518, ~: 787948) -MerkleProofLibTest:testVerifyMultiProofIsInvalid() (gas: 627912) -MerkleProofLibTest:testVerifyMultiProofIsValid() (gas: 629253) -MerkleProofLibTest:testVerifyMultiProofMalicious() (gas: 7998) -MerkleProofLibTest:testVerifyProof(bytes32[],uint256) (runs: 256, μ: 885329, ~: 772683) -MerkleProofLibTest:testVerifyProofBasicCase(bool,bool,bool,bytes32) (runs: 256, μ: 4077, ~: 4222) -MerkleProofLibTest:testVerifyProofBasicCaseIsInvalid() (gas: 3581) -MerkleProofLibTest:testVerifyProofBasicCaseIsValid() (gas: 3590) -MerkleProofLibTest:testVerifyProofForHeightOneTree(bool,bool,bool,bool) (runs: 256, μ: 2697, ~: 2971) -MerkleProofLibTest:test__codesize() (gas: 12117) -MetadataReaderLibTest:testBoundsCheckDifferential(uint256) (runs: 256, μ: 25571, ~: 25536) -MetadataReaderLibTest:testReadBytes32String() (gas: 641910) -MetadataReaderLibTest:testReadBytes32StringTruncated() (gas: 752829) -MetadataReaderLibTest:testReadString(uint256) (runs: 256, μ: 857661, ~: 722894) -MetadataReaderLibTest:testReadStringChopped() (gas: 223238) -MetadataReaderLibTest:testReadStringTruncated(uint256) (runs: 256, μ: 794124, ~: 710222) -MetadataReaderLibTest:testReadUint() (gas: 542217) -MetadataReaderLibTest:testReadUint(uint256) (runs: 256, μ: 22765, ~: 23854) -MetadataReaderLibTest:test__codesize() (gas: 8645) -MinHeapLibTest:testHeapEnqueue(uint256) (runs: 256, μ: 184437, ~: 184485) -MinHeapLibTest:testHeapEnqueueGas(uint256) (runs: 256, μ: 293352, ~: 293395) -MinHeapLibTest:testHeapPushAndPop(uint256) (runs: 256, μ: 108526, ~: 99702) -MinHeapLibTest:testHeapPushPop(uint256) (runs: 256, μ: 246549, ~: 254170) -MinHeapLibTest:testHeapReplace(uint256) (runs: 256, μ: 306102, ~: 314615) -MinHeapLibTest:testHeapRoot(uint256) (runs: 256, μ: 5232, ~: 5232) -MinHeapLibTest:test__codesize() (gas: 5404) -MulticallableTest:testMulticallableBenchmark() (gas: 29588) -MulticallableTest:testMulticallableOriginalBenchmark() (gas: 38849) -MulticallableTest:testMulticallablePreservesMsgSender() (gas: 11193) -MulticallableTest:testMulticallableReturnDataIsProperlyEncoded() (gas: 11667) -MulticallableTest:testMulticallableReturnDataIsProperlyEncoded(string,string,uint256) (runs: 256, μ: 9874, ~: 9484) -MulticallableTest:testMulticallableReturnDataIsProperlyEncoded(uint256,uint256,uint256,uint256) (runs: 256, μ: 11813, ~: 11813) -MulticallableTest:testMulticallableRevertWithCustomError() (gas: 11841) -MulticallableTest:testMulticallableRevertWithMessage() (gas: 13513) -MulticallableTest:testMulticallableRevertWithMessage(string) (runs: 256, μ: 14126, ~: 13974) -MulticallableTest:testMulticallableRevertWithNothing() (gas: 11730) -MulticallableTest:testMulticallableWithNoData() (gas: 6322) -MulticallableTest:test__codesize() (gas: 9796) -OwnableRolesTest:testBytecodeSize() (gas: 350635) -OwnableRolesTest:testGrantAndRemoveRolesDirect(address,uint256,uint256) (runs: 256, μ: 38391, ~: 40582) -OwnableRolesTest:testGrantAndRevokeOrRenounceRoles(address,bool,bool,bool,uint256,uint256) (runs: 256, μ: 27601, ~: 28129) -OwnableRolesTest:testGrantRoles() (gas: 36122) -OwnableRolesTest:testHandoverOwnership() (gas: 32342) -OwnableRolesTest:testHandoverOwnership(address) (runs: 256, μ: 32400, ~: 32386) -OwnableRolesTest:testHandoverOwnershipAfterExpiration() (gas: 36930) -OwnableRolesTest:testHandoverOwnershipBeforeExpiration() (gas: 28667) -OwnableRolesTest:testHandoverOwnershipRevertsIfCompleteIsNotOwner() (gas: 35585) -OwnableRolesTest:testHandoverOwnershipWithCancellation() (gas: 30715) -OwnableRolesTest:testHasAllRoles(address,uint256,uint256,uint256,bool) (runs: 256, μ: 33540, ~: 33537) -OwnableRolesTest:testHasAnyRole(address,uint256,uint256) (runs: 256, μ: 32710, ~: 33332) -OwnableRolesTest:testInitializeOwnerDirect() (gas: 16724) -OwnableRolesTest:testOnlyOwnerModifier(address,bool) (runs: 256, μ: 23708, ~: 32884) -OwnableRolesTest:testOnlyOwnerOrRolesModifier() (gas: 36582) -OwnableRolesTest:testOnlyOwnerOrRolesModifier(address,bool,uint256,uint256) (runs: 256, μ: 55705, ~: 57924) -OwnableRolesTest:testOnlyRolesModifier(address,uint256,uint256) (runs: 256, μ: 53721, ~: 57912) -OwnableRolesTest:testOnlyRolesOrOwnerModifier(address,bool,uint256,uint256) (runs: 256, μ: 56866, ~: 58382) -OwnableRolesTest:testOrdinalsFromRoles() (gas: 3610415) -OwnableRolesTest:testOrdinalsFromRoles(uint256) (runs: 256, μ: 69762, ~: 33845) -OwnableRolesTest:testOwnershipHandoverValidForDefaultValue() (gas: 5825) -OwnableRolesTest:testRenounceOwnership() (gas: 12899) -OwnableRolesTest:testRolesFromOrdinals() (gas: 651259) -OwnableRolesTest:testRolesFromOrdinals(uint8[]) (runs: 256, μ: 78088, ~: 74221) -OwnableRolesTest:testSetOwnerDirect() (gas: 17776) -OwnableRolesTest:testSetOwnerDirect(address) (runs: 256, μ: 17901, ~: 17901) -OwnableRolesTest:testSetRolesDirect(uint256) (runs: 256, μ: 99847, ~: 99819) -OwnableRolesTest:testTransferOwnership() (gas: 19657) -OwnableRolesTest:testTransferOwnership(address,bool,bool) (runs: 256, μ: 14470, ~: 12721) -OwnableRolesTest:test__codesize() (gas: 23704) -OwnableTest:testBytecodeSize() (gas: 235158) -OwnableTest:testHandoverOwnership() (gas: 32072) -OwnableTest:testHandoverOwnership(address) (runs: 256, μ: 32094, ~: 32081) -OwnableTest:testHandoverOwnershipAfterExpiration() (gas: 36642) -OwnableTest:testHandoverOwnershipBeforeExpiration() (gas: 28516) -OwnableTest:testHandoverOwnershipRevertsIfCompleteIsNotOwner() (gas: 35341) -OwnableTest:testHandoverOwnershipWithCancellation() (gas: 30318) -OwnableTest:testInitializeOwnerDirect() (gas: 16599) -OwnableTest:testOnlyOwnerModifier(address,bool) (runs: 256, μ: 23624, ~: 32828) -OwnableTest:testOwnershipHandoverValidForDefaultValue() (gas: 5737) -OwnableTest:testRenounceOwnership() (gas: 12755) -OwnableTest:testSetOwnerDirect() (gas: 17686) -OwnableTest:testSetOwnerDirect(address) (runs: 256, μ: 17812, ~: 17812) -OwnableTest:testTransferOwnership() (gas: 19467) -OwnableTest:testTransferOwnership(address,bool,bool) (runs: 256, μ: 14191, ~: 12487) -OwnableTest:test__codesize() (gas: 12233) -ReceiverTest:testETHReceived() (gas: 9621) -ReceiverTest:testOnERC1155BatchReceived() (gas: 48975) -ReceiverTest:testOnERC1155Received() (gas: 46717) -ReceiverTest:testOnERC721Received() (gas: 64108) -ReceiverTest:test__codesize() (gas: 3310) -RedBlackTreeLibTest:testRedBlackTreeBenchUint160() (gas: 3438446) -RedBlackTreeLibTest:testRedBlackTreeBenchUint256() (gas: 5850739) -RedBlackTreeLibTest:testRedBlackTreeClear() (gas: 57543) -RedBlackTreeLibTest:testRedBlackTreeClear(uint256) (runs: 256, μ: 289199, ~: 214826) -RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove(uint256) (runs: 256, μ: 698454, ~: 525248) -RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove2(uint256) (runs: 256, μ: 420135, ~: 388876) -RedBlackTreeLibTest:testRedBlackTreeInsertAndRemove3() (gas: 21537948) -RedBlackTreeLibTest:testRedBlackTreeInsertBenchStep() (gas: 3711319) -RedBlackTreeLibTest:testRedBlackTreeInsertBenchUint160() (gas: 3476937) -RedBlackTreeLibTest:testRedBlackTreeInsertBenchUint256() (gas: 6376001) -RedBlackTreeLibTest:testRedBlackTreeNearest(uint256) (runs: 256, μ: 227576, ~: 222624) -RedBlackTreeLibTest:testRedBlackTreeNearestAfter(uint256) (runs: 256, μ: 254293, ~: 246502) -RedBlackTreeLibTest:testRedBlackTreeNearestBefore(uint256) (runs: 256, μ: 235352, ~: 220313) -RedBlackTreeLibTest:testRedBlackTreePointers() (gas: 91901) -RedBlackTreeLibTest:testRedBlackTreeRejectsEmptyValue() (gas: 3194) -RedBlackTreeLibTest:testRedBlackTreeRemoveViaPointer() (gas: 58199) -RedBlackTreeLibTest:testRedBlackTreeTreeFullReverts() (gas: 50293) -RedBlackTreeLibTest:testRedBlackTreeTryInsertAndRemove() (gas: 56127) -RedBlackTreeLibTest:test__codesize() (gas: 13237) -SSTORE2Test:testReadInvalidPointerCustomBoundsReverts() (gas: 3242) -SSTORE2Test:testReadInvalidPointerCustomBoundsReverts(address,uint256,uint256) (runs: 256, μ: 765666, ~: 632367) -SSTORE2Test:testReadInvalidPointerCustomStartBoundReverts() (gas: 3241) -SSTORE2Test:testReadInvalidPointerCustomStartBoundReverts(address,uint256) (runs: 256, μ: 702843, ~: 632453) -SSTORE2Test:testReadInvalidPointerRevert(address) (runs: 256, μ: 729579, ~: 632130) -SSTORE2Test:testReadInvalidPointerReverts() (gas: 3215) -SSTORE2Test:testWriteRead() (gas: 69590) -SSTORE2Test:testWriteRead(bytes) (runs: 256, μ: 805883, ~: 673902) -SSTORE2Test:testWriteReadCustomBounds() (gas: 34463) -SSTORE2Test:testWriteReadCustomBounds(bytes,uint256,uint256) (runs: 256, μ: 761817, ~: 666288) -SSTORE2Test:testWriteReadCustomBoundsOutOfRangeReverts(bytes,uint256,uint256) (runs: 256, μ: 835843, ~: 669947) -SSTORE2Test:testWriteReadCustomStartBound() (gas: 34651) -SSTORE2Test:testWriteReadCustomStartBound(bytes,uint256) (runs: 256, μ: 776405, ~: 671909) -SSTORE2Test:testWriteReadCustomStartBoundOutOfRangeReverts(bytes,uint256) (runs: 256, μ: 798063, ~: 670044) -SSTORE2Test:testWriteReadDeterministic(bytes) (runs: 256, μ: 875920, ~: 746015) -SSTORE2Test:testWriteReadEmptyBound() (gas: 33825) -SSTORE2Test:testWriteReadEmptyOutOfBoundsReverts() (gas: 36485) -SSTORE2Test:testWriteReadFullBoundedRead() (gas: 69630) -SSTORE2Test:testWriteReadFullStartBound() (gas: 34815) -SSTORE2Test:testWriteReadOutOfBoundsReverts() (gas: 36463) -SSTORE2Test:testWriteReadOutOfStartBoundReverts() (gas: 36467) -SSTORE2Test:testWriteWithTooBigDataReverts() (gas: 97662218) -SSTORE2Test:test__codesize() (gas: 9625) -SafeCastLibTest:testSafeCastToInt(int256) (runs: 256, μ: 4364, ~: 3415) -SafeCastLibTest:testSafeCastToInt256(uint256) (runs: 256, μ: 942, ~: 390) -SafeCastLibTest:testSafeCastToIntBench() (gas: 383456) -SafeCastLibTest:testSafeCastToUint() (gas: 10560) -SafeCastLibTest:testSafeCastToUint(uint256) (runs: 256, μ: 4150, ~: 3351) -SafeCastLibTest:testSafeCastToUint256(int256) (runs: 256, μ: 1297, ~: 432) -SafeCastLibTest:testSafeCastToUintBench() (gas: 326306) -SafeCastLibTest:test__codesize() (gas: 16001) -SafeTransferLibTest:testApproveWithGarbageReverts(address,uint256) (runs: 256, μ: 110649, ~: 125884) -SafeTransferLibTest:testApproveWithMissingReturn() (gas: 31945) -SafeTransferLibTest:testApproveWithMissingReturn(address,uint256) (runs: 256, μ: 30860, ~: 32104) -SafeTransferLibTest:testApproveWithNonContract() (gas: 2989) -SafeTransferLibTest:testApproveWithNonContract(address,address,uint256) (runs: 256, μ: 3513, ~: 3536) -SafeTransferLibTest:testApproveWithNonGarbage(address,uint256) (runs: 256, μ: 77932, ~: 59187) -SafeTransferLibTest:testApproveWithRetry() (gas: 768579) -SafeTransferLibTest:testApproveWithRetry(address,uint256,uint256) (runs: 256, μ: 767124, ~: 768723) -SafeTransferLibTest:testApproveWithRetryWithNonContract() (gas: 2990) -SafeTransferLibTest:testApproveWithRetryWithNonContract(address,address,uint256) (runs: 256, μ: 3510, ~: 3533) -SafeTransferLibTest:testApproveWithReturnsFalseReverts() (gas: 9515) -SafeTransferLibTest:testApproveWithReturnsFalseReverts(address,uint256) (runs: 256, μ: 9652, ~: 9652) -SafeTransferLibTest:testApproveWithReturnsTooLittleReverts() (gas: 9419) -SafeTransferLibTest:testApproveWithReturnsTooLittleReverts(address,uint256) (runs: 256, μ: 9603, ~: 9603) -SafeTransferLibTest:testApproveWithReturnsTooMuch() (gas: 32324) -SafeTransferLibTest:testApproveWithReturnsTooMuch(address,uint256) (runs: 256, μ: 31266, ~: 32510) -SafeTransferLibTest:testApproveWithReturnsTwoReverts(address,uint256) (runs: 256, μ: 9675, ~: 9675) -SafeTransferLibTest:testApproveWithRevertingReverts() (gas: 9376) -SafeTransferLibTest:testApproveWithRevertingReverts(address,uint256) (runs: 256, μ: 9570, ~: 9570) -SafeTransferLibTest:testApproveWithStandardERC20() (gas: 31786) -SafeTransferLibTest:testApproveWithStandardERC20(address,uint256) (runs: 256, μ: 30725, ~: 31969) -SafeTransferLibTest:testBalanceOfStandardERC20() (gas: 7830) -SafeTransferLibTest:testBalanceOfStandardERC20(address,uint256) (runs: 256, μ: 39516, ~: 40437) -SafeTransferLibTest:testForceTransferETHToGriever() (gas: 1511304) -SafeTransferLibTest:testForceTransferETHToGriever(uint256,uint256) (runs: 256, μ: 532316, ~: 570828) -SafeTransferLibTest:testTransferAllETH() (gas: 34598) -SafeTransferLibTest:testTransferAllETH(address) (runs: 256, μ: 34355, ~: 35018) -SafeTransferLibTest:testTransferAllETHToContractWithoutFallbackReverts() (gas: 10788) -SafeTransferLibTest:testTransferAllETHToContractWithoutFallbackReverts(uint256) (runs: 256, μ: 10898, ~: 10898) -SafeTransferLibTest:testTransferAllFromWithStandardERC20() (gas: 33092) -SafeTransferLibTest:testTransferAllFromWithStandardERC20(address,address,uint256) (runs: 256, μ: 56508, ~: 56508) -SafeTransferLibTest:testTransferAllWithStandardERC20() (gas: 30735) -SafeTransferLibTest:testTransferAllWithStandardERC20(address,uint256) (runs: 256, μ: 42114, ~: 42114) -SafeTransferLibTest:testTransferETH() (gas: 34600) -SafeTransferLibTest:testTransferETH(address,uint256) (runs: 256, μ: 32952, ~: 35250) -SafeTransferLibTest:testTransferETHToContractWithoutFallbackReverts() (gas: 10846) -SafeTransferLibTest:testTransferETHToContractWithoutFallbackReverts(uint256) (runs: 256, μ: 10545, ~: 10928) -SafeTransferLibTest:testTransferFromWithGarbageReverts(address,address,uint256) (runs: 256, μ: 984465, ~: 786287) -SafeTransferLibTest:testTransferFromWithMissingReturn() (gas: 670140) -SafeTransferLibTest:testTransferFromWithMissingReturn(address,address,uint256) (runs: 256, μ: 748670, ~: 669734) -SafeTransferLibTest:testTransferFromWithNonContract() (gas: 3022) -SafeTransferLibTest:testTransferFromWithNonContract(address,address,address,uint256) (runs: 256, μ: 3635, ~: 3694) -SafeTransferLibTest:testTransferFromWithNonGarbage(address,address,uint256) (runs: 256, μ: 893726, ~: 719729) -SafeTransferLibTest:testTransferFromWithReturnsFalseReverts() (gas: 635037) -SafeTransferLibTest:testTransferFromWithReturnsFalseReverts(address,address,uint256) (runs: 256, μ: 699133, ~: 635457) -SafeTransferLibTest:testTransferFromWithReturnsTooLittleReverts() (gas: 635204) -SafeTransferLibTest:testTransferFromWithReturnsTooLittleReverts(address,address,uint256) (runs: 256, μ: 724923, ~: 635291) -SafeTransferLibTest:testTransferFromWithReturnsTooMuch() (gas: 669975) -SafeTransferLibTest:testTransferFromWithReturnsTooMuch(address,address,uint256) (runs: 256, μ: 754128, ~: 670469) -SafeTransferLibTest:testTransferFromWithReturnsTwoReverts(address,address,uint256) (runs: 256, μ: 733108, ~: 635396) -SafeTransferLibTest:testTransferFromWithRevertingReverts() (gas: 628748) -SafeTransferLibTest:testTransferFromWithRevertingReverts(address,address,uint256) (runs: 256, μ: 734431, ~: 628831) -SafeTransferLibTest:testTransferFromWithStandardERC20() (gas: 667720) -SafeTransferLibTest:testTransferFromWithStandardERC20(address,address,uint256) (runs: 256, μ: 715461, ~: 667300) -SafeTransferLibTest:testTransferWithGarbageReverts(address,uint256) (runs: 256, μ: 849643, ~: 747655) -SafeTransferLibTest:testTransferWithMissingReturn() (gas: 655554) -SafeTransferLibTest:testTransferWithMissingReturn(address,uint256) (runs: 256, μ: 765497, ~: 656170) -SafeTransferLibTest:testTransferWithNonContract() (gas: 2988) -SafeTransferLibTest:testTransferWithNonContract(address,address,uint256) (runs: 256, μ: 3534, ~: 3557) -SafeTransferLibTest:testTransferWithNonGarbage(address,uint256) (runs: 256, μ: 816012, ~: 723827) -SafeTransferLibTest:testTransferWithReturnsFalseReverts() (gas: 632101) -SafeTransferLibTest:testTransferWithReturnsFalseReverts(address,uint256) (runs: 256, μ: 787756, ~: 632598) -SafeTransferLibTest:testTransferWithReturnsTooLittleReverts() (gas: 632737) -SafeTransferLibTest:testTransferWithReturnsTooLittleReverts(address,uint256) (runs: 256, μ: 766198, ~: 632456) -SafeTransferLibTest:testTransferWithReturnsTooMuch() (gas: 656022) -SafeTransferLibTest:testTransferWithReturnsTooMuch(address,uint256) (runs: 256, μ: 801430, ~: 656583) -SafeTransferLibTest:testTransferWithReturnsTwoReverts(address,uint256) (runs: 256, μ: 743827, ~: 632574) -SafeTransferLibTest:testTransferWithRevertingReverts() (gas: 631994) -SafeTransferLibTest:testTransferWithRevertingReverts(address,uint256) (runs: 256, μ: 792882, ~: 632490) -SafeTransferLibTest:testTransferWithStandardERC20() (gas: 656603) -SafeTransferLibTest:testTransferWithStandardERC20(address,uint256) (runs: 256, μ: 787015, ~: 656344) -SafeTransferLibTest:testTryTransferAllETH() (gas: 148698) -SafeTransferLibTest:testTryTransferETH() (gas: 148745) -SafeTransferLibTest:testTryTransferETHWithNoGrief() (gas: 537083) -SafeTransferLibTest:testTryTransferETHWithNoStorageWrites() (gas: 192518) -SafeTransferLibTest:test__codesize() (gas: 37521) -SignatureCheckerLibTest:testEmptyCalldataHelpers() (gas: 3972) -SignatureCheckerLibTest:testSignatureChecker(bytes32) (runs: 256, μ: 57309, ~: 46802) -SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSignature() (gas: 21228) -SignatureCheckerLibTest:testSignatureCheckerOnEOAWithInvalidSigner() (gas: 30728) -SignatureCheckerLibTest:testSignatureCheckerOnEOAWithMatchingSignerAndSignature() (gas: 17670) -SignatureCheckerLibTest:testSignatureCheckerOnEOAWithWrongSignedMessageHash() (gas: 21251) -SignatureCheckerLibTest:testSignatureCheckerOnMaliciousWallet() (gas: 31820) -SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSignature() (gas: 77090) -SignatureCheckerLibTest:testSignatureCheckerOnWalletWithInvalidSigner() (gas: 25608) -SignatureCheckerLibTest:testSignatureCheckerOnWalletWithMatchingSignerAndSignature() (gas: 64034) -SignatureCheckerLibTest:testSignatureCheckerOnWalletWithWrongSignedMessageHash() (gas: 64091) -SignatureCheckerLibTest:testSignatureCheckerOnWalletWithZeroAddressSigner() (gas: 12264) -SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes) (runs: 256, μ: 1304, ~: 1305) -SignatureCheckerLibTest:testToEthSignedMessageHashDifferential(bytes32) (runs: 256, μ: 526, ~: 526) -SignatureCheckerLibTest:test__codesize() (gas: 11030) -SoladyTest:test__codesize() (gas: 1102) -TestPlus:test__codesize() (gas: 406) -UUPSUpgradeableTest:testNotDelegatedGuard() (gas: 15875) -UUPSUpgradeableTest:testOnlyProxyGuard() (gas: 8896) -UUPSUpgradeableTest:testUpgradeTo() (gas: 287374) -UUPSUpgradeableTest:testUpgradeToAndCall() (gas: 309608) -UUPSUpgradeableTest:testUpgradeToAndCallRevertWithCustomError() (gas: 286618) -UUPSUpgradeableTest:testUpgradeToAndCallRevertWithUnauthorized() (gas: 15964) -UUPSUpgradeableTest:testUpgradeToAndCallRevertWithUpgradeFailed() (gas: 18264) -UUPSUpgradeableTest:testUpgradeToRevertWithUnauthorized() (gas: 16143) -UUPSUpgradeableTest:testUpgradeToRevertWithUpgradeFailed() (gas: 18464) -UUPSUpgradeableTest:test__codesize() (gas: 7195) -WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 3840, reverts: 1737) -WETHInvariants:test__codesize() (gas: 6008) -WETHTest:testDeposit() (gas: 68090) -WETHTest:testDeposit(uint256) (runs: 256, μ: 65899, ~: 68384) -WETHTest:testFallbackDeposit() (gas: 67796) -WETHTest:testFallbackDeposit(uint256) (runs: 256, μ: 65560, ~: 68045) -WETHTest:testMetadata() (gas: 10098) -WETHTest:testPartialWithdraw() (gas: 79566) -WETHTest:testWithdraw() (gas: 59332) -WETHTest:testWithdraw(uint256,uint256) (runs: 256, μ: 76778, ~: 80326) -WETHTest:testWithdrawToContractWithoutReceiveReverts() (gas: 93923) -WETHTest:test__codesize() (gas: 11080) \ No newline at end of file diff --git a/lib/solady/.github/pull_request_template.md b/lib/solady/.github/pull_request_template.md deleted file mode 100644 index 1ddf674..0000000 --- a/lib/solady/.github/pull_request_template.md +++ /dev/null @@ -1,24 +0,0 @@ -## Description - -Describe the changes made in your pull request here. - -## Checklist - -Ensure you completed **all of the steps** below before submitting your pull request: - -- [ ] Ran `forge fmt`? -- [ ] Ran `forge snapshot`? -- [ ] Ran `forge test`? - -_Pull requests with an incomplete checklist will be thrown out._ - - - - - - - - - - - diff --git a/lib/solady/.github/workflows/ci-all-via-ir.yml b/lib/solady/.github/workflows/ci-all-via-ir.yml deleted file mode 100644 index 3c29422..0000000 --- a/lib/solady/.github/workflows/ci-all-via-ir.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: ci-all-via-ir - -on: - workflow_dispatch: - -jobs: - tests: - name: Forge Testing all via-ir - runs-on: ubuntu-latest - - strategy: - matrix: - profile: [via-ir-0,via-ir-1,via-ir-2,via-ir-3] - - steps: - - uses: actions/checkout@v3 - - - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly - - - name: Install Dependencies - run: forge install - - - name: Run Tests with ${{ matrix.profile }} - run: > - ( [ "${{ matrix.profile }}" = "via-ir-0" ] && - forge test --use 0.8.4 --via-ir && - forge test --use 0.8.5 --via-ir && - forge test --use 0.8.6 --via-ir && - forge test --use 0.8.7 --via-ir && - forge test --use 0.8.8 --via-ir && - forge test --use 0.8.9 --via-ir - ) || - ( [ "${{ matrix.profile }}" = "via-ir-1" ] && - forge test --use 0.8.13 --via-ir && - forge test --use 0.8.12 --via-ir && - forge test --use 0.8.11 --via-ir && - forge test --use 0.8.10 --via-ir - ) || - ( [ "${{ matrix.profile }}" = "via-ir-2" ] && - forge test --use 0.8.14 --via-ir && - forge test --use 0.8.15 --via-ir && - forge test --use 0.8.16 --via-ir && - forge test --use 0.8.17 --via-ir - ) || - ( [ "${{ matrix.profile }}" = "via-ir-3" ] && - forge test --use 0.8.22 --via-ir && - forge test --use 0.8.21 --via-ir && - forge test --use 0.8.20 --via-ir && - forge test --use 0.8.19 --via-ir && - forge test --use 0.8.18 --via-ir - ) diff --git a/lib/solady/.github/workflows/ci-woke.yml b/lib/solady/.github/workflows/ci-woke.yml deleted file mode 100644 index 53a4c94..0000000 --- a/lib/solady/.github/workflows/ci-woke.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: ci - Woke - -on: - workflow_dispatch: - -jobs: - tests: - name: Woke Testing - runs-on: ubuntu-latest - - strategy: - matrix: - profile: [via-ir-off, via-ir-on] - - steps: - - uses: actions/checkout@v3 - - - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly - - - name: Install Dependencies - run: forge install - - - name: Move Woke test files - run: mv ext/woke tests && mv tests/woke*.toml . - - - name: Install Woke - run: pip3 install woke - - - name: Copy Woke config - if: ${{ matrix.profile }} == 'via-ir-on' - run: cp woke-via-ir.toml woke.toml - - - name: Generate pytypes - run: woke init pytypes - - - name: Run tests - run: woke test diff --git a/lib/solady/.github/workflows/ci.yml b/lib/solady/.github/workflows/ci.yml deleted file mode 100644 index df11c78..0000000 --- a/lib/solady/.github/workflows/ci.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: ci - -on: - push: - paths: - - '**.sol' - - '**.yml' - -jobs: - tests: - name: Forge Testing - runs-on: ubuntu-latest - - strategy: - matrix: - profile: [post-shanghai,post-shanghai-via-ir,solc-past-versions-0,solc-past-versions-1,via-ir,min-solc,min-solc-via-ir,intense] - - steps: - - uses: actions/checkout@v3 - - - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly - - - name: Install Dependencies - run: forge install - - - name: Run Tests with ${{ matrix.profile }} - run: > - ( [ "${{ matrix.profile }}" = "post-shanghai" ] && - forge test --use 0.8.22 --evm-version "shanghai" - ) || - ( [ "${{ matrix.profile }}" = "post-shanghai-via-ir" ] && - forge test --use 0.8.22 --evm-version "shanghai" --via-ir - ) || - ( [ "${{ matrix.profile }}" = "solc-past-versions-0" ] && - forge test --use 0.8.5 --fuzz-runs 16 && - forge test --use 0.8.6 --fuzz-runs 16 && - forge test --use 0.8.7 --fuzz-runs 16 && - forge test --use 0.8.8 --fuzz-runs 16 && - forge test --use 0.8.9 --fuzz-runs 16 && - forge test --use 0.8.10 --fuzz-runs 16 && - forge test --use 0.8.11 --fuzz-runs 16 && - forge test --use 0.8.12 --fuzz-runs 16 - ) || - ( [ "${{ matrix.profile }}" = "solc-past-versions-1" ] && - forge test --use 0.8.13 --fuzz-runs 16 && - forge test --use 0.8.14 --fuzz-runs 16 && - forge test --use 0.8.15 --fuzz-runs 16 && - forge test --use 0.8.16 --fuzz-runs 16 && - forge test --use 0.8.17 --fuzz-runs 16 && - forge test --use 0.8.18 --fuzz-runs 16 && - forge test --use 0.8.19 --fuzz-runs 16 && - forge test --use 0.8.20 --fuzz-runs 16 && - forge test --use 0.8.21 --fuzz-runs 16 - ) || - ( [ "${{ matrix.profile }}" = "via-ir" ] && - forge test --via-ir - ) || - ( [ "${{ matrix.profile }}" = "min-solc" ] && - forge fmt --check && - forge test --use 0.8.4 - ) || - ( [ "${{ matrix.profile }}" = "min-solc-via-ir" ] && - forge test --use 0.8.4 --via-ir - ) || - ( [ "${{ matrix.profile }}" = "intense" ] && - forge test --fuzz-runs 5000 - ) - - codespell: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Run codespell - uses: codespell-project/actions-codespell@v2.0 - with: - check_filenames: true - ignore_words_list: usera - skip: ./.git,package-lock.json,ackee-blockchain-solady-report.pdf,EIP712Mock.sol diff --git a/lib/solady/.gitignore b/lib/solady/.gitignore deleted file mode 100644 index ff0f3d5..0000000 --- a/lib/solady/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# NodeJS files -node_modules/ -coverage -coverage.json -typechain - -# Hardhat files -cache -artifacts - -cache/ -out/ - -# Ignore Environment Variables! -.env -.env.prod - -# Ignore all vscode settings -.vscode/ - -# Ignore flattened files -flattened.txt - -broadcast - -# Coverage -lcov.info - -# Woke testing -.woke-build -.woke-logs -pytypes -__pycache__/ -*.py[cod] -.hypothesis/ -woke-coverage.cov diff --git a/lib/solady/.gitmodules b/lib/solady/.gitmodules deleted file mode 100644 index ba5a595..0000000 --- a/lib/solady/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "lib/ds-test"] - path = lib/ds-test - url = https://github.com/dapphub/ds-test - branch = 9310e879db8ba3ea6d5c6489a579118fd264a3f5 \ No newline at end of file diff --git a/lib/solady/LICENSE.txt b/lib/solady/LICENSE.txt deleted file mode 100644 index c1939a1..0000000 --- a/lib/solady/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Solady. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/lib/solady/README.md b/lib/solady/README.md deleted file mode 100644 index 814704d..0000000 --- a/lib/solady/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# solady - -[![NPM][npm-shield]][npm-url] -[![CI][ci-shield]][ci-url] -[![Solidity][solidity-shield]][solidity-ci-url] - -Gas optimized Solidity snippets. - -I'm sooooooOooooooooOoooOoooooooooooooooo... - -## Installation - -To install with [**Foundry**](https://github.com/gakonst/foundry): - -```sh -forge install vectorized/solady -``` - -To install with [**Hardhat**](https://github.com/nomiclabs/hardhat) or [**Truffle**](https://github.com/trufflesuite/truffle): - -```sh -npm install solady -``` - -## Contracts - -The Solidity smart contracts are located in the `src` directory. - -```ml -accounts -├─ Receiver — "Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens" -├─ ERC1271 — "ERC1271 mixin with nested EIP-712 approach" -├─ ERC4337 — "Simple ERC4337 account implementation" -├─ ERC4337Factory — "Simple ERC4337 account factory implementation" -├─ ERC6551 — "Simple ERC6551 account implementation" -├─ ERC6551Proxy — "Relay proxy for upgradeable ERC6551 accounts" -auth -├─ Ownable — "Simple single owner authorization mixin" -├─ OwnableRoles — "Simple single owner and multiroles authorization mixin" -tokens -├─ WETH — "Simple Wrapped Ether implementation" -├─ ERC20 — "Simple ERC20 + EIP-2612 implementation" -├─ ERC4626 — "Simple ERC4626 tokenized Vault implementation" -├─ ERC721 — "Simple ERC721 implementation with storage hitchhiking" -├─ ERC2981 — "Simple ERC2981 NFT Royalty Standard implementation" -├─ ERC1155 — "Simple ERC1155 implementation" -├─ ERC6909 — "Simple EIP-6909 minimal multi-token implementation" -utils -├─ MerkleProofLib — "Library for verification of Merkle proofs" -├─ SignatureCheckerLib — "Library for verification of ECDSA and ERC1271 signatures" -├─ ECDSA — "Library for verification of ECDSA signatures" -├─ EIP712 — "Contract for EIP-712 typed structured data hashing and signing" -├─ ERC1967Factory — "Factory for deploying and managing ERC1967 proxy contracts" -├─ ERC1967FactoryConstants — "The address and bytecode of the canonical ERC1967Factory" -├─ JSONParserLib — "Library for parsing JSONs" -├─ LibSort — "Library for efficient sorting of memory arrays" -├─ LibPRNG — "Library for generating pseudorandom numbers" -├─ Base64 — "Library for Base64 encoding and decoding" -├─ SSTORE2 — "Library for cheaper reads and writes to persistent storage" -├─ CREATE3 — "Deploy to deterministic addresses without an initcode factor" -├─ LibRLP — "Library for computing contract addresses from their deployer and nonce" -├─ LibBit — "Library for bit twiddling and boolean operations" -├─ LibZip — "Library for compressing and decompressing bytes" -├─ Clone — "Class with helper read functions for clone with immutable args" -├─ LibClone — "Minimal proxy library" -├─ UUPSUpgradeable — "UUPS proxy mixin" -├─ LibString — "Library for converting numbers into strings and other string operations" -├─ LibBitmap — "Library for storage of packed booleans" -├─ LibMap — "Library for storage of packed unsigned integers" -├─ MinHeapLib — "Library for managing a min-heap in storage" -├─ RedBlackTreeLib — "Library for managing a red-black-tree in storage" -├─ Multicallable — "Contract that enables a single call to call multiple methods on itself" -├─ GasBurnerLib — "Library for burning gas without reverting" -├─ SafeTransferLib — "Safe ERC20/ETH transfer lib that handles missing return values" -├─ DynamicBufferLib — "Library for buffers with automatic capacity resizing" -├─ MetadataReaderLib — "Library for reading contract metadata robustly" -├─ FixedPointMathLib — "Arithmetic library with operations for fixed-point numbers" -├─ SafeCastLib — "Library for integer casting that reverts on overflow" -└─ DateTimeLib — "Library for date time operations" -``` - -## Directories - -```ml -src — "Solidity smart contracts" -test — "Foundry Forge tests" -js — "Accompanying JavaScript helper library" -ext — "Extra tests" -audits — "Audit reports" -``` - -## Contributing - -This repository serves as a laboratory for cutting edge snippets that may be merged into [Solmate](https://github.com/transmissions11/solmate). - -Feel free to make a pull request. - -Do refer to the [contribution guidelines](https://github.com/Vectorized/solady/issues/19) for more details. - -## Safety - -This is **experimental software** and is provided on an "as is" and "as available" basis. - -We **do not give any warranties** and **will not be liable for any loss** incurred through any use of this codebase. - -While Solady has been heavily tested, there may be parts that may exhibit unexpected emergent behavior when used with other code, or may break in future Solidity versions. - -Please always include your own thorough tests when using Solady to make sure it works correctly with your code. - -## Upgradability - -Most contracts in Solady are compatible with both upgradeable and non-upgradeable (i.e. regular) contracts. - -Please call any required internal initialization methods accordingly. - -## EVM Compatibility - -Some parts of Solady may not be compatible with chains with partial EVM equivalence. - -Please always check and test for compatibility accordingly. - -## Acknowledgements - -This repository is inspired by or directly modified from many sources, primarily: - -- [Solmate](https://github.com/transmissions11/solmate) -- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) -- [ERC721A](https://github.com/chiru-labs/ERC721A) -- [Zolidity](https://github.com/z0r0z/zolidity) -- [🐍 Snekmate](https://github.com/pcaversaccio/snekmate) -- [Femplate](https://github.com/abigger87/femplate) - -[npm-shield]: https://img.shields.io/npm/v/solady.svg -[npm-url]: https://www.npmjs.com/package/solady - -[ci-shield]: https://img.shields.io/github/actions/workflow/status/vectorized/solady/ci.yml?branch=main&label=build -[ci-url]: https://github.com/vectorized/solady/actions/workflows/ci.yml - -[solidity-shield]: https://img.shields.io/badge/solidity-%3E=0.8.4%20%3C=0.8.22-aa6746 -[solidity-ci-url]: https://github.com/Vectorized/solady/actions/workflows/ci-all-via-ir.yml diff --git a/lib/solady/audits/ackee-blockchain-solady-report.pdf b/lib/solady/audits/ackee-blockchain-solady-report.pdf deleted file mode 100644 index 04ac76ccfc3e97f39a96b599052169d00ff55428..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1172173 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%0DrKa^q8V<+d2 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ˡ*qf(9)5# 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⩔Czg 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𝕨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≪MA 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

22wH%WYx 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ĐF38~8zfuXvOhw- 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 U)Q9N=*Ro%J{`rr8`O}yG0RNfUumAu6 diff --git a/lib/solady/audits/cantina-solady-report.pdf b/lib/solady/audits/cantina-solady-report.pdf deleted file mode 100644 index 3854df00f4191a1325b46bbdd2d1e930a778aef4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 950468 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

xgS?K*R9EfgoutqrkaDz&vV5Na^V9 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
    T}Au3u0P@2IFXOTm{Bcb@G0=FQc)C?~KGp%)7 z{`r0sFrlu01fA242ZSPfWbbC7{{*5QbM#5VbWVduzre*5)*-3|d(J1bD*=AujyJtT zz<3pSnu^Lj!=#mokO;Prs+4Ikf{=1kuja<^4I*8T z1>k<5r1;Y%AOblKGf-d6U!HFJoIZUF%Sx>@!0`bB7?fBAQ@pt}wyR~S>siO=Q^_NJ z#$SY%kg?2@J7m~)D?hldeEgmqsVn5*2}mF>_fBJAeU(LZmvr4Z@54w=HI-X}I zW;d~vVAR1k#P^=B%&5vHneY8XVp@~Xk;-IG3E~OIv(N#n&uY9?NjD0GF;p2v_toqL zP#W5nh>H&AP#Pk_mW5pHh9%B<&&Qq`*t$LSAHAQ884qxWIkkyo$-A;17CNlk&GW#O zUccYb@8D%m8P8bB`(^T`Y=WF(0ZdnSJSCkPm;;sN=3WwJ*A#_^FOMjbHF+pfBs?-` zu5sCu_SB@|%AOfI)=9IMhG|!&Uc~G=x?}PS)otDbhmIe6wo9qL{c9l?aoh`gk`M~A zxE@7%PogV535!&XE8@oPM4401j>z;qWo*bZ7&VAS9AA;26=>X1HF-ov(@=p`a&X>B z^iaJleC+IST!Ifh1D}vkLeH{r{;j-|B99J^ot!@BrmHIr_iPTlptjHc_H*~f2Q#s@ zBG1wM)(B^Qh03V2!?w@Mi1NsX?VpEzEJ3&9MB*L^v_O16mPC>cDALbsWi25GA1_i} zXVz!l8R*2(13yQ0Jv-peX$|FW-7%-~gR0%zj>r^yAv_QsJ4X&*E0P+3AK7>O9J0*h zK>H(#T;5&rJC8^eUwDS__;gD{jy=&@6;<>&+~2!fU;*M7JxUz7gNWf_PGxL2HFUR1 z$xTNqloJydUC_Wa=nhn`g7P_J21kJqSsd1xsRljCDmMASY|#+vnlxDv`)p_DXEa?% z6328gmdZIOWz^|*R>vbEySlb&e$nD8>G?Cfbrlachp`ldkyMG?lto8L$3Oi`Rn}!6 zHmL!r=@^-#fy`Ad6G^y{;M$}ERN^G+sBPB;V~@O|j$%=!XiJG8-mjg;$xx-KR;h*y z%c;J`kl;i4aPPYqxOv5St!K!Glov0Xy(D_ehI}yv2{1dp-@U#h>MS9Uf?{pZQVm-b z>jNCED*Yy;jSYVBG_N_F^Jx}y6vI zE|N5P0rL6wfQ)V4qstf6PF;-Th8S-ni&MYU*@(WOliK@C6ADB1o*6mJ!)iQiOMk@p zxHmlI@ZGaPAB+!t4Y9}IglH7OKkGZ1MZs!)Z?7B?=8>E8c`VW;yCg*J#*`&=KB-_AOXxf=g3m!L?KA92+LzPe=b&{pEWVo0@NoA1j9xzGIr7zg3i67bl(kCOU7cve80}Ej&dx$~$n}DyA;(?N(gQ5DQX}w01e_fF|OQK!tz*^JQs=E5us7E>9=(<{VdYXq?2QnMV(HbGjpo z->_V{$f zB`R)+^!dW5vpy1~yLjebk^VQljj7J~z@$5btMFKv7QM$%7ie}0XI?#oY5X3kNT2y5q>Y=Uz*!Q@l)2mas3uHxYOH|q}$EJu*OmxGoNgUZ( zx;qda>T`Chtzq4n3lL0ar)!(HbqZ}>hyJHCRbEQbPAzb?#ED-Y4y-a zRlOgClO-CJcy$*qA$ixM<{Wa#EANEFU7>eUXOHYMQ!hPs?ylVD3o~v|*ZmZ3E>Wz9 z3#ktTI+?Oy+YugpOcPf=gVHFEkXe!NvmZ9FuS6v|Ml!-sp$a;hPE3o`6TR^Kf~s=z zHujMsIK^4Mh|fAqFD9U^5*2@bSeaNRC!KOLooYYADuCw^0^lY=9pY$-fZBF`yV~E_$WU zxzl|X+Q9H21nD$q{gTPBCvZt!Vb8plwL?+ZC?PXbI3#i_U^Xu7^IFI84VX%f1blt5 z-Q#eY+zKBTqI=4dHVcrv@xT+qb49k8%To}Td*Zn-A-)ypssv~65DLTtR4-+q(=I9i zy3Xx2ENOm=2QY|>#TO*=);1o1Pt%?ZIP9U=!hccbARlK?*%sW#6Ch#lz&QS zG`i>Ls(I;IE*|!gXEp~>0XWhEITA`YAN^egMQG&Z!nQ>!qCB7cS$62LJ0SI`oJXNvY^`T*q+kZm^3ywVX5TDV6bY`6DtC)R8F)JloR#nylAzruH*!Bo)IC zp7$JaoB4m0UlXQKqx;a#S@)}$i;j^7DICQ!2RpUg@x2o?&|Z1VO4hTlIVA8AC7yF! z6qh3jp=!6UbP{OspL`}q0yRVh$a<%^l7c^~pePTUENR2~Q+ zo1}2&9`Tm-st|f`X9B+3Pw|P3lmWEUY=w%(-Xw&FLYp0f{qP&;Zd?$2wi4_H*Ej|^ zV3A_^IL%q62nC(%y%+;~lHjQo$C$IqKSI0x_Zd8-NN;9G58=(E&F_%_6*z)D z?`)$|Obk22{ziJa!IIb*+y8-_tT$E=S+yC-J^lL~Z@4PV4z=vD%)4izGSG0~8;o!@|jy+Qypf?*7=T=O7b*!(e1W=31S-?gW zr4eFf``AVkrO}yUy327dN`uZ$ihY!Bic~DhWw+ju(B@ z{c$`-e|HFyDKk|g)TJ<|{L-W*bhn1zwgfiBPE_ytcTJ$-8Xs4|4s|u6mnXr~+l!_r z5-U;59Y$kt4dqTX+_sfPT1H$qNb#;I@LlY51Ve*CsOtyg*5J-|G@Wx2ipQuA?j@%| zxy+34V6J3J5NThR74(FKoL`rGyl3ko>-&ubaT6;!SM%iB3lc?ap$JU0KWqWIqnN>WKRnW8I&y4xWx z_B(D3_?c#1bMcUtY{*ll!lUQ6L55OyzXYi09PjuY>`K_7jZu?#cNCVMR^CVVB7s@{)bFY6m)_X#RJIR#lFmDoo-0AX#SG}6^5TE~J@5{rX-rx5t zNn|M@`x=@gTe8Fm$rj_7l2n!`Eec6lhOtCNc7-gHqLE|?X|c;rLZOs>r|iryW`3`+ z%W|CaS-#)j=kxvJTvzA1&ULPs zhIOk$OQW)h3GeONG&3^o$yOeqvwk2j7@}@*uUnT)!+!gqQ8IlI$FrhDViNNLI*O zb!(B~2C-XTHeC87`nX|nHhYE1zRn=%^YKdgrNsO;Ncqyn?9I^{iT;45(Al<>)tJxc z)EkF3DF%^69SFRp&BnW7hm$h8%ip=^Fk~a|UH=XpnwzS1$DI=g@?l9Z5fZCCUMtT| z5uA6bg6peQ{N82^EQ4b;S^>7!#}}xKs?WJX?{=<(%V_2M`wswvn+W6|AWU8_yC=!E zFW1gn?gW(AbGsHFDfu+vyQg=@^uXl4iPEj&UCBjIwRKITYvilhch``FzI0M|89pVV zk^2cm`vuHMmG+c~N;V57(Ab<5F-3(?V<3D;3Vu*K5hxdk{3F`OSl5x(p~NNt%*s+u ztrCL}LUto-9GJc?m1al2X&a_;@F=c6cdnrU%_!W4*p4czXTWmwiQ2Q0oY2_Sky1LJ z@*+%>XP+rEra1mcgZh)DJ-0AXG|k(6gzDh+vAQw5z0||i6Tak9tYNCtQX;WOAL2nA zw9cxh@d)1_*J4(aScM8Ui#@E1IhW}S`r(}_EjRQ}cvEQOYD3l{i1y|80>10AXa!Ym zAerTpaJLuy>*0^*3iwX<@l(FFgaLBYNJTQ$>Z7tSgit(J!BO<6TuYhvlYxND0z7__ z5+Jh8yWsdy3SIPcqux0PVd&lav{@{uiMBe4`q3C?5__GQlIGb5Ii~psPAMxNY`KMM zdBY>Hh1@0_mL!lK0?RRoBLoDG>YHc}P@r0@yyfIkL-zRhLU_L{F@k1^Tn)jn1U!X) zLZ}PyF1D~>=p1wS16+VcfGIylA>G$=Xv{f_>~k55xL&a5KImOo$5qcZnp;eAc5N}; zxt-_d0a`F6vAWxRdL^NnIMj5I-rKCpEckXz&NHh=`VA5(p9xzWms4JW*|GkSMY?!F z?6AxJ&2GL5!qp9dbL_|*MC+69X3i76F_jYNUZt^$iW9n5i#{_rWj5ga{qU|qYnPSN zdrh*-AxD?m?m)YI*hSOGsepAWIEOyHA?H6+)QiO2hE1#q$bUiG`59Q%hNBQANKBxE zn(cNn6bIljE^X>LAXx{%*dZ((`qXVu^=B!|Q$C#T-7%D8luE`)@uJL7EApBUZ|hGf zWcNn1F9=NdHz9Opnsm;_wRvdt9NGgi8_uH z{ZZ}ar&{-Re*WrXI_A?QuYTCyuzi-5CgL%T&I1|g<`->+M3nW8&ypy41zOW2iVAt$ zUM7$G?Tte%?;XefAgXqT&oUr$-1K}EcdZh4Ki{QCxaaXF;RdYbv`P>#4!JA>v0$MA zs@kN9RI!1CyG%*Yt*46sFE-o9 z8?^OoirKXWEpWt;c;ePx27?}W4+<`i$_#~JD@S25XAmV5%It)qlLk10hGH~pL7Tac zYL4Z^bySt(2jUYDlPet5f#z==%*eXvmf-~x5yllvAiXsZqUNWi3Fm_bpB}t$R}?yB zD(W1@G6;sj_g^nuEsV$+xyznHxm(rc!Qx6EUOh6D8F_xnAj_ZO0fVaN$NQmeD%X!Y z?v{()ti9=lnme~KHf{H_>G7;AMPe4*`3lZ~JA18n1&Ner0e1W2o$9n~w454+lg8%m z659``rD>`q9X6;1Tdh@AkU=*d!31F_rcqW&7%1B zBORBObtq}G-DNu9nfRyl8#aojSAnbJjIT$7uykW>gyD)WYT=pB!8GVg@nQ(A`vAEI zEE3CUH^cGfjRw&6eggtyhTJi%*XSKc7CxVVMNYNQJW$1=9e>ti13BWdNkrePU7EqcYPfP4JiyR|UZ%M{h3*jkX&DrP9o4TL2qbGiA)V*W5D)Nk*bO3asKTfwDu)sv zoZws`c(ZoUUB(^iJsZa)GM>Xku!Jdsn{_Hx5yN)4mN5brA*)d5p9uah7r2m7qJzch zSG#dRXA68%E4BRmx)Vw!l9sd*$t^F1Iytl(hZjMI3)eHvFXjZ%qE;9N>SD8TBq8)J z3Kq-@bz@;DoUI#jf@JX3`~mz0ukqR70~G@C1o!;zP)wlQFw7ml>jXZval8`z z+rW66fjYLCV98q2|FQxM5lCMF?CI+`JJe8Zp^q+NC+eIpKEfa|J~ITgl4;S#swKd- zz6vY!{yxW*r;*$|0U^!kgz4!pJU5lieoHlvw|r^TV7of|_h<7|{jrbZRDu}oEfFiB zNFCA+j-tmz_ejn$dpNbrjM_beDBRx}Klj`}T`LsmqeB7D=cL}e0?)?@`Ml>M?t=CJ zy+JEbW^7UCZjLrVfrCVLoQMuAx}zR3SAFIlGx=1jV6dylNc@}3b9S1Z=Tz*CkIMr< z_j$)2J1F0;Xvfh*ec)I27M7uJ9MI+fyzT3pRaKTpfUGl;i1^_^@e3N=q{Upwu;We@ z;sU}Wf1+$Qr~9^WBvCVer9pPAfOMN zaqwvE`w7DK7M653E)Vz)ve7snK|MfTvt| zY9a#k!RSpLf&`CB`V}HPnrqHYM~hz4fedx-U}91$h}XHP=KZaOuSIy^<&?P#G&~Uo zN^TJh_+tO{kZsce+eGc=BKME(s&|>q#=;_>wXYVf-~>7%Ef;*rd|pS3v5(&KeJtMd zvr06!iJb#u~JumCR_{^1e@D%O=4WSbPeMv_>Tc0;@hj_YEVS{RUbD)P_ z0Ydd)TMyh3XYf3OZNo;PL5R+Tc1^SPOXFL-P(lmpO`J>*ylDgSp(z%qsD!%46UcFweX>_LTDAPo2gxKd4vsXQ3OMO8P4yzfy*fdPTx@V>7?#<7$ zyXJB;cmH&5F5%$YWZukh@WkBg0$SingcnQfz@-65mE&s%kN0iFInA$7z=<~9^ zjQ6t+Pf73<&IFiq!+RL^UA4q8L{6VTrEX9nedgUDgw?|km3+Myj{nZ#FxZK&v%~?b z(6>-rd=?LKhb2c=85&oU#f3^3g|KcM7wJtHfE-mT$SFw3NP=N|Doz=-AByTJ`gq}y znuQmG3l(LE+c9~(DFGEUAJ6O84yb=FGgQEMAE~^3TQtR$ zPX}}VYQa1P6>Y3hxNJ1)9pXX#GVG%b@ea7gh{$Sp28Rs>*@wbzK zC|!#^059tT$rZRRF$XYogevmW5WY1*a&=p+`IC zq9MXV@E8Rv=f>??7dD^E{CIy~fJ=3%M^hc;5#%eX!aFvH-80ZkGv<#1Sp+bsG*(PfFE-&v|XstK`s3#un1|tWKG!# zk@O4O|Aq!AA=Tp$D`{Owz^0QdM03g9d}I}#Yst_XxCH)Ee6}=Zn`^zatwQ+Y+%I{0 zT^zs5mXDKCL1Wkthl0P9o+Mh zLje9_vTOz?r2-8?vRb!+Ntw_jR7yzhB_q5(iKTmZTh=xeCQ>oHqh=>T4MbkZk;dZk z```$f{9x8&q_RCWwFGm5)pKs`SnocwXU{j+UaZU^7H;t^ZvxBeAAUrBO^sz`DW??| z3*O#Z4fV>f@ZFA-W5L#Njh*KaJt$@&b4O?2A5vCn&!h6&~S=9N8H z;n;QZLsZjwXtWo3fJ+cIuyMrmI_@&+93s^pQ;Vg`%VxJQL%xNHh|h++X6KowN@S)C zqNJmY!pMw)Z^WTU=YjFlQ%^2z*_k`p-%iLIwwPns7TOZ=SRu$5){@fL#s;oFl z>Ol3uhJkF|=KERkq!RD;9>z3hPwGhi3Ga}5QYevSxyYUUc9Kp2D5vjX$-rh(A1>QeDXcDa42z@9uiPkiCD9OSqd%FUcBI{UN)>I3}RR8B3V`%#% zQ-HxlOB&(TNg#b=uUJihAm2#AJ0uK48ZeDhwP70z$ATv5?(Tq`P`-I^8en%dl_@-X z%uI4jGA<{R$dt|Z@!R8#r56WR2(#DWMM8@AeyKk$Y4BYL*8FV^2IBmZ;jXNbYlq4%(5RjCf&4&-O#N98*x3ZKyQptv=9EZ*;99ifi&v7x4L;Q# zb~3em9InJ(sHH)5>GBSH(XK08KNlPHSDL%Vgb>YNfWtdrV6)>I7BxB?KL=>{(`J@g z7DQ?s!oU=O{sm{%9lumbVdqyGOfO6~O>u0C$aUUOsG-?Q%xw>djjtAzn{2<)e)6%e z=uH$yNsNIpmGfW}38z#49+ZFeRW=8RW~}h$U)h+b}BSbWSP4BhpU@lPNI($!azYpNW4! z*-7Lz?&x`3&K7n`1>v=kFU)|Dyq`#>F8~0WtSuz(_b-8SI_$aEkv-yZqjDY~M@^&}DE$4UD~U7+TtuQFPmDp$8=Y9{aa9(ci*%61hCR5{qCTGaxz zYef9wr)0@bYwdEZ{8Z9Q&=y-^t!v0@syRQ<2wp@ z`rEHXlX(A37=bk4!81^?S9x_QDsKurHOo{Cls$_`dlqlT%L9$82(=lsPv?gU#*^9Qh$Y?pIHp{MEJU zMWHM6J4zhQikoo6a<4{z%&BoSxfJ-^a%vl{Ub=Wijh#CI;&wOp*%23&BSvs(dDG#; zg50TGb+zW?M>k7EGnptxtt_-U(gTadTUjre4szyblRXaNr++0P>sn!C~4ev#Bz;UXPM2poOn5wBsz&ekY~ zmrcO-Pka$3(X%*kfHM+t+xl*bciibgVvTU$q;u{t1s`b=7n_qPTbYVxuQs)o{iODH zVeZ?6+E%5LLV zgoU-v&Kh3(n2r~UTzn}bTk~!5;vDZn1borrzQNv6D>eVaTqHj56;$<^(O?AZ-MH@$ z;n=!_bD8%&-rY?s3GGCM)g>XLzUos=pv)0{IK>V(jeDE@e6f#jxP0tz);n@palrbK zSJSftu4a-m8f--OZ^$t1vrx>OzOo`paFO*S`D;6qhdx~-jb%7&5~7*|?*QUC|ue2Dnq5UA>`q?<+?O!rw%9|ivo(BGtijE@{x4y3^aorArh zFQmSS8cLl&UY&ZFH5d&n%2!EqgZ)mUhFBbR@S27}W=4v^p6sJOFGhPRN~cbXU8azH zo%InNOtrw(%=QG?W|P`E_-9Pt^GEZPIOCaRk@r9zm@`%YJdjkytdiMUs=5*&a8}sD z4mR;tZwI{Dk0%eWxTNr0VRz*5oXuS#`F~yoFw*5mE#QyjZ_b$Mmg%ib4X#&a&2(?9 zm$_>Wh~vIZCE%*&d%-|x@x8USGZz4&kQ1-%$Q?x+<~8iF#{jF|zT_eN0NZ&SZ(;Xd z>K5+W_fXZ@39PrV-g(6?Pl;^aS>vFW;*vH9LFPLvc>RgsVSm!O(=`Posp#+}It2!~564X#e)*jqLgP}Jt5srzn7Qpe0zgK^q$W;bPL*W;@(av`KOQs8(S?dit z;rI7zTbr1QY4E-|>Ph`^-|ox;rwEJmaZ{ohgIFBS#vIHvaCNpkj(fYn8pK=$y<)2o z)OOh$aR7&>f7jk;n}x4Mg4^fZ`31pg$>i#tufTHyC{%Lc*RtDJ+O;){-LAUlgz!d> zRjLDM7ctE%0`F{2TZcuB6 zLm=)U{WdF77!LvW4bk+D4nfiZ{3z3&wsY0%nK82=-7~!**w|pSyz5!c9ftxeNIxQ=-oT@$<(+_t($b zxP~5S-`!)Tw)^f8x`WX>Zycog%&pDhl2C0n4Gttc-VuH2GmG>yzG<)HWM4KRt^$YK ztmM8zOV9{MaB<6e&kYnUWLfm<@nJkl$pV|$vxEWgK?6DHK*R&ZOREqD;1Mv5L2CFp z_dhMNV*|<>{OHpro~EX)7UY^&eq4f1rT}ng)@}_|x^Ily6el@$iS0WX7#X}R6KjlB zb%Qhan!QAXOMreb$|0-CCdUR0I2PRA<_;9fO?6+C9Xz_*B{F~;`nZ{D#K{S|!oo2y z9yAu?kDHkC@Z^Z=-LSa8H7LI2?)VcVA%l`WV|FYo*!%gZi&w|S)|hIm#lwHiL$vbU z1pIg9IgPES%YWx-J*&GJG>i90XXgHGIge9omyz@p(_JfeN7pF-&cehc`agky zbH(4PwRl;Omt0G5d$JNaK#Z}S%zTV5U2eYRzaA%Tz}T%{(DQqyFC`dH`=qI(XX$`y z^Z*;>R@Nr5ez;kJbwQgdUa`_{t})V%zQoy>6vC*x&x8aUr8y z?4R0ZOZHeJf6eT=)QQD-8-MyXG_uub`@5M#7<^o@Pt`TeE|qh+a9mh)17l|R-{tcB z(rRVAI|;IJi{LHMHHiFZirez}!E{=r2KH&ukuM>(+Ywr`FmJw)FM@d;NB90oArC#g zd|}Z+9?ox|;>xc4S0_J>Z;kh0wqKEI6m zO^3Brb%O3@>lNe_4>W&Y&+vI@zGr<4i1sO7TcFoa?q)~^2I>+()cF^EJbD!9s6?8l zVpWPPLWPL9kjL?oGNu}v^<3`Z+(6`A)&Ti+MeCLl>AG{8r!1U! zhh+Iu>LS1NvX!=72du%KVBQ=qZ~g`U1$9$(c5V3uSHSN0;7<9vR@xZ+7YOA%Wy}rw znr2P`>*DD;>(+XdF{n})ayNPVClq~a9oi}Gv$PoP#Amxg$=AA2(u_TP@g~5PMyQ-n zWDu~PFacC>vyY~CbvZg%`;5bPG_DMn6|CP3FZ-j$pckJ0)$N{YhlhyN-R?j5jr0e_ z)6xn0no2hhiY}VJZG`Mj-w)hmHamhRQH;hIf=tv;u-{lXBY0D943M|(y0ZC31UuL6 ztlv`+x;zz~X(7kOeA<=~ac+mIt^MA0RaF}&Z?22@&8_BqoQ!z<0=U)uZfld~8gBPe z-MCH?=F@k?B&;-?GNON(sSWeso3C-6E;MB;ak(os<)V zx+))z+mS8v)^_3q7A=0~aPHExMAIcXhfcK9<`rbK(r@;bwrP6{ z09m|;5vy9+i8KsYJQIODrpO$-rmlDzKD6Cd0|M|BFYOU-iqI-@X%>1O`e+8fZ`%>! z7kJ)4${%B!w!ICAbZnMbHhfd|pFB5vvgo(V97OF-;Sn=vriR+=gkid|refUc?$!4& zP)va()dO(u?aJF(MC?-&079ha)KNgHTtae2IGY#-b`Hzy8kBUzpadZkZRWS7j~ zmj56S)=l8AL+7J|310)k^c_5cQd51*|<9%i(yXVK=4TGZf=b}bRI?%Ihd7Q$5@uS0s`$r#E6g-<=1P8pB^2ECi zsiSu%3YGoI1nBqify-etfay%Ng2}>>m78vRo#*3Srnjs}&#EQFUfLwKHeTY&OEec< zi5FWDFu4jHaSH9;g>PDZHODbpg1DW4*)P6)L3~%$XRW-qg*z5@4RIf>o~+|eM-^W4 zhesj4MCh+&bd_#OMh#QId)U3!i>yL%dGa-@M)DkG7ctgvVFaHwp4nc9v z!GkF;X(qC-4(gfr8*Z>^f@cn!@-?MjZe=&*d|sQhB#+IobcNwwGjr_?zRZHTxE^#S zdd*I>*Xg=l{N>f*+|Vm%Zofc#%>;JqwU*NY3tlt8dA2-a=s>Mfc6E<&eZIv9TrG3= zIc8G1xpd(1$VlD3jWgG$R+(-$oyZaH3#E*0y3+spK#DjojbCk`0-(LQ<#z)iB>Zu* z?c+sN_ijhWwvdz`$Fc^eUMTFh$Z=HV8m#pTkbFWpAAP1fW*r9--ErjaEP- z?TLj6#h+R3*tX&`BHsBiHel4*b{d+0x9)3^3h!k0*mNQi z-v~!(qt1EU6r0NBkf|oo5L~p;T{hszCQi)Bn7ZZrzXmFBMZRH`p(6{NDx!?u z`1|pw6^{(_)6-qDtvzEc1LQ;KhSrufR0l2k31rIn`DgR! zhjZvL9l$-b%>M+-agOTQCdE-4LR`pjWS1L%hPM9%2l6zi)GSE=PN4M z%Qip6zYaq%Ac{Y~%qHH}V3!H`3-kiB=j<%|{fI>($k^-m#GU9-)^@@I=KXM&e0QoO zeX<;RBT^l2L2D0IB!z=VMV>!n(nDCcoJ4Vp7i_Qv8h&?|-Pw|dqPw8e)im`-Xzgk7 zMa2u#pn%zoPPby@)vyiVghdet@WZ9 z3Q!VVg@LsjMS{vbe;ZupTg3)sbPO;JpW-q$Tf+n6(NOSWl+r|ZM`Tio=6%#!d>^nhUf36^oEZ@GiHX3*&~F(Prh>w>3W6@z(CYr@YTw}7FiVDAwt^Qg;XcFCbeALo2}#9aG+TlIWv z#PqeeV+use7HDo|d@*9nNS-kT;j1@SCwcg?zwB6Dka@fp!BsC84JEzy%mCxM>@*FF zXaBJ^C&Ac5r)B&Wq0JWQ%606zfcfGYsiWYP>-d2!uhj?+^koVySOPNqI!Q$P3N8^S~VRQe#K;B~?yMyd-f8J~Ln1@+(eTTwLn zn-$K-tmkLr((=gAohmRZsmwsn;<$-#=cMi4Tsa|iP>1W0JKdzatRW-5>IZsXEGg#Oq|013^ZF&s;pyjwZiOWlAmOQ=2Em@aHP4@P~92|KMvlT;~dw7>u#p*d?0J&s~imGNV`YiuU!DEmiyCh@O6cw6ku$#Dn3owsI#4nq2@Cv{lvG;p`@ody!0 z?Z9d0aIkSV+D#{gzcE2)UZ6|MQv`il6#1g7>B%> zig6{8+x)EoRd?&%XriO8y93saT{nOJLelZ1My>*yU;l~adtl4?s|CVzw&r#;()1m} z&alBt7Y}Rzi(ToaKE%N)_3X>^6TPUm?N8(9h%jwi$hP5csVdVVgQ4_J;2*=7=r~-p z|@ubDK7J%Z{1EuRL#;x68zygpO;k*Ix zDI`aodf2&NF=`NI`c|o0(a3o^LZJ zH>$-ntg6^69Or5^IHP)uVttZ*<@dnrt|=?!+~mDR*=YzKcx8oC8d1+9-;tcS^cVN> zi16ECPh9}^pV6UU=qr`Za7jZBX24U^YMrDB_S7SUP0|Tz!2#pUl&95}(JkBMPYkEW z$;jv8JRtypzskQd?x2}dKkUg=b$z;jfzX}^`Vh>@Iua8goM2=0phW#G&sDA^1J-nG zJ!jlFC+>DM#C*uCJ#^N!aCS|yN5!C4<}JC9>XOi|FkGFXF#7VlN5?l=AVHdvCGMJlQnqI${ZhL!^`bRbUlVle6y*9q|-HZHp zmKvTP%^0D}et>PXph_))Spxo)Z>x*nv-AQjLe2Z0wK;Ykz68;R)Omu!n+f;|l z72oo7vZ=Sl>IjdF#c@q7Jr_@;LdN2G3p^G{MZ~D?UdlbjCI~istkNg$TFj1SjlJR5 zN|R=el025KsuhP_%Y9hVu#6VZjOrd9jCzQ1evQI!}5NrGh&hC#M4cbp<$ml1@MSuJtIS!7_JW!vr0Rn7?gg zE$`{{n~j}x>9Rp&#l)cjt&?%$<=x*Js$GWv(vblrI7JiGN8FK`PEzgE{u6)Teha7i zQo+wAc+s~J=9=k1mO8-{zUs_iMbCM!M`Q6h*xgdItLQ{q^bz{YI5F>nsO+^Z^0WS^ z9pxI3U41L4Ke(Q`jk)y%)XjWyz1$?nqn`5HsHA%Oq>~^v7m$kfv?FjHI z+m^9kYV?}}3O+CvHVtBBQ+r-sR$4l69IEPluvi8Hv$z@ddC2ztYsd6q$-|{7`b5kg zp}dUFIzth8)N(DHwCT9{N_W|(EzexBTCJ$ioeTZ+Zu+w|Y3JxO_g8{R<>31z#Pyfo z(lq8O_?W^B0&au-t-?#RHcsD%T55D!nTgb_eL{k^$c6%=6YK8B@$AC8QXR)34MqAj z7bq3|#-EKpz7@vk$#DlZ_;HCou#v5MbT20`CrUERDLAne4{3hFfirp02-wDF_FR)! z7Ge#)e=x^r<>xK_tk1L(@P;lob`^gyu@P>6yrgU{98aGqo6Ll=ha!;mts@<=f|r0p zE6X%>^L;ZCfSdUrD+_U7m^Y*6_tu&v^47EqD)s|o;n&h{ z_l@=pHH@&2c=zU~jY9NO-vW76s~-NMA+}+$Zm!1rm?uM@6bR?r3SH(0`L9D@_2uo- zX1AW|xaO$wrzzBb`LB!`;G-nViOSjGzWMa$l_j{;EjbH=2F9WP$Xegn`bi1Cv32NE z)KvI8n)0a%xH~wX{kt*k0K-+HUvwSQ;V*c)U&j1w*#AZ!aT>@pkLC;Bx5eZm?I%C| zgiXM5=4PBT&pm>A_cgxIin#kXGqjIo>}3}AUOcm3->L7vge+b$wG^iOoLARnUTi?c zO00MScUqT$arXU!6OW%^yxe9E9uIge|5iw>5E$$9T$>h=!kKH6_cz_AKNT{AD}Md6 zf9&+hzy@%IW_G+fz4rv)Z+ILrc6afGK_We<&K39juS6jcHNEwYF6`L5VU1sD`-we9 zntuhjDOxZ6yv|W~*UK9njb{fe+^fqg56*hq*3w?GMo7J0rM6Rp#MH2 zwYW7}zBvVs{LC0HmV3>?yz^pJmhaDh;Jd2M%lFGx@X>$? z)}QJNI6ScEd>;!F_2L&?8oR;<2_VleQ)P9akIy&V+gm7MC;-n2UAvGbRCZm5`Xo~!T zPKU^NT*jDwLa$xCb!Ub_?e`FbwqJvQQt{QohCapaa>;nA#}g_fhJcKvZ5Ow6YWPN$`^OMa;WU zjl8F_0TIgeN3D030JS$GO|g?FJYh^O&f?-`T;4earm>&o+F#Rx8V4Tidun;~)qOr@ zTCd%D#Q%|ySQTOQ_%Rb`W6$*ci>wy4>@@yPsr2ncPpaiPgN0&XjhSnr~kw6 zcodMPuh%C@OlsMQd0!#XH6V%F*e5q>p=|f~s_(&z{9U(ih&D_%f2T#qtEi_4RMB6( zkA%rx-M6#kQ~gEzsEkT#@Uv<>QLSpei0ZOJbUjRwK7ja9{;at&Lbc-QRb!2%@34yS z=kGAD|D~gU%#fsx;>+81L;Ve&XJFUQju=si7(5#MgnliP$=jjMAL#e_MNUwkz}J)g z*($i?hC7!3q=!4HnFblk=(hiK>w8#wXW-kgVT#y*Gqu*&cV;Hi6<#j8+~;zj^1McT zsi?pqNu7?XmainwqT*oczO-Snk12u_Ozs5eMKM%dk7BL$h*8;F1uU;`f7XRM?8#s% zOx+llu(4_b>erL{Etgzxx@fBKw`OpB6twK|NICMJ`)p;wKo(>?>%qQ4K z*d+M-rOxl~On2$oU27fw{kG7){3PaN=J%W$H%MRnH zKt3chJkL*Ng%qZ8@AdZ>(TAvAJ2ZBIL(ROv^6G}(D^3Tp(eJxn%YS@GGH)m_sb(-EiZlHkMycfzk%GD>0{&!pE0dyo!2W() z*J4?8Fkf!YyQvN6J-+!c{c{K~PY93BsAVfh-A+J7UIyD>DA z%%QNn*j_WFR4U=Ibg}hWeu?I7>XvZ@aRvKTcAl+id$%w7m26$o@)pr@%JXx}Deu5m zy&d$bN_Gi!ylHIq$u!Xtt#1xGJrF<4+*Ez`Mt%03r>p!zjZdsRUL4z#Cnh`6KppOL zBl87p z!IuGy4`M>Qk@$&wgClp8fy0`zz^|KF8eT{F`4N1!{Zn@#`3MweCW)bVM_YB2g zoYLjNeaa=4FYkIpac7+L@UQpI_R#O{I5Fyqb}4JVQ7fQFdEYrdqN8=O?U6=Zh(y5jp0Kgv4MicL#fyKz_+Z_3TrR z%9Af=c7MKn0N3!&SoOFq>x)Po1zFoiLf2)Btt;<{e?VL+P(*#JDx)2a-dUp3^QB3B z>_Oj)TZX&wr~30OBqEU7A}qM_5jo`42b)*;0?)5)Wj3$w^OxGZ5)BQrKFY`htC2+m z*uq6h29Qmq?d|(qd8$F<={ao?@MAlhn?BgGwh2Dn_z~0HJ{(0NWNC>ur_JLvZ0hsnF3WzsKOiW0CjX3rXHU8AbUs`7SJU|{YRj6yI8S;>&m7u(XA!*B zW1GWlji+zzySLZJSp2hE3_syl#?pePE~Z`#uCXXH?x%>^db{RLww1bO&8sH+Rhs5a2WC#Hf$sh9R>5qPy&wnh zcaXZE;$_Opm+WL+)H5$yFQAG=#cesPE#D_%ART!>N}npz#Gp9Sj_ha)Y7|O&3k(iN z3j#oX0SpWN+CQw+Zm@G17gUCk_dZAanHLJkzYsph#@vs^+uL`;52`x8uF1=Eh{#Af&-RPyY zyJnnK1!RH)ZzK{)IXSi&0<*wS85D~$o9-^X_$2a+-;oz@Rbswe#AJ+O=ga@{UuZG# zFI@osZ&(0K|6ftxEA4`;RIQD~~8XnR{s}ox}4D5qqm&DW3oB{)t5QV4-rGF8FEo#)XdfaB)Zc-%`+@ zr0&D$uL95hp?W?~S13xOV5@h$AVCE+6h|>Ud5RhWRUO-R@ia4zXx+7&%c%6 z2zl^R+WUd+>9Bp8wfw!+v{9SUdt-8=x4%4}Grr1{*f()t7Qk{|{iOMcyxSywJnt>9 z=2^do7f(hBd}FKuRdtGHWl*&+BvxS&v?tgLFULI4g5iJIuQ39hUl@VDd)aF;{jY#c z7X`Ov2K_5g(-EF~I#zyKFDiXM{;k4<+b5=mFEksFs3zb>ceWN!un1iU5Nj;hRNuE1*rOWLEaC~@(En$^g(W=A%HZHB7u!0! z+~T=U;J>NM{dFp|S?50kI{lJTBIL>K;|nxujNoi1r(ThXzp=n!HD>%q$JioPp6Oox z$Io;NJZQJdIzdvx0!TXj-vpBU=6Q7FxPF_RfA15TD|fL~_wPVc+Xn?#oOsh(@C0?4 z|A2w+Us$2D(D(22_eQ`0LjuG>ASiapC=~cMN0r()7y^3z**y!378&ZHhm{*laU(;v zRn9?@!ca=UDGxbk+6$i>ZCu;|2Dq2jSpc2 z@%|G0rH=H{lhQ|Lg3jx_?p;ZbNqI=xxQdRL*jv4})}Y%N0Uwc@*G*TO)RfnWvKI*r z-#z||Q1<@}1R@~IAp47t54nb`VT^sUCZ;${!8f{GeSoh!qP4i+7WbAgGHB|>-#Cg9 z%bF68#K~n&4Wctg2l8i0Ri*WU=mZ2H6f11tnLYh>IF~G*_jHRj-eK{de~Zfh$EY4+ zWk|duu>u*!RXIo6NAavqr>>?SUCWnUXJIhfxKJ3h<_bd>WlMTwUXGi{2P}JAdw`f^ zx4*q+@vXEUcfFMFY&&UNJASFbx*1E7@m-=H@#3RtA1mU*YiVq7f(=KnHdGijGTK9e zwMb*|cF;v(5osHts0Sw+)d8G*AH77O{KvUg+4LEG@_z*PQf^z&C!NpD>XVA5+dHY) zsQ2IJ`Z&%hgbK@14f<}SmihkYQ8O9oGjB@XXTpZ#w^eX-wP)IPp0cQ%P7e9dy-FcsC(U&(-*7=@l02>!Kp$}?#z2FiB{2#Blea#vx@YMe8Rh_m^(Q95trU#;Z;hrsuzmgSY$j+EVAsKTMk$ z(U{M;eEvteRoXO`AsdVc*n#1`>(y}_Uj7fU6iMU>;ebQoh;u>Fwa&)RXKIwS1GtgiPqnP!?ssxwC zhacz2>TI+}j92u=3_U39$9?tb|Mo|FqYe0+#Z;@rRA6D+R9m4rt`VBqZeWX>Kz0wJ zf^{5Qto5*#J_*>QLPBzj!6=Q4W4@0aZiwTnbNd}R$4VM|oB~N{BOU|31dM>x8<(YF z|BkHx&%grX357&Q(x0aW@VKpaj9PDW`xc@bEV?+llr2rU$3b+3BQ5Z~#RfSBFnm|5 zkQ~8jC@6EhwNXnnvJWYk?>lse9a(_-!?Cm#M(C*a9by?S43um3fi#X_S_ww@U{S(g zU}@M;DLb+$NX8K*_Z;VfS8eaSbsU>@<2d%#p;%nKPve-mtYc{w7E>-ag~?OHd+x2c zbRPeDh-2zD?$(LSL6jY0N^TJI4!IAhk9~xcBq>e6l5u2BAjdEdkc8HgI0qktF&_4; zw*F$IaXW^mw6Ve5!r)Wp-Jg4aB`{@G3)R0$3|>D#0hXwxevBpJ%HQG*To!ulF{Zg5 zTgFLrj{=8LZ7k;9_l6GJ|AAp|{m=u_TI|0rQ`!1wy`7sEx@XB;_cYwn4U8zD=x6BU z*S3!xtXRt{wW7J=AJ+l?0;m9vrgix>*H2Q0a4z&kc)^?t|2hF~{cz!5P!l&h&zmV0YGSZF(M$p6nS>LA)^9V)hySu!>E8(i-~|D^>vB%7 zUlHNky5Ktu6q)lKQdR-qA(&L`UwR~z{=6Eox@G!b;sKZ@ve_GEEagibTnk#{0!spP z#HF^vf1JmbU+^Y9yFcemy5I}ENx)6}^~wpv*=y#*>1%kM|5b*Yv4Qo{`+mQxM;OEWLWTa( zeycoYs2^Yoxd^=CUODmVlC(!Uj#U5zc5*b1RE)`?hgvxd2w4c6G)bwPKLP*Lxm`|@ zW}n>9pgN(@$3T?u$Ahfl!EJc5udeM`FNPXRgI*}KQ&bHo73(e zr-mRT^KmQMpgm{U+}myIde2zSi=WlCSki~TGb@4-h8u08k;Uf@)9fLXkDtN251ArQ z67Gxc^pJbXsGT`16@{q3q|%LSuH*nK8FB+uSmP27aWrD|iYB(f0E>fFjmwQ!Zbh+*-}yhXhl|ela92vs^Rwp2f`AB# zNXfBk-&~?ex5V<*rw&9}5CQS^Gyjd%dF3pZ>9*bJ zVJt@l0S`tN%^Fl+FC(R^f#(@>}Izj9RgzcRDR zKF+7aB;F^I$8=haIMTK8C7g zrV%Q!!^ejvr61rL3Q@g=L!Fd-S?^tohqIsrQ^(VV0a%((#xmHkLe#|T`}~CVv@pKF zDWOWF1KxOqNjtM+&>?F~8r_HVZ-}WP?425CJb~}8z0}?*<5)|;V$7>e4W*_!yR)n= zJVZSVX|G2PwVo!VcGe9EnVMlckb?)_;;ekkai;8z7?^BkGpcW3$VblchmSp?d{|Bw z8DZeq-bldEBl|^iasFAIgPw$6g(s&H3x#kT5zm4hM>|V@+>Ac7)1Z*334Xa1zgV2P zSpn%>W9{kxPbR^nC3|Lrk?m-D+1wWP&Jy7$-$9_705^-9;DOVmyLx&Mzi|m4liV<7 z|0YYy{{%}ZYS}csF87j}N55lk`IY=Uqjr%EU{=6dXD-8|moD3^tuue)DLd;F5U+n* zo&Hav6G9|stV}w0b_fm6?Y!O=rG%Ks0M^!C)6!^LxlWGTJ86R#Exp(6Lp$fTGOf(G zpr@A{L->!PD*qpQUjh&1+WsF|veabBQZlj+6=|UilO>@tLc5HLs1&Iz88fm*WtS8t zBFQP03egOqu@$A0q?)k|l4Tal43C-r^UyiJ^Ly8G-uImMeSh!&`S_fg)AP*ZzVGY0 zulriQ-|I=;t}@5_Dyq4d&ijdAX{j77Z(l3ji|_h0{MAw=cC#g_lw;^Dt4U#TT0 zKF6gTH0<{Li75yYhgU>h)|m}zR{P|1=#@F{>HpJ=W&EmIuRrCD{}<&^l4Ix3wFWt- z#4`G+?Fz`mOSF6mwj}_`K#EUWeU`Z1DdPNTHH50rUGl4#-~Y;-V8LabZGWB5_FItt z4Ye2MIx=j!9F*}B`xeY7+pg#VyAgn-SE&QuPbF8s`;oVQ3GRR83XVvzI(`*<8+K#< zFk~(4#{60HjyW3gzoRPhzucHJxWi(~+A`7_7%sQy7N;Kf&6d4~qyYkFlHE0{ZJmfL z1k$+yF;h)N%v_MxCU#8*8uGHi4}1thQ{7vEBu;F+0kmiB;NvJ7J+U3V0^R}BY`m9F z>S%q?3)0kktad6ADx0upDEH4a?(qWKO)+DzXBv?}e#=fq(WbjVICV5@C!-%jp6-9X zLmxBEI*9qk>Rh-I!%^A`0x>{@OaX@6Q(#7D!-j${Yz@YDVhQ*g84MjWLJwt%dzGkl zq{(>M0e)2cDhx~M0KbMfF$cmW06pJK%kiHveC|i&B=;k+m$e=6(=v11DvJ;@p=wy4PVmxhmR}wJ1Rmx8MOTOlPsme|S z`Z6Q1wE~%z$X<`^PsIR5Mg>3)j&R+X@TO<-Ri;0`sIeskC=j&A>)|%qq|taKNJ@`a z7Kpv^J?FwRHoDtP+5;*<18EO=m>38|)jeh}+paHg64V>D#bF)+oPuKvUZ6r-WAQXq z6+~aF5|8r+ktvubc%uhEeSg(kOc{_Xc+*qFzYn}(C<4nf_W2ABsSHUTE$XQ-H_aG1 zK<;ijFCGJ)w12h7$N$Y!;+pCAc0FQpnXEHz0k+lOOEKT>dZqQu1$4vW|H}>S|KOeV z{=eKok1N-hbI?5vtCcbRo-y*$bLEE0`{#FWoO4233H1M!0f@iy{#e$3xl;kF%U^Da z2&>CqZUzQhm%rFU6IK^kU48@E7FHKnU0`+jJH2)PK0*{ejQ-kTL=Vd*CG`qAgEcp0 z14I>W2P={?5g}gEfLnh9sOq6G5`LVdXs1#xOE9M6B^Ugu|K?*urAYKiKoU9f{lyeVS$rhCAL?fR4n;+ta(Bpw(<&Tw}LHs4Oe@ouS70B~N9kUVZarkl0x zi`Z41xBElRV~fG3D@-Fn&ToG}%39i2&b=9q;OWr3gqG&lIX5CZD6bav>w{_PgTH^L zr;s3lJ{syr{VxO$Yt`6#${x~AI;6y_HjIrt)SEEbkLdN)x@H|6tXWe}W;~UPZ5t+4u(N4&8CQt4;>awO_h~z>LoN*}!cGW%ZM(AhlX2+zp zRdxI7A5{SPHN%35Ge?8@!3h+&7ZSoA_fhF$&?5Zr> zMDZ%h2-TCoTgurcpg+=RX85kg4D!6vku0DD%y6TvXnYKboa-ZJtyd3$a!g`*Gypouhq*3Nk z_vnaB$r@=(l%k@}fn|d9gqA1oznq}ZNHZBWDPQiq(hu%U_oXmCjy{aI;WtGjG0 zZaLoaH@M?czSLWMy-{eqf1y)hN}t`EZ`Q?EFuNT&-SOhYC*7PS>kZs=bKWz$w@f-s zvX?FR5yvwVDLx!v@16Z_D76E1S)Lj3%0XsS#yeVs(wi_nDV;(*71$W7rQSa;mUe|C zb9BL8WDH4mm5iYc+A4gFIx>j{tO=gHHPJ=&6$uf+u({_9?Fig!niR3(WZd*k9SJg) zAWAFR#ctZCMVsN)ia)kDj~~SY$uSL7W$n=r{Lrx*_)F-P6yH`fucJ5wXl~Dx5+(WM z;Tw81hEmVuxqn6>r-0DA_}4w+lTX;y=kDR(Z`e!rLasz|kx1G&#^(mZl;6GP0Qpr3 z&y}5jhT6a%#EYEpHjlwk$+NQIEFztS39`!m<}$Q-`b0trPT{3(~Xnt69`w zI*e$v;W<|H-z>|oIj#5)wp|H{FYaj6z%VvD-U|}{M~av%yN7W z0a0CaElc*g&U#bN07(r|z`ew|Hbmt5ifRL158te&m(-K>0v*iXBfC3x#sPTjeIsm_ ztdAT&NAsEvW;JDeCvQie#9rMm+V0mxIyFCN*P|^I8flYg8Nn@yYdW2j8y-1$x~p!G zpyvpU#Z}UXiV`-E_sH|kxv3$#9L019-sn{2a3~Kwb41d-!=bv(fhQEu*oOIG$XQG9 zlx?$gxL zsKooh4}u~8Q&)R%Zv0nAp1KOS?7|iSpPfysn2~GtA~rs9#jofZIqiA}U(uS@5GZzP z0qN@{q4qZbX&S%<+!vuD`%j(WpZ)<$pSakQ`vl!zW?6Y&KE7ZD)(W`*=kwI-FmWZ@ z3V6Kj%CX~cNqJ~JtDf^U?hmSN8cQ` zlhYj~d;H*Px3dQ43@8^IXVPd^OueH8k$rZr95S+A*a~4j49T-pFC4XHZsV5`TMRR@ za+8D=?3=->My7QMTe6$)^#mE6Ld2*p!XVF^<3-MjTy?HOeOMIU(tTmF=Vs3X(gKG_ zhe+Cz7|Y`2pI&9hpQ3)4A9esKbaP%%@TCB0U9n6N%?ru3W{CH;dz~zj=q`?WCQ3oC zhveDffk6uPe!hETdZfgNHQNrpIA?kA#g%ZI92t4My2sR4P|C3 z=S=|o0FNotDzn!?sKDKHf%zrzF9{LxH3W4NnN2c;Jt~sV1N@9bHa%N@1TpVas7RPF zow4fUnwv^~vWX)0vXkh&8i(vew}d-e6w=-48%vz)k)OKu=~h2KxaG2e60p$VoJ70C zISFIQrBO@MLM-wdf81<(C+X{%)NN_@S?jQIZaU{r0uRHlD+Z#uMN3GHku z8azvXmQLGzhu=85B4|m8vn40*XhAc$`@@hv0dYYIo7ZY`wTPc6Ue}r(-q;FBX+c*r zvbw42*uM9t(aNR&73XjVYAMP+rxrS;OPqndy$zo>#q0D;_ziS1IXV3`TQ_fY-zb_H zKCH%FsTzb?v}w_FEZus*ntSlH?%2ob&qH4a0xp})D;t-YCmEJr>P-zhTxGXeR@d$m z$NfsA))ILM2hsfShztGA4l9U$F3-x()E@^mO2~Ap4M!=@t#qm8sXjtDXUI$V=#q15}pBC)Gt} zDg^T}Ja@ssf|Uhf&JUK0km}}-QZ1;5Ffy47vKCPE7e%?5mfbL0{yF}k_`Fhc;mtx! zw!*NIqqK>Ht*};Xi0ThX5Awp*s|#Xd_m2n-YoM0!&QW!c_AvTz9soZ)~Sr$mfjL_C@KU(0O7ZxQ8U2S$? z9ZAwT^UWmta%W9WOifEnHUcTM`~pMXPx5I5y(EY}V=`beq!TWeBi9vJm(zMeJg}tT zR_B_DdGz;N=jqOK`;xnSew>hwkljtq(=8F}Wv{w8YMLxbm0Tp2z1B1es9v5OCHtv! zS1mWZn#OcW_seoj_q%m-=bfG9iY|9u7e04&2W;A_3;x><=KSO+c9DCI zniL}>Axc=G(V)bUyiEy3wX6@_@%% zC9?w?)A__tr~!xImLBofg)ba&QzE5c-F0q<@jCzz;Nt@(;B6c6;`D~C1H1ykeVN|pH9N^g-pQImYAnl6a^2!ir zN?8K*!_-y7JWM)x|p;MxkH z=iYwXPZxQx+cEC?!-8ztgD5x$zLA_ehO0~S_il!M9gxRF`YI~v;5)6e=sZCSgW z%wwIQ^%BjhH({Ai5=ax4JQ{0P0_kuLU0)URHHj^EIh8WQJY0jSokcVB-0w1r^)U4=7VjH*2>mjI!Y5;?YpcFd9?)l+{CNRar7B(HiCz~brF!xO z@6?AB4G(=VaHr5S4Tls5Xs%Up4Va1thxyk89gKu*tJ?y0xMx!@66e>P!J*-*GJ~gd zlw1Xp^Z84}CU-`7whzz_(@zwPY$&f8v+tXhNMC&_!DRLRPe0(R>z`~ej&YSOJ2>{w zBs8PN{|!h3?v2aQ%$ zp>3K*R)j%TCx*veMPMq)ytEBUcwpUq5E$q&CXBI0YS_z=$%|5O3EPvAY)zpMO>-`H zy>`Roi3E|5NC96D%#FrH+uG5pfMJby9CFB*S zx1Z8*O~*Z(p0ZM&Whs-8N*c4RUgMOQ#om&DoIbE$mvu`O^&vp=k#m*9g@Q(e8kl5X za$C}9*Ms{@Jnp$GuS7EUTZl=KITT_gEkgA3%F!baZr%_jcCqe#S=s8tD@m9PC=V*B z_X;WpymA@gg5LeBaZNteq*j04AeM+q?!-vLrQWm=~ZwMO{1IM)Iw5xbaPAEhjObb(;yqr;cO*09?WXcqj?+7Qia zno|ZxL{=s-F-gEtorVPaiHi@ma`z;IT;3V0WHa_e4ET@K=r6;{wk2gZqF;_zh?x)1WYR86NKEx(d4cH0V;L8f5d_w@?qR5jk4A=0-t&Ols z+P6=JrwhWuixpm_6cBBM1j%eqb`MUkS&r9D-Ab|5U}#)U9dzJ@Q)2|t51h?=lhjGm z+^!ax+l`3l{VXdWMY7Vlw^-6ybbDI;{%W1&E8R<&1}papl0?6(1T>`LUxhbc1x{=` z3TEi*DNljmlL{=K3sFz3E|#@ddM%NS#2(AJV?m$UP*l~`?AYh$!}J8f zk;Z1+&teT$4NW{NUd@uKe>H+DwY0?9&=kznH9IuC%G)DD`&FRAmFXvcQ+BoTwBf2$ zwF4%q@&=iU?UfcuSXxTdY^>(=pjOGRUrLA98Zb!*_NKqrYqvXKFSYctS zd&op5h=9(I$`!jX+4+NT%v!6Anupcto;f14F?!%KO@x*~KZ1eSGw)vxhW_%}p?V0c zei@o-8K<~y!T%}<<3rb{=YC5fm-Djvv17PTSCC_+pCz~$nhPYfr29o$5(*wWzEfio zZtSA_9o0Q!vVr>uL~!0v1x`iu=JYav9H2|`U(9as2x#o=KwZlM)AvUUdXK&>y6F3> zVEtE{p8h5W{fo&p1^dKRDu_5mhXq>FF(NB+BD*r_JXWeg+)|M*z9o_>ucbW~QN?gq zqh#@I5ww$u=+w$gOe)4z=wA-K4mLFvEpY}5Z9}P_wx3_Z`c-UzTK|sszbTYnCf;T# z@hGatg7rcIT#Q6D%~ZooIW#8wt>w;$s_B!vFVtk@GoX&}1 z$LNRsik4)kUW4}nJw zSko(U_FwyTS1s(66u?f&pE4?8r{vG5R_2xJ0*Xo4+QL#z)s2E?=b^AC9qTSE12}KQvy3Bf6Xaj zQF#U&kq#8om%m1k(t-LxmSA{AMr;AMD_vEP{#9^nzt&~>s|zaa`8yxjzvvX%uk}TP z`ypXK07sz6j&_Lvow%Ol^-bv0F)-okTT#8q2DwK$@P*d*G;~U4Zkt1~7Y& z;D7C015R%Ydi!2o43j%uhpwJ9;6xsB@=2jgb1~%p-Y9kxG7*b!?~(IKanA{hVv}pA z?Cgf~*6l&TF6NnY7u!R#v&3#SY?)_Zl`bLb}+U~`Xx2ae1o>=ae7KnYf#j^tHo zh&IcC9TuF)Ou;zFAS`!gHUvnbcaagKajqK#XJBGN3c?E6>F8bbLiKFD?k8*lP?|8g zcGrXACEoXd_Rjr0Q?Ma9qs9D7XLUHHtS3x&p@^s_>R?-Pz04a7 zedU#Vjiva^o~9CzO6XofM>K9aKpr^ul#W1?6Fb5~pW-c!HWiqeBQsW!rnr?W6%F0? zu6C~kzMQE9iu|uAwkR|_VOC1|o66EInSa9t0SwOVK-ZA-HLSG0wOyvtEnTNpqT^0{ zV5=~7HyXIO%GY5dPDRrl>f5U3mtUQfeEcOCU(@xAMnPa{;P#f+QLneePpsd*m8v?g zQXK5>;YWVvC}#3^8l(}=MNq1sR#)!Xseed^54Vw2dOA}U6+o^2HXlfQ!$Lf_xGB(4 zQVUwD)-=}o$&l}94H#(rfbc&14!fYuihE&P#wQUuiHXOLu@+rk1i~8#5u^;&4mIh; zL7~i14LZs8gIWxrFY!Tbr3^nbQb<^A0f5zH>C<%DF{xGCz|LCSQ*V@nLV7EDGKG=%1_!&lpMNF285LH?Myc_ zpYlvdz{iF@T;oKp@ARkxTj*q(1EL9tHM^?z^8ji>W&|Uq`Y6v8akV8FP>T@txk@3N zQkYgNVHBH_Ngd(M3LB&<_eH)EZc#8i5ZNJYkX}IavfyWlP8BEsfw?ouL_gl87li7z z$a4NGQMWbjbT|!bahmdG^EUC7Sy-8wwF~jaS6yq)f8KA9*dG;lDK|4v=d*NmR$`G%nf&Qhqf5M> zbGXqpzvv&dV?+@uL1hp9Yj`EmoOtW#*Khi%3jrv2nRY7g5zLx}{W=Hn0%Kgg5 zL$J#IqYctvmHP*KUBM~`tK47fW(Xgw9Vqx<{k5${VPEba>=Ft4a{p*ITUg~_mHR7H zC^%w;Bi3KvUl|S`;PBzEQPBUtOOi|r7!DRM7YJ}}DpSOzx2!mkpQ}fe)%aKXUg|t{ zTm+~{CnjLP0>t;#xHwTii6p!d zECFAwA)2CTv>k0nV1%er*q>i$O6-vJNUIH8TGTs}fIFP_fm*qP@U>GFIsZuS*d-u+ zmoP%k`v(06qrWRy_*IH89$3i{!5MtiW}Hf1J)Qsr41UqIhW=(nkTBCRVna;8;DH+CNRxSn}GxP_v9`8wrwZuMv|@C2LG!rv3QjO@2uG;o98 zf$4N4F>2dVf3qGVu@QHsS;{x9vePU~1G1g8o>y=hgCQx7H-aY`q=(}ePOH6{)N`_ zcWeh#6FSMm{X9%UQH!VaiR(ZL?u+Z_4P$Kl40?C`s>vG5!XH!@A$^b?=74Y-PlsZ~ zLOpfX3pRS{{M7lPJkv@*8IoV>k*F!o2V~YQSdcjC5Dg&Zskmu?LSy67=Ai^?!f!R! z2)~U`?V?QSB#T@ekXfu&jk6hKC(gl5?$@W@*)REb#9&E%%Zekd4(^I=gQS~%uL!wh5|4{xD;S-|w($sgo zpxje?k}D!b_P$yy$e+?836uyA7hJ`S@e=qiIt{qMIWEt*DGKxC9kQ!<)lFCatrp@n ze<6l&qbSE%aj}7#+mx4LC7&T$D@A}>F@nrb7wJ3K9v$9XrzR1>L98+KV6F#Nh($KH zlpKzu)Gao`OmcKAq5tOrrmPZ-AfBYfSLdw72=-vC9!u9}^o$zWzNYirw&!~@1q$oc zj(79dE_}P5D|0NOaM?4lfQ8&63ol+*JMJfF5aXrnkTIbmPgNmLC1O6&eIm(Iuf@Oc zJcf__IJvC2%|x{`IOjsCa?4IPPr6xvqJ`?gbLP+Ro~*&1dG$ifi6z5p5|31+gr%tI z6dJTdaBNK+qEsZZB{wcXD{%v#I-3w#(FKlifg#Zsmw~;`Jhp8tc$DwzvhwcAr6ZO$ z3kxost5;`iioRuI^Ep{3;-GQBbLOMC41}>;<&*01g-6X-rZx_CF{>w%hHDHQGl*bioLBoj1Tvt1C*FuO?pAx#ux; zbG#5%*daVKkBmmV*61=>;;$K`*XzP`r_(wdx`4W6*_Nm;!^-NX9@M8Ktzu+0UiIJk z@XjOeGAZTtnPTr(TuS|>6u(sY+_r4#fX^SsUo`0*HUAXEdN0;)e2w?OQLiYbM!a+D zQv_wRC%<9T5g#|4YM*_1ICWTFT@+{yuvbET0DJJ*`xHPwnZ?DNHw^ttdywLr7loA; zdna5_nV&6^DYDTnG*~rr>xq`6JqpFgJdzr+sEB<{!)-g>pspI9NhVx3f=y3)=&d%H7x%cSJ{P1?f#s-qFd||TDimNb)gSRamgjUOVJgHJ8O=)FMniP9^Z8* zX=iC~gM0gxg9ep99PX>RqUd|lFEkHXEgnkogB z@gmr$Oi5K~z_b&UNeQZP)~%~Bv#MPPwupR`pXuD;v|3MLm%_rp6}O_>sy2Bf`7f{8 zsd4L6LrV4iWfg8g`|6rwmKl8cNS_j0i&U`p9QWh8PuEX$Dc)(u@RrA6A7(iX)!l5o4 z>cXKe9O}Y3J~+n*=lI|p-~W{y-<0^EynlpjIS`iiMs~%fDqlf5mk?S^p^S16f>$k= zX5TX!12X;*WAiYBA*+dkVjO{6fhnJ$%m9+6N|y168HCi}6yFZl*JwiuO`XzGI&C=F7k&>tGLJkh1WwN8VPZxH++Zl!7zk%O|miQ<_ndX$z1M_zb9@ulBz52wdJ)&9c0s`ZvJ8l;Piv8Trbu--uRejCb&*;!@r{V^A37`Ry#L; zet%nw16}RH{AJ?DGD$365%HO~3b2htH1$?#HNgyXd~2{XzyXV%u$R0B5>cu?li0=C zGs7t*j&O*v#0ie?OUxOKfXzd65&$e4#{qIvc2iW0K&;jDW+353KMp$za9eP(NS(X= zR097Tm)hOVSL!gQ*x*>VFvO@~Ie{&{EKomg{kh0q6yll@$I;^pZQ$igU0e19`vbQeIva?L6Efpb3mNTaprCihOO33&$?L)zCTIyG&LCBfKVjbXPK1vYokt$-rC+qjim{ZMROo48+#z zHPE(trxZ^EpOK1ek;e_VwcC?5ZyoAJ+RJJ;l589GctzHdyZome*w&E^H|+c%Jv~H{(1G&43PN|{Qg+zWFr*V@I@Hy6ZKP53GisoSz<*u~-3xFHornWX7so1dEZl*YsV= z<~ORh$)eK?s67~im)+&{QW-IaS@xeCfT z^|2kOOi=xFw%V&9+PfJnt4;fh6*n?Fq}`I~h4_uCg5Wg@EnD2`(ejt}bb~EKIW?R; zuW=6Zekx6}PbvN()vFGN9LG4Y{s)fr<_=jDoeEU~A*hBDN=>{Qtc<6x4cZmma+H}Y zf@L(ZxGk3+2CZgW_T}uYlr44sl8wx!`1AdRe=4%223Gtb1!!>PqMWS3p~cw>olFKR z`cN?+s=!s*3T<2r)_BR8)kQ}54H4;JT^turCAwX%JCmengsAIIE6ZX}08Ai_lP*{F zy-uE6+4;wmQlC2OrmBO*XmG6FDPV2-Ayk%zis&|y^%tOOB}3XRt*b=2QnT8b=a1%d z4zJ9656esDUj&CHpCtTI69h^?7zqf`aUn_AUkxmshi;764s1GqS0EW|I)7Ji4{SPrSELziI$_fZd!7FzniTdre@|#L9I*eca8o#7 z|6Q@qaKQe%0`Xzf37bwh*ZI$pZQy_%4%p#b=RZlPgS}4J>x9ch|0HP^&UOASxlW#d zQ1Hn%&E@4y3$ZtcVrvluK1FPwMPSt<+d%rXfuJApiQF<(CZnhrp-!H(ac1JiYXq0b z{mGARqP!P&=W=liB=@RVoY4Jjfn!>Iy)^nPl&W=F=z4c2MsTpZ+w_Oarz6Go?ewUl z@!l(bc>w5Z}Y_O;R#kvw0trlt$*BVa=y8 zxrAp{v}v#I>9_%|bNc8^3dYIumaX*uTihL|213Yu%d_uNREaL~F+%KiUd||A1F^K1 z9b-=1lcYLTl(%)Jx`nnSlo>JA8u)hk__P7-Lc16*o~B4MACgI20W$fIh>X=UP8fWZ z3Dv)tWMsEGFgf#LQk!v&UZH?his8S#VyN68hyO7OUD#`=n^OXP>Dof(_1&ol~1e2WSZkIga)-aP7ZzTgK{{4nKoN$6&-%g2IdBnT=xe=B0F z;_FS)34W?fBsO5Ntc9oKisdf%R_wLn9*@xF*ERVHj+K(m^G;$I>;pvqR%;DUmsFX{ z8hMw;Zt%Z$V$S6x5S(|~VNl7oHd(Tb&vEw|10n#@OoL^Q8x`76vV*=p$pubj>k>6pulk_TB0;Wt!D>DPnjE%ZO*%ZxbGvikp>q3;weWi(p^y zpX|B=t01g`a5&QZH~V_Qz98%i!bzFG-8m3Gh~a}6&I|tS-lT9u3`fLpMEp0KfWqMj z9FD-@2po>Mo5BS;xJLYsH!_FA5jY%y!;ybB9H9%2w$KO@!=q#56A472#%DueoAX_^ zsNGb%6SmMLZhx(1Qk-ADll7&KwK=;_XC=Pui!JePXgqhl%%LP;ZJ}wl_QdwxPg632 zjP8-B4IbyHN4(bTAlOL`1qr%BRjB94#iOCqEX-!?SFLZATT58ecv4o+J^d)7EAqfI z2)WS)LSj5e917(d*MZoR3)ZZ1%0>^(m2ce=vA zVoErLgAI-8LX9|N^u~@jl$(kM7HkJWCXIQHeIuzA&La*M8lr*?QAU9UE|Q*0!DJA4 z6~e)^*;W^y038-^#Iv{BLl;n~$3PrMhJ52!RUcp)@ml#5VdxS-7YD#a?o#mSS;&pklw>hk5+#o#pU^s3c`MKDC}86-bfH)+&WHwm$b z7GJyIi-1{NYTZ{n^C?QRi)ipQiPwQ3cC{#s@1#w4Xg1)}N7QVr^MDMo`WAGnMv$^_ zq~RN544}__*|tr~hD+O*gMY5&1mNmZc$bJnHIzKeR2Qxcs3T1=`vn70s)g2#*iddi z(TKn=LP(@Y39&aPyc5@y;FeWl(*WzDUMdC%)67672om*_@Ak5hj#_Po87>V#H9|0i z1epDO^(kZZ0KXv8F0|;7tczWz2!<1q?WD!2FbBTcX9KTu9myXO`WmoA)&+F1_YL4H zO@@}K-2k9IMkdfEm{(emw#mn?p=YkDZO`VWY{+pWaB7emSVE7pq$hve8q~BH;&4~N z=4>DuL1gQt7SBvc(QfV_oMi2M+^~lhGNmDYw-+IYQo}|=ib-}l+n6A@%avS;;<9kT zyd?-w{Mx>Gj{H$1e}$tiCst<@gUY3CQwP;YZ98GFq$lB6D~e@cp63|tP)HMZVhE>akBwQ^wDJIxGQ~7*_no2| ztlKv8k~n1f6fo#Ee!6>eIeOiJ#G@HUrM$1w94UO_`4tBS+h=a23mT#b<$12r{xYm^ zD#d5s9^$ z&54FqFQlOl4BYL*t%{KL!h=8wm27h^!joF}@UwSVQEqNtbYZ(sLjbNx<{h zrVGYmC_ei|+&5r1aLyV=6auY_=V=6AqY8k>!ik5_?RrP&W5t+f=V`PBJ_}*B@C(=p z%KSBmRjFOHwYMeI9I-rIa=9^Gnv`Cs0aV3`Nz}o zKK|#f-fF5;+@-qD%zoowGFm3^VA;1cg+{+VXJ7mPQHF)wlF54~*pyQAV@=&~y>TKtVHSpRSBmpSRZLOt*d<1(`)NuNA6HSsS~gOux3UyvehJX)NA!w zDi*^~Oyp%~ztmbNtat08)blaLM7zTmAE_C0_j&MZou|TS)`JFb6$eMvxV)T|3$8T! zBE|O~;j2)>T=@W!z%a$^+<^efi_v&y50cQYj{qiEwFvbsv0=E)V|--XSt{^B?hUJo zL}acIdOOBN@T=NL`PPG80`Tvd=U8B=5oS6B19Z@q6QifD(Z;(<36nJ~j>vRU-(*q~ z^v8L-4S;;mPJSGPb1?`_s2HiV6CAlUhh(vv*Kc{@1}HI zL(8VLm2ZhNUCuM7bwWkV^-iC)L+&7cQ=S^g!Ax$~`*rag6* zg!t3V!M6Sjo5MT%B3j1F{E4IXo}!pbooSsvDqhPZ4w_11PX{(?V$TLPMxo!BN_y_s zwaTN9$V+^s})&nPQs*sD?xHDDe)MlZxjbHS6{kd9o6 zatGg4Q*)}oQM=-KyWl!?7szUg(+j{C3u&t^>K+GpR{irRWGS*QEH zRN(Wsmdda3gI5r* zZ9S@+3Tg`^23=4BV{a|LJ4X2+gcclDl8!nkUCBdiuy-FMB5UYgYsnD@W#}rXTIij# zep2>D%M67^M=expS4FbU@oS&5lF>UgK<^4%uGvXz30f_cHvooo5;(drqIX`{tYepD znK3k>4oY6RZ%Rup^jS;HY4d%3gC<)yly7Q!#)|@7uSN1`pc&JnJL3Edtf=FEh|7VNJ0{D18;`r99Xapgk^(5f;4i(Va4_@ws|+9 z8^HNHCC=&@Bx8lfM4eeDuK`rlHcREZno&mhJ#6cRDHYD(1VJsIQ&>=5y-09M(MU1C zS;|w1iMS&F0DQI6p;Bkvl+SLEIS*f?NJqOpcK5!$6v7g&b#4NT5c7GmUIm7Ap_x8(IdsR~FJU=qjjMY-`AMRf7~2 z+_7n(XEF+Ijzj)|_H64bpq9@mtflf*z)H&V0~3_j@f_*-{G24=4d^JNp#VwGN1c}- z$zS`HzJ2VgIr1gXBcrHCrK5Ln<<|6IgS)-4=TrUPX$7l~Jo9VU2M_Azf-l@waGR+A z4KH^jRVp|)a8_F&G01}wIDaf>cFv!&-hxPLa9EH=J~NFn4Aw{rheVX?7;S5z4(S$0`A*i#u`yZ;09ZUybf-Y z+6?ef4L=Tcbq6p4#znRJNmGkzoWaFx0L`91xVPe=EdqrKQIG@-RzaRU6m%>F;r^hI zAdL(x0DVbKCvdAkwgO}bKzatrcqe2rK^u`24k;?AE4z{{mC5?SxzP=uQQ!n2V>7F; zQl5L+pl5Wb1~H#s7-b~Qjj@rOi%d|d4W|qs$sf9pGkQ}njaTYV@w^{@#v`SuMCD-L z^E=5pgF72{ruW8{eS2}R7*w3uD2fAa6L8N(8I|uhbNzJx(|vMPFW^i&6cktCJZs=r z_r`vi-6D_}AcHrMA2m1UKzk^(N(9%&8l0*8i{2~Nl8}flS}LoH#zJ}qT?JJOvJqh5 z2Px{R)Es+o%LIa(!?4^`D>&B%GB&dc3mzcmryt?;Bb*WI&{tc{P6o((ioSzZ^ zFS*rKiEdoTIXO*WsJ_`7YGInko-n{;pbEFvfMB${4l`U4pQUqzF>G@dNR45j+?UXCOc{KFOymDaO zz0!K$nV{L->ZKSE?*mW1l9n!&5#w0SIrz31lWOIt~&+UwkB{q?ORahcm z{M;tmmH)wa6SJR@ki0_CzIV*|UNUd#LVsQZZhGv;s~5==C$jnVA=utnJ~O1a6*V=9 zN^Qd8rIF zL+KQ@I084tZ9{TBvAC)H;yl;Ylu_oiAgX}2^(nC6Yx*|XK6ZuOwWiVY!+`=)_FJQL z8m|SN@41M=Vb&#>%^Zj^0FXGl&#q)9HgsLC4Q1vEj-C3f9C+$He=7--clzDijF#st zaB1HYyIUtRB3rtQ1wB{_gUCz$P!8OlPt3lkrbT?W;5?T^nZSfhjh}4=Ur%%kOW|J; zu$&tQ*e3*Av82!*WboAbm=-=F`^yQW88GR#B5CZaV5H4!k;_|s0aNZ?-s86n+)Ruc zZW^>(wF87#>p$)zCr z$HlvmsH}5bM&4E1drj2BRqM;NtIu)s*QG<;*PT!-3sP5ewtwseIl&N@ay>}Q(}lRM zAj2BsFhdN=lnoo7fXz_kK?0s-2C`KfBw5cf_;gSDgWrXi+BrKM8s@ma(33!N9!Nm^ z=}BPT=tk!3uaok8JNIRfS{j@?3O;mtmZ5ngle}$?6b%wtXNkP%oos7}h7H~rW%RfK z^@|Z9_Um$%L%%ba1dR(M94a*8E9S_%;9MZ0Yw0ZO6B^tssdsKHvy^P#?*o{!k@ECT zX=^_KaSzcuzfYgbvCO`>VI#zFCI^7@%pdz=$?u`r^u~Ip$U(mXdej)$A7XC);Ju;c za~#w++B$Q5_Mbn57S4^SUha(sMsn`+_ovJ~F4urC6pNNQ>939*i`w0Q(7>ib69So? z(A-1$i4eupd=eZfBqC|(K9I&M2C_o=XlP}?3H?09V<{j#7-GUs4K34bh=L?HIVa44 zHq8dc+L*@;U`8Jj@Eh%dS(*Ni@S6*+Ya(OOZ*&gKsRSg*J~^FPK_>TVeE$OH^e^NL zE;?lFXBA@h$qnGIhL#>2Iz(cPF`5{DO18 zk8w_jvrhsW0Q@ZDytyx@LIbCr$Z4LORX9)r>2p-rBoM{`tqG*F5cw9o5t6avkcw2K{v>; z`oTs2E+X`sJvVfw_Rk3@58<}&Qdh4G)jO9aX3n`&> z0-y_hIZ5aHQ@(#7b031*_P7BQn&N&f%;h-+{=VzRcvjGqd7qZeN&?zGpq2%zF9oN` z@Td!>>mADhM-CNZoYonRmD+8G#!ZW4ocu4v`!E=ejtBtd;UW6z8kdf ziXPf(nK26SlAEW$t2?+wI-zx#)iX$g$Uj1c;^%z`SvYVkkn}=l?I%deH=b2?@|QV_ z@_qWXByiEci->KFJDELj;`<(%Geoocjsxl4bB_a!R{EUL`dQn4{_rLp(tC*NFJs2O z^|OhZ)ucJQHq}kdhD-pcJvi?)!0_h*gR()t=|{|N{WHF^K>7d2-kU%}-M;a|*>{yJ zF{H?nELlfoDME_SA}S$!VTf!)NC*wB60%2&>|4xaA8XclJ=c*N=}C%(kH;(?lM77>s*58GtH^nKr&g-YvHm)Q&n;VAO};|RAO2C z0`Hol2$sA~H=0Cu0=oWALchYQ`w_u&O;dcgWU#vfY94UYcZag(n7{!6KU(^xLkM65 zulUWXFu=>i5-FwCm4Il7E*=m~bxqs91APXZYx0V4;KxKObxuFnVB!L2e(&A4|m9I$?@H3?HSY^Vz z8ZpgK5J5mWfwT^SaVUdu><$FWZ*U+uTOif`Af+X0O>hbVus=m2seF;qyu4Vy9}!29Qh*F+d?WiN%T_0zK?pJiPV=&*VR+slI+A zfyIb4{KE$U-Y3j2h7kFHLkiZlv>sq8 z-b@21oEeI3&^q6vr&sTZN+Md)&{>Eme%K*#ERgD(;cEl%N`$~ZJVIoYZ{gZkcOdld z-i8$9kb<1==1&T82*D^R$N|{`As8S9Im9rZ6y!j;I241Df*ewiLx`eDK@Jp2LM0ti zCFfh%55>+PEGAWQ2&Dm1B?n4(31A>qa)_xisgeU#vx(&iP>mr~azM?URLOZmTsRswKAmttCPSF+*M(EtE2#FCi{t%yiyB-x>|Vxqz<` z2KBvnbj^Lm@1t!V*;b1-1)qL+8tUocXpo2SbQUxgVyib4n?t^LX-XWzcu+f-$pxL6 zX!uJH;qsUv!;KbhcSmeCT0qa&26w}=B<2SE<=Q6;kO@MH_n_~uJp*z;CTOfZQrwKV zy!?(hV7I#=L|LF~D#5@vBNM!fKwr=#HY-nwjS~9)pe+s!uzky_UoN2(d9K)Fp+wq|Atv8Iclc zQX)-Cq)BxbP^===U5FJbQkf3aDoJHJA{an|-ZT34<`?O;G}3En zfl{Qm7zuB5lHOwcR#zpx^a{NcN_y#)^o}&?9cj`#(%;_VC%sZnJl}xyN0|o`7Wg6fJnyykxq{yogSqMHs+B|k0Kp5MmlVabT%Ed3y*Xh5a~D| z(ou$_B}UQ`BWa0|bWkekTwT(+x`g9>N$2X4j$tMp!%RB0n{;aT+R1pNw-~{R@uWlE zNp}MLzjh};&Fa`Y#fDASxv2Vb3#sYpRZ=AFOWL;Hhh!y1i3-UBd24p z`j|XNefn)I>G*N|gbN|o-R__;aYrFou{oP~xd7pI0C$f`!P6Tp4DM)v|3bG*xR?E( zUn8LO|NC1fc!?KgG#4x0>(fs2hHjt$D<#k+4&aWBeu}w6-}hV=7Xs}Qt7gze5x_k{ zf}XJ=UQ_|@frvWiJwv>EW9?c9;x!V`4G}~0-(GI12=inj9uV-;-4sLl6G~wBPSm;4 zpsQo|J%flt05^6(*FSvuNQg+4y_3JEsnA-+hl7BX(Cu3+h!|s31zPU0e5h1 zhGKIV)Cf4GMDgfUZ{Ux&4oPtN@0XO2;PU?+c3G){h-)-x3gR=!48C8VQXV4=nYAF4 z&Gl=i5w4tpcFWEy-dijovfXSbbZre}Mjl@$%WYruDanWrO#rvso*pC>RU zbd^qh9nqelJ-FZ#5|Hf?x`eLYA=oVOl9^`jwM$-B=b&47R)*JI4b!pZ_D15JGlWQc zu;U8|~BgHPHQ1=ii)Fov`|6U3vWk#SP1ukFw zHIE}D(tkYvkOUVJT>it2d?dJ#;6j26sj3U^DkoKS*VeE|Rb5h5m$=eHs_K%ex}>V^ z+986ZsxDYLBvo}uE7ITA%?Y>EfZ&<9N)PV)Bvo}uRb5h5_vh^vq%s|;Oh?)t_2Zfn z()K9QZaUI#I%roRX*V5ovf!_8%93`|k#^IO_Uf)}mi{02>du^4*+{Enw29ur-rULg zvW2M~^b_L3MS3Y&neBVFLq91f&`X?jcd*zlVW4y2qJ^0=y~K$t7n})CH0?~SEa)Xv zY@D5REiS7f>>LpG7WU5D<>@7~EbOhEt+z|b%7S)|(@Pw-u(7gs-Y&UEQkq`kw2e9V zo#Y<*J@gW)2wTME6Aq?kpoc>iE;eQs8kbGo=_S-{Y@IDGZ$B%cZfojnamd09VQyhW zucSo3Hc&UR(MMM3bEn=03S3Wn<-_x|h4o0%de zRwbrd7$V1V;qR49q_1Yr?ITmD#!rf7roJ4A99i8or?}ATw{h>}P3aYSt1`o~o?hF9 z{m~SI^n;~~4y9C9%X9e}JXfki_mAbY!hOmWw&|RU*GWe*tXuNRuXPIqbDbv|luCun7E;5>EXQ^nu zXnJdY4a7O;=E^bwhvCesm5UE}UlfV!j83}kgT)$qCGw7Dt?m$tcHoPqv`*ZN^YJdV zc<^W@kivBd=kfe_a24L>ioWskl5*$#JMS;j(c9xt$!-)YOb=Ga!j?w!a2~7!lWu3i z8oig7x|Tl-;rn!aZ!(9`_}n0}X!TQ=_gB0l_|k5Rrxcp6WEtuypRxB(uW%ew_6XB1KClSpl$kpB$ZlR~uDyq% zbI2T5K%e&N%k^cKb|2WJn*x(b$6L9@nw2VUr}|7ExqUC-;J1;N(iTVh_S*-`ErzO zfp%Ec-7Usl7sARz$#a@5Fpi4n9H-91E*F9EC@B@SZM?d2mdwuC9nrl#dinY5;%XF9 zAtrKR^h+0y!%h>FKs5|rrdHh1R#c9E8rhhZ|9+*IEGLuF120&xaCm|_OlW_s(L~_V zGFKCpWfT?FcZc75=zZVhN%Icb{LT^D127y6-a|gfo#Vf{W!pBLs?@^av_Rgg@@=1S zv>3;_7x}m^54_rv6t{&5m1d3DFFh}3Rt4X&L3Dd`*}db=edOKIH+jg=qBWMsW60D; z4h=uzfzRu*FJAuY)s|qmEo@M_E-mvlSs*vPbvZII6%KwAIueR&DIWbSI-EL#;^pnt=suzpmPZJt#dP{hj!eSr&6LRGZrDjFkj`&3@{Qk-8#z`VK`u3$Q? zNL=~N^YU!-%vyFNkJ}~_&;}RA@tu*Yao^iM!wUu$A&MI*bNVoPvG88yK=&(dg-W{- zuLjev&h7?JZ->CgUk=8sa_7xiDa54J)kdw3J}5o}rsnQ}k3mjgratE?<0qZf^uNq) zD`ss1y*A){R>vp2HA)sI@5C|>=f z_)2R;G3Hq9nX?l`5}luM)~WDu?_S>%-YQFqF%4D0JsnQQP(w`fSc;9^bC*^V%3C`O zEMM?0f8a0xGBd=v^_J2!E(fhH2B&Pv{1C3ZLSDc4*@gV0Fd>e8!hGLn9 zCnh=jgQarU7N82Oz@t)FnBv?*EaCZ-|HR6u6%O|3H7rbd<(>A{n5tIRS5tn9kk$sL zv3Q+x3l!8tlQPk7UE9qzPM{h-p@d=z+=T+bvpW|0t2N^g)TTY~K|%;zjrXBXHrwk` zi>JqnkO*fNgu-IK7Y)+MjjKXxWa2uQLZJ^l#&hZ_f?0>(z!LN5>X#F#KnMcp$u}o( z+%aW(U_Ki^sxk733s4}liQT}3m%saD(GW2h@4!- zf{o>KZkJ7S$6Uyu4k|ItZ7xgVjNSOBQ4*^TR$yq3IF>$Cp4CKqKs-|6N@lIAE3N}+ zra6VpZaiPO%WvN8qNTr>s|t|!(QtT-*LX|%9?!58!9s6j$|E#9{18H6zIzuKOytlC ziWh>0Kr5_K@p8m!d}@lvt2sQ6E2OKVPv9}$E4Ph;e5+1SJ@-UtKo~fh3O(GnO7=8` zKD_5bAP=NHmT?rv`GV2s6{Yx^1M{=48^E|TE0B19j;j;O1E0U3Ow0!8}l_l*Ekp* z0zI>^8Ccnotf`H8Jvd}J65qjsa~pn){2IlkLl?d}7uFmw4@Q(NfSGKm8(f0#j@fw$ zv1A51M*&bT2JtK5Cl498*YQ#S6@13I zuWpT~`h4H+YCjmz?bon$g_p3~lcJNNt2~4;-fo5gXT-a@_{8ePSYA07G{lFP$xqLV z1?6J|B38RR_fIfELhAqzUq@}(*2r>X9`SV(=pq`>*kky4uQ9jP#i2X3t9G;H0`Fl~ zH7z4$xYvc52%QY!tX-o)XIb0&4YO^-hF;Y+7^9Pr_}r!CqoD$K#zAZV_&PPH4X~nwLn;tZ>KY zzH9g_u{T^ne|aU(+N@?0+c9W-`J=2WZZX4_afEN_LbzJKKsOt^nw-C)V@O$KgxW!v z9s<2MaQ3s@%;@B3_QoFXDyDaFD1j4yNX2 z1{@t6*`kGFeQDq4v(td}089W)hVhv7paD=Sr9l)jUlJw5xh>B!K$8nPRU9Z+SO8z~ zz!j&z{HWxj9xixpYsc0$wYkb)=I9u5ycsruo)(`OU;J=l<9kQd2lLpG#;-Tz-NR`M zy`7@$n$%CvVuQSk7P6wogC(>J_GC$>hKz<7vcC^^IBme%!8%tVMt_M#oW8UA15+Pn z9)}$HsIjeJ_L=x73XX$hv`K#%Cuj5)W)-FUh)uE|zin z5Wc9gm;yCZ?Szoy;9DGD&`Y&<=ijfyihn}EJHLWC`HYX)q2vmq;m1}+Do^*=&kJH? z`pT-Y8RA(ImWIw}%ZyC*%ulp$$rn!5wAFhyN1c18qO`T>fwrj#_7T7 zv$pTwq8&fNk4lDu9?7niN{SlQ@#q;+mm;dJ?NSXyo$#k;|2Km?3wY`rJQE@1ou< z>NwOHbhD1zQe~jM2F$1erf6>*>*HR?_k6XN#vpOG8~%;=*Lij$-lBb@C{gB1{K&iK zBmV}cT;anMP3o_4L+^^cSLfiduW0O?sUWB#f+AAtGP~84hv;z&%fo}l?yx$uwyHE{ zB!5VGXEiSA!QWt|DMsJ2AxG~4{EIg<S20+o%+VI6o_UVPN=-=WvmAVm1;l!EydE ztYRu+HuHHouKGiUTo!!zuedl4OlU*rlOEWf-baJHhhUGoVF;ySnuF8MJHm0jOFbH} zM;$PsM@av!?cr6aVMaW2>g<8Jw=tT3MeLn3ta+48>Pon+drW#u@?$X@FF9Kx(6eoo zvHZ3%X=TAtUd1Pn_>ERZ9+=Th3`zfGsNowrs~cfW#Z|4Y&KE(OpQ$Tex5_SjnQv0J zn#E2W+q-(vD507tBHQCLu$hB%%Kk>R{7KCx5=ndC=sX9%Vx-mBE*@ zJtH|d`MGqC9_t6fSqDYK4F5-++tOtSy+oQdY4Cc@3`n~Ui`6Lxx}xOv`NDCpu*Ii` z=~J^l&hL8)UvVDyo_(*>c}pPJcGLf^{^HpGv8ezqX!(X4L7myh_$V_i!RsFTiY z*QV_{z>&k$Lc?Wx<{*D(5?dZeC*!bsDo^Le!p(MpHni^@#%^7m@fh5vM9A0PsPUhB4 zg?*hnmYV{&4{cX`dMO0RTs5?)WTmPegk98aTMbZf^_yEK`xxej|GSK>RdHVPF>h}?PRU&yO+E{T z&FzjAc<^$SmJcAIgp1<&4EDz6a-93U{}X{Up&sP#jEK=6I2x6RVxPyBdMivwea1cM zg7IsFa)17?>@k0NX>R2)*;DqzTgv2`sOcL^sTOX&6gcN+f2hlp zO9g&ze-dx!rou`43BMc3DHetvFJ7O;!2%y$ZhpK9#koGx*Y}|%FZXBU^A3!EoEuv{ zN^xB5mf&@}IEGH@VG7iq7jc{ITIjnN>;jzm+nC2_-rvY8m$`0V;Br_wXNR+&OEg}& z-`XgAd0^nhII3rP?9Ne%S=^>3b#dI*y_ZiG56=AOHdQ zUzr`Y6h=*Qhk329+fbpb@9MXox0X(VWx7}^52@$yB=2x8&sF^r572dR{~TXZjK10H z*JnCm#e1g{LL}yJ!VR!k&neASIj8>wQrhzu@` zehuV)QDFtgvD)P%!L8A<B}eWT?|>=rJ|{BsYWY z^M2V&K7kr=GpmpAZLB)`KQgd2q0PC(#*Ztl%>&spUwUk`5!@>erh&|;L&&WYOL1GJ z*`1omy8dl+9>k`;56|mM_h4nSt^UHCE2!uN#<;93;$VeBqDNwr-ONVYeI>5{o8=~c z6B;mGfM&p#=jWC6ZNzumU6(u42%7X5cgMj>W2zHmYG7tn%pRqA|3c)U+W@-|nH=&I zfFFjYzvy3o*D9*tVZJ4&`65of|DIFhEUpQJ`Sl4ev~8ri;~zsJM-VCZ@s@0F$XlIa5Oz%vdwxI!UUC#fnWI6>HuhU zNy$&{+&?+SKMYo5iDn+@C=>bdT~|RRICt!d`wK>0ss-p9R=ySU_D!7G@v5gxhnA#4_T^A$!R#3RaA{u;6F7hLaZi@!` zV&~r?)0rk>Xi8)>;G+ZbAU?AOFew!Un#97EzOKx)LRBsCBN>8|z^co@-_ZnG>qK{4 zg#2XU;Lx?XKL<|DM2A3#ocaina3{oUw_MKku2LG@7R7+bgbnfa%e`VZ#PtxCR83D% zH*nB10A&aunPF5A9+&dBK>aUL2T{r`4h=_9OhbN?`Xx8B)h)-CLguM6gbIaABP%%k zWdX>+-^a$S7)i$uLg_Yt2~!k_%ua|?(xB8Jo=8g?-V&i@AawtL!h|V~$v{RH>)3YGb_d)D+NWuNj19f2nQ%H6nQ<5P158yPWfZ%@)v*rdP-PT zH5M9(@ie^Zhe$xNf5vVrR{Y31!IVb{6_VDAyE2u&VG~fO=G`xfU0k?@jFi>0_U?Yr zf*VaVlvT9@57PaUB=4D=VthS|O`=0Q{6k3o7i2ZLG%-Z>Ro<3PKf zuXjy0NP+ZcF*ko0=s}pM&g^c=!~l1+y*K+jFBTRTx<3`7b&U@C%^P8q&A%T zQf(;t(Ti>On0P8xsb?p8R&Yo~{+|WM_q>G*G`>iY+T3P~VEp`{nl7iA9M?W#kkh<~ zX`cJ~9D(jA$`6pqO0_>>c*5vpo{Yf>7gmGg*~zxnwi7_Ras{CusIQ29 zPgJZoBeWj2mwAt)K90+cnTeUV)@U_vKllxCaQbN#7!W9 zn;>XjEka6|Tv+AVCIfbQ$;XGq^0-ciwrycG7fIkgu}N32Q1I+M{rhiW=>7vR?KuFX zWzl>bwRK_*PJXD-d;BI?-SIz-^Ju&-^FMU1W3s<0jA!pm;?+`sf_T~!)M^{l4zrOL zsq9E*eOjrHuWk0Yc1=s|o*6y4$dPyEA;Bl92sg(!f!2u~ zVCp*FpR{mpLr({LhVIkb+|qq5Oh<(hett3ht)0j(u+*Lq?%I_RQ{qBzn=(GQpcBCS zNl5na=48g;4Z*iWDBrWivAp=h)J62}w+Zwz5=IcQFPFE|It{?-lj+MV&FW5=$yWYm zb)iY@a+u=0hfa;nzNCXHvWYucWfCag{LB0ul!b0?Q_7I|uK4=2h~r7@XHmN~M(F6J z*z`ho(LND2x-0hV&?!?R>gXN4jHaL7JmojG%Y#$D!dL3;u6t1gxa1j}d0}}H6vo`} z@j}LKV@Y;7`< zM};$zEVVnDWXrj)n{&fR?*t>8G5;d`7?_xi~lltl@DME%L2XDnB2Onsy& zbI8iiU(5o+znIt+IRfv1A-5sV)gke|LdHXzcgohU6Mp{*mS=xprgabiGdI+>Xo=|a zy9O-!A19vW-^WO1Lwgrzp9QB@{NmnLa)Rd>D%k@cr@WGxi*cO5H)0MR7kJix^4bwC zft2A})O2re-qCHkaYsk{$*9O#`qZc5B4@c$ckQPaeQJ0@P6m?*1mHmq(D*25*<6;* z#fg4=tM#)@*NmF~27sVKThn_0hNhi42MOjs>wMNsF{k+uVsxcTIWG%7?(t;ol;&>D z`%mtipc9e2ADyhqwezI(5tW1dN0hWx-v65_PPS{CeoAUqr8!RhkDBNxd!kvwS(and zik$&WG=tR$b9X#R16jq~UTV9XXYg^a8UO8Ra(@s<Ak zuQy81Xc`2+x$xZaqqX9}gUNigl2l}Dk1u7Z-G3sY_dx7sB;_-f&)A$Auc#3wiO^4* z>||PD`+4S;dJ<9LGuUMv%nX;QtYfa8Knlyr3nF{91HV(FXXkb5sM^R+30aRzBeN1q z?nKI3sA@cRxi`jY0JxDVPdYO;HMz{5el%Iiy*|U_bj}}HQAF=lg8133vUK-$#xX4{ zd`$Kz4@nexbyF^K2o<|BH~(T7)y3<1X3J3n?Q?b~Yo7jQBI5D3yE4|92){G15xu88 z7W=8%wzMDf9n%^Wdj2#cioED)#-moOm%@!B|2h)g`+NGdbD9+pp3a*`dTRnMf%{ZZ2lN=%_q1;wj<%l$@@?0*wwYk1?b+G4|SacLy=_Yt0{Z%u98c88XFqO-FgjI zuv}`z|9@vJ+7Zlm)tMx7+b^Emdqzk1*3%o8wGJLo8;-Qq6L@yyjRixZpzj^Zi}VW% z_bollGB`Bk^o{Z#;@Zcm&CWEy@)euZFaF;dNxRkoSXsYLfNsbO?ES=9N~(v!!d0x4 zC-0@zu%AkJcE~7i&qWqt%{aljiu@+(EpCREn~ss$$&|tT!mM^F++XM}d_99zsycAg z9pAPTdwaF0wVuU5Tl8-zF1()gxMUrQL!15zq|Pj_}z- zZdo58>G)>TMY7=Lf5cM!lg;T;z7H&@`&gSm<+HC+?>5^jUnosA+NfOk8149ue@ck` z!2v|c!tIm41u^T)RrjucLUNcF%&wX+QEGRy>Ul#Zohs2Hmn|ToL4?`wKy=TPv<@Kj z#UA1!B+Wh*nEn)7eshXh{(5&0Xp+ks3J2%w@XCPoe6Ns?pPeZ?tz+PG`#%kZPhw|X z6(>_ywro0@0SEKkdu2xV#4mvz!Nozu8n@FoQj}GI(R~dVwXpNtABXY=;E@BUSZ>#a%ZnN5o~X)0n?tsHfU^n z3w8vKrwPT_OoN32YAx9Q`ZqsCo4*bUs3=!R)#1be7-|}OO9(B$ulLeO94O4jTJNWKSs)*$kJW%4t4;V8jHnO(re2>;ufOqLFtU zp7wG~Z@S0U4vfnoU=KqwiqoFq=V%CWhHrN2-Z`Mn9?bH@8hf)3!UI1>a)u?m;%+~~ zh)f-mN#<+d5nPAF}0Xbw7b$#@nYZobNgZ^Zb3~F>IDe=ZuwEMugbPUUdGu0qNMa@U89zxGk7jc4UB*`>FD$?WHUkn^-Y^T;IHZ5S@Z7|j4 z*wrIhA3CiQcj4OcH+p1xU~uJ`{<}CBwZ8NQE84B)dysQ8nR$P3aYtC5)Tro@v+ZjA zf_Vmn&oqb4Ffw4%sm{72R|i9`4!~c}VZ9OF3zI@{K_ABNFHngRs;l8n|A zmfBowV&CJ@pEYneJ}C^j2T08t!8n5V=7P1@<>p4=QM^10BOr$?c2)e+Zia)|!+l>f znjECMX?lPwpeC{GPu?P4*f*H9IReoV0#z@1=H)~+v ztaDO6pe<$4R>{_I8?z@G48%m`H=G?8wi&3enUJZ1m8Y4RDwJ_#s7s?WN8&YP-ooB> zFIDq{O8d2}2V_8UBfCr(DPB3$KMdXbL?bTYkAm~RV{&GHr}Z^>fKhF&+XCI_8G}SR z;7LB0M-8K%Pq;5^o?tSO^d(5H{TP|q)+T?m2JZTUisU~>F5lI9FKGr53J;j5YH`y) z_k1s|eJR7z1OKUr*P|V&YWl4uZ9+>F2$RsvlWz{L1>`@t2WZs)4rBRwL@+#H)hE}m z#n1(m_|1yEm@g&3(+Zd31JQ7D11%E?iuU>q`X@@ZQaksRa9xY^``uj=6(Cj5wJ7M+ zbWr9EY-}YZ^Zlz=`qJpha^r0-2CeAHp8a4K2o6RjDwshK+;d29g$})p6a#-18)LSm zw&(7xhg30=UuOkY0!56~V5oWQ91X}M-0`*hdEH8o6z$snm6Sx)%0e*VV7_VkP$~4z z@n3_AwNdJ}RNyQ(p=Pn4Qw$#oJd{WclH(djjcwe=Y9~_(JKgQLN`ZsXgWxHme+pyyfrhP7zdB{eW|MU9_};P9o_OJBodH)`;i${*pL!`9)rWetabJ=q zu3&UvX?z}M0;h60@l81f+P-5laSSwt4)u@ryLN7+C#NSra`{J)`a{*`aXTxTAMBm6 z&VehPzvKNR>ucoe&sgz5?iOyuIgfI$d3S`?u~DmxxKWD@QJf_g=|xk>jO4+15uoKO zG6HHB*Tud+HQT33-RywyME9@gT0DG97_NONT>RyD%&I$rb~L%fUgU?eG9rQcfoxOB zoRUqL|6#BeK3e1v{*0+Wd6L zAflS)=%==F@XJ{14~E;M)J1;q>vYq1ke8JX>@O^ zXi^`Mx;fhHhVcA4!}3M-zC@a@9M>2M!+bFqE?ztqgT%nep*-Q_4^W#(G7XjaigOF| zn+#BjNc{~l$4jaO-OMVOM4Rtat-b&~*k%tN1X>V)zjq3o4=`%1xJu1X`wcDUi6h+< zV1c`t`8T_}XU_RHdlI+Rk^E9lQf3(!fVJFG0-!H^?CBm9s85c12+m0x=zarsTZ@l4 zHyEpg==8wrsD0||Gn${YS~seB!_TmKk29AP^Zt#ThbQ_EcUlPVW3x<9HQg!2D$O3i zcS6$1R8WfbQE;e#Q1xLKi`(I%;fo%4Oy=uXGzlnmBtCfm_#95xK->R4>rdA8V-Rj; zxNgmR{lR~r&YA<7;T?R;mOILuY^vo)70kC|?(*9_h#xu`5yb3&yIQy2U~8~eWxfBy zYF%fMee8mK{KG{_UUHpdyP*|U5GsJZ(eU@~4}j04TT7@wB#PuLv82w3B)?<>Pzaca zgFS~#x+?H5Cd0s{{i#OOeCs|Y&cH{3j7hGScaDJ>(@QtU?_uGr-N3`3di!a_%RO5- zgO90Il^t^4*(honi@IuhC8yZ{VVmFM%QTPmKi%V=GLNk=0a^d=qA0n%4`KxafJnPk zsNV9cf7?kRVEDv(?v0Y=e;5Wo!u(-4w^Ff3IVKal2XV0gf-ds}lka;<{S&6YP)M#! zMifyN_e>-9KfK0zgj|FLM1+FA36vM1ABdqL(KfPln@2mOH_`k}E!0E5=p})lbMMrl zHXBXTmv3Q!A?H>t5;2aGOs?I1ka?C|6#11S!m0IJfi*3ZU6T~e7Sv?{e{LVpCYPmS z70|s4et=TDZ!CPrKX`-V9>zxjH_IDTsay8%z2gFO;s0<*cWV%%g-@t|eD%rta}hy| zYAFe3rhNNYsIz1la?P8(a)FC4TE5z0f;i{!aV#H+fSBTsKrxZ#Ay~`;pLwVJU!ck^ z!eTjN`X3a;nAGzr^$knugJyLz%w+j8GvIvjSHmt7sEVsuk@Q8oGNR~1=)RG0>SNzZ zXTbBXGE72fodm;E*NPB7nJfd_E)nK{<`Y=c!5tLCkGM69z@*ExoP}>53XnQbRiRf+;a#S>DrQHg^%J-_W>_Dr#r&v) z{A9K!Vnl5`;Vb6m<}HD6@L@IXt#Y;DYxk zd1;Q#UBh(EEgkYoQd?Ueq9wgoM(J4Dp8r`boCDkcK`+zcL`G%=!+W8XRm{MH3 zOV4t%Ivzcl4nqtx;YWz&S)jf`^D5OHPWohc(ms*0K%*`JU<6c z?ImNo@I=SC%8=hzwng!RFC{x|J8#i}l5yJ62IqXm(iN>JIg6?5%-$brGRdpRt31*9pO!gH7$39^ww74yk3$9`x}lpjBuu!azWYLV_rwtIip=`c z%^>~D98+47Yl00LUBRQk%RU>GxD{sbpId2@$R??*0zVwBSZSvschN9$EgN%ODsQ8# zk5M%1d=s=qevACuXa;Tu?&l6UB`@h|D1>MnE^i2Ah_(fivt5&1Qhfb@5gUQ})Sv;b zW&Om+`<{6`AH~NF-zCQbqieHnd<)}s`4}c&=w%}dCAzlRjjsTj1>W_vU&L9jeoi#y zU!>h~i<)(m$)b%>>1&hO?73cpUIYI`s)keYTjlrq+@+8|*K1B;wcp?v#5%$9!sRCC zFaP5hXr+}+h_>dkv(gQJ*Q)5V2KVmtHsA=Cr@dfp{1OxQzB2P82f3PP z5hc-kv#pLrN$E9Gg;Ir{l0VaX&fm`$^uE`poa&gILx$gb=UxVeBem|LCtw$CN}F~6b)tSao%$G7SsZi$%HxIV#?hk~`} z4pVRqF!|4meQmGY_BxYZgCY94!ycdppo@Z%8fs=2he9HRJ^LcyY6y1V5rqMYiRHBQ z)ECF?cf$|Z7}p1sPT?mF?mExhOkBIigcsKUs(5+i%gEUZ*cdM}7kfbPVa?N@CiI5Pa;Fj2S^?5F8$*? z3;cv2U03tfDqOW@>`p5ggT(y@yct+t^^xNmMCrtg+~IZfT)AYO=!jb&>$JWHyx82C zoMv+bx=YD~_URo1H5IianG@P`)b*u2+>Gl9qK{k%Cf{D#$9yawbXveydyPuEzzAd9 z>q7O1umjAJJNt{=8(3na>FYwSI`0>N5H3pOG;c*jyD#Rxlk0*_%)_)&Wljs8GC!gU zb((aoiW^~{40491s|;CY?P zgQzKN&L@A-fPP+h%IH)a|n>~=5pmOw!m2Z63RlDdL!*R zp5zJ}MoAhC2ZD0L8(qX`&S^jNWaH}jE3z`+1(xDf(_5m)AHU=~CHMndf~WeQT|t+! z*9G#F_I-0H>-z08Oo+R=!9BffjJ`X)M2w+3ometlKVRQWrdjny@tN}WM(^C)xOT^M ztQ&r&Zl#MGG2@o6TuOgO?U~H!q}u-4q+l(ZCpYd{f*;Q1-h-RqQir3tqhGA~if^iY z>F5?LOj|DHdi-nnY>e*`+gknZ9?1R~*Z_)nS{I))-owXjv)I`-^j;Wob671XXW9{ff@%nHd{Ir zmn_qFXF>m@|Ln2-JQ!7*)yvV686oHshMaaX^{}p$)E&5my2@1>H2nO;Tlis)msvG@ zry_rXvIS7K3Rlw^T!gYWE}9d>3Phj$L(cO1=(BD}X{^3KIexk~oC^GNRSaY&gz7<- zE&*0swtoO6(@AW^S%boc#^9;TiF&vN+&Fl``5*yBjRcb}cfP$l>JP|@+?_=BVt^?; zB5WV!Z>0`gCUO!QZApiKbE_D0@82cUsOc>4?)$+@S^Vc9`N!6O4hq{s9N1>~)OgV9y;IT6TA5ggwG= zf)h~3Qm(^0P##_fukem#*bwTVQEwP3ZR$QS-;mgq)Xg~giK)6wbqdU6=YE!q!-ds< zgxG7g7N77Lz#x@`@NB&;4GAl#M*A~ZQRS!3GlF?9pDDNm6>Ft*LH^rX~B3=sAxR@7L?@>EW+6>1=JEH#n=qNGJ2VmvyNJEO0u-8w;ja9XDiwk00k3XI;Qq5C zsm`lXZPvHoc^y#CgT?^-&nJ%5YW?G+Rj9-!P*h)?{ab$`6<}ZO^75(=?Z&@DJU z)n)PQW^OuTo3@-X!Wri(vfJyWDX#qjM~+?wcx$#wfN4q{);2VTiQkXfyB1agf9!;J zaArM*DnCJ4AF{gN98f1g!brLACdF`{HhT#p!GHf@UfoH~OU9h3QMOlcTi+S>Jat~Z zT#n9M3-QIpJNI#2!m(jLAab*g&k4|*@qwx->{MQ8AJ*!_} z1=Y0(Za-5uNNSAYXNk_JTY05qUBsg^m+iqR5ji2^HaFlg?z!*wPq-n3N3*2DvZ;QN zRj8|IM1xUR*T7Y=?%Wcw-QC&ibXKuPPhCXX?!i;3wpUUH@D2q`^WL7PpT!>oQf`KN zxEUg0e;xGjcyNC&s7uw7VYz=e8i0v6^4!}@E#12QC@9LCR!LH=E)PY_#&-K%x%;{m z_Gs$Sk$JE=8#wf(BKC1qwz&Nb`1wyAA*W$r0$UBt1JP&u)*`Z(j=-G&5e*4vzqfNL zW*gXDZXP z*oB5U_@@Ue7ZTB#9n3}Sqo^$nOM`0u$i%VP{Ey0QLzvC}VxgJB7eM;64yT!P129L2 z@51?c3SWo49D?`W} z7T;LBm^`me*WS0)jl#)6;g{w!O+4Hh$z8=9>geZfd&9-nyvLIY6}D*&>670P)CI$P z*Y0y+5B~zz&~Jc{|D_4yh8+EzdKXL)4R2v!N_AjN-BFLH>z1bdMwln?4z_fgXyz{g z|6p&}7sJ$@wdNfe?i)P0u=3T^@*7jQA{bYc*|Y9`-mCN;G%wfl)C3XjwxUcwhdq&A z?LF`o>Bn}gl)S2hvsOiE;9I;!G=nJ1%^yIx^UcQ2<~cM{*E;Hk`5zHI2?}A|2gjsN zVLMM#j|fH;!3EFNsJJkPFJ1rv^`kLY@iv%Htn0x{oZAf5;sn|QQLiH*lTj+Et6t*S zz}yD~?7$47xdP2vMbG3o2>vt|z)hh9OvTejH0yjL-|2bmbvwhFW2MhPPUNzmZ3K0q zp*YR}kyGP6{1&Nd;(r3PzbSzKB6C9?F@kyOC@;q}C=pr7L<8cN{8TW0`;Ib27!)6~ zi)8Bh?=k$=IKyXkgnpKE#s?0Ng-lGG1Riy7u>nG1a%v1VNb)6t(Wn|*dL2hQzYuI-iZWq{7>@T)my@i_TmG#_TNdK!8GfP8oKHKx!~5g`Nl2SPzg z>$K{S#d5=M&C>vy04xXcKHvcSMaV6yiQbf95?k@!u)fXm=_w#|-cj8r@MHT@4kHw1 zTXw$~VNx*FDt)$fA-7DToLEcCkRuaCm0_=ENb4Cy(-H+16|JJg`*A&-9MVev(y=Ih zE77@0;L>CJ0Yu+Kup9f%HOQH5 z?Ysr}mmQA^;>d`6p*1hz8NUP9?!2UeaqQen<$>R|h^T}2z!oml-m__~=ep(T+f8%p zi}fheb)_52=Wx9BF#@aB89=h)KF`_2>H9tib8lHwaoEv=vdqM2PL{bUxlgwat%8x9 zPXyF;A!PL^cP!#2Tn=X+$cJ&9>Is>~sy1TYm?LgR?Qv{*^Ax;1PL%;WFivsUOJj-r;%VApz3}j|&Oc5Z>I0c zInA`7D0mliSk_VLjt}B6wvB;fZ^9bSvdC?y_TIo?0{8hoz0FA9kNDNe!6b2x0^#c+ zi4HcD$21}6hU_;XtJ+NrkuP$GYL^G?$PSsoUJD|4Ni}z~55B2Z&$Hf$SSOO<+NXrn zJn+}E8K$v*b`s$2#>A>)2RKiBoIdvF9#0H3M(q#W&6cMBtyAX1P^Urr%U<>O5&~3+ zZ@HLn?ul}tn>iA1Ouf$%;@lh}MFroYVo-;ILxySf-QF^Vd~FLVlQ`h}z$hB6I0jG} zvB-E%@FuAJ2Xn^&-8lHY1KZBMaL}XW(CBNSJr@Pp53?&{y1UP}NG;PwUi`>*W{<~B z5)?ey5NWM}N3HrA)igB})s&%|%Xm@Z7!g8e?c+iKIE~)BYhkO%FN+-K!5Wh~ic{G! z_?q|6_-5CFp`&L(xo@pfGt5>ADHA2 zJEjhAE3MQkkRgBYxHn>}t%IRkP3aHHiDuKNJebb<#Ujp({fuRAGEa9l2z>J;f{@ zTK0Hzfhd(mg^6!n_Vz{Y8Y@j@CV>4XlziVL*d#^a<>b%^F}@lto2bEONBItNjO0_< z)i*=Ocf%`SJu7;?IQf?;aP;@kXDAB@=+q1g8

    kT`I8*}YYS@bX0}2`InY;tnJ4Uznc3i~0oO^Je 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 zŴx#qQBKK|%m6-MYy=CMV>XB8{ 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 hMs-6BJa^)>iL*E(P?RnGfxNz} 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

    PL)D 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∨COA*vrtfw~G82t(a$6M8`08fCOrU0d2 zR{vsFLZU?+U~@oez94eQ5y%p6!T zNcjGMK#Il6jO z>lC_aXBl;u;9v%+{+5&mkw@^4!(i`*I*blV z2p|OmNPx6~ao_UbQ-y1B7sw>Qr1;%ED8L4*{xd6KpS}#K#-liVZ-6lsLFkoFKfMU# zA`CZg+|ppPzxa1KcJWCjC5xta&LOLn%%Yil^#ueE zc`4XN?n$n}2f-Yfu_paWj68NR`p3h~7h6cVqG8cFZ2C}+6r0Bz{mkQg%ZWSJNau89 zFPsHExk2B-0X%yYGK8_o5Rzhaq_e(|Ali4@jN$wmFYi4f-8S$EUMPTRmxUk#cP2J>GjwK(l zzad>jI6+ayGkC=slgo|Gw~D>Yh2%z7m{9B_T@*N;c`V(Hum=EdhMMmaf|-6>Ahyw- z^kYir#mV@djMO5v-ZLFvOzLc2r2>rCnA+*MR@)zl5H*2Ty9vraRUUK) zIr=LC&o?&XI)2DMW#573tKfo%RWi-BQI62|!e_MF@|sZ_Y7Ye)9 zshl8~gpg`d`?F=%-iE(YyX!6jx-&cJhdErrPF3o7f_XjT;oGE;H`|_QQEv;PVRcIv z0wBv0>w&>pLP_8yJ^(JRLLHi|!RNkunacOaQK)yBAyh8VRD|PfSCKyuB9*@{4WQyu zw4|7kgI;T+U+*Bi>l!XXe#3rD$qHzO6K@fsL;t1W(=XZZpqbebSTG5^mM^8Si9?f- z49!Ri7Dk`1(MK=J*({(;ePn>haA4A0bBsDybH9gUSVdyLkKVqtm?5{twOkJ9=B1ab zs6fui|GE=egJ^+wOEwcit}a$5!L5^Rld1{%=OJ`DAp0^5yIckmFaxOYYEOrzDg2>k zvssBAa>AZv@3|`WuI1v8I57-!iboy-q!e&$co3`EqfnHImr|LkV7jSv23Xxm+eUqj zI!O-=H!h4A{)KSK{9hvrTWYITPJ!G5beo^rqs@34|`l>ydPc@)a!+X6t{TO{j4O> z8R-_dv&^d*)eVbmP}m~vuHz8Lq6*I)_h|Vdt9<5$EU|M`&{YZ4+Qf7KG4qxLs zOPF%kp%;Vp$jlvE_H8GXBT++zlcvavBWdb0)Bt~lWM8_L@*XkBR)TC!FYIWjn18)B zH=37%Dk=>c_jsA@rrgN|X5uk$g>yx%w?0>CtP6pz-83Lev`kcV6qjHZn%3pEMYzK# z;xmx+=IGw!Tc>;DmiAfZxQ<>`Pv%6723+;fIz~{WVXwg83d0aawTq&_U{lJA!Z3`J zP&}Cx7f1KR?U@x-i~k@JHX-nN6qp-@!kGjXhWr4z%|7IytVwX;h|stT^74%PBwtsS z^-^*RUo!zrPJp>%8S+<9a}TnWVewN2XF-kKnhvuOj_ai&aaZI*C~`M<Q zYFC0HftPk~u(dFFjq)?5;8v=^SL2X*DEuz{n_#dTDzk zD6goXmAQtd4RSN9Mh~LbF8~U-s|Wunn}I&QB3{N|0l--ycoksS4`Z`iNs< zHhXL&to}k_EcibGNhh}01o{Rz=fKgqMT;6d-Exg+oVIW#3Q=r=M`R(XH0er=+o5`6 zIKz6@xqLvx&F@Z)U%z*M-lte{6Rwy{3#8dtV{dz5ES|G<9B&AS7X$CCF*-IZMw;cE zZAG-gJe#6D<3h`lR)yAxjXbk9_OvzH#tG2Obool`1X0MMp;b@rt!Dqzu^?AM(UZK) zKaR>pEkZkm;gy zh2GOkrj^Q%n~&YH1u#B|z6s#VDSdIbLYCrN07p7yd1!YILgP+6RdV2%g(M({WU7RoxOTw75W*t31s(i|fyF`)wdjzBIE_E^E zzg3|F8c-J?5KIkFG{FE`y9K{l4zYV_);fNeL!m`DLlswLQEqeNWKY^I2_+b674 zs{Y}l{flHAM!FUVgdTbt2!QNX*TxP@^Fx5?kJPim@<%PG+TU~jaX<}^t@s@*OH^G0 z?ei|vFgzI(?>dbPe_*aVL$DfFykCe|>7Mi`GCf`8`P^o`cIrIg3>z1W=VuSw`*?G* zvH>DYZJga?lSbgU;G~1z6L)3|TKzV6`}2udvI&4tebYQKR5eMrz~&-~6b~1;ZhB>(n91qfjpjIL%7l)8iD66QI>dRy#6hQqG*Mo~CJ%o~E`z(KgPy4mqtP0LKi zHZy57Rs;6lV(0BQNPu*G3E83iU0-I!DB~0FgMugedLM3JbsLe&1x%9A7<(ye#o8<; zatRTc_CUOcc*+sDG3dz3*8o-J!^Z`Af6=;BDSHq5PGAw37d+TpE;uI&KxUF}H{n!y zdDm_%prsyS43|C2(I{J$t3PS4=9^T#cYp&>{Rc06iK~X4#3WK+GC%p*a|E-PdS06< z+j|Yt69AE{gX)$2`;IXg>n28^1|c_F0uR}fds7j zQ9fE`c-SiU@Qu!)?&>&)**|Oirp1Vr4Mw=Uwnp*$ZqVAGlI}*)tLP%TUQ^(1lSggHZx8gfmI6y`wKlP`P?u zH<~Q7@oK-P)j}vfcDwVHJTcvUWzOC3_*8Q1RSwY>AXe0XP~f-%Bm#WChGdG0xHG%|m9k#XYYZ^CEQ@~?6<9{FA+Q+3@>O}S#6Gs$iYLwQCWZ`d}XdSM0R@z%4ck7CGo$ z8D~Lo%CdLlT%}(42ZF?8896nK3Bk)1Z9eAnQm>>8kUNsOMy>1vZ|l`M?1gqcvlk7! z9dA`--t?qTGeKWz<*(+faENQLs{&ag$zsO}igzyrTYF0S=sa8nFHk!?@C{zoHNNTC zDf>{q<0Q%|!Hq;3w|Artv{_01>{2fxg<}P1`MRE$IKmou*D!!B*YS#7GtG`|^?{gUN31zX( zSQ9hukj|B)hKn-TMjxRfXAof6XPoulD&!x!E>CyD6kpWmXNZFjZB1W<-(sm`DmnKC9f*kYlU$(BirN8=fOn`u`6cQR)_v z>121u%l?8#>xm2pEk#;3y(l~^azJrrU-0H)P-cO`=cK4quTp=q9g2I|JbUI24UW%$Ild z3y*0~SgJOpx3gw-5a97de4|n(RqRYZN<)A~rx0CL;_)D%;z9o7)W#koSAE?PQ&Fg3 zH0GNV6M`+P9hjUO(qyLz`b~4U5e9R3o%l=px1X3vF97+}7*;?K=0P+Ulf9tzk+Ma-$(m7>Oq0RO&-3(lh##dS$IL=Vr;~O zjxA1a+9!Z@f&?|HH8+e$(3LRBxU%{Y4q%NG(E$ma`pE7-5k+286J|!B)rMT8QVI}% zq?8~XViSJfu19OP}4&!E%N%@R3_&n)#T-h$6x&0i5I8t^D8 z7%vnBq#P5;inEx;#U^;WXyL5%$2nqiWqa^?gP&*??xzJgSc(fB-hSwm5a{$*-T-m} zQ-tmHQ=px1T>Mp<;;OlO~tV2aM)cG^Po%`)J_1qF%GJ#wr3V1mC$ zOSbY(QNG@ldyncsBeB&YBLJsH5`j@#Z#C7#OWhgc9PF#oiySr{PJ|mZVdc%x9N!rU zel50h;&_8Motj3$}a#U4m5ABcR#$sE)j=3k3 zFP8lmx{@PySPeP=!_-bvuP_5h@gC$No_YHS@@)I_wl|w7fFk+M*y>A`bJn z-`!;4XVFWcwcawD$v%175uhXIUHgyl10$BQ=rhBv{CHlq*^({5Pi_w}d{KuMSdX=u zT9y40@*_BMXOM}gw&N8u+91=PL@Q}18Hp3PthS;d8O9b)lZg{k{Nci3b2p;^RUP06Km3Z7-C1 z1}tGpw{bzVC#)K~dxc7~CDqq2b{cO*HJHiV$nb@Eh?EQGX%V4`{fB}1GVkne;xVDa*R4W>*c2j?$>@gndj}-#*RvQTZ zQQ0E9dX1t28zEo+6l?+X`4|!LIc!YN1FXSQeJ9<>-=<>u)BuyEdB3LewiwBY(;X5} z$n8+Zc=kvW-ahJbfldWo#~{tel^Jt*quyQc{;FtH5cg#it)<3;Sa5$6A!Cx*usGTq zzl#x(M=}G3K!n6(DNbNS^x+qj1VRynk6^?yH9`j8QX)PV&9VL9M&#UC$GeOvinh}O z=BMaH6f5#owY5n$E&e~elc`sdpMug0IjI{Iv^=KAPOR>yLq%Rncz-e!p76N6qO18& z8}vCGG_Q0s(A8ld=tgh6 z>|TUlW%D9rcsu~>gf4-nHXBm4SDC;NW2!Mj!cQ4KLtfvpo}6rd`bR!|Y6^;x8?5!N zJ0G+!eV1?}i${lm`L9V0jNs*dPPPnFp~vqMb%Lv+(2~oTYpGL~{r+d4UOD<=Rtw4FCPHz7@tdR0$R7}A%_n!x(Z%;|K&F8M&T*)~fcRt~wnpTX(8uM)kRYd6 z_?BSCFdiz%Y#4?7v>d27XQs3}KEBr-L0GOMZm4cf@oP%xVrbV%=4dNS;Ci&XdcX*G zIC*y0;acEVE5Lq8y@gAx2Tdc%0M0gdf|>%sJ9O=y?DaWp-^_L_o|<21rErZ2ty>bM zgBiyN@;jX8r;~Sn4ELe`@GU_k>Uo*WROvm>O+6@hy{oYmF>~7pZ?z3JV-!mO+4ut{ zxtG+lVp$Wt%05fB{CBiRhmLf5g<2enH=j;ZesMC}{{o7lSV4~+WL$B$;s+XFLlTrA zC!jd}SPeL)JkSKEe#e-zT3lk%F4X)KL|NG&hl&F4o{TvaJ%H0pE#bEKwUKIa3f7pz z!8LwQn!O8-L#650ZQ^|@eJm`365$uIat*XDFFzg z2%>4rEcB?IKTD_Asm*O)y_KcdP|+C`(N@LbhN3T5E~FAU2eCi77cvioMe@vln{6Q{ ze88eLL^)f-4=kp)CHK^rn5l^aY8_la?9TAmzxjfj1G|X_3RI?$hd>q_L=!)%Z6K7J zv_7U-e5v}m)>y13GtkcnCxH8&XApGrz!O`)6=*{h1q>EAI4b?OR;5cNHA#SCF1lw< zTpi6-ni-W*8Uc|@;%?*TmLVt8|4HT2w+yKpQA(ZjX{Kb_L2WXL1?dGOy_TOp_u&iN5f!hUM zi9lh61&=<>>)|98MV8iv1n+gCPbh`L^h~-lV4IfdRpb0geaU>*6I#z{+kBYZHP1dV z1$K8@pc}Ys31TQ0-}-5!N~kb)giqoq3be0jKw^3}Foi1}kPTBVPh)gIM*?^{Q!Uw< zZet%MO(22D)P{C9-h{Lw$0ZW)L9bStx5lE=91bBUTIZM#!pX_rhXsWGJmJZd$nsDY ztCZ&$Q#A$1P?4U*4jS{P0#%G+T(j$tFJjdsbBMiE&r^{zw<@{JHVuEV3{_14T>QE2 z^HRQajjcMSJsx~`tqa)b^=$vK5HsB_a1uWuGYc79U9m{EVu|%7$Ilh^ZJNS^=XAdZ zS#@zo)Hej&0ZoAYNG+95h_PjpxXVkZgh=DESA;HK4G!u9Q zW!98T3?7v<#m;#uP0fkMkZPPQSwriR>!_k`z>&yqvZ<43giB6R=z*iO)tucMl9l&Y z5&=dj+(eLm2AW7lBrps5>uFQGiPX&B(S znD}v7e1j$8yRa09zm^BRK2&oa(no_( zk~IMaW*nSf95zj^7?&irw&}4afQtXb0hcRWBnmTr`*2wKW}sN~k{e0$gFKg^QWk># zNpqK|%9I`VtiBFH`}Br^ik#JO_od2iq_bVD?(bNel2XxV?5kj#F5)liMP`*>Nu@09 zPX*@OvhvesHdTv%J_Beg5H8#*_a&!^AUv?oXYEV8730&pcMtX2*t1d`xb3V8=iG}j ztg43qr9eF?n!|R&Bq0AFAVcz31@y(y*M@1bLIh}H_acYq8-|NSQWh;5lk(A0n)bX` z8^1V;yZ>(ZTACL$0Ib&jkM8UFiR*)pJAE(ha(S`XsRfV2z@mjpm1W z@=yirq09iGKCeIX2ju|mgib@<5fH{V+-lqrRxnv$#2Hwi+rtwY?jr2JV!Mc}gEe;mUB^IP5pq z#5pkKb2l+RqE+%n#*gGI_W@io9UCYv|J#d?v~B_i0qR2(#`uU_0ZKcpE?XrB*Cbgg zaz3k$bUlG{XN*XSw9ijYl~x&)<-9?>fH8{vP%L03VWn>!=f>PEt|}75Nq#N2lsJ2! zpV$U1YTxI2U56*2?-YK3-_HdR;#88k3!pOMm=^!?GdQET-|8I}Kf{W3&_+;)^Gp0A z@Nz+{!fQ<1G{0J7lR-EocV`N`OrF1Ih#%^$Q)k9CEaF37DRnEM;28ThAnIYgbV%67 zr;qHV&whR#|HL6~8Qbr05M~@8M%Ymd7>V|>a?27|(XTN^^>d>*re9w^k#ieJCdtAV zN@a9z`(QyuKA-q=;D(|IqVP-tE*pEkVMuPpxNDd^f0Ib16IYCf0){cCbTJ+H(0hFG z%tY1pH5X^#P?MupxHCSfx4m3pah2ETLl)K#CJ3`;yJT<>B_mzhAG>7!8JgEk}o(&vl+|)aRK>-=z=^-vk^zL)F2p(YwoDOzwUa>HG$WH>W zg5L%jEWXn3T(YD}lu4G(3w=MY@Jm;dt|?y(sv4jA-xH12;<>$g!9y9A4jU*lM|zQ8 zUd9OK+M4$!8uZVa>XPQgy!#&=K!|>W*w*?6T|4dAe#|D5I?T|~Ld*o1AanXFl#NEX_W*Z*Dae|cd=68ReB!kBC%V;z;ARbXTYp4Qbk@vd zVE8THhmG96tSJSp8jO!+E159sJ%v~fl2R98=`O&sn(;I1Wf7#4nbR~ol1zh@%Ovb> z#t8mQ0OhgEAfp+<(a;Y?tqAb)D+~q`09nj&z~jHn9U}dK}m)6&YwjZzM8jhUa zCb-4L}ZnT8|-0Ik5djriXv5W zQ-6HvK0*t-7)Vu?pI9uI6F}-08!G$g>=u{&LX749QKL_8rnQ@7_X3XL*$%avkpMdK z|D|*oT1#qMsq!ncW6IJ&?__ac6-8WSLV&DKH&2ndTwt-;8VG)ZL1JgJC*~1qdMP2P zif}}$$WpU%ixlMgySwU;cEq$CWj2+H7Xmd5$#jz;IBy+TXeK+YDtB_8NcHg5szi2M zl1G{@d_7`r=-a;U`{AU2m|TM6aOOoLS<*tyGi1PrWi~3|RJ{S{O8>W;?GVVe4$mY5 zI5K1)k64@9QJHov1%DPifzg3KDyJ^Hzx2EDrZT-xEHx9^Ptwz%QvDY5O)Y{Fx&e zxULllFHfAp^zCoo5%in%cD;uolHQ7i86W3cUYf!axd_AT}6Eks+J`&(XW-C4zcX+2SK_m0AA)0h@ z5)OC{l`{=f$>lR~-qx0fDBQOtt1BBKp1O*tEEO+vQ0}T0XHg)H1UnisOLMAKKrZ7F z-ABy$LJSj_8zEN=1Qxcp7Vw{rKOVhM!?aMWMoU?9cJC3I1W6z5pu8`ATDmXnVPCAJ zQ6qEUCB{&h4j~%TKjG9IxX?}x*P(cpaz&ZuI;dva$bh)gkPn<3;MLK>G0w4AapKo{ zt;pg2hY;jq{3(azEmCl=x9q;&t%L4_ZtE+C_J2%n_=J4g9tIgr=ixb(9cSP@ugAVw zT313r`dwqFD6b84zCXFV)CSh?lb6NP)Ch#qG+k)aEi%Rn?YZ_q7ZRd>{H_!>x zGa@oMSQ?57#WSqPr(g&4UnJoDKMxRifbvIovbzvi4FjKnXv9T**lX&EV^_Uon>Zlw z14mwsjM)$aLj`-WQOAf*2GpZqSzyfBs%A>mlU=?M;-o->Yi#DYsOxi|M|)v|&ZeB) zxtu!L(JCbRB&buxrG?Odx6Y>$sT5tJ0*Mebn=hj%jA=J#l^REdeRI!o1l{&>(WsF> z+v2DXQO9g2!5^QbY9&WA<|$q=zK-yvy4h^<%HWNw5=vbPsd@#s#BCdLu4gj)o?O*O zmU~=-XW%ZHwB_tNCRYY9k8cCezdEEvsO?Akhu$CtA%2~HaJDGLQefuN^vBD4tUj;H z+uGuH`e!8%1ec7wED!1m=`AMBc!vv&aZC+WAHzjXM$AJR<%>k^jOvYp(UQ%V9#gY1 zGyXe@hU|}NX#4L?cP?Sj>2r+UZm@uhRu_=`0;jTE?rI+x-LZY$o0_5ff=6tXuR08} zQqT_Z`^H(tAp<=YO>Y|WHyd{5fk`PycLK>krpB;8futgvxia7M_uu2#^RLwX!8V+G zVF>t#cO5^d;Cg^R%{`=kDmL1Gv#BRV?6RUAX`V=ufxW~Kg3G`w*{x*sud$xlAfFre zy!`oFBCN-57HEVHRN>Uco#`I;J+ugh5{?^8LT(}u=i(&k@_Ol!k>~k)@zeI^m?he-;IR-?^~Mmu^BdgmvM3SSRS#p#f-|1j*`aE_4?>b}a^%@< z3b=@spl5`Y5-l)*GJT6-alXOm+-zV-)_I6I>$E=^tG7`gPr(kRS$s_RSQ`IxE6EGca};-yUY;!%kYdEy1CEm=1tU$;YmO1b<;4 z#pvFQ!aZo!4VMd?;bmHM!ruk=V30y9hsGPU-5TF$4fI++c*K`;(a*Q}D1fwiNZI@Z z%#(O5-r7?7%Q{5z43sP=H);9u_rW1UUOz^5Id zd5dj}q9`l8y}S~5Tv^Qb{6!tD<1m5oy!GrY;4*r@I}v~Zs@uES1>8Kl0#PzP178%$&ldsAa!XOauLr*`4K`ui91YJRf*$Qx}X&Ok>(*i>s_1Y;hp> zvFWUE+vV-f6S7oy_hiZHfEsN%qf{hOGWu6Sl*FTgvIrN}wx%_;5d84aJ^R<;7_~A8 zf+$km;GH0}>196-Ki_{*M1`OCXI0JjYCM4kNuro-mRkvuhGm)$?WZHEJbRx%P2s_j zuQ2G2U1vx9p7GmZgQd5F5?vEIOBVaFDzJjTrHO|uvV!K;(UGV&#tjPRUOgygdIKlX>(QtP9sr=X-h5gi05p@HKbD=b_eeh-{TIgE{6`%}*j34Y(l`yQQXv@B}A#;)=~Q%6pgIYpX^w9fZE(1V`zMYhBU zX7kKc{M_@I(9&>C6C!>@hJd-{Qf1VCZ$eAqzw zoQJ*Gq%>gaa-%Vpo7Xs=vx1>}yEkkxGuungo9MuU8E5?N3ZE()uyWv`*rI5>%$nrE zS-ytzTSD6kyW|oUb2;VICm6d*Y3O?s`%R47icoYCj!HzqqvB`R3_Pg7eJVyls}RqR zWq7TrfqrZa3MG8+K7?6~Z*mm^-Q@G!Z-go0i!ctQ9?VD+6OZ)Aa6Fretz1K0s|$zDZ~AJwvWhVN;b>@XP*F@dA_ z_cA42UDHeazZ)N=x_T~g8&;&CM$zd+gdnLf!9-3DmMvmPhjM0~9FlGFYpkdMBg_8k z4sX?_a`4AYT5!rspqBc$mv;`B1`RD9Y;5fx)s@c>DUVn?=f$tf)~Utz5t%iQc%=5^ zKlTG7RNIO4d>?bv3&oPLxBvo@igmo^nuu!UE~*l(59=h&Mj)|8WvRYg2z902qgT4x z)S>GJIFa#6Z8+OqQ4p*lt81 z<&6zsu0*)Rmu<`&vmRrJM6{VoRqRWVp8RDwJk|(Oykr9Txnl+nL|00cH2H7R=*vgH z@d>-#Wo<=x#k$ZWeMGve2%JD2m;^VSRz_5o&EsbI2ap74=ZG1QwLld9LBdZ+gXOO` zTXSut$IT7y ztIur%3BwK5B5!vGUsY@c9q=rM0|7Hn?2^07p)?MN2~4g94$uXa|4ZoEqau0FWqI`9 zU!=?JR-lWsHpXE@2ce_!G%irf&Qldu(#gkaIKS*eg-nhbpY_F^%>*tQA&*EnT%43Q z34Ilr<~%ASLV%{nE8=O};>5 zNQOYlnFDw{LT=izDgpE#zYfqSLFu7>CTCVEh{3z=i@Uq&%yVlJ!!?0SK}2|7Tc`0rct>0O9l_0zXwZ_?cQwS5`yn%xI0jlzA(d?CchWeFM{yY)GQ&txN#3W4 zX1+;0CshTkC*~V$FR}dgVv$vmh+gJ3&!@)&I2Z8+keSo}%Iir3LV%ILcgQ0!aAHjJ z&v5mK*!Q#4#DKoVxU3z*2beOn_#knFl^`*+BUy{apUynjFI*f}EoGzvRndAJqqC4A z)l7`D`^+YBI9ZW=ViDK%d)WRu0dXva>^wfBMUfZ7`1iVe+OaQI*9HGGfg z6s8WM_4MK-&14@C%=lvkwG#MF+c;)H0PC;&wumI)6D;NqfI_8!C5=A{(N6z?h}Kvb zJwn}qXAMV4`gNSWt}3ncBj@vtUq6;XL6;E4VvUae62LN=epp|dAzJc9Oo~%P9YYcS zGjWB*?D2si4SFd25D$`@0F+2LtT%Q;<779#PWCt$zN7k2bqZx}WOH4?5av(28Y+-a|L}g=dWMv9IJ_>Vm za%Ev{3V7OVyJK)}-L@^9WW~mc^~APq+qP}3*tTsuE4FRhwvF%Id!K!7-CJMHKb#XcyZ^A&sKugaFP!d&EW}v612hh_pL6eXOIhq(aTiDqO8#tSA z0@O^50ZJzJ00u??BRxF>Gzmb+&fde(!pz(mKyF0wZzDj}+Q8Vt#=;SxYG-ZdYGGs! z;C6L&<#%;*p>=fOrTr&K$;1TUY;FQDwXik;2+7H7NXUu-$i-w;0AeP#CXNQy0C^Wf zYYQWQw1tt0t&<4_z|_tWVErEjU}R@&Z1FElPPG39VC!n)==={bQ%5@+fQ*Q;fT*0T zGC)L#PFV!XXk%aM-6cPU&-3we8vx7Mg(p zU~FOJ3@|h?v#^Dx`xkE#wx)If*8iA|UF`qs`UjBHKllLT|8PhFFg7v$C)&l@TGqhE z1VAoiXJhZ;Y~l!zu`@Ptv<3W&dpAdme=PqEHL$U;_W1t|{+|d5XM=yp5U@4-hd%oM zm@J$`E!<6v(sI&N|2qW#c8k~=*%@2dngNuY|KZcX(fEH2|8~n8So~*#{)^?m69r)SKe~*8 zv!jJOK#QLCUn4^Qujjv#&i{!L6tr{qq+z6I2hcFF(gPTn*q8zAOzhtO7hEG3M@JJ| z=YNy_Pc8q||2<45ChjIi&}++fMqELbsm&qfJ|cy)6<}1e)A}k@%M8FPQ{6XONEU_f3Qbu_my`6O&N{VLHoLuf;?aWE^uUk>l#|SvIp;N!4y8OuVhmCGW$k@nh}&_i3y-#96gp(c8Jgd) zCi_&?h_mLw1=ogruX}4Cp?QJ3us0-_dgcY?{`QBVn)&rtmO@$@@^cKG;^?_r2bh?` zTOp*|t`%P~gPQ>Y_g74N;Axv~0?UWmPkD61^WYH$U^m=yo7;YTGPrWLoDf-ypHh|F zDiN_;Pdv;O9p2%GyECpYKGSMy+GGw9e$LZmI@^--B81QZto$&Co+CDUoH^@KOU}{` z8~mQ*92;Y+YRHths1kVQgc~`QqUhZcL`#_M`API+~nV z_LIrPMujYxG8$tBJOx8hz4#p)iIq@{g%6@!Uf0;|gSx4D1!xz&nFpyIld2Gk%XPT`IPbj{y6qT7$}zPnx~ z1)%sqdzWV~6pXFtEwh4#?~D|W70Zz-3PCt$K{?RQbTI8+Zrm(u(;3bF)NRL_PZzuEu~au+c-NEB!y{Nc4*Gtl zQhFu%m~E7TcD4jT6XUyZA1yxSpz+4IRL>UY~YFKdSZoc9iyRFDq;hQC;&1YuI zxl(5SzSsAVQwT~%qI$+Tz(u}^Lu1>i^cC3IhxzH+i{*E}5*isv`@&E{RmWvCEi{aRIei1CI;};I@R$2H4D6r^GMLA(=p@weMNd|03ns60 zx`j_{Yx34=pG}7y+oIhul&dt}epq2qw$Ua?4&B~7XFaiN{zp)zJPE|n7M6P(x>Xv6 z_(}ODISe;nHSaj}x+T&tZE6o;fMP-0Ujf2waI(5u4BAjDghfk@^h!7HqFun#kMj6z z;&l6g0?TlKXb(h>naM)`-Ola=$bho9jbROHNy5c&R^`+xJX!+HUzjvIEmBrM$vh@m z)^w8UR8Y8~QsGeMVH=-_p%ne0DmLQIJ#>C36mdb>DGutOo)!#NJT}1h?kOVu{dFt8}6JGBUi>AYSbvH?redfT-jk{Q=q$IHS;hl-EGtON{Vv~Jb3Ast{EJSfs2SeR(XSpONF_jNOynPHj6It<{AMj6`H3 zn|R4b0=e#>@Sg^AS_TSj{j~Bw?ZbuvQJ2He`GRv!jm-C{d04$G^drfqQg5%bAPaz- zJA+ApZ?8$7O5@b9ajcQlZ3+9O5Yls4Xe%3>$c3~oZQu9a+?sf3+@#7QH!f8=h0|X+ z=!dwAHR~W~VL5`2=8qsh4N?+mRrbJ_hxFMF%`sNN3>JtspiE`vj#5i8ZkEBVUSe}Z zI;TIn%T9L8{>;=JH8peajR9-nh#zW_37yAF0F+{QdN`;>=49|Tn%7-DTbtJS8I*N4 zMP4?ZFmV(W*gf5>6(u+3*#gx;f7sRI$kxZ*r5MO%g45up)U!Ken`|D57upS`FW-uD{C1rw|}7{W5egvO`s%HUlr8 z(QDD4dWMWuq2*%&pDr}&oC?+Q?Z-*01apqMmU|~3lV*R19v9~JrlK=<&G-&y_9AxG zF~TP1%IY-S(2D8~z;gO@Ka>`3`fP?G`*W7cfjTq~KUJSsTnIRw(hnq_tk2G@f>d|qs<+cx z=LKt4mob62S?JH!5#J=a=z4pxml-dO-H4a;z73Oef}4B)%NQRTz3Cht-<@#y;n-gj zA^~-Hi3}{u)Izm-xSL=MttnU!`&WwmL(fu`Rqe1+OfK^64IY(%{UpfeWSWM+knmNJ zgK_VD+@Z@&(d1YR3CZnyhe}^afT+}ZSVgxsgQq7c{S0)p@cVRb`H0>?P#btV*NNF> zbVQaa>fjIH*X7JAy}VcP#`J%+@qZ6Q1@55OUc3SY6Vqv@StkFM1rX&$mfWq zr)%lKzJ!XYr-v{oa{?}^QQ6|epj&_}=3SHehwOOZyG&%wmW)Ushu$Qu7aGzhM2Qy; z+JyO%m9-o96l~mmn^H>Sw~a33%2CgCf$G|J=sq`D{?BnA7i0J08ATApdX(ek0D}^| z!|I2hiS#(6TZ3O!QoDxmU6CE;FU`9Sh8Gj(wh5lv&X`?KB9&@63O4*)jfj*y0?FcT zS;X)FE2uv|gl`)Ierp_J_eDvfpk`=JoN!EZ(e}(B=^jJ{8 z>_^S&Ol_Hz+FMWHQd}|Kcz0J0a!UFFg~ z5hd~C#r>Mj7J+DkeG7?y4w-e0*QX=YV=1$w;)7}BSu#n-K=q6EWrq?9q2U$*7pDr;#lB%}MM z@3Q&>Di3W_ZkbQOxw}-XtH_CitS&11oL=~m$-i~G#W9>NVd~lSXQNdc zxjZq8xu$>>(+hoG)eU4=%^;kyj_ohFH8jVNsd~^)m zF(-^KPPK@k!&F13QmJsqgIyvJ1HT-aP;QsYtO<{2PnQFEO(YH2 zx3G+olwl-k5Jfx2NoIpyr}Id@5~z2l>{g5>#6mlM=HX`5@8qUh`ogy)1l`JAJEFYV zYmp~FU;rKU#&~GH5>MS#iNGPHh<-4>Y-buibr&79#=@Wu4mMpbQMS_YHUY~NPWXKz z2FvLp3d(((%%DUo2FdHt1RJuA`c=}XXm?0g41}3y}3UrwC(YW3(nW+aE1BEq>A#|B>uFr-f->7m?I%= zyrI-s+{oU^b1JbL7Goxd^9f;lJ(T)WJSoJ3qG=BW2K7tHu zd%+0dKIo%oXgFm-n!KyO6cLc*J}rf;G-Nkvu4{`1SqD%~c#q;v z+~EdW>d4;O&H1OZ9`8L;-Ar}L)cfrv_zj0T6hbg z`(TjZuHsadgE8{0lmOa?aRb-R`GXXTi=bI6u8=A|9o)DKK z@t18zHQ1@`3g=#`Q$(mPZCw3P1jGhS`fxdLo!1^Od#}0>IZJO&0L#=kBQYsr_|sX! zD(^!)F7ad?M{_70x zwdE+4~ExQ%qe!DoNI#40= zfAE^bAI-lt7m^94{~k<+IBTfiPf@^T-7+o?Cys%1oJC9SwypnCo@$5ZvjX3kN8H}L@1cpY{sLVEV&pVnfiqCfw zo-qBzJKdZ4!C>UCE#Yx~bBL{a64MxxA7gw?3F@A4^RyYV0A;w6h_CrH&_kt5-_Y?V z!CL*pE{V9=)k&TpcBzi@DVmAwoyTf=1jZm(DGoJ#t%12jCjGssDcp9Z|8sH3olsJC5#n)+ui27NrT}Cfo zOZM_230!4U&a+V)m)`)v`{}T({PM4V9yn^>sZ3<+t3r)*oRXIZl$6=3NK*UFbN1Xh z4w7T1-M%w0s~^FE-mcCu^2`FGo*E>0Z$HfpfZ7#TmMb`J+<-AT&*H_{q)2qWQ54rD zIK>UVFIIYeZ(bGcL@he+^}LCU8NVPw|M?y%NpB^Owz@qg;?+MVda!tvM3VP>=@jGyZzYP4 z>AM*j2}3aI=xG_4vWQoIazAnqLMPc&0-7(nLw~FBPr*>JDv-Hlqy0 zIA@pW^+~dh>P#&QU#eCzWheY^H?Qnx94&hy`Ha9p_}p8j4U2YBKjG)PSzaimEBFhv zN4`}k*ua?0?yRWY9wz`8{Dw4Ynm?h^KEwqV;)?;d21;h^p>*=Y-S*$SN5-wTx?d`q zH3N%&cqYs96_RcrH1<#+%1WwyW^tC;daszDA%+QnMxtOaBswM%E$x=h(4~QE$Iwl; z>9uCIZp(uzhH0-5GG+2@1(@A0?7)2TT)-iFI=tykEl5}}eZi}xw&Yyd{&n9aiBk~d(DTGAyAcKMD4yIYHO~gJvkh^c0ZdMv zhRmP!!jSPnA`>B&G-Q=ci?8 z!$%I3P^<(zwva8;!zI-z9Z5%DtnS-L2Z@QcI7VVg2&7r}i4M+y^B}0RH;oH1nocz- zPB-fA{kh?4VR5s&t9VQdb)hy+`Vuv&;hH%y`}k0`J1ntGS|+!Va=JiBz>~AG)crZcDm)r@JM!tA zMUqyc-9<7TDHZa|{P1Nt%rZA!&O_s%or=|!5k+V0hK%FMueQ9yj*hAvvs)%x=Lsz_ z6dFYyedq-n&Re9|juqHG9MKCcdU$!nQQxG>YDk-@y#C}l1?VkcTvo09Aw0LSh)&ZW zJ^`0<$_*fRO!xB3kW(d}maN%_1mgjF`{)RHRhb$`(9akmqERP8N<+JLx+WtfT;;nF zxvt>eELjx$Pom|nH+=QVO2ZXAQ*GfQU~mGfuV|{+JUoA3&dVjsGYFEoy+g4a&GG7P zA?bkozZZDwm#GEXV40EQ9@dbuk`>r1aB|4;&LZ;12tk4;uPVeBiDYwi1i{6eC+Ce7 z$s+Z`rwZPt-?*1vwIrG^FyfG511xP9TPUV7!Nu9M|<5A`b~ zWW~HaN~`mJ=ySx6(U4zuBhy1X8ffAR^!4-(8(MF>qI!grS%@025C{)}$@W$`ru%AX zw#0ja4|$@aUWstwW4;ygWD+(3lD0TkNKDp*i6D_|!d!7d%>Yjz1#xQ7_aN&DOi)4G zLSmXXC|b`v`kQd=!!3?x$u2Si@+#*xO3Rf)4YQn^jCXcM_Gjo0xy=U6veCMVIMv&2 zl^5U=?#1#;q%56=uK3c^UEjKr_aR(#?+Lq;617rw;2WbH`giBgNtpE|Id`)6_$w1B z=fj4N3Y=v6HX>;F1=zv@+eSF?ex*=u2`1=7X*ODE;EXJB*$@UoD^)g*?z!CHv}!rG zn7t-?r5q3pT{&~=)LpcG%M$6DriSKkWUMBP?0iY|(pS`izuaLWy*0r_!p1!2@_1x6 zxIR@@Fjl_1zo)35@I=VTNc0BqoJZnMmu1~IJ_ck9G(3*p!qLryC|ohE14 ztP49_Vxdpdu&EV1%yrMAe>0XD$&k{6`SeCwwZl77Wr5@*=Nj7()S4FY^=TO`d!uLg zfoh#SgdYZ;!TIJ|q=ttu_)Aq5^G0@BVHOE1GhTlke5i>6LYLo{2&wUKcFM{=wVr~mm4Pq7E*URmKrhB+ zG?729SV=WCW2t%3e!{vAGUs0zhfpmU7uSO~ofxsBc2ttCtR)~h;W8?)d0@8XzYrdS z2WZKKteyS&v6Xb{UQ(na6l&+?5LjQHtim_IVu;5}b-;#e4jO0l zHDDoo^fB2sAkI zL3Sb-a?52ye>%{dBI%V)I|otKlhc#X?)!^J|G@*iQz^|^mhdsXb?QOknuQDF9HK1r z0~zaFea(Gd1k4&krg5($mrfzsnW;o&CrwaZjH=560#K%(GF(IsfFLm74)nK$?v>y! ztz0GAV>XA&l5KK19h1l12pC>pjTJ^Gm$4jhI({k9&^<-o1H1zElvutsIt1s~GSwn7 z9;{2i(6#VqJHGQ#oQSgzbTJ3;CvsFv%`Nyr#5z#$e&rkd z=8_7&wo!J5=bu4=7rd1ZrU&oTAdhdjZ@5}}I=U9#{!6Qs0~ZwP^}b0md4*G;rH9?{ z2Q&x@dRsY2*!_&Tmx`>@%Wn7>1euGX04Rjoeu~!e$o75B0L$~OxHetTPsWnzFrthu z-UjM-^I7Co8>6!y`M=ZAOVOZ>>tV%MuPeb73e5>Kl^WQsWHDW_Em7V%8hIl~Ny2hUbH76{DTnRR?x+tEc@+d3Ft(#B z4rY9Yf+yDCR=UB<_V4Q#M4yGrkZr36{rQ5ZfK;h6K+c3A2_hJ(NDqaZJI}fx1D%4g zAXT0RF&Y##+v*CKVLl#g4tuU|GGiCDfPz86cnj)bR#~7bpi}53w5F4GH|ga8{cZFp zqM&JCv7^6EDuwkXRs(OXz2`B(@^ByJePHuvQH4!vhwqwvx{D}Mlz<{((QcKAsxAck z-5SB4%GE6$cIvy7aAk4uLlpoOet#=?%E2MA0=^Avz<*MDYf8UU;T2Km(gxR_>_s{+ zK_q`J<;8x@R6+D+xdlCzBry38dD&`D)U^P0;k9*+>%GnHPK9P>Sxzr`O~yxa`JMdD zY3;Yf`|-@W6=JW+^j^@z+#!b|m^caXi5^Ve9=v~i-Z3QiyO8`%g(_!hTF=PeB8V+3 zBxMhBEYYZYk+MdJR(lhMQ-J=CAvx^Xd9~PIZJxi)j>h^S20rl7TDpMpx78;k$2$Tv zf;l+BPG|rkh@z+hc4qmz7MhVyayu?oSd5MkYUdOsDIRqruC%}0v1#}3u5HP!x*TQl z?{p&T%8D^jVOQ@fwEe9=6)NzFx{@m7qKu`GZ zGn{l{L#JuU9UUq~PkrZ;$TuwGavBnH2AmP46MU;r!k{z}c7hgCA8kE69jXvIArS&z z>3_k!Kx(FstxTnsk0CW-X(l$Hf`rkKBd-X|qleL1GSk%-l+LJZi4sV#R`Aj?CFYW6 zJAGc4x<%oVRp|~B8L!*I>h|qN%9x+y;^<_n7_uh0Uv(zMAu6!9x9MH~hVe>9Y;tsSQSX4hj>B z8i1g?Hfw5;DyJ3LzmKJKB^fmlaHFZw6*9KXXPPr07OxdT+8hXJRso#}QV%mFhZZeS ztuBg#wWI4Kd;NyzE6UxJ1t|)3%)5KGqdht2w<(S#cVvrfR2O!l|BLPgXU?u<(NwwE z_x%XtXxB0QC84M{nMSCrFz@p(A8+}E@>!$FO?j@JFR3P*A+*EF-}2iGmk3|u)7 zSrWDkUT(5^nL2h2AquKIzjKUG7?Cw;12pYw&-rL(mP)A@B_sw=K=eT!wlVhDrM+6A#a{1ZsU1KVg>q7nz=CkM^dGT`zXS4$66uDc=zbr|^;FM0D?zWcp5lv)CPA ziOx+87P>RuwyzhaVNy(DEABvLsLXph3u&X=NqcT_LnBL`8;Kf}AO=0bmuJYol< z&n>*#pU_#h=U#pS>5Xiovvb*e#hxYgM>s-XV^rRGM`iRVuLt~GK1oppT!Q}MF5wJA z%{7)D-O5B8;F5u3c<}bVyV9|@y*LfxGdKB+*cjug-nm2!S+lO>*;scCgL&T2TtVco zEWq!5S8oxKCAkuYNjVJk??S8;mNP~m!)j7{3S#$T&Ki*xO5+uHTFqd6xK?lnf&DDCB}v_?2TiYscETRIsq^8~CK8$ISSp-`K|XNy+~4W*n&At<(tIl|}6 zQfcen3G9)^^0@`(=(})!?Dc{?8Quy3*EQAqnfjKjk^%2O=j4zeO9Sw^?jyHsWGGLg$($5_Ig2qK{M46DS6%hC!?LXcN0sK+Gv$~3<&Ji{_DS3KDJ3ot=V$fz z!c)v%ODJr!UsURr%KvF_G=HeC@;nmSi%wbhX2pK=x@jzmr%lVkd#fC{YgQ%?mQFX- z8^Py#2IZlTSk3X$`tHU&CO6dFeWppsuoly7fTZG6g2ZyU5{$?)`6JP} zeyB~mLqhbI6OB8xm2dwKBfTB1vtG=lx~ZANKGe5|PdYw%Bj`@++_6HeMsS!FtNF*e zUO|=DX^4se2eMdcj6ND#wM4sy27kUH8fy&p8*%9^_6&f~gwxAJ19~CESXKGWpi5sk zxXTB^%A59Wj^$_dm_6~3EG5yhmDcR?3Wje9*aILB?^w&N&%P;OV zVGOeUM9hJ8wEZRg71V&CPo{Em?Kl zS-`0y(o0*@?S4C-lfoxT`QDoVE3}&D*xrJ2Qo=gIPklId5CsY-fCjcrxpQaYK&P!l zjo;3GeeFmFQ?>EN&{FH}Mv^QzKyuEa*TSObbJL2qMIy44a&mxeuH{PEOMe-O8P6ev zA_4hchTF`|H9JL9K{)v~c+J0XN1nwQ#AmKN0GPD1;L>tG=3~ zxt6)_cQkDtkmsd5;oL-2Vb8aX1n;~^Av}WB%jruR4qed%E9b`b!cIXwF4WTalWZ=k zuZ&mIfQ2quk;J5Qn7epl{Fl^zE;42TQmf0B6-H?SQ)Of`vc|&eIfNY~^jElaX8L)( z!&^O`i8_|vTkJ(ZpUt*>)(Cy4l0Gnil41E7Jl`_oy!3M@Z@9vf!-H8m!vnODnyVQVA z>f2yukA&Mc#-7&5-1UsuUy_7E$Pwu8Y)E?9JWp2=b3C!_2OGgr9@5KXk-w`U%5z$3 zMBP?|eFcbv`m8?~=_)U);xs@gB+hGP(*}NYd%D_4m|+yQ#5;*;pUNu*2cIXvTfyc< z?dq}uk?E7RCjFT>)H=tqna8~T*yHo@ui7Nbe{$2I6L)vI0V(u%AUJEUiEPh8V>>IbY(>lq*Ll&!+Z5$I zARzI#{4GB?&Urhg^+!dm5FBETtCp%OLJ+zk6|Ftmp2ZBw0!7LLtdv!Yd(S1PP5OdA z!9OUtCKKhsDRT5D$*mRDZL*WV>dhfS^GXl<9m|e`EjPIWAI>%gEi_jC<*8bcp;Fi1 z+PXNR@^QZU01@$v*P$flSJTm?KYivrjgyCJJL~}sosqHhAICP%0`Wv|olzZQEd#c> zTn$phw|NA;B8{whFE7f9wmB(3KwCJXFj{tWyY1|tzJ$YZq6plOEBo0@@$@jdHvV?Fl5bQ@s8B2cZj+nEIY?YvSYH-=5kfRG<9qp z7d{+Wot}|4zxU=Ux7s))6eO_SV^6TG({%r3 zia$_U!k0pci+Ih_jDj|tD3(9oe-=fZn&;HmW7LErReYfOd@v1;{M8&nFUHG8_Dk%N z^SLsvg~2md|CEy>)8PN8c8(a%Z|2Ur{lZKOw)(BMcSO^m&#8Pms68eB zYzuK%Snz3oN&J^Yk|%<0*nQ< z-zvRXNh4RDSJI>NgH!tL5X#T$D&JVZp=UDE%b0+Byhwha1|0nPudKp$)ui4HmPbU~ zimbe+>;w#Hsy2-`G6eVNPKgpLupw!@m7x(F3qacrYtgYwNyj4W0H5a7D zU=7s=X(e|AtiZI?%4g~{EP}mhI&@64W(`{f_+a0fIM@8bG(_6EDJLz1=A}XOt9aA( zaj%_gVdH#ao`{h>#gw3?iyrqa+iLjpj>Wa0C)6F8bd2Y}YFa65viP`0=UvK)H1edP zx(~&j=c|9QOv>}LI#{ODPgxzv0TQ+PvQl*5&1e7V3F|#ST9)|(e_x@#&PWmVu<&t7 z{xYFbfTopq$Ef3bj z?d1C7T?$7s1?X1rnTVYGnm7w@25J`WezZoMm>o_U}3kA)ttSEGs*tWKA2@cK^J(GNo^@bE|q;D~Hx0&^36lq*fcFVQTNbXh{Dspo@nLGBmGo!@JVEO7YL z#IkP_9ET!hu$d%N(GT>9`lC{@Zc#4U&9d(u#JG>}Y4b2IBRVU)gD6B3qdnHC27XC^}N*mFh-k!nq}m_6DjU@-eo zC*1H)VY|d2Tvyy2QRh|+cnHEe3P*ahaAD{D*Sp__sRX!Dwsx%*treDsumwHFPa_Aj z=f0N_JKwHv?j7pf>g9nqP-!%Ko0?wN9|{5*y8Q|}c$>~%3bmpk_WRsTr>;8td*$Er zJn8QTpopN+LpS4t(j(K`&DAO zUi>yH19S#8&Djp)q=fkh_RhKzJJFti;@@zH+9W~7v{26 z@1j3Hvd>HiD3Z|--%WY(ofCBl39aK3v-mM*6$l&Ii%XdNh5&T^;}ifzO6jC+86>G8 zsP7Zt_&fKN6MJBub<)$RP>ei=u7bzEwP+2Sfl4k{JRaS}ii05A{u&nAhL5cz{Lg?9T{EG+Dw0p_))@^rixFJC% zRYn0W&&oA5IpUC#dbTRp|Mz#)-qO9|w)dgpu;qug_k_0HCU2-IIi!p81cMsnJxhwBt&6c6IvN zQkrmN5@B?gutL_S7%{iU_m=y4!cFn6VzR>0>$_21CY_Hr*R}X|W-}`+dqW-$>bmPl z=*F$r4DHydPA7jq)J60#9N{T>Vh8h&`}_m;?t{S22EWbV|NGWJs7=)-o}Vtwu<6q* zNR?lb?b9cINVQ>l6k-?Z7;)m+to7x`D6x_n0T*XCzppjb+C}~;ZKI@7s2L6$P&j{2 zCleqV!NiXcL{i-x`o@dzRo(|VZ}iREiE=AZIdB%+hp{K@Ot1Ra>$)3Op!9Xa$paB< zFh#xLlqee)ju#>@lD=(g9+6JZTk|vY#`xS#!J)W-HhuBOxAUz7a;as`=sa4Za2#6O z+dQ0(&jy>MbTuW=kLy5*9`y|kwPJYwF@XA3L22Qrs1zA5WG#|fiPeojsV;RA+slqxU7K@j6nPh^D*N||Qod2xY$Gxa0}ouADp68M z0a@grS-UNd4l@Q(CTRgmwJOU-&gHE)DRauj@}4mt1O)x+F2c3Gl&}lib)<**ba_uK zs?^HyhDAPzcr0Us@|QI#s6s|FYvHy!j>(N*qOa2{`<#U_{?ab9ar1DMqqSU911d`X zl{L%0%aBDJ9zNoZFH=b*_+&74o*#XS{FKj#3}Q!LzK$t;{dvAvS{lqk1Xn@*BU6jT15jUJ$vQv-I#d)Qi*V@w$Q1S7DiMZHX#K_I4 z4^n$IqD+}KBl+Sa!(X2&5q!bHTyn3C+p7EstXEsWmeJ)cq+WmMrLxJlDMxda;m19<7V$hyr; zdCK#BA{EG4^W~m#?`Y5gsaXBwJ;>M^g+sByQ5m}EM2lw4R|?Y`qP$|KEseprgCWfV z89@rtYRjJm=PX5r{=jjVCmO1;B(n^(t#Z2(@vt9~XEjR}gTr>k0V~C}aJowGLdoxx z-s2?zOSs=x;>A0V*?n04;7Af@+k}9Lt~V{A|3~*S=ZL+8k>6i?M49suDso)LG|=+7x#9&BDw7b5aGHV`9+^n+W>=?40uzHk>hZqC)Fe zt_?c)iWu8gmMPL}eo7=AXi3{Q1B3rEVms`PI`4a=PYJqDe$Fe8g|yLQzr`FMeVa`z z_DBqQ-hM+Nfs?njYD|Tvz!XG!vs5}w0>)> z4>JB_26yZypNReCQ}E56lILtV}S9A6vx>a{*52x#SYdy>fo-U@|e4Pn3> z(qm|Nik}p3-I}`DA8{$)wU(zBdg}3v)G}$ukp9-tb<~4AeD?@192E?I5$wKEI#@sA zb{X?qj>uOU01HSYNL@C1x=_XG;1If1K0bijtsPi@&>FdP#dwz>tLr4Kx{tXm@aAha zz9lx;TSTidMYiR&3AN`SB5a24KBV{nPZ!saUq`rx$nGi6iNn~2o8{^SOeZwxKt@%T zt=t*{q)hMv>qn`y=iPNj5pmaYem${6a%DBcKw|lmxqTqT)_~FE-uO}ZYB5UGeDQYn z$g9N-(J2gQIrY-hhd`ROchL)cSJn~TK%7oZ`^!QV_7G2!sMLfI%=?! zC_KTkcsNoUgmF<38nk9WC^Ws(?wso>LHr0&I)^fW$}%h=-vwCI_AAni^Yc#z3cUVf zb4i(z)|J<0--qZ|+871T2UGY~k{l=(Pv({anipqTOUw2(RinD5D(^D9)aMk#C1u^p z`%AoV9hvZ=JaKO=5WA8T{cOQHrIpmiO&B@~I!nEvK-J7r6=?zvd$gJ5-;DpQ02nhnlGy+()-_*$jU>V0s7NBgoc4QjaR zqdym5H$AW4#skf7(VmOmc%ePCvoG6(*0FFYB2~z!I52Vyldo>jiP0mQwZD}MZBX?Dn}pkl^w#9C zKvPp6kVvl#O)(p+G=a38>MFT)$DqL5&gc@0bBC<6S@Z3L_CS|w6OP?zVJCr&y^>^L z&$4@YT*T=W3z0M((ZxO=?%qU`cm%jxLBmwvv9;+AFKEmbB;VSac>=v{^{D(>_@c?a zuD`ZVehe|CA-8Arh-xZ5KFhtqs$bZ>EV0+OvEn_wh{!~J^S>sDf<27<)1d>K9m1A^ zayzUV`{4yF;G4_Hh>KY8y`rLNVmtI<;#LDZ7ix!F*7olDn{eee%p#$VLd`v1C0rmD zu}E({xS1Y0pb&aHN;YS@ownDG5ALu)wHaT(Z^i-BSyGs33FPOD_63nEkd%Xpcj4|9=zNw|q30tedzF}ismmE@4{<`e4P0Hi zB|R-C`7j)2a>jDr72Ef+JRNZ_2k`vRG-Hi}8SqV&USXHO=*YPWlpmZ>3=XO&&^+$% z2j0?PEsqGPDb_!?H}hr#WtVZ66%Tn(^Mx1T)`@c_2BH@+?#D87{7{Ak<7C@08aQYV zx;0=Yh>K|WdE`hD!~2*}?*z2o_LTXIQxS`_M(A$}8x)S9iFv$ttABVRwot?-^auVz zMsiY4u?oO`Gl>TDjqU4>%YWll@Zce#mY6d?J?Xg2)Kzo!TVl&H+MNS69iOV)E0Lu% z(gua)o{t$)K{<{~sSa@ZC;66M4H%*J3Wp>mHG)J6=%*&ZTTVtdSv1PZg&x{1o~$=nO8_a;phlJU@PVc-W*}1*3!Fh&$=to zs12owOs68@|-cb**CS_R*xCO1m|B%p1*Tgj4I@StsGC%*U*S4k%EVj>i^})=PY}OaL~E%{EXXA8V!^g$7l{@nLvADXcXQ{B*CaUSjMov zY;eQi8IhU4(Uy$>qs6HpzT@6@WCiS4`L7=?KAq6*~aBBIzhi~`_ zUGLXwLitDSLQp6Y-yQ>ekWWT$I@`zpk$T*od0=-NN0%y^w)_vuJm(tiDCw}Y={eCz)nX?{wQM1J))}Su@-?IJP zLya0GmfsUDrb(GH_ap<`_beb|Y99!5- z&KdGn@J(ab2}kfdw|;BJvvtv_M#RI9^LwZRwVYz2Oh%p&u$HYT-xYu8iPCn^BFwr+ z{|nf+O&tez#J`2FL?ZFOs$}E;ViXf7?_!O^b-D9~YD)-(g4OwBH{05@g$8e@U)jcB z?+g&^;{U@yjZ|SimH`B49P%bIkC;j`6?nn&3WO1niuQ^Q;|^BEzl9eoiF!AntFR&L z+M1Y1s0}QA$BB=%K?Av>Lla{3196e}eM+j{>1`u}ib!7iS>4&Nu7{5|kzgSF^$RZH zL6QjYumHMjvZ?S6@TKCYBvY{3^6^HP%4fOQ;~W}#0k~aX@oMa z=nJYl75Fr)*o4qw4qyw_RHs`~hPWx69__g|`Z|vDJ=0WK#@xpnBu-gnF>3wOfjDwD`BWK21No%h*nxJ-LYZ}Xxd15j4h`8|+fnY~5l65?Q z6!qLA_cyh+Dk%U5TlUCw_a4zQg*ew{=;uz+9Jb5`W$j77>O`(<+JA;M)P^nB8|P)R zt!tQe61KF}kt!tHw?qTbHs6KVPrg!Jb+YC~ORAdrvoX%c?(svnzY9jh)wsYQ9h`t4 zw29LAYz;R8#m_5+YNw=CDGIC}0!H3`Nu!}3kX%8=g&P#>!A#$>zrX|7W3D41o{GEh zI(z${=Q?vT&LGCVK0FD5+`!w`+Vmy(o4Fp9irAI59Hi#K`hWX#B45_+GLOoH4Va2? z5%>d1^LS=W60ja@wv~0{Rx9TnXwZ!^xJjk7W(;V|)b!0N^z4C@J>z=U-94s_IkU4R zOmQK=-I?hGRMhN-gfFKe=COJSX;e{pgTF1=yK4$`2|vEXDvM+C27cj4PDXIZoB)w* z`2S$I$Yc-5zKkXJno5hGZJ{@Mcg5JJC@McOC~{l`e#m-iBju2T@q0Z5IVErMubwj* zoB?4;kekXTJ}N+)#ar6hwo%W5_u>c|-0aIUuHUxU8TTFz8N3&fwslA%aw>OI{UsBR z#Xa!YneR5LAM2g*zBvc29EkaQm*=Qq7a@>VP;HQQ#gHTp46KM`ukpn!yAJgXcVk&b z6FETy<|Vb(MqkRU79F?GCoD9iU6xhd*t?~WVq8~kfs0%^ZU}} zE@$CC5MozniQ^c@cYWWw)@x?n^Htg#A5Aev*SRJG8AA%<5FW8WhGyAh=Yi;52`s1u z076ZKSIO+&1MYY}fM=LGK;_>6l74E!lzRC7|53d`-GCf84#G68kCaM1U4ONe}EAtVsTxa{#y zP?DDJl7CpJXE-=Ap02^|soezU3o;_=D*>C<#emvu2@L`@VGmpwTp2f#7+ z8TbBbKN^hCNj?1&s^2%E764H|uD>~!4C~%=%u*esqtmiTo;+{9)sL0h ztDA5m(+cQyh19W}0lk+bcD^2hUJKmH--pMh+CShHbYg>eQll94IbYvgD>l2=mAJ-S z0V|>@;hzukOuYN;1lwG!^PF6S{SQK#JapCnYmE{1>37e=(k-5?^G7~a@K3QQ`{!6s z=z*myw%5%-N%x=2ogou)_g`iK^v^u)GS&u_q#{>(4-TC3;kYXBZYInfXN%wBak8r} zdu>rtor0BcljrFfI|#Pz5nl^!SY)frqBS$bPIPLOa!``BkD8j{PUzv^1M zrOm{m1#NdtuwDkr4=YdRN$@MXwOipFSJsx)wcgb4TNR+m#PaKHY8?Q6Wq$hhhdyD{ zLVRKmc6#mBD^Kf^Mwb;mm7bQpP$gD1kFkQzIcYSs=od>Pn}V(rV`K}8NsIL?g9(U% z*$;P7*f_We8F-2q7}$_44&) z=H1i0B=+i#DDV?c2VHq$q-x1Iy^YMo^B=eD2p4U4kJ5krICU1fQpJ_Q9lllY^C&8F z7}y*VoJa}i$>yaMYvZLiy`r&VpaX*m&_8-zLgDx+rL8jj{=~BfaP)1 z?xFJ6(<4GaxiWILSwG1ik9WB4B|$OGWoq7IUK)VoLUlU`f5;>CwsX!dhO9k60MUy2Y2s@p3~A=E#ND^ zcrg`^R-!}aV9eIoycJH!Za&2`kA-8NYu^NBL9$T7jg2f%>pu3inV^#8&+q4-jY?pD z$2#3#`THutlgyTZJ}3ng{=5R?CGL#4DQ6=G?63nhkP!yP&;@)U^*wca`1_u3y3jxP z`Am-)y~bLD%{~)}SfvtUDh=fdnEmO!HPq^xpS1X=Wr{i|b78p!5dHhDlZO6GE19I= zG%oQXP`~BS9xIj4JxMFeuI)AONaJCuPRZZ5G10X?lpS#B!on5TH&$Ze$?c@=2*+BX zKtYQsB7Wmkwm|dmoZ>_KVPy`)kNiTt?oOKMe_Oz-wj{obe~NPg0;llQc!o)9zIr%L zCmdHc@)8Zm{jYX;MD{`WlbQtuOFxnmMJ7TkoPg*;`{4$4b~UUCq;R$fbBX-F43>TltLQB-0lKc z*g*2qrY2EEQ8mtNLcfei4*-;7So57vmcdIUwU?u83)8`b1~aM8uYxW7UD-KNZ` z-UT<$&gs^{)OuvU!ad1-@|;oJwF((`UlbDT-M>yK+h(P*a$3u+vyYcS@8o5?MT+tA z&(E|$Q=o!K%ED4~*+@TJCL2ot!aKAD43Yts`4HgOpM> zWA_4B%5{tQv>_oQ{02;oTdR9ZBGXNi;=kBvShWVwpD>2tYwvy^#G-)g@ty6l^f+8L(X{yq@?-J1)D$qY}KE4u#!2^QCsZ6su9c}yT_!!7p zBX_1qHF%$TP;zJ*9T%hnLlTbxM@NLqK5VTIaW}y((GYoXyolA0yEiL-L?VYrhl&Wq zlVtqCXI<8hv!p1*C#4tn9$l;tnTN(HE)6LwG%n6c-I@>z|Aly@8l0CWv=@=VsmKFB zcxp+A!F3{XI?Oxof12LsrKuL*YUl1xkTC=v^#$pDSDzu4_hRiEGR~d@-4-Z)FU*@6 zU+@iU4eR?e5yW2vuP;$^@s zKUtaKeFDLfZ!>J#^Y|>kzRn#9WzU0V=2e8T+fF>F)d%$A26?U=G2c}{@h+f#2_=gS z#zuGP^ig98Yu2DkM24a!*l?0s{rmfA4|sRKi+efQv&x+#(l5STX*-AOF@~g4Q8e`< zu^kK{Ef?x>!9f5c5Z&X?2O?V)J z2J~>nhB3%&EH(9L!f8&zxseCQtC6$H-)m>s&C{@r9SiSzjQ?5m(NyO$}H&q z+1-9h9RjAISeoX_`4UXtk!q?Vy1jvEQKbG7om3-=HrGXCM)s7ZEb^I8P+sXT<4(jN zG{X=TTSpZ8GP+Z(rjJ5QAU;Gk(4L^#K(Wvdzt-{^T-|tEwg#K-=c5UK$-2knGLA$u zIam#&#`Ur%4ZW*od_i7gArwHz9Qj#>HJzUN=J}Oxdj1_`txM&uVc%Kt@?3~wh9#iLt% z1gYnESDp^t94Qy1!qYPOygdq6A2bg6#*oXakFJ2{&E|AvG&-@?#mQt{Z=R24z&W>Y zYF8Qy#E^LI`d-x-v>uzoa7K=FTR9$XyvevC*hBh)onTVnSL%7jJ*B~@Db0ec0yf4X zW9`0Ev9Ia>sIw{Y)ci1|4BF8!k`UvWHM+=r-Y9U9#1Qa9{K>ebYr9X$`;>rxF7k9S zRI;17%?M(f_jr=`CvMPnuF1;b#sADhS6VYA*Jl+@_5%lsWd;jTnP=Ug-L175!l?o~ z7_8PD$qP;0l{BL}Vt1--!lI1o@2$aP1=pCo-#5AS6Pmt?Di1!oYW2R>Kyiqw7mD^SAQWAb*|F;*O~M3ibvE=sL4D&+yj1S z`(fNER3mH7y4qWR$NNIUS0TY4y727wlx47LMS0SB!rR0NwsVA-GlI3{_1vTi_|a5@ z!ZMf(g_x|+hfOztj5)-3WF3U_^tr3y3?F0tJH^$Z#Au8u_gL_`zo=_<)63fT^k!I9 zg?GB-jG$V-Z|E1Ij(3+$Yg7fI5-l(w7SOp+PM>KLx&)pII!O(h<8k7+eReT(D**}2 zjiVxdRM$*rHq&1Uuw}3O3&D_V{I1Oxl(UUCBY@b@d^;0s6k*}_b#lm-du9TrUOYx3 zFG7O2pSmnl^q(*BG>@chnYv}8krY@0aLfarZ3ViNIK6WGgd+~uBxW{)SbwoDvj|N= z(a$Yc90`=nl4uLaYt`C1;%}Kx^bf>U(yyWAE(rt&i2G8k(&dD$a4r+j=F|2by6HKu zPJ!7jDD7m{n*gJZGp2-o zKFj$AY#c^cDx9aH+Dmn&Ss=kb(zkI26W)uL92wzf7x_NGnO{Q-#SX{+n;xPPposSbdc)?xMR7ZaN8+-MCZNCv?lscXkyC=*=$)TQJDQ zZS@BoOI6eOx}sEfx%u~`v}%KF6<14c@^u-Q>1nxw$h6wpWD3S}3OYg9>kZwrb77f6 zAHV1SeRs@jMS%a5Db8)ASQj*oaLYst{P@UKfSD%=*VOtuQh|?8W(rAx8(JRcH=f_a z>niwKl)-aWK(hAiha#Z5U&o)3fg)aT_O{Ko-lFJqu|gVV(v_8R9O#+0HQtGQP@rU> zWmx-+Dpe68Do5&GR14M?&-t6glkGxc(&KOP03e|uFxmz^4Ds`LZR=RmN=299ynm^E zYiIbltUs4b5+oxrka6KoJ?OHsKn;)Q7N)uDUKf_5f97OkWWSsVt^k;MHN73$7T?IP zcs?=jSd9oo14fJu*U9ekOuh0Db0V4YYr(R2Ej1U^(ZymXa}^A8uue|hNU#y!y*^B0 zC{0R$*uXcSwvDZZ6z&I498?NQ=ShZtRGxlPh;NOnH7qKRzovq~F_S%ahskV(+>Xu0 z+?Cw;Y@?UySNZz8V0k=gD8mcgPZ;<(l$SL$OqxRfOHSAxjd&+) zA|69FI0x_gGTK}!)k{}0XHbeXhjC57mT#;QFv1h9ww+ch=Qk~*#Hq8uYaEUikcSGPU2M%u~0BxE|ZV%2uZ&t-`HI#cAN3pQYij&kL)y4o#N9 zw5{3YU^RvCo&*y0%^iV5@D%Lk1LWR&oyAHt2r}pO&P4iA57$_=)tyIa}V5GK@{KC)F*e^EA2+$3&t>1b@ zi}-#SL>w3YjjfIa+UO-!b2KMdbaxfCF&*bOG>)@zW+6Q0_uLZuu9n!kd_`oR%x*Gy2avLxPX0;3)qfI*uYlfV?%37K)^5 z@-rhSm2&K?0|1heFP}#1X{>j(;V^sM@rGuj^D#vUvVjwLim;wV6Q@k_fKhc>nXk;p zly8vYd2VPuBcyRqM}wpXfk zn&-%-+2G5X>mTj9_y3l}&M^F_k6WBfeCs$`OirlqW?VYmY(-bxGb zyXTlO6JL;~Y6-wBbY4d=9t_8V9hm#2CkIxQXx9^W-Ir91Kt!$PMS?!tEUS;rEiJVI zd#~Zgf~o0gbaiQbv;ungldR2A^YXeujlvC$~5R=~5oG0JI$Ka5Im>iy2@RBsu6iILNGYBGAn zom71_kav_~ zs_d)=y=whEfIWE}*5p;{r9HukgD3vRv~+_Nr#7T~4LG+EElps$fCNoFa~G%ek7Y~j zgCKgoe5Q=G4N&nqMo;;WRU>x`<0*)r|48;@E`LapHBh5R;$iSrFe&bC-dL)H`yAc( zI}GL=(>RqpRfn3RGBfe)0vl{byR+cMPhS60E!3~-)POJcSV_bTY5Ke~8>dtByOexcn* z;nUnK%_RJ1oGT9YLtex`q$|fpxVr#GG-c_zy>2$y%wT9x*IcIa2(%^;`A&pDE;QMI z$}PzS1JtOXA6t3{JBtxoiRq92owOLmZDlf)0T=ynH*_ylpIj0lQ~>4XBi}(nTc-`1 z(qZeTE&unKLdF|_%|YT*kLvMenNXenL>>Gt2^J-B$jNc$q=X7{$_+0glZU1;!grJ{ z(Zb5m7%C33mZPomk*`tyDFlxccBtAkCVbXZ*YW7oE+}=*-o&2s8sB0w9vxlP<0t;# z9*(|3;wwB3BT&WU|BB9W`b2;K-;`bcC}5OMt5JMUI-UgJdPBf16?61oBdh2dWrBN# zbWB+(jEIH)5Wd^p@fCZQRYuN3Y5d-xu+)@aIiO&S9QTmIwE3aIC-}0dWV3LDp7R>L;?)9jet!Hk6sW(QvS+m$m0_-(YnLh1ymbkqYbH8Zy^#ANzBp?=42cnL1FCl}W^ijOf1 zw`Oki2JR>D!2`QM-@HFC^bNEWlbfE0zDmWOr&edCeab_!VgjX6EPdyd=G;aN2GGp+ zfOWEDjd1yhHGr@NX2v=q2Bg zc%$F_f1rbLa6B{mg44`&n!;9-I-?9ZQVYME2@>V0wT5`` zR&jANPbe=KNm98`m$L6v!8PXcb6@?_3;eHcfx(<3atY zxV=VKEB36cN?drt6WFXy;M^45zS{NAb-}Q!Z}N)!1nqSP{ABs?-cxH3M$Oyi9lC~# zIPKp+^T!89A|E?}c@Sl(LmEL83NMn>e9<4rHK8J(hR%ZJ_3lyB%qI6G7N%Jv5KPmEY#`J zQXlj0Cnj2bs+@qdj}3>?C;vXD&!ng28`#bW`_ zKIN*tV8+l42xnP0RbgR2$LF)V`oZ2m){r$s#WTs$@_A)Ze{P;=b<7aqSD~>ItXJYI zn^5|80bd>Dv?_91>T-1ghdMHi0sVMRXYjMgl9hfIGyRgn17lpNWhGvns0;BOd-YZ= z=v)GmUvm6>Ts+!4G_|#EFNP8yErgjK$om=Z^6NL+>ceMB7#taC-6RW+i)FB90z>7& zJ$0g%``%A{9t{3a_9w9QGRqM{6#c<7mi~kW>m_&O@FyAb5^k3JTQP37IGx?$(V{?p zL6BJx$y?jzG`K&SKTkl96W?2Rbe*h|0i}9-P0;%mAeZdI1N|&30IgL?7h&VQ;<2T^ zi&EVolNwOV>*um`-+{l{PPPWby2Gbdf&JtfeY+-@OZ6RNW&qf`(BM^mS^d7&BDLwCOfw zACdBKuE0~a1L=z9*86C)tE-o}aUrO6jfrM9iC_19?92my5Msx+i$8+7xu99YYGf`? zHuvtY@&*Ej!%e9hBKG(j!gFLMit950KhT{w1YSD_%0u&P$Vv5#qM28=!oodFHg}#r zv_w>2yvWkNN)bHm-buAT9+}xp#I`ecSn!Rl{l{Ar;LI|toztFw0gYPG3@{8h`B8%k zbN8L41)J10oxbx$GRwbo_G$U&c0H$U<)g>on(n^)koyO976Am!BJ6L;s|wO*vbN3G zbk8@M8pHqV-vXwwL?wk=gp6>DaAJdAGm%9hOOtoV1uIIoNkc(n>*zDhLHxvrB^BdWqDd^r>)SH4eOS=nBurNJ zIFux|bV-q|i$fR^8I)G1rQ3!gxtd2gb1(BtBMo%LAb9PM;O2q&*X5^@1AvTc5N?6Y@0t`ElPBG3C{njzw^!$Q--X3t`wY;n_>~OUgLyM(Z z!09hH%^+(OAhu;K9(ToxGs=N)tf?X3%Y)-&hVx`rdAsl6QC)YM1%B)1p9MWVqLQJz zEta)AhrgsdrVI2$H}=jT*$@K^#{w2pW@EjHlGxHHZfd~GYHQdf%Y6gp z^|W1{Pe*q0_Pq#uZQ1-!Ir% zmUpewm6=oXz4ky}ws=&a&8g-Vv=T%>kmf+5-?gUDiBo?sTm(&wyX5o12RF&~B1<5+ zt%F)U7au@io+Jm^qok&nAv7?AZ;2|Yx-r#M5S+9K`nLT@X?eSX(unrS+x)|@kVi-P zHUf!(jn?h2N{pA_{9f{jD*8CvjWaiz_O3}66aNo$tK)5>C8J7RvSs#r2J`oEEtklF zq8Gp$34)Y)&wp461X8axH(hX+B*pBc?eIKt#^Cvd2i$`1k&*uiX@2F5Sge3aY?^5!DKxc9RbJ2assOa7ZcMF(3b-E# zbbpI2CD3*-dpnq$&AY}@*c&H~c5k2d8Qk_ln}aW`0AD(L?$t6XIxSJ?esgT#M;;^e zt9LJ{k?&Y^cfnRn$@g+{*S!P@DfJlpPJ##Ua+Wr5sUWNA(b68`4tBM3MlBz>?Hj`tskYq&-PV*#{ zzUXv2LQ|j<)F=mDw#7Xp$Z^IV$<+q5KUtgrBw}Fet|24W&H*^fs){>n;5TKTTMeeZ z^?t^xyw-E&viqc+?_N|P3pS*!vdzm6wPv-VoOhAH?Dc~A zt$(Z3sAB^KMd#miz?vmfcINne1U;RNmAk-_ape4hBa|aE!#3UezVYk!TdgwXJZ200 z^G$5+32r2}=YDuWJG3K-say#?Wfd@U5I{2heH-bA5Rz;nKdxD(5?sZf1rO$eR?$WO z!4Z-lrp$bBV67Sqnaso1N1`CdD7B!8Lw$X2r8-GWfC+EE5g}vY)h0JaDzX!9Pn>MH z<-)c^~H@Q(*SG3d4aLq@oEXR96e?{s>QI@n+E((81JQnrj0F5NW&lR=m z{hip*Z)|1`{~^ABkQOd25#lQDAVv*Z0vRvvvPja)+lJoQ>YL5SOmv8dGP21_c}T8P zZNy1Q<5xs5a(p>@GyBC#3GHb4fMwXeg)(DSD->ZcpDmjvK_e)k-@llz+8zguJK?`j zUpM#Iz~c_Ak;Ilr-iK8%3TrNBQXaThzp{uA@5$SRT*rkb!`g`MHLkRKG*O%JO+9zx zb~s22OzXy|^a2MW1eTKY_hBnTe5P)tA-^2P@a4i!AZE<2umu>aC~1DPL9T-5ET9ea+uYv0!< zDm5Vz57PZ*kYJq~L-2IGI_~6!n`BYmF=i0wrm>esAbu=yoT@xsG0M_omT6!KzfTJH z@EP6X0&pS{V)!6q=N~0)Q`LiI4hK#qPmUNWnOVK*%0@K7~ z`Q;_Xo=>07+>!`D4`T8SdFA0Of&pp#mM-V7Fv0Ho#0=yFHR-hkW^FaT2-RhkLf%wlB*_3>HI)s zKffj=S8ED)W~!;b?sTU~O91U(Q2|ocxE1!bokFfb50P`NLo(%mb*W~LQB6cgYO8Dw z`1C03@%5N^q{~S2K^Tm?@9QIqN^WFYArf>EpEAWHJ$En;Fh2}cm;+#^LXxz3S5kIW2w)x8*DbHK!w%R2D zwS#(@1Vm@q*XM9m)y2p1Bzry1l3_Q*`8M zKnxS0S0U}{E%IW|oQ@0p-EflnWz!^?J;uxV0_xVaJ-dEH+1+ZLo0v+H?gLj4F`PD5CLFJE$Ou!1@bbTc;I0IjkLSR-NH z)qZKE_c;P%9@4aAcC;eWAVOp~7IP>(Ony|TF?VlZwLY_6bkDoClrQViCe^$gEw^Bp z!)!49m6v)4Ph4?u)zCep(At{gG~2nzHqF~aV9abM?Dgw0{mI(udEl`|LQo@{@tBXf z{6|I0zkh)$-2;4_GASG;@*TB5JNVqwC~@Jszx!XRig%j)E(s|}Hfz)+r-w6s3eqRT z?T*KY=IlNpn!i5;)=(;yuNoTdq*}w#T`H27n&kB5PlG^b0(h*WXb?cN#X~rg*5iUz z{Vx%8oy6P(_8V;2G*IQ;Ir-Tfl2i?|%R92Bhh8Wgcm(=HMmRzkUS)h4LV^P#WrW|Q z4vBAYd;!-)@~p&OL;q|lZ8&gKQ|}%1FpLJo~KxKeSs{H%ZxunCyGvG+J<0_fS#;YMb+qTD zR3SZ@Z_7$J z*R~iR+K(xLg_p_TcxnqHa@FymRgL6H5U}6$EMQa*f&ABgRsiU2Y+K6q46hyEs>tuX ztv(M0IcpM`Mgvufhh4RU@q_1J)nIb9~B#=KUa^*7YoNBQf+$m@;CYPq8fa;0}E`1&Nj z6x^9kik%DHmQEQQenNMc)A3IGc6S^)7WWwE?`!S`|4AG}>@!@zX31jM7ggIl51-$G z{;MoUkJw&oEdJPvd14oIOp4hY4PfzOj|2e4S`=_AHMhWF%hwOYz>FnUmwVn04q4~G zRRNGCUGVQ28NscTty;9K#*r=WQ_1Xj~aDW}tBr!;=*&W$F zShWJ4^6DY;=UZdE+71|I%%t7V^2kRO?U*6}uH&kMm-ga89k$HywFi1qu1|!7+&22s(jRgml;k=sDgJnp zq#?>xDw!wCY&1O^w;h!4rzLHb#p?jmP?Ejr%f}22apZ`^F`@Y96)ZAHYW&DKIo^gD z5RGPr)j`@D1Wk9$!d8ZsJx{m5(<9$1cMj?EYZXJ^l6tr6F?jSnW^?i{AlS>ZG}V#S zAbyO;c9BZ(rupT?az~k;FQa5^{-q?EaWFS9-vhzQ@i{z^y2w-cXw>4Kyxa`}WQj!I zT+5s}noKu$hIt%dzbzUeN={Oxc77_v%4_|P?U&4hrTA(fkdG)+zLW&ZUqap6Ed-6I zqU*ynu39VSt_~B zS-~d10$S%CxQFj;)ygT372&zQ-sG zXNOzcmu08db{7r58D8uz9?2eHWP-C-(`H+4zXiNuddXIi{xu*!dFvk*^_dbao(J+= zdT%O=rhk}ttWDs8rz?0r`7t{idH1xDP6V!!JzLQAA^xIZYNn9Sw6z1IyyPllhU`W9 zkb@j*v9nm~yP*GGewUJtB$T|vVjH;8F&K~P0y&diUxMtp(3vjUHld9 ztytD1&Sl&(AZ&PA0HfmI85!LN6!+KtR#TK8u_8M?2uf36dj$4QWvuMNvK4D=ACcBS z{z1d!YO_+62Z1+FKP78(0&AJcwbS87W%W}EKDkC*+uiq8z;H(@mtGWps>iM3HT<5h~@{Ev#)?SzZ2l~)K zjZoFtsq5>XQ4GBE#{--2{JCP*XPIBDA);{_%Q5ff`wjkyh2;<1*ibx&iUE4(D_C<(n6zV-w;_xXr=!C@$bJ*NxWSx;1dGJ$@*Tw}GKD zD%h9pdvcI=s6@VF1iiA{A3t0;Rdsn8)%5p08?XH7w(rqw7HX^D-Ap!U-r9IqZ$N@e z!B@}kPclHC8|~At52?~k^fm~H(-gOwpXzm6zpA!&J&pp>x!-%>QKLCBQqqn7i?KHV_ruLZQPL_hTYov1NSlqOw(Ce zg0U=sF_p5E`UNTpkenV+n$@@s9uyI5Hr*$)L<83(Wys6{%xs7Dp;ddj@MDdH-Jbs5 z|IxX<{Bx~NmnG2DV&48)Y0z0G@28XdR9T<_kE!8Dg`&##!x27<WsF3dw^m$Zp&SL6s4@z~!4Y zMdpep1&!4V&@humzZP0nU!DA6gU*UglM6oKdS~dU0Vk;rF1!)q` z+a0hFd@LGy7z&AfL)NK>4BT!4;VDf?#|u~*!CdO8;tH5oU%>M z)GX$$ae>7=yBra+Y>in|96rCCjH9a@>gki>9`>3-Etde&OIR;vgpG)IkIttGf;?*gj{t zC<#k2&oi$>nW|qo#Jw%hOUwl^4mH44X%1HL?Usjd2_uvP?Kb0YGcv`t(d=>F?^e}9 z$-0rpegQH6_ShM#b`9VjP{qQBE$nNfo|w9Z!{WDm{ZUXpob_D*ik{5Dw(rVqPi+Aw zXZ$POt7vsI3jc0_rK#YO4JT0u-ZMUC?nolTY5{<+pIkA<5zZhtB)dwWGtyRn!J)aD z)V_U=wlv+e%a}4i(V@v^j@Kyx06&O~w&1sZpI4ZUIKFTsHZyAG_wGqI7JsgX*dH3` z^#;?DDtlMIcBr9Dq!fq|%l?7+f&tk(gGAi&S)fcE)3K%GF%X~Soe600o%F^9F8@b$ zXuz?JvR>y4zA6@w?wQ;N&=Bq>Su6c=!&Ib(Osd)&dQU`fqB#17|9b#)GtBV*dI%~y z5#_qzrianI*zRlD{xJf@k;okKE6-Vetz%Rbrd_$6U^{_?_+)f!({B5YfvOTrCGl$q zmPd-==|-<^2z}}AK%C(xntVcW_`v!as)3<+H@Te3vg%>)T#Vd8%_@MrsSXjk34_wv zPf8OPSO7r1C-T|^YoaNCgNx4eT`cEEdh0ARcK20G0hq*gyV?Hi#vxh1A^QB4N0GdS z=W`ZiF-i1p2XYS1Q?#kDv4~l;bWGWU$2r{tAK!NT#J8;EAOPS>nY|7*41wecYkY5D zYOj9XX&>w+UHjPGngrT)e?e?uk}-59iMkm)dz=Lq%aXz>F;i#FSoTXzs);a?4P3|V zIcirdm_fPbJX}Z?8WmP^7U~aBzt;rn2)gE2IR>f=pZA0HfB1C(OAqLCy zxm_G#uE!Vefb7Q(+uZ|()vPucjj)Yt&2}~F>U)o8mIa1!kKF9^@`#3MX`}_s{TRV* znqWq+!8RWyNz_b68nRQ3=~k%;8?Aw zd=xTT`3NviycQ;7n0%Tf{ z2tp74TP!==C!^uO#*y{Q%pgE2{ObO!*~<=#Rp{SKA~+C`POry}C_N7D;EI~wF|<07 zAuN_9E|88g(0AN{Fsma=Z<*I_bQ-MTrj60BL4 z9{@8?e-81K`K3Z}E5ML%$($jJwHNA)np@y#Phs_yw{{nbpWd(cMi{0j8!W1!3xr)~ z02Z;PeceGPxgz6+Ea$;$75XYd!h)L?bzsb9G|&{06#_tv4g!@(EBz>_Wf=f zj$-hs5?(Z!QZ;JP7C@@j$Bsl8xFKmeL zjyWjC8vrhEcRj&DfUBNRH@dj0ZSmZiLv>1 zym1KOc)RY|?h&fb@ee+1l{i2EU1Kpp2AEsnhNgsW((V|3u567y{y=un1mz;=N8CwCEu6kjuqih@@cPn}eeB|iweae*L%l)G?ha7C83?KY>&CWwk~#}0ic@YJI8S`J}+f}51!xZ zV*o@Q71QyDh=B(0IE2uqt-pwo{&9)n;Ug&^`5nB4jWq&P70cAN%DN-SWDfY>XotcG zP3U?xrp`-p3bqJuFG@5%pD!(Nlu`&jDXZg(tiRU6ruA+bVIDyMnlHfsy?<)e-@#I6 z+OO%Y8Bs5==+@*>Z3@?au|sf>A!F)2pw+NQ0S1_%S_cRjk&*mZ`4^Z~Ndg*Mb4Be) z$fH)7^Z9p^jab7f@8Ae+1tS6C}1b0hZ_GpFr|JcMdXrl+>W_mTzL) zAtqz+IM(+sfLr2x-do%+VLpI{KPgIrX8V~3N8K@et#KfQH-{s)+fMMn%gk17-IpYE zyUaga^ZuUzBof>0Ek=B@lBJAS*5w~|$SzcKMm_F1Qq<&}w31BW(7Hw&)J8#HaJ+sB z+}`b1-uH|Fh&Q->N|i0k)(70kw|JQ?ak2da)lVVNmoGRIkY4bz*&cX8f5!9siAxe? zin$yGT>srMuDL02A(@Aox<%{(x^08JSwl$b2q5{>kW$c!8o520p+pAD6ZOAkEcz!p?o$jo+Ae z&}C(N#+7gqaj-w zhA5&+n=^^oN4_H^>5%ZqdNruAQn3JlX2Apu_bYbsxQR;U23BP2C)4JI8_;6y?(jVY z-0j}cPpB;-UA3ejH)`$l$fJPkPi|4l4BBQ8KLVYzTO$(miR)qm^@DU+sG2n=m15Jr z7aI-^zUdSB;f3B=`_1PIJZQkLAyMD2k(nG}Cbp_3$#$8;o-V_|f<$ER&1|?;u4>Q% zE1V^7y#k68mR??Nk3#I$|5;vjLwyZcN$U4;6=16UU^r2}17NH04ov8ksrQ=Wqp(8Xq{K)l@}R9%`}vn^`d+I%x>TdkyHZgBT$&VF%cUTo0@FNL$slCv7vaD_+urz0tLTg9;Ij;lBB zd>v5wTHW90>@;hIHCQ$97N?J*!F2MHi?4sO$#=sTXXt(F7Lbsa6-Z7pf!?I^ zy)S8wW$_FcW}$cs+CPIIUbtRCt;z-R3m?Z@{(oWUv}^zbO;sXVyULgR&iMX8*wFJ? zU%^4d)0jodncRY~$wD0l&Jx`AyZ^UBlhN`JG1N>_o#ndp(xAH?mxmZ<7%A|X#Qt&Q zs7GRLG`xoR%w#&_Z=vay?J=7WVBrfSmX5`I4LRX_*csaVPUT~-MQN0K$x@gobt*ID zQfAP{CXJHdsz4g%@%}H0Pk}9X5eXyrCW(_$``FYIR_Gjxe$~$&wh>=J;?}3WNDA|1po}%QsQ3=E2%y-I!f&|)GO-HU>%~v{YLG{8$GZ?m-oYK^ zb%}{n+JbnP$ld{elhhm*Y6k!lr=gcn2EPd;Vd?Ptj%yS9mi$OH{?`$&gQgxc^kF%! zz9P_v;`1Y(v*Ap`%HwFARVTt=N^68Cka-epU#-JwSNFw55Z)te$jF>G;%{3DUewhv zgSEC^o|7^?l?OOnyQJNp4N@>LlbF81KekX4GxJLT1CKk8?*_>HA#*wEXgM)lG8q)OuGMgHt8AtpG@z=$K z0XRTH2TRLQ9+Qpb-%Fd~VCy{ntklIOlVxve;IEiFCMOavKqKC|cC1|W>1~&|44h|2 zH%+^g-E(kkZMP`;BrCRU+vZBLR&3k0ZQJIGZF|MGZFX$uG)cc>DLZG5X&vdc0fYW(abb&iu_^F zdfJPndF2l+vpnZAt*plG5}N`H@oei$&+h66PE_(P%`>0&IS8Kg@ksp%ccAHh^Bh-- z<#IZ}str)FKzHDU=|c*m(hHjHwKRQT0`BU{!#+O4vSlu&g~vZ1VTO%m<2$(@c#hHP zSq#Q74c!=-oRCXw8>8tW7tdo4A!7}}8L-ul@PuVdEt@OyLNPYCnSMO-Jn>2>s9=mz zY2(AZSitwA;EhDkMBO=9ld21Cc+CpcBb0q?oIPR)$s7|%`u$Rt<;F!z{Q0w9M zX9vPa+S&p_$H$z|*ftd)zeqn5& z#ZazDOrtC3z$O=48Ur0#J~#qFRf-0^=UKjAok4U^B!< zin#H+?~{v*idcsn2FbgQZlQW})(WoLU+i4z?WP9e#ERTtMOH%zJRC+gScHM0CVMKg zNJelGC7}Ke#19K{l`|CdM{+bCx|;~f*aN>`=Ot^mQOgWG^E#=!DfAExJy&Bqp;H$J z$tJLje=qCDUTen_K};%32xL_Xyw(yiB}UL+oqYSUOb$%4 zmIjOFULpOa5<{$PA`@~nI*$&K+kyl~w!;i+X}Vzg*@}rb+a0DR?0IQ|LLwj;{!YkQ{`+#l){)|?Y=i(^Czss`4u&~L zK{ThXpo^6oPq950jT=(*M6lbz;pJj^URSYHSIj;gY>o`(MMV$sHa4NF%IsQzpk&V} zC?EAf?cA^64g2t^S3Fq#0zqQ&8nM;Xf8m+lJZ%La9A$16qL<1>qE9*@5}fiNO4LDTZnPdH&!uutGKgjYx@Qs#b+Ao6 z0=uegyb}mF&iJN3=`5g|;G5MyD0remu9wCG!63>=0<(Xo zJ7Hp;gMy17Xz z9Kv^Rxgd$+io@m7{PWHU33dv5DCKb%o@sjo_so8a8j*WBWWdAZgZS|WhpOxqui^FJ z#$NlJc*3>l(v+JFE0u_?;WaVk2*1Gix?`T)y8)%&v z*US^2m=lLUEU2zmV;glk_b?#pQKFqY1TATg0$nI5e)MH7Mz7+j>=bo}tQ{YhF3mR@}NkdmYY9dn)9j|CR{+>`+yGl2ILg$oO zN{(W`OXPE%lXH1R4s>6TFVwpS8h0u1y~K8ICI~>5g|q50OHQ#O+`>;{!g8MDeZjwt zz(y8I)APG!{b7$OTtrg^I3vHYKqdFB6OtDZeBkQ_j0|(v980;~`j>sw5K0o5aO%=( z#g2NBIo2o}pnqtiK~TUTeF&Y|S_OMQ@mcUemGJK2hPm%TvrY?F=>*j#_}lWhF`Sb9 znJE%}LkHX;_w%2>lHY`C9CeL(;&z!wB@*xIp#U;;{YN~bZaB!7;UsIYNr%}>_s_D9 zKsf@=TEvPDwjQB~@E$W#emA%2P+T*dGi4&YQ@4Z+(82$B@WEP|%AVd?qbQaCiCeg> zV`{7Q^_wuK3%4vG0#HhrQpId!9wa_@ljx%z-Od!7X{l6vJ}@nOQMI}fmC^Qa9GX1G z`=#P)_#=-^*@wr53LTX3xKHKrL6BS-5tWweMF#4n65aT5(fOrbGA| zvHdRfl-oz>xDd}Uf)QRR^c@h7)TlIr`G#4(5Br-{5wTFbh&yrWKImbRcPK*ja#x`j z5#Q&BD-0)-1qQt`g{7XQ`Q7ry=S^~3`02h`ZPn^8|MG3A zb#g=#h0*c(;%AwugGjgvV_78##SsHx(U$bf4i7^Re7e%}atVS8gGOKHyiG!}?JJ(; z9Awt6q6cD}eW=Q~BhG`Lm#2R0fYBZVHC9(RWSB95-(0Wd)P4@3M0uR5EN=ymrGbkJ zSV>iRt4i~}bBxwvSay>tD+@?29E)gsZNNC}!5KRV@;7(efmbEmE5M15%V?0fMvFwpZg6MfJy@*yP0T&}b5^kXzicmZ_|8RoG0Ws_6{O#^ps20US8?u`$c_dP7 z$)=5!GuB*|ES99DND(Wmc4@joURnc#i>6uaOWm1JaLb~=A+SAWbpqKl5(A`ICXD7i)C`$o81WkDUGT-g zN;9^5DlYNBC&Xf>@YRSG{$U^y^rf*$#T_@C4&j4Tzl<>e-|OeL%7V}rqt4;S)g6v(kE{269s;~4Qp0xFoqiW?}E-LhtumqUp(MuC|g^Pv_ z&!pqC4<6n>js>gl$F%jQp$onF_^qy?y3bQf`u{GWOoSoxn1Z6WrvB2S+AP)42a}jJ{F?H`~ui1E1nhH%0 zR+V7DTHG*y7{*3`!Wp=mbrkl9^pR+YQ+#xQS2_yLS=Hx;Gp)6IQN*Jc??5Wle%%i^ zr-(=_K(88CE>bn^DRkzFl|UQD{%8zeztG<{_z9wX98M%;U}JXRKB1tY!@Q2F*?&Jz zNdpRt80LJVp0b-K2Ro&o)}!1GYJgb>3(qWZBrw4Q1^|slr+EB6sW?=n^)nfTUr|GC z1RSgpLq;OWjFsLS0cRM9!5?YNB8EuV0nRLgipVqoe7%{gjq6dc`0;c;`Vi0;q_s118mT#f>8V-aMX^<^ z`5l!G(aDEINSTx7*@{8q0FckK_$wvde0nFn!2Np$LzFB7aD@{f-Oc0y$W5SQW5l*a zmEZWMRuiZ}6WfJZT^Pw+0TjpwP~h3TRLC1oykKd=Xiol+?(762=j}yt`0JO$zG|5~ zKbrM!r=GYx#}u^rVLX+~bs-xJ8mgA-awKrBx$l+q^b?m)PdF&j2xXD zjP2pI|g$#{988KjJDOr6Y_2$`5zS^s^-OvuW`#PaVe7DC2aa3 zY{#+yqAE-GCo<6k!Ti}S_!B#c8OS^?0T~#$h%{N{r-Fu%(B(=-j#1cyN&SiE>3dwIXK($3P#&Lm75I_eu!LJ%qhMv*>Vq)#A2 zNbG=nzh3(!@II`4DF1>x@i| zTPR4#?`M{bj*bFn)%r36!9hYFfk4>pqw^yo1TEJ{aY9)`;sy+$!F{4x!a6U2_RsXJ zfnB%f&+6O~6YI!{Lc*hn2nO#WHbN3q%Sm&p;qAUi0{wopG{I&h9l%;fRXNCYbPOf& z+6Dd!$YbPuc9qnNN)6+}J&N`}v3R#-geQK(arFNKtdPH#?~uO-6vhFxcYIELOG5?; z0DF;+_z1w;-FXAnfn)$$nEe6Ph7c%Z$50QHRDb~LCt^VO^y8D^CYOhqi3tevC<>fY zP|I%lx`i7)DmK=Fj`(yX7%>*E~JYjUR8WoHMg1Ac(WIpcYta zQ11;-J^2d@h@9d?OjzQ6ajli8Q&gP4xv0W@sj+$bK3j-qdk_G9d60e^1i|~{kES>L zh@{ZRLO@rx{kOoMicn!c`?H9EwtA!uXsF2|?jSD79(5=02;P00W&|9~^m%!Go~GYK z_kpyIDgA(VRIl)3Mtp;LJo}CAQKESTs-_E@JCx;*5)oED)e#pHYzCmr>KNs0 zUfTsK2>gi*h_{qQWyl|c}KRWH|=B^Ye-nA>w<)IR)Ue|_VKbN zD3tlR-*Y;%;l}Ue?+QqivUQSuZ~C&0kvIAv!X}^X+|8n<$1bb!XsPwDgjMROdmfDA z;SXmSEl5ZxmaFs73I$E#{B1ZT`s*Ux_=4oOzxJ=z*nVyH<+F@F=x{{6@60xk+djKU zsm_W37@1b9M5xes@r$HGsyCet>OF`^w)vkm-WQsrb&K$Ngs~xe zslqT72@LnW}>sHM0P84sm+Pi=5^y+w)M#Kq7qYfqI9$MHF?zKCo50)Y*G zR(`mDAMiH++4*I-P62xVcaWYaKefB5>`gRgu<&@YO}^t3jo@L*<%2=;hZ|dSr5pJ~ zZ&>*PBLUg3evv=b=^vRTt8;7OlLtxh^bbfJuHOWf0%Ze;}@NRYXeG#q||GOndiby!0U2D|5THrs2`u6p zqn&R}FlZm`7fwrRem30qyjnQWS{As^t4gt(dArozODY1z1wvN4UMSR}Z8X%u_oNo} zgqo6f$_(yPj(E|I5wqAvY-ml9@Z8wtH()$zsVTrG%fOimn<(~uJ}VXui$qr57?xh+ zkC&h>|7B45&^2SJJX;=R16JouMnDh3Q%*G!ajmK>5H~nr|C|uNihqN284Fh2a`!s-b#HO&CN*zgdC0rY zCJ;VF-$wKCe=IDOL39YZ5HHO!77)l5O|;e4%Y;IH!Z(7!Fp6*GZhf=_R~zN;Ow1q$@sL5%dh7_@ zW3W2zI7Ww|cT#+Y_oGR64K=i1?xLS2guF8lxX^xFz`aJqdpg%(H&WvkNU9O4ueA1uLfOn!Uc=5#VBN5TFSjXO;cO|KAfDZR zc66fu;%HF)QZjJ7U1zV@A1}XA*!Siq-e_={Ugulv{l5BP`}^5wka(X z18e<=*ew;HmT2}Z8uT7paz#!T!b_jK?%~wJI+^#v4Ip&wEKh~r7_k|n1q?6*2md|# zx@2~SJu|=FJm9GN_)?Mi+z3_t9*BR}%>T(*Ii)|UBfFbAq6|qWd5Z=3RQsUpo8NQ) zF^;d~Xg=bAz3E^HN4SH6lB>0xfq$-K6q1{!{KwCUO|VGW1ytsmJ;NbNt&qbCsVYYt z6YFN(3Omk6yLmnSGN?LQvOw@E_XmSf?8^Qk3c69csL&xZrBCs|ixX8rseHOm%_tM{ zzS3eQ1xDMCP!Wu0#FA2RQsTcMOSL=6a z#{0?lK$5E97AamDQu?296hI%1LPkD9UA`=ha2>=-L-MN>J{T7f3~e;Ze|F;nsVFir zR?(r)i#l5^uPY}h-)lX>1!FRki79_p(ianMw{b}b(ERb-ZG{}H07n+UI9kCvM?Rn40+Mq4dy+m zVUZFa9!?+53(a}3rywZ@!kzVI>XU&N_q2nBrAnf6n^%QJi=@b#=Yv?v%Erwy4eLzA;6DFr8yRtLxO!U%~+) z*qy-1xK>v|f_Lc?o8qa!tNF$}_QDcSXPS2(zUjS>r`wFEPgfq_2`J;X%I(_Hpd1wC zBgW6{(gBE|E^p{)*6}3ExQyd_5y`Bv;Qv`XNk%pIT?}kq>NU`-z(w`MkAv!JFcoI) zpxV0nODpTMTq>Q&+KZdE_*67_rFmw8g|}B#wTVBd{xSxhau-f7JlZl1BapC&j=C?Z zg-H%6a6K6jSQ$|_v{VN!fwKuWnzTQPfQi|nLPkc)r*ef^?M52!ZjJg9gwtfX)3g=u zUZ%>^^4iVh2`BXuGgm!Sw$GHzFPp2XqxVc$C)vMbj`6a5=+_dIPO~X*4QFE7X}r&p=#Y=ch`& zp8|Az6O2-L*s5Igy7fI+-`rTZ!$@B9s-;GWyD^XI(Q}Y_8ab>lz&(`N`83LYQ1*&n zPRV|}payq>r3^SL$GpwNUuH^WTR1d^fB_UHr9_o=j1!k}W(`$orfo}Uf+M#v?PaR{ z@4a|$XGX;YGrh9A45zPv(vzadUjJWJ)d~n3<)G2&uGvm|I<#{Y#xKA2{o#APFP{*V zRgCUVd5>GRQtwPG^CUA+;<@Mj z(QyM)>STQTpm#P-Tu8ewKl87i!63O>kEJE=)&yr6RZ4Q87Cdq#`m4k|2FD23zi@j8 zz917m_lQ9cq;c8l>P=F5A>#a3$E$u8bAH6Y*q%)mwB}CPdUPpI-2wInebPjU!1itb z%H9I%h^xi~)-6bV_b>8(=!D0wC>X7)(;7eGcA>|qCX^+z5tcaIH)vZ)?HbBPCY*;N zZoC*6K61ztm@;O566zMO+9>|qvZ!>2ZCE1~~wwXK<(j2es_iqFzq$nYq|IY4Zc61FinS-dZS;y%_hE z>IHxh;G~K*?)n<&n5pY<(=onuAok%fq}YbK5Lnr`=l=(28(3aebXB_+cKTC~1hqy8xL7A=zc)g?i(H z5jKLqP>KuPkL17WqXQg-LJfWe6PHokRXm(;u?vUIN1wlZxNjrYarFW1)??e+9)!5! zuLnyA+kyVTddU&bE$MXYO-4@c8(kR*ToG}ESG(YZ0e(t|GO3Y+HIDSO^s%{}v@O|M z&M2!4pXI}C)ylCWisumpuGUoHT&8=PKYcLU+v=GWA3(SXj(RpqujUu4Z=|Tm(ZmM$ z2kB37>r1s{!5-D;`gJCuXV%u=sWG|#=(NgNFrUHeG2VHgh;`#!t9PqK z65P+dE3Hp}-n&*lQPBs}u7?QUw7QSgeYCqGkgLUUq$1veke!(g26_eA*UH zH@T45hE8*}-|@I18B)=Bfpl-0VF#ZzU7O|x`!1~rlVLCS_t}Amwejy5zZq})BuHWZ zl!WE?q?4Y$1?)=k!!+UL+*_tzOPa!-N`30~^4sDR+XzVwh|jJC-a|*kC#c(ClZsm2 zWo<0YnY$X}gpWn(cb+TR#*0=T-c-YK+P;?OGne|)O%)AVb!YJh^(wBt_PkK>OTtjQ zA_-Dm_Tm5pYQe4nU4cL6#n?e@L-8)NOmBzUl_ix+!c#PG?r75RZ2k=~Gf6Ki;(E>E zK7%I;spML2RJo{9YsnNesUABO?L^J#4|Sah`yNI2`ln9=L&cVoWC~dNy&ECFai6Q* zVen-Ziet#D&#zUU>b3joeiBFtX8mZFy@ObQ?JeF`6|i_Ut%XxKEqs& z&zXD#+o!XVlBt+<&){~5eb^v9G!_jbywThXzjilhwn=?7f|0u{p_nh>CgQ=1aeF~` znx@!)Q@o-ns@B&$Mem98a=aRLidn=9isHJnOj?AGhvuV_lA}YYjj@=O=NK9Qe=MJS zKyxruTXEr1>8{7Do;U4^JWl;kTDK~wYW>rPdwVj)R**CA1AU;*P>{J7qrMW&lcawT zn=#~CnOV_tPJH(g23K%CmxD$WaR81%70r zGdc=Wr~G?g7eV#DeR?Ny)YnQ1G*q(YDNq zrTr6E!KzK#_G0Vt`^nAj3>|P(^-J{WGD-ln$b)#VvX}Xyu}l#)^((Lz$Dsj#E>F|~ z9&Q$1bl%!6G*^ccF{G}^eZ6*~lvCbZS$lCNH!c-{l_=T>w-&XpxbY145%r3w*;}66 z1pSec{o?J&)8LL1d50@@$t*&S`gTKUjm`9Yy|%jU8R}t^MyfYq!;|hliE*BtUVA=A zxepl`jZ@z#5Gv(V)alYZebWRtIV+u<#gXi?UY9LN5u=*?KxyLik?Me2VSbV{%1DcO zskqu^Qt{9!~QEg~pw7Np=#OkD=UUszL@$OFt3f-AV2j7*?%D8?FXoNuB1f3H#_+`w?JI+u`%Ec0_Y>+Q}n|3V+C3@@#>6sSr>vFZag|K z^R0V5e4{EQStzwDq(MZYF5JvRNCT|Xsj*R!*s5m)K=Koy>jTHU9`Y91yA6h}Y{$i} z`#R0X?34FF1=QE+4Ool({!v?St&^1vzfV*rzMqKbyI`9<+CupgZApWgYKBRwo$P2t zicQDM(ILmA8ZP~kkm6E9g|?i{I#j?| z&W$R{rJGNn_Od=!jNrgd=4nMVUtRds@2tr;hHiavrp1sqd++WC+ToQ`&7P013p80D z^-AVI@i-Us{FR4qtB~j7jfg0>M5 z;0exv8N0^G^Ibl4;^WuG<<{igwsq~fh4yD9 zPSb;_x#qio+}wThc5T_<*jJ9Nl!Se~fbeUa%iO+_MTINo_0otn4p%l@X@jkf*yf~g z$0a{-eYZFSP;V_gIp;(x$ro4TFF-STw|W7{k382!lKXYjjp-_>$(PA}apg~l*ZZ&G z|J0-A`j0(o4vv4fqL~SqIa!(hef7Whs9D%J+5V?JYHLMz-Xy?V^HqHaBm?pob08GT zYRE{p6q5c#C*}}rgPLOm?P>zwm`_lmw<={>9_@utL_K=&dC*m(%sh*;<1AoEAmK=A#E=MB!gDE+`9AWMaSC_&+8gDQm0EW_3W{6UFvYsn3~p@{t0 z#K6NjX>&w~Ak88e!YJ0|-{0-j5Dj4xAW@02Nqd12G(vj>uoy|7!BFaQbc}xDn|q4v zp2!d8LMlm0!q_2vh5{oJLIJ`d;OEhS31fCzT(PSL#1RpY&7%z;4XXI$L@;UHW!VX@ zev`lTDl5J7$`QgTlDkvx3)V+~RH>8j4#n+ssQL}maR`8>K<1nG1t|M)2ovxUAric5 zj{S%n_DT)SdoClqQS*g1i+JYo2e*G8;1y&B3M>_BB+LLhLGTkaV@U2QxCs3CdqwJm zAhOG(pXUQA@b^bBiNC%YLfqp%Bv~HFPd}+bKmEL~)aR?1AJAZoYOGjx5PasUw+-BI z;Cw#kbaF<-?2vW=l){8^K=n@_9o(WFTCl*eFrQd9;kRJGXpV)wWr~lYz9nDVVn2R> zR8=A?38TmM|%oaq#L6ZEw#{JQRGny)d%RJ3coVu|}A!v9l70eti~#|QT! z{CpB9^3zwHQkRzgnZvyYJO|iU?d!^+XHK9Tu+L{t_y<(^1K2=(XoV2bFq*8O%cu1K zB4)s_Tj2Lh#?QV>J`)XS*jE_StKi>nJA%mJCWBX~VZ-kZrAYYFfAwy1{P5CEL{O3L z7-~fesD2S!%WT_5q`XaU{gAXo0uB7hIy?ge?9DsZ!Cvp&rJ+kJXI`&cb%*KToJw3a z6(3eWplZ@;OSPv+UaV7_pPmy~dwIhxe>z~+D@Ne0JV7Z>G9x;)OmoOr?msG_` zrcGuYd8+9z`Ii&Y992dZVM6-TEMuAlGlR3`qWoi!K0%K*tNs_*%|pXH%clyh0dh+O z^-7fqIBH`MJ4v@%`p##qe?zC&LX4lA=zx(!PW$?^mGI?)`qWSxn|}nmPmAgg1lU%6 z3>}f&1Hxtc)<~`t^S|qFm*qAdP3fmi^V^EFG2^M3Itr(U8(5xB2V@wLVixt!n$_OK z#Ik+*N8Q7rGgbzxHP8f&e2KmTMO>wYv96iUHPA^ho)`)#nG7UCOs0HXkGqo|jx`q3 zfM&sf;e_M1pb`3BY>J8TyarHVrlEE&a`OU^z^Y?ql?1r4VOqe9Zkv^zMrHGrm)7CO z@9m9=)D9XwU=L5@4xn{Gf20VtV@&hSBqOE^Ruh6;HFFzJd-*9JC?odlS&Eq%=uB=6 zU@{3QTS^HNtD%$Ma;0{gD>%>^uPp;oY7U+?v+1vg(C%;Q!+u|D7)zQvu61n&3=s+`e9O4E8j9jx^eQ~jK2>cO36M%MIWnCfg0FE*FN#|0I7~i{b z3Nb2STo719aqi-1YY%epJ4EjC=w|^`HgLdO*4uuhiWJ)M)>TOGyu5ANiq5t`BBZ3T z!|yhj0;47l*#VdPp$t4RuQiAJ-L%?a@Z^nxevDsqWq?R6Q|aHESLg?PIKd-g9=7$0 z(qqBrcE7N-7x;7$6OP$%lCkQkuRpn$caQAc563XV-`t%mNgrhi4rC{g6xRP;Il%d+r?*lv`AnO ze_&*-^+(k>HN)E1zwl0Jj24Vc43fR{YV=rNkXF3i)x}iv0RBaXoOWxe%(_rlZ@(TY zEa~@*DWEz6A*Ls*gYl@_;}`SfP|+*o0;CiFcJ9E@3b?3+qMGdEleJf{>V8n1rAu54 z&!lV4AyefVb9M@=CrysvE2TRP`fTs)W1hp&)Otk={i-gjw~)ysuA|Wv54}dgB5#)I z+(%A{x&6N4h*VG5V%7vIJGNfMv}$?X-K4>%aKCinCKJ7`iA~=GWf2%x_L((l684fb z-)SsOv1NBkt;cD-;&zbn+~sU{Cj$G}^1k-qA743g3OYBFJhB&};Ct=){3dWX@po;i zkIWLIb0v6;(WMa8JHARoPt)cv7n43atqE1&Vf=!YV|KI|^{qJn_@iGQ+i4hV za!bi6!qsH5O4A4qNx#=1+mh6$kqZE^J1iLB!68{8T%isGh13L<-6ohm!8tilIN3VL z9G48P1%Ln-EBA!BdwnSsWJmg&hDb6;c!iIG=(u^MSU)k4toFZ*BV7>(`-8;V2=gl({sre|aGw6OBU&bknQjxH?+MwAD>jCIhd%L4oP?9v_OIz{Day&M_QUR`G z&EiCh-E+qB?u@0(8`38A6-@BVj_X|`AB16vnw(htiKDH}Nn6E5dY8|z;r3^NxdLvk zX?#u=jGoW4RgKn%?pfNLKmJM`TV;kdb6_u}=kLm2EtSaDh*7@+A?Pi9c#H*ZUUo#9S^UzVsO!Oc&U{IzrL096?AQCrR-N zaaHq=1XUk%kWIl8Hitj!wwO2~hWd^~o^Or(SH~a$!vc+5h&ui%|Zb{GVr zyA>fXmdT5>kx>WFJZ$@vssS)$+yaMMA?i4MpM-t9H#e*l$M=~_Me(t%O~IM&HNPTK znE2j9gmdp4ID%4-xI*ruU&steACHSpN_Fe@9!+ax#UNRfNsJg$G-hNOeQv4{jJ7)E z%h*l!@&r)0+uL`^UP5e%_b1$ng*T-dt-Yh-6w_2-H?eg&nsD%gPj=w!v-0-9@ZeEL z$}pidej#QdOVZ~SWkwq->~qaC>tOXd7(mrr)|}@GSqluxx*F&OWIR~kSYPv@b4*51 zP12HkXbDsG!&e?67`jbE2((SEyW*b6m`2|1C4CQLpxSEB(f95YJJj=*PkLQ9rsCvl z%!)*cAH&-mXY4v(HT56RX@PJvA#(d5T z4_oHM+cU_srPg55FG%go66WYNynF6s7AgCaIx2dRL8%qab$cmrGOl6KJj~tFvee%0 zSnv^$SNMo9%D8lT`RNvo!BDIo|M0sE*kY_C8XF#AX{u2>0y{K_HBBmOVTi?UaO%`b zC}tH`*Go~!l1+QPuKOLxpqAZW-bFid{B%Gu_YoU}7c$>Lkc6j_mv2Yo`Z%SeU=ssJ zCteSvoQiKC#48wz8rULw!gtbOx_oV7)xhTLq1q4*!G3Q0T5B;A-)rva7>hc?{>Eg~ zBrY(@PPWg+bIu;A!h4%Blp5cMg!J!<^TXvE)>Cj(jtjvuX5*8STMr#cMiSbkf;is@ zj%#;jw>K*KJibe8L3?Eq!X2{rBJ3M+8}|=u1lb>Q_)HBEhYl#YBkLQNRZFy2#=y7t%Q=oLUIhVSD!TR6;bouidtgQ$>cCc-4@S>@;+2^d* zcY*qXGTw+33yN>uNk>QUYxcbHG>tXJJVjY(-+UDauEYMrjVCA5|8e6<$jQd|UpJJ5 z|KGcwqLq7X3d9h*?$EkI#nON5+Zg`AQcr2`6^nPFD5z`7SRe718LGhS)FZfkTCXY3 zN>=e4>0NTXDL?Ciu(|YkYnId&;16bft6Y3yj9o5+G5;9e&Dz*xGj!z(-o0fD;=gaH zxWjm`#_rH=Eq!~YJ)bP3g$`FLI{ z@l3q*wVl5|WemN)zN<-Htl7I#GVJ?wbC1p@uvLgJ)wIc)!&KVe$sOluX| zjV~*vFN41Ki_T^%CstS>9Pa_tF`-JEFBVhiZzZ%BAbs&DbTW)3Aea zg9Bi&pxL0!%)8j@YFS$$LzX0|SHkX6VA#>286`A|U>Kix~R`Py+8ddewL2?5 z#Z`MCg*%GXk#!Jd`rTk#0g##=oRXUEiA`N!lA4~lCa^ehk78|QA4F|r9Z21g{nuL_ zqgb9@2e93#S-npQEcb4s=^kXJZr|w*+4r9wpBN}9ioiOZ2M7#0x5JJnWWE`zc2lg0 zvOi=UNqz0Gw~|RhRT$6c95Ym85V+~=EcUFgeI#6$%_ATWPhju-2``$DUd2?oe z6L{cl>f}B@tdM^JUF4kn{ZB-h{}a*w?)m%=h$bud#$-w$cHW`g!P8j753mgVMO070 zWSo<5U!`o$-ylXe;fPtmUN2*av{|oojv55E;9?CMbt(o+?Ckkv84du_jjg#J0(c=5&Tl38!t<97+>E^)p2*Bk0*~SgU(Bt$JHh zh?7?Sqjd9MO2_YY)eBF{u{#N zP$xN*W{5I0GFXji>raIt+-^ODd1pP9#+lM26o|IjnCg^?#;Z1r8Jkjt*&kblHSg6v zPCoe20B3kYtL7Wg1qP7qWl$5?yb5?InNh6$jPg+T1-}9VOovauvg|cX;by=rrSJQo zROwF$_gjp>i4AL z688NLQ>uUEBrOUK#V_9|Q~!&uXZE+##H`+L$@iT9<@7uK-<@WCJC*szDXDLUy5ql` zivHu&Vk?F!tlshJ$PkNR8D2=q}~<`1ir`^Fa9YCcvny_3>x z&8Y=AQ?66a)g9kTD`e+aE;rRTQ#vd-?vGn?I&s#HC6B8sdlI3a77%)OT=efQhUd>bZ&?=7>`$hryPtg5l8(!L_oy{)2BjSZ z;IES1OPU^oFJ_YWk<=%8h2BdC7k3Q>oD$O%5Na{giq2jiX?spO_+I5^JAKoqFI#c& zhxNKtBBww1ymE9#X4m!X1)OcpLcfK|MtBc@ehcL(Rpef=QaZ-}AkMwec67415fJQZ z#@CUNp*wBS$GBK&ds%3-u?V5S_*Wg(wgusE^3}S#EIZu28Qb)zuKrk75M3o#!(q)N zLz4;@iW2Gzh73jqYg`Cs@BVikRg2yK{&8X;Gb(5lp)fKt*297&2FM#&53=yWFQH+g zwJ7~}9bM2{n7GEYsrjy>YgRoRiKKg~1=2WugZ~mLXE+OCLk$|W&=wBdT_7GXbMpa(3pu8M;%|kb znjXCT#WHlMbO~dkJ~sG_$;%!UAF0hegp0%UT~G(zV5HhG=d4kN(rgun2I?x&uq#iB zy9(DUcY4vIZp>B_nBG?+m?ld)tPC~;G+9EXq8^4`tVfnBCvZbjH z6xTU*pGnV^0z~`(sXQ*jY^qDqA#RfIB8v1-FC!ewd7RZRNzc`9NY6F=H-W{T)bxCe z%#>HS?I5b_J9r&O(>W7ZI*-5YeTUQ0f8`G2WTqJm-)iZ<)e>WWS6V0?cJ zHG$U(T??1sC8hjwjLNk?aa%(O(gF8x)72!HKl7}AU zF_~4KG9M@dla=8{(wQ@VVVv=fBvS*&9bo_b80hKUkTrzTej;2`2#7@OxdiNB@D`q* z{wJcW|A{Em|9G|geeZMHSE(_NK! z2yhZl}w!SDrGvP@iY%@1<^st8Ra2B96->V`%Q??(K%u?pDwnf-@#Rdjm|S ztpI$*)#$XG;YhcDz}wN+nPN`mXLP06Cck{-^4Lk5bgRV2j=btKiYF+D=#?A52FSz-c zX|!G6ktH(?f13o)j;)Ag2`s3_UaeqMmb!;zb@Bt2wWZw-bIEw%rnF;^o(F)0|#x`qDU~TnDubG-)Udppp-h7i&e#lgsx@X`dOwNC6iJ z$a_eDUfORx3U~+xjY?8E1&xA@q808f)Jsy+yn&4kbOsX{q(j5hbQp}H!(_u`W8n>1 zub{K6VPC(RBn^LFG@B2xs{;v*+l40L`hN^`m4X^Pjb@!2T9C z=P`G{BG?s~%;*D1>)v-2{q?UZ3Z`GLu{@*4*#yaiokJiPxced=fM6pu=2*uw8}+>d z?=3(*#es<*JQmLIz#uh>X;|bRt4v!Uiu55)>p@IM>q$)Zw3fl(H0|aLr|dLtwP1$4 z8qG$q0?h_ecc=83m`~G7QF$zeA54!Xl>*=bh96EnEK3B!vO}*ZXT~2;ou-unWc&cx zuxV~Zvne&xh`0E`*muGGFQ3oB2e5p>2U1<%lE1nB?`0Hv zltAY@ruB(l<|iT?i_^WZQCUTAAxieNnH);N3UpXX$`9}V%!8DWZ$Z^SID4j(`-aR% z2FB>&c7N>XY}1w81ku0FNS?m34=xJhy@RkzUMc@4qip}DQ4aS1K8Eu@TJB0)?lU5G zy{VcZ1V8SGB;bis6Ya!ef^=A$_tm!y)+JXG)kSBc-d?G)IArLr3VVjR=XB0?zRWyd zt^?L$D_kt!cj6m3SQ?MJS$$p$PC7d}D(R+0=d*I6%cPt?T-^+6yfv#Q!#7JRtQHqa zAS89z4tKTEX#ih^1LuGv5r59}?^D<>4B6&OzgeDbO?>%LyL!}NeDC<4KCGEAd!9Et zdveDkr+1U2y}8w{YPgukO>&J`VDIMdg*Vw{rFb2ts`OWt%sqo5V7H7WBBZO`FVBk4 zh8I@QkspGLl(ln)IJ{CsH8t#XbVCxksx!0!lgCyDV2k}_J$fn#TL(ooQo0N?-37T(K86h2THo8oljo>Nr&M1}!WI5+oT1=Jq&6LI#&=8#&Xt`sc8b?PPKL z4J^faYn?(+jkSJQ_@K#sQoYJkA0z$R>vIqCBce_<2j%>kRpcj(p$eSC?Fr85zzNRl z*v;~HY&HIBF3Rz5T|=N&PHXFkcs3t!mOdJ2&S`yonB#!HUY*@*$zKeaFVuU;aJmts z0w4Y#AAP5?gt~cMgVETm2+Zo$|Cjt-|f(gnqKAMw&~DLin#ge;O_+`JOz zVHV+QII#^NOJXosq1w(>VF#JC?Q7IM4eon;iD$*W@DbQwg*Ng(?Zg;oQQ}*XxO_#J z!i%1*@snC7!xLK66;^8e`m-n(1a#mIZNLty#bQxU}wX;WHKlWuanl36A7q*&yIDCep}Rio<=f33Y$QvX)g%{V(?3GANQSTldDHX$t5@ z8>ex1cW9(>cXxMpcXxO9#@(%PcXxN!TmP9kC(gao5oadum;1hPBC3iNNhVdB*uQ!* z*LrfVRW@3L)We15+8sx{XT4k=Vy2L_wtiYGx{{y#bwnX)6ir$oXk*p>!xn%6Cs-RC#I#-!Dk&ycp~EPuj@ z`JZuOX8YH1vXGas+6LlupyZ5@ungXhOXvfOzw{}F)m$yXv|dR>oaO6JX_%n*mpe?P zaz!pgiOh}BwyV!}4<`;BQwoiZNOwa^Xs}GOIb3W$&dyXF1egV-+0O z)He=Qn9sSR+uk@48kXvUs8y+#ZpW}QgWls9xqr+gb<}vQc~-wvO|MSf+p=R*>C@s= zD3;eiD@~1kw3$(+y|eB_+`PNgh)=g(whUL+9L!YKy;Z(dPsQG#sG;_g*I3-0iQ8t? z@Px0<+B{()6zn8`!)-Hx3bFELrv`IHgYbr4k`Y5!gJgdiVg;E+Y=%bm?&+6x#&hug z(V~uMN)IBE6bMq9GpXp)-3&%6;3}^12-l-qt!E$e6Ab$kdWjLCwz2(G7;XB1x{&`? zI2bKL@3(wRq(Hl4tmvREI8%L2xTK#T$IR)icUP`PH&-nqUajxPa7v%^ZHA&_^C*&V zvIy=IP@%3y2hIA{!2kg25ZzL{j40b9)PVd{$Sufy)V_m2-3yCI<>wB6u??2NdXWtl zVeF$9RR5O_OI@@gZen=T;l=3sU59j^s%Q=FG3}PaypBZJ>C}Z2x*1<^K^72IJlexZxc* zwl+H!dZ%nW*$v_Kv}lFMyv)5STkk_cgvXn~=PeQ(0EVo>GiC71*a_Ij4aZfwi#XHY z@l_O(dha4K4R)x(f3Hm3Xs1cI9p>)EN^Godxg7HR+!A2)IHFitID>Be$Q{ivrzx75 z;)mRsGVLdKvxZzdooF4XIg$cjC%wob86j-{Tvd8X?#6v4x^l6MfJw!s^~*q}{QHXH z#yH^v*-P(`1a{KIl{C91TNj=tlD0oML}|z(_$QEjm@Hjekk25g-r?Z9fEYJkP*Qcu z0BBigk^B+J9Aa>9dlQ&!3|I>k^mGakhWx?VfC)TGC=MaC6vD|E?-*Ahus|WSc9>q1 zQG*}oS4f_*_aM1zNU*4Vn>@}z3uG6aDa-K=HR!-;WYdlt#7!!76~p|WaC_#c$#kMkMhE@ zZ{HrA0y^mKWWUx921NOg^nC$y7H#+U4UBjF329++&el{Ge|(Ac_-aaGJ=Tu1T8r9E zL*=KX3fXx}`B<>CnIs9s0pMwZ{W|S(IWn}Fqyo`NZ*vpYLHQheIWd6Rau$9){a~3f zS(?k_11Zx8Y<@1e_DSV7kBOC9qpclz$}8l+_wY{~u>P|ku+jeO8Gj34nqq|=vF${u zItb*bn~8@|mKqK`Umu+97T8YpvNE!7_fxQeKm)?lLsYFsJ+R@9r%D0H2%Zr=txTQ0v$Xe*IYJ?C&}bR0IW5OGyM zUA;Mnwgz1pXi+BVzEe=)NX>BAysD3SiF>36UMZ}8{G_7d%0viigu&oPElW7u5i3pJ z|L9l~r2DWQP#d3l!H1RD2z7CI;l@E&p_W{^VnP)oQ_^i-Di3A7_o^@%RLj(=Y<3t< zka(VQm8l3Vm?l#U^j-!d1(kMRz~#OMe%>icy}GDfcx&q+x|_G5f9Q z-J@y~&aP?~-k!=9ub-V-!TRSpBI`y;1?yZvs!8c@QkU45GtEH?E_WkMAPrL)B*}TcavIUcXb(cjeQo zbOJ&zss<=gv|2IJsj`y5rA}C(SVlask5|O7XX{pz#Lb(n#I91WDTa+IL`S!&JHViY zRb9YuTEY}Bqx32mS({=nAlh~pmF%B1#@MAjwWGGev95k4+Nws{P+N}CX$%4eH|0L| zztNPyZ%8M+T{zr#vMhacjJ`i8+;=uEeXJXNys*FU44r;t*L?VmzatR8@Wd~D;Ol<` z5x;cOxg-|0jfpg-TQa>j?~t{$W3IWRPRH?IptK`969m$&XV&$;du!YZTW#H4^)Ez8 zT=7&YALIN9>VNi+;<2)^{>zDt|EHjSFuixjS(of(jT`d7khCH@$TNtcy@Dn-9^tWJ zhkv;$lh(?Q|D+t{QM;iNs7Kd%iFiX%o1}AE^!eKroN2&xE^f=rUYKXA##it(+>NNB zCO34g`+i%p{m$v_E5>%D-iiL#!P!9*bglk3+|lXbohNJOFl~JOU#cP89r?Z{_ZKS~ z$Tv|?Z3Ud|xVe+J70C*X7duzgXWMnyT6(|YZHW@o#!7?rJ{D3P+xDLJQq_vom-2Bd z(B?{&$uo2fV2Y`{zw!!tQ-LajzYacq2A}I5YLI z1IY&Z%4eYLn4Sww*iTPN=8s*6*SuDabYUt`jzt80AunT7w-a@_Z3S(9J_hP?KS5Cz z?6pVp)eyo86C4#Atb8XPpTp1_29*8C)P^UrMaB&g#O^0ghPv{#%Wh3Pg(NLU87$6Y z8eA+(5%>vJxLA}g0+x6KY=cOf{WeP_tLcs+htv(o^|c&LQY5fET6=P$M3$xmj>i2vd&!h`O-eh^KA-i>-+>3+NxG# z$Pd&LD6rxsiz+UyDF*I%miz; zEpv|tkwJ&|=Jp3BqBOAWinp?x5$FTXZjB_^E^P;28 za%9ynWzpuZBV&dNeZM};BskWuUv0-K_SBAx?DmLGTSG4U8ppbX>VlpgwT%OxBe`>>WRvn~kQaL}7-q1YG zF5K(T*B$Xt3tfoUlHoQ3E)M5(g~G}@J&c*Wpi$@Rg=JRI-JRf0vPm@9kvno>I`4B0 z?gnSH8yr<1KVutiXJZz5#BRw7mG#Ur;r!F#F|hR1tE%>+{rCHW!F^{{%}4v#`vdjM z29R?;!w=|$H(dLBFWRM#oY8j#{1+a^rH_c;wTycfLM?mtWmgl}2vcWEhvPHxn#isd zhXq}jbm3R`+1U5cr@1_?pQP{2^&jmv|9Sz!18BGTTa6H9MIirn z(-jSlGQq$Elwfa?STwg(33{GP77leF=+)z4EIh6u)V2D~@qTPH4f<5mqs2|R$Ivc^yap2v?IM|KC~JM&(At9lO9=lRM0-q#!a`$h`C%s)L{RkWrEBWj zmf>R5lxs!egU$`3R8{_iU0quSJI`HjZOgleHXdhs)F2uHov1L-S@X(03+Qm)z;080 zH#%n7-#M8oOP&v8YS2578q_!`TDv^WtDncLw@j7T4LZ-_A|R7``TVzvz1J_vP>DKb z5oS6_@c`aX-T@y%2wm`S?|IN5Zvd+WtF{0hI6=`cJ9r*Q9w{O%8#+qMtUAG_v4)?F zohV=+F;IH|H5@8D@EfRJ(+G0^F}=cz8A}Lg4%2iUzIvrMU1DSz94o%M-jDY0kpfy7 zwrx=3=p38rc6Qn$Hrr_FD#cM zt99V2EJo3n&3n-A&O)zHliQeg1IEzvI>vxTo9ov}?f%clVw%Y)?!Soij2lq&ob}BH zwZ6oHWUSIDUj3;K6)a+e<0wJ`gVHF6q;6v>iNgxYuXYmL%yeq(#ErD@DSMB)*4%LaA)lJiHbV$|@v@K`Rxsh=r zX}J@3?8W(1r!yuOAb-$_k-hJ5QTBV4{@w`9OMT6VmluA8F51^5b1!DgB!1j7j^*lU zQ)02@p9=B!LsuGF`MHr1p0@jhuh*fFg4n$VqZ3w#{oF_Wm9|oa*nR9p&($#-Q@YarGBzJa(*Pq z6)sTg5Z6M@GFNy}h$*#xJ9g6SfHr@S-_vgY2K^VJaW71E5-i^;?#kIlfrw%n#if|! zb2`kF6?t~cdWv~86cl$!v1GR7bjt0|U%Yg+SdDCXcT0Aw_mw;eo_CzA$RL**9RkFQ zmn&2B1wY2RIsA9eR!nSzBsy_@_hiR#-}X%~HbgEYXghJ$SRCKNTHeL8Nz5k(cgpn$%%T(vw;;U&j9lbOYTIePFzu%I^L`% zM22tPdo4(QfM zW9T>e*X3I>LYkOX6@!6vZR7j{lZk@iGCXRun&5E@fo(%y2a2-_!b9S&UMHhN&}Ng` zXg@#3qJ-zLQGDyoE7ujjGx0>)$rv&nm`tgCNB6Ls$Mwsq^>hug{i{AW{){vXtPdJa zsCbFG&u1ZKITTK&*N!7kuo`82*{c~3wpA(0nF#b~NbV1vNS+FvpsRiq$GZB~Uj7*V zFdaH^Xx4A^X!18~lh;BgCar(~)@?HwHf}Sx*KRY+p=IT405_`usa6Y~*hJfAcno+1 zmIoijn}K`L^cii3P4rDX>f=$801V0cyhde%Kb#RhUuBtIH|mxUdyzoSefj7vX;Om2 zC665Z;>nt-A4PkiP@gVs|9uN@0Oi_qn&rUeHVI^n!M=2d^TPGP69qenpyp86X>kvu z|F{GG9fT`kWA#sr{?Tjm?=T9qtpckWHz?}@1^3YdfgpvU-@n1gBD7X4o7OKNrvpo0 z-}&L+UhdLT>RQW6gMLixurOHRXluE*dZjm8I^Wmss+K`}VM*$p7|lJ5;kPsu2i?~_ zyxsP`j5{5y?L@TT2Hj6d9oMu}SiP*-qYO2Aytp2}rrfcW+|snZ`}uMoU;q9{eR52# zn{%B%HyWJjj;j4V$|#%ZIAfL#gjDArkn-R-33%m=*3Yv;%zG#S+~6F;_rFVyP1rWJ zc3=wTxCX8d^_8yF#{qS84H~GUkB;fD?+qL9Q?@1h2s$^c5mxyRXDxDdt8XtIH zjGRG+kBqkrGE6d7IIMbi_)!TusEmoGM8+O>m=dZ0qr6w(33BtIy*W-q?d%dDP7`Hsp~> zT}&Jysl;3AeE>7kPX~Qd`aG?PT|&KgNXE44JR_IAEzim`cI7c_xIGZ`mob`X3A(aCbX#*FS4dE=9b;6$=yPW~znwZx;hT$?B`{5ya zE}q8K+mLreG^1c0i?MW!E1`6pK+|fe)J+=lg}(*5fKm6e+$|OGZcxwe=Ob)#V88`* zz>y~186~%Czp()gt3vMcDFSxg>~@?xzJJqDAi)`d+?e-EI;bSvdO{@JMP1trHho67 zw%ZKrVkF#oRY1W63TFrbx1J+VLpzbOa%Ms%lGj2eF2_P9Vnl^zemK?7rn&N$sFhgSHqQ$`879oV>j#*rUV$O-B!a zI@(J744zrG$^0iq|1Zrp9t!`RjzayXjw<}Kj;`?hLq{Kya{i{H$Uq(Suw!eneEmyD zO&;8fZ~{A)T4Xem(oAQt2Y@eFz6Gy|&*s2W-7HI1+~;%26t&I^@RLfa7Xl~_XuB^(`*EvpIcCr@#hE!Qv^k~- zW26+%R*~!YgjO5lc^sB49=McArtgO=6FZkeu2#NJZ<8%slkLSP6A-RFzzSqLd$0_< za)tcKeMjZP-r)J+>}y!bMv9abNfEy)=KSJq1xJ=+1g7I!*ylvms@a-KC7+Y!kNfa^ z&I^+BWjlwn4}R>uGHsJ+bAH(5uRiEL{@^hP{!r+k6_h^bTWncPv~su zE|K0pyGf@CwlQ#^M_|K4<;ZF086gy?n(4Kpd-f3QRZcQ?y75ol5d(WAsp z{AB&3sVCT#4=b#Lecu@}owlKi?4Ypg+h9nRoh87W4IbB_+apBIwhg3m@#ie#mxJ$? z$^v|>M3KZVOZ70!7?pq7X`X-j;?D04re^Bh{VHTX>3l@`A07Sobu>xm@;`Kx$Nv0{ zoc)eOe<}*Nbmc#Ev<9$=^e0CDV76gpVEDK4#{b0~5O(IzDi8B}JZQGp(^#$I9yVkp zLjEiA=A>7rCxd@iDRlH3=gq6(>*=b;(-UW{MCV=YV;OSiYYq>fK5K7-`Q`Au^6+G4 z+-dM)ou%WO5<=AW!Q{fkJL&mN(RPVh6axNZN!f-}1a2NTg6(_7Ss~&n!MWCJ*3x

    ;Cb*5v(Kp0rj$JSb@^@r4uV4&vB^Y@a*w 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 diff --git a/lib/solady/audits/shung-solady-erc721-audit.pdf b/lib/solady/audits/shung-solady-erc721-audit.pdf deleted file mode 100644 index 471a9ffb139eee0bb2a13c5a5ad532b563cc16ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102720 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 T$idFR$H#|ALnEOmiTJ+&r)$!m diff --git a/lib/solady/ext/woke/EIP712Mock.sol b/lib/solady/ext/woke/EIP712Mock.sol deleted file mode 100644 index b0a7916..0000000 --- a/lib/solady/ext/woke/EIP712Mock.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT - -import "src/utils/EIP712.sol"; - -contract EIP712Mock is EIP712 { - string constant public NAME = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec ipsum massa, ullamcorper in, auctor et, scelerisque sed, est. Maecenas libero. Phasellus et lorem id felis nonummy placerat. Vestibulum erat nulla, ullamcorper nec, rutrum non, nonummy ac, erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Etiam commodo dui eget wisi. Duis viverra diam non justo. Proin pede metus, vulputate nec, fermentum fringilla, vehicula vitae, justo. Mauris elementum mauris vitae tortor. Ut tempus purus at lorem. Maecenas sollicitudin. Vivamus ac leo pretium faucibus. Vivamus luctus egestas leo. Proin in tellus sit amet nibh dignissim sagittis. Aliquam erat volutpat. Cras elementum. Curabitur ligula sapien, pulvinar a vestibulum quis, facilisis vel sapien. Fusce wisi. Suspendisse sagittis ultrices augue. Aenean id metus id velit ullamcorper pulvinar. Cras pede libero, dapibus nec, pretium sit amet, tempor quis. Cras elementum. Nunc auctor. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Nulla est. Aliquam ornare wisi eu metus. Et harum quidem rerum facilis est et expedita distinctio. Mauris metus. Phasellus faucibus molestie nisl. Mauris tincidunt sem sed arcu. Aenean placerat. Etiam neque. Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Nullam dapibus fermentum ipsum. Mauris tincidunt sem sed arcu. Nam quis nulla. Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Pellentesque sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Vestibulum erat nulla, ullamcorper nec, rutrum non, nonummy ac, erat. Maecenas aliquet accumsan leo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nullam rhoncus aliquam metus. Praesent vitae arcu tempor neque lacinia pretium. Nulla pulvinar eleifend sem. Etiam bibendum elit eget erat. Aenean placerat. Mauris metus. Aenean fermentum risus id tortor. Suspendisse nisl. Fusce wisi. Fusce nibh. Sed vel lectus. Donec odio tempus molestie, porttitor ut, iaculis quis, sem. Curabitur ligula sapien, pulvinar a vestibulum quis, facilisis vel sapien. Mauris elementum mauris vitae tortor. Duis sapien nunc, commodo et, interdum suscipit, sollicitudin et, dolor. Quisque tincidunt scelerisque libero. Vivamus luctus egestas leo. Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. Duis risus. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Suspendisse nisl. Cras elementum. Phasellus rhoncus. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. In enim a arcu imperdiet malesuada. In dapibus augue non sapien. "; - string constant public VERSION = "1.0.0"; - - function _domainNameAndVersion() internal pure override returns (string memory, string memory) { - return (NAME, VERSION); - } - - function hashTypedData(bytes32 structHash) external view returns(bytes32) { - return _hashTypedData(structHash); - } - -} \ No newline at end of file diff --git a/lib/solady/ext/woke/ERC1155Mock.sol b/lib/solady/ext/woke/ERC1155Mock.sol deleted file mode 100644 index bcc75ee..0000000 --- a/lib/solady/ext/woke/ERC1155Mock.sol +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: MIT -import "src/tokens/ERC1155.sol"; - -contract ERC1155Mock is ERC1155 { - event BeforeTokenTransfer(address from, address to, uint256[] ids, uint256[] amounts, bytes data); - - event AfterTokenTransfer(address from, address to, uint256[] ids, uint256[] amounts, bytes data); - - bool immutable private _enableHooks; - - constructor(bool enableHooks_) { - _enableHooks = enableHooks_; - } - - function _useBeforeTokenTransfer() internal view override returns (bool) { - return _enableHooks; - } - - function _useAfterTokenTransfer() internal view override returns (bool) { - return _enableHooks; - } - - function _beforeTokenTransfer( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal override { - emit BeforeTokenTransfer(from, to, ids, amounts, data); - } - - function _afterTokenTransfer( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal override { - emit AfterTokenTransfer(from, to, ids, amounts, data); - } - - function uri(uint256 id) public view override returns (string memory) {} - - function mint(address to, uint256 id, uint256 amount, bytes memory data) external { - _mint(to, id, amount, data); - } - - function batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) external { - _batchMint(to, ids, amounts, data); - } - - function burnUnchecked(address by, address from, uint256 id, uint256 amount) external { - _burn(by, from, id, amount); - } - - function burn(address from, uint256 id, uint256 amount) external { - _burn(msg.sender, from, id, amount); - } - - function batchBurnUnchecked(address by, address from, uint256[] memory ids, uint256[] memory amounts) external { - _batchBurn(by, from, ids, amounts); - } - - function batchBurn(address from, uint256[] memory ids, uint256[] memory amounts) external { - _batchBurn(msg.sender, from, ids, amounts); - } - - function setApprovalForAllUnchecked(address by, address operator, bool approved) external { - _setApprovalForAll(by, operator, approved); - } - - function safeTransferUnchecked( - address by, - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) external { - _safeTransfer(by, from, to, id, amount, data); - } - - function safeBatchTransferUnchecked( - address by, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) external { - _safeBatchTransfer(by, from, to, ids, amounts, data); - } -} - -contract ERC1155ReceiverMock { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata data - ) external pure returns(bytes4) { - require(keccak256(data) == keccak256(hex"00112233"), "ERC1155ReceiverMock: invalid payload received"); - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata data - ) external pure returns(bytes4) { - require(keccak256(data) == keccak256(hex"00112233"), "ERC1155ReceiverMock: invalid payload received"); - return this.onERC1155BatchReceived.selector; - } -} - -contract ERC1155ReentrancyAttacker { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata data - ) external returns(bytes4) { - if (data.length == 0) - ERC1155Mock(msg.sender).mint(address(this), 1024, 1, hex"00112233"); - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata data - ) external returns(bytes4) { - if (data.length == 0) - ERC1155Mock(msg.sender).mint(address(this), 1024, 1, hex"00112233"); - return this.onERC1155BatchReceived.selector; - } -} \ No newline at end of file diff --git a/lib/solady/ext/woke/ERC20Mock.sol b/lib/solady/ext/woke/ERC20Mock.sol deleted file mode 100644 index a4a2fae..0000000 --- a/lib/solady/ext/woke/ERC20Mock.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/* -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣷⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠿⣿⣿⣿⣿⣿⣿⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣴⣶⣾⣿⣷⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠻⢿⣿⣿⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⢿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⣿⡿⠿⠟⠛⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⣿⣿⣿⣆⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⡿⠟⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣧⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣇⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣶⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠙⠋⠀⠀⠀ -⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣾⠟⢋⣥⣤⠀⣶⣶⣶⣦⣤⣌⣉⠛⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⣴⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⢁⣴⣿⣿⡿⠀⣿⣿⣿⣿⣿⣿⣿⣷⡄⠀⠀⠀⠀⠀ -⠀⠀⠀⣼⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣶⣶⣾⣿⣿⣿⣿⣷⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⠁⠀⠀⢹⣿⣿⣿⣿⣿⣿⢻⣿⡄⠀⠀⠀⠀ -⠀⠀⠀⠛⠋⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⠿⠛⣛⣉⣉⣀⣀⡀⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⢸⣿⣿⡄⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⡿⢋⣩⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣶⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣦⣀⣀⣴⣿⣿⣿⣿⣿⡿⢸⣿⢿⣷⡀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣡⣄⠀⠋⠁⠀⠈⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⡟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⡿⠀⠛⠃⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣧⡀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠛⠃⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⠈⠁⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⢿⣿⣿⣿⣷⣦⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⣶⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⣿⠇⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⢠⣿⣿⣿⠟⠉⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⠀⠀⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⢸⣿⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⣼⣿⡟⠁⣠⣦⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠉⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡆⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⠏⠀⣸⡏⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⣿⡏⠀⠀⣿⣿⡀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⢹⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣇⠀⠀⠀⠙⢿⣿⣿⡿⠟⠁⠀⣸⡿⠁⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⢸⣿⠁⠀⠀⢸⣿⣇⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣦⡀⠀⠀⠀⠈⠉⠀⠀⠀⣼⡿⠁⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠈⠁⠀⠀⠀⠀⢿⣿⡄⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⣼⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣷⣦⣄⣀⠀⠀⢀⡈⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣆⠀⠀⠀⠉⠛⠿⢿⣿⣿⠿⠛⠁⠀⠀⠀⣠⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣷⣿⣯⣤⣶⠄⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣷⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠙⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢿⣷⣤⣀⠀⠀⠀⠀⠀⠀⠀⠺⣿⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⢻⣿⣶⣤⣤⣤⣶⣷⣤⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⡿⠿⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠶⢤⣄⣀⣀⣤⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀*/ - -import "src/tokens/ERC20.sol"; -import "src/utils/SafeTransferLib.sol"; - -interface IERC20 { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); - function totalSupply() external view returns (uint256); - function balanceOf(address account) external view returns (uint256); - function transfer(address to, uint256 amount) external returns (bool); - function allowance(address owner, address spender) external view returns (uint256); - function approve(address spender, uint256 amount) external returns (bool); - function transferFrom(address from, address to, uint256 amount) external returns (bool); -} - -contract ERC20Mock is ERC20 { - string private $name; - string private $symbol; - uint8 private $decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) { - $name = name_; - $symbol = symbol_; - $decimals = decimals_; - } - - function mint(address account, uint256 amount) public { - _mint(account, amount); - } - - function burn(address account, uint256 amount) public { - _burn(account, amount); - } - - function name() public view virtual override returns (string memory) { - return $name; - } - - function symbol() public view virtual override returns (string memory) { - return $symbol; - } - - function decimals() public view virtual override returns (uint8) { - return $decimals; - } - - function safeTransferETH(address to, uint256 value) public { - SafeTransferLib.safeTransferETH(to, value); - } - - function forceSafeTransferETH(address to, uint256 value) public { - SafeTransferLib.forceSafeTransferETH(to, value); - } - - function forceSafeTransferETHGas(address to, uint256 value, uint256 gas) public { - SafeTransferLib.forceSafeTransferETH(to, value, gas); - } - - function trySafeTransferETH(address to, uint256 value, uint256 gasStipend) public returns (bool) { - return SafeTransferLib.trySafeTransferETH(to, value, gasStipend); - } - - function safeTransferFrom(address token, address from, address to, uint256 value) public { - SafeTransferLib.safeTransferFrom(token, from, to, value); - } - - function safeTransferAllFrom(address token, address from, address to) public { - SafeTransferLib.safeTransferAllFrom(token, from, to); - } - - function safeTransfer(address token, address to, uint256 value) public { - SafeTransferLib.safeTransfer(token, to, value); - } - - function safeTransferAll(address token, address to) public { - SafeTransferLib.safeTransferAll(token, to); - } - - function safeApprove(address token, address spender, uint256 value) public { - SafeTransferLib.safeApprove(token, spender, value); - } - - function balanceOfoor(address token, address account) public view returns (uint256) { - return SafeTransferLib.balanceOf(token, account); - } -} diff --git a/lib/solady/ext/woke/ERC721Mock.sol b/lib/solady/ext/woke/ERC721Mock.sol deleted file mode 100644 index 88aa7f1..0000000 --- a/lib/solady/ext/woke/ERC721Mock.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -import "src/tokens/ERC721.sol"; -contract ERC721Mock is ERC721 { - - event BeforeTokenTransfer(address from, address to, uint256 id); - - event AfterTokenTransfer(address from, address to, uint256 id); - - uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; - /// @dev Returns the token collection name. - function name() public view override returns (string memory) { - return "Mock ERC721"; - } - /// @dev Returns the token collection symbol. - function symbol() public view override returns (string memory) { - return "MERC721"; - } - /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. - function tokenURI(uint256 id) public view override returns (string memory) { - return "aaa"; - } - function getAux(address owner) public view returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner) - result := shr(32, sload(keccak256(0x0c, 0x1c))) - } - } - function _beforeTokenTransfer( - address from, - address to, - uint256 id - ) internal override { - emit BeforeTokenTransfer(from, to, id); - } - - function _afterTokenTransfer( - address from, - address to, - uint256 id - ) internal override { - emit AfterTokenTransfer(from, to, id); - } - - function mint(address to, uint256 id) public { - _mint(to, id); - } - - function burnZero(uint256 id) public { - _burn(id); - } - - function burn(uint256 id) public { - _burn(msg.sender, id); - } - - function transfer(address from, address to, uint256 id) public { - _transfer(msg.sender, from, to, id); - } - - - function balanceOf(address owner) public view virtual override returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - // Revert if the `owner` is the zero address. - if iszero(owner) { - mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`. - revert(0x1c, 0x04) - } - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner) - result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE) - } - } - function ownerOf(uint256 id) public view virtual override returns (address result) { - result = _ownerOf(id); - /// @solidity memory-safe-assembly - assembly { - if iszero(result) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - } - } -} \ No newline at end of file diff --git a/lib/solady/ext/woke/MerkleProofMock.sol b/lib/solady/ext/woke/MerkleProofMock.sol deleted file mode 100644 index 57c28a0..0000000 --- a/lib/solady/ext/woke/MerkleProofMock.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -import "src/utils/MerkleProofLib.sol"; - -contract MerkleProofMock { - function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) external pure returns (bool) { - return MerkleProofLib.verify(proof, root, leaf); - } - - function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) external pure returns (bool) { - return MerkleProofLib.verify(proof, root, leaf); - } - - function verifyMultiProof(bytes32[] memory proof, bytes32 root, bytes32[] memory leaves, bool[] memory flags) external pure returns (bool) { - return MerkleProofLib.verifyMultiProof(proof, root, leaves, flags); - } - - function verifyMultiProofCalldata(bytes32[] calldata proof, bytes32 root, bytes32[] calldata leaves, bool[] calldata flags) external pure returns (bool) { - return MerkleProofLib.verifyMultiProof(proof, root, leaves, flags); - } -} \ No newline at end of file diff --git a/lib/solady/ext/woke/NoETHMock.sol b/lib/solady/ext/woke/NoETHMock.sol deleted file mode 100644 index 7453f38..0000000 --- a/lib/solady/ext/woke/NoETHMock.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/* -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣷⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠿⣿⣿⣿⣿⣿⣿⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣤⣴⣶⣾⣿⣷⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠻⢿⣿⣿⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⢿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⣿⡿⠿⠟⠛⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⣿⣿⣿⣆⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⡿⠟⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣧⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣇⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣶⣿⣿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠙⠋⠀⠀⠀ -⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣤⣾⠟⢋⣥⣤⠀⣶⣶⣶⣦⣤⣌⣉⠛⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⣴⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⢁⣴⣿⣿⡿⠀⣿⣿⣿⣿⣿⣿⣿⣷⡄⠀⠀⠀⠀⠀ -⠀⠀⠀⣼⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣤⣶⣶⣾⣿⣿⣿⣿⣷⣶⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⣿⣿⠁⠀⠀⢹⣿⣿⣿⣿⣿⣿⢻⣿⡄⠀⠀⠀⠀ -⠀⠀⠀⠛⠋⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⠿⠛⣛⣉⣉⣀⣀⡀⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⣿⣿⣿⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⢸⣿⣿⡄⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⡿⢋⣩⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣶⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣦⣀⣀⣴⣿⣿⣿⣿⣿⡿⢸⣿⢿⣷⡀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣡⣄⠀⠋⠁⠀⠈⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣿⡟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢸⡿⠀⠛⠃⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣧⡀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠛⠃⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⠈⠁⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⢿⣿⣿⣿⣷⣦⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣶⣶⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⣿⠇⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⢠⣿⣿⣿⠟⠉⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣿⠀⠀⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⢸⣿⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⣼⣿⡟⠁⣠⣦⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠉⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⡆⠀⠀⢻⣿⣿⣿⣿⣿⣿⣿⠏⠀⣸⡏⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⣿⡏⠀⠀⣿⣿⡀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠀⢹⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⣇⠀⠀⠀⠙⢿⣿⣿⡿⠟⠁⠀⣸⡿⠁⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⢸⣿⠁⠀⠀⢸⣿⣇⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⢀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣦⡀⠀⠀⠀⠈⠉⠀⠀⠀⣼⡿⠁⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠈⠁⠀⠀⠀⠀⢿⣿⡄⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⣼⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣷⣦⣄⣀⠀⠀⢀⡈⠙⠁⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣿⣆⠀⠀⠀⠉⠛⠿⢿⣿⣿⠿⠛⠁⠀⠀⠀⣠⣿⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣷⣿⣯⣤⣶⠄⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣷⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠙⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢿⣷⣤⣀⠀⠀⠀⠀⠀⠀⠀⠺⣿⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⢻⣿⣶⣤⣤⣤⣶⣷⣤⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣿⣿⡿⠿⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠶⢤⣄⣀⣀⣤⠶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀*/ - -contract NoETHMock { - receive() external payable { - revert("No ETH pls"); - } - fallback() external payable { - revert("No ETH pls"); - } -} diff --git a/lib/solady/ext/woke/SignatureCheckerMock.sol b/lib/solady/ext/woke/SignatureCheckerMock.sol deleted file mode 100644 index c28adc3..0000000 --- a/lib/solady/ext/woke/SignatureCheckerMock.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -import "src/utils/SignatureCheckerLib.sol"; - -contract SignatureCheckerMock { - function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) external view returns (bool) { - return SignatureCheckerLib.isValidSignatureNow(signer, hash, signature); - } - - function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) external view returns (bool) { - return SignatureCheckerLib.isValidSignatureNowCalldata(signer, hash, signature); - } - - function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) external view returns(bool) { - return SignatureCheckerLib.isValidSignatureNow(signer, hash, r, vs); - } - - function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) external view returns(bool) { - return SignatureCheckerLib.isValidSignatureNow(signer, hash, v, r, s); - } - - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) external view returns (bool) { - return SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, signature); - } - - function isValidERC1271SignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) external view returns (bool) { - return SignatureCheckerLib.isValidERC1271SignatureNowCalldata(signer, hash, signature); - } - - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) external view returns(bool) { - return SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, r, vs); - } - - function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) external view returns(bool) { - return SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, v, r, s); - } -} - -contract ERC1217SignatureChecker { - bytes4 constant internal MAGICVALUE = 0x1626ba7e; - - function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4) { - uint8 v; - bytes32 r; - bytes32 s; - - assembly { - r := mload(add(_signature, 0x20)) - s := mload(add(_signature, 0x40)) - v := byte(0, mload(add(_signature, 0x60))) - } - - if (tx.origin == ecrecover(_hash, v, r, s)) { - return MAGICVALUE; - } else { - return 0x0; - } - } -} \ No newline at end of file diff --git a/lib/solady/ext/woke/__init__.py b/lib/solady/ext/woke/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/lib/solady/ext/woke/test_eip712.py b/lib/solady/ext/woke/test_eip712.py deleted file mode 100644 index d56b431..0000000 --- a/lib/solady/ext/woke/test_eip712.py +++ /dev/null @@ -1,84 +0,0 @@ -from dataclasses import dataclass, field - -from eth_account._utils.structured_data.hashing import hash_message -from woke.testing import * -from pytypes.src.utils.ERC1967Factory import ERC1967Factory -from pytypes.tests.EIP712Mock import EIP712Mock - - -@dataclass -class Person: - name: str - wallet: Address - - -@dataclass -class Mail: - from_: Person = field(metadata={"original_name": "from"}) - to: Person - contents: str - - -mail = Mail( - from_=Person("Cow", Address("0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826")), - to=Person("Bob", Address("0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB")), - contents="Hello, Bob!", -) - - -@default_chain.connect() -def test_eip712(): - default_chain.set_default_accounts(default_chain.accounts[0]) - signer = Account.new() - - eip712 = EIP712Mock.deploy() - assert eip712.eip712Domain() == ( - 0b01111.to_bytes(1, "big"), - eip712.NAME(), - eip712.VERSION(), - default_chain.chain_id, - eip712.address, - b"\x00" * 32, - [], - ) - - mail_hash = hash_message(signer._prepare_eip712_dict(mail, Eip712Domain(), False)) - - sign1 = signer.sign_hash(eip712.hashTypedData(mail_hash)) - sign2 = signer.sign_structured(mail, Eip712Domain( - name=eip712.NAME(), - version=eip712.VERSION(), - chainId=default_chain.chain_id, - verifyingContract=eip712.address, - )) - assert sign1 == sign2 - - -@default_chain.connect() -def test_eip712_proxy(): - default_chain.set_default_accounts(default_chain.accounts[0]) - signer = Account.new() - - proxy_factory = ERC1967Factory.deploy() - impl = EIP712Mock.deploy() - eip712 = EIP712Mock(proxy_factory.deploy_(impl, signer).return_value) - assert eip712.eip712Domain() == ( - 0b01111.to_bytes(1, "big"), - eip712.NAME(), - eip712.VERSION(), - default_chain.chain_id, - eip712.address, - b"\x00" * 32, - [], - ) - - mail_hash = hash_message(signer._prepare_eip712_dict(mail, Eip712Domain(), False)) - - sign1 = signer.sign_hash(eip712.hashTypedData(mail_hash)) - sign2 = signer.sign_structured(mail, Eip712Domain( - name=eip712.NAME(), - version=eip712.VERSION(), - chainId=default_chain.chain_id, - verifyingContract=eip712.address, - )) - assert sign1 == sign2 diff --git a/lib/solady/ext/woke/test_eip712_fuzz.py b/lib/solady/ext/woke/test_eip712_fuzz.py deleted file mode 100644 index 85fb8ae..0000000 --- a/lib/solady/ext/woke/test_eip712_fuzz.py +++ /dev/null @@ -1,65 +0,0 @@ -from dataclasses import dataclass, field - -from eth_account._utils.structured_data.hashing import hash_message -from woke.testing import * -from woke.testing.fuzzing import * -from pytypes.src.utils.ERC1967Factory import ERC1967Factory -from pytypes.tests.EIP712Mock import EIP712Mock - - -@dataclass -class Person: - name: str - wallet: Address - - -@dataclass -class Mail: - from_: Person = field(metadata={"original_name": "from"}) - to: Person - contents: str - - -class Eip712FuzzTest(FuzzTest): - _proxy_factory: ERC1967Factory - _eip712: EIP712Mock - _eip712_proxy: EIP712Mock - _signer: Account - - def __init__(self): - self._proxy_factory = ERC1967Factory.deploy() - - def pre_sequence(self) -> None: - self._eip712 = EIP712Mock.deploy() - self._signer = Account.new() - self._eip712_proxy = EIP712Mock( - self._proxy_factory.deploy_(self._eip712, self._signer).return_value - ) - - @flow() - def sign_flow(self, mail: Mail) -> None: - mail_hash = hash_message(self._signer._prepare_eip712_dict(mail, Eip712Domain(), False)) - - sign1 = self._signer.sign_hash(self._eip712.hashTypedData(mail_hash)) - sign2 = self._signer.sign_structured(mail, Eip712Domain( - name=self._eip712.NAME(), - version=self._eip712.VERSION(), - chainId=default_chain.chain_id, - verifyingContract=self._eip712.address, - )) - assert sign1 == sign2 - - sign1 = self._signer.sign_hash(self._eip712_proxy.hashTypedData(mail_hash)) - sign2 = self._signer.sign_structured(mail, Eip712Domain( - name=self._eip712_proxy.NAME(), - version=self._eip712_proxy.VERSION(), - chainId=default_chain.chain_id, - verifyingContract=self._eip712_proxy.address, - )) - assert sign1 == sign2 - - -@default_chain.connect() -def test_eip712_fuzz(): - default_chain.set_default_accounts(default_chain.accounts[0]) - Eip712FuzzTest().run(10, 10) diff --git a/lib/solady/ext/woke/test_erc1155.py b/lib/solady/ext/woke/test_erc1155.py deleted file mode 100644 index 8a2ce7a..0000000 --- a/lib/solady/ext/woke/test_erc1155.py +++ /dev/null @@ -1,443 +0,0 @@ -from woke.testing import * -from pytypes.tests.ERC1155Mock import ERC1155Mock, ERC1155ReceiverMock - - -@default_chain.connect() -def test_erc1155_misc(): - a = default_chain.accounts[0] - - erc1155 = ERC1155Mock.deploy(False, from_=a) - - # ERC-165 - assert erc1155.supportsInterface(bytes.fromhex("01ffc9a7")) - # ERC-1155 - assert erc1155.supportsInterface(bytes.fromhex("d9b67a26")) - # ERC-1155 Metadata URI - assert erc1155.supportsInterface(bytes.fromhex("0e89341c")) - - assert not erc1155.supportsInterface(bytes.fromhex("deadbeef")) - - erc1155.mint(a, 0, 100, b"\x00\x11", from_=a) - erc1155.setApprovalForAll(a, False, from_=a) - assert not erc1155.isApprovedForAll(a, a) - erc1155.safeTransferFrom(a, a, 0, 100, b"\x00\x11", from_=a) - assert erc1155.balanceOf(a, 0) == 100 - - # check overflow when sending to self - erc1155.mint(a, 0, 2 ** 256 - 1 - 100, b"", from_=a) - erc1155.safeTransferFrom(a, a, 0, 2 ** 256 - 1, b"", from_=a) - assert erc1155.balanceOf(a, 0) == 2 ** 256 - 1 - - -@default_chain.connect() -def test_erc1155_events(): - assert keccak256("TransferSingle(address,address,address,uint256,uint256)".encode()) == bytes.fromhex("c3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62") - assert keccak256("TransferBatch(address,address,address,uint256[],uint256[])".encode()) == bytes.fromhex("4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb") - assert keccak256("ApprovalForAll(address,address,bool)".encode()) == bytes.fromhex("17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31") - - a = default_chain.accounts[0] - b = default_chain.accounts[1] - - erc1155 = ERC1155Mock.deploy(True, from_=a) - - tx = erc1155.mint(a, 0, 100, b"\x00\x11", from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(Address.ZERO, a.address, [0], [100], bytearray(b"\x00\x11")), - ERC1155Mock.TransferSingle(a.address, Address.ZERO, a.address, 0, 100), - ERC1155Mock.AfterTokenTransfer(Address.ZERO, a.address, [0], [100], bytearray(b"\x00\x11")), - ] - - tx = erc1155.burn(a, 0, 100, from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, Address.ZERO, [0], [100], bytearray(b"")), - ERC1155Mock.TransferSingle(a.address, a.address, Address.ZERO, 0, 100), - ERC1155Mock.AfterTokenTransfer(a.address, Address.ZERO, [0], [100], bytearray(b"")), - ] - - tx = erc1155.batchMint(a, [0, 1, 2], [100, 200, 300], b"\x11\x22", from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(Address.ZERO, a.address, [0, 1, 2], [100, 200, 300], bytearray(b"\x11\x22")), - ERC1155Mock.TransferBatch(a.address, Address.ZERO, a.address, [0, 1, 2], [100, 200, 300]), - ERC1155Mock.AfterTokenTransfer(Address.ZERO, a.address, [0, 1, 2], [100, 200, 300], bytearray(b"\x11\x22")), - ] - - tx = erc1155.batchBurn(a, [0, 1, 2], [100, 200, 300], from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, Address.ZERO, [0, 1, 2], [100, 200, 300], bytearray(b"")), - ERC1155Mock.TransferBatch(a.address, a.address, Address.ZERO, [0, 1, 2], [100, 200, 300]), - ERC1155Mock.AfterTokenTransfer(a.address, Address.ZERO, [0, 1, 2], [100, 200, 300], bytearray(b"")), - ] - - erc1155.mint(a, 0, 100, b"", from_=a) - tx = erc1155.safeTransferFrom(a, b, 0, 100, b"", from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, b.address, [0], [100], bytearray(b"")), - ERC1155Mock.TransferSingle(a.address, a.address, b.address, 0, 100), - ERC1155Mock.AfterTokenTransfer(a.address, b.address, [0], [100], bytearray(b"")), - ] - - tx = erc1155.safeTransferFrom(b, b, 0, 100, b"\x33", from_=b) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(b.address, b.address, [0], [100], bytearray(b"\x33")), - ERC1155Mock.TransferSingle(b.address, b.address, b.address, 0, 100), - ERC1155Mock.AfterTokenTransfer(b.address, b.address, [0], [100], bytearray(b"\x33")), - ] - - erc1155.setApprovalForAll(a, True, from_=b) - tx = erc1155.safeTransferFrom(b, a, 0, 100, b"", from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(b.address, a.address, [0], [100], bytearray(b"")), - ERC1155Mock.TransferSingle(a.address, b.address, a.address, 0, 100), - ERC1155Mock.AfterTokenTransfer(b.address, a.address, [0], [100], bytearray(b"")), - ] - erc1155.setApprovalForAll(a, False, from_=b) - - erc1155.setApprovalForAll(b, True, from_=a) - tx = erc1155.safeBatchTransferFrom(a, b, [0, 0, 0], [35, 30, 35], b"", from_=b) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, b.address, [0, 0, 0], [35, 30, 35], bytearray(b"")), - ERC1155Mock.TransferBatch(b.address, a.address, b.address, [0, 0, 0], [35, 30, 35]), - ERC1155Mock.AfterTokenTransfer(a.address, b.address, [0, 0, 0], [35, 30, 35], bytearray(b"")), - ] - erc1155.setApprovalForAll(b, False, from_=a) - - erc1155.setApprovalForAll(a, True, from_=b) - tx = erc1155.burn(b, 0, 100, from_=a) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(b.address, Address.ZERO, [0], [100], bytearray(b"")), - ERC1155Mock.TransferSingle(a.address, b.address, Address.ZERO, 0, 100), - ERC1155Mock.AfterTokenTransfer(b.address, Address.ZERO, [0], [100], bytearray(b"")), - ] - - assert erc1155.isApprovedForAll(a, b) == False - tx = erc1155.setApprovalForAll(b, False, from_=a) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, False)] - assert erc1155.isApprovedForAll(a, b) == False - - tx = erc1155.setApprovalForAll(b, True, from_=a) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, True)] - assert erc1155.isApprovedForAll(a, b) == True - - tx = erc1155.setApprovalForAll(b, True, from_=a) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, True)] - assert erc1155.isApprovedForAll(a, b) == True - - tx = erc1155.setApprovalForAll(b, False, from_=a) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, False)] - assert erc1155.isApprovedForAll(a, b) == False - - c = default_chain.accounts[2] - - tx = erc1155.setApprovalForAllUnchecked(a, b, True, from_=c) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, True)] - tx = erc1155.setApprovalForAllUnchecked(a, b, True, from_=c) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, True)] - tx = erc1155.setApprovalForAllUnchecked(a, b, False, from_=c) - assert tx.events == [ERC1155Mock.ApprovalForAll(a.address, b.address, False)] - - erc1155.batchMint(a, [0, 1, 2], [100, 200, 300], b"", from_=a) - tx = erc1155.burnUnchecked(Address.ZERO, a, 2, 100, from_=c) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, Address.ZERO, [2], [100], bytearray(b"")), - ERC1155Mock.TransferSingle(c.address, a.address, Address.ZERO, 2, 100), - ERC1155Mock.AfterTokenTransfer(a.address, Address.ZERO, [2], [100], bytearray(b"")), - ] - - tx = erc1155.batchBurnUnchecked(Address.ZERO, a, [0, 1, 2], [100, 100, 100], from_=c) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, Address.ZERO, [0, 1, 2], [100, 100, 100], bytearray(b"")), - ERC1155Mock.TransferBatch(c.address, a.address, Address.ZERO, [0, 1, 2], [100, 100, 100]), - ERC1155Mock.AfterTokenTransfer(a.address, Address.ZERO, [0, 1, 2], [100, 100, 100], bytearray(b"")), - ] - - tx = erc1155.safeTransferUnchecked(Address.ZERO, a, b, 1, 50, b"\x11", from_=c) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, b.address, [1], [50], bytearray(b"\x11")), - ERC1155Mock.TransferSingle(c.address, a.address, b.address, 1, 50), - ERC1155Mock.AfterTokenTransfer(a.address, b.address, [1], [50], bytearray(b"\x11")), - ] - - tx = erc1155.safeBatchTransferUnchecked(Address.ZERO, a, b, [0, 1, 2], [0, 50, 0], b"\x22", from_=c) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(a.address, b.address, [0, 1, 2], [0, 50, 0], bytearray(b"\x22")), - ERC1155Mock.TransferBatch(c.address, a.address, b.address, [0, 1, 2], [0, 50, 0]), - ERC1155Mock.AfterTokenTransfer(a.address, b.address, [0, 1, 2], [0, 50, 0], bytearray(b"\x22")), - ] - - -@default_chain.connect() -def test_erc1155_mint_burn(): - a = default_chain.accounts[0] - b = default_chain.accounts[1] - - erc1155 = ERC1155Mock.deploy(False, from_=a) - - assert erc1155.balanceOfBatch([], []) == [] - - erc1155.mint(b, 0, 100, b"", from_=a) - assert erc1155.balanceOf(b, 0) == 100 - - erc1155.burn(b, 0, 50, from_=b) - assert erc1155.balanceOf(b, 0) == 50 - - # b is not owner nor approved - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.burn(b, 0, 50, from_=a) - - # insufficient balance - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.burn(b, 0, 51, from_=b) - - erc1155.burn(b, 0, 50, from_=b) - assert erc1155.balanceOf(b, 0) == 0 - - # mint to zero address - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.mint(Address.ZERO, 0, 100, b"", from_=a) - - # balance overflow - erc1155.mint(b, 0, 2 ** 256 - 1, b"", from_=a) - assert erc1155.balanceOf(b, 0) == 2 ** 256 - 1 - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.mint(b, 0, 1, b"", from_=a) - erc1155.burn(b, 0, 2 ** 256 - 1, from_=b) - - # ids and amounts length mismatch - with must_revert(ERC1155Mock.ArrayLengthsMismatch): - erc1155.batchMint(b, [0, 1], [100], b"", from_=a) - - # ids and amounts length mismatch - with must_revert(ERC1155Mock.ArrayLengthsMismatch): - erc1155.batchBurn(b, [0, 1], [100], from_=a) - - # mint to zero address - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.batchMint(Address.ZERO, [0, 1], [100, 200], b"", from_=a) - - # balance overflow - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.batchMint(b, [0, 1, 0], [2 ** 256 - 1, 1, 1], b"", from_=a) - - # not owner nor approved - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.batchBurn(b, [0, 1, 0], [1, 1, 1], from_=a) - - # insufficient balance - erc1155.mint(b, 0, 100, b"", from_=a) - erc1155.mint(b, 1, 1, b"", from_=a) - assert erc1155.balanceOf(b, 0) == 100 - assert erc1155.balanceOf(b, 1) == 1 - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.batchBurn(b, [0, 1, 0], [70, 1, 31], from_=b) - erc1155.burn(b, 0, 100, from_=b) - erc1155.burn(b, 1, 1, from_=b) - assert erc1155.balanceOf(b, 0) == 0 - assert erc1155.balanceOf(b, 1) == 0 - - -@default_chain.connect() -def test_erc1155_transfers(): - a = default_chain.accounts[0] - b = default_chain.accounts[1] - - erc1155 = ERC1155Mock.deploy(False, from_=a) - - erc1155.mint(a, 0, 100, b"", from_=a) - erc1155.mint(a, 1, 100, b"", from_=a) - assert erc1155.balanceOf(a, 0) == 100 - assert erc1155.balanceOf(a, 1) == 100 - - erc1155.safeTransferFrom(a, b, 0, 50, b"", from_=a) - assert erc1155.balanceOf(a, 0) == 50 - assert erc1155.balanceOf(a, 1) == 100 - assert erc1155.balanceOf(b, 0) == 50 - assert erc1155.balanceOfBatch([a, a, b, a], [0, 1, 0, 2]) == [50, 100, 50, 0] - - erc1155.safeBatchTransferFrom(a, b, [], [], b"", from_=a) - assert erc1155.balanceOfBatch([a, a, b, a], [0, 1, 0, 2]) == [50, 100, 50, 0] - - # owners and ids length mismatch - with must_revert(ERC1155Mock.ArrayLengthsMismatch): - assert erc1155.balanceOfBatch([a, a, b], [0, 1, 0, 2]) == [50, 100, 50, 0] - - # not owner nor approved - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.safeTransferFrom(a, b, 0, 50, b"", from_=b) - - # insufficient balance - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeTransferFrom(a, b, 0, 51, b"", from_=a) - - # transfer to zero address - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeTransferFrom(a, Address.ZERO, 0, 50, b"", from_=a) - - # transfer to self - erc1155.safeTransferFrom(a, a, 0, 50, b"", from_=a) - assert erc1155.balanceOf(a, 0) == 50 - - # balance overflow - erc1155.mint(a, 0, 2 ** 256 - 1 - 50, b"", from_=a) - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeTransferFrom(a, b, 0, 2 ** 256 - 1 - 49, b"", from_=a) - - # transfer to non-erc1155 receiver - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeTransferFrom(a, erc1155, 0, 50, b"", from_=a) - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeBatchTransferFrom(a, erc1155, [0], [50], b"", from_=a) - - # clear balances - c = default_chain.accounts[2] - assert erc1155.isApprovedForAll(a, c) is False - erc1155.batchBurnUnchecked(Address.ZERO, a, [0, 1], [2 ** 256 - 1, 100], from_=c) - erc1155.burnUnchecked(Address.ZERO, b, 0, 50, from_=c) - - erc1155.batchMint(a, [0, 1], [100, 100], b"", from_=a) - erc1155.safeBatchTransferFrom(a, b, [0, 1], [70, 30], b"", from_=a) - assert erc1155.balanceOfBatch([a, a, b, b], [0, 1, 0, 1]) == [30, 70, 70, 30] - - with must_revert(ERC1155Mock.ArrayLengthsMismatch()): - erc1155.safeBatchTransferFrom(a, b, [0, 1], [30], b"", from_=a) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeBatchTransferFrom(a, Address.ZERO, [0, 1], [30, 30], b"", from_=a) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.safeBatchTransferFrom(a, b, [0, 1], [30, 30], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeBatchTransferFrom(a, b, [0, 1], [31, 30], b"", from_=a) - - erc1155.mint(a, 0, 2 ** 256 - 1 - 30, b"", from_=a) - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeBatchTransferFrom(a, b, [0, 1], [2 ** 256 - 1 - 29, 30], b"", from_=a) - - -@default_chain.connect() -def test_erc1155_contract_receiver(): - a = default_chain.accounts[0] - - erc1155 = ERC1155Mock.deploy(False, from_=a) - receiver = ERC1155ReceiverMock.deploy(from_=a) - - erc1155.mint(receiver, 0, 100, b"\x00\x11\x22\x33", from_=a) - assert erc1155.balanceOf(receiver, 0) == 100 - - with must_revert(Error("ERC1155ReceiverMock: invalid payload received")): - erc1155.mint(receiver, 0, 100, b"", from_=a) - - erc1155.mint(a, 1, 100, b"", from_=a) - erc1155.safeTransferFrom(a, receiver, 1, 50, b"\x00\x11\x22\x33", from_=a) - assert erc1155.balanceOfBatch([receiver, receiver], [0, 1]) == [100, 50] - - with must_revert(Error("ERC1155ReceiverMock: invalid payload received")): - erc1155.safeTransferFrom(a, receiver, 1, 50, b"\x00\x11\x22", from_=a) - - erc1155.safeBatchTransferFrom(a, receiver, [0, 1], [0, 50], b"\x00\x11\x22\x33", from_=a) - assert erc1155.balanceOfBatch([receiver, receiver], [0, 1]) == [100, 100] - - with must_revert(Error("ERC1155ReceiverMock: invalid payload received")): - erc1155.safeBatchTransferFrom(a, receiver, [0, 1], [0, 0], b"\x11\x22\x33", from_=a) - - erc1155.batchMint(receiver, [0, 1], [100, 100], b"\x00\x11\x22\x33", from_=a) - assert erc1155.balanceOfBatch([receiver, receiver], [0, 1]) == [200, 200] - - with must_revert(Error("ERC1155ReceiverMock: invalid payload received")): - erc1155.batchMint(receiver, [0, 1], [100, 100], b"", from_=a) - - -@default_chain.connect() -def test_erc1155_unchecked(): - a = default_chain.accounts[0] - b = default_chain.accounts[1] - c = default_chain.accounts[2] - default_chain.set_default_accounts(c) - - erc1155 = ERC1155Mock.deploy(False, from_=a) - - erc1155.mint(a, 0, 100, b"", from_=c) - assert erc1155.balanceOf(a, 0) == 100 - - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, b, [0], [100], b"", from_=c) - assert erc1155.balanceOf(a, 0) == 0 - assert erc1155.balanceOf(b, 0) == 100 - - erc1155.setApprovalForAllUnchecked(b, a, True, from_=c) - erc1155.safeTransferUnchecked(a, b, a, 0, 100, b"", from_=c) - assert erc1155.balanceOf(a, 0) == 100 - assert erc1155.balanceOf(b, 0) == 0 - - erc1155.setApprovalForAllUnchecked(a, b, True, from_=c) - erc1155.burnUnchecked(b, a, 0, 100, from_=c) - assert erc1155.balanceOf(a, 0) == 0 - assert erc1155.balanceOf(b, 0) == 0 - - erc1155.batchMint(a, [0, 1], [100, 100], b"", from_=c) - erc1155.batchBurnUnchecked(b, a, [0, 1], [100, 100], from_=c) - assert erc1155.balanceOf(a, 0) == 0 - assert erc1155.balanceOf(a, 1) == 0 - - erc1155.setApprovalForAllUnchecked(a, b, False, from_=c) - erc1155.setApprovalForAllUnchecked(b, a, False, from_=c) - erc1155.mint(a, 0, 100, b"", from_=c) - assert erc1155.balanceOf(a, 0) == 100 - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.safeTransferUnchecked(b, a, b, 0, 100, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.NotOwnerNorApproved.selector)): - erc1155.safeBatchTransferUnchecked(b, a, b, [0], [100], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeTransferUnchecked(Address.ZERO, a, Address.ZERO, 0, 100, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeTransferUnchecked(a, a, Address.ZERO, 0, 100, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, Address.ZERO, [0], [100], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.TransferToZeroAddress.selector)): - erc1155.safeBatchTransferUnchecked(a, a, Address.ZERO, [0], [100], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeTransferUnchecked(Address.ZERO, a, b, 0, 101, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeTransferUnchecked(a, a, b, 0, 101, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, b, [0], [101], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.InsufficientBalance.selector)): - erc1155.safeBatchTransferUnchecked(a, a, b, [0], [101], b"", from_=c) - - erc1155.mint(b, 0, 2 ** 256 - 10, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeTransferUnchecked(Address.ZERO, a, b, 0, 100, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeTransferUnchecked(a, a, b, 0, 100, b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, b, [0, 0], [9, 21], b"", from_=c) - - with must_revert(UnknownTransactionRevertedError(ERC1155Mock.AccountBalanceOverflow.selector)): - erc1155.safeBatchTransferUnchecked(a, a, b, [0, 0], [9, 21], b"", from_=c) - - with must_revert(ERC1155Mock.ArrayLengthsMismatch): - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, b, [0], [100, 100], b"", from_=c) - - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeTransferUnchecked(Address.ZERO, a, erc1155, 0, 100, b"", from_=c) - - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeTransferUnchecked(a, a, erc1155, 0, 100, b"", from_=c) - - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeBatchTransferUnchecked(Address.ZERO, a, erc1155, [0], [100], b"", from_=c) - - with must_revert(ERC1155Mock.TransferToNonERC1155ReceiverImplementer): - erc1155.safeBatchTransferUnchecked(a, a, erc1155, [0], [100], b"", from_=c) diff --git a/lib/solady/ext/woke/test_erc1155_fuzz.py b/lib/solady/ext/woke/test_erc1155_fuzz.py deleted file mode 100644 index f85fbf8..0000000 --- a/lib/solady/ext/woke/test_erc1155_fuzz.py +++ /dev/null @@ -1,537 +0,0 @@ -import logging -from collections import defaultdict -import random -from typing import DefaultDict, Set - -from woke.testing import * -from woke.testing.fuzzing import * -from pytypes.tests.ERC1155Mock import ERC1155Mock - - -logger = logging.getLogger(__name__) -#logger.setLevel(logging.DEBUG) - - -class ERC1155FuzzTest(FuzzTest): - _erc1155: ERC1155Mock - _balances: DefaultDict[Account, DefaultDict[uint256, uint256]] - _approvals: DefaultDict[Account, Set[Account]] - _token_ids: List[uint256] - - def pre_sequence(self) -> None: - self._erc1155 = ERC1155Mock.deploy(True) - self._balances = defaultdict(lambda: defaultdict(lambda: 0)) - self._approvals = defaultdict(set) - self._token_ids = [random_int(0, 2 ** 256 - 1, edge_values_prob=0.25) for _ in range(10)] - - @flow() - def flow_mint(self, payload: bytearray) -> None: - a = random_account() - id = random.choice(self._token_ids) - amount = random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) - - try: - tx = self._erc1155.mint(a, id, amount, payload) - assert self._balances[a][id] + amount < 2 ** 256 - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(Address.ZERO, a.address, [id], [amount], payload), - ERC1155Mock.TransferSingle(tx.from_.address, Address.ZERO, a.address, id, amount), - ERC1155Mock.AfterTokenTransfer(Address.ZERO, a.address, [id], [amount], payload), - ] - self._balances[a][id] += amount - - logger.debug(f"Minted {amount} of {id} to {a}") - except UnknownTransactionRevertedError as e: - assert e.data == ERC1155Mock.AccountBalanceOverflow.selector - assert self._balances[a][id] + amount >= 2 ** 256 - - logger.debug(f"Failed to mint {amount} of {id} to {a}") - - @flow() - def flow_batch_mint(self, payload: bytearray) -> None: - a = random_account() - ids = [random.choice(self._token_ids) for _ in range(random_int(0, 10, edge_values_prob=0.05))] - amounts = [random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) for _ in range(len(ids))] - - try: - tx = self._erc1155.batchMint(a, ids, amounts, payload) - for id, amount in zip(ids, amounts): - assert self._balances[a][id] + amount < 2 ** 256 - self._balances[a][id] += amount - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(Address.ZERO, a.address, ids, amounts, payload), - ERC1155Mock.TransferBatch(tx.from_.address, Address.ZERO, a.address, ids, amounts), - ERC1155Mock.AfterTokenTransfer(Address.ZERO, a.address, ids, amounts, payload), - ] - - logger.debug(f"Minted {amounts} of {ids} to {a}") - except UnknownTransactionRevertedError as e: - assert e.data == ERC1155Mock.AccountBalanceOverflow.selector - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[a][id] + amount >= 2 ** 256 for id, amount in amounts_by_ids.items()) - - logger.debug(f"Failed to mint {amounts} of {ids} to {a}") - - @flow() - def flow_burn(self) -> None: - owner = random_account() - - if random.random() < 0.8 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - else: - id = random.choice(self._token_ids) - - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), min_prob=0.05, max_prob=0.01) - - operator = random.choices( - default_chain.accounts, - [0.5 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] - )[0] - - try: - tx = self._erc1155.burn(owner, id, amount, from_=operator) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, Address.ZERO, [id], [amount], bytearray()), - ERC1155Mock.TransferSingle(operator.address, owner.address, Address.ZERO, id, amount), - ERC1155Mock.AfterTokenTransfer(owner.address, Address.ZERO, [id], [amount], bytearray()), - ] - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - - assert operator == owner or operator in self._approvals[owner] - - logger.debug(f"Burned {amount} of {id} from {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - assert self._balances[owner][id] - amount < 0 - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to burn {amount} of {id} from {owner}") - - @flow() - def flow_burn_batch(self) -> None: - owner = random_account() - - ids = [] - amounts = [] - for _ in range(random_int(0, 10, edge_values_prob=0.05)): - if random.random() < 0.98 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - ids.append(id) - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), edge_values_prob=0.05) - amounts.append(amount) - else: - id = random.choice(self._token_ids) - ids.append(id) - amount = random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) - amounts.append(amount) - - operator = random.choices( - default_chain.accounts, - [0.5 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] - )[0] - - try: - tx = self._erc1155.batchBurn(owner, ids, amounts, from_=operator) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, Address.ZERO, ids, amounts, bytearray()), - ERC1155Mock.TransferBatch(operator.address, owner.address, Address.ZERO, ids, amounts), - ERC1155Mock.AfterTokenTransfer(owner.address, Address.ZERO, ids, amounts, bytearray()), - ] - for id, amount in zip(ids, amounts): - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - - assert operator == owner or operator in self._approvals[owner] - - logger.debug(f"Burned {amounts} of {ids} from {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] - amount < 0 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to burn {amounts} of {ids} from {owner}") - - @flow() - def flow_burn_unchecked(self) -> None: - owner = random_account() - - if random.random() < 0.8 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - else: - id = random.choice(self._token_ids) - - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), min_prob=0.05, max_prob=0.01) - - operator = random.choices( - default_chain.accounts + (Account(0), ), - [0.25 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] + [0.25] - )[0] - executor = random_account() - - try: - tx = self._erc1155.burnUnchecked(operator, owner, id, amount, from_=executor) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, Address.ZERO, [id], [amount], bytearray()), - ERC1155Mock.TransferSingle(executor.address, owner.address, Address.ZERO, id, amount), - ERC1155Mock.AfterTokenTransfer(owner.address, Address.ZERO, [id], [amount], bytearray()), - ] - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - - assert operator == owner or operator == Account(0) or operator in self._approvals[owner] - - logger.debug(f"Burned {amount} of {id} from {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - assert self._balances[owner][id] - amount < 0 - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator != Account(0) and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to burn {amount} of {id} from {owner}") - - @flow() - def flow_burn_batch_unchecked(self): - owner = random_account() - - ids = [] - amounts = [] - for _ in range(random_int(0, 10, edge_values_prob=0.05)): - if random.random() < 0.98 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - ids.append(id) - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), edge_values_prob=0.05) - amounts.append(amount) - else: - id = random.choice(self._token_ids) - ids.append(id) - amount = random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) - amounts.append(amount) - - operator = random.choices( - default_chain.accounts + (Account(0), ), - [0.25 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] + [0.25] - )[0] - executor = random_account() - - try: - tx = self._erc1155.batchBurnUnchecked(operator, owner, ids, amounts, from_=executor) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, Address.ZERO, ids, amounts, bytearray()), - ERC1155Mock.TransferBatch(executor.address, owner.address, Address.ZERO, ids, amounts), - ERC1155Mock.AfterTokenTransfer(owner.address, Address.ZERO, ids, amounts, bytearray()), - ] - for id, amount in zip(ids, amounts): - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - - assert operator == owner or operator == Account(0) or operator in self._approvals[owner] - - logger.debug(f"Burned {amounts} of {ids} from {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] - amount < 0 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator != Account(0) and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to burn {amounts} of {ids} from {owner}") - - @flow() - def flow_change_approval(self) -> None: - a = random_account() - operator = random_account() - approval = operator in self._approvals[a] - - tx = self._erc1155.setApprovalForAll(operator, not approval, from_=a) - assert tx.events == [ - ERC1155Mock.ApprovalForAll(a.address, operator.address, not approval), - ] - - if approval: - self._approvals[a].remove(operator) - else: - self._approvals[a].add(operator) - - logger.debug(f"Changed approval of {operator} for {a} to {not approval}") - - @flow() - def flow_change_approval_unchecked(self) -> None: - owner = random_account() - operator = random_account() - executor = random_account() - approval = operator in self._approvals[owner] - - tx = self._erc1155.setApprovalForAllUnchecked(owner, operator, not approval, from_=executor) - assert tx.events == [ - ERC1155Mock.ApprovalForAll(owner.address, operator.address, not approval), - ] - - if approval: - self._approvals[owner].remove(operator) - else: - self._approvals[owner].add(operator) - - logger.debug(f"Changed approval of {operator} for {owner} to {not approval}") - - @flow() - def flow_safe_transfer(self, payload: bytearray): - owner = random_account() - recipient = random_account() - - if random.random() < 0.8 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - else: - id = random.choice(self._token_ids) - - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), min_prob=0.05, max_prob=0.01) - - operator = random.choices( - default_chain.accounts, - [0.5 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] - )[0] - - try: - tx = self._erc1155.safeTransferFrom(owner, recipient, id, amount, payload, from_=operator) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, recipient.address, [id], [amount], payload), - ERC1155Mock.TransferSingle(operator.address, owner.address, recipient.address, id, amount), - ERC1155Mock.AfterTokenTransfer(owner.address, recipient.address, [id], [amount], payload), - ] - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - assert self._balances[recipient][id] + amount <= 2 ** 256 - 1 - self._balances[recipient][id] += amount - - assert operator == owner or operator in self._approvals[owner] - - logger.debug(f"Transferred {amount} of {id} from {owner} to {recipient}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - assert self._balances[owner][id] - amount < 0 - elif e.data == ERC1155Mock.AccountBalanceOverflow.selector: - assert self._balances[recipient][id] + amount > 2 ** 256 - 1 - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to transfer {amount} of {id} from {owner} to {recipient}") - - @flow() - def flow_safe_batch_transfer(self, payload: bytearray) -> None: - owner = random_account() - ids = [] - amounts = [] - for _ in range(random_int(0, 10, edge_values_prob=0.05)): - if random.random() < 0.98 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - ids.append(id) - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), edge_values_prob=0.05) - amounts.append(amount) - else: - id = random.choice(self._token_ids) - ids.append(id) - amount = random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) - amounts.append(amount) - - operator = random.choices( - default_chain.accounts, - [0.5 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] - )[0] - - try: - tx = self._erc1155.safeBatchTransferFrom(owner, owner, ids, amounts, payload, from_=operator) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, owner.address, ids, amounts, payload), - ERC1155Mock.TransferBatch(operator.address, owner.address, owner.address, ids, amounts), - ERC1155Mock.AfterTokenTransfer(owner.address, owner.address, ids, amounts, payload), - ] - for id, amount in zip(ids, amounts): - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - assert self._balances[owner][id] + amount <= 2 ** 256 - 1 - self._balances[owner][id] += amount - - assert operator == owner or operator in self._approvals[owner] - - logger.debug(f"Transferred {amounts} of {ids} from {owner} to {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] - amount < 0 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.AccountBalanceOverflow.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] + amount > 2 ** 256 - 1 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to transfer {amounts} of {ids} from {owner} to {owner}") - - @flow() - def flow_safe_transfer_unchecked(self, payload: bytearray) -> None: - owner = random_account() - recipient = random_account() - - if random.random() < 0.8 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - else: - id = random.choice(self._token_ids) - - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), min_prob=0.05, max_prob=0.01) - - operator = random.choices( - default_chain.accounts + (Account(0), ), - [0.25 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] + [0.25] - )[0] - executor = random_account() - - try: - tx = self._erc1155.safeTransferUnchecked(operator, owner, recipient, id, amount, payload, from_=executor) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, recipient.address, [id], [amount], payload), - ERC1155Mock.TransferSingle(executor.address, owner.address, recipient.address, id, amount), - ERC1155Mock.AfterTokenTransfer(owner.address, recipient.address, [id], [amount], payload), - ] - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - assert self._balances[recipient][id] + amount <= 2 ** 256 - 1 - self._balances[recipient][id] += amount - - assert operator == owner or operator == Account(0) or operator in self._approvals[owner] - - logger.debug(f"Transferred {amount} of {id} from {owner} to {recipient}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - assert self._balances[owner][id] - amount < 0 - elif e.data == ERC1155Mock.AccountBalanceOverflow.selector: - assert self._balances[recipient][id] + amount > 2 ** 256 - 1 - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator != Account(0) and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to transfer {amount} of {id} from {owner} to {recipient}") - - @flow() - def flow_safe_batch_transfer_unchecked(self, payload: bytearray) -> None: - owner = random_account() - ids = [] - amounts = [] - for _ in range(random_int(0, 10, edge_values_prob=0.05)): - if random.random() < 0.98 and sum(self._balances[owner].values()) > 0: - id = random.choice([k for k in self._balances[owner].keys() if self._balances[owner][k] > 0]) - ids.append(id) - if self._balances[owner][id] == 0: - amount = random.choice([0, 1]) - else: - amount = random_int(0, min(self._balances[owner][id] + 1, 2 ** 256 - 1), edge_values_prob=0.05) - amounts.append(amount) - else: - id = random.choice(self._token_ids) - ids.append(id) - amount = random_int(0, 2 ** 256 - 1, edge_values_prob=0.05) - amounts.append(amount) - - operator = random.choices( - default_chain.accounts + (Account(0), ), - [0.25 if a == owner else 0.5 / (len(default_chain.accounts) - 1) for a in default_chain.accounts] + [0.25] - )[0] - executor = random_account() - - try: - tx = self._erc1155.safeBatchTransferUnchecked(operator, owner, owner, ids, amounts, payload, from_=executor) - assert tx.events == [ - ERC1155Mock.BeforeTokenTransfer(owner.address, owner.address, ids, amounts, payload), - ERC1155Mock.TransferBatch(executor.address, owner.address, owner.address, ids, amounts), - ERC1155Mock.AfterTokenTransfer(owner.address, owner.address, ids, amounts, payload), - ] - for id, amount in zip(ids, amounts): - assert self._balances[owner][id] - amount >= 0 - self._balances[owner][id] -= amount - assert self._balances[owner][id] + amount <= 2 ** 256 - 1 - self._balances[owner][id] += amount - - assert operator == owner or operator == Account(0) or operator in self._approvals[owner] - - logger.debug(f"Transferred {amounts} of {ids} from {owner} to {owner}") - except UnknownTransactionRevertedError as e: - if e.data == ERC1155Mock.InsufficientBalance.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] - amount < 0 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.AccountBalanceOverflow.selector: - amounts_by_ids = defaultdict(int) - for id, amount in zip(ids, amounts): - amounts_by_ids[id] += amount - assert any(self._balances[owner][id] + amount > 2 ** 256 - 1 for id, amount in amounts_by_ids.items()) - elif e.data == ERC1155Mock.NotOwnerNorApproved.selector: - assert operator != owner and operator != Account(0) and operator not in self._approvals[owner] - else: - raise - - logger.debug(f"Failed to transfer {amounts} of {ids} from {owner} to {owner}") - - @invariant(period=20) - def invariant_balances(self) -> None: - for a, balances in self._balances.items(): - assert self._erc1155.balanceOfBatch([a] * len(balances), list(balances.keys())) == list(balances.values()) - for id, balance in balances.items(): - assert self._erc1155.balanceOf(a, id) == balance - - @invariant(period=20) - def invariant_approvals(self) -> None: - for a in default_chain.accounts: - for b in default_chain.accounts: - assert self._erc1155.isApprovedForAll(a, b) == (b in self._approvals[a]) - - -@default_chain.connect(accounts=20) -def test_erc1155_fuzz(): - default_chain.set_default_accounts(default_chain.accounts[0]) - ERC1155FuzzTest().run(1, 100) diff --git a/lib/solady/ext/woke/test_erc20.py b/lib/solady/ext/woke/test_erc20.py deleted file mode 100644 index 94fb8cd..0000000 --- a/lib/solady/ext/woke/test_erc20.py +++ /dev/null @@ -1,242 +0,0 @@ -from woke.testing import * - -from pytypes.tests.ERC20Mock import ERC20Mock -from pytypes.tests.NoETHMock import NoETHMock -from pytypes.tests.weird.Approval import ApprovalRaceToken -from pytypes.tests.weird.ApprovalToZero import ApprovalToZeroToken -from pytypes.tests.weird.BlockList import BlockableToken -from pytypes.tests.weird.HighDecimals import HighDecimalToken -from pytypes.tests.weird.Bytes32Metadata import ERC20 as Bytes32MetadataToken -from pytypes.tests.weird.MissingReturns import MissingReturnToken -from pytypes.tests.weird.NoRevert import NoRevertToken -from pytypes.tests.weird.Pausable import PausableToken -from pytypes.tests.weird.Proxied import ProxiedToken, TokenProxy -from pytypes.tests.weird.Reentrant import ReentrantToken -from pytypes.tests.weird.ReturnsFalse import ReturnsFalseToken -from pytypes.tests.weird.TransferFee import TransferFeeToken -from pytypes.tests.weird.Uint96 import Uint96ERC20 -from pytypes.tests.weird.Upgradable import Proxy as UpgradableToken - -from pytypes.src.utils.SafeTransferLib import SafeTransferLib - - -@default_chain.connect() -def test_erc20(): - milady = default_chain.accounts[0] - accountoor = default_chain.accounts[1] - default_chain.set_default_accounts(milady) - - tokenoor = ERC20Mock.deploy("Mockoor", "MOCK", 18) - - tokenoor.mint(milady, 2**30) - assert tokenoor.balanceOf(milady) == 2**30 - - tokenoor.approve(accountoor, 2**30) - assert tokenoor.allowance(milady, accountoor) == 2**30 - - tokenoor.transferFrom(milady, accountoor, 2**30, from_=accountoor) - assert tokenoor.allowance(milady, accountoor) == 0 - - assert tokenoor.balanceOf(milady) == 0 - assert tokenoor.balanceOf(accountoor) == 2**30 - -@default_chain.connect() -def test_safe_transfer_eth(): - milady = default_chain.accounts[0] - accountoor = default_chain.accounts[1] - default_chain.set_default_accounts(milady) - - SafeTransferLib.deploy() - - tokenoor = ERC20Mock.deploy("Mockoor", "MOCK", 18) - tokenoor.balance = 5000 - accountoor.balance = 0 - - # should change balance - tokenoor.safeTransferETH(accountoor, 1000) - assert tokenoor.balance == 4000 - assert accountoor.balance == 1000 - - noeth = NoETHMock.deploy() - - # should revert - with must_revert(): - tokenoor.safeTransferETH(noeth, 1000) - - # should change balance - tokenoor.forceSafeTransferETH(noeth, 1000) - assert tokenoor.balance == 3000 - assert noeth.balance == 1000 - - # should force on bad gas stipend - tokenoor.forceSafeTransferETHGas(accountoor, 1000, 0) - assert tokenoor.balance == 2000 - assert accountoor.balance == 2000 - - # should change balance - tokenoor.trySafeTransferETH(accountoor, 1000, 0) - assert tokenoor.balance == 1000 - assert accountoor.balance == 3000 - - # should not revert - tokenoor.trySafeTransferETH(noeth, 1000, 0) - assert tokenoor.balance == 1000 - assert noeth.balance == 1000 - -@default_chain.connect() -def test_safe_transfer(): - milady = default_chain.accounts[0] - default_chain.set_default_accounts(milady) - - SafeTransferLib.deploy() - - tokenoor = ERC20Mock.deploy("Mockoor", "MOCK", 18) - tokenoor.mint(tokenoor, 2**30) - - tokenoor.safeTransfer(tokenoor, milady, 2**30) - assert tokenoor.balanceOf(milady) == 2**30 - assert tokenoor.balanceOf(tokenoor) == 0 - - tokenoor.approve(tokenoor, 2**30) - assert tokenoor.allowance(milady, tokenoor) == 2**30 - tokenoor.safeTransferFrom(tokenoor, milady, tokenoor, 2**30) - assert tokenoor.balanceOf(milady) == 0 - assert tokenoor.balanceOf(tokenoor) == 2**30 - - tokenoor.mint(tokenoor, 2**30) - tokenoor.safeTransferAll(tokenoor, milady) - assert tokenoor.balanceOf(milady) == 2**30 * 2 - assert tokenoor.balanceOf(tokenoor) == 0 - - # test safe balanceOf - assert tokenoor.balanceOfoor(milady, milady) == 0 - assert tokenoor.balanceOfoor(tokenoor, milady) == 2**30 * 2 - -def safe_transfer_weird(weird: Account): - milady = default_chain.accounts[0] - weird = ERC20Mock(weird) - - SafeTransferLib.deploy() - - tokenoor = ERC20Mock.deploy("Mockoor", "MOCK", 18) - - weird.mint(tokenoor, 2**30) - tokenoor.safeTransfer(weird, milady, 2**30) - assert weird.balanceOf(milady) == 2**30 - assert weird.balanceOf(tokenoor) == 0 - - weird.mint(tokenoor, 2**30) - tokenoor.safeTransferAll(weird, milady) - assert weird.balanceOf(milady) == 2**30 * 2 - assert weird.balanceOf(tokenoor) == 0 - - weird.mint(tokenoor, 2**30) - tokenoor.safeTransferFrom(weird, tokenoor, milady, 2**30) - assert weird.balanceOf(milady) == 2**30 * 3 - assert weird.balanceOf(tokenoor) == 0 - - weird.approve(tokenoor, 2**30) - assert weird.allowance(milady, tokenoor) == 2**30 - tokenoor.safeTransferFrom(weird, milady, tokenoor, 2**30) - assert weird.balanceOf(milady) == 2**30 * 2 - assert weird.balanceOf(tokenoor) == 2**30 - - # test safe balanceOf - assert tokenoor.balanceOfoor(milady, milady) == 0 - assert tokenoor.balanceOfoor(weird, milady) == 2**30 * 2 - -@default_chain.connect() -def test_safe_transfer_weird_1(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = ApprovalRaceToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_2(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = ApprovalToZeroToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_3(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = BlockableToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_4(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = HighDecimalToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_5(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = Bytes32MetadataToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_6(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = MissingReturnToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_7(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = NoRevertToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_8(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = PausableToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_9(): - default_chain.set_default_accounts(default_chain.accounts[0]) - impl = ProxiedToken.deploy(0) - weird_proxy = TokenProxy.deploy(impl) - weird = ProxiedToken(weird_proxy) - weird.setDelegator(weird_proxy, True) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_10(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = ReturnsFalseToken.deploy(0) - with must_revert(): - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_11(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = ReentrantToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_12(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = TransferFeeToken.deploy(0,0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_13(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = Uint96ERC20.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_safe_transfer_weird_14(): - default_chain.set_default_accounts(default_chain.accounts[0]) - weird = UpgradableToken.deploy(0) - safe_transfer_weird(weird) - -@default_chain.connect() -def test_mint_to_zero_address(): - milady = default_chain.accounts[0] - default_chain.set_default_accounts(milady) - tokenoor = ERC20Mock.deploy("Mockoor", "MOCK", 18) - tokenoor.mint(Address(0), 2**256-1) - assert tokenoor.balanceOf(Address(0)) == 2**256-1 \ No newline at end of file diff --git a/lib/solady/ext/woke/test_erc721_fuzz.py b/lib/solady/ext/woke/test_erc721_fuzz.py deleted file mode 100644 index fb29f77..0000000 --- a/lib/solady/ext/woke/test_erc721_fuzz.py +++ /dev/null @@ -1,365 +0,0 @@ -import random - -from woke.testing.fuzzing import * -from woke.testing import * - -from pytypes.tests.ERC721Mock import ERC721Mock - - -################################################################### -####################### PYTHON ERC721 MODEL ####################### -################################################################### -class ERC721: - # mapping owner -> token id - owners: dict[int, Address] - # mapping owner -> token count - balances: dict[Address, int] - # mapping token id -> approved address - approvals: dict[int, Address] - # mapping operator approval owner -> operator - operators: dict[Address, Address] - def __init__(self): - self.owners = {} - self.balances = {} - self.approvals = {} - self.operators = {} - - def balance_of(self, _owner: Address): - return self.balances[_owner] - - def owner_of(self, _token_id: uint): - return self.owners[_token_id] - - def safe_transfer_from(_from: Address, _to: Address, _token_id: uint, _data: bytes): - return - - def safe_transfer_from(_from: Address, _to: Address, _token_id: uint): - return - - def transfer_from(self,_by: Address, _from: Address, _to: Address, _token_id: uint): - self.transfer(_by, _from, _to, _token_id) - - def transfer(self, _by: Address, _from: Address, _to: Address, _token_id: uint): - if self.owners[_token_id] != _by and self.approvals[_token_id] != _by and self.operators[_from] != _by: - return - self.balances[_from] -= 1 - if _to in self.balances.keys(): - self.balances[_to] += 1 - else: - self.balances[_to] = 1 - - self.owners[_token_id] = _to - if _token_id in self.approvals.keys(): - del self.approvals[_token_id] - - def approve(self, _approved: Address, _token_id: uint): - if _approved == Address(0): - del self.approvals[_token_id] - self.approvals[_token_id] = _approved - - def set_approval_for_all(self, _operator: Address, _owner: Address): - self.operators[_owner] = _operator - - def get_approved(self, _token_id: uint): - return self.approvals[_token_id] - - def is_approve_for_all(self, _owner: Address, _operator: Address): - if self.operators[_owner]: - return True - return False - - def mint(self, _to: Address, _token_id: int): - self.owners[_token_id] = _to - if _to in self.balances.keys(): - self.balances[_to] += 1 - else: - self.balances[_to] = 1 - - def burn(self,_token_id: int): - if _token_id in self.owners.keys(): - self.balances[self.owner_of(_token_id)] -= 1 - del self.owners[_token_id] - if _token_id in self.approvals.keys(): - del self.approvals[_token_id] - - -################################################################### -########################### Fuzz Test ############################# -################################################################### - -class ERC721FuzzTest(FuzzTest): - _erc721: ERC721Mock - _py_erc721: ERC721 - _id_counter: int - _ids: List[int] - # We dont want to use random addresses in flows - # We want more interaction by addresses that are already managing something - _addresses: List[Address] - - def pre_sequence(self) -> None: - self._erc721 = ERC721Mock.deploy() - self._py_erc721 = ERC721() - self._addresses = [] - for i in range(20): - self._addresses.append(random_address()) - - ######################## MINT ######################## - @flow(weight=100) - def mint(self) -> None: - # Random data - to = random.choice(self._addresses) - token_id = random_int(0,(2**256)-1) - # Mint in contract - tx = self._erc721.mint(to, token_id) - # Check events - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(Address(0), to, token_id), - ERC721Mock.Transfer(Address(0), to, token_id), - ERC721Mock.AfterTokenTransfer(Address(0), to, token_id) - ] - # Mint in Py model - self._py_erc721.mint(to, token_id) - - ######################## BURNS ######################## - @flow(weight=50) - def burn_owner(self) -> None: - if self._py_erc721.owners: - # Random token with owner - token_id, owner = random.choice(list(self._py_erc721.owners.items())) - # Burn in contract, msg.sender == owner - tx = self._erc721.burn(token_id, from_=owner) - # Check events - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, Address(0), token_id), - ERC721Mock.Transfer(owner, Address(0), token_id), - ERC721Mock.AfterTokenTransfer(owner, Address(0), token_id) - ] - # Burn in Py model - self._py_erc721.burn(token_id) - - @flow(weight=40) - def burn_approved(self) -> None: - if self._py_erc721.approvals: - # Random token with owner - token_id, approved = random.choice(list(self._py_erc721.approvals.items())) - if token_id in self._py_erc721.owners.keys(): - owner = self._py_erc721.owners[token_id] - # Burn in contract, msg.sender == approved - tx = self._erc721.burn(token_id, from_=approved) - # Check events - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, Address(0), token_id), - ERC721Mock.Transfer(owner, Address(0), token_id), - ERC721Mock.AfterTokenTransfer(owner, Address(0), token_id) - ] - # Burn in Py model - self._py_erc721.burn(token_id) - - @flow(weight=40) - def burn_operator(self) -> None: - if self._py_erc721.operators: - owner, operator = random.choice(list(self._py_erc721.operators.items())) - if owner in self._py_erc721.owners.keys(): - token_id = self._py_erc721.owners[owner] - # Burn in contract, msg.sender == operator - tx = self._erc721.burn(token_id, from_=operator) - # Check events - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, Address(0), token_id), - ERC721Mock.Transfer(owner, Address(0), token_id), - ERC721Mock.AfterTokenTransfer(owner, Address(0), token_id) - ] - # Burn in Py model - self._py_erc721.burn(token_id) - - ######################## TRANSFER ######################## - @flow(weight=80) - def transfer_owner(self) -> None: - # from == by == owner - if self._py_erc721.owners: - token_id, owner = random.choice(list(self._py_erc721.owners.items())) - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == owner - tx = self._erc721.transfer(owner, to, token_id, from_ = owner) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer(owner, owner, to, token_id) - - @flow(weight=60) - def transfer_approved(self) -> None: - # by == approved, from == owner - if self._py_erc721.approvals: - token_id, approved = random.choice(list(self._py_erc721.approvals.items())) - if token_id in self._py_erc721.owners.keys(): - owner = self._py_erc721.owners[token_id] - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == approved - tx = self._erc721.transfer(owner, to, token_id, from_ = approved) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer(approved, owner, to, token_id) - - @flow(weight=60) - def transfer_operator(self) -> None: - # by == operator, from == owner - if self._py_erc721.operators: - owner, operator = random.choice(list(self._py_erc721.operators.items())) - if owner in self._py_erc721.owners.keys(): - token_id = self._py_erc721.owners[owner] - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == operator - tx = self._erc721.transfer(owner, to, token_id, from_ = operator) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer(operator, owner, to, token_id) - - ###################### TRANSFERS FROM ###################### - @flow(weight=80) - def transfer_from_owner(self) -> None: - # from == by == owner - if self._py_erc721.owners: - token_id, owner = random.choice(list(self._py_erc721.owners.items())) - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == owner - tx = self._erc721.transferFrom(owner, to, token_id, from_ = owner) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer_from(owner, owner, to, token_id) - - @flow(weight=60) - def transfer_from_approved(self) -> None: - # by == approved, from == owner - if self._py_erc721.approvals: - token_id, approved = random.choice(list(self._py_erc721.approvals.items())) - if token_id in self._py_erc721.owners.keys(): - owner = self._py_erc721.owners[token_id] - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == approved - tx = self._erc721.transferFrom(owner, to, token_id, from_ = approved) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer_from(approved, owner, to, token_id) - - @flow(weight=60) - def transfer_from_operator(self) -> None: - # by == operator, from == owner - if self._py_erc721.operators: - owner, operator = random.choice(list(self._py_erc721.operators.items())) - if owner in self._py_erc721.owners.keys(): - token_id = self._py_erc721.owners[owner] - to = random.choice(self._addresses) - # Transfer in contract, msg.sender == operator - tx = self._erc721.transferFrom(owner, to, token_id, from_ = operator) - assert tx.events == [ - ERC721Mock.BeforeTokenTransfer(owner, to, token_id), - ERC721Mock.Transfer(owner, to, token_id), - ERC721Mock.AfterTokenTransfer(owner, to, token_id) - ] - # Transfer in Py model - self._py_erc721.transfer_from(operator, owner, to, token_id) - - ######################## APPROVALS ######################## - @flow(weight=50) - def approve_owner(self) -> None: - if self._py_erc721.owners: - token_id, owner = random.choice(list(self._py_erc721.owners.items())) - account = random.choice(self._addresses) - # Approve in contract - tx = self._erc721.approve(account, token_id, from_=owner) - # Check events - assert tx.events == [ - ERC721Mock.Approval(owner, account, token_id), - - ] - # Approve in Py model - self._py_erc721.approve(account, token_id) - - @flow(weight=40) - def dis_approve_owner(self) -> None: - if self._py_erc721.owners: - token_id, owner = random.choice(list(self._py_erc721.owners.items())) - if token_id in self._py_erc721.approvals.keys(): - # Delete approval in contract - tx = self._erc721.approve(Address(0), token_id, from_=owner) - # Check events - assert tx.events == [ - ERC721Mock.Approval(owner, Address(0), token_id), - ] - # Delete approval in Py model - self._py_erc721.approve(Address(0), token_id) - - @flow(weight=40) - def approve_operator(self) -> None: - if self._py_erc721.operators: - owner, operator = random.choice(list(self._py_erc721.operators.items())) - if owner in self._py_erc721.owners.keys(): - token_id = self._py_erc721.owners[owner] - account = random.choice(self._addresses) - # Approve in contract - tx = self._erc721.approve(account, token_id, from_=operator) - # Check events - assert tx.events == [ - ERC721Mock.Approval(owner, account, token_id), - ] - # Approve in Py model - self._py_erc721.approve(account, token_id) - - #################### APPROVE FOR ALL ######################## - @flow(weight=40) - def approve_for_all(self) -> None: - if self._py_erc721.owners: - _, owner = random.choice(list(self._py_erc721.owners.items())) - operator = random.choice(self._addresses) - # Set approve for all in contract - tx = self._erc721.setApprovalForAll(operator, True, from_=owner) - assert tx.events == [ - ERC721Mock.ApprovalForAll(owner, operator, True), - ] - # Set approve for all in Py model - self._py_erc721.set_approval_for_all(operator, owner) - - - @invariant(period=20) - def invariant_owners(self) -> None: - owners = self._py_erc721.owners.items() - for token_id, owner in owners: - assert self._erc721.ownerOf(token_id) == owner - - @invariant(period=20) - def invariant_balances(self) -> None: - balances = self._py_erc721.balances.items() - for owner, count in balances: - assert self._erc721.balanceOf(owner) == count - - @invariant(period=20) - def invariant_approvals(self) -> None: - approvals = self._py_erc721.approvals.items() - for token_id, approved in approvals: - assert self._erc721.getApproved(token_id) == approved - - -@default_chain.connect() -def test_eip712_fuzz(): - default_chain.set_default_accounts(default_chain.accounts[0]) - ERC721FuzzTest().run(30, 600) - diff --git a/lib/solady/ext/woke/test_merkle_proof.py b/lib/solady/ext/woke/test_merkle_proof.py deleted file mode 100644 index 33e1214..0000000 --- a/lib/solady/ext/woke/test_merkle_proof.py +++ /dev/null @@ -1,55 +0,0 @@ -import random - -from woke.testing import * -from woke.testing.fuzzing import random_bytes, random_int -from pytypes.tests.MerkleProofMock import MerkleProofMock - -from .utils import MerkleTree - - -@default_chain.connect() -def test_merkle_proof(): - default_chain.set_default_accounts(default_chain.accounts[0]) - - tree = MerkleTree() - for _ in range(100): - tree.add_leaf(random_bytes(0, 1_000)) - - merkle_proof_mock = MerkleProofMock.deploy() - - for i in range(100): - assert merkle_proof_mock.verify(tree.get_proof(i), tree.root, keccak256(tree.values[i])) - assert merkle_proof_mock.verifyCalldata(tree.get_proof(i), tree.root, keccak256(tree.values[i])) - - -@default_chain.connect() -def test_merkle_multiproof_single(): - default_chain.set_default_accounts(default_chain.accounts[0]) - - tree = MerkleTree() - tree.add_leaf(random_bytes(0, 1_000)) - - merkle_proof_mock = MerkleProofMock.deploy() - assert merkle_proof_mock.verifyMultiProof([], tree.root, [keccak256(tree.values[0])], []) - assert merkle_proof_mock.verifyMultiProofCalldata([], tree.root, [keccak256(tree.values[0])], []) - - assert merkle_proof_mock.verifyMultiProof([keccak256(tree.values[0])], tree.root, [], []) - assert merkle_proof_mock.verifyMultiProofCalldata([keccak256(tree.values[0])], tree.root, [], []) - - -@default_chain.connect() -def test_merkle_multiproof(): - default_chain.set_default_accounts(default_chain.accounts[0]) - - tree = MerkleTree() - for _ in range(1_000): - tree.add_leaf(random_bytes(0, 1_000)) - - merkle_proof_mock = MerkleProofMock.deploy() - - for _ in range(100): - indexes = sorted(random.sample(range(len(tree.values)), random_int(1, 100))) - leaves = [tree.values[i] for i in indexes] - proof, flags = tree.get_multiproof(indexes) - assert merkle_proof_mock.verifyMultiProof(proof, tree.root, [keccak256(leaf) for leaf in leaves], flags) - assert merkle_proof_mock.verifyMultiProofCalldata(proof, tree.root, [keccak256(leaf) for leaf in leaves], flags) diff --git a/lib/solady/ext/woke/test_merkle_proof_fuzz.py b/lib/solady/ext/woke/test_merkle_proof_fuzz.py deleted file mode 100644 index 6006c45..0000000 --- a/lib/solady/ext/woke/test_merkle_proof_fuzz.py +++ /dev/null @@ -1,124 +0,0 @@ -import random - -from woke.testing import * -from woke.testing.fuzzing import * -from pytypes.tests.MerkleProofMock import MerkleProofMock - -from .utils import MerkleTree - - -class MerkleProofFuzzTest(FuzzTest): - _merkle_proof: MerkleProofMock - _tree: MerkleTree - - def __init__(self): - self._merkle_proof = MerkleProofMock.deploy() - - def pre_sequence(self) -> None: - self._tree = MerkleTree() - for _ in range(random_int(1, 1_000)): - self._tree.add_leaf(random_bytes(0, 100)) - - @flow() - def flow_verify(self) -> None: - index = random_int(0, len(self._tree.values) - 1) - proof = self._tree.get_proof(index) - leaf = self._tree.values[index] - - assert self._merkle_proof.verify(proof, self._tree.root, keccak256(leaf)) - assert self._merkle_proof.verifyCalldata(proof, self._tree.root, keccak256(leaf)) - - @flow(weight=40) - def flow_verify_invalid_random(self, proof: List[bytes32], root: bytes32, leaf: bytes) -> None: - try: - index = self._tree.values.index(leaf) - assert self._tree.root == root - assert self._tree.get_proof(index) == proof - return - except Exception: - pass - - leaf_hash = keccak256(leaf) - assert not self._merkle_proof.verify(proof, root, leaf_hash) - assert not self._merkle_proof.verifyCalldata(proof, root, leaf_hash) - - @flow(weight=60) - def flow_verify_invalid_modified(self) -> None: - index = random_int(0, len(self._tree.values) - 1) - leaf = self._tree.values[index] - proof = self._tree.get_proof(index) - root = self._tree.root - - r = random_int(0, 2) - if r == 0: - leaf = random_bytes(32) - elif r == 1: - if len(proof) != 0: - proof[random_int(0, len(proof) - 1)] = random_bytes(32) - else: - proof.append(random_bytes(32)) - else: - root = random_bytes(32) - - assert not self._merkle_proof.verify(proof, root, keccak256(leaf)) - assert not self._merkle_proof.verifyCalldata(proof, root, keccak256(leaf)) - - @flow() - def flow_verify_multiproof(self) -> None: - indexes = sorted(random.sample(range(len(self._tree.values)), random_int(1, len(self._tree.values)))) - leaves = [self._tree.values[i] for i in indexes] - proof, flags = self._tree.get_multiproof(indexes) - - assert self._merkle_proof.verifyMultiProof(proof, self._tree.root, [keccak256(leaf) for leaf in leaves], flags) - assert self._merkle_proof.verifyMultiProofCalldata(proof, self._tree.root, [keccak256(leaf) for leaf in leaves], flags) - - @flow(weight=40) - def flow_verify_multiproof_invalid_random(self, proof: List[bytes32], root: bytes32, leaves: List[bytes], flags: List[bool]) -> None: - try: - indexes = sorted([self._tree.values.index(leaf) for leaf in leaves]) - assert self._tree.root == root - assert self._tree.get_multiproof(indexes) == (proof, flags) - return - except Exception: - pass - - leaf_hashes = [keccak256(leaf) for leaf in leaves] - assert not self._merkle_proof.verifyMultiProof(proof, root, leaf_hashes, flags) - assert not self._merkle_proof.verifyMultiProofCalldata(proof, root, leaf_hashes, flags) - - @flow(weight=60) - def flow_verify_multiproof_invalid_modified(self) -> None: - indexes = sorted(random.sample(range(len(self._tree.values)), random_int(1, len(self._tree.values)))) - leaves = [self._tree.values[i] for i in indexes] - proof, flags = self._tree.get_multiproof(indexes) - root = self._tree.root - - r = random_int(0, 3) - if r == 0: - if len(leaves) != 0: - leaves[random_int(0, len(leaves) - 1)] = random_bytes(32) - else: - leaves.append(random_bytes(32)) - elif r == 1: - if len(proof) != 0: - proof[random_int(0, len(proof) - 1)] = random_bytes(32) - else: - proof.append(random_bytes(32)) - elif r == 2: - if len(flags) != 0: - pos = random_int(0, len(flags) - 1) - flags[pos] = not flags[pos] - else: - flags.append(random.choice([True, False])) - else: - root = random_bytes(32) - - leaf_hashes = [keccak256(leaf) for leaf in leaves] - assert not self._merkle_proof.verifyMultiProof(proof, root, leaf_hashes, flags) - assert not self._merkle_proof.verifyMultiProofCalldata(proof, root, leaf_hashes, flags) - - -@default_chain.connect() -def test_merkle_proof_fuzz(): - default_chain.set_default_accounts(default_chain.accounts[0]) - MerkleProofFuzzTest().run(10, 100) diff --git a/lib/solady/ext/woke/test_signature_checker_fuzz.py b/lib/solady/ext/woke/test_signature_checker_fuzz.py deleted file mode 100644 index c2833b8..0000000 --- a/lib/solady/ext/woke/test_signature_checker_fuzz.py +++ /dev/null @@ -1,178 +0,0 @@ -from woke.testing import * -from woke.testing.fuzzing import * -from pytypes.tests.SignatureCheckerMock import SignatureCheckerMock, ERC1217SignatureChecker - -class SignatureCheckerFuzzTest(FuzzTest): - _signature_checker: SignatureCheckerMock - _erc1271_signature_checker: ERC1217SignatureChecker - _signer: Account - - def pre_sequence(self) -> None: - self._signature_checker = SignatureCheckerMock.deploy() - self._erc1271_signature_checker = ERC1217SignatureChecker.deploy() - self._signer = Account.new() - - @flow() - def flow_check_signature(self) -> None: - data = random_bytes(0, 1000) - hash = keccak256(data) - signature = self._signer.sign_hash(hash) - r = signature[:32] - s = signature[32:64] - v = signature[64] - - assert self._signature_checker.isValidSignatureNow(self._signer, hash, signature) - assert self._signature_checker.isValidSignatureNow_( - self._signer, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - ) - assert self._signature_checker.isValidSignatureNow__(self._signer, hash, v, r, s) - assert self._signature_checker.isValidSignatureNowCalldata(self._signer, hash, signature) - - # erc1271 - assert self._signature_checker.isValidSignatureNow(self._erc1271_signature_checker, hash, signature, from_=self._signer) - assert self._signature_checker.isValidSignatureNow_( - self._erc1271_signature_checker, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - from_=self._signer, - ) - assert self._signature_checker.isValidSignatureNow__(self._erc1271_signature_checker, hash, v, r, s, from_=self._signer) - assert self._signature_checker.isValidSignatureNowCalldata(self._erc1271_signature_checker, hash, signature, from_=self._signer) - - assert not self._signature_checker.isValidSignatureNow(self._erc1271_signature_checker, hash, signature) - assert not self._signature_checker.isValidSignatureNow_( - self._erc1271_signature_checker, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - ) - assert not self._signature_checker.isValidSignatureNow__(self._erc1271_signature_checker, hash, v, r, s) - assert not self._signature_checker.isValidSignatureNowCalldata(self._erc1271_signature_checker, hash, signature) - - @flow(weight=40) - def flow_check_signature_invalid_random(self, signer: Address, hash: bytes32) -> None: - signature = random_bytes(65) - r = signature[:32] - s = signature[32:64] - v = signature[64] - - assert not self._signature_checker.isValidSignatureNow(signer, hash, signature) - assert not self._signature_checker.isValidSignatureNow_( - self._signer, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - ) - assert not self._signature_checker.isValidSignatureNow__(signer, hash, v, r, s) - assert not self._signature_checker.isValidSignatureNowCalldata(signer, hash, signature) - - assert not self._signature_checker.isValidSignatureNow(self._erc1271_signature_checker, hash, signature, from_=signer) - assert not self._signature_checker.isValidSignatureNow_( - self._erc1271_signature_checker, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - from_=signer, - ) - assert not self._signature_checker.isValidSignatureNow__(self._erc1271_signature_checker, hash, v, r, s, from_=signer) - assert not self._signature_checker.isValidSignatureNowCalldata(self._erc1271_signature_checker, hash, signature, from_=signer) - - assert not self._signature_checker.isValidERC1271SignatureNow(self._erc1271_signature_checker, hash, signature, from_=signer) - assert not self._signature_checker.isValidERC1271SignatureNow_( - self._erc1271_signature_checker, - hash, - r, - s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:], - from_=signer, - ) - assert not self._signature_checker.isValidERC1271SignatureNow__(self._erc1271_signature_checker, hash, v, r, s, from_=signer) - assert not self._signature_checker.isValidERC1271SignatureNowCalldata(self._erc1271_signature_checker, hash, signature, from_=signer) - - @flow(weight=60) - def flow_check_signature_invalid_modified(self) -> None: - signer = self._signer.address - data = random_bytes(0, 1000) - hash = bytearray(keccak256(data)) - signature = bytearray(self._signer.sign_hash(hash)) - original_v = None - - x = random_int(0, 2) - if x == 0: - signer_bytes = bytearray(bytes(signer)) - pos = random_int(0, 19) - new_byte = random_bytes(1)[0] - while signer_bytes[pos] == new_byte: - new_byte = random_bytes(1)[0] - signer_bytes[pos] = new_byte - signer = Address(signer_bytes.hex()) - elif x == 1: - pos = random_int(0, 31) - new_byte = random_bytes(1)[0] - while hash[pos] == new_byte: - new_byte = random_bytes(1)[0] - hash[pos] = new_byte - elif x == 2: - pos = random_int(0, 64) - if pos == 64: - original_v = signature[64] - - new_byte = random_bytes(1)[0] - while signature[pos] == new_byte: - new_byte = random_bytes(1)[0] - signature[pos] = new_byte - else: - assert False - - r = signature[:32] - s = signature[32:64] - v = signature[64] - - if original_v is None: - # v was not modified - vs = s if v == 27 else (s[0] | 0x80).to_bytes(1, "big") + s[1:] - else: - # v was modified - vs = s if original_v == 28 else (s[0] | 0x80).to_bytes(1, "big") + s[1:] - - assert not self._signature_checker.isValidSignatureNow(signer, hash, signature) - assert not self._signature_checker.isValidSignatureNow_( - signer, - hash, - r, - vs, - ) - assert not self._signature_checker.isValidSignatureNow__(signer, hash, v, r, s) - assert not self._signature_checker.isValidSignatureNowCalldata(signer, hash, signature) - - # erc1271 - assert not self._signature_checker.isValidSignatureNow(self._erc1271_signature_checker, hash, signature, from_=signer) - assert not self._signature_checker.isValidSignatureNow_( - self._erc1271_signature_checker, - hash, - r, - vs, - from_=signer, - ) - assert not self._signature_checker.isValidSignatureNow__(self._erc1271_signature_checker, hash, v, r, s, from_=signer) - assert not self._signature_checker.isValidSignatureNowCalldata(self._erc1271_signature_checker, hash, signature, from_=signer) - - assert not self._signature_checker.isValidERC1271SignatureNow(self._erc1271_signature_checker, hash, signature, from_=signer) - assert not self._signature_checker.isValidERC1271SignatureNow_( - self._erc1271_signature_checker, - hash, - r, - vs, - from_=signer, - ) - assert not self._signature_checker.isValidERC1271SignatureNow__(self._erc1271_signature_checker, hash, v, r, s, from_=signer) - assert not self._signature_checker.isValidERC1271SignatureNowCalldata(self._erc1271_signature_checker, hash, signature, from_=signer) - - -@default_chain.connect() -def test_signature_checker(): - default_chain.set_default_accounts(default_chain.accounts[0]) - SignatureCheckerFuzzTest().run(10, 20) diff --git a/lib/solady/ext/woke/utils.py b/lib/solady/ext/woke/utils.py deleted file mode 100644 index dfdab8b..0000000 --- a/lib/solady/ext/woke/utils.py +++ /dev/null @@ -1,89 +0,0 @@ -from typing import List, Tuple - -from woke.testing import keccak256 - - -class MerkleTree: - _is_ready: bool - _leaves: List[bytes] - _levels: List[List[bytes]] - - def __init__(self): - self._is_ready = False - self._leaves = [] - self._levels = [] - - @property - def root(self) -> bytes: - if not self._is_ready: - self._build_tree() - return self._levels[-1][0] - - @property - def values(self) -> Tuple[bytes, ...]: - return tuple(self._leaves) - - def get_proof(self, index: int) -> List[bytes]: - if not self._is_ready: - self._build_tree() - - proof = [] - for level in self._levels[:-1]: - if index % 2 == 0: - proof.append(level[index + 1]) - else: - proof.append(level[index - 1]) - index //= 2 - return proof - - def get_multiproof(self, indexes: List[int]) -> Tuple[List[bytes], List[bool]]: - if not self._is_ready: - self._build_tree() - - proof = [] - flags = [] - known = indexes - assert known == sorted(known), "Leaves must be sorted" - - for level in self._levels[:-1]: - new_known = [] - for i in known: - if i % 2 == 0: - if i + 1 in known: - flags.append(True) - else: - flags.append(False) - if i + 1 < len(level): - proof.append(level[i + 1]) - else: - proof.append(level[i]) - else: - if i - 1 in known: - pass # already processed - else: - flags.append(False) - proof.append(level[i - 1]) - if len(new_known) == 0 or new_known[-1] != i // 2: - new_known.append(i // 2) - known = new_known - - return proof, flags - - def add_leaf(self, leaf: bytes): - self._leaves.append(leaf) - self._is_ready = False - - def _build_tree(self) -> None: - self._levels.append([keccak256(leaf) for leaf in self._leaves]) - while len(self._levels[-1]) > 1: - self._levels.append(self._build_level(self._levels[-1])) - self._is_ready = True - - def _build_level(self, level: List[bytes]) -> List[bytes]: - if len(level) % 2 == 1: - level.append(level[-1]) - return [ - keccak256(level[i] + level[i + 1]) if level[i] < level[i + 1] - else keccak256(level[i + 1] + level[i]) - for i in range(0, len(level), 2) - ] diff --git a/lib/solady/ext/woke/weird/Approval.sol b/lib/solady/ext/woke/weird/Approval.sol deleted file mode 100644 index ff1026f..0000000 --- a/lib/solady/ext/woke/weird/Approval.sol +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract ApprovalRaceToken is ERC20 { - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public {} - - // --- Token --- - function approve(address usr, uint wad) override public returns (bool) { - require(allowance[msg.sender][usr] == 0, "unsafe-approve"); - return super.approve(usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/ApprovalToZero.sol b/lib/solady/ext/woke/weird/ApprovalToZero.sol deleted file mode 100644 index 01ffff3..0000000 --- a/lib/solady/ext/woke/weird/ApprovalToZero.sol +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract ApprovalToZeroToken is ERC20 { - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public {} - - // --- Token --- - function approve(address usr, uint wad) override public returns (bool) { - require(usr != address(0), "no approval for the zero address"); - return super.approve(usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/BlockList.sol b/lib/solady/ext/woke/weird/BlockList.sol deleted file mode 100644 index a4f76f1..0000000 --- a/lib/solady/ext/woke/weird/BlockList.sol +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract BlockableToken is ERC20 { - // --- Access Control --- - address owner; - modifier auth() { require(msg.sender == owner, "unauthorised"); _; } - - // --- BlockList --- - mapping(address => bool) blocked; - function block(address usr) auth public { blocked[usr] = true; } - function allow(address usr) auth public { blocked[usr] = false; } - - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public { - owner = msg.sender; - } - - // --- Token --- - function transferFrom(address src, address dst, uint wad) override public returns (bool) { - require(!blocked[src], "blocked"); - require(!blocked[dst], "blocked"); - return super.transferFrom(src, dst, wad); - } -} diff --git a/lib/solady/ext/woke/weird/Bytes32Metadata.sol b/lib/solady/ext/woke/weird/Bytes32Metadata.sol deleted file mode 100644 index 3a03f27..0000000 --- a/lib/solady/ext/woke/weird/Bytes32Metadata.sol +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract Math { - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } -} - -contract ERC20 is Math { - // --- ERC20 Data --- - bytes32 public constant name = "Token"; - bytes32 public constant symbol = "TKN"; - uint8 public constant decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) virtual public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) virtual public returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - return true; - } - function approve(address usr, uint wad) virtual public returns (bool) { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - return true; - } - - function mint(address usr, uint wad) virtual public { - balanceOf[usr] = add(balanceOf[usr], wad); - totalSupply = add(totalSupply, wad); - emit Transfer(address(0), usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/DaiPermit.sol b/lib/solady/ext/woke/weird/DaiPermit.sol deleted file mode 100644 index 80188b8..0000000 --- a/lib/solady/ext/woke/weird/DaiPermit.sol +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract Math { - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } -} - -contract DaiPermit is Math { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public decimals = 18; - uint256 public totalSupply; - bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb; - bytes32 public immutable DOMAIN_SEPARATOR = keccak256( - abi.encode( - keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), - keccak256(bytes(name)), - keccak256(bytes('1')), - block.chainid, - address(this) - ) - ); - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - mapping (address => uint) public nonces; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) virtual public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) virtual public returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - return true; - } - function approve(address usr, uint wad) virtual public returns (bool) { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - return true; - } - function permit(address holder, address spender, uint256 nonce, uint256 expiry, - bool allowed, uint8 v, bytes32 r, bytes32 s) external - { - bytes32 digest = - keccak256(abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR, - keccak256(abi.encode(PERMIT_TYPEHASH, - holder, - spender, - nonce, - expiry, - allowed)) - )); - - require(holder != address(0), "Dai/invalid-address-0"); - require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit"); - require(expiry == 0 || block.timestamp <= expiry, "Dai/permit-expired"); - require(nonce == nonces[holder]++, "Dai/invalid-nonce"); - uint wad = allowed ? type(uint256).max : 0; - allowance[holder][spender] = wad; - } -} diff --git a/lib/solady/ext/woke/weird/ERC20.sol b/lib/solady/ext/woke/weird/ERC20.sol deleted file mode 100644 index 15adf96..0000000 --- a/lib/solady/ext/woke/weird/ERC20.sol +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract Math { - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } -} - -contract ERC20 is Math { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) virtual public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) virtual public returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - return true; - } - function approve(address usr, uint wad) virtual public returns (bool) { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - return true; - } - - function mint(address usr, uint wad) virtual public { - balanceOf[usr] = add(balanceOf[usr], wad); - totalSupply = add(totalSupply, wad); - emit Transfer(address(0), usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/HighDecimals.sol b/lib/solady/ext/woke/weird/HighDecimals.sol deleted file mode 100644 index 423c2f4..0000000 --- a/lib/solady/ext/woke/weird/HighDecimals.sol +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract HighDecimalToken is ERC20 { - constructor(uint _totalSupply) ERC20(_totalSupply) public { - decimals = 50; - } -} diff --git a/lib/solady/ext/woke/weird/LowDecimals.sol b/lib/solady/ext/woke/weird/LowDecimals.sol deleted file mode 100644 index aa5284d..0000000 --- a/lib/solady/ext/woke/weird/LowDecimals.sol +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract LowDecimalToken is ERC20 { - constructor(uint _totalSupply) ERC20(_totalSupply) public { - decimals = 2; - } -} diff --git a/lib/solady/ext/woke/weird/MissingReturns.sol b/lib/solady/ext/woke/weird/MissingReturns.sol deleted file mode 100644 index 6793b53..0000000 --- a/lib/solady/ext/woke/weird/MissingReturns.sol +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract MissingReturnToken { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public constant decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) external { - transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) public { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - } - function approve(address usr, uint wad) external { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - } - - function mint(address usr, uint wad) external { - balanceOf[usr] = add(balanceOf[usr], wad); - totalSupply = add(totalSupply, wad); - emit Transfer(address(0), usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/NoRevert.sol b/lib/solady/ext/woke/weird/NoRevert.sol deleted file mode 100644 index 01e5abc..0000000 --- a/lib/solady/ext/woke/weird/NoRevert.sol +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract NoRevertToken { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) external returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) virtual public returns (bool) { - if (balanceOf[src] < wad) return false; // insufficient src bal - if (balanceOf[dst] >= (type(uint256).max - wad)) return false; // dst bal too high - - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - if (allowance[src][msg.sender] < wad) return false; // insufficient allowance - allowance[src][msg.sender] = allowance[src][msg.sender] - wad; - } - - balanceOf[src] = balanceOf[src] - wad; - balanceOf[dst] = balanceOf[dst] + wad; - - emit Transfer(src, dst, wad); - return true; - } - function approve(address usr, uint wad) virtual external returns (bool) { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - return true; - } - - function mint(address usr, uint wad) virtual external { - balanceOf[usr] = balanceOf[usr] + wad; - totalSupply = totalSupply + wad; - emit Transfer(address(0), usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/Pausable.sol b/lib/solady/ext/woke/weird/Pausable.sol deleted file mode 100644 index cc0518f..0000000 --- a/lib/solady/ext/woke/weird/Pausable.sol +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract PausableToken is ERC20 { - // --- Access Control --- - address owner; - modifier auth() { require(msg.sender == owner, "unauthorised"); _; } - - // --- Pause --- - bool live = true; - function stop() auth external { live = false; } - function start() auth external { live = true; } - - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public { - owner = msg.sender; - } - - // --- Token --- - function approve(address usr, uint wad) override public returns (bool) { - require(live, "paused"); - return super.approve(usr, wad); - } - function transfer(address dst, uint wad) override public returns (bool) { - require(live, "paused"); - return super.transfer(dst, wad); - } - function transferFrom(address src, address dst, uint wad) override public returns (bool) { - require(live, "paused"); - return super.transferFrom(src, dst, wad); - } -} diff --git a/lib/solady/ext/woke/weird/Proxied.sol b/lib/solady/ext/woke/weird/Proxied.sol deleted file mode 100644 index 6ae632d..0000000 --- a/lib/solady/ext/woke/weird/Proxied.sol +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -/* - Provides two contracts: - - 1. ProxiedToken: The underlying token, state modifications must be made through a proxy - 2. TokenProxy: Proxy contract, appends the original msg.sender to any calldata provided by the user - - The ProxiedToken can have many trusted frontends (TokenProxy's). - The frontends should append the address of their caller to calldata when calling into the backend. - Admin users of the ProxiedToken can add new delegators. -*/ - -contract ProxiedToken { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public constant decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } - - // --- Init --- - constructor(uint _totalSupply) public { - admin[msg.sender] = true; - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Access Control --- - mapping(address => bool) public admin; - function rely(address usr) external auth { admin[usr] = true; } - function deny(address usr) external auth { admin[usr] = false; } - modifier auth() { require(admin[msg.sender], "non-admin-call"); _; } - - mapping(address => bool) public delegators; - modifier delegated() { require(delegators[msg.sender], "non-delegator-call"); _; } - function setDelegator(address delegator, bool status) external { - delegators[delegator] = status; - } - - // --- Token --- - function transfer(address dst, uint wad) delegated external returns (bool) { - return _transferFrom(_getCaller(), _getCaller(), dst, wad); - } - function transferFrom(address src, address dst, uint wad) delegated external returns (bool) { - return _transferFrom(_getCaller(), src, dst, wad); - } - function approve(address usr, uint wad) delegated external returns (bool) { - return _approve(_getCaller(), usr, wad); - } - - // --- Internals --- - function _transferFrom( - address caller, address src, address dst, uint wad - ) internal returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != caller && allowance[src][caller] != type(uint).max) { - require(allowance[src][caller] >= wad, "insufficient-allowance"); - allowance[src][caller] = sub(allowance[src][caller], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - return true; - } - function _approve(address caller, address usr, uint wad) internal returns (bool) { - allowance[caller][usr] = wad; - emit Approval(caller, usr, wad); - return true; - } - // grabs the first word after the calldata and masks it with 20bytes of 1's - // to turn it into an address - function _getCaller() internal pure returns (address result) { - bytes memory array = msg.data; - uint256 index = msg.data.length; - assembly { - result := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff) - } - return result; - } - - function mint(address usr, uint wad) external { - balanceOf[usr] = add(balanceOf[usr], wad); - totalSupply = add(totalSupply, wad); - emit Transfer(address(0), usr, wad); - } -} - -contract TokenProxy { - address payable immutable public impl; - constructor(address _impl) public { - impl = payable(_impl); - } - - receive() external payable { revert("don't send me ETH!"); } - - fallback() external payable { - address _impl = impl; // pull impl onto the stack - assembly { - // get free data pointer - let ptr := mload(0x40) - - // write calldata to ptr - calldatacopy(ptr, 0, calldatasize()) - // store msg.sender after the calldata - mstore(add(ptr, calldatasize()), caller()) - - // call impl with the contents of ptr as caldata - let result := call(gas(), _impl, callvalue(), ptr, add(calldatasize(), 32), 0, 0) - - // copy the returndata to ptr - let size := returndatasize() - returndatacopy(ptr, 0, size) - - switch result - // revert if the call failed - case 0 { revert(ptr, size) } - // return ptr otherwise - default { return(ptr, size) } - } - } - -} diff --git a/lib/solady/ext/woke/weird/Reentrant.sol b/lib/solady/ext/woke/weird/Reentrant.sol deleted file mode 100644 index f14dbe9..0000000 --- a/lib/solady/ext/woke/weird/Reentrant.sol +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract ReentrantToken is ERC20 { - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public {} - - // --- Call Targets --- - mapping (address => Target) public targets; - struct Target { - bytes data; - address addr; - } - function setTarget(address addr, bytes calldata data) external { - targets[msg.sender] = Target(data, addr); - } - - // --- Token --- - function transferFrom(address src, address dst, uint wad) override public returns (bool res) { - res = super.transferFrom(src, dst, wad); - Target memory target = targets[src]; - if (target.addr != address(0)) { - (bool status,) = target.addr.call{gas: gasleft()}(target.data); - require(status, "call failed"); - } - } -} diff --git a/lib/solady/ext/woke/weird/ReturnsFalse.sol b/lib/solady/ext/woke/weird/ReturnsFalse.sol deleted file mode 100644 index 5800805..0000000 --- a/lib/solady/ext/woke/weird/ReturnsFalse.sol +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract ReturnsFalseToken { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public constant decimals = 18; - uint256 public totalSupply; - - mapping (address => uint) public balanceOf; - mapping (address => mapping (address => uint)) public allowance; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Math --- - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } - - // --- Init --- - constructor(uint _totalSupply) public { - totalSupply = _totalSupply; - balanceOf[msg.sender] = _totalSupply; - emit Transfer(address(0), msg.sender, _totalSupply); - } - - // --- Token --- - function transfer(address dst, uint wad) external returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) public returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], wad); - emit Transfer(src, dst, wad); - return false; - } - function approve(address usr, uint wad) external returns (bool) { - allowance[msg.sender][usr] = wad; - emit Approval(msg.sender, usr, wad); - return false; - } - - function mint(address usr, uint wad) external returns (bool) { - balanceOf[usr] = add(balanceOf[usr], wad); - totalSupply = add(totalSupply, wad); - emit Transfer(address(0), usr, wad); - return false; - } -} diff --git a/lib/solady/ext/woke/weird/RevertToZero.sol b/lib/solady/ext/woke/weird/RevertToZero.sol deleted file mode 100644 index 6a50c90..0000000 --- a/lib/solady/ext/woke/weird/RevertToZero.sol +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract RevertToZeroToken is ERC20 { - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public {} - - // --- Token --- - function transferFrom(address src, address dst, uint wad) override public returns (bool) { - require(dst != address(0), "transfer-to-zero"); - return super.transferFrom(src, dst, wad); - } -} diff --git a/lib/solady/ext/woke/weird/RevertZero.sol b/lib/solady/ext/woke/weird/RevertZero.sol deleted file mode 100644 index 1357578..0000000 --- a/lib/solady/ext/woke/weird/RevertZero.sol +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract RevertZeroToken is ERC20 { - // --- Init --- - constructor(uint _totalSupply) ERC20(_totalSupply) public {} - - // --- Token --- - function transferFrom(address src, address dst, uint wad) override public returns (bool) { - require(wad != 0, "zero-value-transfer"); - return super.transferFrom(src, dst, wad); - } -} diff --git a/lib/solady/ext/woke/weird/TransferFee.sol b/lib/solady/ext/woke/weird/TransferFee.sol deleted file mode 100644 index 0a1eb58..0000000 --- a/lib/solady/ext/woke/weird/TransferFee.sol +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract TransferFeeToken is ERC20 { - - uint immutable fee; - - // --- Init --- - constructor(uint _totalSupply, uint _fee) ERC20(_totalSupply) public { - fee = _fee; - } - - // --- Token --- - function transferFrom(address src, address dst, uint wad) override public returns (bool) { - require(balanceOf[src] >= wad, "insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { - require(allowance[src][msg.sender] >= wad, "insufficient-allowance"); - allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); - } - - balanceOf[src] = sub(balanceOf[src], wad); - balanceOf[dst] = add(balanceOf[dst], sub(wad, fee)); - balanceOf[address(0)] = add(balanceOf[address(0)], fee); - - emit Transfer(src, dst, sub(wad, fee)); - emit Transfer(src, address(0), fee); - - return true; - } -} diff --git a/lib/solady/ext/woke/weird/Uint96.sol b/lib/solady/ext/woke/weird/Uint96.sol deleted file mode 100644 index 26a862e..0000000 --- a/lib/solady/ext/woke/weird/Uint96.sol +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (C) 2017, 2018, 2019, 2020 dbrock, rain, mrchico, d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -contract Uint96ERC20 { - // --- ERC20 Data --- - string public constant name = "Token"; - string public constant symbol = "TKN"; - uint8 public decimals = 18; - uint96 internal supply; - - mapping (address => uint96) internal balances; - mapping (address => mapping (address => uint96)) internal allowances; - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - - // --- Math --- - function add(uint96 x, uint96 y) internal pure returns (uint96 z) { - require((z = x + y) >= x); - } - function sub(uint96 x, uint96 y) internal pure returns (uint96 z) { - require((z = x - y) <= x); - } - function safe96(uint256 n) internal pure returns (uint96) { - require(n < 2**96); - return uint96(n); - } - - // --- Init --- - constructor(uint96 _supply) public { - supply = _supply; - balances[msg.sender] = _supply; - emit Transfer(address(0), msg.sender, _supply); - } - - // --- Getters --- - function totalSupply() external view returns (uint) { - return supply; - } - function balanceOf(address usr) external view returns (uint) { - return balances[usr]; - } - function allowance(address src, address dst) external view returns (uint) { - return allowances[src][dst]; - } - - // --- Token --- - function transfer(address dst, uint wad) virtual public returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - function transferFrom(address src, address dst, uint wad) virtual public returns (bool) { - uint96 amt = safe96(wad); - - if (src != msg.sender && allowances[src][msg.sender] != type(uint96).max) { - allowances[src][msg.sender] = sub(allowances[src][msg.sender], amt); - } - - balances[src] = sub(balances[src], amt); - balances[dst] = add(balances[dst], amt); - emit Transfer(src, dst, wad); - return true; - } - function approve(address usr, uint wad) virtual public returns (bool) { - uint96 amt; - if (wad == type(uint).max) { - amt = type(uint96).max; - } else { - amt = safe96(wad); - } - - allowances[msg.sender][usr] = amt; - - emit Approval(msg.sender, usr, amt); - return true; - } - - function mint(address usr, uint wad) virtual public { - uint96 amt = safe96(wad); - - balances[usr] = add(balances[usr], amt); - supply = add(supply, amt); - - emit Transfer(address(0), usr, wad); - } -} diff --git a/lib/solady/ext/woke/weird/Upgradable.sol b/lib/solady/ext/woke/weird/Upgradable.sol deleted file mode 100644 index fa04d3a..0000000 --- a/lib/solady/ext/woke/weird/Upgradable.sol +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2020 d-xo -// SPDX-License-Identifier: AGPL-3.0-only - -pragma solidity >=0.6.12; - -import {ERC20} from "./ERC20.sol"; - -contract Proxy { - bytes32 constant ADMIN_KEY = bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1); - bytes32 constant IMPLEMENTATION_KEY = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); - - // --- init --- - - constructor(uint totalSupply) public { - - // Manual give() - bytes32 slot = ADMIN_KEY; - address usr = msg.sender; - assembly { sstore(slot, usr) } - - upgrade(address(new ERC20(totalSupply))); - - } - - // --- auth --- - - modifier auth() { require(msg.sender == owner(), "unauthorised"); _; } - - function owner() public view returns (address usr) { - bytes32 slot = ADMIN_KEY; - assembly { usr := sload(slot) } - } - - function give(address usr) public auth { - bytes32 slot = ADMIN_KEY; - assembly { sstore(slot, usr) } - } - - // --- upgrade --- - - function implementation() public view returns (address impl) { - bytes32 slot = IMPLEMENTATION_KEY; - assembly { impl := sload(slot) } - } - - function upgrade(address impl) public auth { - bytes32 slot = IMPLEMENTATION_KEY; - assembly { sstore(slot, impl) } - } - - // --- proxy --- - - fallback() external payable { - address impl = implementation(); - (bool success, bytes memory returndata) = impl.delegatecall{gas: gasleft()}(msg.data); - require(success); - assembly { return(add(returndata, 0x20), mload(returndata)) } - } - - receive() external payable { revert("don't send me ETH!"); } -} diff --git a/lib/solady/ext/woke/woke-via-ir.toml b/lib/solady/ext/woke/woke-via-ir.toml deleted file mode 100644 index e999716..0000000 --- a/lib/solady/ext/woke/woke-via-ir.toml +++ /dev/null @@ -1,28 +0,0 @@ -[compiler.solc] -ignore_paths = ["node_modules", ".woke-build", "venv", "lib", "test"] -include_paths = ["node_modules"] -remappings = [ - "ds-test/=lib/ds-test/src/", - "forge-std/=test/utils/forge-std/", -] -via_IR = true - -[compiler.solc.optimizer] -enabled = true -runs = 1000 - -[detectors] -exclude = ["unused-contract"] -ignore_paths = ["node_modules", ".woke-build", "venv", "lib"] - -[testing] -cmd = "anvil" - -[testing.anvil] -cmd_args = "--prune-history 100 --transaction-block-keeper 10 --steps-tracing --silent" - -[testing.ganache] -cmd_args = "-k istanbul -q" - -[testing.hardhat] -cmd_args = "" \ No newline at end of file diff --git a/lib/solady/ext/woke/woke.toml b/lib/solady/ext/woke/woke.toml deleted file mode 100644 index 476a151..0000000 --- a/lib/solady/ext/woke/woke.toml +++ /dev/null @@ -1,27 +0,0 @@ -[compiler.solc] -ignore_paths = ["node_modules", ".woke-build", "venv", "lib", "test"] -include_paths = ["node_modules"] -remappings = [ - "ds-test/=lib/ds-test/src/", - "forge-std/=test/utils/forge-std/", -] - -[compiler.solc.optimizer] -enabled = true -runs = 1000 - -[detectors] -exclude = ["unused-contract"] -ignore_paths = ["node_modules", ".woke-build", "venv", "lib"] - -[testing] -cmd = "anvil" - -[testing.anvil] -cmd_args = "--prune-history 100 --transaction-block-keeper 10 --steps-tracing --silent" - -[testing.ganache] -cmd_args = "-k istanbul -q" - -[testing.hardhat] -cmd_args = "" diff --git a/lib/solady/foundry.toml b/lib/solady/foundry.toml deleted file mode 100644 index 7f418d8..0000000 --- a/lib/solady/foundry.toml +++ /dev/null @@ -1,21 +0,0 @@ -# Foundry Configuration File -# Default definitions: https://github.com/gakonst/foundry/blob/b7917fa8491aedda4dd6db53fbb206ea233cd531/config/src/lib.rs#L782 -# See more config options at: https://github.com/gakonst/foundry/tree/master/config - -# The Default Profile -[profile.default] -solc_version = "0.8.22" -evm_version = "paris" # Shanghai will be tested in the CI. -auto_detect_solc = false -optimizer = true -optimizer_runs = 1_000 -gas_limit = 100_000_000 # ETH is 30M, but we use a higher value. -remappings = [ - "forge-std=test/utils/forge-std/" -] - -[fmt] -line_length = 100 # While we allow up to 120, we lint at 100 for readability. - -[profile.default.fuzz] -runs = 256 diff --git a/lib/solady/js/solady.d.ts b/lib/solady/js/solady.d.ts deleted file mode 100644 index 318c86b..0000000 --- a/lib/solady/js/solady.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Accompanying JavaScript library for Solady. - * - * To install: - * - * ``` - * npm install solady - * ``` - * - * Module exports: - * - * - `LibZip` - * - `flzCompress(data)`: Compresses hex encoded data with FastLZ. - * - `flzDecompress(data)`: Decompresses hex encoded data with FastLZ. - * - `cdCompress(data)`: Compresses hex encoded calldata. - * - `cdDecompress(data)`: Decompresses hex encoded calldata. - * - * - `ERC1967Factory` - * - `address`: Canonical address of Solady's ERC1967Factory. - * - `abi`: ABI of Solady's ERC1967Factory. - */ -declare module "solady" { - /** - * FastLZ and calldata compression / decompression functions. - */ - namespace LibZip { - /** - * Compresses hex encoded data with the FastLZ LZ77 algorithm. - * @param data - A hex encoded string representing the original data. - * @returns The compressed result as a hex encoded string. - */ - function flzCompress(data: string): string; - /** - * Decompresses hex encoded data with the FastLZ LZ77 algorithm. - * @param data - A hex encoded string representing the compressed data. - * @returns The decompressed result as a hex encoded string. - */ - function flzDecompress(data: string): string; - /** - * Compresses hex encoded calldata. - * @param data - A hex encoded string representing the original data. - * @returns The compressed result as a hex encoded string. - */ - function cdCompress(data: string): string; - /** - * Decompresses hex encoded calldata. - * @param data - A hex encoded string representing the compressed data. - * @returns The decompressed result as a hex encoded string. - */ - function cdDecompress(data: string): string; - } - /** - * ERC1967Factory canonical address and ABI. - */ - namespace ERC1967Factory { - /** - * Canonical address of Solady's ERC1967Factory. - */ - var address: string; - /** - * ABI of Solady's ERC1967Factory. - */ - var abi: any; - } -} - diff --git a/lib/solady/js/solady.js b/lib/solady/js/solady.js deleted file mode 100644 index 9b90c80..0000000 --- a/lib/solady/js/solady.js +++ /dev/null @@ -1,255 +0,0 @@ -/** - * Accompanying JavaScript library for Solady. - * - * To install: - * - * ``` - * npm install solady - * ``` - * - * Module exports: - * - * - `LibZip` - * - `flzCompress(data)`: Compresses hex encoded data with FastLZ. - * - `flzDecompress(data)`: Decompresses hex encoded data with FastLZ. - * - `cdCompress(data)`: Compresses hex encoded calldata. - * - `cdDecompress(data)`: Decompresses hex encoded calldata. - * - * - `ERC1967Factory` - * - `address`: Canonical address of Solady's ERC1967Factory. - * - `abi`: ABI of Solady's ERC1967Factory. - * - * @module solady - */ -(function(global, factory) { - - "use strict"; - - if (typeof module === "object" && typeof module.exports === "object") { - module.exports = factory(global, 1); - if (typeof exports === "object") { - exports.LibZip = module.exports.LibZip; - exports.ERC1967Factory = module.exports.ERC1967Factory; - } - } else { - factory(global); - } - -})(typeof window !== "undefined" ? window : this, function(window, noGlobal) { - - "use strict"; - - var solady = {}; - - /*============================================================*/ - /* LibZip Operations */ - /*============================================================*/ - - // See: https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol - - /** - * FastLZ and calldata compression / decompression functions. - * @namespace - * @alias module:solady.LibZip - */ - var LibZip = {}; - - solady.LibZip = LibZip; - - function hexString(data) { - if (typeof data === "string" || data instanceof String) { - if (data = data.match(/^[\s\uFEFF\xA0]*(0[Xx])?([0-9A-Fa-f]*)[\s\uFEFF\xA0]*$/)) { - if (data[2].length % 2) { - throw new Error("Hex string length must be a multiple of 2."); - } - return data[2]; - } - } - throw new Error("Data must be a hex string."); - } - - function byteToString(b) { - return (b | 0x100).toString(16).slice(1); - } - - function parseByte(data, i) { - return parseInt(data.substr(i, 2), 16); - } - - function hexToBytes(data) { - var a = [], i = 0; - for (; i < data.length; i += 2) a.push(parseByte(data, i)); - return a; - } - - function bytesToHex(a) { - var o = "0x", i = 0; - for (; i < a.length; o += byteToString(a[i++])) ; - return o; - } - - /** - * Compresses hex encoded data with the FastLZ LZ77 algorithm. - * @param {string} data A hex encoded string representing the original data. - * @returns {string} The compressed result as a hex encoded string. - */ - LibZip.flzCompress = function(data) { - var ib = hexToBytes(hexString(data)), b = ib.length - 4; - var ht = [], ob = [], a = 0, i = 2, o = 0, j, s, h, d, c, l, r, p, q, e, m = 0xffffff; - - function u32(i) { - return ib[i] | (ib[++i] << 8) | (ib[++i] << 16) | (ib[++i] << 24); - } - - function hash(x) { - return ((2654435769 * x) >> 19) & 8191; - } - - function literals(r, s) { - while (r >= 32) for (ob[o++] = 31, j = 32; j--; r--) ob[o++] = ib[s++]; - if (r) for (ob[o++] = r - 1; r--; ) ob[o++] = ib[s++]; - } - - while (i < b - 9) { - do { - r = ht[h = hash(s = u32(i) & m)]; - c = (d = (ht[h] = i) - r) < 8192 ? u32(r) & m : m + 1; - } while (i < b - 9 && i++ && s != c); - if (i >= b - 9) break; - if (--i > a) literals(i - a, a); - for (l = 0, p = r + 3, q = i + 3, e = b - q; l < e; l++) e *= ib[p + l] === ib[q + l]; - s = u32(i += l); - for (--d; l > 262; l -= 262) ob[o++] = 224 + (d >> 8), ob[o++] = 253, ob[o++] = d & 255; - if (l < 7) ob[o++] = (l << 5) + (d >> 8), ob[o++] = d & 255; - else ob[o++] = 224 + (d >> 8), ob[o++] = l - 7, ob[o++] = d & 255; - ht[hash(s & m)] = i++, ht[hash(s >> 8)] = i++, a = i; - } - literals(b + 4 - a, a); - return bytesToHex(ob); - } - - /** - * Decompresses hex encoded data with the FastLZ LZ77 algorithm. - * @param {string} data A hex encoded string representing the compressed data. - * @returns {string} The decompressed result as a hex encoded string. - */ - LibZip.flzDecompress = function(data) { - var ib = hexToBytes(hexString(data)), i = 0, o = 0, l, f, t, r, h, ob = []; - while (i < ib.length) { - if (!(t = ib[i] >> 5)) { - for (l = 1 + ib[i++]; l--;) ob[o++] = ib[i++]; - } else { - f = 256 * (ib[i] & 31) + ib[i + 2 - (t = t < 7)]; - l = t ? 2 + (ib[i] >> 5) : 9 + ib[i + 1]; - i = i + 3 - t; - r = o - f - 1; - while (l--) ob[o++] = ob[r++]; - } - } - return bytesToHex(ob); - } - - /** - * Compresses hex encoded calldata. - * @param {string} data A hex encoded string representing the original data. - * @returns {string} The compressed result as a hex encoded string. - */ - LibZip.cdCompress = function(data) { - data = hexString(data); - var o = "0x", z = 0, y = 0, i = 0, c; - - function pushByte(b) { - o += byteToString(((o.length < 4 * 2 + 2) * 0xff) ^ b); - } - - function rle(v, d) { - pushByte(0x00); - pushByte(d - 1 + v * 0x80); - } - - for (; i < data.length; i += 2) { - c = parseByte(data, i); - if (!c) { - if (y) rle(1, y), y = 0; - if (++z === 0x80) rle(0, 0x80), z = 0; - continue; - } - if (c === 0xff) { - if (z) rle(0, z), z = 0; - if (++y === 0x20) rle(1, 0x20), y = 0; - continue; - } - if (y) rle(1, y), y = 0; - if (z) rle(0, z), z = 0; - pushByte(c); - } - if (y) rle(1, y), y = 0; - if (z) rle(0, z), z = 0; - return o; - } - - /** - * Decompresses hex encoded calldata. - * @param {string} data A hex encoded string representing the compressed data. - * @returns {string} The decompressed result as a hex encoded string. - */ - LibZip.cdDecompress = function(data) { - data = hexString(data); - var o = "0x", i = 0, j, c, s; - - while (i < data.length) { - c = ((i < 4 * 2) * 0xff) ^ parseByte(data, i); - i += 2; - if (!c) { - c = ((i < 4 * 2) * 0xff) ^ parseByte(data, i); - s = (c & 0x7f) + 1; - i += 2; - for (j = 0; j < s; ++j) o += byteToString((c >> 7 && j < 32) * 0xff); - continue; - } - o += byteToString(c); - } - return o; - } - - /*============================================================*/ - /* ERC1967Factory */ - /*============================================================*/ - - // See: https://github.com/vectorized/solady/blob/main/src/utils/ERC1967Factory.sol - - /** - * ERC1967Factory canonical address and ABI. - * @namespace - * @alias module:solady.ERC1967Factory - */ - var ERC1967Factory = {}; - - solady.ERC1967Factory = ERC1967Factory; - - /** - * Canonical address of Solady's ERC1967Factory. - * @type {string} - */ - ERC1967Factory.address = "0x0000000000006396FF2a80c067f99B3d2Ab4Df24"; - - /** - * ABI of Solady's ERC1967Factory. - * @type {Object} - */ - ERC1967Factory.abi = JSON.parse('[{0:[],1:"DeploymentFailed"96"SaltDoesNotStartWithCaller"96"Unauthorized"96"UpgradeFailed",2:3959790,9791],1:"AdminChanged",2:10959790,9792,9791],1:"Deployed",2:10959790,9792],1:"Upgraded",2:10},{0:[{90],1:"adminOf",12:[{9199{0:[{90,{91],1:"changeAdmin",12:[],13:"nonpayable",2:15},{0:[{92,{91],1:"deploy",12:[{9098,{0:[{92,{91,{94],1:"deployAndCall",12:[{9098,{0:[{92,{91,{93],1:"deployDeterministic",12:[{9098,{0:[{92,{91,{93,{94],1:"deployDeterministicAndCall",12:[{9098,{0:[],1:"initCodeHash",12:[{6:19,1:"result",2:19}99{0:[{93],1:"predictDeterministicAddress",12:[{6:7,1:"predicted",2:7}99{0:[{90,{92],1:"upgrade",12:[98,{0:[{90,{92,{94],1:"upgradeAndCall",12:[98]'.replace(/9\d/g, function (m) { return ["6:7,1:8,2:7}","6:7,1:9,2:7}","6:7,1:11,2:7}","6:19,1:20,2:19}","6:17,1:18,2:17}","},{4:false,0:[",",2:3},{0:[],1:","{5:true,","],13:16,2:15}","],13:14,2:15},"][m-90] }).replace(/\d+/g, function (m) { return '"' + ("inputs,name,type,error,anonymous,indexed,internalType,address,proxy,admin,event,implementation,outputs,stateMutability,view,function,payable,bytes,data,bytes32,salt".split(",")[m]) + '"' })); - - /*--------------------------- END ----------------------------*/ - - if (typeof define === "function" && define.amd) { - define("solady", [], function() { - return solady - }); - } - - if (!noGlobal) { - window.solady = solady; - } - - return solady; -}); diff --git a/lib/solady/js/solady.test.js b/lib/solady/js/solady.test.js deleted file mode 100644 index 7049b49..0000000 --- a/lib/solady/js/solady.test.js +++ /dev/null @@ -1,123 +0,0 @@ -var solady = require("./solady.js"); - -function test(msg, fn) { - msg = msg.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "").replace(/([^\.])$/, "$1."); - try { - fn(); - console.log("\x1b[32m[PASS]\x1b[0m", msg); - } catch (e) { - process.exitCode = 1; - console.error("\x1b[31m[FAIL]\x1b[0m", msg); - console.error(e.stack); - } -} - -function assert(cond, msg) { - if (!cond) throw new Error(msg); -} - -function assertEq(a, b) { - assert(a === b, "Assertion failed!\n Expected: " + b + "\n Actual: " + a); -} - -function expectRevert(fn) { - var hasRevert = false; - try { fn() } catch (e) { hasRevert = true } - assert(hasRevert, "Revert expected.\n" + fn); -} - -function randomData() { - var n = ~~(Math.random() * 2000); - var s = Math.random() < 0.5 ? "" : "0x"; - var g = Math.random() < 0.5 ? 0.45 : (Math.random() ? 0.99 : 0.999); - var h = g + 0.5 * (1.0 - g); - for (var i = 0; i < n; ++i) { - var r = Math.random(); - if (r < g) { - s += "00"; - } else if (r < h) { - s += "ff"; - } else { - var b = ((Math.random() * 0x100) & 0xff).toString(16); - s += b.length === 1 ? "0" + b : b; - } - } - return Math.random() < 0.5 ? s.toUpperCase() : s.toLowerCase(); -} - -function padRandomWhitespace(data) { - var before = ""; - var after = ""; - while (Math.random() < 0.5) before += Math.random() ? "\t" : " "; - while (Math.random() < 0.5) after += Math.random() ? "\t" : " "; - return before + data + after; -} - -function testCompressDecompress(compress, decompress) { - var totalDataLength = 0; - var totalCompressedLength = 0; - for (var t = 0; t < 1000; ++t) { - var data = randomData(); - var compressed = compress(padRandomWhitespace(data)); - var decompressed = decompress(padRandomWhitespace(compressed)); - totalDataLength += data.length; - totalCompressedLength += compressed.length; - assertEq(compressed.slice(0, 2), "0x"); - assertEq(decompressed.slice(0, 2), "0x"); - assertEq(decompressed.replace(/^0x/, ""), data.toLowerCase().replace(/^0x/, "")); - } - assert(totalCompressedLength < totalDataLength, "Compress not working as intended."); - - assertEq(compress(""), "0x"); - assertEq(compress("0x"), "0x"); - assertEq(decompress(""), "0x"); - assertEq(decompress("0x"), "0x"); - - function checkRevertOnInvalidInputs(fn) { - expectRevert(function () { fn("hehe") }); - expectRevert(function () { fn("0xa") }); - expectRevert(function () { fn("0xas") }); - expectRevert(function () { fn(123) }); - expectRevert(function () { fn(false) }); - expectRevert(function () { fn(null) }); - expectRevert(function () { fn(undefined) }); - expectRevert(function () { fn([]) }); - expectRevert(function () { fn({}) }); - } - - checkRevertOnInvalidInputs(compress); - checkRevertOnInvalidInputs(decompress); -} - -test("LibZip: FastLZ compress / decompress.", function() { - testCompressDecompress(solady.LibZip.flzCompress, solady.LibZip.flzDecompress); -}); - -test("LibZip: Calldata compress / decompress.", function() { - testCompressDecompress(solady.LibZip.cdCompress, solady.LibZip.cdDecompress); -}); - -test("LibZip: Calldata compress", function() { - var data = "0xac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a40c49ccbe000000000000000000000000000000000000000000000000000000000005b70e00000000000000000000000000000000000000000000000000000dfc79825feb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000645c48a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f7865000000000000000000000000000000000000000000000000000000000005b70e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f1cdf1a632eaaab40d1c263edf49faf749010a1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064df2ab5bb0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c3160700000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f1cdf1a632eaaab40d1c263edf49faf749010a100000000000000000000000000000000000000000000000000000000"; - var expected = "0x5369af27001e20001e04001e80001d0160001d0220001d02a0001ea40c49ccbe001c05b70e00190dfc79825feb005b645c48a7003a84fc6f7865001c05b70e002f008f000f008f003a4449404b7c002b1f1cdf1a632eaaab40d1c263edf49faf749010a1003a64df2ab5bb000b7f5c764cbc14f9669b88837ca1490cca17c31607002b1f1cdf1a632eaaab40d1c263edf49faf749010a1001b"; - assertEq(solady.LibZip.cdCompress(data), expected); -}); - -test("LibZip: Calldata decompress on invalid input", function() { - var data = "0xffffffff00ff"; - var expected = "0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - assertEq(solady.LibZip.cdDecompress(data), expected); -}); - -test("ERC1967Factory: ABI and address", function() { - function hashFnv32a(s) { - var h = 0x811c9dc5; - for (var i = 0; i < s.length; i++) { - h ^= s.charCodeAt(i); - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } - return h >>> 0; - } - assertEq(hashFnv32a(JSON.stringify(solady.ERC1967Factory.abi)), 1277805820); - assertEq(solady.ERC1967Factory.address, "0x0000000000006396FF2a80c067f99B3d2Ab4Df24"); -}); diff --git a/lib/solady/lib/ds-test/.gitignore b/lib/solady/lib/ds-test/.gitignore deleted file mode 100644 index 63f0b2c..0000000 --- a/lib/solady/lib/ds-test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/.dapple -/build -/out diff --git a/lib/solady/lib/ds-test/LICENSE b/lib/solady/lib/ds-test/LICENSE deleted file mode 100644 index 94a9ed0..0000000 --- a/lib/solady/lib/ds-test/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/lib/solady/lib/ds-test/Makefile b/lib/solady/lib/ds-test/Makefile deleted file mode 100644 index 661dac4..0000000 --- a/lib/solady/lib/ds-test/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all:; dapp build - -test: - -dapp --use solc:0.4.23 build - -dapp --use solc:0.4.26 build - -dapp --use solc:0.5.17 build - -dapp --use solc:0.6.12 build - -dapp --use solc:0.7.5 build - -demo: - DAPP_SRC=demo dapp --use solc:0.7.5 build - -hevm dapp-test --verbose 3 - -.PHONY: test demo diff --git a/lib/solady/lib/ds-test/default.nix b/lib/solady/lib/ds-test/default.nix deleted file mode 100644 index cf65419..0000000 --- a/lib/solady/lib/ds-test/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ solidityPackage, dappsys }: solidityPackage { - name = "ds-test"; - src = ./src; -} diff --git a/lib/solady/lib/ds-test/demo/demo.sol b/lib/solady/lib/ds-test/demo/demo.sol deleted file mode 100644 index f3bb48e..0000000 --- a/lib/solady/lib/ds-test/demo/demo.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.5.0; - -import "../src/test.sol"; - -contract DemoTest is DSTest { - function test_this() public pure { - require(true); - } - function test_logs() public { - emit log("-- log(string)"); - emit log("a string"); - - emit log("-- log_named_uint(string, uint)"); - emit log_named_uint("uint", 512); - - emit log("-- log_named_int(string, int)"); - emit log_named_int("int", -512); - - emit log("-- log_named_address(string, address)"); - emit log_named_address("address", address(this)); - - emit log("-- log_named_bytes32(string, bytes32)"); - emit log_named_bytes32("bytes32", "a string"); - - emit log("-- log_named_bytes(string, bytes)"); - emit log_named_bytes("bytes", hex"cafefe"); - - emit log("-- log_named_string(string, string)"); - emit log_named_string("string", "a string"); - - emit log("-- log_named_decimal_uint(string, uint, uint)"); - emit log_named_decimal_uint("decimal uint", 1.0e18, 18); - - emit log("-- log_named_decimal_int(string, int, uint)"); - emit log_named_decimal_int("decimal int", -1.0e18, 18); - } - event log_old_named_uint(bytes32,uint); - function test_old_logs() public { - emit log_old_named_uint("key", 500); - emit log_named_bytes32("bkey", "val"); - } - function test_trace() public view { - this.echo("string 1", "string 2"); - } - function test_multiline() public { - emit log("a multiline\\nstring"); - emit log("a multiline string"); - emit log_bytes("a string"); - emit log_bytes("a multiline\nstring"); - emit log_bytes("a multiline\\nstring"); - emit logs(hex"0000"); - emit log_named_bytes("0x0000", hex"0000"); - emit logs(hex"ff"); - } - function echo(string memory s1, string memory s2) public pure - returns (string memory, string memory) - { - return (s1, s2); - } - - function prove_this(uint x) public { - emit log_named_uint("sym x", x); - assertGt(x + 1, 0); - } - - function test_logn() public { - assembly { - log0(0x01, 0x02) - log1(0x01, 0x02, 0x03) - log2(0x01, 0x02, 0x03, 0x04) - log3(0x01, 0x02, 0x03, 0x04, 0x05) - } - } - - event MyEvent(uint, uint indexed, uint, uint indexed); - function test_events() public { - emit MyEvent(1, 2, 3, 4); - } - - function test_asserts() public { - string memory err = "this test has failed!"; - emit log("## assertTrue(bool)\n"); - assertTrue(false); - emit log("\n"); - assertTrue(false, err); - - emit log("\n## assertEq(address,address)\n"); - assertEq(address(this), msg.sender); - emit log("\n"); - assertEq(address(this), msg.sender, err); - - emit log("\n## assertEq32(bytes32,bytes32)\n"); - assertEq32("bytes 1", "bytes 2"); - emit log("\n"); - assertEq32("bytes 1", "bytes 2", err); - - emit log("\n## assertEq(bytes32,bytes32)\n"); - assertEq32("bytes 1", "bytes 2"); - emit log("\n"); - assertEq32("bytes 1", "bytes 2", err); - - emit log("\n## assertEq(uint,uint)\n"); - assertEq(uint(0), 1); - emit log("\n"); - assertEq(uint(0), 1, err); - - emit log("\n## assertEq(int,int)\n"); - assertEq(-1, -2); - emit log("\n"); - assertEq(-1, -2, err); - - emit log("\n## assertEqDecimal(int,int,uint)\n"); - assertEqDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertEqDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertEqDecimal(uint,uint,uint)\n"); - assertEqDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertGt(uint,uint)\n"); - assertGt(uint(0), 0); - emit log("\n"); - assertGt(uint(0), 0, err); - - emit log("\n## assertGt(int,int)\n"); - assertGt(-1, -1); - emit log("\n"); - assertGt(-1, -1, err); - - emit log("\n## assertGtDecimal(int,int,uint)\n"); - assertGtDecimal(-2.0e18, -1.1e18, 18); - emit log("\n"); - assertGtDecimal(-2.0e18, -1.1e18, 18, err); - - emit log("\n## assertGtDecimal(uint,uint,uint)\n"); - assertGtDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertGe(uint,uint)\n"); - assertGe(uint(0), 1); - emit log("\n"); - assertGe(uint(0), 1, err); - - emit log("\n## assertGe(int,int)\n"); - assertGe(-1, 0); - emit log("\n"); - assertGe(-1, 0, err); - - emit log("\n## assertGeDecimal(int,int,uint)\n"); - assertGeDecimal(-2.0e18, -1.1e18, 18); - emit log("\n"); - assertGeDecimal(-2.0e18, -1.1e18, 18, err); - - emit log("\n## assertGeDecimal(uint,uint,uint)\n"); - assertGeDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertLt(uint,uint)\n"); - assertLt(uint(0), 0); - emit log("\n"); - assertLt(uint(0), 0, err); - - emit log("\n## assertLt(int,int)\n"); - assertLt(-1, -1); - emit log("\n"); - assertLt(-1, -1, err); - - emit log("\n## assertLtDecimal(int,int,uint)\n"); - assertLtDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertLtDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertLtDecimal(uint,uint,uint)\n"); - assertLtDecimal(uint(2.0e18), 1.1e18, 18); - emit log("\n"); - assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); - - emit log("\n## assertLe(uint,uint)\n"); - assertLe(uint(1), 0); - emit log("\n"); - assertLe(uint(1), 0, err); - - emit log("\n## assertLe(int,int)\n"); - assertLe(0, -1); - emit log("\n"); - assertLe(0, -1, err); - - emit log("\n## assertLeDecimal(int,int,uint)\n"); - assertLeDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertLeDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertLeDecimal(uint,uint,uint)\n"); - assertLeDecimal(uint(2.0e18), 1.1e18, 18); - emit log("\n"); - assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); - - emit log("\n## assertEq(string,string)\n"); - string memory s1 = "string 1"; - string memory s2 = "string 2"; - assertEq(s1, s2); - emit log("\n"); - assertEq(s1, s2, err); - - emit log("\n## assertEq0(bytes,bytes)\n"); - assertEq0(hex"abcdef01", hex"abcdef02"); - emit log("\n"); - assertEq0(hex"abcdef01", hex"abcdef02", err); - } -} - -contract DemoTestWithSetUp { - function setUp() public { - } - function test_pass() public pure { - } -} diff --git a/lib/solady/lib/ds-test/src/test.sol b/lib/solady/lib/ds-test/src/test.sol deleted file mode 100644 index 515a3bd..0000000 --- a/lib/solady/lib/ds-test/src/test.sol +++ /dev/null @@ -1,469 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -pragma solidity >=0.5.0; - -contract DSTest { - event log (string); - event logs (bytes); - - event log_address (address); - event log_bytes32 (bytes32); - event log_int (int); - event log_uint (uint); - event log_bytes (bytes); - event log_string (string); - - event log_named_address (string key, address val); - event log_named_bytes32 (string key, bytes32 val); - event log_named_decimal_int (string key, int val, uint decimals); - event log_named_decimal_uint (string key, uint val, uint decimals); - event log_named_int (string key, int val); - event log_named_uint (string key, uint val); - event log_named_bytes (string key, bytes val); - event log_named_string (string key, string val); - - bool public IS_TEST = true; - bool private _failed; - - address constant HEVM_ADDRESS = - address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); - - modifier mayRevert() { _; } - modifier testopts(string memory) { _; } - - function failed() public returns (bool) { - if (_failed) { - return _failed; - } else { - bool globalFailed = false; - if (hasHEVMContext()) { - (, bytes memory retdata) = HEVM_ADDRESS.call( - abi.encodePacked( - bytes4(keccak256("load(address,bytes32)")), - abi.encode(HEVM_ADDRESS, bytes32("failed")) - ) - ); - globalFailed = abi.decode(retdata, (bool)); - } - return globalFailed; - } - } - - function fail() internal { - if (hasHEVMContext()) { - (bool status, ) = HEVM_ADDRESS.call( - abi.encodePacked( - bytes4(keccak256("store(address,bytes32,bytes32)")), - abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) - ) - ); - status; // Silence compiler warnings - } - _failed = true; - } - - function hasHEVMContext() internal view returns (bool) { - uint256 hevmCodeSize = 0; - assembly { - hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) - } - return hevmCodeSize > 0; - } - - modifier logs_gas() { - uint startGas = gasleft(); - _; - uint endGas = gasleft(); - emit log_named_uint("gas", startGas - endGas); - } - - function assertTrue(bool condition) internal { - if (!condition) { - emit log("Error: Assertion Failed"); - fail(); - } - } - - function assertTrue(bool condition, string memory err) internal { - if (!condition) { - emit log_named_string("Error", err); - assertTrue(condition); - } - } - - function assertEq(address a, address b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [address]"); - emit log_named_address(" Expected", b); - emit log_named_address(" Actual", a); - fail(); - } - } - function assertEq(address a, address b, string memory err) internal { - if (a != b) { - emit log_named_string ("Error", err); - assertEq(a, b); - } - } - - function assertEq(bytes32 a, bytes32 b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [bytes32]"); - emit log_named_bytes32(" Expected", b); - emit log_named_bytes32(" Actual", a); - fail(); - } - } - function assertEq(bytes32 a, bytes32 b, string memory err) internal { - if (a != b) { - emit log_named_string ("Error", err); - assertEq(a, b); - } - } - function assertEq32(bytes32 a, bytes32 b) internal { - assertEq(a, b); - } - function assertEq32(bytes32 a, bytes32 b, string memory err) internal { - assertEq(a, b, err); - } - - function assertEq(int a, int b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [int]"); - emit log_named_int(" Expected", b); - emit log_named_int(" Actual", a); - fail(); - } - } - function assertEq(int a, int b, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - function assertEq(uint a, uint b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [uint]"); - emit log_named_uint(" Expected", b); - emit log_named_uint(" Actual", a); - fail(); - } - } - function assertEq(uint a, uint b, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - function assertEqDecimal(int a, int b, uint decimals) internal { - if (a != b) { - emit log("Error: a == b not satisfied [decimal int]"); - emit log_named_decimal_int(" Expected", b, decimals); - emit log_named_decimal_int(" Actual", a, decimals); - fail(); - } - } - function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEqDecimal(a, b, decimals); - } - } - function assertEqDecimal(uint a, uint b, uint decimals) internal { - if (a != b) { - emit log("Error: a == b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Expected", b, decimals); - emit log_named_decimal_uint(" Actual", a, decimals); - fail(); - } - } - function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEqDecimal(a, b, decimals); - } - } - - function assertGt(uint a, uint b) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertGt(uint a, uint b, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGt(a, b); - } - } - function assertGt(int a, int b) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertGt(int a, int b, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGt(a, b); - } - } - function assertGtDecimal(int a, int b, uint decimals) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGtDecimal(a, b, decimals); - } - } - function assertGtDecimal(uint a, uint b, uint decimals) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGtDecimal(a, b, decimals); - } - } - - function assertGe(uint a, uint b) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertGe(uint a, uint b, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGe(a, b); - } - } - function assertGe(int a, int b) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertGe(int a, int b, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGe(a, b); - } - } - function assertGeDecimal(int a, int b, uint decimals) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - function assertGeDecimal(uint a, uint b, uint decimals) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - - function assertLt(uint a, uint b) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertLt(uint a, uint b, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLt(a, b); - } - } - function assertLt(int a, int b) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertLt(int a, int b, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLt(a, b); - } - } - function assertLtDecimal(int a, int b, uint decimals) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLtDecimal(a, b, decimals); - } - } - function assertLtDecimal(uint a, uint b, uint decimals) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLtDecimal(a, b, decimals); - } - } - - function assertLe(uint a, uint b) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertLe(uint a, uint b, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLe(a, b); - } - } - function assertLe(int a, int b) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertLe(int a, int b, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLe(a, b); - } - } - function assertLeDecimal(int a, int b, uint decimals) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLeDecimal(a, b, decimals); - } - } - function assertLeDecimal(uint a, uint b, uint decimals) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - - function assertEq(string memory a, string memory b) internal { - if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { - emit log("Error: a == b not satisfied [string]"); - emit log_named_string(" Expected", b); - emit log_named_string(" Actual", a); - fail(); - } - } - function assertEq(string memory a, string memory b, string memory err) internal { - if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - - function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { - ok = true; - if (a.length == b.length) { - for (uint i = 0; i < a.length; i++) { - if (a[i] != b[i]) { - ok = false; - } - } - } else { - ok = false; - } - } - function assertEq0(bytes memory a, bytes memory b) internal { - if (!checkEq0(a, b)) { - emit log("Error: a == b not satisfied [bytes]"); - emit log_named_bytes(" Expected", b); - emit log_named_bytes(" Actual", a); - fail(); - } - } - function assertEq0(bytes memory a, bytes memory b, string memory err) internal { - if (!checkEq0(a, b)) { - emit log_named_string("Error", err); - assertEq0(a, b); - } - } -} diff --git a/lib/solady/logo.svg b/lib/solady/logo.svg deleted file mode 100644 index 8589e73..0000000 --- a/lib/solady/logo.svg +++ /dev/null @@ -1 +0,0 @@ -solady diff --git a/lib/solady/package-lock.json b/lib/solady/package-lock.json deleted file mode 100644 index e58c913..0000000 --- a/lib/solady/package-lock.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "solady", - "version": "0.0.55", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "solady", - "version": "0.0.55", - "license": "MIT" - } - } -} diff --git a/lib/solady/package.json b/lib/solady/package.json deleted file mode 100644 index f471f5d..0000000 --- a/lib/solady/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "solady", - "license": "MIT", - "version": "0.0.145", - "description": "Optimized Solidity snippets.", - "files": [ - "src/**/*.sol", - "js/**/*" - ], - "main": "js/solady.js", - "module": "js/solady.js", - "types": "js/solady.d.ts", - "repository": { - "type": "git", - "url": "git+https://github.com/vectorized/solady.git" - } -} diff --git a/lib/solady/src/Milady.sol b/lib/solady/src/Milady.sol deleted file mode 100644 index 75748c2..0000000 --- a/lib/solady/src/Milady.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./accounts/Receiver.sol"; -import "./accounts/ERC1271.sol"; -import "./accounts/ERC4337.sol"; -import "./accounts/ERC4337Factory.sol"; -import "./accounts/ERC6551.sol"; -import "./accounts/ERC6551Proxy.sol"; -import "./auth/Ownable.sol"; -import "./auth/OwnableRoles.sol"; -import "./tokens/WETH.sol"; -import "./tokens/ERC20.sol"; -import "./tokens/ERC4626.sol"; -import "./tokens/ERC721.sol"; -import "./tokens/ERC2981.sol"; -import "./tokens/ERC1155.sol"; -import "./tokens/ERC6909.sol"; -import "./utils/Base64.sol"; -import "./utils/CREATE3.sol"; -import "./utils/Clone.sol"; -import "./utils/DateTimeLib.sol"; -import "./utils/DynamicBufferLib.sol"; -import "./utils/ECDSA.sol"; -import "./utils/EIP712.sol"; -import "./utils/ERC1967Factory.sol"; -import "./utils/ERC1967FactoryConstants.sol"; -import "./utils/FixedPointMathLib.sol"; -import "./utils/GasBurnerLib.sol"; -import "./utils/JSONParserLib.sol"; -import "./utils/LibBit.sol"; -import "./utils/LibBitmap.sol"; -import "./utils/LibMap.sol"; -import "./utils/LibClone.sol"; -import "./utils/LibPRNG.sol"; -import "./utils/LibRLP.sol"; -import "./utils/LibSort.sol"; -import "./utils/LibString.sol"; -import "./utils/LibZip.sol"; -import "./utils/MerkleProofLib.sol"; -import "./utils/MetadataReaderLib.sol"; -import "./utils/MinHeapLib.sol"; -import "./utils/Multicallable.sol"; -import "./utils/RedBlackTreeLib.sol"; -import "./utils/SSTORE2.sol"; -import "./utils/SafeCastLib.sol"; -import "./utils/SafeTransferLib.sol"; -import "./utils/SignatureCheckerLib.sol"; -import "./utils/UUPSUpgradeable.sol"; - -library Milady { - string internal constant WEBSITE = "https://miladymaker.net"; - - address internal constant CONTRACT = 0x5Af0D9827E0c53E4799BB226655A1de152A425a5; -} diff --git a/lib/solady/src/accounts/ERC1271.sol b/lib/solady/src/accounts/ERC1271.sol deleted file mode 100644 index 61e4aeb..0000000 --- a/lib/solady/src/accounts/ERC1271.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {EIP712} from "../utils/EIP712.sol"; -import {SignatureCheckerLib} from "../utils/SignatureCheckerLib.sol"; - -/// @notice ERC1271 mixin with nested EIP-712 approach. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC1271.sol) -abstract contract ERC1271 is EIP712 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC1271 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the ERC1271 signer. - /// Override to return the signer `isValidSignature` checks against. - function _erc1271Signer() internal view virtual returns (address); - - /// @dev Validates the signature with ERC1271 return, - /// so that this account can also be used as a signer. - /// - /// This implementation uses ECDSA recovery. It also uses a nested EIP-712 approach to - /// prevent signature replays when a single EOA owns multiple smart contract accounts, - /// while still enabling wallet UIs (e.g. Metamask) to show the EIP-712 values. - /// - /// For the nested EIP-712 workflow, the final hash will be: - /// ``` - /// keccak256(\x19\x01 || DOMAIN_SEP_A || - /// hashStruct(Parent({ - /// childHash: keccak256(\x19\x01 || DOMAIN_SEP_B || hashStruct(originalStruct)), - /// child: hashStruct(originalStruct) - /// })) - /// ) - /// ``` - /// where `||` denotes the concatenation operator for bytes. - /// The signature will be `r || s || v || PARENT_TYPEHASH || DOMAIN_SEP_B || child`. - /// - /// The `DOMAIN_SEP_B` and `child` will be used to verify if `childHash` is indeed correct. - /// - /// For the `personal_sign` workflow, the final hash will be: - /// ``` - /// keccak256(\x19\x01 || DOMAIN_SEP_A || - /// hashStruct(Parent({ - /// childHash: personalSign(someBytes) - /// })) - /// ) - /// ``` - /// where `||` denotes the concatenation operator for bytes. - /// The signature will be `r || s || v || PARENT_TYPEHASH`. - /// - /// For demo and typescript code, see: - /// - https://github.com/junomonster/nested-eip-712 - /// - https://github.com/frangio/eip712-wrapper-for-eip1271 - /// - /// Of course, if you are a wallet app maker and can update your app's UI at will, - /// you can choose a more minimalistic signature scheme like - /// `keccak256(abi.encode(address(this), hash))` instead of all these acrobatics. - /// All these are just for widespead out-of-the-box compatibility with other wallet apps. - /// - /// The `hash` parameter is the `childHash`. - function isValidSignature(bytes32 hash, bytes calldata signature) - public - view - virtual - returns (bytes4 result) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - let o := add(signature.offset, sub(signature.length, 0x60)) - calldatacopy(0x00, o, 0x60) // Copy the `DOMAIN_SEP_B` and child's structHash. - mstore(0x00, 0x1901) // Store the "\x19\x01" prefix, overwriting 0x00. - for {} 1 {} { - // Use the nested EIP-712 workflow if the reconstructed childHash matches, - // and the signature is at least 96 bytes long. - if iszero(or(xor(keccak256(0x1e, 0x42), hash), lt(signature.length, 0x60))) { - // Truncate the `signature.length` by 3 words (96 bytes). - signature.length := sub(signature.length, 0x60) - mstore(0x00, calldataload(o)) // Store the `PARENT_TYPEHASH`. - mstore(0x20, hash) // Store the `childHash`. - // The child's structHash is already at 0x40. - hash := keccak256(0x00, 0x60) // Compute the parent's structHash. - break - } - // Else, use the `personal_sign` workflow. - // Truncate the `signature.length` by 1 word (32 bytes), until zero. - signature.length := mul(gt(signature.length, 0x20), sub(signature.length, 0x20)) - // The `PARENT_TYPEHASH` is already at 0x40. - mstore(0x60, hash) // Store the `childHash`. - hash := keccak256(0x40, 0x40) // Compute the parent's structHash. - mstore(0x60, 0) // Restore the zero pointer. - break - } - mstore(0x40, m) // Restore the free memory pointer. - } - bool success = SignatureCheckerLib.isValidSignatureNowCalldata( - _erc1271Signer(), _hashTypedData(hash), signature - ); - /// @solidity memory-safe-assembly - assembly { - // `success ? bytes4(keccak256("isValidSignature(bytes32,bytes)")) : 0xffffffff`. - result := shl(224, or(0x1626ba7e, sub(0, iszero(success)))) - } - } -} diff --git a/lib/solady/src/accounts/ERC4337.sol b/lib/solady/src/accounts/ERC4337.sol deleted file mode 100644 index 093a22e..0000000 --- a/lib/solady/src/accounts/ERC4337.sol +++ /dev/null @@ -1,386 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Receiver} from "./Receiver.sol"; -import {LibZip} from "../utils/LibZip.sol"; -import {Ownable} from "../auth/Ownable.sol"; -import {UUPSUpgradeable} from "../utils/UUPSUpgradeable.sol"; -import {SignatureCheckerLib, ERC1271} from "../accounts/ERC1271.sol"; - -/// @notice Simple ERC4337 account implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC4337.sol) -/// @author Infinitism (https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol) -/// -/// Recommended usage: -/// 1. Deploy the ERC4337 as an implementation contract, and verify it on Etherscan. -/// 2. Create a factory that uses `LibClone.deployERC1967` or -/// `LibClone.deployDeterministicERC1967` to clone the implementation. -/// See: `ERC4337Factory.sol`. -abstract contract ERC4337 is Ownable, UUPSUpgradeable, Receiver, ERC1271 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ERC4337 user operation (userOp) struct. - struct UserOperation { - address sender; - uint256 nonce; - bytes initCode; - bytes callData; - uint256 callGasLimit; - uint256 verificationGasLimit; - uint256 preVerificationGas; - uint256 maxFeePerGas; - uint256 maxPriorityFeePerGas; - bytes paymasterAndData; - bytes signature; - } - - /// @dev Call struct for the `executeBatch` function. - struct Call { - address target; - uint256 value; - bytes data; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INITIALIZER */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Initializes the account with the owner. Can only be called once. - function initialize(address newOwner) public payable virtual { - _initializeOwner(newOwner); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ENTRY POINT */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the canonical ERC4337 EntryPoint contract. - /// Override this function to return a different EntryPoint. - function entryPoint() public view virtual returns (address) { - return 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* VALIDATION OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Validates the signature and nonce. - /// The EntryPoint will make the call to the recipient only if - /// this validation call returns successfully. - /// - /// Signature failure should be reported by returning 1 (see: `_validateSignature`). - /// This allows making a "simulation call" without a valid signature. - /// Other failures (e.g. nonce mismatch, or invalid signature format) - /// should still revert to signal failure. - function validateUserOp( - UserOperation calldata userOp, - bytes32 userOpHash, - uint256 missingAccountFunds - ) - external - payable - virtual - onlyEntryPoint - payPrefund(missingAccountFunds) - returns (uint256 validationData) - { - validationData = _validateSignature(userOp, userOpHash); - _validateNonce(userOp.nonce); - } - - /// @dev Validate `userOp.signature` for the `userOpHash`. - function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) - internal - virtual - returns (uint256 validationData) - { - bool success = SignatureCheckerLib.isValidSignatureNowCalldata( - owner(), SignatureCheckerLib.toEthSignedMessageHash(userOpHash), userOp.signature - ); - /// @solidity memory-safe-assembly - assembly { - // Returns 0 if the recovered address matches the owner. - // Else returns 1, which is equivalent to: - // `(success ? 0 : 1) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48))` - // where `validUntil` is 0 (indefinite) and `validAfter` is 0. - validationData := iszero(success) - } - } - - /// @dev Override to validate the nonce of the userOp. - /// This method may validate the nonce requirement of this account. - /// e.g. - /// To limit the nonce to use sequenced userOps only (no "out of order" userOps): - /// `require(nonce < type(uint64).max)` - /// For a hypothetical account that *requires* the nonce to be out-of-order: - /// `require(nonce & type(uint64).max == 0)` - /// - /// The actual nonce uniqueness is managed by the EntryPoint, and thus no other - /// action is needed by the account itself. - function _validateNonce(uint256 nonce) internal virtual { - nonce = nonce; // Silence unused variable warning. - } - - /// @dev Sends to the EntryPoint (i.e. `msg.sender`) the missing funds for this transaction. - /// Subclass MAY override this modifier for better funds management. - /// (e.g. send to the EntryPoint more than the minimum required, so that in future transactions - /// it will not be required to send again) - /// - /// `missingAccountFunds` is the minimum value this modifier should send the EntryPoint, - /// which MAY be zero, in case there is enough deposit, or the userOp has a paymaster. - modifier payPrefund(uint256 missingAccountFunds) virtual { - _; - /// @solidity memory-safe-assembly - assembly { - if missingAccountFunds { - // Ignore failure (it's EntryPoint's job to verify, not the account's). - pop(call(gas(), caller(), missingAccountFunds, codesize(), 0x00, codesize(), 0x00)) - } - } - } - - /// @dev Requires that the caller is the EntryPoint. - modifier onlyEntryPoint() virtual { - if (msg.sender != entryPoint()) revert Unauthorized(); - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EXECUTION OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Execute a call from this account. - function execute(address target, uint256 value, bytes calldata data) - public - payable - virtual - onlyEntryPointOrOwner - returns (bytes memory result) - { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - calldatacopy(result, data.offset, data.length) - if iszero(call(gas(), target, value, result, data.length, codesize(), 0x00)) { - // Bubble up the revert if the call reverts. - returndatacopy(result, 0x00, returndatasize()) - revert(result, returndatasize()) - } - mstore(result, returndatasize()) // Store the length. - let o := add(result, 0x20) - returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. - mstore(0x40, add(o, returndatasize())) // Allocate the memory. - } - } - - /// @dev Execute a sequence of calls from this account. - function executeBatch(Call[] calldata calls) - public - payable - virtual - onlyEntryPointOrOwner - returns (bytes[] memory results) - { - /// @solidity memory-safe-assembly - assembly { - results := mload(0x40) - mstore(results, calls.length) - let r := add(0x20, results) - let m := add(r, shl(5, calls.length)) - calldatacopy(r, calls.offset, shl(5, calls.length)) - for { let end := m } iszero(eq(r, end)) { r := add(r, 0x20) } { - let e := add(calls.offset, mload(r)) - let o := add(e, calldataload(add(e, 0x40))) - calldatacopy(m, add(o, 0x20), calldataload(o)) - // forgefmt: disable-next-item - if iszero(call(gas(), calldataload(e), calldataload(add(e, 0x20)), - m, calldataload(o), codesize(), 0x00)) { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - mstore(r, m) // Append `m` into `results`. - mstore(m, returndatasize()) // Store the length, - let p := add(m, 0x20) - returndatacopy(p, 0x00, returndatasize()) // and copy the returndata. - m := add(p, returndatasize()) // Advance `m`. - } - mstore(0x40, m) // Allocate the memory. - } - } - - /// @dev Execute a delegatecall with `delegate` on this account. - function delegateExecute(address delegate, bytes calldata data) - public - payable - virtual - onlyEntryPointOrOwner - delegateExecuteGuard - returns (bytes memory result) - { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - calldatacopy(result, data.offset, data.length) - // Forwards the `data` to `delegate` via delegatecall. - if iszero(delegatecall(gas(), delegate, result, data.length, codesize(), 0x00)) { - // Bubble up the revert if the call reverts. - returndatacopy(result, 0x00, returndatasize()) - revert(result, returndatasize()) - } - mstore(result, returndatasize()) // Store the length. - let o := add(result, 0x20) - returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. - mstore(0x40, add(o, returndatasize())) // Allocate the memory. - } - } - - /// @dev Ensures that the owner and implementation slots' values aren't changed. - /// You can override this modifier to ensure the sanctity of other storage slots too. - modifier delegateExecuteGuard() virtual { - bytes32 ownerSlotValue; - bytes32 implementationSlotValue; - /// @solidity memory-safe-assembly - assembly { - implementationSlotValue := sload(_ERC1967_IMPLEMENTATION_SLOT) - ownerSlotValue := sload(_OWNER_SLOT) - } - _; - /// @solidity memory-safe-assembly - assembly { - if iszero( - and( - eq(implementationSlotValue, sload(_ERC1967_IMPLEMENTATION_SLOT)), - eq(ownerSlotValue, sload(_OWNER_SLOT)) - ) - ) { revert(codesize(), 0x00) } - } - } - - /// @dev Requires that the caller is the EntryPoint, the owner, or the account itself. - modifier onlyEntryPointOrOwner() virtual { - if (msg.sender != entryPoint()) _checkOwner(); - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DIRECT STORAGE OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the raw storage value at `storageSlot`. - function storageLoad(bytes32 storageSlot) public view virtual returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - result := sload(storageSlot) - } - } - - /// @dev Writes the raw storage value at `storageSlot`. - function storageStore(bytes32 storageSlot, bytes32 storageValue) - public - payable - virtual - onlyEntryPointOrOwner - storageStoreGuard(storageSlot) - { - /// @solidity memory-safe-assembly - assembly { - sstore(storageSlot, storageValue) - } - } - - /// @dev Ensures that the `storageSlot` is not prohibited for direct storage writes. - /// You can override this modifier to ensure the sanctity of other storage slots too. - modifier storageStoreGuard(bytes32 storageSlot) virtual { - /// @solidity memory-safe-assembly - assembly { - if or(eq(storageSlot, _OWNER_SLOT), eq(storageSlot, _ERC1967_IMPLEMENTATION_SLOT)) { - revert(codesize(), 0x00) - } - } - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DEPOSIT OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the account's balance on the EntryPoint. - function getDeposit() public view virtual returns (uint256 result) { - address ep = entryPoint(); - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, address()) // Store the `account` argument. - mstore(0x00, 0x70a08231) // `balanceOf(address)`. - result := - mul( // Returns 0 if the EntryPoint does not exist. - mload(0x20), - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), ep, 0x1c, 0x24, 0x20, 0x20) - ) - ) - } - } - - /// @dev Deposit more funds for this account in the EntryPoint. - function addDeposit() public payable virtual { - address ep = entryPoint(); - /// @solidity memory-safe-assembly - assembly { - // The EntryPoint has balance accounting logic in the `receive()` function. - // forgefmt: disable-next-item - if iszero(mul(extcodesize(ep), call(gas(), ep, callvalue(), codesize(), 0x00, codesize(), 0x00))) { - revert(codesize(), 0x00) // For gas estimation. - } - } - } - - /// @dev Withdraw ETH from the account's deposit on the EntryPoint. - function withdrawDepositTo(address to, uint256 amount) public payable virtual onlyOwner { - address ep = entryPoint(); - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0x205c2878000000000000000000000000) // `withdrawTo(address,uint256)`. - if iszero(mul(extcodesize(ep), call(gas(), ep, 0, 0x10, 0x44, codesize(), 0x00))) { - returndatacopy(mload(0x40), 0x00, returndatasize()) - revert(mload(0x40), returndatasize()) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OVERRIDES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Requires that the caller is the owner or the account itself. - /// This override affects the `onlyOwner` modifier. - function _checkOwner() internal view virtual override(Ownable) { - if (msg.sender != owner()) if (msg.sender != address(this)) revert Unauthorized(); - } - - /// @dev To prevent double-initialization (reuses the owner storage slot for efficiency). - function _guardInitializeOwner() internal pure virtual override(Ownable) returns (bool) { - return true; - } - - /// @dev Uses the `owner` as the ERC1271 signer. - function _erc1271Signer() internal view virtual override(ERC1271) returns (address) { - return owner(); - } - - /// @dev To ensure that only the owner or the account itself can upgrade the implementation. - function _authorizeUpgrade(address) internal virtual override(UUPSUpgradeable) onlyOwner {} - - /// @dev Handle token callbacks. If no token callback is triggered, - /// use `LibZip.cdFallback` for generalized calldata decompression. - /// If you don't need either, re-override this function. - fallback() external payable virtual override(Receiver) receiverFallback { - LibZip.cdFallback(); - } -} diff --git a/lib/solady/src/accounts/ERC4337Factory.sol b/lib/solady/src/accounts/ERC4337Factory.sol deleted file mode 100644 index 8d0e2bc..0000000 --- a/lib/solady/src/accounts/ERC4337Factory.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {LibClone} from "../utils/LibClone.sol"; - -/// @notice Simple ERC4337 account factory implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC4337Factory.sol) -/// -/// Note: -/// - Unlike the ERC1967Factory, this factory does NOT store any admin info on the factory itself. -/// The deployed ERC4337 accounts are minimal ERC1967 proxies to an ERC4337 implementation. -/// The proxy bytecode does NOT contain any upgrading logic. -/// - This factory does NOT contain any logic for upgrading the ERC4337 accounts. -/// Upgrading must be done via UUPS logic on the accounts themselves. -/// - The ERC4337 standard expects the factory to use deterministic deployment. -/// As such, this factory does not include any non-deterministic deployment methods. -contract ERC4337Factory { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* IMMUTABLES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Address of the ERC4337 implementation. - address public immutable implementation; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTRUCTOR */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - constructor(address erc4337) payable { - implementation = erc4337; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DEPLOY FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deploys an ERC4337 account with `salt` and returns its deterministic address. - /// If the account is already deployed, it will simply return its address. - /// Any `msg.value` will simply be forwarded to the account, regardless. - function createAccount(address owner, bytes32 salt) public payable virtual returns (address) { - // Check that the salt is tied to the owner if required, regardless. - LibClone.checkStartsWith(salt, owner); - // Constructor data is optional, and is omitted for easier Etherscan verification. - (bool alreadyDeployed, address account) = - LibClone.createDeterministicERC1967(msg.value, implementation, salt); - - if (!alreadyDeployed) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, owner) // Store the `owner` argument. - mstore(0x00, 0xc4d66de8000000000000000000000000) // `initialize(address)`. - if iszero(call(gas(), account, 0, 0x10, 0x24, codesize(), 0x00)) { - returndatacopy(mload(0x40), 0x00, returndatasize()) - revert(mload(0x40), returndatasize()) - } - } - } - return account; - } - - /// @dev Returns the deterministic address of the account created via `createAccount`. - function getAddress(bytes32 salt) public view virtual returns (address) { - return LibClone.predictDeterministicAddressERC1967(implementation, salt, address(this)); - } - - /// @dev Returns the initialization code hash of the ERC4337 account (a minimal ERC1967 proxy). - /// Used for mining vanity addresses with create2crunch. - function initCodeHash() public view virtual returns (bytes32) { - return LibClone.initCodeHashERC1967(implementation); - } -} diff --git a/lib/solady/src/accounts/ERC6551.sol b/lib/solady/src/accounts/ERC6551.sol deleted file mode 100644 index 718d358..0000000 --- a/lib/solady/src/accounts/ERC6551.sol +++ /dev/null @@ -1,321 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Receiver} from "./Receiver.sol"; -import {ERC1271} from "./ERC1271.sol"; -import {LibZip} from "../utils/LibZip.sol"; -import {UUPSUpgradeable} from "../utils/UUPSUpgradeable.sol"; - -/// @notice Simple ERC6551 account implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC6551.sol) -/// @author ERC6551 team (https://github.com/erc6551/reference/blob/main/src/examples/upgradeable/ERC6551AccountUpgradeable.sol) -/// -/// Recommended usage (regular): -/// 1. Deploy the ERC6551 as an implementation contract, and verify it on Etherscan. -/// 2. Use the canonical ERC6551Registry to deploy a clone to the ERC6551 implementation. -/// The UUPSUpgradeable functions will simply become no-ops. -/// -/// Recommended usage (upgradeable): -/// 1. Deploy the ERC6551 as an implementation contract, and verify it on Etherscan. -/// 2. Deploy the ERC6551Proxy pointing to the implementation. -/// This relay proxy is required, but Etherscan verification of it is optional. -/// 3. Use the canonical ERC6551Registry to deploy a clone to the ERC6551Proxy. -/// If you want to reveal the "Read as Proxy" and "Write as Proxy" tabs on Etherscan, -/// send 0 ETH to the clone to initialize its ERC1967 implementation slot, -/// the click on "Is this a proxy?" on the clone's page on Etherscan. -/// -/// Note: -/// - ERC6551 accounts are not compatible with ERC4337 -/// (at least not without crazy hacks) -/// due to storage access limitations during ERC4337 UserOp validation. -/// - Please refer to the official [ERC6551](https://github.com/erc6551/reference) reference -/// for latest updates on the ERC6551 standard, as well as canonical registry information. -abstract contract ERC6551 is UUPSUpgradeable, Receiver, ERC1271 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Call struct for the `executeBatch` function. - struct Call { - address target; - uint256 value; - bytes data; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The caller is not authorized to call the function. - error Unauthorized(); - - /// @dev The operation is not supported. - error OperationNotSupported(); - - /// @dev Self ownership detected. - error SelfOwnDetected(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ERC6551 state slot is given by: - /// `bytes32(~uint256(uint32(bytes4(keccak256("_ERC6551_STATE_SLOT_NOT")))))`. - /// It is intentionally chosen to be a high value - /// to avoid collision with lower slots. - /// The choice of manual storage layout is to enable compatibility - /// with both regular and upgradeable contracts. - uint256 internal constant _ERC6551_STATE_SLOT = - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffb919c7a5; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* TOKEN-BOUND OWNERSHIP OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the token-bound information. - function token() - public - view - virtual - returns (uint256 chainId, address tokenContract, uint256 tokenId) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - extcodecopy(address(), 0x00, 0x4d, 0x60) - chainId := mload(0x00) - tokenContract := mload(0x20) // Upper 96 bits will be clean. - tokenId := mload(0x40) - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Returns the owner of the contract. - function owner() public view virtual returns (address result) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - extcodecopy(address(), 0x00, 0x4d, 0x60) - if eq(mload(0x00), chainid()) { - let tokenContract := mload(0x20) - // `tokenId` is already at 0x40. - mstore(0x20, 0x6352211e) // `ownerOf(uint256)`. - result := - mul( // Returns `address(0)` on failure or if contract does not exist. - mload(0x20), - and( - gt(returndatasize(), 0x1f), - staticcall(gas(), tokenContract, 0x3c, 0x24, 0x20, 0x20) - ) - ) - } - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Returns if `signer` is an authorized signer. - function _isValidSigner(address signer) internal view virtual returns (bool) { - return signer == owner(); - } - - /// @dev Requires that the caller is a valid signer (i.e. the owner), or the contract itself. - modifier onlyValidSigner() virtual { - if (!_isValidSigner(msg.sender)) if (msg.sender != address(this)) revert Unauthorized(); - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STATE OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the current value of the state counter. - function state() public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - result := sload(_ERC6551_STATE_SLOT) - } - } - - /// @dev Increments the state counter. This modifier is required for every - /// public / external function that may modify storage or emit events. - modifier incrementState() virtual { - /// @solidity memory-safe-assembly - assembly { - let s := _ERC6551_STATE_SLOT - sstore(s, add(1, sload(s))) - } - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EXECUTION OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Execute a call from this account. - function execute(address target, uint256 value, bytes calldata data, uint8 operation) - public - payable - virtual - onlyValidSigner - onlyValidExecuteOperation(operation) - incrementState - returns (bytes memory result) - { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - calldatacopy(result, data.offset, data.length) - if iszero(call(gas(), target, value, result, data.length, codesize(), 0x00)) { - // Bubble up the revert if the call reverts. - returndatacopy(result, 0x00, returndatasize()) - revert(result, returndatasize()) - } - mstore(result, returndatasize()) // Store the length. - let o := add(result, 0x20) - returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. - mstore(0x40, add(o, returndatasize())) // Allocate the memory. - } - } - - /// @dev Execute a sequence of calls from this account. - function executeBatch(Call[] calldata calls, uint8 operation) - public - payable - virtual - onlyValidSigner - onlyValidExecuteOperation(operation) - incrementState - returns (bytes[] memory results) - { - /// @solidity memory-safe-assembly - assembly { - results := mload(0x40) - mstore(results, calls.length) - let r := add(0x20, results) - let m := add(r, shl(5, calls.length)) - calldatacopy(r, calls.offset, shl(5, calls.length)) - for { let end := m } iszero(eq(r, end)) { r := add(r, 0x20) } { - let e := add(calls.offset, mload(r)) - let o := add(e, calldataload(add(e, 0x40))) - calldatacopy(m, add(o, 0x20), calldataload(o)) - // forgefmt: disable-next-item - if iszero(call(gas(), calldataload(e), calldataload(add(e, 0x20)), - m, calldataload(o), codesize(), 0x00)) { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - mstore(r, m) // Append `m` into `results`. - mstore(m, returndatasize()) // Store the length, - let p := add(m, 0x20) - returndatacopy(p, 0x00, returndatasize()) // and copy the returndata. - m := add(p, returndatasize()) // Advance `m`. - } - mstore(0x40, m) // Allocate the memory. - } - } - - /// @dev Requires that the execute `operation` is supported. - modifier onlyValidExecuteOperation(uint8 operation) virtual { - if (operation != 0) revert OperationNotSupported(); - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC165 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns true if this contract implements the interface defined by `interfaceId`. - /// See: https://eips.ethereum.org/EIPS/eip-165 - /// This function call must use less than 30000 gas. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, interfaceId) - // ERC165: 0x01ffc9a7, ERC6551: 0x6faff5f1, ERC6551Executable: 0x74420f4c. - result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x6faff5f1)), eq(s, 0x74420f4c)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OVERRIDES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev To ensure that only the owner or the account itself can upgrade the implementation. - function _authorizeUpgrade(address) - internal - virtual - override(UUPSUpgradeable) - onlyValidSigner - incrementState - {} - - /// @dev Uses the `owner` as the ERC1271 signer. - function _erc1271Signer() internal view virtual override(ERC1271) returns (address) { - return owner(); - } - - /// @dev For handling token callbacks. - /// Safe-transferred ERC721 tokens will trigger a ownership cycle check. - modifier receiverFallback() override(Receiver) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, calldataload(0x00)) - // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. - if eq(s, 0x150b7a02) { - extcodecopy(address(), 0x00, 0x4d, 0x60) // `chainId`, `tokenContract`, `tokenId`. - mstore(0x60, 0xfc0c546a) // `token()`. - for {} 1 {} { - let tokenContract := mload(0x20) - // `tokenId` is already at 0x40. - mstore(0x20, 0x6352211e) // `ownerOf(uint256)`. - let chainsEq := eq(mload(0x00), chainid()) - let currentOwner := - mul( - mload(0x20), - and( - and(gt(returndatasize(), 0x1f), chainsEq), - staticcall(gas(), tokenContract, 0x3c, 0x24, 0x20, 0x20) - ) - ) - if iszero( - or( - eq(currentOwner, address()), - and( - and(chainsEq, eq(tokenContract, caller())), - eq(mload(0x40), calldataload(0x44)) - ) - ) - ) { - if iszero( - and( - gt(returndatasize(), 0x5f), - staticcall(gas(), currentOwner, 0x7c, 0x04, 0x00, 0x60) - ) - ) { - mstore(0x40, s) // Store `msg.sig`. - return(0x5c, 0x20) // Return `msg.sig`. - } - continue - } - mstore(0x00, 0xaed146d3) // `SelfOwnDetected()`. - revert(0x1c, 0x04) - } - } - // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. - // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. - if or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81)) { - mstore(0x20, s) // Store `msg.sig`. - return(0x3c, 0x20) // Return `msg.sig`. - } - } - _; - } - - /// @dev Handle token callbacks. If no token callback is triggered, - /// use `LibZip.cdFallback` for generalized calldata decompression. - /// If you don't need either, re-override this function. - fallback() external payable virtual override(Receiver) receiverFallback { - LibZip.cdFallback(); - } -} diff --git a/lib/solady/src/accounts/ERC6551Proxy.sol b/lib/solady/src/accounts/ERC6551Proxy.sol deleted file mode 100644 index eb82b74..0000000 --- a/lib/solady/src/accounts/ERC6551Proxy.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Relay proxy for upgradeable ERC6551 accounts. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/accounts/ERC6551Proxy.sol) -/// @author ERC6551 team (https://github.com/erc6551/reference/blob/main/src/examples/upgradeable/ERC6551AccountProxy.sol) -/// -/// Note: This relay proxy is required for upgradeable ERC6551 accounts. -/// -/// ERC6551 clone -> ERC6551Proxy (relay) -> ERC6551 account implementation. -/// -/// This relay proxy also allows for correctly revealing the -/// "Read as Proxy" and "Write as Proxy" tabs on Etherscan. -/// -/// After using the registry to deploy a ERC6551 clone pointing to this relay proxy, -/// users must send 0 ETH to the clone before clicking on "Is this a proxy?" on Etherscan. -/// Verification of this relay proxy on Etherscan is optional. -contract ERC6551Proxy { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* IMMUTABLES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The default implementation. - uint256 internal immutable _defaultImplementation; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ERC-1967 storage slot for the implementation in the proxy. - /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`. - bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTRUCTOR */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - constructor(address defaultImplementation) payable { - _defaultImplementation = uint256(uint160(defaultImplementation)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* FALLBACK */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - fallback() external payable virtual { - uint256 d = _defaultImplementation; - assembly { - mstore(0x40, returndatasize()) // Some optimization trick. - calldatacopy(returndatasize(), returndatasize(), calldatasize()) - let implementation := sload(_ERC1967_IMPLEMENTATION_SLOT) - // If the implementation is zero, initialize it to the default. - if iszero(implementation) { - implementation := d - // Only initialize if the calldatasize is zero, so that staticcalls to - // functions (which will have 4-byte function selectors) won't revert. - // Some users may be fine without Etherscan proxy detection and thus may - // choose to not initialize the ERC1967 implementation slot. - if iszero(calldatasize()) { sstore(_ERC1967_IMPLEMENTATION_SLOT, d) } - } - // forgefmt: disable-next-item - if iszero(delegatecall(gas(), implementation, - returndatasize(), calldatasize(), codesize(), returndatasize())) { - returndatacopy(0x00, 0x00, returndatasize()) - revert(0x00, returndatasize()) - } - returndatacopy(0x00, 0x00, returndatasize()) - return(0x00, returndatasize()) - } - } -} diff --git a/lib/solady/src/accounts/Receiver.sol b/lib/solady/src/accounts/Receiver.sol deleted file mode 100644 index 372038b..0000000 --- a/lib/solady/src/accounts/Receiver.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Receiver mixin for ETH and safe-transferred ERC721 and ERC1155 tokens. -/// @author Solady (https://github.com/Vectorized/solady/blob/main/src/accounts/Receiver.sol) -/// -/// @dev Note: -/// - Handles all ERC721 and ERC1155 token safety callbacks. -/// - Collapses function table gas overhead and code size. -/// - Utilizes fallback so unknown calldata will pass on. -abstract contract Receiver { - /// @dev For receiving ETH. - receive() external payable virtual {} - - /// @dev Fallback function with the `receiverFallback` modifier. - fallback() external payable virtual receiverFallback {} - - /// @dev Modifier for the fallback function to handle token callbacks. - modifier receiverFallback() virtual { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, calldataload(0)) - // 0x150b7a02: `onERC721Received(address,address,uint256,bytes)`. - // 0xf23a6e61: `onERC1155Received(address,address,uint256,uint256,bytes)`. - // 0xbc197c81: `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. - if or(eq(s, 0x150b7a02), or(eq(s, 0xf23a6e61), eq(s, 0xbc197c81))) { - mstore(0x20, s) // Store `msg.sig`. - return(0x3c, 0x20) // Return `msg.sig`. - } - } - _; - } -} diff --git a/lib/solady/src/auth/Ownable.sol b/lib/solady/src/auth/Ownable.sol deleted file mode 100644 index a9d3214..0000000 --- a/lib/solady/src/auth/Ownable.sol +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple single owner authorization mixin. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) -/// -/// @dev Note: -/// This implementation does NOT auto-initialize the owner to `msg.sender`. -/// You MUST call the `_initializeOwner` in the constructor / initializer. -/// -/// While the ownable portion follows -/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, -/// the nomenclature for the 2-step ownership handover may be unique to this codebase. -abstract contract Ownable { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The caller is not authorized to call the function. - error Unauthorized(); - - /// @dev The `newOwner` cannot be the zero address. - error NewOwnerIsZeroAddress(); - - /// @dev The `pendingOwner` does not have a valid handover request. - error NoHandoverRequest(); - - /// @dev Cannot double-initialize. - error AlreadyInitialized(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ownership is transferred from `oldOwner` to `newOwner`. - /// This event is intentionally kept the same as OpenZeppelin's Ownable to be - /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), - /// despite it not being as lightweight as a single argument event. - event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); - - /// @dev An ownership handover to `pendingOwner` has been requested. - event OwnershipHandoverRequested(address indexed pendingOwner); - - /// @dev The ownership handover to `pendingOwner` has been canceled. - event OwnershipHandoverCanceled(address indexed pendingOwner); - - /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. - uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = - 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; - - /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. - uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = - 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; - - /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. - uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = - 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The owner slot is given by: - /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. - /// It is intentionally chosen to be a high value - /// to avoid collision with lower slots. - /// The choice of manual storage layout is to enable compatibility - /// with both regular and upgradeable contracts. - bytes32 internal constant _OWNER_SLOT = - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; - - /// The ownership handover slot of `newOwner` is given by: - /// ``` - /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) - /// let handoverSlot := keccak256(0x00, 0x20) - /// ``` - /// It stores the expiry timestamp of the two-step ownership handover. - uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. - function _guardInitializeOwner() internal pure virtual returns (bool guard) {} - - /// @dev Initializes the owner directly without authorization guard. - /// This function must be called upon initialization, - /// regardless of whether the contract is upgradeable or not. - /// This is to enable generalization to both regular and upgradeable contracts, - /// and to save gas in case the initial owner is not the caller. - /// For performance reasons, this function will not check if there - /// is an existing owner. - function _initializeOwner(address newOwner) internal virtual { - if (_guardInitializeOwner()) { - /// @solidity memory-safe-assembly - assembly { - let ownerSlot := _OWNER_SLOT - if sload(ownerSlot) { - mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. - revert(0x1c, 0x04) - } - // Clean the upper 96 bits. - newOwner := shr(96, shl(96, newOwner)) - // Store the new value. - sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) - // Emit the {OwnershipTransferred} event. - log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) - } - } else { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits. - newOwner := shr(96, shl(96, newOwner)) - // Store the new value. - sstore(_OWNER_SLOT, newOwner) - // Emit the {OwnershipTransferred} event. - log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) - } - } - } - - /// @dev Sets the owner directly without authorization guard. - function _setOwner(address newOwner) internal virtual { - if (_guardInitializeOwner()) { - /// @solidity memory-safe-assembly - assembly { - let ownerSlot := _OWNER_SLOT - // Clean the upper 96 bits. - newOwner := shr(96, shl(96, newOwner)) - // Emit the {OwnershipTransferred} event. - log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) - // Store the new value. - sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) - } - } else { - /// @solidity memory-safe-assembly - assembly { - let ownerSlot := _OWNER_SLOT - // Clean the upper 96 bits. - newOwner := shr(96, shl(96, newOwner)) - // Emit the {OwnershipTransferred} event. - log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) - // Store the new value. - sstore(ownerSlot, newOwner) - } - } - } - - /// @dev Throws if the sender is not the owner. - function _checkOwner() internal view virtual { - /// @solidity memory-safe-assembly - assembly { - // If the caller is not the stored owner, revert. - if iszero(eq(caller(), sload(_OWNER_SLOT))) { - mstore(0x00, 0x82b42900) // `Unauthorized()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Returns how long a two-step ownership handover is valid for in seconds. - /// Override to return a different value if needed. - /// Made internal to conserve bytecode. Wrap it in a public function if needed. - function _ownershipHandoverValidFor() internal view virtual returns (uint64) { - return 48 * 3600; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PUBLIC UPDATE FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Allows the owner to transfer the ownership to `newOwner`. - function transferOwnership(address newOwner) public payable virtual onlyOwner { - /// @solidity memory-safe-assembly - assembly { - if iszero(shl(96, newOwner)) { - mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. - revert(0x1c, 0x04) - } - } - _setOwner(newOwner); - } - - /// @dev Allows the owner to renounce their ownership. - function renounceOwnership() public payable virtual onlyOwner { - _setOwner(address(0)); - } - - /// @dev Request a two-step ownership handover to the caller. - /// The request will automatically expire in 48 hours (172800 seconds) by default. - function requestOwnershipHandover() public payable virtual { - unchecked { - uint256 expires = block.timestamp + _ownershipHandoverValidFor(); - /// @solidity memory-safe-assembly - assembly { - // Compute and set the handover slot to `expires`. - mstore(0x0c, _HANDOVER_SLOT_SEED) - mstore(0x00, caller()) - sstore(keccak256(0x0c, 0x20), expires) - // Emit the {OwnershipHandoverRequested} event. - log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) - } - } - } - - /// @dev Cancels the two-step ownership handover to the caller, if any. - function cancelOwnershipHandover() public payable virtual { - /// @solidity memory-safe-assembly - assembly { - // Compute and set the handover slot to 0. - mstore(0x0c, _HANDOVER_SLOT_SEED) - mstore(0x00, caller()) - sstore(keccak256(0x0c, 0x20), 0) - // Emit the {OwnershipHandoverCanceled} event. - log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) - } - } - - /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. - /// Reverts if there is no existing ownership handover requested by `pendingOwner`. - function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { - /// @solidity memory-safe-assembly - assembly { - // Compute and set the handover slot to 0. - mstore(0x0c, _HANDOVER_SLOT_SEED) - mstore(0x00, pendingOwner) - let handoverSlot := keccak256(0x0c, 0x20) - // If the handover does not exist, or has expired. - if gt(timestamp(), sload(handoverSlot)) { - mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. - revert(0x1c, 0x04) - } - // Set the handover slot to 0. - sstore(handoverSlot, 0) - } - _setOwner(pendingOwner); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PUBLIC READ FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the owner of the contract. - function owner() public view virtual returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := sload(_OWNER_SLOT) - } - } - - /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. - function ownershipHandoverExpiresAt(address pendingOwner) - public - view - virtual - returns (uint256 result) - { - /// @solidity memory-safe-assembly - assembly { - // Compute the handover slot. - mstore(0x0c, _HANDOVER_SLOT_SEED) - mstore(0x00, pendingOwner) - // Load the handover slot. - result := sload(keccak256(0x0c, 0x20)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MODIFIERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Marks a function as only callable by the owner. - modifier onlyOwner() virtual { - _checkOwner(); - _; - } -} diff --git a/lib/solady/src/auth/OwnableRoles.sol b/lib/solady/src/auth/OwnableRoles.sol deleted file mode 100644 index cd20f7d..0000000 --- a/lib/solady/src/auth/OwnableRoles.sol +++ /dev/null @@ -1,530 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Ownable} from "./Ownable.sol"; - -/// @notice Simple single owner and multiroles authorization mixin. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) -/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173) -/// for compatibility, the nomenclature for the 2-step ownership handover and roles -/// may be unique to this codebase. -abstract contract OwnableRoles is Ownable { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The `user`'s roles is updated to `roles`. - /// Each bit of `roles` represents whether the role is set. - event RolesUpdated(address indexed user, uint256 indexed roles); - - /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`. - uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE = - 0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The role slot of `user` is given by: - /// ``` - /// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED)) - /// let roleSlot := keccak256(0x00, 0x20) - /// ``` - /// This automatically ignores the upper bits of the `user` in case - /// they are not clean, as well as keep the `keccak256` under 32-bytes. - /// - /// Note: This is equivalent to `uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))`. - uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Overwrite the roles directly without authorization guard. - function _setRoles(address user, uint256 roles) internal virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, user) - // Store the new value. - sstore(keccak256(0x0c, 0x20), roles) - // Emit the {RolesUpdated} event. - log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles) - } - } - - /// @dev Updates the roles directly without authorization guard. - /// If `on` is true, each set bit of `roles` will be turned on, - /// otherwise, each set bit of `roles` will be turned off. - function _updateRoles(address user, uint256 roles, bool on) internal virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, user) - let roleSlot := keccak256(0x0c, 0x20) - // Load the current value. - let current := sload(roleSlot) - // Compute the updated roles if `on` is true. - let updated := or(current, roles) - // Compute the updated roles if `on` is false. - // Use `and` to compute the intersection of `current` and `roles`, - // `xor` it with `current` to flip the bits in the intersection. - if iszero(on) { updated := xor(current, and(current, roles)) } - // Then, store the new value. - sstore(roleSlot, updated) - // Emit the {RolesUpdated} event. - log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), updated) - } - } - - /// @dev Grants the roles directly without authorization guard. - /// Each bit of `roles` represents the role to turn on. - function _grantRoles(address user, uint256 roles) internal virtual { - _updateRoles(user, roles, true); - } - - /// @dev Removes the roles directly without authorization guard. - /// Each bit of `roles` represents the role to turn off. - function _removeRoles(address user, uint256 roles) internal virtual { - _updateRoles(user, roles, false); - } - - /// @dev Throws if the sender does not have any of the `roles`. - function _checkRoles(uint256 roles) internal view virtual { - /// @solidity memory-safe-assembly - assembly { - // Compute the role slot. - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, caller()) - // Load the stored value, and if the `and` intersection - // of the value and `roles` is zero, revert. - if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { - mstore(0x00, 0x82b42900) // `Unauthorized()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Throws if the sender is not the owner, - /// and does not have any of the `roles`. - /// Checks for ownership first, then lazily checks for roles. - function _checkOwnerOrRoles(uint256 roles) internal view virtual { - /// @solidity memory-safe-assembly - assembly { - // If the caller is not the stored owner. - // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. - if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { - // Compute the role slot. - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, caller()) - // Load the stored value, and if the `and` intersection - // of the value and `roles` is zero, revert. - if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { - mstore(0x00, 0x82b42900) // `Unauthorized()`. - revert(0x1c, 0x04) - } - } - } - } - - /// @dev Throws if the sender does not have any of the `roles`, - /// and is not the owner. - /// Checks for roles first, then lazily checks for ownership. - function _checkRolesOrOwner(uint256 roles) internal view virtual { - /// @solidity memory-safe-assembly - assembly { - // Compute the role slot. - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, caller()) - // Load the stored value, and if the `and` intersection - // of the value and `roles` is zero, revert. - if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) { - // If the caller is not the stored owner. - // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`. - if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) { - mstore(0x00, 0x82b42900) // `Unauthorized()`. - revert(0x1c, 0x04) - } - } - } - } - - /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`. - /// This is meant for frontends like Etherscan, and is therefore not fully optimized. - /// Not recommended to be called on-chain. - /// Made internal to conserve bytecode. Wrap it in a public function if needed. - function _rolesFromOrdinals(uint8[] memory ordinals) internal pure returns (uint256 roles) { - /// @solidity memory-safe-assembly - assembly { - for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } { - // We don't need to mask the values of `ordinals`, as Solidity - // cleans dirty upper bits when storing variables into memory. - roles := or(shl(mload(add(ordinals, i)), 1), roles) - } - } - } - - /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap. - /// This is meant for frontends like Etherscan, and is therefore not fully optimized. - /// Not recommended to be called on-chain. - /// Made internal to conserve bytecode. Wrap it in a public function if needed. - function _ordinalsFromRoles(uint256 roles) internal pure returns (uint8[] memory ordinals) { - /// @solidity memory-safe-assembly - assembly { - // Grab the pointer to the free memory. - ordinals := mload(0x40) - let ptr := add(ordinals, 0x20) - let o := 0 - // The absence of lookup tables, De Bruijn, etc., here is intentional for - // smaller bytecode, as this function is not meant to be called on-chain. - for { let t := roles } 1 {} { - mstore(ptr, o) - // `shr` 5 is equivalent to multiplying by 0x20. - // Push back into the ordinals array if the bit is set. - ptr := add(ptr, shl(5, and(t, 1))) - o := add(o, 1) - t := shr(o, roles) - if iszero(t) { break } - } - // Store the length of `ordinals`. - mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20)))) - // Allocate the memory. - mstore(0x40, ptr) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PUBLIC UPDATE FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Allows the owner to grant `user` `roles`. - /// If the `user` already has a role, then it will be an no-op for the role. - function grantRoles(address user, uint256 roles) public payable virtual onlyOwner { - _grantRoles(user, roles); - } - - /// @dev Allows the owner to remove `user` `roles`. - /// If the `user` does not have a role, then it will be an no-op for the role. - function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner { - _removeRoles(user, roles); - } - - /// @dev Allow the caller to remove their own roles. - /// If the caller does not have a role, then it will be an no-op for the role. - function renounceRoles(uint256 roles) public payable virtual { - _removeRoles(msg.sender, roles); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PUBLIC READ FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the roles of `user`. - function rolesOf(address user) public view virtual returns (uint256 roles) { - /// @solidity memory-safe-assembly - assembly { - // Compute the role slot. - mstore(0x0c, _ROLE_SLOT_SEED) - mstore(0x00, user) - // Load the stored value. - roles := sload(keccak256(0x0c, 0x20)) - } - } - - /// @dev Returns whether `user` has any of `roles`. - function hasAnyRole(address user, uint256 roles) public view virtual returns (bool) { - return rolesOf(user) & roles != 0; - } - - /// @dev Returns whether `user` has all of `roles`. - function hasAllRoles(address user, uint256 roles) public view virtual returns (bool) { - return rolesOf(user) & roles == roles; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MODIFIERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Marks a function as only callable by an account with `roles`. - modifier onlyRoles(uint256 roles) virtual { - _checkRoles(roles); - _; - } - - /// @dev Marks a function as only callable by the owner or by an account - /// with `roles`. Checks for ownership first, then lazily checks for roles. - modifier onlyOwnerOrRoles(uint256 roles) virtual { - _checkOwnerOrRoles(roles); - _; - } - - /// @dev Marks a function as only callable by an account with `roles` - /// or the owner. Checks for roles first, then lazily checks for ownership. - modifier onlyRolesOrOwner(uint256 roles) virtual { - _checkRolesOrOwner(roles); - _; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ROLE CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // IYKYK - - uint256 internal constant _ROLE_0 = 1 << 0; - uint256 internal constant _ROLE_1 = 1 << 1; - uint256 internal constant _ROLE_2 = 1 << 2; - uint256 internal constant _ROLE_3 = 1 << 3; - uint256 internal constant _ROLE_4 = 1 << 4; - uint256 internal constant _ROLE_5 = 1 << 5; - uint256 internal constant _ROLE_6 = 1 << 6; - uint256 internal constant _ROLE_7 = 1 << 7; - uint256 internal constant _ROLE_8 = 1 << 8; - uint256 internal constant _ROLE_9 = 1 << 9; - uint256 internal constant _ROLE_10 = 1 << 10; - uint256 internal constant _ROLE_11 = 1 << 11; - uint256 internal constant _ROLE_12 = 1 << 12; - uint256 internal constant _ROLE_13 = 1 << 13; - uint256 internal constant _ROLE_14 = 1 << 14; - uint256 internal constant _ROLE_15 = 1 << 15; - uint256 internal constant _ROLE_16 = 1 << 16; - uint256 internal constant _ROLE_17 = 1 << 17; - uint256 internal constant _ROLE_18 = 1 << 18; - uint256 internal constant _ROLE_19 = 1 << 19; - uint256 internal constant _ROLE_20 = 1 << 20; - uint256 internal constant _ROLE_21 = 1 << 21; - uint256 internal constant _ROLE_22 = 1 << 22; - uint256 internal constant _ROLE_23 = 1 << 23; - uint256 internal constant _ROLE_24 = 1 << 24; - uint256 internal constant _ROLE_25 = 1 << 25; - uint256 internal constant _ROLE_26 = 1 << 26; - uint256 internal constant _ROLE_27 = 1 << 27; - uint256 internal constant _ROLE_28 = 1 << 28; - uint256 internal constant _ROLE_29 = 1 << 29; - uint256 internal constant _ROLE_30 = 1 << 30; - uint256 internal constant _ROLE_31 = 1 << 31; - uint256 internal constant _ROLE_32 = 1 << 32; - uint256 internal constant _ROLE_33 = 1 << 33; - uint256 internal constant _ROLE_34 = 1 << 34; - uint256 internal constant _ROLE_35 = 1 << 35; - uint256 internal constant _ROLE_36 = 1 << 36; - uint256 internal constant _ROLE_37 = 1 << 37; - uint256 internal constant _ROLE_38 = 1 << 38; - uint256 internal constant _ROLE_39 = 1 << 39; - uint256 internal constant _ROLE_40 = 1 << 40; - uint256 internal constant _ROLE_41 = 1 << 41; - uint256 internal constant _ROLE_42 = 1 << 42; - uint256 internal constant _ROLE_43 = 1 << 43; - uint256 internal constant _ROLE_44 = 1 << 44; - uint256 internal constant _ROLE_45 = 1 << 45; - uint256 internal constant _ROLE_46 = 1 << 46; - uint256 internal constant _ROLE_47 = 1 << 47; - uint256 internal constant _ROLE_48 = 1 << 48; - uint256 internal constant _ROLE_49 = 1 << 49; - uint256 internal constant _ROLE_50 = 1 << 50; - uint256 internal constant _ROLE_51 = 1 << 51; - uint256 internal constant _ROLE_52 = 1 << 52; - uint256 internal constant _ROLE_53 = 1 << 53; - uint256 internal constant _ROLE_54 = 1 << 54; - uint256 internal constant _ROLE_55 = 1 << 55; - uint256 internal constant _ROLE_56 = 1 << 56; - uint256 internal constant _ROLE_57 = 1 << 57; - uint256 internal constant _ROLE_58 = 1 << 58; - uint256 internal constant _ROLE_59 = 1 << 59; - uint256 internal constant _ROLE_60 = 1 << 60; - uint256 internal constant _ROLE_61 = 1 << 61; - uint256 internal constant _ROLE_62 = 1 << 62; - uint256 internal constant _ROLE_63 = 1 << 63; - uint256 internal constant _ROLE_64 = 1 << 64; - uint256 internal constant _ROLE_65 = 1 << 65; - uint256 internal constant _ROLE_66 = 1 << 66; - uint256 internal constant _ROLE_67 = 1 << 67; - uint256 internal constant _ROLE_68 = 1 << 68; - uint256 internal constant _ROLE_69 = 1 << 69; - uint256 internal constant _ROLE_70 = 1 << 70; - uint256 internal constant _ROLE_71 = 1 << 71; - uint256 internal constant _ROLE_72 = 1 << 72; - uint256 internal constant _ROLE_73 = 1 << 73; - uint256 internal constant _ROLE_74 = 1 << 74; - uint256 internal constant _ROLE_75 = 1 << 75; - uint256 internal constant _ROLE_76 = 1 << 76; - uint256 internal constant _ROLE_77 = 1 << 77; - uint256 internal constant _ROLE_78 = 1 << 78; - uint256 internal constant _ROLE_79 = 1 << 79; - uint256 internal constant _ROLE_80 = 1 << 80; - uint256 internal constant _ROLE_81 = 1 << 81; - uint256 internal constant _ROLE_82 = 1 << 82; - uint256 internal constant _ROLE_83 = 1 << 83; - uint256 internal constant _ROLE_84 = 1 << 84; - uint256 internal constant _ROLE_85 = 1 << 85; - uint256 internal constant _ROLE_86 = 1 << 86; - uint256 internal constant _ROLE_87 = 1 << 87; - uint256 internal constant _ROLE_88 = 1 << 88; - uint256 internal constant _ROLE_89 = 1 << 89; - uint256 internal constant _ROLE_90 = 1 << 90; - uint256 internal constant _ROLE_91 = 1 << 91; - uint256 internal constant _ROLE_92 = 1 << 92; - uint256 internal constant _ROLE_93 = 1 << 93; - uint256 internal constant _ROLE_94 = 1 << 94; - uint256 internal constant _ROLE_95 = 1 << 95; - uint256 internal constant _ROLE_96 = 1 << 96; - uint256 internal constant _ROLE_97 = 1 << 97; - uint256 internal constant _ROLE_98 = 1 << 98; - uint256 internal constant _ROLE_99 = 1 << 99; - uint256 internal constant _ROLE_100 = 1 << 100; - uint256 internal constant _ROLE_101 = 1 << 101; - uint256 internal constant _ROLE_102 = 1 << 102; - uint256 internal constant _ROLE_103 = 1 << 103; - uint256 internal constant _ROLE_104 = 1 << 104; - uint256 internal constant _ROLE_105 = 1 << 105; - uint256 internal constant _ROLE_106 = 1 << 106; - uint256 internal constant _ROLE_107 = 1 << 107; - uint256 internal constant _ROLE_108 = 1 << 108; - uint256 internal constant _ROLE_109 = 1 << 109; - uint256 internal constant _ROLE_110 = 1 << 110; - uint256 internal constant _ROLE_111 = 1 << 111; - uint256 internal constant _ROLE_112 = 1 << 112; - uint256 internal constant _ROLE_113 = 1 << 113; - uint256 internal constant _ROLE_114 = 1 << 114; - uint256 internal constant _ROLE_115 = 1 << 115; - uint256 internal constant _ROLE_116 = 1 << 116; - uint256 internal constant _ROLE_117 = 1 << 117; - uint256 internal constant _ROLE_118 = 1 << 118; - uint256 internal constant _ROLE_119 = 1 << 119; - uint256 internal constant _ROLE_120 = 1 << 120; - uint256 internal constant _ROLE_121 = 1 << 121; - uint256 internal constant _ROLE_122 = 1 << 122; - uint256 internal constant _ROLE_123 = 1 << 123; - uint256 internal constant _ROLE_124 = 1 << 124; - uint256 internal constant _ROLE_125 = 1 << 125; - uint256 internal constant _ROLE_126 = 1 << 126; - uint256 internal constant _ROLE_127 = 1 << 127; - uint256 internal constant _ROLE_128 = 1 << 128; - uint256 internal constant _ROLE_129 = 1 << 129; - uint256 internal constant _ROLE_130 = 1 << 130; - uint256 internal constant _ROLE_131 = 1 << 131; - uint256 internal constant _ROLE_132 = 1 << 132; - uint256 internal constant _ROLE_133 = 1 << 133; - uint256 internal constant _ROLE_134 = 1 << 134; - uint256 internal constant _ROLE_135 = 1 << 135; - uint256 internal constant _ROLE_136 = 1 << 136; - uint256 internal constant _ROLE_137 = 1 << 137; - uint256 internal constant _ROLE_138 = 1 << 138; - uint256 internal constant _ROLE_139 = 1 << 139; - uint256 internal constant _ROLE_140 = 1 << 140; - uint256 internal constant _ROLE_141 = 1 << 141; - uint256 internal constant _ROLE_142 = 1 << 142; - uint256 internal constant _ROLE_143 = 1 << 143; - uint256 internal constant _ROLE_144 = 1 << 144; - uint256 internal constant _ROLE_145 = 1 << 145; - uint256 internal constant _ROLE_146 = 1 << 146; - uint256 internal constant _ROLE_147 = 1 << 147; - uint256 internal constant _ROLE_148 = 1 << 148; - uint256 internal constant _ROLE_149 = 1 << 149; - uint256 internal constant _ROLE_150 = 1 << 150; - uint256 internal constant _ROLE_151 = 1 << 151; - uint256 internal constant _ROLE_152 = 1 << 152; - uint256 internal constant _ROLE_153 = 1 << 153; - uint256 internal constant _ROLE_154 = 1 << 154; - uint256 internal constant _ROLE_155 = 1 << 155; - uint256 internal constant _ROLE_156 = 1 << 156; - uint256 internal constant _ROLE_157 = 1 << 157; - uint256 internal constant _ROLE_158 = 1 << 158; - uint256 internal constant _ROLE_159 = 1 << 159; - uint256 internal constant _ROLE_160 = 1 << 160; - uint256 internal constant _ROLE_161 = 1 << 161; - uint256 internal constant _ROLE_162 = 1 << 162; - uint256 internal constant _ROLE_163 = 1 << 163; - uint256 internal constant _ROLE_164 = 1 << 164; - uint256 internal constant _ROLE_165 = 1 << 165; - uint256 internal constant _ROLE_166 = 1 << 166; - uint256 internal constant _ROLE_167 = 1 << 167; - uint256 internal constant _ROLE_168 = 1 << 168; - uint256 internal constant _ROLE_169 = 1 << 169; - uint256 internal constant _ROLE_170 = 1 << 170; - uint256 internal constant _ROLE_171 = 1 << 171; - uint256 internal constant _ROLE_172 = 1 << 172; - uint256 internal constant _ROLE_173 = 1 << 173; - uint256 internal constant _ROLE_174 = 1 << 174; - uint256 internal constant _ROLE_175 = 1 << 175; - uint256 internal constant _ROLE_176 = 1 << 176; - uint256 internal constant _ROLE_177 = 1 << 177; - uint256 internal constant _ROLE_178 = 1 << 178; - uint256 internal constant _ROLE_179 = 1 << 179; - uint256 internal constant _ROLE_180 = 1 << 180; - uint256 internal constant _ROLE_181 = 1 << 181; - uint256 internal constant _ROLE_182 = 1 << 182; - uint256 internal constant _ROLE_183 = 1 << 183; - uint256 internal constant _ROLE_184 = 1 << 184; - uint256 internal constant _ROLE_185 = 1 << 185; - uint256 internal constant _ROLE_186 = 1 << 186; - uint256 internal constant _ROLE_187 = 1 << 187; - uint256 internal constant _ROLE_188 = 1 << 188; - uint256 internal constant _ROLE_189 = 1 << 189; - uint256 internal constant _ROLE_190 = 1 << 190; - uint256 internal constant _ROLE_191 = 1 << 191; - uint256 internal constant _ROLE_192 = 1 << 192; - uint256 internal constant _ROLE_193 = 1 << 193; - uint256 internal constant _ROLE_194 = 1 << 194; - uint256 internal constant _ROLE_195 = 1 << 195; - uint256 internal constant _ROLE_196 = 1 << 196; - uint256 internal constant _ROLE_197 = 1 << 197; - uint256 internal constant _ROLE_198 = 1 << 198; - uint256 internal constant _ROLE_199 = 1 << 199; - uint256 internal constant _ROLE_200 = 1 << 200; - uint256 internal constant _ROLE_201 = 1 << 201; - uint256 internal constant _ROLE_202 = 1 << 202; - uint256 internal constant _ROLE_203 = 1 << 203; - uint256 internal constant _ROLE_204 = 1 << 204; - uint256 internal constant _ROLE_205 = 1 << 205; - uint256 internal constant _ROLE_206 = 1 << 206; - uint256 internal constant _ROLE_207 = 1 << 207; - uint256 internal constant _ROLE_208 = 1 << 208; - uint256 internal constant _ROLE_209 = 1 << 209; - uint256 internal constant _ROLE_210 = 1 << 210; - uint256 internal constant _ROLE_211 = 1 << 211; - uint256 internal constant _ROLE_212 = 1 << 212; - uint256 internal constant _ROLE_213 = 1 << 213; - uint256 internal constant _ROLE_214 = 1 << 214; - uint256 internal constant _ROLE_215 = 1 << 215; - uint256 internal constant _ROLE_216 = 1 << 216; - uint256 internal constant _ROLE_217 = 1 << 217; - uint256 internal constant _ROLE_218 = 1 << 218; - uint256 internal constant _ROLE_219 = 1 << 219; - uint256 internal constant _ROLE_220 = 1 << 220; - uint256 internal constant _ROLE_221 = 1 << 221; - uint256 internal constant _ROLE_222 = 1 << 222; - uint256 internal constant _ROLE_223 = 1 << 223; - uint256 internal constant _ROLE_224 = 1 << 224; - uint256 internal constant _ROLE_225 = 1 << 225; - uint256 internal constant _ROLE_226 = 1 << 226; - uint256 internal constant _ROLE_227 = 1 << 227; - uint256 internal constant _ROLE_228 = 1 << 228; - uint256 internal constant _ROLE_229 = 1 << 229; - uint256 internal constant _ROLE_230 = 1 << 230; - uint256 internal constant _ROLE_231 = 1 << 231; - uint256 internal constant _ROLE_232 = 1 << 232; - uint256 internal constant _ROLE_233 = 1 << 233; - uint256 internal constant _ROLE_234 = 1 << 234; - uint256 internal constant _ROLE_235 = 1 << 235; - uint256 internal constant _ROLE_236 = 1 << 236; - uint256 internal constant _ROLE_237 = 1 << 237; - uint256 internal constant _ROLE_238 = 1 << 238; - uint256 internal constant _ROLE_239 = 1 << 239; - uint256 internal constant _ROLE_240 = 1 << 240; - uint256 internal constant _ROLE_241 = 1 << 241; - uint256 internal constant _ROLE_242 = 1 << 242; - uint256 internal constant _ROLE_243 = 1 << 243; - uint256 internal constant _ROLE_244 = 1 << 244; - uint256 internal constant _ROLE_245 = 1 << 245; - uint256 internal constant _ROLE_246 = 1 << 246; - uint256 internal constant _ROLE_247 = 1 << 247; - uint256 internal constant _ROLE_248 = 1 << 248; - uint256 internal constant _ROLE_249 = 1 << 249; - uint256 internal constant _ROLE_250 = 1 << 250; - uint256 internal constant _ROLE_251 = 1 << 251; - uint256 internal constant _ROLE_252 = 1 << 252; - uint256 internal constant _ROLE_253 = 1 << 253; - uint256 internal constant _ROLE_254 = 1 << 254; - uint256 internal constant _ROLE_255 = 1 << 255; -} diff --git a/lib/solady/src/tokens/ERC1155.sol b/lib/solady/src/tokens/ERC1155.sol deleted file mode 100644 index a5a173e..0000000 --- a/lib/solady/src/tokens/ERC1155.sol +++ /dev/null @@ -1,1114 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple ERC1155 implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC1155.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC1155/ERC1155.sol) -/// -/// @dev Note: -/// - The ERC1155 standard allows for self-approvals. -/// For performance, this implementation WILL NOT revert for such actions. -/// Please add any checks with overrides if desired. -/// - The transfer functions use the identity precompile (0x4) -/// to copy memory internally. -/// -/// If you are overriding: -/// - Make sure all variables written to storage are properly cleaned -// (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood). -/// - Check that the overridden function is actually used in the function you want to -/// change the behavior of. Much of the code has been manually inlined for performance. -abstract contract ERC1155 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The lengths of the input arrays are not the same. - error ArrayLengthsMismatch(); - - /// @dev Cannot mint or transfer to the zero address. - error TransferToZeroAddress(); - - /// @dev The recipient's balance has overflowed. - error AccountBalanceOverflow(); - - /// @dev Insufficient balance. - error InsufficientBalance(); - - /// @dev Only the token owner or an approved account can manage the tokens. - error NotOwnerNorApproved(); - - /// @dev Cannot safely transfer to a contract that does not implement - /// the ERC1155Receiver interface. - error TransferToNonERC1155ReceiverImplementer(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when `amount` of token `id` is transferred - /// from `from` to `to` by `operator`. - event TransferSingle( - address indexed operator, - address indexed from, - address indexed to, - uint256 id, - uint256 amount - ); - - /// @dev Emitted when `amounts` of token `ids` are transferred - /// from `from` to `to` by `operator`. - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] amounts - ); - - /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. - event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); - - /// @dev Emitted when the Uniform Resource Identifier (URI) for token `id` - /// is updated to `value`. This event is not used in the base contract. - /// You may need to emit this event depending on your URI logic. - /// - /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata - event URI(string value, uint256 indexed id); - - /// @dev `keccak256(bytes("TransferSingle(address,address,address,uint256,uint256)"))`. - uint256 private constant _TRANSFER_SINGLE_EVENT_SIGNATURE = - 0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62; - - /// @dev `keccak256(bytes("TransferBatch(address,address,address,uint256[],uint256[])"))`. - uint256 private constant _TRANSFER_BATCH_EVENT_SIGNATURE = - 0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb; - - /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`. - uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = - 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The `ownerSlotSeed` of a given owner is given by. - /// ``` - /// let ownerSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner)) - /// ``` - /// - /// The balance slot of `owner` is given by. - /// ``` - /// mstore(0x20, ownerSlotSeed) - /// mstore(0x00, id) - /// let balanceSlot := keccak256(0x00, 0x40) - /// ``` - /// - /// The operator approval slot of `owner` is given by. - /// ``` - /// mstore(0x20, ownerSlotSeed) - /// mstore(0x00, operator) - /// let operatorApprovalSlot := keccak256(0x0c, 0x34) - /// ``` - uint256 private constant _ERC1155_MASTER_SLOT_SEED = 0x9a31110384e0b0c9; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC1155 METADATA */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the URI for token `id`. - /// - /// You can either return the same templated URI for all token IDs, - /// (e.g. "https://example.com/api/{id}.json"), - /// or return a unique URI for each `id`. - /// - /// See: https://eips.ethereum.org/EIPS/eip-1155#metadata - function uri(uint256 id) public view virtual returns (string memory); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC1155 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the amount of `id` owned by `owner`. - function balanceOf(address owner, uint256 id) public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, _ERC1155_MASTER_SLOT_SEED) - mstore(0x14, owner) - mstore(0x00, id) - result := sload(keccak256(0x00, 0x40)) - } - } - - /// @dev Returns whether `operator` is approved to manage the tokens of `owner`. - function isApprovedForAll(address owner, address operator) - public - view - virtual - returns (bool result) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, _ERC1155_MASTER_SLOT_SEED) - mstore(0x14, owner) - mstore(0x00, operator) - result := sload(keccak256(0x0c, 0x34)) - } - } - - /// @dev Sets whether `operator` is approved to manage the tokens of the caller. - /// - /// Emits a {ApprovalForAll} event. - function setApprovalForAll(address operator, bool isApproved) public virtual { - /// @solidity memory-safe-assembly - assembly { - // Convert to 0 or 1. - isApproved := iszero(iszero(isApproved)) - // Update the `isApproved` for (`msg.sender`, `operator`). - mstore(0x20, _ERC1155_MASTER_SLOT_SEED) - mstore(0x14, caller()) - mstore(0x00, operator) - sstore(keccak256(0x0c, 0x34), isApproved) - // Emit the {ApprovalForAll} event. - mstore(0x00, isApproved) - // forgefmt: disable-next-line - log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))) - } - } - - /// @dev Transfers `amount` of `id` from `from` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - `from` must have at least `amount` of `id`. - /// - If the caller is not `from`, - /// it must be approved to manage the tokens of `from`. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155Reveived}, which is called upon a batch transfer. - /// - /// Emits a {Transfer} event. - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes calldata data - ) public virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, to, _single(id), _single(amount), data); - } - /// @solidity memory-safe-assembly - assembly { - let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from)) - let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to)) - mstore(0x20, fromSlotSeed) - // Clear the upper 96 bits. - from := shr(96, fromSlotSeed) - to := shr(96, toSlotSeed) - // Revert if `to` is the zero address. - if iszero(to) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // If the caller is not `from`, do the authorization check. - if iszero(eq(caller(), from)) { - mstore(0x00, caller()) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Subtract and store the updated balance of `from`. - { - mstore(0x00, id) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - // Increase and store the updated balance of `to`. - { - mstore(0x20, toSlotSeed) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - // Emit a {TransferSingle} event. - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), from, to) - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, to, _single(id), _single(amount), data); - } - /// @solidity memory-safe-assembly - assembly { - // Do the {onERC1155Received} check if `to` is a smart contract. - if extcodesize(to) { - // Prepare the calldata. - let m := mload(0x40) - // `onERC1155Received(address,address,uint256,uint256,bytes)`. - mstore(m, 0xf23a6e61) - mstore(add(m, 0x20), caller()) - mstore(add(m, 0x40), from) - mstore(add(m, 0x60), id) - mstore(add(m, 0x80), amount) - mstore(add(m, 0xa0), 0xa0) - calldatacopy(add(m, 0xc0), sub(data.offset, 0x20), add(0x20, data.length)) - // Revert if the call reverts. - if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, data.length), m, 0x20)) { - if returndatasize() { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - // Load the returndata and compare it with the function selector. - if iszero(eq(mload(m), shl(224, 0xf23a6e61))) { - mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. - revert(0x1c, 0x04) - } - } - } - } - - /// @dev Transfers `amounts` of `ids` from `from` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - `from` must have at least `amount` of `id`. - /// - `ids` and `amounts` must have the same length. - /// - If the caller is not `from`, - /// it must be approved to manage the tokens of `from`. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer. - /// - /// Emits a {TransferBatch} event. - function safeBatchTransferFrom( - address from, - address to, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) public virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, to, ids, amounts, data); - } - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(ids.length, amounts.length)) { - mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`. - revert(0x1c, 0x04) - } - let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, from)) - let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, shl(96, to)) - mstore(0x20, fromSlotSeed) - // Clear the upper 96 bits. - from := shr(96, fromSlotSeed) - to := shr(96, toSlotSeed) - // Revert if `to` is the zero address. - if iszero(to) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // If the caller is not `from`, do the authorization check. - if iszero(eq(caller(), from)) { - mstore(0x00, caller()) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Loop through all the `ids` and update the balances. - { - for { let i := shl(5, ids.length) } i {} { - i := sub(i, 0x20) - let amount := calldataload(add(amounts.offset, i)) - // Subtract and store the updated balance of `from`. - { - mstore(0x20, fromSlotSeed) - mstore(0x00, calldataload(add(ids.offset, i))) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - // Increase and store the updated balance of `to`. - { - mstore(0x20, toSlotSeed) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - } - } - // Emit a {TransferBatch} event. - { - let m := mload(0x40) - // Copy the `ids`. - mstore(m, 0x40) - let n := add(0x20, shl(5, ids.length)) - let o := add(m, 0x40) - calldatacopy(o, sub(ids.offset, 0x20), n) - // Copy the `amounts`. - mstore(add(m, 0x20), add(0x40, n)) - calldatacopy(add(o, n), sub(amounts.offset, 0x20), n) - // Do the emit. - log4(m, add(add(n, n), 0x40), _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), from, to) - } - } - if (_useAfterTokenTransfer()) { - _afterTokenTransferCalldata(from, to, ids, amounts, data); - } - /// @solidity memory-safe-assembly - assembly { - // Do the {onERC1155BatchReceived} check if `to` is a smart contract. - if extcodesize(to) { - mstore(0x00, to) // Cache `to` to prevent stack too deep. - let m := mload(0x40) - // Prepare the calldata. - // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. - mstore(m, 0xbc197c81) - mstore(add(m, 0x20), caller()) - mstore(add(m, 0x40), from) - // Copy the `ids`. - mstore(add(m, 0x60), 0xa0) - let n := add(0x20, shl(5, ids.length)) - let o := add(m, 0xc0) - calldatacopy(o, sub(ids.offset, 0x20), n) - // Copy the `amounts`. - let s := add(0xa0, n) - mstore(add(m, 0x80), s) - calldatacopy(add(o, n), sub(amounts.offset, 0x20), n) - // Copy the `data`. - mstore(add(m, 0xa0), add(s, n)) - calldatacopy(add(o, add(n, n)), sub(data.offset, 0x20), add(0x20, data.length)) - let nAll := add(0xc4, add(data.length, add(n, n))) - // Revert if the call reverts. - if iszero(call(gas(), mload(0x00), 0, add(m, 0x1c), nAll, m, 0x20)) { - if returndatasize() { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - // Load the returndata and compare it with the function selector. - if iszero(eq(mload(m), shl(224, 0xbc197c81))) { - mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. - revert(0x1c, 0x04) - } - } - } - } - - /// @dev Returns the amounts of `ids` for `owners. - /// - /// Requirements: - /// - `owners` and `ids` must have the same length. - function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) - public - view - virtual - returns (uint256[] memory balances) - { - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(ids.length, owners.length)) { - mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`. - revert(0x1c, 0x04) - } - balances := mload(0x40) - mstore(balances, ids.length) - let o := add(balances, 0x20) - let i := shl(5, ids.length) - mstore(0x40, add(i, o)) - // Loop through all the `ids` and load the balances. - for {} i {} { - i := sub(i, 0x20) - let owner := calldataload(add(owners.offset, i)) - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, shl(96, owner))) - mstore(0x00, calldataload(add(ids.offset, i))) - mstore(add(o, i), sload(keccak256(0x00, 0x40))) - } - } - } - - /// @dev Returns true if this contract implements the interface defined by `interfaceId`. - /// See: https://eips.ethereum.org/EIPS/eip-165 - /// This function call must use less than 30000 gas. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, interfaceId) - // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c. - result := or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL MINT FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Mints `amount` of `id` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155Reveived}, which is called upon a batch transfer. - /// - /// Emits a {Transfer} event. - function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(address(0), to, _single(id), _single(amount), data); - } - /// @solidity memory-safe-assembly - assembly { - let to_ := shl(96, to) - // Revert if `to` is the zero address. - if iszero(to_) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // Increase and store the updated balance of `to`. - { - mstore(0x20, _ERC1155_MASTER_SLOT_SEED) - mstore(0x14, to) - mstore(0x00, id) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - // Emit a {TransferSingle} event. - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), 0, shr(96, to_)) - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(address(0), to, _single(id), _single(amount), data); - } - if (_hasCode(to)) _checkOnERC1155Received(address(0), to, id, amount, data); - } - - /// @dev Mints `amounts` of `ids` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - `ids` and `amounts` must have the same length. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer. - /// - /// Emits a {TransferBatch} event. - function _batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(address(0), to, ids, amounts, data); - } - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(mload(ids), mload(amounts))) { - mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`. - revert(0x1c, 0x04) - } - let to_ := shl(96, to) - // Revert if `to` is the zero address. - if iszero(to_) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // Loop through all the `ids` and update the balances. - { - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_)) - for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } { - let amount := mload(add(amounts, i)) - // Increase and store the updated balance of `to`. - { - mstore(0x00, mload(add(ids, i))) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - } - } - // Emit a {TransferBatch} event. - { - let m := mload(0x40) - // Copy the `ids`. - mstore(m, 0x40) - let n := add(0x20, shl(5, mload(ids))) - let o := add(m, 0x40) - pop(staticcall(gas(), 4, ids, n, o, n)) - // Copy the `amounts`. - mstore(add(m, 0x20), add(0x40, returndatasize())) - o := add(o, returndatasize()) - n := add(0x20, shl(5, mload(amounts))) - pop(staticcall(gas(), 4, amounts, n, o, n)) - n := sub(add(o, returndatasize()), m) - // Do the emit. - log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), 0, shr(96, to_)) - } - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(address(0), to, ids, amounts, data); - } - if (_hasCode(to)) _checkOnERC1155BatchReceived(address(0), to, ids, amounts, data); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL BURN FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to `_burn(address(0), from, id, amount)`. - function _burn(address from, uint256 id, uint256 amount) internal virtual { - _burn(address(0), from, id, amount); - } - - /// @dev Destroys `amount` of `id` from `from`. - /// - /// Requirements: - /// - `from` must have at least `amount` of `id`. - /// - If `by` is not the zero address, it must be either `from`, - /// or approved to manage the tokens of `from`. - /// - /// Emits a {Transfer} event. - function _burn(address by, address from, uint256 id, uint256 amount) internal virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, address(0), _single(id), _single(amount), ""); - } - /// @solidity memory-safe-assembly - assembly { - let from_ := shl(96, from) - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_)) - // If `by` is not the zero address, and not equal to `from`, - // check if it is approved to manage all the tokens of `from`. - if iszero(or(iszero(shl(96, by)), eq(shl(96, by), from_))) { - mstore(0x00, by) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Decrease and store the updated balance of `from`. - { - mstore(0x00, id) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - // Emit a {TransferSingle} event. - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), 0) - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, address(0), _single(id), _single(amount), ""); - } - } - - /// @dev Equivalent to `_batchBurn(address(0), from, ids, amounts)`. - function _batchBurn(address from, uint256[] memory ids, uint256[] memory amounts) - internal - virtual - { - _batchBurn(address(0), from, ids, amounts); - } - - /// @dev Destroys `amounts` of `ids` from `from`. - /// - /// Requirements: - /// - `ids` and `amounts` must have the same length. - /// - `from` must have at least `amounts` of `ids`. - /// - If `by` is not the zero address, it must be either `from`, - /// or approved to manage the tokens of `from`. - /// - /// Emits a {TransferBatch} event. - function _batchBurn(address by, address from, uint256[] memory ids, uint256[] memory amounts) - internal - virtual - { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, address(0), ids, amounts, ""); - } - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(mload(ids), mload(amounts))) { - mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`. - revert(0x1c, 0x04) - } - let from_ := shl(96, from) - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_)) - // If `by` is not the zero address, and not equal to `from`, - // check if it is approved to manage all the tokens of `from`. - let by_ := shl(96, by) - if iszero(or(iszero(by_), eq(by_, from_))) { - mstore(0x00, by) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Loop through all the `ids` and update the balances. - { - for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } { - let amount := mload(add(amounts, i)) - // Decrease and store the updated balance of `from`. - { - mstore(0x00, mload(add(ids, i))) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - } - } - // Emit a {TransferBatch} event. - { - let m := mload(0x40) - // Copy the `ids`. - mstore(m, 0x40) - let n := add(0x20, shl(5, mload(ids))) - let o := add(m, 0x40) - pop(staticcall(gas(), 4, ids, n, o, n)) - // Copy the `amounts`. - mstore(add(m, 0x20), add(0x40, returndatasize())) - o := add(o, returndatasize()) - n := add(0x20, shl(5, mload(amounts))) - pop(staticcall(gas(), 4, amounts, n, o, n)) - n := sub(add(o, returndatasize()), m) - // Do the emit. - log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), 0) - } - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, address(0), ids, amounts, ""); - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL APPROVAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Approve or remove the `operator` as an operator for `by`, - /// without authorization checks. - /// - /// Emits a {ApprovalForAll} event. - function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual { - /// @solidity memory-safe-assembly - assembly { - // Convert to 0 or 1. - isApproved := iszero(iszero(isApproved)) - // Update the `isApproved` for (`by`, `operator`). - mstore(0x20, _ERC1155_MASTER_SLOT_SEED) - mstore(0x14, by) - mstore(0x00, operator) - sstore(keccak256(0x0c, 0x34), isApproved) - // Emit the {ApprovalForAll} event. - mstore(0x00, isApproved) - let m := shr(96, not(0)) - log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, and(m, by), and(m, operator)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL TRANSFER FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to `_safeTransfer(address(0), from, to, id, amount, data)`. - function _safeTransfer(address from, address to, uint256 id, uint256 amount, bytes memory data) - internal - virtual - { - _safeTransfer(address(0), from, to, id, amount, data); - } - - /// @dev Transfers `amount` of `id` from `from` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - `from` must have at least `amount` of `id`. - /// - If `by` is not the zero address, it must be either `from`, - /// or approved to manage the tokens of `from`. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155Reveived}, which is called upon a batch transfer. - /// - /// Emits a {Transfer} event. - function _safeTransfer( - address by, - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, to, _single(id), _single(amount), data); - } - /// @solidity memory-safe-assembly - assembly { - let from_ := shl(96, from) - let to_ := shl(96, to) - // Revert if `to` is the zero address. - if iszero(to_) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, from_)) - // If `by` is not the zero address, and not equal to `from`, - // check if it is approved to manage all the tokens of `from`. - let by_ := shl(96, by) - if iszero(or(iszero(by_), eq(by_, from_))) { - mstore(0x00, by) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Subtract and store the updated balance of `from`. - { - mstore(0x00, id) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - // Increase and store the updated balance of `to`. - { - mstore(0x20, or(_ERC1155_MASTER_SLOT_SEED, to_)) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - // Emit a {TransferSingle} event. - mstore(0x20, amount) - // forgefmt: disable-next-line - log4(0x00, 0x40, _TRANSFER_SINGLE_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_)) - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, to, _single(id), _single(amount), data); - } - if (_hasCode(to)) _checkOnERC1155Received(from, to, id, amount, data); - } - - /// @dev Equivalent to `_safeBatchTransfer(address(0), from, to, ids, amounts, data)`. - function _safeBatchTransfer( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - _safeBatchTransfer(address(0), from, to, ids, amounts, data); - } - - /// @dev Transfers `amounts` of `ids` from `from` to `to`. - /// - /// Requirements: - /// - `to` cannot be the zero address. - /// - `ids` and `amounts` must have the same length. - /// - `from` must have at least `amounts` of `ids`. - /// - If `by` is not the zero address, it must be either `from`, - /// or approved to manage the tokens of `from`. - /// - If `to` refers to a smart contract, it must implement - /// {ERC1155-onERC1155BatchReveived}, which is called upon a batch transfer. - /// - /// Emits a {TransferBatch} event. - function _safeBatchTransfer( - address by, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - if (_useBeforeTokenTransfer()) { - _beforeTokenTransfer(from, to, ids, amounts, data); - } - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(mload(ids), mload(amounts))) { - mstore(0x00, 0x3b800a46) // `ArrayLengthsMismatch()`. - revert(0x1c, 0x04) - } - let from_ := shl(96, from) - let to_ := shl(96, to) - // Revert if `to` is the zero address. - if iszero(to_) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - let fromSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, from_) - let toSlotSeed := or(_ERC1155_MASTER_SLOT_SEED, to_) - mstore(0x20, fromSlotSeed) - // If `by` is not the zero address, and not equal to `from`, - // check if it is approved to manage all the tokens of `from`. - let by_ := shl(96, by) - if iszero(or(iszero(by_), eq(by_, from_))) { - mstore(0x00, by) - if iszero(sload(keccak256(0x0c, 0x34))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Loop through all the `ids` and update the balances. - { - for { let i := shl(5, mload(ids)) } i { i := sub(i, 0x20) } { - let amount := mload(add(amounts, i)) - // Subtract and store the updated balance of `from`. - { - mstore(0x20, fromSlotSeed) - mstore(0x00, mload(add(ids, i))) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - sstore(fromBalanceSlot, sub(fromBalance, amount)) - } - // Increase and store the updated balance of `to`. - { - mstore(0x20, toSlotSeed) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - } - } - } - // Emit a {TransferBatch} event. - { - let m := mload(0x40) - // Copy the `ids`. - mstore(m, 0x40) - let n := add(0x20, shl(5, mload(ids))) - let o := add(m, 0x40) - pop(staticcall(gas(), 4, ids, n, o, n)) - // Copy the `amounts`. - mstore(add(m, 0x20), add(0x40, returndatasize())) - o := add(o, returndatasize()) - n := add(0x20, shl(5, mload(amounts))) - pop(staticcall(gas(), 4, amounts, n, o, n)) - n := sub(add(o, returndatasize()), m) - // Do the emit. - log4(m, n, _TRANSFER_BATCH_EVENT_SIGNATURE, caller(), shr(96, from_), shr(96, to_)) - } - } - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, to, ids, amounts, data); - } - if (_hasCode(to)) _checkOnERC1155BatchReceived(from, to, ids, amounts, data); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HOOKS FOR OVERRIDING */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Override this function to return true if `_beforeTokenTransfer` is used. - /// This is to help the compiler avoid producing dead bytecode. - function _useBeforeTokenTransfer() internal view virtual returns (bool) { - return false; - } - - /// @dev Hook that is called before any token transfer. - /// This includes minting and burning, as well as batched variants. - /// - /// The same hook is called on both single and batched variants. - /// For single transfers, the length of the `id` and `amount` arrays are 1. - function _beforeTokenTransfer( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual {} - - /// @dev Override this function to return true if `_afterTokenTransfer` is used. - /// This is to help the compiler avoid producing dead bytecode. - function _useAfterTokenTransfer() internal view virtual returns (bool) { - return false; - } - - /// @dev Hook that is called after any token transfer. - /// This includes minting and burning, as well as batched variants. - /// - /// The same hook is called on both single and batched variants. - /// For single transfers, the length of the `id` and `amount` arrays are 1. - function _afterTokenTransfer( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual {} - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Helper for calling the `_afterTokenTransfer` hook. - /// This is to help the compiler avoid producing dead bytecode. - function _afterTokenTransferCalldata( - address from, - address to, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) private { - if (_useAfterTokenTransfer()) { - _afterTokenTransfer(from, to, ids, amounts, data); - } - } - - /// @dev Returns if `a` has bytecode of non-zero length. - function _hasCode(address a) private view returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := extcodesize(a) // Can handle dirty upper bits. - } - } - - /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155Received} on `to`. - /// Reverts if the target does not support the function correctly. - function _checkOnERC1155Received( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) private { - /// @solidity memory-safe-assembly - assembly { - // Prepare the calldata. - let m := mload(0x40) - // `onERC1155Received(address,address,uint256,uint256,bytes)`. - mstore(m, 0xf23a6e61) - mstore(add(m, 0x20), caller()) - mstore(add(m, 0x40), shr(96, shl(96, from))) - mstore(add(m, 0x60), id) - mstore(add(m, 0x80), amount) - mstore(add(m, 0xa0), 0xa0) - let n := mload(data) - mstore(add(m, 0xc0), n) - if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xe0), n)) } - // Revert if the call reverts. - if iszero(call(gas(), to, 0, add(m, 0x1c), add(0xc4, n), m, 0x20)) { - if returndatasize() { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - // Load the returndata and compare it with the function selector. - if iszero(eq(mload(m), shl(224, 0xf23a6e61))) { - mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Perform a call to invoke {IERC1155Receiver-onERC1155BatchReceived} on `to`. - /// Reverts if the target does not support the function correctly. - function _checkOnERC1155BatchReceived( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) private { - /// @solidity memory-safe-assembly - assembly { - // Prepare the calldata. - let m := mload(0x40) - // `onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)`. - mstore(m, 0xbc197c81) - mstore(add(m, 0x20), caller()) - mstore(add(m, 0x40), shr(96, shl(96, from))) - // Copy the `ids`. - mstore(add(m, 0x60), 0xa0) - let n := add(0x20, shl(5, mload(ids))) - let o := add(m, 0xc0) - pop(staticcall(gas(), 4, ids, n, o, n)) - // Copy the `amounts`. - let s := add(0xa0, returndatasize()) - mstore(add(m, 0x80), s) - o := add(o, returndatasize()) - n := add(0x20, shl(5, mload(amounts))) - pop(staticcall(gas(), 4, amounts, n, o, n)) - // Copy the `data`. - mstore(add(m, 0xa0), add(s, returndatasize())) - o := add(o, returndatasize()) - n := add(0x20, mload(data)) - pop(staticcall(gas(), 4, data, n, o, n)) - n := sub(add(o, returndatasize()), add(m, 0x1c)) - // Revert if the call reverts. - if iszero(call(gas(), to, 0, add(m, 0x1c), n, m, 0x20)) { - if returndatasize() { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - // Load the returndata and compare it with the function selector. - if iszero(eq(mload(m), shl(224, 0xbc197c81))) { - mstore(0x00, 0x9c05499b) // `TransferToNonERC1155ReceiverImplementer()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Returns `x` in an array with a single element. - function _single(uint256 x) private pure returns (uint256[] memory result) { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - mstore(0x40, add(result, 0x40)) - mstore(result, 1) - mstore(add(result, 0x20), x) - } - } -} diff --git a/lib/solady/src/tokens/ERC20.sol b/lib/solady/src/tokens/ERC20.sol deleted file mode 100644 index 98b5d67..0000000 --- a/lib/solady/src/tokens/ERC20.sol +++ /dev/null @@ -1,546 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple ERC20 + EIP-2612 implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) -/// -/// @dev Note: -/// - The ERC20 standard allows minting and transferring to and from the zero address, -/// minting and transferring zero tokens, as well as self-approvals. -/// For performance, this implementation WILL NOT revert for such actions. -/// Please add any checks with overrides if desired. -/// - The `permit` function uses the ecrecover precompile (0x1). -/// -/// If you are overriding: -/// - NEVER violate the ERC20 invariant: -/// the total sum of all balances must be equal to `totalSupply()`. -/// - Check that the overridden function is actually used in the function you want to -/// change the behavior of. Much of the code has been manually inlined for performance. -abstract contract ERC20 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The total supply has overflowed. - error TotalSupplyOverflow(); - - /// @dev The allowance has overflowed. - error AllowanceOverflow(); - - /// @dev The allowance has underflowed. - error AllowanceUnderflow(); - - /// @dev Insufficient balance. - error InsufficientBalance(); - - /// @dev Insufficient allowance. - error InsufficientAllowance(); - - /// @dev The permit is invalid. - error InvalidPermit(); - - /// @dev The permit has expired. - error PermitExpired(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. - event Transfer(address indexed from, address indexed to, uint256 amount); - - /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. - uint256 private constant _TRANSFER_EVENT_SIGNATURE = - 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; - - /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. - uint256 private constant _APPROVAL_EVENT_SIGNATURE = - 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The storage slot for the total supply. - uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; - - /// @dev The balance slot of `owner` is given by: - /// ``` - /// mstore(0x0c, _BALANCE_SLOT_SEED) - /// mstore(0x00, owner) - /// let balanceSlot := keccak256(0x0c, 0x20) - /// ``` - uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2; - - /// @dev The allowance slot of (`owner`, `spender`) is given by: - /// ``` - /// mstore(0x20, spender) - /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) - /// mstore(0x00, owner) - /// let allowanceSlot := keccak256(0x0c, 0x34) - /// ``` - uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; - - /// @dev The nonce slot of `owner` is given by: - /// ``` - /// mstore(0x0c, _NONCES_SLOT_SEED) - /// mstore(0x00, owner) - /// let nonceSlot := keccak256(0x0c, 0x20) - /// ``` - uint256 private constant _NONCES_SLOT_SEED = 0x38377508; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. - uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; - - /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. - bytes32 private constant _DOMAIN_TYPEHASH = - 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; - - /// @dev `keccak256("1")`. - bytes32 private constant _VERSION_HASH = - 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; - - /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. - bytes32 private constant _PERMIT_TYPEHASH = - 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC20 METADATA */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the name of the token. - function name() public view virtual returns (string memory); - - /// @dev Returns the symbol of the token. - function symbol() public view virtual returns (string memory); - - /// @dev Returns the decimals places of the token. - function decimals() public view virtual returns (uint8) { - return 18; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC20 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the amount of tokens in existence. - function totalSupply() public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - result := sload(_TOTAL_SUPPLY_SLOT) - } - } - - /// @dev Returns the amount of tokens owned by `owner`. - function balanceOf(address owner) public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x0c, _BALANCE_SLOT_SEED) - mstore(0x00, owner) - result := sload(keccak256(0x0c, 0x20)) - } - } - - /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. - function allowance(address owner, address spender) - public - view - virtual - returns (uint256 result) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, spender) - mstore(0x0c, _ALLOWANCE_SLOT_SEED) - mstore(0x00, owner) - result := sload(keccak256(0x0c, 0x34)) - } - } - - /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. - /// - /// Emits a {Approval} event. - function approve(address spender, uint256 amount) public virtual returns (bool) { - /// @solidity memory-safe-assembly - assembly { - // Compute the allowance slot and store the amount. - mstore(0x20, spender) - mstore(0x0c, _ALLOWANCE_SLOT_SEED) - mstore(0x00, caller()) - sstore(keccak256(0x0c, 0x34), amount) - // Emit the {Approval} event. - mstore(0x00, amount) - log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) - } - return true; - } - - /// @dev Transfer `amount` tokens from the caller to `to`. - /// - /// Requirements: - /// - `from` must at least have `amount`. - /// - /// Emits a {Transfer} event. - function transfer(address to, uint256 amount) public virtual returns (bool) { - _beforeTokenTransfer(msg.sender, to, amount); - /// @solidity memory-safe-assembly - assembly { - // Compute the balance slot and load its value. - mstore(0x0c, _BALANCE_SLOT_SEED) - mstore(0x00, caller()) - let fromBalanceSlot := keccak256(0x0c, 0x20) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x20) - // Add and store the updated balance of `to`. - // Will not overflow because the sum of all user balances - // cannot exceed the maximum uint256 value. - sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) - // Emit the {Transfer} event. - mstore(0x20, amount) - log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) - } - _afterTokenTransfer(msg.sender, to, amount); - return true; - } - - /// @dev Transfers `amount` tokens from `from` to `to`. - /// - /// Note: Does not update the allowance if it is the maximum uint256 value. - /// - /// Requirements: - /// - `from` must at least have `amount`. - /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. - /// - /// Emits a {Transfer} event. - function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { - _beforeTokenTransfer(from, to, amount); - /// @solidity memory-safe-assembly - assembly { - let from_ := shl(96, from) - // Compute the allowance slot and load its value. - mstore(0x20, caller()) - mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) - let allowanceSlot := keccak256(0x0c, 0x34) - let allowance_ := sload(allowanceSlot) - // If the allowance is not the maximum uint256 value. - if add(allowance_, 1) { - // Revert if the amount to be transferred exceeds the allowance. - if gt(amount, allowance_) { - mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated allowance. - sstore(allowanceSlot, sub(allowance_, amount)) - } - // Compute the balance slot and load its value. - mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) - let fromBalanceSlot := keccak256(0x0c, 0x20) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x20) - // Add and store the updated balance of `to`. - // Will not overflow because the sum of all user balances - // cannot exceed the maximum uint256 value. - sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) - // Emit the {Transfer} event. - mstore(0x20, amount) - log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) - } - _afterTokenTransfer(from, to, amount); - return true; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EIP-2612 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev For more performance, override to return the constant value - /// of `keccak256(bytes(name()))` if `name()` will never change. - function _constantNameHash() internal view virtual returns (bytes32 result) {} - - /// @dev Returns the current nonce for `owner`. - /// This value is used to compute the signature for EIP-2612 permit. - function nonces(address owner) public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - // Compute the nonce slot and load its value. - mstore(0x0c, _NONCES_SLOT_SEED) - mstore(0x00, owner) - result := sload(keccak256(0x0c, 0x20)) - } - } - - /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, - /// authorized by a signed approval by `owner`. - /// - /// Emits a {Approval} event. - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - bytes32 nameHash = _constantNameHash(); - // We simply calculate it on-the-fly to allow for cases where the `name` may change. - if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); - /// @solidity memory-safe-assembly - assembly { - // Revert if the block timestamp is greater than `deadline`. - if gt(timestamp(), deadline) { - mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. - revert(0x1c, 0x04) - } - let m := mload(0x40) // Grab the free memory pointer. - // Clean the upper 96 bits. - owner := shr(96, shl(96, owner)) - spender := shr(96, shl(96, spender)) - // Compute the nonce slot and load its value. - mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) - mstore(0x00, owner) - let nonceSlot := keccak256(0x0c, 0x20) - let nonceValue := sload(nonceSlot) - // Prepare the domain separator. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), nameHash) - mstore(add(m, 0x40), _VERSION_HASH) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - mstore(0x2e, keccak256(m, 0xa0)) - // Prepare the struct hash. - mstore(m, _PERMIT_TYPEHASH) - mstore(add(m, 0x20), owner) - mstore(add(m, 0x40), spender) - mstore(add(m, 0x60), value) - mstore(add(m, 0x80), nonceValue) - mstore(add(m, 0xa0), deadline) - mstore(0x4e, keccak256(m, 0xc0)) - // Prepare the ecrecover calldata. - mstore(0x00, keccak256(0x2c, 0x42)) - mstore(0x20, and(0xff, v)) - mstore(0x40, r) - mstore(0x60, s) - let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) - // If the ecrecover fails, the returndatasize will be 0x00, - // `owner` will be checked if it equals the hash at 0x00, - // which evaluates to false (i.e. 0), and we will revert. - // If the ecrecover succeeds, the returndatasize will be 0x20, - // `owner` will be compared against the returned address at 0x20. - if iszero(eq(mload(returndatasize()), owner)) { - mstore(0x00, 0xddafbaef) // `InvalidPermit()`. - revert(0x1c, 0x04) - } - // Increment and store the updated nonce. - sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. - // Compute the allowance slot and store the value. - // The `owner` is already at slot 0x20. - mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) - sstore(keccak256(0x2c, 0x34), value) - // Emit the {Approval} event. - log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) - mstore(0x40, m) // Restore the free memory pointer. - mstore(0x60, 0) // Restore the zero pointer. - } - } - - /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. - function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { - bytes32 nameHash = _constantNameHash(); - // We simply calculate it on-the-fly to allow for cases where the `name` may change. - if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Grab the free memory pointer. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), nameHash) - mstore(add(m, 0x40), _VERSION_HASH) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - result := keccak256(m, 0xa0) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL MINT FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Mints `amount` tokens to `to`, increasing the total supply. - /// - /// Emits a {Transfer} event. - function _mint(address to, uint256 amount) internal virtual { - _beforeTokenTransfer(address(0), to, amount); - /// @solidity memory-safe-assembly - assembly { - let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) - let totalSupplyAfter := add(totalSupplyBefore, amount) - // Revert if the total supply overflows. - if lt(totalSupplyAfter, totalSupplyBefore) { - mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. - revert(0x1c, 0x04) - } - // Store the updated total supply. - sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) - // Compute the balance slot and load its value. - mstore(0x0c, _BALANCE_SLOT_SEED) - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x20) - // Add and store the updated balance. - sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) - // Emit the {Transfer} event. - mstore(0x20, amount) - log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) - } - _afterTokenTransfer(address(0), to, amount); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL BURN FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Burns `amount` tokens from `from`, reducing the total supply. - /// - /// Emits a {Transfer} event. - function _burn(address from, uint256 amount) internal virtual { - _beforeTokenTransfer(from, address(0), amount); - /// @solidity memory-safe-assembly - assembly { - // Compute the balance slot and load its value. - mstore(0x0c, _BALANCE_SLOT_SEED) - mstore(0x00, from) - let fromBalanceSlot := keccak256(0x0c, 0x20) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Subtract and store the updated total supply. - sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) - // Emit the {Transfer} event. - mstore(0x00, amount) - log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) - } - _afterTokenTransfer(from, address(0), amount); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL TRANSFER FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Moves `amount` of tokens from `from` to `to`. - function _transfer(address from, address to, uint256 amount) internal virtual { - _beforeTokenTransfer(from, to, amount); - /// @solidity memory-safe-assembly - assembly { - let from_ := shl(96, from) - // Compute the balance slot and load its value. - mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) - let fromBalanceSlot := keccak256(0x0c, 0x20) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x20) - // Add and store the updated balance of `to`. - // Will not overflow because the sum of all user balances - // cannot exceed the maximum uint256 value. - sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) - // Emit the {Transfer} event. - mstore(0x20, amount) - log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) - } - _afterTokenTransfer(from, to, amount); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL ALLOWANCE FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { - /// @solidity memory-safe-assembly - assembly { - // Compute the allowance slot and load its value. - mstore(0x20, spender) - mstore(0x0c, _ALLOWANCE_SLOT_SEED) - mstore(0x00, owner) - let allowanceSlot := keccak256(0x0c, 0x34) - let allowance_ := sload(allowanceSlot) - // If the allowance is not the maximum uint256 value. - if add(allowance_, 1) { - // Revert if the amount to be transferred exceeds the allowance. - if gt(amount, allowance_) { - mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated allowance. - sstore(allowanceSlot, sub(allowance_, amount)) - } - } - } - - /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. - /// - /// Emits a {Approval} event. - function _approve(address owner, address spender, uint256 amount) internal virtual { - /// @solidity memory-safe-assembly - assembly { - let owner_ := shl(96, owner) - // Compute the allowance slot and store the amount. - mstore(0x20, spender) - mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) - sstore(keccak256(0x0c, 0x34), amount) - // Emit the {Approval} event. - mstore(0x00, amount) - log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HOOKS TO OVERRIDE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Hook that is called before any transfer of tokens. - /// This includes minting and burning. - function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} - - /// @dev Hook that is called after any transfer of tokens. - /// This includes minting and burning. - function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} -} diff --git a/lib/solady/src/tokens/ERC2981.sol b/lib/solady/src/tokens/ERC2981.sol deleted file mode 100644 index be1c7a7..0000000 --- a/lib/solady/src/tokens/ERC2981.sol +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple ERC2981 NFT Royalty Standard implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC2981.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/common/ERC2981.sol) -abstract contract ERC2981 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The royalty fee numerator exceeds the fee denominator. - error RoyaltyOverflow(); - - /// @dev The royalty receiver cannot be the zero address. - error RoyaltyReceiverIsZeroAddress(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The default royalty info is given by: - /// ``` - /// let packed := sload(_ERC2981_MASTER_SLOT_SEED) - /// let receiver := shr(96, packed) - /// let royaltyFraction := xor(packed, shl(96, receiver)) - /// ``` - /// - /// The per token royalty info is given by. - /// ``` - /// mstore(0x00, tokenId) - /// mstore(0x20, _ERC2981_MASTER_SLOT_SEED) - /// let packed := sload(keccak256(0x00, 0x40)) - /// let receiver := shr(96, packed) - /// let royaltyFraction := xor(packed, shl(96, receiver)) - /// ``` - uint256 private constant _ERC2981_MASTER_SLOT_SEED = 0xaa4ec00224afccfdb7; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC2981 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Checks that `_feeDenominator` is non-zero. - constructor() { - require(_feeDenominator() != 0, "Fee denominator cannot be zero."); - } - - /// @dev Returns the denominator for the royalty amount. - /// Defaults to 10000, which represents fees in basis points. - /// Override this function to return a custom amount if needed. - function _feeDenominator() internal pure virtual returns (uint96) { - return 10000; - } - - /// @dev Returns true if this contract implements the interface defined by `interfaceId`. - /// See: https://eips.ethereum.org/EIPS/eip-165 - /// This function call must use less than 30000 gas. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, interfaceId) - // ERC165: 0x01ffc9a7, ERC2981: 0x2a55205a. - result := or(eq(s, 0x01ffc9a7), eq(s, 0x2a55205a)) - } - } - - /// @dev Returns the `receiver` and `royaltyAmount` for `tokenId` sold at `salePrice`. - function royaltyInfo(uint256 tokenId, uint256 salePrice) - public - view - virtual - returns (address receiver, uint256 royaltyAmount) - { - uint256 feeDenominator = _feeDenominator(); - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, tokenId) - mstore(0x20, _ERC2981_MASTER_SLOT_SEED) - let packed := sload(keccak256(0x00, 0x40)) - receiver := shr(96, packed) - if iszero(receiver) { - packed := sload(mload(0x20)) - receiver := shr(96, packed) - } - let x := salePrice - let y := xor(packed, shl(96, receiver)) // `feeNumerator`. - // Overflow check, equivalent to `require(y == 0 || x <= type(uint256).max / y)`. - // Out-of-gas revert. Should not be triggered in practice, but included for safety. - returndatacopy(returndatasize(), returndatasize(), mul(y, gt(x, div(not(0), y)))) - royaltyAmount := div(mul(x, y), feeDenominator) - } - } - - /// @dev Sets the default royalty `receiver` and `feeNumerator`. - /// - /// Requirements: - /// - `receiver` must not be the zero address. - /// - `feeNumerator` must not be greater than the fee denominator. - function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual { - uint256 feeDenominator = _feeDenominator(); - /// @solidity memory-safe-assembly - assembly { - feeNumerator := shr(160, shl(160, feeNumerator)) - if gt(feeNumerator, feeDenominator) { - mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`. - revert(0x1c, 0x04) - } - let packed := shl(96, receiver) - if iszero(packed) { - mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`. - revert(0x1c, 0x04) - } - sstore(_ERC2981_MASTER_SLOT_SEED, or(packed, feeNumerator)) - } - } - - /// @dev Sets the default royalty `receiver` and `feeNumerator` to zero. - function _deleteDefaultRoyalty() internal virtual { - /// @solidity memory-safe-assembly - assembly { - sstore(_ERC2981_MASTER_SLOT_SEED, 0) - } - } - - /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId`. - /// - /// Requirements: - /// - `receiver` must not be the zero address. - /// - `feeNumerator` must not be greater than the fee denominator. - function _setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) - internal - virtual - { - uint256 feeDenominator = _feeDenominator(); - /// @solidity memory-safe-assembly - assembly { - feeNumerator := shr(160, shl(160, feeNumerator)) - if gt(feeNumerator, feeDenominator) { - mstore(0x00, 0x350a88b3) // `RoyaltyOverflow()`. - revert(0x1c, 0x04) - } - let packed := shl(96, receiver) - if iszero(packed) { - mstore(0x00, 0xb4457eaa) // `RoyaltyReceiverIsZeroAddress()`. - revert(0x1c, 0x04) - } - mstore(0x00, tokenId) - mstore(0x20, _ERC2981_MASTER_SLOT_SEED) - sstore(keccak256(0x00, 0x40), or(packed, feeNumerator)) - } - } - - /// @dev Sets the royalty `receiver` and `feeNumerator` for `tokenId` to zero. - function _resetTokenRoyalty(uint256 tokenId) internal virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, tokenId) - mstore(0x20, _ERC2981_MASTER_SLOT_SEED) - sstore(keccak256(0x00, 0x40), 0) - } - } -} diff --git a/lib/solady/src/tokens/ERC4626.sol b/lib/solady/src/tokens/ERC4626.sol deleted file mode 100644 index 0429e33..0000000 --- a/lib/solady/src/tokens/ERC4626.sol +++ /dev/null @@ -1,524 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC20} from "./ERC20.sol"; -import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; - -/// @notice Simple ERC4626 tokenized Vault implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC4626.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol) -abstract contract ERC4626 is ERC20 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The default underlying decimals. - uint8 internal constant _DEFAULT_UNDERLYING_DECIMALS = 18; - - /// @dev The default decimals offset. - uint8 internal constant _DEFAULT_DECIMALS_OFFSET = 0; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Cannot deposit more than the max limit. - error DepositMoreThanMax(); - - /// @dev Cannot mint more than the max limit. - error MintMoreThanMax(); - - /// @dev Cannot withdraw more than the max limit. - error WithdrawMoreThanMax(); - - /// @dev Cannot redeem more than the max limit. - error RedeemMoreThanMax(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted during a mint call or deposit call. - event Deposit(address indexed by, address indexed owner, uint256 assets, uint256 shares); - - /// @dev Emitted during a withdraw call or redeem call. - event Withdraw( - address indexed by, - address indexed to, - address indexed owner, - uint256 assets, - uint256 shares - ); - - /// @dev `keccak256(bytes("Deposit(address,address,uint256,uint256)"))`. - uint256 private constant _DEPOSIT_EVENT_SIGNATURE = - 0xdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7; - - /// @dev `keccak256(bytes("Withdraw(address,address,address,uint256,uint256)"))`. - uint256 private constant _WITHDRAW_EVENT_SIGNATURE = - 0xfbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC4626 CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev To be overridden to return the address of the underlying asset. - /// - /// - MUST be an ERC20 token contract. - /// - MUST NOT revert. - function asset() public view virtual returns (address); - - /// @dev To be overridden to return the number of decimals of the underlying asset. - /// Default: 18. - /// - /// - MUST NOT revert. - function _underlyingDecimals() internal view virtual returns (uint8) { - return _DEFAULT_UNDERLYING_DECIMALS; - } - - /// @dev Override to return a non-zero value to make the inflation attack even more unfeasible. - /// Only used when {_useVirtualShares} returns true. - /// Default: 0. - /// - /// - MUST NOT revert. - function _decimalsOffset() internal view virtual returns (uint8) { - return _DEFAULT_DECIMALS_OFFSET; - } - - /// @dev Returns whether virtual shares will be used to mitigate the inflation attack. - /// See: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3706 - /// Override to return true or false. - /// Default: true. - /// - /// - MUST NOT revert. - function _useVirtualShares() internal view virtual returns (bool) { - return true; - } - - /// @dev Returns the decimals places of the token. - /// - /// - MUST NOT revert. - function decimals() public view virtual override(ERC20) returns (uint8) { - if (!_useVirtualShares()) return _underlyingDecimals(); - return _underlyingDecimals() + _decimalsOffset(); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ASSET DECIMALS GETTER HELPER */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Helper function to get the decimals of the underlying asset. - /// Useful for setting the return value of `_underlyingDecimals` during initialization. - /// If the retrieval succeeds, `success` will be true, and `result` will hold the result. - /// Otherwise, `success` will be false, and `result` will be zero. - /// - /// Example usage: - /// ``` - /// (bool success, uint8 result) = _tryGetAssetDecimals(underlying); - /// _decimals = success ? result : _DEFAULT_UNDERLYING_DECIMALS; - /// ``` - function _tryGetAssetDecimals(address underlying) - internal - view - returns (bool success, uint8 result) - { - /// @solidity memory-safe-assembly - assembly { - // Store the function selector of `decimals()`. - mstore(0x00, 0x313ce567) - // Arguments are evaluated last to first. - success := - and( - // Returned value is less than 256, at left-padded to 32 bytes. - and(lt(mload(0x00), 0x100), gt(returndatasize(), 0x1f)), - // The staticcall succeeds. - staticcall(gas(), underlying, 0x1c, 0x04, 0x00, 0x20) - ) - result := mul(mload(0x00), success) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ACCOUNTING LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the total amount of the underlying asset managed by the Vault. - /// - /// - SHOULD include any compounding that occurs from the yield. - /// - MUST be inclusive of any fees that are charged against assets in the Vault. - /// - MUST NOT revert. - function totalAssets() public view virtual returns (uint256 assets) { - assets = SafeTransferLib.balanceOf(asset(), address(this)); - } - - /// @dev Returns the amount of shares that the Vault will exchange for the amount of - /// assets provided, in an ideal scenario where all conditions are met. - /// - /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. - /// - MUST NOT show any variations depending on the caller. - /// - MUST NOT reflect slippage or other on-chain conditions, during the actual exchange. - /// - MUST NOT revert. - /// - /// Note: This calculation MAY NOT reflect the "per-user" price-per-share, and instead - /// should reflect the "average-user's" price-per-share, i.e. what the average user should - /// expect to see when exchanging to and from. - function convertToShares(uint256 assets) public view virtual returns (uint256 shares) { - if (!_useVirtualShares()) { - uint256 supply = totalSupply(); - return _eitherIsZero(assets, supply) - ? _initialConvertToShares(assets) - : FixedPointMathLib.fullMulDiv(assets, supply, totalAssets()); - } - uint256 o = _decimalsOffset(); - if (o == 0) { - return FixedPointMathLib.fullMulDiv(assets, totalSupply() + 1, _inc(totalAssets())); - } - return FixedPointMathLib.fullMulDiv(assets, totalSupply() + 10 ** o, _inc(totalAssets())); - } - - /// @dev Returns the amount of assets that the Vault will exchange for the amount of - /// shares provided, in an ideal scenario where all conditions are met. - /// - /// - MUST NOT be inclusive of any fees that are charged against assets in the Vault. - /// - MUST NOT show any variations depending on the caller. - /// - MUST NOT reflect slippage or other on-chain conditions, during the actual exchange. - /// - MUST NOT revert. - /// - /// Note: This calculation MAY NOT reflect the "per-user" price-per-share, and instead - /// should reflect the "average-user's" price-per-share, i.e. what the average user should - /// expect to see when exchanging to and from. - function convertToAssets(uint256 shares) public view virtual returns (uint256 assets) { - if (!_useVirtualShares()) { - uint256 supply = totalSupply(); - return supply == 0 - ? _initialConvertToAssets(shares) - : FixedPointMathLib.fullMulDiv(shares, totalAssets(), supply); - } - uint256 o = _decimalsOffset(); - if (o == 0) { - return FixedPointMathLib.fullMulDiv(shares, totalAssets() + 1, _inc(totalSupply())); - } - return FixedPointMathLib.fullMulDiv(shares, totalAssets() + 1, totalSupply() + 10 ** o); - } - - /// @dev Allows an on-chain or off-chain user to simulate the effects of their deposit - /// at the current block, given current on-chain conditions. - /// - /// - MUST return as close to and no more than the exact amount of Vault shares that - /// will be minted in a deposit call in the same transaction, i.e. deposit should - /// return the same or more shares as `previewDeposit` if call in the same transaction. - /// - MUST NOT account for deposit limits like those returned from `maxDeposit` and should - /// always act as if the deposit will be accepted, regardless of approvals, etc. - /// - MUST be inclusive of deposit fees. Integrators should be aware of this. - /// - MUST not revert. - /// - /// Note: Any unfavorable discrepancy between `convertToShares` and `previewDeposit` SHOULD - /// be considered slippage in share price or some other type of condition, meaning - /// the depositor will lose assets by depositing. - function previewDeposit(uint256 assets) public view virtual returns (uint256 shares) { - shares = convertToShares(assets); - } - - /// @dev Allows an on-chain or off-chain user to simulate the effects of their mint - /// at the current block, given current on-chain conditions. - /// - /// - MUST return as close to and no fewer than the exact amount of assets that - /// will be deposited in a mint call in the same transaction, i.e. mint should - /// return the same or fewer assets as `previewMint` if called in the same transaction. - /// - MUST NOT account for mint limits like those returned from `maxMint` and should - /// always act as if the mint will be accepted, regardless of approvals, etc. - /// - MUST be inclusive of deposit fees. Integrators should be aware of this. - /// - MUST not revert. - /// - /// Note: Any unfavorable discrepancy between `convertToAssets` and `previewMint` SHOULD - /// be considered slippage in share price or some other type of condition, - /// meaning the depositor will lose assets by minting. - function previewMint(uint256 shares) public view virtual returns (uint256 assets) { - if (!_useVirtualShares()) { - uint256 supply = totalSupply(); - return supply == 0 - ? _initialConvertToAssets(shares) - : FixedPointMathLib.fullMulDivUp(shares, totalAssets(), supply); - } - uint256 o = _decimalsOffset(); - if (o == 0) { - return FixedPointMathLib.fullMulDivUp(shares, totalAssets() + 1, _inc(totalSupply())); - } - return FixedPointMathLib.fullMulDivUp(shares, totalAssets() + 1, totalSupply() + 10 ** o); - } - - /// @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal - /// at the current block, given the current on-chain conditions. - /// - /// - MUST return as close to and no fewer than the exact amount of Vault shares that - /// will be burned in a withdraw call in the same transaction, i.e. withdraw should - /// return the same or fewer shares as `previewWithdraw` if call in the same transaction. - /// - MUST NOT account for withdrawal limits like those returned from `maxWithdraw` and should - /// always act as if the withdrawal will be accepted, regardless of share balance, etc. - /// - MUST be inclusive of withdrawal fees. Integrators should be aware of this. - /// - MUST not revert. - /// - /// Note: Any unfavorable discrepancy between `convertToShares` and `previewWithdraw` SHOULD - /// be considered slippage in share price or some other type of condition, - /// meaning the depositor will lose assets by depositing. - function previewWithdraw(uint256 assets) public view virtual returns (uint256 shares) { - if (!_useVirtualShares()) { - uint256 supply = totalSupply(); - return _eitherIsZero(assets, supply) - ? _initialConvertToShares(assets) - : FixedPointMathLib.fullMulDivUp(assets, supply, totalAssets()); - } - uint256 o = _decimalsOffset(); - if (o == 0) { - return FixedPointMathLib.fullMulDivUp(assets, totalSupply() + 1, _inc(totalAssets())); - } - return FixedPointMathLib.fullMulDivUp(assets, totalSupply() + 10 ** o, _inc(totalAssets())); - } - - /// @dev Allows an on-chain or off-chain user to simulate the effects of their redemption - /// at the current block, given current on-chain conditions. - /// - /// - MUST return as close to and no more than the exact amount of assets that - /// will be withdrawn in a redeem call in the same transaction, i.e. redeem should - /// return the same or more assets as `previewRedeem` if called in the same transaction. - /// - MUST NOT account for redemption limits like those returned from `maxRedeem` and should - /// always act as if the redemption will be accepted, regardless of approvals, etc. - /// - MUST be inclusive of withdrawal fees. Integrators should be aware of this. - /// - MUST NOT revert. - /// - /// Note: Any unfavorable discrepancy between `convertToAssets` and `previewRedeem` SHOULD - /// be considered slippage in share price or some other type of condition, - /// meaning the depositor will lose assets by depositing. - function previewRedeem(uint256 shares) public view virtual returns (uint256 assets) { - assets = convertToAssets(shares); - } - - /// @dev Private helper to return if either value is zero. - function _eitherIsZero(uint256 a, uint256 b) private pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := or(iszero(a), iszero(b)) - } - } - - /// @dev Private helper to return the value plus one. - function _inc(uint256 x) private pure returns (uint256) { - unchecked { - return x + 1; - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DEPOSIT / WITHDRAWAL LIMIT LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the maximum amount of the underlying asset that can be deposited - /// into the Vault for `to`, via a deposit call. - /// - /// - MUST return a limited value if `to` is subject to some deposit limit. - /// - MUST return `2**256-1` if there is no maximum limit. - /// - MUST NOT revert. - function maxDeposit(address to) public view virtual returns (uint256 maxAssets) { - to = to; // Silence unused variable warning. - maxAssets = type(uint256).max; - } - - /// @dev Returns the maximum amount of the Vault shares that can be minter for `to`, - /// via a mint call. - /// - /// - MUST return a limited value if `to` is subject to some mint limit. - /// - MUST return `2**256-1` if there is no maximum limit. - /// - MUST NOT revert. - function maxMint(address to) public view virtual returns (uint256 maxShares) { - to = to; // Silence unused variable warning. - maxShares = type(uint256).max; - } - - /// @dev Returns the maximum amount of the underlying asset that can be withdrawn - /// from the `owner`'s balance in the Vault, via a withdraw call. - /// - /// - MUST return a limited value if `owner` is subject to some withdrawal limit or timelock. - /// - MUST NOT revert. - function maxWithdraw(address owner) public view virtual returns (uint256 maxAssets) { - maxAssets = convertToAssets(balanceOf(owner)); - } - - /// @dev Returns the maximum amount of Vault shares that can be redeemed - /// from the `owner`'s balance in the Vault, via a redeem call. - /// - /// - MUST return a limited value if `owner` is subject to some withdrawal limit or timelock. - /// - MUST return `balanceOf(owner)` otherwise. - /// - MUST NOT revert. - function maxRedeem(address owner) public view virtual returns (uint256 maxShares) { - maxShares = balanceOf(owner); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DEPOSIT / WITHDRAWAL LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Mints `shares` Vault shares to `to` by depositing exactly `assets` - /// of underlying tokens. - /// - /// - MUST emit the {Deposit} event. - /// - MAY support an additional flow in which the underlying tokens are owned by the Vault - /// contract before the deposit execution, and are accounted for during deposit. - /// - MUST revert if all of `assets` cannot be deposited, such as due to deposit limit, - /// slippage, insufficient approval, etc. - /// - /// Note: Most implementations will require pre-approval of the Vault with the - /// Vault's underlying `asset` token. - function deposit(uint256 assets, address to) public virtual returns (uint256 shares) { - if (assets > maxDeposit(to)) _revert(0xb3c61a83); // `DepositMoreThanMax()`. - shares = previewDeposit(assets); - _deposit(msg.sender, to, assets, shares); - } - - /// @dev Mints exactly `shares` Vault shares to `to` by depositing `assets` - /// of underlying tokens. - /// - /// - MUST emit the {Deposit} event. - /// - MAY support an additional flow in which the underlying tokens are owned by the Vault - /// contract before the mint execution, and are accounted for during mint. - /// - MUST revert if all of `shares` cannot be deposited, such as due to deposit limit, - /// slippage, insufficient approval, etc. - /// - /// Note: Most implementations will require pre-approval of the Vault with the - /// Vault's underlying `asset` token. - function mint(uint256 shares, address to) public virtual returns (uint256 assets) { - if (shares > maxMint(to)) _revert(0x6a695959); // `MintMoreThanMax()`. - assets = previewMint(shares); - _deposit(msg.sender, to, assets, shares); - } - - /// @dev Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `to`. - /// - /// - MUST emit the {Withdraw} event. - /// - MAY support an additional flow in which the underlying tokens are owned by the Vault - /// contract before the withdraw execution, and are accounted for during withdraw. - /// - MUST revert if all of `assets` cannot be withdrawn, such as due to withdrawal limit, - /// slippage, insufficient balance, etc. - /// - /// Note: Some implementations will require pre-requesting to the Vault before a withdrawal - /// may be performed. Those methods should be performed separately. - function withdraw(uint256 assets, address to, address owner) - public - virtual - returns (uint256 shares) - { - if (assets > maxWithdraw(owner)) _revert(0x936941fc); // `WithdrawMoreThanMax()`. - shares = previewWithdraw(assets); - _withdraw(msg.sender, to, owner, assets, shares); - } - - /// @dev Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `to`. - /// - /// - MUST emit the {Withdraw} event. - /// - MAY support an additional flow in which the underlying tokens are owned by the Vault - /// contract before the redeem execution, and are accounted for during redeem. - /// - MUST revert if all of shares cannot be redeemed, such as due to withdrawal limit, - /// slippage, insufficient balance, etc. - /// - /// Note: Some implementations will require pre-requesting to the Vault before a redeem - /// may be performed. Those methods should be performed separately. - function redeem(uint256 shares, address to, address owner) - public - virtual - returns (uint256 assets) - { - if (shares > maxRedeem(owner)) _revert(0x4656425a); // `RedeemMoreThanMax()`. - assets = previewRedeem(shares); - _withdraw(msg.sender, to, owner, assets, shares); - } - - /// @dev Internal helper for reverting efficiently. - function _revert(uint256 s) private pure { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, s) - revert(0x1c, 0x04) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev For deposits and mints. - /// - /// Emits a {Deposit} event. - function _deposit(address by, address to, uint256 assets, uint256 shares) internal virtual { - SafeTransferLib.safeTransferFrom(asset(), by, address(this), assets); - _mint(to, shares); - /// @solidity memory-safe-assembly - assembly { - // Emit the {Deposit} event. - mstore(0x00, assets) - mstore(0x20, shares) - let m := shr(96, not(0)) - log3(0x00, 0x40, _DEPOSIT_EVENT_SIGNATURE, and(m, by), and(m, to)) - } - _afterDeposit(assets, shares); - } - - /// @dev For withdrawals and redemptions. - /// - /// Emits a {Withdraw} event. - function _withdraw(address by, address to, address owner, uint256 assets, uint256 shares) - internal - virtual - { - if (by != owner) _spendAllowance(owner, by, shares); - _beforeWithdraw(assets, shares); - _burn(owner, shares); - SafeTransferLib.safeTransfer(asset(), to, assets); - /// @solidity memory-safe-assembly - assembly { - // Emit the {Withdraw} event. - mstore(0x00, assets) - mstore(0x20, shares) - let m := shr(96, not(0)) - log4(0x00, 0x40, _WITHDRAW_EVENT_SIGNATURE, and(m, by), and(m, to), and(m, owner)) - } - } - - /// @dev Internal conversion function (from assets to shares) to apply when the Vault is empty. - /// Only used when {_useVirtualShares} returns false. - /// - /// Note: Make sure to keep this function consistent with {_initialConvertToAssets} - /// when overriding it. - function _initialConvertToShares(uint256 assets) - internal - view - virtual - returns (uint256 shares) - { - shares = assets; - } - - /// @dev Internal conversion function (from shares to assets) to apply when the Vault is empty. - /// Only used when {_useVirtualShares} returns false. - /// - /// Note: Make sure to keep this function consistent with {_initialConvertToShares} - /// when overriding it. - function _initialConvertToAssets(uint256 shares) - internal - view - virtual - returns (uint256 assets) - { - assets = shares; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HOOKS TO OVERRIDE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Hook that is called before any withdrawal or redemption. - function _beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} - - /// @dev Hook that is called after any deposit or mint. - function _afterDeposit(uint256 assets, uint256 shares) internal virtual {} -} diff --git a/lib/solady/src/tokens/ERC6909.sol b/lib/solady/src/tokens/ERC6909.sol deleted file mode 100644 index 871bba1..0000000 --- a/lib/solady/src/tokens/ERC6909.sol +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple EIP-6909 implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC6909.sol) -/// -/// @dev Note: -/// The ERC6909 standard allows minting and transferring to and from the zero address, -/// minting and transferring zero tokens, as well as self-approvals. -/// For performance, this implementation WILL NOT revert for such actions. -/// Please add any checks with overrides if desired. -/// -/// If you are overriding: -/// - Make sure all variables written to storage are properly cleaned -// (e.g. the bool value for `isOperator` MUST be either 1 or 0 under the hood). -/// - Check that the overridden function is actually used in the function you want to -/// change the behavior of. Much of the code has been manually inlined for performance. -abstract contract ERC6909 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Insufficient balance. - error InsufficientBalance(); - - /// @dev Insufficient permission to perform the action. - error InsufficientPermission(); - - /// @dev The balance has overflowed. - error BalanceOverflow(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when `by` transfers `amount` of token `id` from `from` to `to`. - event Transfer( - address by, address indexed from, address indexed to, uint256 indexed id, uint256 amount - ); - - /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. - event OperatorSet(address indexed owner, address indexed operator, bool approved); - - /// @dev Emitted when `owner` approves `spender` to use `amount` of `id` token. - event Approval( - address indexed owner, address indexed spender, uint256 indexed id, uint256 amount - ); - - /// @dev `keccak256(bytes("Transfer(address,address,address,uint256,uint256)"))`. - uint256 private constant _TRANSFER_EVENT_SIGNATURE = - 0x1b3d7edb2e9c0b0e7c525b20aaaef0f5940d2ed71663c7d39266ecafac728859; - - /// @dev `keccak256(bytes("OperatorSet(address,address,bool)"))`. - uint256 private constant _OPERATOR_SET_EVENT_SIGNATURE = - 0xceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267; - - /// @dev `keccak256(bytes("Approval(address,address,uint256,uint256)"))`. - uint256 private constant _APPROVAL_EVENT_SIGNATURE = - 0xb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The `ownerSlotSeed` of a given owner is given by. - /// ``` - /// let ownerSlotSeed := or(_ERC6909_MASTER_SLOT_SEED, shl(96, owner)) - /// ``` - /// - /// The balance slot of `owner` is given by. - /// ``` - /// mstore(0x20, ownerSlotSeed) - /// mstore(0x00, id) - /// let balanceSlot := keccak256(0x00, 0x40) - /// ``` - /// - /// The operator approval slot of `owner` is given by. - /// ``` - /// mstore(0x20, ownerSlotSeed) - /// mstore(0x00, operator) - /// let operatorApprovalSlot := keccak256(0x0c, 0x34) - /// ``` - /// - /// The allowance slot of (`owner`, `spender`, `id`) is given by: - /// ``` - /// mstore(0x34, ownerSlotSeed) - /// mstore(0x14, spender) - /// mstore(0x00, id) - /// let allowanceSlot := keccak256(0x00, 0x54) - /// ``` - uint256 private constant _ERC6909_MASTER_SLOT_SEED = 0xedcaa89a82293940; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC6909 METADATA */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the token collection name. - function name() public view virtual returns (string memory); - - /// @dev Returns the token collection symbol. - function symbol() public view virtual returns (string memory); - - /// @dev Returns the number of decimals for token `id`. - /// Returns 18 by default. - /// Please override this function if you need to return a custom value. - function decimals(uint256 id) public view virtual returns (uint8) { - id = id; // Silence compiler warning. - return 18; - } - - /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. - function tokenURI(uint256 id) public view virtual returns (string memory); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC6909 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the amount of token `id` owned by `owner`. - function balanceOf(address owner, uint256 id) public view virtual returns (uint256 amount) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, owner) - mstore(0x00, id) - amount := sload(keccak256(0x00, 0x40)) - } - } - - /// @dev Returns the amount of token `id` that `spender` can spend on behalf of `owner`. - function allowance(address owner, address spender, uint256 id) - public - view - virtual - returns (uint256 amount) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x34, _ERC6909_MASTER_SLOT_SEED) - mstore(0x28, owner) - mstore(0x14, spender) - mstore(0x00, id) - amount := sload(keccak256(0x00, 0x54)) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x34, 0x00) - } - } - - /// @dev Checks if a `spender` is approved by `owner` to manage all of their tokens. - function isOperator(address owner, address spender) public view virtual returns (bool status) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, owner) - mstore(0x00, spender) - status := sload(keccak256(0x0c, 0x34)) - } - } - - /// @dev Transfers `amount` of token `id` from the caller to `to`. - /// - /// Requirements: - /// - caller must at least have `amount`. - /// - /// Emits a {Transfer} event. - function transfer(address to, uint256 id, uint256 amount) - public - payable - virtual - returns (bool) - { - _beforeTokenTransfer(msg.sender, to, id, amount); - /// @solidity memory-safe-assembly - assembly { - /// Compute the balance slot and load its value. - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, caller()) - mstore(0x00, id) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x14, to) - mstore(0x00, id) - let toBalanceSlot := keccak256(0x00, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - // Revert if the balance overflows. - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x89560ca1) // `BalanceOverflow()`. - revert(0x1c, 0x04) - } - // Store the updated balance of `to`. - sstore(toBalanceSlot, toBalanceAfter) - // Emit the {Transfer} event. - mstore(0x00, caller()) - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, shl(96, to)), id) - } - _afterTokenTransfer(msg.sender, to, id, amount); - return true; - } - - /// @dev Transfers `amount` of token `id` from `from` to `to`. - /// - /// Note: Does not update the allowance if it is the maximum uint256 value. - /// - /// Requirements: - /// - `from` must at least have `amount` of token `id`. - /// - The caller must have at least `amount` of allowance to transfer the - /// tokens of `from` or approved as an operator. - /// - /// Emits a {Transfer} event. - function transferFrom(address from, address to, uint256 id, uint256 amount) - public - payable - virtual - returns (bool) - { - _beforeTokenTransfer(from, to, id, amount); - /// @solidity memory-safe-assembly - assembly { - // Compute the operator slot and load its value. - mstore(0x34, _ERC6909_MASTER_SLOT_SEED) - mstore(0x28, from) - mstore(0x14, caller()) - // Check if the caller is an operator. - if iszero(sload(keccak256(0x20, 0x34))) { - // Compute the allowance slot and load its value. - mstore(0x00, id) - let allowanceSlot := keccak256(0x00, 0x54) - let allowance_ := sload(allowanceSlot) - // If the allowance is not the maximum uint256 value. - if add(allowance_, 1) { - // Revert if the amount to be transferred exceeds the allowance. - if gt(amount, allowance_) { - mstore(0x00, 0xdeda9030) // `InsufficientPermission()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated allowance. - sstore(allowanceSlot, sub(allowance_, amount)) - } - } - // Compute the balance slot and load its value. - mstore(0x14, id) - let fromBalanceSlot := keccak256(0x14, 0x40) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x28, to) - mstore(0x14, id) - let toBalanceSlot := keccak256(0x14, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - // Revert if the balance overflows. - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x89560ca1) // `BalanceOverflow()`. - revert(0x1c, 0x04) - } - // Store the updated balance of `to`. - sstore(toBalanceSlot, toBalanceAfter) - // Emit the {Transfer} event. - mstore(0x00, caller()) - mstore(0x20, amount) - // forgefmt: disable-next-line - log4(0x00, 0x40, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), shr(96, shl(96, to)), id) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x34, 0x00) - } - _afterTokenTransfer(from, to, id, amount); - return true; - } - - /// @dev Sets `amount` as the allowance of `spender` for the caller for token `id`. - /// - /// Emits a {Approval} event. - function approve(address spender, uint256 id, uint256 amount) - public - payable - virtual - returns (bool) - { - /// @solidity memory-safe-assembly - assembly { - // Compute the allowance slot and store the amount. - mstore(0x34, _ERC6909_MASTER_SLOT_SEED) - mstore(0x28, caller()) - mstore(0x14, spender) - mstore(0x00, id) - sstore(keccak256(0x00, 0x54), amount) - // Emit the {Approval} event. - mstore(0x00, amount) - log4(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x20)), id) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x34, 0x00) - } - return true; - } - - /// @dev Sets whether `operator` is approved to manage the tokens of the caller. - /// - /// Emits {OperatorSet} event. - function setOperator(address operator, bool approved) public payable virtual returns (bool) { - /// @solidity memory-safe-assembly - assembly { - // Convert `approved` to `0` or `1`. - let approvedCleaned := iszero(iszero(approved)) - // Compute the operator slot and store the approved. - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, caller()) - mstore(0x00, operator) - sstore(keccak256(0x0c, 0x34), approvedCleaned) - // Emit the {OperatorSet} event. - mstore(0x20, approvedCleaned) - log3(0x20, 0x20, _OPERATOR_SET_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) - } - return true; - } - - /// @dev Returns true if this contract implements the interface defined by `interfaceId`. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, interfaceId) - // ERC165: 0x01ffc9a7, ERC6909: 0x0f632fb3. - result := or(eq(s, 0x01ffc9a7), eq(s, 0x0f632fb3)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Mints `amount` of token `id` to `to`. - /// - /// Emits a {Transfer} event. - function _mint(address to, uint256 id, uint256 amount) internal virtual { - _beforeTokenTransfer(address(0), to, id, amount); - /// @solidity memory-safe-assembly - assembly { - // Compute the balance slot. - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, to) - mstore(0x00, id) - let toBalanceSlot := keccak256(0x00, 0x40) - // Add and store the updated balance - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - // Revert if the balance overflows. - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x89560ca1) // `BalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceAfter) - // Emit the {Transfer} event. - mstore(0x00, caller()) - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, shl(96, to)), id) - } - _afterTokenTransfer(address(0), to, id, amount); - } - - /// @dev Burns `amount` token `id` from `from`. - /// - /// Emits a {Transfer} event. - function _burn(address from, uint256 id, uint256 amount) internal virtual { - _beforeTokenTransfer(from, address(0), id, amount); - /// @solidity memory-safe-assembly - assembly { - // Compute the balance slot. - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, from) - mstore(0x00, id) - let fromBalanceSlot := keccak256(0x00, 0x40) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Emit the {Transfer} event. - mstore(0x00, caller()) - mstore(0x20, amount) - log4(0x00, 0x40, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0, id) - } - _afterTokenTransfer(from, address(0), id, amount); - } - - /// @dev Transfers `amount` of token `id` from `from` to `to`. - /// - /// Note: Does not update the allowance if it is the maximum uint256 value. - /// - /// Requirements: - /// - `from` must at least have `amount` of token `id`. - /// - If `by` is not the zero address, - /// it must have at least `amount` of allowance to transfer the - /// tokens of `from` or approved as an operator. - /// - /// Emits a {Transfer} event. - function _transfer(address by, address from, address to, uint256 id, uint256 amount) - internal - virtual - { - _beforeTokenTransfer(from, to, id, amount); - /// @solidity memory-safe-assembly - assembly { - let bitmaskAddress := 0xffffffffffffffffffffffffffffffffffffffff - // Compute the operator slot and load its value. - mstore(0x34, _ERC6909_MASTER_SLOT_SEED) - mstore(0x28, from) - // If `by` is not the zero address. - if and(bitmaskAddress, by) { - mstore(0x14, by) - // Check if the `by` is an operator. - if iszero(sload(keccak256(0x20, 0x34))) { - // Compute the allowance slot and load its value. - mstore(0x00, id) - let allowanceSlot := keccak256(0x00, 0x54) - let allowance_ := sload(allowanceSlot) - // If the allowance is not the maximum uint256 value. - if add(allowance_, 1) { - // Revert if the amount to be transferred exceeds the allowance. - if gt(amount, allowance_) { - mstore(0x00, 0xdeda9030) // `InsufficientPermission()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated allowance. - sstore(allowanceSlot, sub(allowance_, amount)) - } - } - } - // Compute the balance slot and load its value. - mstore(0x14, id) - let fromBalanceSlot := keccak256(0x14, 0x40) - let fromBalance := sload(fromBalanceSlot) - // Revert if insufficient balance. - if gt(amount, fromBalance) { - mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. - revert(0x1c, 0x04) - } - // Subtract and store the updated balance. - sstore(fromBalanceSlot, sub(fromBalance, amount)) - // Compute the balance slot of `to`. - mstore(0x28, to) - mstore(0x14, id) - let toBalanceSlot := keccak256(0x14, 0x40) - let toBalanceBefore := sload(toBalanceSlot) - let toBalanceAfter := add(toBalanceBefore, amount) - // Revert if the balance overflows. - if lt(toBalanceAfter, toBalanceBefore) { - mstore(0x00, 0x89560ca1) // `BalanceOverflow()`. - revert(0x1c, 0x04) - } - // Store the updated balance of `to`. - sstore(toBalanceSlot, toBalanceAfter) - // Emit the {Transfer} event. - mstore(0x00, and(bitmaskAddress, by)) - mstore(0x20, amount) - // forgefmt: disable-next-line - log4(0x00, 0x40, _TRANSFER_EVENT_SIGNATURE, and(bitmaskAddress, from), and(bitmaskAddress, to), id) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x34, 0x00) - } - _afterTokenTransfer(from, to, id, amount); - } - - /// @dev Sets `amount` as the allowance of `spender` for `owner` for token `id`. - /// - /// Emits a {Approval} event. - function _approve(address owner, address spender, uint256 id, uint256 amount) - internal - virtual - { - /// @solidity memory-safe-assembly - assembly { - // Compute the allowance slot and store the amount. - mstore(0x34, _ERC6909_MASTER_SLOT_SEED) - mstore(0x28, owner) - mstore(0x14, spender) - mstore(0x00, id) - sstore(keccak256(0x00, 0x54), amount) - // Emit the {Approval} event. - mstore(0x00, amount) - // forgefmt: disable-next-line - log4(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, mload(0x34)), shr(96, mload(0x20)), id) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x34, 0x00) - } - } - - /// @dev Sets whether `operator` is approved to manage the tokens of `owner`. - /// - /// Emits {OperatorSet} event. - function _setOperator(address owner, address operator, bool approved) internal virtual { - /// @solidity memory-safe-assembly - assembly { - // Convert `approved` to `0` or `1`. - let approvedCleaned := iszero(iszero(approved)) - // Compute the operator slot and store the approved. - mstore(0x20, _ERC6909_MASTER_SLOT_SEED) - mstore(0x14, owner) - mstore(0x00, operator) - sstore(keccak256(0x0c, 0x34), approvedCleaned) - // Emit the {OperatorSet} event. - mstore(0x20, approvedCleaned) - // forgefmt: disable-next-line - log3(0x20, 0x20, _OPERATOR_SET_EVENT_SIGNATURE, shr(96, shl(96, owner)), shr(96, mload(0x0c))) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HOOKS TO OVERRIDE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Hook that is called before any transfer of tokens. - /// This includes minting and burning. - function _beforeTokenTransfer(address from, address to, uint256 id, uint256 amount) - internal - virtual - {} - - /// @dev Hook that is called after any transfer of tokens. - /// This includes minting and burning. - function _afterTokenTransfer(address from, address to, uint256 id, uint256 amount) - internal - virtual - {} -} diff --git a/lib/solady/src/tokens/ERC721.sol b/lib/solady/src/tokens/ERC721.sol deleted file mode 100644 index d21b424..0000000 --- a/lib/solady/src/tokens/ERC721.sol +++ /dev/null @@ -1,890 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Simple ERC721 implementation with storage hitchhiking. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol) -/// -/// @dev Note: -/// - The ERC721 standard allows for self-approvals. -/// For performance, this implementation WILL NOT revert for such actions. -/// Please add any checks with overrides if desired. -/// - For performance, methods are made payable where permitted by the ERC721 standard. -/// - The `safeTransfer` functions use the identity precompile (0x4) -/// to copy memory internally. -/// -/// If you are overriding: -/// - NEVER violate the ERC721 invariant: -/// the balance of an owner MUST always be equal to their number of ownership slots. -/// The transfer functions do not have an underflow guard for user token balances. -/// - Make sure all variables written to storage are properly cleaned -// (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood). -/// - Check that the overridden function is actually used in the function you want to -/// change the behavior of. Much of the code has been manually inlined for performance. -abstract contract ERC721 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev An account can hold up to 4294967295 tokens. - uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Only the token owner or an approved account can manage the token. - error NotOwnerNorApproved(); - - /// @dev The token does not exist. - error TokenDoesNotExist(); - - /// @dev The token already exists. - error TokenAlreadyExists(); - - /// @dev Cannot query the balance for the zero address. - error BalanceQueryForZeroAddress(); - - /// @dev Cannot mint or transfer to the zero address. - error TransferToZeroAddress(); - - /// @dev The token must be owned by `from`. - error TransferFromIncorrectOwner(); - - /// @dev The recipient's balance has overflowed. - error AccountBalanceOverflow(); - - /// @dev Cannot safely transfer to a contract that does not implement - /// the ERC721Receiver interface. - error TransferToNonERC721ReceiverImplementer(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when token `id` is transferred from `from` to `to`. - event Transfer(address indexed from, address indexed to, uint256 indexed id); - - /// @dev Emitted when `owner` enables `account` to manage the `id` token. - event Approval(address indexed owner, address indexed account, uint256 indexed id); - - /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens. - event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); - - /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. - uint256 private constant _TRANSFER_EVENT_SIGNATURE = - 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; - - /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. - uint256 private constant _APPROVAL_EVENT_SIGNATURE = - 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; - - /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`. - uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE = - 0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ownership data slot of `id` is given by: - /// ``` - /// mstore(0x00, id) - /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - /// let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - /// ``` - /// Bits Layout: - /// - [0..159] `addr` - /// - [160..255] `extraData` - /// - /// The approved address slot is given by: `add(1, ownershipSlot)`. - /// - /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip - /// - /// The balance slot of `owner` is given by: - /// ``` - /// mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - /// mstore(0x00, owner) - /// let balanceSlot := keccak256(0x0c, 0x1c) - /// ``` - /// Bits Layout: - /// - [0..31] `balance` - /// - [32..255] `aux` - /// - /// The `operator` approval slot of `owner` is given by: - /// ``` - /// mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) - /// mstore(0x00, owner) - /// let operatorApprovalSlot := keccak256(0x0c, 0x30) - /// ``` - uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; - - /// @dev Pre-shifted and pre-masked constant. - uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC721 METADATA */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the token collection name. - function name() public view virtual returns (string memory); - - /// @dev Returns the token collection symbol. - function symbol() public view virtual returns (string memory); - - /// @dev Returns the Uniform Resource Identifier (URI) for token `id`. - function tokenURI(uint256 id) public view virtual returns (string memory); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC721 */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the owner of token `id`. - /// - /// Requirements: - /// - Token `id` must exist. - function ownerOf(uint256 id) public view virtual returns (address result) { - result = _ownerOf(id); - /// @solidity memory-safe-assembly - assembly { - if iszero(result) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Returns the number of tokens owned by `owner`. - /// - /// Requirements: - /// - `owner` must not be the zero address. - function balanceOf(address owner) public view virtual returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - // Revert if the `owner` is the zero address. - if iszero(owner) { - mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`. - revert(0x1c, 0x04) - } - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner) - result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE) - } - } - - /// @dev Returns the account approved to manage token `id`. - /// - /// Requirements: - /// - Token `id` must exist. - function getApproved(uint256 id) public view virtual returns (address result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - if iszero(shl(96, sload(ownershipSlot))) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - result := sload(add(1, ownershipSlot)) - } - } - - /// @dev Sets `account` as the approved account to manage token `id`. - /// - /// Requirements: - /// - Token `id` must exist. - /// - The caller must be the owner of the token, - /// or an approved operator for the token owner. - /// - /// Emits an {Approval} event. - function approve(address account, uint256 id) public payable virtual { - _approve(msg.sender, account, id); - } - - /// @dev Returns whether `operator` is approved to manage the tokens of `owner`. - function isApprovedForAll(address owner, address operator) - public - view - virtual - returns (bool result) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x1c, operator) - mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) - mstore(0x00, owner) - result := sload(keccak256(0x0c, 0x30)) - } - } - - /// @dev Sets whether `operator` is approved to manage the tokens of the caller. - /// - /// Emits an {ApprovalForAll} event. - function setApprovalForAll(address operator, bool isApproved) public virtual { - /// @solidity memory-safe-assembly - assembly { - // Convert to 0 or 1. - isApproved := iszero(iszero(isApproved)) - // Update the `isApproved` for (`msg.sender`, `operator`). - mstore(0x1c, operator) - mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED) - mstore(0x00, caller()) - sstore(keccak256(0x0c, 0x30), isApproved) - // Emit the {ApprovalForAll} event. - mstore(0x00, isApproved) - // forgefmt: disable-next-item - log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))) - } - } - - /// @dev Transfers token `id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - `from` must be the owner of the token. - /// - `to` cannot be the zero address. - /// - The caller must be the owner of the token, or be approved to manage the token. - /// - /// Emits a {Transfer} event. - function transferFrom(address from, address to, uint256 id) public payable virtual { - _beforeTokenTransfer(from, to, id); - /// @solidity memory-safe-assembly - assembly { - // Clear the upper 96 bits. - let bitmaskAddress := shr(96, not(0)) - from := and(bitmaskAddress, from) - to := and(bitmaskAddress, to) - // Load the ownership data. - mstore(0x00, id) - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller())) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let ownershipPacked := sload(ownershipSlot) - let owner := and(bitmaskAddress, ownershipPacked) - // Revert if `from` is not the owner, or does not exist. - if iszero(mul(owner, eq(owner, from))) { - if iszero(owner) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`. - revert(0x1c, 0x04) - } - // Revert if `to` is the zero address. - if iszero(to) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // Load, check, and update the token approval. - { - mstore(0x00, from) - let approvedAddress := sload(add(1, ownershipSlot)) - // Revert if the caller is not the owner, nor approved. - if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) { - if iszero(sload(keccak256(0x0c, 0x30))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Delete the approved address if any. - if approvedAddress { sstore(add(1, ownershipSlot), 0) } - } - // Update with the new owner. - sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) - // Decrement the balance of `from`. - { - let fromBalanceSlot := keccak256(0x0c, 0x1c) - sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) - } - // Increment the balance of `to`. - { - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x1c) - let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) - if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceSlotPacked) - } - // Emit the {Transfer} event. - log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) - } - _afterTokenTransfer(from, to, id); - } - - /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`. - function safeTransferFrom(address from, address to, uint256 id) public payable virtual { - transferFrom(from, to, id); - if (_hasCode(to)) _checkOnERC721Received(from, to, id, ""); - } - - /// @dev Transfers token `id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - `from` must be the owner of the token. - /// - `to` cannot be the zero address. - /// - The caller must be the owner of the token, or be approved to manage the token. - /// - If `to` refers to a smart contract, it must implement - /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - /// - /// Emits a {Transfer} event. - function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) - public - payable - virtual - { - transferFrom(from, to, id); - if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); - } - - /// @dev Returns true if this contract implements the interface defined by `interfaceId`. - /// See: https://eips.ethereum.org/EIPS/eip-165 - /// This function call must use less than 30000 gas. - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let s := shr(224, interfaceId) - // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f. - result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f)) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL QUERY FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns if token `id` exists. - function _exists(uint256 id) internal view virtual returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))) - } - } - - /// @dev Returns the owner of token `id`. - /// Returns the zero address instead of reverting if the token does not exist. - function _ownerOf(uint256 id) internal view virtual returns (address result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL DATA HITCHHIKING FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // For performance, no events are emitted for the hitchhiking setters. - // Please emit your own events if required. - - /// @dev Returns the auxiliary data for `owner`. - /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. - /// Auxiliary data can be set for any address, even if it does not have any tokens. - function _getAux(address owner) internal view virtual returns (uint224 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner) - result := shr(32, sload(keccak256(0x0c, 0x1c))) - } - } - - /// @dev Set the auxiliary data for `owner` to `value`. - /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data. - /// Auxiliary data can be set for any address, even if it does not have any tokens. - function _setAux(address owner, uint224 value) internal virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner) - let balanceSlot := keccak256(0x0c, 0x1c) - let packed := sload(balanceSlot) - sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed))))) - } - } - - /// @dev Returns the extra data for token `id`. - /// Minting, transferring, burning a token will not change the extra data. - /// The extra data can be set on a non-existent token. - function _getExtraData(uint256 id) internal view virtual returns (uint96 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20))))) - } - } - - /// @dev Sets the extra data for token `id` to `value`. - /// Minting, transferring, burning a token will not change the extra data. - /// The extra data can be set on a non-existent token. - function _setExtraData(uint256 id, uint96 value) internal virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let packed := sload(ownershipSlot) - sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed))))) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL MINT FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Mints token `id` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must not exist. - /// - `to` cannot be the zero address. - /// - /// Emits a {Transfer} event. - function _mint(address to, uint256 id) internal virtual { - _beforeTokenTransfer(address(0), to, id); - /// @solidity memory-safe-assembly - assembly { - // Clear the upper 96 bits. - to := shr(96, shl(96, to)) - // Revert if `to` is the zero address. - if iszero(to) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // Load the ownership data. - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let ownershipPacked := sload(ownershipSlot) - // Revert if the token already exists. - if shl(96, ownershipPacked) { - mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`. - revert(0x1c, 0x04) - } - // Update with the owner. - sstore(ownershipSlot, or(ownershipPacked, to)) - // Increment the balance of the owner. - { - mstore(0x00, to) - let balanceSlot := keccak256(0x0c, 0x1c) - let balanceSlotPacked := add(sload(balanceSlot), 1) - if iszero(and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(balanceSlot, balanceSlotPacked) - } - // Emit the {Transfer} event. - log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id) - } - _afterTokenTransfer(address(0), to, id); - } - - /// @dev Equivalent to `_safeMint(to, id, "")`. - function _safeMint(address to, uint256 id) internal virtual { - _safeMint(to, id, ""); - } - - /// @dev Mints token `id` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must not exist. - /// - `to` cannot be the zero address. - /// - If `to` refers to a smart contract, it must implement - /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - /// - /// Emits a {Transfer} event. - function _safeMint(address to, uint256 id, bytes memory data) internal virtual { - _mint(to, id); - if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL BURN FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to `_burn(address(0), id)`. - function _burn(uint256 id) internal virtual { - _burn(address(0), id); - } - - /// @dev Destroys token `id`, using `by`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - If `by` is not the zero address, - /// it must be the owner of the token, or be approved to manage the token. - /// - /// Emits a {Transfer} event. - function _burn(address by, uint256 id) internal virtual { - address owner = ownerOf(id); - _beforeTokenTransfer(owner, address(0), id); - /// @solidity memory-safe-assembly - assembly { - // Clear the upper 96 bits. - by := shr(96, shl(96, by)) - // Load the ownership data. - mstore(0x00, id) - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let ownershipPacked := sload(ownershipSlot) - // Reload the owner in case it is changed in `_beforeTokenTransfer`. - owner := shr(96, shl(96, ownershipPacked)) - // Revert if the token does not exist. - if iszero(owner) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - // Load and check the token approval. - { - mstore(0x00, owner) - let approvedAddress := sload(add(1, ownershipSlot)) - // If `by` is not the zero address, do the authorization check. - // Revert if the `by` is not the owner, nor approved. - if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) { - if iszero(sload(keccak256(0x0c, 0x30))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Delete the approved address if any. - if approvedAddress { sstore(add(1, ownershipSlot), 0) } - } - // Clear the owner. - sstore(ownershipSlot, xor(ownershipPacked, owner)) - // Decrement the balance of `owner`. - { - let balanceSlot := keccak256(0x0c, 0x1c) - sstore(balanceSlot, sub(sload(balanceSlot), 1)) - } - // Emit the {Transfer} event. - log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id) - } - _afterTokenTransfer(owner, address(0), id); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL APPROVAL FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it. - /// - /// Requirements: - /// - Token `id` must exist. - function _isApprovedOrOwner(address account, uint256 id) - internal - view - virtual - returns (bool result) - { - /// @solidity memory-safe-assembly - assembly { - result := 1 - // Clear the upper 96 bits. - account := shr(96, shl(96, account)) - // Load the ownership data. - mstore(0x00, id) - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account)) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let owner := shr(96, shl(96, sload(ownershipSlot))) - // Revert if the token does not exist. - if iszero(owner) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - // Check if `account` is the `owner`. - if iszero(eq(account, owner)) { - mstore(0x00, owner) - // Check if `account` is approved to manage the token. - if iszero(sload(keccak256(0x0c, 0x30))) { - result := eq(account, sload(add(1, ownershipSlot))) - } - } - } - } - - /// @dev Returns the account approved to manage token `id`. - /// Returns the zero address instead of reverting if the token does not exist. - function _getApproved(uint256 id) internal view virtual returns (address result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20))))) - } - } - - /// @dev Equivalent to `_approve(address(0), account, id)`. - function _approve(address account, uint256 id) internal virtual { - _approve(address(0), account, id); - } - - /// @dev Sets `account` as the approved account to manage token `id`, using `by`. - /// - /// Requirements: - /// - Token `id` must exist. - /// - If `by` is not the zero address, `by` must be the owner - /// or an approved operator for the token owner. - /// - /// Emits a {Transfer} event. - function _approve(address by, address account, uint256 id) internal virtual { - assembly { - // Clear the upper 96 bits. - let bitmaskAddress := shr(96, not(0)) - account := and(bitmaskAddress, account) - by := and(bitmaskAddress, by) - // Load the owner of the token. - mstore(0x00, id) - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let owner := and(bitmaskAddress, sload(ownershipSlot)) - // Revert if the token does not exist. - if iszero(owner) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - // If `by` is not the zero address, do the authorization check. - // Revert if `by` is not the owner, nor approved. - if iszero(or(iszero(by), eq(by, owner))) { - mstore(0x00, owner) - if iszero(sload(keccak256(0x0c, 0x30))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Sets `account` as the approved account to manage `id`. - sstore(add(1, ownershipSlot), account) - // Emit the {Approval} event. - log4(codesize(), 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id) - } - } - - /// @dev Approve or remove the `operator` as an operator for `by`, - /// without authorization checks. - /// - /// Emits an {ApprovalForAll} event. - function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual { - /// @solidity memory-safe-assembly - assembly { - // Clear the upper 96 bits. - by := shr(96, shl(96, by)) - operator := shr(96, shl(96, operator)) - // Convert to 0 or 1. - isApproved := iszero(iszero(isApproved)) - // Update the `isApproved` for (`by`, `operator`). - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator)) - mstore(0x00, by) - sstore(keccak256(0x0c, 0x30), isApproved) - // Emit the {ApprovalForAll} event. - mstore(0x00, isApproved) - log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTERNAL TRANSFER FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to `_transfer(address(0), from, to, id)`. - function _transfer(address from, address to, uint256 id) internal virtual { - _transfer(address(0), from, to, id); - } - - /// @dev Transfers token `id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - `from` must be the owner of the token. - /// - `to` cannot be the zero address. - /// - If `by` is not the zero address, - /// it must be the owner of the token, or be approved to manage the token. - /// - /// Emits a {Transfer} event. - function _transfer(address by, address from, address to, uint256 id) internal virtual { - _beforeTokenTransfer(from, to, id); - /// @solidity memory-safe-assembly - assembly { - // Clear the upper 96 bits. - let bitmaskAddress := shr(96, not(0)) - from := and(bitmaskAddress, from) - to := and(bitmaskAddress, to) - by := and(bitmaskAddress, by) - // Load the ownership data. - mstore(0x00, id) - mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by)) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let ownershipPacked := sload(ownershipSlot) - let owner := and(bitmaskAddress, ownershipPacked) - // Revert if `from` is not the owner, or does not exist. - if iszero(mul(owner, eq(owner, from))) { - if iszero(owner) { - mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`. - revert(0x1c, 0x04) - } - mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`. - revert(0x1c, 0x04) - } - // Revert if `to` is the zero address. - if iszero(to) { - mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. - revert(0x1c, 0x04) - } - // Load, check, and update the token approval. - { - mstore(0x00, from) - let approvedAddress := sload(add(1, ownershipSlot)) - // If `by` is not the zero address, do the authorization check. - // Revert if the `by` is not the owner, nor approved. - if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) { - if iszero(sload(keccak256(0x0c, 0x30))) { - mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`. - revert(0x1c, 0x04) - } - } - // Delete the approved address if any. - if approvedAddress { sstore(add(1, ownershipSlot), 0) } - } - // Update with the new owner. - sstore(ownershipSlot, xor(ownershipPacked, xor(from, to))) - // Decrement the balance of `from`. - { - let fromBalanceSlot := keccak256(0x0c, 0x1c) - sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1)) - } - // Increment the balance of `to`. - { - mstore(0x00, to) - let toBalanceSlot := keccak256(0x0c, 0x1c) - let toBalanceSlotPacked := add(sload(toBalanceSlot), 1) - if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) { - mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`. - revert(0x1c, 0x04) - } - sstore(toBalanceSlot, toBalanceSlotPacked) - } - // Emit the {Transfer} event. - log4(codesize(), 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id) - } - _afterTokenTransfer(from, to, id); - } - - /// @dev Equivalent to `_safeTransfer(from, to, id, "")`. - function _safeTransfer(address from, address to, uint256 id) internal virtual { - _safeTransfer(from, to, id, ""); - } - - /// @dev Transfers token `id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - `from` must be the owner of the token. - /// - `to` cannot be the zero address. - /// - The caller must be the owner of the token, or be approved to manage the token. - /// - If `to` refers to a smart contract, it must implement - /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - /// - /// Emits a {Transfer} event. - function _safeTransfer(address from, address to, uint256 id, bytes memory data) - internal - virtual - { - _transfer(address(0), from, to, id); - if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); - } - - /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`. - function _safeTransfer(address by, address from, address to, uint256 id) internal virtual { - _safeTransfer(by, from, to, id, ""); - } - - /// @dev Transfers token `id` from `from` to `to`. - /// - /// Requirements: - /// - /// - Token `id` must exist. - /// - `from` must be the owner of the token. - /// - `to` cannot be the zero address. - /// - If `by` is not the zero address, - /// it must be the owner of the token, or be approved to manage the token. - /// - If `to` refers to a smart contract, it must implement - /// {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. - /// - /// Emits a {Transfer} event. - function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data) - internal - virtual - { - _transfer(by, from, to, id); - if (_hasCode(to)) _checkOnERC721Received(from, to, id, data); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HOOKS FOR OVERRIDING */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Hook that is called before any token transfers, including minting and burning. - function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {} - - /// @dev Hook that is called after any token transfers, including minting and burning. - function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {} - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns if `a` has bytecode of non-zero length. - function _hasCode(address a) private view returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := extcodesize(a) // Can handle dirty upper bits. - } - } - - /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`. - /// Reverts if the target does not support the function correctly. - function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data) - private - { - /// @solidity memory-safe-assembly - assembly { - // Prepare the calldata. - let m := mload(0x40) - let onERC721ReceivedSelector := 0x150b7a02 - mstore(m, onERC721ReceivedSelector) - mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`. - mstore(add(m, 0x40), shr(96, shl(96, from))) - mstore(add(m, 0x60), id) - mstore(add(m, 0x80), 0x80) - let n := mload(data) - mstore(add(m, 0xa0), n) - if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) } - // Revert if the call reverts. - if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) { - if returndatasize() { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - // Load the returndata and compare it. - if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) { - mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`. - revert(0x1c, 0x04) - } - } - } -} diff --git a/lib/solady/src/tokens/WETH.sol b/lib/solady/src/tokens/WETH.sol deleted file mode 100644 index a0e3841..0000000 --- a/lib/solady/src/tokens/WETH.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC20} from "./ERC20.sol"; - -/// @notice Simple Wrapped Ether implementation. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/WETH.sol) -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol) -/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) -contract WETH is ERC20 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ETH transfer has failed. - error ETHTransferFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when `amount` is deposited from `from`. - event Deposit(address indexed from, uint256 amount); - - /// @dev Emitted when `amount` is withdrawn to `to`. - event Withdrawal(address indexed to, uint256 amount); - - /// @dev `keccak256(bytes("Deposit(address,uint256)"))`. - uint256 private constant _DEPOSIT_EVENT_SIGNATURE = - 0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c; - - /// @dev `keccak256(bytes("Withdrawal(address,uint256)"))`. - uint256 private constant _WITHDRAWAL_EVENT_SIGNATURE = - 0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC20 METADATA */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the name of the token. - function name() public view virtual override returns (string memory) { - return "Wrapped Ether"; - } - - /// @dev Returns the symbol of the token. - function symbol() public view virtual override returns (string memory) { - return "WETH"; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* WETH */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deposits `amount` ETH of the caller and mints `amount` WETH to the caller. - /// - /// Emits a {Deposit} event. - function deposit() public payable virtual { - _mint(msg.sender, msg.value); - /// @solidity memory-safe-assembly - assembly { - // Emit the {Deposit} event. - mstore(0x00, callvalue()) - log2(0x00, 0x20, _DEPOSIT_EVENT_SIGNATURE, caller()) - } - } - - /// @dev Burns `amount` WETH of the caller and sends `amount` ETH to the caller. - /// - /// Emits a {Withdrawal} event. - function withdraw(uint256 amount) public virtual { - _burn(msg.sender, amount); - /// @solidity memory-safe-assembly - assembly { - // Emit the {Withdrawal} event. - mstore(0x00, amount) - log2(0x00, 0x20, _WITHDRAWAL_EVENT_SIGNATURE, caller()) - // Transfer the ETH and check if it succeeded or not. - if iszero(call(gas(), caller(), amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Equivalent to `deposit()`. - receive() external payable virtual { - deposit(); - } -} diff --git a/lib/solady/src/utils/Base64.sol b/lib/solady/src/utils/Base64.sol deleted file mode 100644 index 17754bc..0000000 --- a/lib/solady/src/utils/Base64.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library to encode strings in Base64. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Base64.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Base64.sol) -/// @author Modified from (https://github.com/Brechtpd/base64/blob/main/base64.sol) by Brecht Devos - . -library Base64 { - /// @dev Encodes `data` using the base64 encoding described in RFC 4648. - /// See: https://datatracker.ietf.org/doc/html/rfc4648 - /// @param fileSafe Whether to replace '+' with '-' and '/' with '_'. - /// @param noPadding Whether to strip away the padding. - function encode(bytes memory data, bool fileSafe, bool noPadding) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let dataLength := mload(data) - - if dataLength { - // Multiply by 4/3 rounded up. - // The `shl(2, ...)` is equivalent to multiplying by 4. - let encodedLength := shl(2, div(add(dataLength, 2), 3)) - - // Set `result` to point to the start of the free memory. - result := mload(0x40) - - // Store the table into the scratch space. - // Offsetted by -1 byte so that the `mload` will load the character. - // We will rewrite the free memory pointer at `0x40` later with - // the allocated size. - // The magic constant 0x0670 will turn "-_" into "+/". - mstore(0x1f, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef") - mstore(0x3f, xor("ghijklmnopqrstuvwxyz0123456789-_", mul(iszero(fileSafe), 0x0670))) - - // Skip the first slot, which stores the length. - let ptr := add(result, 0x20) - let end := add(ptr, encodedLength) - - // Run over the input, 3 bytes at a time. - for {} 1 {} { - data := add(data, 3) // Advance 3 bytes. - let input := mload(data) - - // Write 4 bytes. Optimized for fewer stack operations. - mstore8(0, mload(and(shr(18, input), 0x3F))) - mstore8(1, mload(and(shr(12, input), 0x3F))) - mstore8(2, mload(and(shr(6, input), 0x3F))) - mstore8(3, mload(and(input, 0x3F))) - mstore(ptr, mload(0x00)) - - ptr := add(ptr, 4) // Advance 4 bytes. - if iszero(lt(ptr, end)) { break } - } - mstore(0x40, add(end, 0x20)) // Allocate the memory. - // Equivalent to `o = [0, 2, 1][dataLength % 3]`. - let o := div(2, mod(dataLength, 3)) - // Offset `ptr` and pad with '='. We can simply write over the end. - mstore(sub(ptr, o), shl(240, 0x3d3d)) - // Set `o` to zero if there is padding. - o := mul(iszero(iszero(noPadding)), o) - mstore(sub(ptr, o), 0) // Zeroize the slot after the string. - mstore(result, sub(encodedLength, o)) // Store the length. - } - } - } - - /// @dev Encodes `data` using the base64 encoding described in RFC 4648. - /// Equivalent to `encode(data, false, false)`. - function encode(bytes memory data) internal pure returns (string memory result) { - result = encode(data, false, false); - } - - /// @dev Encodes `data` using the base64 encoding described in RFC 4648. - /// Equivalent to `encode(data, fileSafe, false)`. - function encode(bytes memory data, bool fileSafe) - internal - pure - returns (string memory result) - { - result = encode(data, fileSafe, false); - } - - /// @dev Decodes base64 encoded `data`. - /// - /// Supports: - /// - RFC 4648 (both standard and file-safe mode). - /// - RFC 3501 (63: ','). - /// - /// Does not support: - /// - Line breaks. - /// - /// Note: For performance reasons, - /// this function will NOT revert on invalid `data` inputs. - /// Outputs for invalid inputs will simply be undefined behaviour. - /// It is the user's responsibility to ensure that the `data` - /// is a valid base64 encoded string. - function decode(string memory data) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - let dataLength := mload(data) - - if dataLength { - let decodedLength := mul(shr(2, dataLength), 3) - - for {} 1 {} { - // If padded. - if iszero(and(dataLength, 3)) { - let t := xor(mload(add(data, dataLength)), 0x3d3d) - // forgefmt: disable-next-item - decodedLength := sub( - decodedLength, - add(iszero(byte(30, t)), iszero(byte(31, t))) - ) - break - } - // If non-padded. - decodedLength := add(decodedLength, sub(and(dataLength, 3), 1)) - break - } - result := mload(0x40) - - // Write the length of the bytes. - mstore(result, decodedLength) - - // Skip the first slot, which stores the length. - let ptr := add(result, 0x20) - let end := add(ptr, decodedLength) - - // Load the table into the scratch space. - // Constants are optimized for smaller bytecode with zero gas overhead. - // `m` also doubles as the mask of the upper 6 bits. - let m := 0xfc000000fc00686c7074787c8084888c9094989ca0a4a8acb0b4b8bcc0c4c8cc - mstore(0x5b, m) - mstore(0x3b, 0x04080c1014181c2024282c3034383c4044484c5054585c6064) - mstore(0x1a, 0xf8fcf800fcd0d4d8dce0e4e8ecf0f4) - - for {} 1 {} { - // Read 4 bytes. - data := add(data, 4) - let input := mload(data) - - // Write 3 bytes. - // forgefmt: disable-next-item - mstore(ptr, or( - and(m, mload(byte(28, input))), - shr(6, or( - and(m, mload(byte(29, input))), - shr(6, or( - and(m, mload(byte(30, input))), - shr(6, mload(byte(31, input))) - )) - )) - )) - ptr := add(ptr, 3) - if iszero(lt(ptr, end)) { break } - } - mstore(0x40, add(end, 0x20)) // Allocate the memory. - mstore(end, 0) // Zeroize the slot after the bytes. - mstore(0x60, 0) // Restore the zero slot. - } - } - } -} diff --git a/lib/solady/src/utils/CREATE3.sol b/lib/solady/src/utils/CREATE3.sol deleted file mode 100644 index dc8dcc8..0000000 --- a/lib/solady/src/utils/CREATE3.sol +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Deploy to deterministic addresses without an initcode factor. -/// @author Solady (https://github.com/vectorized/solmady/blob/main/src/utils/CREATE3.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) -library CREATE3 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unable to deploy the contract. - error DeploymentFailed(); - - /// @dev Unable to initialize the contract. - error InitializationFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* BYTECODE CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /** - * -------------------------------------------------------------------+ - * Opcode | Mnemonic | Stack | Memory | - * -------------------------------------------------------------------| - * 36 | CALLDATASIZE | cds | | - * 3d | RETURNDATASIZE | 0 cds | | - * 3d | RETURNDATASIZE | 0 0 cds | | - * 37 | CALLDATACOPY | | [0..cds): calldata | - * 36 | CALLDATASIZE | cds | [0..cds): calldata | - * 3d | RETURNDATASIZE | 0 cds | [0..cds): calldata | - * 34 | CALLVALUE | value 0 cds | [0..cds): calldata | - * f0 | CREATE | newContract | [0..cds): calldata | - * -------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * -------------------------------------------------------------------| - * 67 bytecode | PUSH8 bytecode | bytecode | | - * 3d | RETURNDATASIZE | 0 bytecode | | - * 52 | MSTORE | | [0..8): bytecode | - * 60 0x08 | PUSH1 0x08 | 0x08 | [0..8): bytecode | - * 60 0x18 | PUSH1 0x18 | 0x18 0x08 | [0..8): bytecode | - * f3 | RETURN | | [0..8): bytecode | - * -------------------------------------------------------------------+ - */ - - /// @dev The proxy bytecode. - uint256 private constant _PROXY_BYTECODE = 0x67363d3d37363d34f03d5260086018f3; - - /// @dev Hash of the `_PROXY_BYTECODE`. - /// Equivalent to `keccak256(abi.encodePacked(hex"67363d3d37363d34f03d5260086018f3"))`. - bytes32 private constant _PROXY_BYTECODE_HASH = - 0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CREATE3 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deploys `creationCode` deterministically with a `salt`. - /// The deployed contract is funded with `value` (in wei) ETH. - /// Returns the deterministic address of the deployed contract, - /// which solely depends on `salt`. - function deploy(bytes32 salt, bytes memory creationCode, uint256 value) - internal - returns (address deployed) - { - /// @solidity memory-safe-assembly - assembly { - // Store the `_PROXY_BYTECODE` into scratch space. - mstore(0x00, _PROXY_BYTECODE) - // Deploy a new contract with our pre-made bytecode via CREATE2. - let proxy := create2(0, 0x10, 0x10, salt) - - // If the result of `create2` is the zero address, revert. - if iszero(proxy) { - // Store the function selector of `DeploymentFailed()`. - mstore(0x00, 0x30116425) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Store the proxy's address. - mstore(0x14, proxy) - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01). - // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex). - mstore(0x00, 0xd694) - // Nonce of the proxy contract (1). - mstore8(0x34, 0x01) - - deployed := keccak256(0x1e, 0x17) - - // If the `call` fails, revert. - if iszero( - call( - gas(), // Gas remaining. - proxy, // Proxy's address. - value, // Ether value. - add(creationCode, 0x20), // Start of `creationCode`. - mload(creationCode), // Length of `creationCode`. - 0x00, // Offset of output. - 0x00 // Length of output. - ) - ) { - // Store the function selector of `InitializationFailed()`. - mstore(0x00, 0x19b991a8) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // If the code size of `deployed` is zero, revert. - if iszero(extcodesize(deployed)) { - // Store the function selector of `InitializationFailed()`. - mstore(0x00, 0x19b991a8) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - } - } - - /// @dev Returns the deterministic address for `salt` with `deployer`. - function getDeployed(bytes32 salt, address deployer) internal pure returns (address deployed) { - /// @solidity memory-safe-assembly - assembly { - // Cache the free memory pointer. - let m := mload(0x40) - // Store `deployer`. - mstore(0x00, deployer) - // Store the prefix. - mstore8(0x0b, 0xff) - // Store the salt. - mstore(0x20, salt) - // Store the bytecode hash. - mstore(0x40, _PROXY_BYTECODE_HASH) - - // Store the proxy's address. - mstore(0x14, keccak256(0x0b, 0x55)) - // Restore the free memory pointer. - mstore(0x40, m) - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01). - // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex). - mstore(0x00, 0xd694) - // Nonce of the proxy contract (1). - mstore8(0x34, 0x01) - - deployed := keccak256(0x1e, 0x17) - } - } - - /// @dev Returns the deterministic address for `salt`. - function getDeployed(bytes32 salt) internal view returns (address deployed) { - deployed = getDeployed(salt, address(this)); - } -} diff --git a/lib/solady/src/utils/Clone.sol b/lib/solady/src/utils/Clone.sol deleted file mode 100644 index 697a1ea..0000000 --- a/lib/solady/src/utils/Clone.sol +++ /dev/null @@ -1,387 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Class with helper read functions for clone with immutable args. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol) -/// @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie -/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) -abstract contract Clone { - /// @dev Reads all of the immutable args. - function _getArgBytes() internal pure returns (bytes memory arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := mload(0x40) - let length := sub(calldatasize(), add(2, offset)) // 2 bytes are used for the length. - mstore(arg, length) // Store the length. - calldatacopy(add(arg, 0x20), offset, length) - let o := add(add(arg, 0x20), length) - mstore(o, 0) // Zeroize the slot after the bytes. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - - /// @dev Reads an immutable arg with type bytes. - function _getArgBytes(uint256 argOffset, uint256 length) - internal - pure - returns (bytes memory arg) - { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := mload(0x40) - mstore(arg, length) // Store the length. - calldatacopy(add(arg, 0x20), add(offset, argOffset), length) - let o := add(add(arg, 0x20), length) - mstore(o, 0) // Zeroize the slot after the bytes. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - - /// @dev Reads an immutable arg with type address. - function _getArgAddress(uint256 argOffset) internal pure returns (address arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(96, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads a uint256 array stored in the immutable args. - function _getArgUint256Array(uint256 argOffset, uint256 length) - internal - pure - returns (uint256[] memory arg) - { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := mload(0x40) - mstore(arg, length) // Store the length. - calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length)) - mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory. - } - } - - /// @dev Reads a bytes32 array stored in the immutable args. - function _getArgBytes32Array(uint256 argOffset, uint256 length) - internal - pure - returns (bytes32[] memory arg) - { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := mload(0x40) - mstore(arg, length) // Store the length. - calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length)) - mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory. - } - } - - /// @dev Reads an immutable arg with type bytes32. - function _getArgBytes32(uint256 argOffset) internal pure returns (bytes32 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := calldataload(add(offset, argOffset)) - } - } - - /// @dev Reads an immutable arg with type uint256. - function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := calldataload(add(offset, argOffset)) - } - } - - /// @dev Reads an immutable arg with type uint248. - function _getArgUint248(uint256 argOffset) internal pure returns (uint248 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(8, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint240. - function _getArgUint240(uint256 argOffset) internal pure returns (uint240 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(16, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint232. - function _getArgUint232(uint256 argOffset) internal pure returns (uint232 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(24, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint224. - function _getArgUint224(uint256 argOffset) internal pure returns (uint224 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(0x20, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint216. - function _getArgUint216(uint256 argOffset) internal pure returns (uint216 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(40, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint208. - function _getArgUint208(uint256 argOffset) internal pure returns (uint208 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(48, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint200. - function _getArgUint200(uint256 argOffset) internal pure returns (uint200 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(56, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint192. - function _getArgUint192(uint256 argOffset) internal pure returns (uint192 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(64, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint184. - function _getArgUint184(uint256 argOffset) internal pure returns (uint184 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(72, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint176. - function _getArgUint176(uint256 argOffset) internal pure returns (uint176 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(80, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint168. - function _getArgUint168(uint256 argOffset) internal pure returns (uint168 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(88, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint160. - function _getArgUint160(uint256 argOffset) internal pure returns (uint160 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(96, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint152. - function _getArgUint152(uint256 argOffset) internal pure returns (uint152 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(104, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint144. - function _getArgUint144(uint256 argOffset) internal pure returns (uint144 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(112, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint136. - function _getArgUint136(uint256 argOffset) internal pure returns (uint136 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(120, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint128. - function _getArgUint128(uint256 argOffset) internal pure returns (uint128 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(128, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint120. - function _getArgUint120(uint256 argOffset) internal pure returns (uint120 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(136, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint112. - function _getArgUint112(uint256 argOffset) internal pure returns (uint112 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(144, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint104. - function _getArgUint104(uint256 argOffset) internal pure returns (uint104 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(152, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint96. - function _getArgUint96(uint256 argOffset) internal pure returns (uint96 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(160, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint88. - function _getArgUint88(uint256 argOffset) internal pure returns (uint88 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(168, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint80. - function _getArgUint80(uint256 argOffset) internal pure returns (uint80 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(176, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint72. - function _getArgUint72(uint256 argOffset) internal pure returns (uint72 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(184, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint64. - function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(192, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint56. - function _getArgUint56(uint256 argOffset) internal pure returns (uint56 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(200, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint48. - function _getArgUint48(uint256 argOffset) internal pure returns (uint48 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(208, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint40. - function _getArgUint40(uint256 argOffset) internal pure returns (uint40 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(216, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint32. - function _getArgUint32(uint256 argOffset) internal pure returns (uint32 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(224, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint24. - function _getArgUint24(uint256 argOffset) internal pure returns (uint24 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(232, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint16. - function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(240, calldataload(add(offset, argOffset))) - } - } - - /// @dev Reads an immutable arg with type uint8. - function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) { - uint256 offset = _getImmutableArgsOffset(); - /// @solidity memory-safe-assembly - assembly { - arg := shr(248, calldataload(add(offset, argOffset))) - } - } - - /// @return offset The offset of the packed immutable args in calldata. - function _getImmutableArgsOffset() internal pure returns (uint256 offset) { - /// @solidity memory-safe-assembly - assembly { - offset := sub(calldatasize(), shr(240, calldataload(sub(calldatasize(), 2)))) - } - } -} diff --git a/lib/solady/src/utils/DateTimeLib.sol b/lib/solady/src/utils/DateTimeLib.sol deleted file mode 100644 index 005ba76..0000000 --- a/lib/solady/src/utils/DateTimeLib.sol +++ /dev/null @@ -1,516 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for date time operations. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DateTimeLib.sol) -/// -/// Conventions: -/// --------------------------------------------------------------------+ -/// Unit | Range | Notes | -/// --------------------------------------------------------------------| -/// timestamp | 0..0x1e18549868c76ff | Unix timestamp. | -/// epochDay | 0..0x16d3e098039 | Days since 1970-01-01. | -/// year | 1970..0xffffffff | Gregorian calendar year. | -/// month | 1..12 | Gregorian calendar month. | -/// day | 1..31 | Gregorian calendar day of month. | -/// weekday | 1..7 | The day of the week (1-indexed). | -/// --------------------------------------------------------------------+ -/// All timestamps of days are rounded down to 00:00:00 UTC. -library DateTimeLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Weekdays are 1-indexed for a traditional rustic feel. - - // "And on the seventh day God finished his work that he had done, - // and he rested on the seventh day from all his work that he had done." - // -- Genesis 2:2 - - uint256 internal constant MON = 1; - uint256 internal constant TUE = 2; - uint256 internal constant WED = 3; - uint256 internal constant THU = 4; - uint256 internal constant FRI = 5; - uint256 internal constant SAT = 6; - uint256 internal constant SUN = 7; - - // Months and days of months are 1-indexed for ease of use. - - uint256 internal constant JAN = 1; - uint256 internal constant FEB = 2; - uint256 internal constant MAR = 3; - uint256 internal constant APR = 4; - uint256 internal constant MAY = 5; - uint256 internal constant JUN = 6; - uint256 internal constant JUL = 7; - uint256 internal constant AUG = 8; - uint256 internal constant SEP = 9; - uint256 internal constant OCT = 10; - uint256 internal constant NOV = 11; - uint256 internal constant DEC = 12; - - // These limits are large enough for most practical purposes. - // Inputs that exceed these limits result in undefined behavior. - - uint256 internal constant MAX_SUPPORTED_YEAR = 0xffffffff; - uint256 internal constant MAX_SUPPORTED_EPOCH_DAY = 0x16d3e098039; - uint256 internal constant MAX_SUPPORTED_TIMESTAMP = 0x1e18549868c76ff; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DATE TIME OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the number of days since 1970-01-01 from (`year`,`month`,`day`). - /// See: https://howardhinnant.github.io/date_algorithms.html - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedDate} to check if the inputs are supported. - function dateToEpochDay(uint256 year, uint256 month, uint256 day) - internal - pure - returns (uint256 epochDay) - { - /// @solidity memory-safe-assembly - assembly { - year := sub(year, lt(month, 3)) - let doy := add(shr(11, add(mul(62719, mod(add(month, 9), 12)), 769)), day) - let yoe := mod(year, 400) - let doe := sub(add(add(mul(yoe, 365), shr(2, yoe)), doy), div(yoe, 100)) - epochDay := sub(add(mul(div(year, 400), 146097), doe), 719469) - } - } - - /// @dev Returns (`year`,`month`,`day`) from the number of days since 1970-01-01. - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedDays} to check if the inputs is supported. - function epochDayToDate(uint256 epochDay) - internal - pure - returns (uint256 year, uint256 month, uint256 day) - { - /// @solidity memory-safe-assembly - assembly { - epochDay := add(epochDay, 719468) - let doe := mod(epochDay, 146097) - let yoe := - div(sub(sub(add(doe, div(doe, 36524)), div(doe, 1460)), eq(doe, 146096)), 365) - let doy := sub(doe, sub(add(mul(365, yoe), shr(2, yoe)), div(yoe, 100))) - let mp := div(add(mul(5, doy), 2), 153) - day := add(sub(doy, shr(11, add(mul(mp, 62719), 769))), 1) - month := sub(add(mp, 3), mul(gt(mp, 9), 12)) - year := add(add(yoe, mul(div(epochDay, 146097), 400)), lt(month, 3)) - } - } - - /// @dev Returns the unix timestamp from (`year`,`month`,`day`). - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedDate} to check if the inputs are supported. - function dateToTimestamp(uint256 year, uint256 month, uint256 day) - internal - pure - returns (uint256 result) - { - unchecked { - result = dateToEpochDay(year, month, day) * 86400; - } - } - - /// @dev Returns (`year`,`month`,`day`) from the given unix timestamp. - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedTimestamp} to check if the inputs are supported. - function timestampToDate(uint256 timestamp) - internal - pure - returns (uint256 year, uint256 month, uint256 day) - { - (year, month, day) = epochDayToDate(timestamp / 86400); - } - - /// @dev Returns the unix timestamp from - /// (`year`,`month`,`day`,`hour`,`minute`,`second`). - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedDateTime} to check if the inputs are supported. - function dateTimeToTimestamp( - uint256 year, - uint256 month, - uint256 day, - uint256 hour, - uint256 minute, - uint256 second - ) internal pure returns (uint256 result) { - unchecked { - result = dateToEpochDay(year, month, day) * 86400 + hour * 3600 + minute * 60 + second; - } - } - - /// @dev Returns (`year`,`month`,`day`,`hour`,`minute`,`second`) - /// from the given unix timestamp. - /// Note: Inputs outside the supported ranges result in undefined behavior. - /// Use {isSupportedTimestamp} to check if the inputs are supported. - function timestampToDateTime(uint256 timestamp) - internal - pure - returns ( - uint256 year, - uint256 month, - uint256 day, - uint256 hour, - uint256 minute, - uint256 second - ) - { - unchecked { - (year, month, day) = epochDayToDate(timestamp / 86400); - uint256 secs = timestamp % 86400; - hour = secs / 3600; - secs = secs % 3600; - minute = secs / 60; - second = secs % 60; - } - } - - /// @dev Returns if the `year` is leap. - function isLeapYear(uint256 year) internal pure returns (bool leap) { - /// @solidity memory-safe-assembly - assembly { - leap := iszero(and(add(mul(iszero(mod(year, 25)), 12), 3), year)) - } - } - - /// @dev Returns number of days in given `month` of `year`. - function daysInMonth(uint256 year, uint256 month) internal pure returns (uint256 result) { - bool flag = isLeapYear(year); - /// @solidity memory-safe-assembly - assembly { - // `daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31]`. - // `result = daysInMonths[month - 1] + isLeapYear(year)`. - result := - add(byte(month, shl(152, 0x1F1C1F1E1F1E1F1F1E1F1E1F)), and(eq(month, 2), flag)) - } - } - - /// @dev Returns the weekday from the unix timestamp. - /// Monday: 1, Tuesday: 2, ....., Sunday: 7. - function weekday(uint256 timestamp) internal pure returns (uint256 result) { - unchecked { - result = ((timestamp / 86400 + 3) % 7) + 1; - } - } - - /// @dev Returns if (`year`,`month`,`day`) is a supported date. - /// - `1970 <= year <= MAX_SUPPORTED_YEAR`. - /// - `1 <= month <= 12`. - /// - `1 <= day <= daysInMonth(year, month)`. - function isSupportedDate(uint256 year, uint256 month, uint256 day) - internal - pure - returns (bool result) - { - uint256 md = daysInMonth(year, month); - /// @solidity memory-safe-assembly - assembly { - let w := not(0) - result := - and( - lt(sub(year, 1970), sub(MAX_SUPPORTED_YEAR, 1969)), - and(lt(add(month, w), 12), lt(add(day, w), md)) - ) - } - } - - /// @dev Returns if (`year`,`month`,`day`,`hour`,`minute`,`second`) is a supported date time. - /// - `1970 <= year <= MAX_SUPPORTED_YEAR`. - /// - `1 <= month <= 12`. - /// - `1 <= day <= daysInMonth(year, month)`. - /// - `hour < 24`. - /// - `minute < 60`. - /// - `second < 60`. - function isSupportedDateTime( - uint256 year, - uint256 month, - uint256 day, - uint256 hour, - uint256 minute, - uint256 second - ) internal pure returns (bool result) { - if (isSupportedDate(year, month, day)) { - /// @solidity memory-safe-assembly - assembly { - result := and(lt(hour, 24), and(lt(minute, 60), lt(second, 60))) - } - } - } - - /// @dev Returns if `epochDay` is a supported unix epoch day. - function isSupportedEpochDay(uint256 epochDay) internal pure returns (bool result) { - unchecked { - result = epochDay < MAX_SUPPORTED_EPOCH_DAY + 1; - } - } - - /// @dev Returns if `timestamp` is a supported unix timestamp. - function isSupportedTimestamp(uint256 timestamp) internal pure returns (bool result) { - unchecked { - result = timestamp < MAX_SUPPORTED_TIMESTAMP + 1; - } - } - - /// @dev Returns the unix timestamp of the given `n`th weekday `wd`, in `month` of `year`. - /// Example: 3rd Friday of Feb 2022 is `nthWeekdayInMonthOfYearTimestamp(2022, 2, 3, 5)` - /// Note: `n` is 1-indexed for traditional consistency. - /// Invalid weekdays (i.e. `wd == 0 || wd > 7`) result in undefined behavior. - function nthWeekdayInMonthOfYearTimestamp(uint256 year, uint256 month, uint256 n, uint256 wd) - internal - pure - returns (uint256 result) - { - uint256 d = dateToEpochDay(year, month, 1); - uint256 md = daysInMonth(year, month); - /// @solidity memory-safe-assembly - assembly { - let diff := sub(wd, add(mod(add(d, 3), 7), 1)) - let date := add(mul(sub(n, 1), 7), add(mul(gt(diff, 6), 7), diff)) - result := mul(mul(86400, add(date, d)), and(lt(date, md), iszero(iszero(n)))) - } - } - - /// @dev Returns the unix timestamp of the most recent Monday. - function mondayTimestamp(uint256 timestamp) internal pure returns (uint256 result) { - uint256 t = timestamp; - /// @solidity memory-safe-assembly - assembly { - let day := div(t, 86400) - result := mul(mul(sub(day, mod(add(day, 3), 7)), 86400), gt(t, 345599)) - } - } - - /// @dev Returns whether the unix timestamp falls on a Saturday or Sunday. - /// To check whether it is a week day, just take the negation of the result. - function isWeekEnd(uint256 timestamp) internal pure returns (bool result) { - result = weekday(timestamp) > FRI; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DATE TIME ARITHMETIC OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Adds `numYears` to the unix timestamp, and returns the result. - /// Note: The result will share the same Gregorian calendar month, - /// but different Gregorian calendar years for non-zero `numYears`. - /// If the Gregorian calendar month of the result has less days - /// than the Gregorian calendar month day of the `timestamp`, - /// the result's month day will be the maximum possible value for the month. - /// (e.g. from 29th Feb to 28th Feb) - function addYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) { - (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400); - result = _offsetted(year + numYears, month, day, timestamp); - } - - /// @dev Adds `numMonths` to the unix timestamp, and returns the result. - /// Note: If the Gregorian calendar month of the result has less days - /// than the Gregorian calendar month day of the `timestamp`, - /// the result's month day will be the maximum possible value for the month. - /// (e.g. from 29th Feb to 28th Feb) - function addMonths(uint256 timestamp, uint256 numMonths) - internal - pure - returns (uint256 result) - { - (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400); - month = _sub(month + numMonths, 1); - result = _offsetted(year + month / 12, _add(month % 12, 1), day, timestamp); - } - - /// @dev Adds `numDays` to the unix timestamp, and returns the result. - function addDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) { - result = timestamp + numDays * 86400; - } - - /// @dev Adds `numHours` to the unix timestamp, and returns the result. - function addHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) { - result = timestamp + numHours * 3600; - } - - /// @dev Adds `numMinutes` to the unix timestamp, and returns the result. - function addMinutes(uint256 timestamp, uint256 numMinutes) - internal - pure - returns (uint256 result) - { - result = timestamp + numMinutes * 60; - } - - /// @dev Adds `numSeconds` to the unix timestamp, and returns the result. - function addSeconds(uint256 timestamp, uint256 numSeconds) - internal - pure - returns (uint256 result) - { - result = timestamp + numSeconds; - } - - /// @dev Subtracts `numYears` from the unix timestamp, and returns the result. - /// Note: The result will share the same Gregorian calendar month, - /// but different Gregorian calendar years for non-zero `numYears`. - /// If the Gregorian calendar month of the result has less days - /// than the Gregorian calendar month day of the `timestamp`, - /// the result's month day will be the maximum possible value for the month. - /// (e.g. from 29th Feb to 28th Feb) - function subYears(uint256 timestamp, uint256 numYears) internal pure returns (uint256 result) { - (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400); - result = _offsetted(year - numYears, month, day, timestamp); - } - - /// @dev Subtracts `numYears` from the unix timestamp, and returns the result. - /// Note: If the Gregorian calendar month of the result has less days - /// than the Gregorian calendar month day of the `timestamp`, - /// the result's month day will be the maximum possible value for the month. - /// (e.g. from 29th Feb to 28th Feb) - function subMonths(uint256 timestamp, uint256 numMonths) - internal - pure - returns (uint256 result) - { - (uint256 year, uint256 month, uint256 day) = epochDayToDate(timestamp / 86400); - uint256 yearMonth = _totalMonths(year, month) - _add(numMonths, 1); - result = _offsetted(yearMonth / 12, _add(yearMonth % 12, 1), day, timestamp); - } - - /// @dev Subtracts `numDays` from the unix timestamp, and returns the result. - function subDays(uint256 timestamp, uint256 numDays) internal pure returns (uint256 result) { - result = timestamp - numDays * 86400; - } - - /// @dev Subtracts `numHours` from the unix timestamp, and returns the result. - function subHours(uint256 timestamp, uint256 numHours) internal pure returns (uint256 result) { - result = timestamp - numHours * 3600; - } - - /// @dev Subtracts `numMinutes` from the unix timestamp, and returns the result. - function subMinutes(uint256 timestamp, uint256 numMinutes) - internal - pure - returns (uint256 result) - { - result = timestamp - numMinutes * 60; - } - - /// @dev Subtracts `numSeconds` from the unix timestamp, and returns the result. - function subSeconds(uint256 timestamp, uint256 numSeconds) - internal - pure - returns (uint256 result) - { - result = timestamp - numSeconds; - } - - /// @dev Returns the difference in Gregorian calendar years - /// between `fromTimestamp` and `toTimestamp`. - /// Note: Even if the true time difference is less than a year, - /// the difference can be non-zero is the timestamps are - /// from different Gregorian calendar years - function diffYears(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - toTimestamp - fromTimestamp; - (uint256 fromYear,,) = epochDayToDate(fromTimestamp / 86400); - (uint256 toYear,,) = epochDayToDate(toTimestamp / 86400); - result = _sub(toYear, fromYear); - } - - /// @dev Returns the difference in Gregorian calendar months - /// between `fromTimestamp` and `toTimestamp`. - /// Note: Even if the true time difference is less than a month, - /// the difference can be non-zero is the timestamps are - /// from different Gregorian calendar months. - function diffMonths(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - toTimestamp - fromTimestamp; - (uint256 fromYear, uint256 fromMonth,) = epochDayToDate(fromTimestamp / 86400); - (uint256 toYear, uint256 toMonth,) = epochDayToDate(toTimestamp / 86400); - result = _sub(_totalMonths(toYear, toMonth), _totalMonths(fromYear, fromMonth)); - } - - /// @dev Returns the difference in days between `fromTimestamp` and `toTimestamp`. - function diffDays(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - result = (toTimestamp - fromTimestamp) / 86400; - } - - /// @dev Returns the difference in hours between `fromTimestamp` and `toTimestamp`. - function diffHours(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - result = (toTimestamp - fromTimestamp) / 3600; - } - - /// @dev Returns the difference in minutes between `fromTimestamp` and `toTimestamp`. - function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - result = (toTimestamp - fromTimestamp) / 60; - } - - /// @dev Returns the difference in seconds between `fromTimestamp` and `toTimestamp`. - function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp) - internal - pure - returns (uint256 result) - { - result = toTimestamp - fromTimestamp; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unchecked arithmetic for computing the total number of months. - function _totalMonths(uint256 numYears, uint256 numMonths) - private - pure - returns (uint256 total) - { - unchecked { - total = numYears * 12 + numMonths; - } - } - - /// @dev Unchecked arithmetic for adding two numbers. - function _add(uint256 a, uint256 b) private pure returns (uint256 c) { - unchecked { - c = a + b; - } - } - - /// @dev Unchecked arithmetic for subtracting two numbers. - function _sub(uint256 a, uint256 b) private pure returns (uint256 c) { - unchecked { - c = a - b; - } - } - - /// @dev Returns the offsetted timestamp. - function _offsetted(uint256 year, uint256 month, uint256 day, uint256 timestamp) - private - pure - returns (uint256 result) - { - uint256 dm = daysInMonth(year, month); - if (day >= dm) { - day = dm; - } - result = dateToEpochDay(year, month, day) * 86400 + (timestamp % 86400); - } -} diff --git a/lib/solady/src/utils/DynamicBufferLib.sol b/lib/solady/src/utils/DynamicBufferLib.sol deleted file mode 100644 index b8b94d2..0000000 --- a/lib/solady/src/utils/DynamicBufferLib.sol +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for buffers with automatic capacity resizing. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/DynamicBuffer.sol) -/// @author Modified from cozyco (https://github.com/samkingco/cozyco/blob/main/contracts/utils/DynamicBuffer.sol) -library DynamicBufferLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Type to represent a dynamic buffer in memory. - /// You can directly assign to `data`, and the `p` function will - /// take care of the memory allocation. - struct DynamicBuffer { - bytes data; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Reserves at least `minimum` amount of contiguous memory. - function reserve(DynamicBuffer memory buffer, uint256 minimum) - internal - pure - returns (DynamicBuffer memory result) - { - _deallocate(result); - result = buffer; - uint256 n = buffer.data.length; - if (minimum > n) { - uint256 i = 0x40; - do {} while ((i <<= 1) < minimum); - bytes memory data; - /// @solidity memory-safe-assembly - assembly { - data := 0x00 - mstore(data, sub(i, n)) - } - result = p(result, data); - } - } - - /// @dev Clears the buffer without deallocating the memory. - function clear(DynamicBuffer memory buffer) - internal - pure - returns (DynamicBuffer memory result) - { - _deallocate(result); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(buffer), 0) - } - result = buffer; - } - - /// @dev Returns a string pointing to the underlying bytes data. - /// Note: The string WILL change if the buffer is updated. - function s(DynamicBuffer memory buffer) internal pure returns (string memory) { - return string(buffer.data); - } - - /// @dev Appends `data` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p(DynamicBuffer memory buffer, bytes memory data) - internal - pure - returns (DynamicBuffer memory result) - { - _deallocate(result); - result = buffer; - if (data.length == 0) return result; - /// @solidity memory-safe-assembly - assembly { - let w := not(0x1f) - let bufData := mload(buffer) - let bufDataLen := mload(bufData) - let newBufDataLen := add(mload(data), bufDataLen) - // Some random prime number to multiply `cap`, so that - // we know that the `cap` is for a dynamic buffer. - // Selected to be larger than any memory pointer realistically. - let prime := 1621250193422201 - let cap := mload(add(bufData, w)) // `mload(sub(bufData, 0x20))`. - // Extract `cap`, initializing it to zero if it is not a multiple of `prime`. - cap := mul(div(cap, prime), iszero(mod(cap, prime))) - - // Expand / Reallocate memory if required. - // Note that we need to allocate an extra word for the length, and - // and another extra word as a safety word (giving a total of 0x40 bytes). - // Without the safety word, the backwards copying can cause a buffer overflow. - for {} iszero(lt(newBufDataLen, cap)) {} { - // Approximately more than double the capacity to ensure more than enough space. - let newCap := and(add(cap, add(or(cap, newBufDataLen), 0x20)), w) - // If the memory is contiguous, we can simply expand it. - if iszero(or(xor(mload(0x40), add(bufData, add(0x40, cap))), eq(bufData, 0x60))) { - // Store `cap * prime` in the word before the length. - mstore(add(bufData, w), mul(prime, newCap)) - mstore(0x40, add(bufData, add(0x40, newCap))) // Expand the memory allocation. - break - } - // Set the `newBufData` to point to the word after `cap`. - let newBufData := add(mload(0x40), 0x20) - mstore(0x40, add(newBufData, add(0x40, newCap))) // Reallocate the memory. - mstore(buffer, newBufData) // Store the `newBufData`. - // Copy `bufData` one word at a time, backwards. - for { let o := and(add(bufDataLen, 0x20), w) } 1 {} { - mstore(add(newBufData, o), mload(add(bufData, o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - // Store `cap * prime` in the word before the length. - mstore(add(newBufData, w), mul(prime, newCap)) - bufData := newBufData // Assign `newBufData` to `bufData`. - break - } - // If it's a reserve operation, set the variables to skip the appending. - if iszero(data) { - mstore(data, data) - newBufDataLen := bufDataLen - } - // Copy `data` one word at a time, backwards. - for { let o := and(add(mload(data), 0x20), w) } 1 {} { - mstore(add(add(bufData, bufDataLen), o), mload(add(data, o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - mstore(add(add(bufData, 0x20), newBufDataLen), 0) // Zeroize the word after the buffer. - mstore(bufData, newBufDataLen) // Store the length. - } - } - - /// @dev Appends `data0`, `data1` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p(DynamicBuffer memory buffer, bytes memory data0, bytes memory data1) - internal - pure - returns (DynamicBuffer memory result) - { - _deallocate(result); - result = p(p(buffer, data0), data1); - } - - /// @dev Appends `data0` .. `data2` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p( - DynamicBuffer memory buffer, - bytes memory data0, - bytes memory data1, - bytes memory data2 - ) internal pure returns (DynamicBuffer memory result) { - _deallocate(result); - result = p(p(p(buffer, data0), data1), data2); - } - - /// @dev Appends `data0` .. `data3` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p( - DynamicBuffer memory buffer, - bytes memory data0, - bytes memory data1, - bytes memory data2, - bytes memory data3 - ) internal pure returns (DynamicBuffer memory result) { - _deallocate(result); - result = p(p(p(p(buffer, data0), data1), data2), data3); - } - - /// @dev Appends `data0` .. `data4` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p( - DynamicBuffer memory buffer, - bytes memory data0, - bytes memory data1, - bytes memory data2, - bytes memory data3, - bytes memory data4 - ) internal pure returns (DynamicBuffer memory result) { - _deallocate(result); - result = p(p(p(p(p(buffer, data0), data1), data2), data3), data4); - } - - /// @dev Appends `data0` .. `data5` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p( - DynamicBuffer memory buffer, - bytes memory data0, - bytes memory data1, - bytes memory data2, - bytes memory data3, - bytes memory data4, - bytes memory data5 - ) internal pure returns (DynamicBuffer memory result) { - _deallocate(result); - result = p(p(p(p(p(p(buffer, data0), data1), data2), data3), data4), data5); - } - - /// @dev Appends `data0` .. `data6` to `buffer`. - /// Returns the same buffer, so that it can be used for function chaining. - function p( - DynamicBuffer memory buffer, - bytes memory data0, - bytes memory data1, - bytes memory data2, - bytes memory data3, - bytes memory data4, - bytes memory data5, - bytes memory data6 - ) internal pure returns (DynamicBuffer memory result) { - _deallocate(result); - result = p(p(p(p(p(p(p(buffer, data0), data1), data2), data3), data4), data5), data6); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Helper for deallocating a automatically allocated `buffer` pointer. - function _deallocate(DynamicBuffer memory result) private pure { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, result) // Deallocate, as we have already allocated. - } - } -} diff --git a/lib/solady/src/utils/ECDSA.sol b/lib/solady/src/utils/ECDSA.sol deleted file mode 100644 index 9462498..0000000 --- a/lib/solady/src/utils/ECDSA.sol +++ /dev/null @@ -1,411 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Gas optimized ECDSA wrapper. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol) -/// -/// @dev Note: -/// - The recovery functions use the ecrecover precompile (0x1). -/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure. -/// This is for more safety by default. -/// Use the `tryRecover` variants if you need to get the zero address back -/// upon recovery failure instead. -/// - As of Solady version 0.0.134, all `bytes signature` variants accept both -/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. -/// See: https://eips.ethereum.org/EIPS/eip-2098 -/// This is for calldata efficiency on smart accounts prevalent on L2s. -/// -/// WARNING! Do NOT use signatures as unique identifiers: -/// - Use a nonce in the digest to prevent replay attacks on the same contract. -/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. -/// EIP-712 also enables readable signing of typed data for better user safety. -/// This implementation does NOT check if a signature is non-malleable. -library ECDSA { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The signature is invalid. - error InvalidSignature(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* RECOVERY OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. - function recover(bytes32 hash, bytes memory signature) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - let m := mload(0x40) // Cache the free memory pointer. - for {} 1 {} { - mstore(0x00, hash) - mstore(0x40, mload(add(signature, 0x20))) // `r`. - if eq(mload(signature), 64) { - let vs := mload(add(signature, 0x40)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - break - } - if eq(mload(signature), 65) { - mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. - mstore(0x60, mload(add(signature, 0x40))) // `s`. - break - } - result := 0 - break - } - result := - mload( - staticcall( - gas(), // Amount of gas left for the transaction. - result, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(returndatasize()) { - mstore(0x00, 0x8baa579f) // `InvalidSignature()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. - function recoverCalldata(bytes32 hash, bytes calldata signature) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - result := 1 - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - for {} 1 {} { - if eq(signature.length, 64) { - let vs := calldataload(add(signature.offset, 0x20)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, calldataload(signature.offset)) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - break - } - if eq(signature.length, 65) { - mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. - calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. - break - } - result := 0 - break - } - result := - mload( - staticcall( - gas(), // Amount of gas left for the transaction. - result, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(returndatasize()) { - mstore(0x00, 0x8baa579f) // `InvalidSignature()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, - /// and the EIP-2098 short form signature defined by `r` and `vs`. - function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, r) - mstore(0x60, shr(1, shl(1, vs))) // `s`. - result := - mload( - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(returndatasize()) { - mstore(0x00, 0x8baa579f) // `InvalidSignature()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, - /// and the signature defined by `v`, `r`, `s`. - function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - mstore(0x20, and(v, 0xff)) - mstore(0x40, r) - mstore(0x60, s) - result := - mload( - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(returndatasize()) { - mstore(0x00, 0x8baa579f) // `InvalidSignature()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* TRY-RECOVER OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // WARNING! - // These functions will NOT revert upon recovery failure. - // Instead, they will return the zero address upon recovery failure. - // It is critical that the returned address is NEVER compared against - // a zero address (e.g. an uninitialized address variable). - - /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. - function tryRecover(bytes32 hash, bytes memory signature) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - result := 1 - let m := mload(0x40) // Cache the free memory pointer. - for {} 1 {} { - mstore(0x00, hash) - mstore(0x40, mload(add(signature, 0x20))) // `r`. - if eq(mload(signature), 64) { - let vs := mload(add(signature, 0x40)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - break - } - if eq(mload(signature), 65) { - mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. - mstore(0x60, mload(add(signature, 0x40))) // `s`. - break - } - result := 0 - break - } - pop( - staticcall( - gas(), // Amount of gas left for the transaction. - result, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x40, // Start of output. - 0x20 // Size of output. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - result := mload(xor(0x60, returndatasize())) - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, and the `signature`. - function tryRecoverCalldata(bytes32 hash, bytes calldata signature) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - result := 1 - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - for {} 1 {} { - if eq(signature.length, 64) { - let vs := calldataload(add(signature.offset, 0x20)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, calldataload(signature.offset)) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - break - } - if eq(signature.length, 65) { - mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. - calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`. - break - } - result := 0 - break - } - pop( - staticcall( - gas(), // Amount of gas left for the transaction. - result, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x40, // Start of output. - 0x20 // Size of output. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - result := mload(xor(0x60, returndatasize())) - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, - /// and the EIP-2098 short form signature defined by `r` and `vs`. - function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, r) - mstore(0x60, shr(1, shl(1, vs))) // `s`. - pop( - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x40, // Start of output. - 0x20 // Size of output. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - result := mload(xor(0x60, returndatasize())) - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Recovers the signer's address from a message digest `hash`, - /// and the signature defined by `v`, `r`, `s`. - function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (address result) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x00, hash) - mstore(0x20, and(v, 0xff)) - mstore(0x40, r) - mstore(0x60, s) - pop( - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x40, // Start of output. - 0x20 // Size of output. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - result := mload(xor(0x60, returndatasize())) - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HASHING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an Ethereum Signed Message, created from a `hash`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, hash) // Store into scratch space for keccak256. - mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. - result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. - } - } - - /// @dev Returns an Ethereum Signed Message, created from `s`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - /// Note: Supports lengths of `s` up to 999999 bytes. - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let sLength := mload(s) - let o := 0x20 - mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. - mstore(0x00, 0x00) - // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. - for { let temp := sLength } 1 {} { - o := sub(o, 1) - mstore8(o, add(48, mod(temp, 10))) - temp := div(temp, 10) - if iszero(temp) { break } - } - let n := sub(0x3a, o) // Header length: `26 + 32 - o`. - // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. - returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) - mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. - result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) - mstore(s, sLength) // Restore the length. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EMPTY CALLDATA HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an empty calldata bytes. - function emptySignature() internal pure returns (bytes calldata signature) { - /// @solidity memory-safe-assembly - assembly { - signature.length := 0 - } - } -} diff --git a/lib/solady/src/utils/EIP712.sol b/lib/solady/src/utils/EIP712.sol deleted file mode 100644 index f77ee14..0000000 --- a/lib/solady/src/utils/EIP712.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Contract for EIP-712 typed structured data hashing and signing. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol) -/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol) -/// -/// @dev Note, this implementation: -/// - Uses `address(this)` for the `verifyingContract` field. -/// - Does NOT use the optional EIP-712 salt. -/// - Does NOT use any EIP-712 extensions. -/// This is for simplicity and to save gas. -/// If you need to customize, please fork / modify accordingly. -abstract contract EIP712 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS AND IMMUTABLES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. - bytes32 internal constant _DOMAIN_TYPEHASH = - 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; - - uint256 private immutable _cachedThis; - uint256 private immutable _cachedChainId; - bytes32 private immutable _cachedNameHash; - bytes32 private immutable _cachedVersionHash; - bytes32 private immutable _cachedDomainSeparator; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTRUCTOR */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Cache the hashes for cheaper runtime gas costs. - /// In the case of upgradeable contracts (i.e. proxies), - /// or if the chain id changes due to a hard fork, - /// the domain separator will be seamlessly calculated on-the-fly. - constructor() { - _cachedThis = uint256(uint160(address(this))); - _cachedChainId = block.chainid; - - string memory name; - string memory version; - if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion(); - bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name)); - bytes32 versionHash = - _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version)); - _cachedNameHash = nameHash; - _cachedVersionHash = versionHash; - - bytes32 separator; - if (!_domainNameAndVersionMayChange()) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Load the free memory pointer. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), nameHash) - mstore(add(m, 0x40), versionHash) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - separator := keccak256(m, 0xa0) - } - } - _cachedDomainSeparator = separator; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* FUNCTIONS TO OVERRIDE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Please override this function to return the domain name and version. - /// ``` - /// function _domainNameAndVersion() - /// internal - /// pure - /// virtual - /// returns (string memory name, string memory version) - /// { - /// name = "Solady"; - /// version = "1"; - /// } - /// ``` - /// - /// Note: If the returned result may change after the contract has been deployed, - /// you must override `_domainNameAndVersionMayChange()` to return true. - function _domainNameAndVersion() - internal - view - virtual - returns (string memory name, string memory version); - - /// @dev Returns if `_domainNameAndVersion()` may change - /// after the contract has been deployed (i.e. after the constructor). - /// Default: false. - function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {} - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HASHING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the EIP-712 domain separator. - function _domainSeparator() internal view virtual returns (bytes32 separator) { - if (_domainNameAndVersionMayChange()) { - separator = _buildDomainSeparator(); - } else { - separator = _cachedDomainSeparator; - if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator(); - } - } - - /// @dev Returns the hash of the fully encoded EIP-712 message for this domain, - /// given `structHash`, as defined in - /// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct. - /// - /// The hash can be used together with {ECDSA-recover} to obtain the signer of a message: - /// ``` - /// bytes32 digest = _hashTypedData(keccak256(abi.encode( - /// keccak256("Mail(address to,string contents)"), - /// mailTo, - /// keccak256(bytes(mailContents)) - /// ))); - /// address signer = ECDSA.recover(digest, signature); - /// ``` - function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) { - // We will use `digest` to store the domain separator to save a bit of gas. - if (_domainNameAndVersionMayChange()) { - digest = _buildDomainSeparator(); - } else { - digest = _cachedDomainSeparator; - if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator(); - } - /// @solidity memory-safe-assembly - assembly { - // Compute the digest. - mstore(0x00, 0x1901000000000000) // Store "\x19\x01". - mstore(0x1a, digest) // Store the domain separator. - mstore(0x3a, structHash) // Store the struct hash. - digest := keccak256(0x18, 0x42) - // Restore the part of the free memory slot that was overwritten. - mstore(0x3a, 0) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EIP-5267 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev See: https://eips.ethereum.org/EIPS/eip-5267 - function eip712Domain() - public - view - virtual - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ) - { - fields = hex"0f"; // `0b01111`. - (name, version) = _domainNameAndVersion(); - chainId = block.chainid; - verifyingContract = address(this); - salt = salt; // `bytes32(0)`. - extensions = extensions; // `new uint256[](0)`. - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the EIP-712 domain separator. - function _buildDomainSeparator() private view returns (bytes32 separator) { - // We will use `separator` to store the name hash to save a bit of gas. - bytes32 versionHash; - if (_domainNameAndVersionMayChange()) { - (string memory name, string memory version) = _domainNameAndVersion(); - separator = keccak256(bytes(name)); - versionHash = keccak256(bytes(version)); - } else { - separator = _cachedNameHash; - versionHash = _cachedVersionHash; - } - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Load the free memory pointer. - mstore(m, _DOMAIN_TYPEHASH) - mstore(add(m, 0x20), separator) // Name hash. - mstore(add(m, 0x40), versionHash) - mstore(add(m, 0x60), chainid()) - mstore(add(m, 0x80), address()) - separator := keccak256(m, 0xa0) - } - } - - /// @dev Returns if the cached domain separator has been invalidated. - function _cachedDomainSeparatorInvalidated() private view returns (bool result) { - uint256 cachedChainId = _cachedChainId; - uint256 cachedThis = _cachedThis; - /// @solidity memory-safe-assembly - assembly { - result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis))) - } - } -} diff --git a/lib/solady/src/utils/ERC1967Factory.sol b/lib/solady/src/utils/ERC1967Factory.sol deleted file mode 100644 index 5d13141..0000000 --- a/lib/solady/src/utils/ERC1967Factory.sol +++ /dev/null @@ -1,419 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Factory for deploying and managing ERC1967 proxy contracts. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ERC1967Factory.sol) -/// @author jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) -contract ERC1967Factory { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The caller is not authorized to call the function. - error Unauthorized(); - - /// @dev The proxy deployment failed. - error DeploymentFailed(); - - /// @dev The upgrade failed. - error UpgradeFailed(); - - /// @dev The salt does not start with the caller. - error SaltDoesNotStartWithCaller(); - - /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`. - uint256 internal constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900; - - /// @dev `bytes4(keccak256(bytes("DeploymentFailed()")))`. - uint256 internal constant _DEPLOYMENT_FAILED_ERROR_SELECTOR = 0x30116425; - - /// @dev `bytes4(keccak256(bytes("UpgradeFailed()")))`. - uint256 internal constant _UPGRADE_FAILED_ERROR_SELECTOR = 0x55299b49; - - /// @dev `bytes4(keccak256(bytes("SaltDoesNotStartWithCaller()")))`. - uint256 internal constant _SALT_DOES_NOT_START_WITH_CALLER_ERROR_SELECTOR = 0x2f634836; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The admin of a proxy contract has been changed. - event AdminChanged(address indexed proxy, address indexed admin); - - /// @dev The implementation for a proxy has been upgraded. - event Upgraded(address indexed proxy, address indexed implementation); - - /// @dev A proxy has been deployed. - event Deployed(address indexed proxy, address indexed implementation, address indexed admin); - - /// @dev `keccak256(bytes("AdminChanged(address,address)"))`. - uint256 internal constant _ADMIN_CHANGED_EVENT_SIGNATURE = - 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f; - - /// @dev `keccak256(bytes("Upgraded(address,address)"))`. - uint256 internal constant _UPGRADED_EVENT_SIGNATURE = - 0x5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c7; - - /// @dev `keccak256(bytes("Deployed(address,address,address)"))`. - uint256 internal constant _DEPLOYED_EVENT_SIGNATURE = - 0xc95935a66d15e0da5e412aca0ad27ae891d20b2fb91cf3994b6a3bf2b8178082; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // The admin slot for a `proxy` is `shl(96, proxy)`. - - /// @dev The ERC-1967 storage slot for the implementation in the proxy. - /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`. - uint256 internal constant _IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ADMIN FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the admin of the proxy. - function adminOf(address proxy) public view returns (address admin) { - assembly { - admin := sload(shl(96, proxy)) - } - } - - /// @dev Sets the admin of the proxy. - /// The caller of this function must be the admin of the proxy on this factory. - function changeAdmin(address proxy, address admin) public { - assembly { - // Check if the caller is the admin of the proxy. - if iszero(eq(sload(shl(96, proxy)), caller())) { - mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - // Store the admin for the proxy. - sstore(shl(96, proxy), admin) - // Emit the {AdminChanged} event. - log3(0, 0, _ADMIN_CHANGED_EVENT_SIGNATURE, proxy, admin) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* UPGRADE FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Upgrades the proxy to point to `implementation`. - /// The caller of this function must be the admin of the proxy on this factory. - function upgrade(address proxy, address implementation) public payable { - upgradeAndCall(proxy, implementation, _emptyData()); - } - - /// @dev Upgrades the proxy to point to `implementation`. - /// Then, calls the proxy with abi encoded `data`. - /// The caller of this function must be the admin of the proxy on this factory. - function upgradeAndCall(address proxy, address implementation, bytes calldata data) - public - payable - { - assembly { - // Check if the caller is the admin of the proxy. - if iszero(eq(sload(shl(96, proxy)), caller())) { - mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - // Set up the calldata to upgrade the proxy. - let m := mload(0x40) - mstore(m, implementation) - mstore(add(m, 0x20), _IMPLEMENTATION_SLOT) - calldatacopy(add(m, 0x40), data.offset, data.length) - // Try upgrading the proxy and revert upon failure. - if iszero(call(gas(), proxy, callvalue(), m, add(0x40, data.length), 0x00, 0x00)) { - // Revert with the `UpgradeFailed` selector if there is no error returndata. - if iszero(returndatasize()) { - mstore(0x00, _UPGRADE_FAILED_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - // Otherwise, bubble up the returned error. - returndatacopy(0x00, 0x00, returndatasize()) - revert(0x00, returndatasize()) - } - // Emit the {Upgraded} event. - log3(0, 0, _UPGRADED_EVENT_SIGNATURE, proxy, implementation) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DEPLOY FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deploys a proxy for `implementation`, with `admin`, - /// and returns its address. - /// The value passed into this function will be forwarded to the proxy. - function deploy(address implementation, address admin) public payable returns (address proxy) { - proxy = deployAndCall(implementation, admin, _emptyData()); - } - - /// @dev Deploys a proxy for `implementation`, with `admin`, - /// and returns its address. - /// The value passed into this function will be forwarded to the proxy. - /// Then, calls the proxy with abi encoded `data`. - function deployAndCall(address implementation, address admin, bytes calldata data) - public - payable - returns (address proxy) - { - proxy = _deploy(implementation, admin, bytes32(0), false, data); - } - - /// @dev Deploys a proxy for `implementation`, with `admin`, `salt`, - /// and returns its deterministic address. - /// The value passed into this function will be forwarded to the proxy. - function deployDeterministic(address implementation, address admin, bytes32 salt) - public - payable - returns (address proxy) - { - proxy = deployDeterministicAndCall(implementation, admin, salt, _emptyData()); - } - - /// @dev Deploys a proxy for `implementation`, with `admin`, `salt`, - /// and returns its deterministic address. - /// The value passed into this function will be forwarded to the proxy. - /// Then, calls the proxy with abi encoded `data`. - function deployDeterministicAndCall( - address implementation, - address admin, - bytes32 salt, - bytes calldata data - ) public payable returns (address proxy) { - assembly { - // If the salt does not start with the zero address or the caller. - if iszero(or(iszero(shr(96, salt)), eq(caller(), shr(96, salt)))) { - mstore(0x00, _SALT_DOES_NOT_START_WITH_CALLER_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - } - proxy = _deploy(implementation, admin, salt, true, data); - } - - /// @dev Deploys the proxy, with optionality to deploy deterministically with a `salt`. - function _deploy( - address implementation, - address admin, - bytes32 salt, - bool useSalt, - bytes calldata data - ) internal returns (address proxy) { - bytes32 m = _initCode(); - assembly { - // Create the proxy. - switch useSalt - case 0 { proxy := create(0, add(m, 0x13), 0x88) } - default { proxy := create2(0, add(m, 0x13), 0x88, salt) } - // Revert if the creation fails. - if iszero(proxy) { - mstore(0x00, _DEPLOYMENT_FAILED_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - - // Set up the calldata to set the implementation of the proxy. - mstore(m, implementation) - mstore(add(m, 0x20), _IMPLEMENTATION_SLOT) - calldatacopy(add(m, 0x40), data.offset, data.length) - // Try setting the implementation on the proxy and revert upon failure. - if iszero(call(gas(), proxy, callvalue(), m, add(0x40, data.length), 0x00, 0x00)) { - // Revert with the `DeploymentFailed` selector if there is no error returndata. - if iszero(returndatasize()) { - mstore(0x00, _DEPLOYMENT_FAILED_ERROR_SELECTOR) - revert(0x1c, 0x04) - } - // Otherwise, bubble up the returned error. - returndatacopy(0x00, 0x00, returndatasize()) - revert(0x00, returndatasize()) - } - - // Store the admin for the proxy. - sstore(shl(96, proxy), admin) - - // Emit the {Deployed} event. - log4(0, 0, _DEPLOYED_EVENT_SIGNATURE, proxy, implementation, admin) - } - } - - /// @dev Returns the address of the proxy deployed with `salt`. - function predictDeterministicAddress(bytes32 salt) public view returns (address predicted) { - bytes32 hash = initCodeHash(); - assembly { - // Compute and store the bytecode hash. - mstore8(0x00, 0xff) // Write the prefix. - mstore(0x35, hash) - mstore(0x01, shl(96, address())) - mstore(0x15, salt) - // Note: `predicted` has dirty upper 96 bits. We won't clean it here - // as it will be automatically cleaned when it is copied into the returndata. - // Please clean as needed if used in other inline assembly blocks. - predicted := keccak256(0x00, 0x55) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x35, 0) - } - } - - /// @dev Returns the initialization code hash of the proxy. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash() public view returns (bytes32 result) { - bytes32 m = _initCode(); - assembly { - result := keccak256(add(m, 0x13), 0x88) - } - } - - /// @dev Returns a pointer to the initialization code of a proxy created via this factory. - function _initCode() internal view returns (bytes32 m) { - assembly { - /** - * -------------------------------------------------------------------------------------+ - * CREATION (9 bytes) | - * -------------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * -------------------------------------------------------------------------------------| - * 60 runSize | PUSH1 runSize | r | | - * 3d | RETURNDATASIZE | 0 r | | - * 81 | DUP2 | r 0 r | | - * 60 offset | PUSH1 offset | o r 0 r | | - * 3d | RETURNDATASIZE | 0 o r 0 r | | - * 39 | CODECOPY | 0 r | [0..runSize): runtime code | - * f3 | RETURN | | [0..runSize): runtime code | - * -------------------------------------------------------------------------------------| - * RUNTIME (127 bytes) | - * -------------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * -------------------------------------------------------------------------------------| - * | - * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | 0 | | - * 3d | RETURNDATASIZE | 0 0 | | - * | - * ::: check if caller is factory ::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 33 | CALLER | c 0 0 | | - * 73 factory | PUSH20 factory | f c 0 0 | | - * 14 | EQ | isf 0 0 | | - * 60 0x57 | PUSH1 0x57 | dest isf 0 0 | | - * 57 | JUMPI | 0 0 | | - * | - * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 | | - * 3d | RETURNDATASIZE | 0 cds 0 0 | | - * 3d | RETURNDATASIZE | 0 0 cds 0 0 | | - * 37 | CALLDATACOPY | 0 0 | [0..calldatasize): calldata | - * | - * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | - * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | - * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | - * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | - * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | - * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | - * | - * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | - * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | - * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | - * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | - * | - * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::::::: | - * 60 0x52 | PUSH1 0x52 | dest succ | [0..returndatasize): returndata | - * 57 | JUMPI | | [0..returndatasize): returndata | - * | - * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | - * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | - * fd | REVERT | | [0..returndatasize): returndata | - * | - * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | | [0..returndatasize): returndata | - * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | - * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | - * f3 | RETURN | | [0..returndatasize): returndata | - * | - * ::: set new implementation (caller is factory) ::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | 0 0 | | - * 3d | RETURNDATASIZE | 0 0 0 | | - * 35 | CALLDATALOAD | impl 0 0 | | - * 06 0x20 | PUSH1 0x20 | w impl 0 0 | | - * 35 | CALLDATALOAD | slot impl 0 0 | | - * 55 | SSTORE | 0 0 | | - * | - * ::: no extra calldata, return :::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 60 0x40 | PUSH1 0x40 | 2w 0 0 | | - * 80 | DUP1 | 2w 2w 0 0 | | - * 36 | CALLDATASIZE | cds 2w 2w 0 0 | | - * 11 | GT | gt 2w 0 0 | | - * 15 | ISZERO | lte 2w 0 0 | | - * 60 0x52 | PUSH1 0x52 | dest lte 2w 0 0 | | - * 57 | JUMPI | 2w 0 0 | | - * | - * ::: copy extra calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 2w 0 0 | | - * 03 | SUB | t 0 0 | | - * 80 | DUP1 | t t 0 0 | | - * 60 0x40 | PUSH1 0x40 | 2w t t 0 0 | | - * 3d | RETURNDATASIZE | 0 2w t t 0 0 | | - * 37 | CALLDATACOPY | t 0 0 | [0..t): extra calldata | - * | - * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | 0 t 0 0 | [0..t): extra calldata | - * 3d | RETURNDATASIZE | 0 0 t 0 0 | [0..t): extra calldata | - * 35 | CALLDATALOAD | i 0 t 0 0 | [0..t): extra calldata | - * 5a | GAS | g i 0 t 0 0 | [0..t): extra calldata | - * f4 | DELEGATECALL | succ | [0..t): extra calldata | - * | - * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds succ | [0..t): extra calldata | - * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..t): extra calldata | - * 80 | DUP1 | 0 0 rds succ | [0..t): extra calldata | - * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | - * | - * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::::::: | - * 60 0x52 | PUSH1 0x52 | dest succ | [0..returndatasize): returndata | - * 57 | JUMPI | | [0..returndatasize): returndata | - * | - * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | - * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | - * fd | REVERT | | [0..returndatasize): returndata | - * -------------------------------------------------------------------------------------+ - */ - - m := mload(0x40) - // forgefmt: disable-start - switch shr(112, address()) - case 0 { - // If the factory's address has six or more leading zero bytes. - mstore(add(m, 0x75), 0x604c573d6000fd) // 7 - mstore(add(m, 0x6e), 0x3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e) // 32 - mstore(add(m, 0x4e), 0x3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b) // 32 - mstore(add(m, 0x2e), 0x14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc) // 32 - mstore(add(m, 0x0e), address()) // 14 - mstore(m, 0x60793d8160093d39f33d3d336d) // 9 + 4 - } - default { - mstore(add(m, 0x7b), 0x6052573d6000fd) // 7 - mstore(add(m, 0x74), 0x3d356020355560408036111560525736038060403d373d3d355af43d6000803e) // 32 - mstore(add(m, 0x54), 0x3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b) // 32 - mstore(add(m, 0x34), 0x14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc) // 32 - mstore(add(m, 0x14), address()) // 20 - mstore(m, 0x607f3d8160093d39f33d3d3373) // 9 + 4 - } - // forgefmt: disable-end - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Helper function to return an empty bytes calldata. - function _emptyData() internal pure returns (bytes calldata data) { - assembly { - data.length := 0 - } - } -} diff --git a/lib/solady/src/utils/ERC1967FactoryConstants.sol b/lib/solady/src/utils/ERC1967FactoryConstants.sol deleted file mode 100644 index 61ae337..0000000 --- a/lib/solady/src/utils/ERC1967FactoryConstants.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice The address and bytecode of the canonical ERC1967Factory deployment. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ERC1967FactoryLib.sol) -/// @author jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) -/// -/// @dev The canonical ERC1967Factory is deployed permissionlessly via -/// 0age's ImmutableCreate2Factory located at 0x0000000000FFe8B47B3e2130213B802212439497. -/// -/// `ADDRESS = immutableCreate2Factory.safeCreate2(SALT, INITCODE)` -/// -/// If the canonical ERC1967Factory has not been deployed on your EVM chain of choice, -/// please feel free to deploy via 0age's ImmutableCreate2Factory. -/// -/// If 0age's ImmutableCreate2Factory has not been deployed on your EVM chain of choice, -/// please refer to 0age's ImmutableCreate2Factory deployment instructions at: -/// https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md -/// -/// Contract verification: -/// - Source code: -/// https://github.com/Vectorized/solady/blob/5212e50fef1f2ff1b1b5e03a5d276a0d23c02713/src/utils/ERC1967Factory.sol -/// (The EXACT source code is required. Use the file at the commit instead of the latest copy.) -/// - Optimization Enabled: Yes with 1000000 runs -/// - Compiler Version: v0.8.19+commit.7dd6d404 -/// - Other Settings: default evmVersion, MIT license -library ERC1967FactoryConstants { - /// @dev The canonical ERC1967Factory address for EVM chains. - address internal constant ADDRESS = 0x0000000000006396FF2a80c067f99B3d2Ab4Df24; - - /// @dev The canonical ERC1967Factory bytecode for EVM chains. - /// Useful for forge tests: - /// `vm.etch(ADDRESS, BYTECODE)`. - bytes internal constant BYTECODE = - hex"6080604052600436106100b15760003560e01c8063545e7c611161006957806399a88ec41161004e57806399a88ec41461019d578063a97b90d5146101b0578063db4c545e146101c357600080fd5b8063545e7c61146101775780639623609d1461018a57600080fd5b80633729f9221161009a5780633729f922146101315780634314f120146101445780635414dff01461015757600080fd5b80631acfd02a146100b65780632abbef15146100d8575b600080fd5b3480156100c257600080fd5b506100d66100d1366004610604565b6101e6565b005b3480156100e457600080fd5b506101076100f3366004610637565b30600c908152600091909152602090205490565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010761013f366004610652565b610237565b6101076101523660046106d7565b61024e565b34801561016357600080fd5b50610107610172366004610738565b610267565b610107610185366004610604565b61029a565b6100d66101983660046106d7565b6102af565b6100d66101ab366004610604565b61035f565b6101076101be366004610751565b610370565b3480156101cf57600080fd5b506101d86103a9565b604051908152602001610128565b30600c52816000526020600c2033815414610209576382b429006000526004601cfd5b81905580827f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f600080a35050565b60006102468484843685610370565b949350505050565b600061025e8585838087876103c2565b95945050505050565b6000806102726103a9565b905060ff600053806035523060601b6001528260155260556000209150600060355250919050565b60006102a88383368461024e565b9392505050565b30600c5283600052336020600c2054146102d1576382b429006000526004601cfd5b6040518381527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015281836040830137600080836040018334895af1610331573d610327576355299b496000526004601cfd5b3d6000803e3d6000fd5b5082847f5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c7600080a350505050565b61036c82823660006102af565b5050565b60008360601c33148460601c151761039057632f6348366000526004601cfd5b61039f868686600187876103c2565b9695505050505050565b6000806103b461049c565b608960139091012092915050565b6000806103cd61049c565b90508480156103e757866089601384016000f592506103f3565b6089601383016000f092505b50816104075763301164256000526004601cfd5b8781527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc602082015282846040830137600080846040018334865af161045a573d6103275763301164256000526004601cfd5b30600c5281600052866020600c20558688837fc95935a66d15e0da5e412aca0ad27ae891d20b2fb91cf3994b6a3bf2b8178082600080a4509695505050505050565b6040513060701c801561054257666052573d6000fd607b8301527f3d356020355560408036111560525736038060403d373d3d355af43d6000803e60748301527f3735a920a3ca505d382bbc545af43d6000803e6052573d6000fd5b3d6000f35b60548301527f14605757363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc60348301523060148301526c607f3d8160093d39f33d3d337382525090565b66604c573d6000fd60758301527f3d3560203555604080361115604c5736038060403d373d3d355af43d6000803e606e8301527f3735a920a3ca505d382bbc545af43d6000803e604c573d6000fd5b3d6000f35b604e8301527f14605157363d3d37363d7f360894a13ba1a3210667c828492db98dca3e2076cc602e83015230600e8301526c60793d8160093d39f33d3d336d82525090565b803573ffffffffffffffffffffffffffffffffffffffff811681146105ff57600080fd5b919050565b6000806040838503121561061757600080fd5b610620836105db565b915061062e602084016105db565b90509250929050565b60006020828403121561064957600080fd5b6102a8826105db565b60008060006060848603121561066757600080fd5b610670846105db565b925061067e602085016105db565b9150604084013590509250925092565b60008083601f8401126106a057600080fd5b50813567ffffffffffffffff8111156106b857600080fd5b6020830191508360208285010111156106d057600080fd5b9250929050565b600080600080606085870312156106ed57600080fd5b6106f6856105db565b9350610704602086016105db565b9250604085013567ffffffffffffffff81111561072057600080fd5b61072c8782880161068e565b95989497509550505050565b60006020828403121561074a57600080fd5b5035919050565b60008060008060006080868803121561076957600080fd5b610772866105db565b9450610780602087016105db565b935060408601359250606086013567ffffffffffffffff8111156107a357600080fd5b6107af8882890161068e565b96999598509396509294939250505056fea26469706673582212200ac7c3ccbc2d311c48bf5465b021542e0e306fe3c462c060ba6a3d2f81ff6c5f64736f6c63430008130033"; - - /// @dev The initcode used to deploy the canonical ERC1967Factory. - bytes internal constant INITCODE = abi.encodePacked( - hex"608060405234801561001057600080fd5b506107f6806100206000396000f3fe", BYTECODE - ); - - /// @dev For deterministic deployment via 0age's ImmutableCreate2Factory. - bytes32 internal constant SALT = - 0x0000000000000000000000000000000000000000e75e4f228818c80007508f33; -} diff --git a/lib/solady/src/utils/FixedPointMathLib.sol b/lib/solady/src/utils/FixedPointMathLib.sol deleted file mode 100644 index 590f809..0000000 --- a/lib/solady/src/utils/FixedPointMathLib.sol +++ /dev/null @@ -1,993 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Arithmetic library with operations for fixed-point numbers. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) -library FixedPointMathLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The operation failed, as the output exceeds the maximum value of uint256. - error ExpOverflow(); - - /// @dev The operation failed, as the output exceeds the maximum value of uint256. - error FactorialOverflow(); - - /// @dev The operation failed, due to an overflow. - error RPowOverflow(); - - /// @dev The mantissa is too big to fit. - error MantissaOverflow(); - - /// @dev The operation failed, due to an multiplication overflow. - error MulWadFailed(); - - /// @dev The operation failed, either due to a - /// multiplication overflow, or a division by a zero. - error DivWadFailed(); - - /// @dev The multiply-divide operation failed, either due to a - /// multiplication overflow, or a division by a zero. - error MulDivFailed(); - - /// @dev The division failed, as the denominator is zero. - error DivFailed(); - - /// @dev The full precision multiply-divide operation failed, either due - /// to the result being larger than 256 bits, or a division by a zero. - error FullMulDivFailed(); - - /// @dev The output is undefined, as the input is less-than-or-equal to zero. - error LnWadUndefined(); - - /// @dev The input outside the acceptable domain. - error OutOfDomain(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The scalar of ETH and most ERC20s. - uint256 internal constant WAD = 1e18; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* SIMPLIFIED FIXED POINT OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Equivalent to `(x * y) / WAD` rounded down. - function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. - if mul(y, gt(x, div(not(0), y))) { - mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. - revert(0x1c, 0x04) - } - z := div(mul(x, y), WAD) - } - } - - /// @dev Equivalent to `(x * y) / WAD` rounded up. - function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. - if mul(y, gt(x, div(not(0), y))) { - mstore(0x00, 0xbac65e5b) // `MulWadFailed()`. - revert(0x1c, 0x04) - } - z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) - } - } - - /// @dev Equivalent to `(x * WAD) / y` rounded down. - function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. - if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { - mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. - revert(0x1c, 0x04) - } - z := div(mul(x, WAD), y) - } - } - - /// @dev Equivalent to `(x * WAD) / y` rounded up. - function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. - if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { - mstore(0x00, 0x7c5f487d) // `DivWadFailed()`. - revert(0x1c, 0x04) - } - z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) - } - } - - /// @dev Equivalent to `x` to the power of `y`. - /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. - function powWad(int256 x, int256 y) internal pure returns (int256) { - // Using `ln(x)` means `x` must be greater than 0. - return expWad((lnWad(x) * y) / int256(WAD)); - } - - /// @dev Returns `exp(x)`, denominated in `WAD`. - function expWad(int256 x) internal pure returns (int256 r) { - unchecked { - // When the result is less than 0.5 we return zero. - // This happens when `x <= floor(log(0.5e18) * 1e18) ≈ -42e18`. - if (x <= -42139678854452767551) return r; - - /// @solidity memory-safe-assembly - assembly { - // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as - // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. - if iszero(slt(x, 135305999368893231589)) { - mstore(0x00, 0xa37bfec9) // `ExpOverflow()`. - revert(0x1c, 0x04) - } - } - - // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96` - // for more intermediate precision and a binary basis. This base conversion - // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. - x = (x << 78) / 5 ** 18; - - // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers - // of two such that exp(x) = exp(x') * 2**k, where k is an integer. - // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). - int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; - x = x - k * 54916777467707473351141471128; - - // `k` is in the range `[-61, 195]`. - - // Evaluate using a (6, 7)-term rational approximation. - // `p` is made monic, we'll multiply by a scale factor later. - int256 y = x + 1346386616545796478920950773328; - y = ((y * x) >> 96) + 57155421227552351082224309758442; - int256 p = y + x - 94201549194550492254356042504812; - p = ((p * y) >> 96) + 28719021644029726153956944680412240; - p = p * x + (4385272521454847904659076985693276 << 96); - - // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. - int256 q = x - 2855989394907223263936484059900; - q = ((q * x) >> 96) + 50020603652535783019961831881945; - q = ((q * x) >> 96) - 533845033583426703283633433725380; - q = ((q * x) >> 96) + 3604857256930695427073651918091429; - q = ((q * x) >> 96) - 14423608567350463180887372962807573; - q = ((q * x) >> 96) + 26449188498355588339934803723976023; - - /// @solidity memory-safe-assembly - assembly { - // Div in assembly because solidity adds a zero check despite the unchecked. - // The q polynomial won't have zeros in the domain as all its roots are complex. - // No scaling is necessary because p is already `2**96` too large. - r := sdiv(p, q) - } - - // r should be in the range `(0.09, 0.25) * 2**96`. - - // We now need to multiply r by: - // - The scale factor `s ≈ 6.031367120`. - // - The `2**k` factor from the range reduction. - // - The `1e18 / 2**96` factor for base conversion. - // We do this all at once, with an intermediate result in `2**213` - // basis, so the final right shift is always by a positive amount. - r = int256( - (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) - ); - } - } - - /// @dev Returns `ln(x)`, denominated in `WAD`. - function lnWad(int256 x) internal pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - if iszero(sgt(x, 0)) { - mstore(0x00, 0x1615e638) // `LnWadUndefined()`. - revert(0x1c, 0x04) - } - // We want to convert `x` from `10**18` fixed point to `2**96` fixed point. - // We do this by multiplying by `2**96 / 10**18`. But since - // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here - // and add `ln(2**96 / 10**18)` at the end. - - // Compute `k = log2(x) - 96`, `t = 159 - k = 255 - log2(x) = 255 ^ log2(x)`. - let t := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - t := or(t, shl(6, lt(0xffffffffffffffff, shr(t, x)))) - t := or(t, shl(5, lt(0xffffffff, shr(t, x)))) - t := or(t, shl(4, lt(0xffff, shr(t, x)))) - t := or(t, shl(3, lt(0xff, shr(t, x)))) - // forgefmt: disable-next-item - t := xor(t, byte(and(0x1f, shr(shr(t, x), 0x8421084210842108cc6318c6db6d54be)), - 0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)) - - // Reduce range of x to (1, 2) * 2**96 - // ln(2^k * x) = k * ln(2) + ln(x) - x := shr(159, shl(t, x)) - - // Evaluate using a (8, 8)-term rational approximation. - // `p` is made monic, we will multiply by a scale factor later. - // forgefmt: disable-next-item - let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir. - sar(96, mul(add(43456485725739037958740375743393, - sar(96, mul(add(24828157081833163892658089445524, - sar(96, mul(add(3273285459638523848632254066296, - x), x))), x))), x)), 11111509109440967052023855526967) - p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857) - p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526) - p := sub(mul(p, x), shl(96, 795164235651350426258249787498)) - - // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. - // `q` is monic by convention. - let q := add(5573035233440673466300451813936, x) - q := add(71694874799317883764090561454958, sar(96, mul(x, q))) - q := add(283447036172924575727196451306956, sar(96, mul(x, q))) - q := add(401686690394027663651624208769553, sar(96, mul(x, q))) - q := add(204048457590392012362485061816622, sar(96, mul(x, q))) - q := add(31853899698501571402653359427138, sar(96, mul(x, q))) - q := add(909429971244387300277376558375, sar(96, mul(x, q))) - - // `r` is in the range `(0, 0.125) * 2**96`. - - // Finalization, we need to: - // - Multiply by the scale factor `s = 5.549…`. - // - Add `ln(2**96 / 10**18)`. - // - Add `k * ln(2)`. - // - Multiply by `10**18 / 2**96 = 5**18 >> 78`. - - // The q polynomial is known not to have zeros in the domain. - // No scaling required because p is already `2**96` too large. - r := sdiv(p, q) - // Multiply by the scaling factor: `s * 5e18 * 2**96`, base is now `5**18 * 2**192`. - r := mul(1677202110996718588342820967067443963516166, r) - // Add `ln(2) * k * 5e18 * 2**192`. - // forgefmt: disable-next-item - r := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, t)), r) - // Add `ln(2**96 / 10**18) * 5e18 * 2**192`. - r := add(600920179829731861736702779321621459595472258049074101567377883020018308, r) - // Base conversion: mul `2**18 / 2**192`. - r := sar(174, r) - } - } - - /// @dev Returns `W_0(x)`, denominated in `WAD`. - /// See: https://en.wikipedia.org/wiki/Lambert_W_function - /// a.k.a. Product log function. This is an approximation of the principal branch. - function lambertW0Wad(int256 x) internal pure returns (int256 w) { - if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`. - uint256 c; // Whether we need to avoid catastrophic cancellation. - uint256 i = 4; // Number of iterations. - if (w <= 0x1ffffffffffff) { - if (-0x4000000000000 <= w) { - i = 1; // Inputs near zero only take one step to converge. - } else if (w <= -0x3ffffffffffffff) { - i = 32; // Inputs near `-1/e` take very long to converge. - } - } else if (w >> 63 == 0) { - /// @solidity memory-safe-assembly - assembly { - // Inline log2 for more performance, since the range is small. - let v := shr(49, w) - let l := shl(3, lt(0xff, v)) - // forgefmt: disable-next-item - l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)), - 0x0706060506020504060203020504030106050205030304010505030400000000)), 49) - w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13)) - c := gt(l, 60) - i := add(2, add(gt(l, 53), c)) - } - } else { - // `ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`. - int256 ll = lnWad(w = lnWad(w)); - /// @solidity memory-safe-assembly - assembly { - w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll)) - i := add(3, iszero(shr(68, x))) - c := iszero(shr(143, x)) - } - if (c == 0) { - int256 wad = int256(WAD); - int256 p = x; - // If `x` is big, use Newton's so that intermediate values won't overflow. - do { - int256 e = expWad(w); - /// @solidity memory-safe-assembly - assembly { - let t := mul(w, div(e, wad)) - w := sub(w, sdiv(sub(t, x), div(add(e, t), wad))) - i := sub(i, 1) - } - if (p <= w) break; - p = w; - } while (i != 0); - /// @solidity memory-safe-assembly - assembly { - w := sub(w, sgt(w, 2)) - } - return w; - } - } - // forgefmt: disable-next-item - unchecked { - int256 wad = int256(WAD); - int256 p = x; - do { // Otherwise, use Halley's for faster convergence. - int256 e = expWad(w); - /// @solidity memory-safe-assembly - assembly { - let t := add(w, wad) - let s := sub(mul(w, e), mul(x, wad)) - w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t))))) - } - if (p <= w) break; - p = w; - } while (--i != c); - /// @solidity memory-safe-assembly - assembly { - w := sub(w, sgt(w, 2)) - } - // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of - // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation. - if (c != 0) { - /// @solidity memory-safe-assembly - assembly { - x := sdiv(mul(x, wad), w) - } - x = (w * (wad + lnWad(x))); - /// @solidity memory-safe-assembly - assembly { - w := sdiv(x, add(wad, w)) - } - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* GENERAL NUMBER UTILITIES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Calculates `floor(a * b / d)` with full precision. - /// Throws if result overflows a uint256 or when `d` is zero. - /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv - function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - for {} 1 {} { - // 512-bit multiply `[p1 p0] = x * y`. - // Compute the product mod `2**256` and mod `2**256 - 1` - // then use the Chinese Remainder Theorem to reconstruct - // the 512 bit result. The result is stored in two 256 - // variables such that `product = p1 * 2**256 + p0`. - - // Least significant 256 bits of the product. - let p0 := mul(x, y) - let mm := mulmod(x, y, not(0)) - // Most significant 256 bits of the product. - let p1 := sub(mm, add(p0, lt(mm, p0))) - - // Handle non-overflow cases, 256 by 256 division. - if iszero(p1) { - if iszero(d) { - mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. - revert(0x1c, 0x04) - } - result := div(p0, d) - break - } - - // Make sure the result is less than `2**256`. Also prevents `d == 0`. - if iszero(gt(d, p1)) { - mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. - revert(0x1c, 0x04) - } - - /*------------------- 512 by 256 division --------------------*/ - - // Make division exact by subtracting the remainder from `[p1 p0]`. - // Compute remainder using mulmod. - let r := mulmod(x, y, d) - // `t` is the least significant bit of `d`. - // Always greater or equal to 1. - let t := and(d, sub(0, d)) - // Divide `d` by `t`, which is a power of two. - d := div(d, t) - // Invert `d mod 2**256` - // Now that `d` is an odd number, it has an inverse - // modulo `2**256` such that `d * inv = 1 mod 2**256`. - // Compute the inverse by starting with a seed that is correct - // correct for four bits. That is, `d * inv = 1 mod 2**4`. - let inv := xor(mul(3, d), 2) - // Now use Newton-Raphson iteration to improve the precision. - // Thanks to Hensel's lifting lemma, this also works in modular - // arithmetic, doubling the correct bits in each step. - inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 - inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 - inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 - inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 - inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 - result := - mul( - // Divide [p1 p0] by the factors of two. - // Shift in bits from `p1` into `p0`. For this we need - // to flip `t` such that it is `2**256 / t`. - or(mul(sub(p1, gt(r, p0)), add(div(sub(0, t), t), 1)), div(sub(p0, r), t)), - // inverse mod 2**256 - mul(inv, sub(2, mul(d, inv))) - ) - break - } - } - } - - /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. - /// Throws if result overflows a uint256 or when `d` is zero. - /// Credit to Uniswap-v3-core under MIT license: - /// https://github.com/Uniswap/v3-core/blob/contracts/libraries/FullMath.sol - function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { - result = fullMulDiv(x, y, d); - /// @solidity memory-safe-assembly - assembly { - if mulmod(x, y, d) { - result := add(result, 1) - if iszero(result) { - mstore(0x00, 0xae47f702) // `FullMulDivFailed()`. - revert(0x1c, 0x04) - } - } - } - } - - /// @dev Returns `floor(x * y / d)`. - /// Reverts if `x * y` overflows, or `d` is zero. - function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { - mstore(0x00, 0xad251c27) // `MulDivFailed()`. - revert(0x1c, 0x04) - } - z := div(mul(x, y), d) - } - } - - /// @dev Returns `ceil(x * y / d)`. - /// Reverts if `x * y` overflows, or `d` is zero. - function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { - mstore(0x00, 0xad251c27) // `MulDivFailed()`. - revert(0x1c, 0x04) - } - z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d)) - } - } - - /// @dev Returns `ceil(x / d)`. - /// Reverts if `d` is zero. - function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - if iszero(d) { - mstore(0x00, 0x65244e4e) // `DivFailed()`. - revert(0x1c, 0x04) - } - z := add(iszero(iszero(mod(x, d))), div(x, d)) - } - } - - /// @dev Returns `max(0, x - y)`. - function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := mul(gt(x, y), sub(x, y)) - } - } - - /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`. - /// Reverts if the computation overflows. - function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`. - if x { - z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x` - let half := shr(1, b) // Divide `b` by 2. - // Divide `y` by 2 every iteration. - for { y := shr(1, y) } y { y := shr(1, y) } { - let xx := mul(x, x) // Store x squared. - let xxRound := add(xx, half) // Round to the nearest number. - // Revert if `xx + half` overflowed, or if `x ** 2` overflows. - if or(lt(xxRound, xx), shr(128, x)) { - mstore(0x00, 0x49f7642b) // `RPowOverflow()`. - revert(0x1c, 0x04) - } - x := div(xxRound, b) // Set `x` to scaled `xxRound`. - // If `y` is odd: - if and(y, 1) { - let zx := mul(z, x) // Compute `z * x`. - let zxRound := add(zx, half) // Round to the nearest number. - // If `z * x` overflowed or `zx + half` overflowed: - if or(xor(div(zx, x), z), lt(zxRound, zx)) { - // Revert if `x` is non-zero. - if iszero(iszero(x)) { - mstore(0x00, 0x49f7642b) // `RPowOverflow()`. - revert(0x1c, 0x04) - } - } - z := div(zxRound, b) // Return properly scaled `zxRound`. - } - } - } - } - } - - /// @dev Returns the square root of `x`. - function sqrt(uint256 x) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. - z := 181 // The "correct" value is 1, but this saves a multiplication later. - - // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad - // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. - - // Let `y = x / 2**r`. We check `y >= 2**(k + 8)` - // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`. - let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffffff, shr(r, x)))) - z := shl(shr(1, r), z) - - // Goal was to get `z*z*y` within a small factor of `x`. More iterations could - // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`. - // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small. - // That's not possible if `x < 256` but we can just verify those cases exhaustively. - - // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`. - // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`. - // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps. - - // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)` - // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`, - // with largest error when `s = 1` and when `s = 256` or `1/256`. - - // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`. - // Then we can estimate `sqrt(y)` using - // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`. - - // There is no overflow risk here since `y < 2**136` after the first branch above. - z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181. - - // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - - // If `x+1` is a perfect square, the Babylonian method cycles between - // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. - // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division - z := sub(z, lt(div(x, z), z)) - } - } - - /// @dev Returns the cube root of `x`. - /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license: - /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy - function cbrt(uint256 x) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - - z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3))) - - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - z := div(add(add(div(x, mul(z, z)), z), z), 3) - - z := sub(z, lt(div(x, mul(z, z)), z)) - } - } - - /// @dev Returns the square root of `x`, denominated in `WAD`. - function sqrtWad(uint256 x) internal pure returns (uint256 z) { - unchecked { - z = 10 ** 9; - if (x <= type(uint256).max / 10 ** 36 - 1) { - x *= 10 ** 18; - z = 1; - } - z *= sqrt(x); - } - } - - /// @dev Returns the cube root of `x`, denominated in `WAD`. - function cbrtWad(uint256 x) internal pure returns (uint256 z) { - unchecked { - z = 10 ** 12; - if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) { - if (x >= type(uint256).max / 10 ** 36) { - x *= 10 ** 18; - z = 10 ** 6; - } else { - x *= 10 ** 36; - z = 1; - } - } - z *= cbrt(x); - } - } - - /// @dev Returns the factorial of `x`. - function factorial(uint256 x) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - if iszero(lt(x, 58)) { - mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`. - revert(0x1c, 0x04) - } - for { result := 1 } x { x := sub(x, 1) } { result := mul(result, x) } - } - } - - /// @dev Returns the log2 of `x`. - /// Equivalent to computing the index of the most significant bit (MSB) of `x`. - /// Returns 0 if `x` is zero. - function log2(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - // forgefmt: disable-next-item - r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), - 0x0706060506020504060203020504030106050205030304010505030400000000)) - } - } - - /// @dev Returns the log2 of `x`, rounded up. - /// Returns 0 if `x` is zero. - function log2Up(uint256 x) internal pure returns (uint256 r) { - r = log2(x); - /// @solidity memory-safe-assembly - assembly { - r := add(r, lt(shl(r, 1), x)) - } - } - - /// @dev Returns the log10 of `x`. - /// Returns 0 if `x` is zero. - function log10(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - if iszero(lt(x, 100000000000000000000000000000000000000)) { - x := div(x, 100000000000000000000000000000000000000) - r := 38 - } - if iszero(lt(x, 100000000000000000000)) { - x := div(x, 100000000000000000000) - r := add(r, 20) - } - if iszero(lt(x, 10000000000)) { - x := div(x, 10000000000) - r := add(r, 10) - } - if iszero(lt(x, 100000)) { - x := div(x, 100000) - r := add(r, 5) - } - r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999))))) - } - } - - /// @dev Returns the log10 of `x`, rounded up. - /// Returns 0 if `x` is zero. - function log10Up(uint256 x) internal pure returns (uint256 r) { - r = log10(x); - /// @solidity memory-safe-assembly - assembly { - r := add(r, lt(exp(10, r), x)) - } - } - - /// @dev Returns the log256 of `x`. - /// Returns 0 if `x` is zero. - function log256(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(shr(3, r), lt(0xff, shr(r, x))) - } - } - - /// @dev Returns the log256 of `x`, rounded up. - /// Returns 0 if `x` is zero. - function log256Up(uint256 x) internal pure returns (uint256 r) { - r = log256(x); - /// @solidity memory-safe-assembly - assembly { - r := add(r, lt(shl(shl(3, r), 1), x)) - } - } - - /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`. - /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent). - function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) { - /// @solidity memory-safe-assembly - assembly { - mantissa := x - if mantissa { - if iszero(mod(mantissa, 1000000000000000000000000000000000)) { - mantissa := div(mantissa, 1000000000000000000000000000000000) - exponent := 33 - } - if iszero(mod(mantissa, 10000000000000000000)) { - mantissa := div(mantissa, 10000000000000000000) - exponent := add(exponent, 19) - } - if iszero(mod(mantissa, 1000000000000)) { - mantissa := div(mantissa, 1000000000000) - exponent := add(exponent, 12) - } - if iszero(mod(mantissa, 1000000)) { - mantissa := div(mantissa, 1000000) - exponent := add(exponent, 6) - } - if iszero(mod(mantissa, 10000)) { - mantissa := div(mantissa, 10000) - exponent := add(exponent, 4) - } - if iszero(mod(mantissa, 100)) { - mantissa := div(mantissa, 100) - exponent := add(exponent, 2) - } - if iszero(mod(mantissa, 10)) { - mantissa := div(mantissa, 10) - exponent := add(exponent, 1) - } - } - } - } - - /// @dev Convenience function for packing `x` into a smaller number using `sci`. - /// The `mantissa` will be in bits [7..255] (the upper 249 bits). - /// The `exponent` will be in bits [0..6] (the lower 7 bits). - /// Use `SafeCastLib` to safely ensure that the `packed` number is small - /// enough to fit in the desired unsigned integer type: - /// ``` - /// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether)); - /// ``` - function packSci(uint256 x) internal pure returns (uint256 packed) { - (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`. - /// @solidity memory-safe-assembly - assembly { - if shr(249, x) { - mstore(0x00, 0xce30380c) // `MantissaOverflow()`. - revert(0x1c, 0x04) - } - packed := or(shl(7, x), packed) - } - } - - /// @dev Convenience function for unpacking a packed number from `packSci`. - function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) { - unchecked { - unpacked = (packed >> 7) * 10 ** (packed & 0x7f); - } - } - - /// @dev Returns the average of `x` and `y`. - function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { - unchecked { - z = (x & y) + ((x ^ y) >> 1); - } - } - - /// @dev Returns the average of `x` and `y`. - function avg(int256 x, int256 y) internal pure returns (int256 z) { - unchecked { - z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1); - } - } - - /// @dev Returns the absolute value of `x`. - function abs(int256 x) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(sub(0, shr(255, x)), add(sub(0, shr(255, x)), x)) - } - } - - /// @dev Returns the absolute distance between `x` and `y`. - function dist(int256 x, int256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x)) - } - } - - /// @dev Returns the minimum of `x` and `y`. - function min(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, y), lt(y, x))) - } - } - - /// @dev Returns the minimum of `x` and `y`. - function min(int256 x, int256 y) internal pure returns (int256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, y), slt(y, x))) - } - } - - /// @dev Returns the maximum of `x` and `y`. - function max(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, y), gt(y, x))) - } - } - - /// @dev Returns the maximum of `x` and `y`. - function max(int256 x, int256 y) internal pure returns (int256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, y), sgt(y, x))) - } - } - - /// @dev Returns `x`, bounded to `minValue` and `maxValue`. - function clamp(uint256 x, uint256 minValue, uint256 maxValue) - internal - pure - returns (uint256 z) - { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, minValue), gt(minValue, x))) - z := xor(z, mul(xor(z, maxValue), lt(maxValue, z))) - } - } - - /// @dev Returns `x`, bounded to `minValue` and `maxValue`. - function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { - /// @solidity memory-safe-assembly - assembly { - z := xor(x, mul(xor(x, minValue), sgt(minValue, x))) - z := xor(z, mul(xor(z, maxValue), slt(maxValue, z))) - } - } - - /// @dev Returns greatest common divisor of `x` and `y`. - function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - for { z := x } y {} { - let t := y - y := mod(z, y) - z := t - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* RAW NUMBER OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns `x + y`, without checking for overflow. - function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { - unchecked { - z = x + y; - } - } - - /// @dev Returns `x + y`, without checking for overflow. - function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { - unchecked { - z = x + y; - } - } - - /// @dev Returns `x - y`, without checking for underflow. - function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { - unchecked { - z = x - y; - } - } - - /// @dev Returns `x - y`, without checking for underflow. - function rawSub(int256 x, int256 y) internal pure returns (int256 z) { - unchecked { - z = x - y; - } - } - - /// @dev Returns `x * y`, without checking for overflow. - function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { - unchecked { - z = x * y; - } - } - - /// @dev Returns `x * y`, without checking for overflow. - function rawMul(int256 x, int256 y) internal pure returns (int256 z) { - unchecked { - z = x * y; - } - } - - /// @dev Returns `x / y`, returning 0 if `y` is zero. - function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := div(x, y) - } - } - - /// @dev Returns `x / y`, returning 0 if `y` is zero. - function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { - /// @solidity memory-safe-assembly - assembly { - z := sdiv(x, y) - } - } - - /// @dev Returns `x % y`, returning 0 if `y` is zero. - function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := mod(x, y) - } - } - - /// @dev Returns `x % y`, returning 0 if `y` is zero. - function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { - /// @solidity memory-safe-assembly - assembly { - z := smod(x, y) - } - } - - /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. - function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := addmod(x, y, d) - } - } - - /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. - function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := mulmod(x, y, d) - } - } -} diff --git a/lib/solady/src/utils/GasBurnerLib.sol b/lib/solady/src/utils/GasBurnerLib.sol deleted file mode 100644 index 6150073..0000000 --- a/lib/solady/src/utils/GasBurnerLib.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for burning gas without reverting. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/GasBurnerLib.sol) -library GasBurnerLib { - /// @dev Burns approximately `x` amount of gas. - /// Intended for Contract Secured Revenue (CSR). - /// - /// Recommendation: pass in an admin-controlled dynamic value instead of a hardcoded one. - /// This is so that you can adjust your contract as needed depending on market conditions, - /// and to give you and your users a leeway in case the L2 chain change the rules. - function burn(uint256 x) internal pure { - /// @solidity memory-safe-assembly - assembly { - mstore(0x10, or(1, x)) - let n := mul(gt(x, 120), div(x, 91)) - // We use keccak256 instead of blake2f precompile for better widespread compatibility. - for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } { - mstore(0x10, keccak256(0x10, 0x10)) // Yes. - } - if iszero(mload(0x10)) { invalid() } - } - } -} diff --git a/lib/solady/src/utils/JSONParserLib.sol b/lib/solady/src/utils/JSONParserLib.sol deleted file mode 100644 index eaa7649..0000000 --- a/lib/solady/src/utils/JSONParserLib.sol +++ /dev/null @@ -1,815 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for parsing JSONs. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/JSONParserLib.sol) -library JSONParserLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The input is invalid. - error ParsingFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // There are 6 types of variables in JSON (excluding undefined). - - /// @dev For denoting that an item has not been initialized. - /// A item returned from `parse` will never be of an undefined type. - /// Parsing a invalid JSON string will simply revert. - uint8 internal constant TYPE_UNDEFINED = 0; - - /// @dev Type representing an array (e.g. `[1,2,3]`). - uint8 internal constant TYPE_ARRAY = 1; - - /// @dev Type representing an object (e.g. `{"a":"A","b":"B"}`). - uint8 internal constant TYPE_OBJECT = 2; - - /// @dev Type representing a number (e.g. `-1.23e+21`). - uint8 internal constant TYPE_NUMBER = 3; - - /// @dev Type representing a string (e.g. `"hello"`). - uint8 internal constant TYPE_STRING = 4; - - /// @dev Type representing a boolean (i.e. `true` or `false`). - uint8 internal constant TYPE_BOOLEAN = 5; - - /// @dev Type representing null (i.e. `null`). - uint8 internal constant TYPE_NULL = 6; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A pointer to a parsed JSON node. - struct Item { - // Do NOT modify the `_data` directly. - uint256 _data; - } - - // Private constants for packing `_data`. - - uint256 private constant _BITPOS_STRING = 32 * 7 - 8; - uint256 private constant _BITPOS_KEY_LENGTH = 32 * 6 - 8; - uint256 private constant _BITPOS_KEY = 32 * 5 - 8; - uint256 private constant _BITPOS_VALUE_LENGTH = 32 * 4 - 8; - uint256 private constant _BITPOS_VALUE = 32 * 3 - 8; - uint256 private constant _BITPOS_CHILD = 32 * 2 - 8; - uint256 private constant _BITPOS_SIBLING_OR_PARENT = 32 * 1 - 8; - uint256 private constant _BITMASK_POINTER = 0xffffffff; - uint256 private constant _BITMASK_TYPE = 7; - uint256 private constant _KEY_INITED = 1 << 3; - uint256 private constant _VALUE_INITED = 1 << 4; - uint256 private constant _CHILDREN_INITED = 1 << 5; - uint256 private constant _PARENT_IS_ARRAY = 1 << 6; - uint256 private constant _PARENT_IS_OBJECT = 1 << 7; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* JSON PARSING OPERATION */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Parses the JSON string `s`, and returns the root. - /// Reverts if `s` is not a valid JSON as specified in RFC 8259. - /// Object items WILL simply contain all their children, inclusive of repeated keys, - /// in the same order which they appear in the JSON string. - /// - /// Note: For efficiency, this function WILL NOT make a copy of `s`. - /// The parsed tree WILL contain offsets to `s`. - /// Do NOT pass in a string that WILL be modified later on. - function parse(string memory s) internal pure returns (Item memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, result) // We will use our own allocation instead. - } - bytes32 r = _query(_toInput(s), 255); - /// @solidity memory-safe-assembly - assembly { - result := r - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* JSON ITEM OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Note: - // - An item is a node in the JSON tree. - // - The value of a string item WILL be double-quoted, JSON encoded. - // - We make a distinction between `index` and `key`. - // - Items in arrays are located by `index` (uint256). - // - Items in objects are located by `key` (string). - // - Keys are always strings, double-quoted, JSON encoded. - // - // These design choices are made to balance between efficiency and ease-of-use. - - /// @dev Returns the string value of the item. - /// This is its exact string representation in the original JSON string. - /// The returned string WILL have leading and trailing whitespace trimmed. - /// All inner whitespace WILL be preserved, exactly as it is in the original JSON string. - /// If the item's type is string, the returned string WILL be double-quoted, JSON encoded. - /// - /// Note: This function lazily instantiates and caches the returned string. - /// Do NOT modify the returned string. - function value(Item memory item) internal pure returns (string memory result) { - bytes32 r = _query(_toInput(item), 0); - /// @solidity memory-safe-assembly - assembly { - result := r - } - } - - /// @dev Returns the index of the item in the array. - /// It the item's parent is not an array, returns 0. - function index(Item memory item) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - if and(mload(item), _PARENT_IS_ARRAY) { - result := and(_BITMASK_POINTER, shr(_BITPOS_KEY, mload(item))) - } - } - } - - /// @dev Returns the key of the item in the object. - /// It the item's parent is not an object, returns an empty string. - /// The returned string WILL be double-quoted, JSON encoded. - /// - /// Note: This function lazily instantiates and caches the returned string. - /// Do NOT modify the returned string. - function key(Item memory item) internal pure returns (string memory result) { - if (item._data & _PARENT_IS_OBJECT != 0) { - bytes32 r = _query(_toInput(item), 1); - /// @solidity memory-safe-assembly - assembly { - result := r - } - } - } - - /// @dev Returns the key of the item in the object. - /// It the item is neither an array nor object, returns an empty array. - /// - /// Note: This function lazily instantiates and caches the returned array. - /// Do NOT modify the returned array. - function children(Item memory item) internal pure returns (Item[] memory result) { - bytes32 r = _query(_toInput(item), 3); - /// @solidity memory-safe-assembly - assembly { - result := r - } - } - - /// @dev Returns the number of children. - /// It the item is neither an array nor object, returns zero. - function size(Item memory item) internal pure returns (uint256 result) { - bytes32 r = _query(_toInput(item), 3); - /// @solidity memory-safe-assembly - assembly { - result := mload(r) - } - } - - /// @dev Returns the item at index `i` for (array). - /// If `item` is not an array, the result's type WILL be undefined. - /// If there is no item with the index, the result's type WILL be undefined. - function at(Item memory item, uint256 i) internal pure returns (Item memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, result) // Free the default allocation. We'll allocate manually. - } - bytes32 r = _query(_toInput(item), 3); - /// @solidity memory-safe-assembly - assembly { - result := mload(add(add(r, 0x20), shl(5, i))) - if iszero(and(lt(i, mload(r)), eq(and(mload(item), _BITMASK_TYPE), TYPE_ARRAY))) { - result := 0x60 // Reset to the zero pointer. - } - } - } - - /// @dev Returns the item at key `k` for (object). - /// If `item` is not an object, the result's type WILL be undefined. - /// The key MUST be double-quoted, JSON encoded. This is for efficiency reasons. - /// - Correct : `item.at('"k"')`. - /// - Wrong : `item.at("k")`. - /// For duplicated keys, the last item with the key WILL be returned. - /// If there is no item with the key, the result's type WILL be undefined. - function at(Item memory item, string memory k) internal pure returns (Item memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, result) // Free the default allocation. We'll allocate manually. - result := 0x60 // Initialize to the zero pointer. - } - if (isObject(item)) { - bytes32 kHash = keccak256(bytes(k)); - Item[] memory r = children(item); - // We'll just do a linear search. The alternatives are very bloated. - for (uint256 i = r.length << 5; i != 0;) { - /// @solidity memory-safe-assembly - assembly { - item := mload(add(r, i)) - i := sub(i, 0x20) - } - if (keccak256(bytes(key(item))) != kHash) continue; - result = item; - break; - } - } - } - - /// @dev Returns the item's type. - function getType(Item memory item) internal pure returns (uint8 result) { - result = uint8(item._data & _BITMASK_TYPE); - } - - /// Note: All types are mutually exclusive. - - /// @dev Returns whether the item is of type undefined. - function isUndefined(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_UNDEFINED; - } - - /// @dev Returns whether the item is of type array. - function isArray(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_ARRAY; - } - - /// @dev Returns whether the item is of type object. - function isObject(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_OBJECT; - } - - /// @dev Returns whether the item is of type number. - function isNumber(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_NUMBER; - } - - /// @dev Returns whether the item is of type string. - function isString(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_STRING; - } - - /// @dev Returns whether the item is of type boolean. - function isBoolean(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_BOOLEAN; - } - - /// @dev Returns whether the item is of type null. - function isNull(Item memory item) internal pure returns (bool result) { - result = item._data & _BITMASK_TYPE == TYPE_NULL; - } - - /// @dev Returns the item's parent. - /// If the item does not have a parent, the result's type will be undefined. - function parent(Item memory item) internal pure returns (Item memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, result) // Free the default allocation. We've already allocated. - result := and(shr(_BITPOS_SIBLING_OR_PARENT, mload(item)), _BITMASK_POINTER) - if iszero(result) { result := 0x60 } // Reset to the zero pointer. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* UTILITY FUNCTIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Parses an unsigned integer from a string (in decimal, i.e. base 10). - /// Reverts if `s` is not a valid uint256 string matching the RegEx `^[0-9]+$`, - /// or if the parsed number is too big for a uint256. - function parseUint(string memory s) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - let n := mload(s) - let preMulOverflowThres := div(not(0), 10) - for { let i := 0 } 1 {} { - i := add(i, 1) - let digit := sub(and(mload(add(s, i)), 0xff), 48) - let mulOverflowed := gt(result, preMulOverflowThres) - let product := mul(10, result) - result := add(product, digit) - n := mul(n, iszero(or(or(mulOverflowed, lt(result, product)), gt(digit, 9)))) - if iszero(lt(i, n)) { break } - } - if iszero(n) { - mstore(0x00, 0x10182796) // `ParsingFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Parses a signed integer from a string (in decimal, i.e. base 10). - /// Reverts if `s` is not a valid int256 string matching the RegEx `^[+-]?[0-9]+$`, - /// or if the parsed number is too big for a int256. - function parseInt(string memory s) internal pure returns (int256 result) { - uint256 n = bytes(s).length; - uint256 sign; - uint256 isNegative; - /// @solidity memory-safe-assembly - assembly { - if n { - let c := and(mload(add(s, 1)), 0xff) - isNegative := eq(c, 45) - if or(eq(c, 43), isNegative) { - sign := c - s := add(s, 1) - mstore(s, sub(n, 1)) - } - if iszero(or(sign, lt(sub(c, 48), 10))) { s := 0x60 } - } - } - uint256 x = parseUint(s); - /// @solidity memory-safe-assembly - assembly { - if shr(255, x) { - mstore(0x00, 0x10182796) // `ParsingFailed()`. - revert(0x1c, 0x04) - } - if sign { - mstore(s, sign) - s := sub(s, 1) - mstore(s, n) - } - result := xor(x, mul(xor(x, add(not(x), 1)), isNegative)) - } - } - - /// @dev Parses an unsigned integer from a string (in hexadecimal, i.e. base 16). - /// Reverts if `s` is not a valid uint256 hex string matching the RegEx - /// `^(0[xX])?[0-9a-fA-F]+$`, or if the parsed number is too big for a uint256. - function parseUintFromHex(string memory s) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - let n := mload(s) - // Skip two if starts with '0x' or '0X'. - let i := shl(1, and(eq(0x3078, or(shr(240, mload(add(s, 0x20))), 0x20)), gt(n, 1))) - for {} 1 {} { - i := add(i, 1) - let c := - byte( - and(0x1f, shr(and(mload(add(s, i)), 0xff), 0x3e4088843e41bac000000000000)), - 0x3010a071000000b0104040208000c05090d060e0f - ) - n := mul(n, iszero(or(iszero(c), shr(252, result)))) - result := add(shl(4, result), sub(c, 1)) - if iszero(lt(i, n)) { break } - } - if iszero(n) { - mstore(0x00, 0x10182796) // `ParsingFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Decodes a JSON encoded string. - /// The string MUST be double-quoted, JSON encoded. - /// Reverts if the string is invalid. - /// As you can see, it's pretty complex for a deceptively simple looking task. - function decodeString(string memory s) internal pure returns (string memory result) { - /// @solidity memory-safe-assembly - assembly { - function fail() { - mstore(0x00, 0x10182796) // `ParsingFailed()`. - revert(0x1c, 0x04) - } - - function decodeUnicodeEscapeSequence(pIn_, end_) -> _unicode, _pOut { - _pOut := add(pIn_, 4) - let b_ := iszero(gt(_pOut, end_)) - let t_ := mload(pIn_) // Load the whole word. - for { let i_ := 0 } iszero(eq(i_, 4)) { i_ := add(i_, 1) } { - let c_ := sub(byte(i_, t_), 48) - if iszero(and(shr(c_, 0x7e0000007e03ff), b_)) { fail() } // Not hexadecimal. - c_ := sub(c_, add(mul(gt(c_, 16), 7), shl(5, gt(c_, 48)))) - _unicode := add(shl(4, _unicode), c_) - } - } - - function decodeUnicodeCodePoint(pIn_, end_) -> _unicode, _pOut { - _unicode, _pOut := decodeUnicodeEscapeSequence(pIn_, end_) - if iszero(or(lt(_unicode, 0xd800), gt(_unicode, 0xdbff))) { - let t_ := mload(_pOut) // Load the whole word. - end_ := mul(end_, eq(shr(240, t_), 0x5c75)) // Fail if not starting with '\\u'. - t_, _pOut := decodeUnicodeEscapeSequence(add(_pOut, 2), end_) - _unicode := add(0x10000, add(shl(10, and(0x3ff, _unicode)), and(0x3ff, t_))) - } - } - - function appendCodePointAsUTF8(pIn_, c_) -> _pOut { - if iszero(gt(c_, 0x7f)) { - mstore8(pIn_, c_) - _pOut := add(pIn_, 1) - leave - } - mstore8(0x1f, c_) - mstore8(0x1e, shr(6, c_)) - if iszero(gt(c_, 0x7ff)) { - mstore(pIn_, shl(240, or(0xc080, and(0x1f3f, mload(0x00))))) - _pOut := add(pIn_, 2) - leave - } - mstore8(0x1d, shr(12, c_)) - if iszero(gt(c_, 0xffff)) { - mstore(pIn_, shl(232, or(0xe08080, and(0x0f3f3f, mload(0x00))))) - _pOut := add(pIn_, 3) - leave - } - mstore8(0x1c, shr(18, c_)) - mstore(pIn_, shl(224, or(0xf0808080, and(0x073f3f3f, mload(0x00))))) - _pOut := add(pIn_, shl(2, lt(c_, 0x110000))) - } - - function chr(p_) -> _c { - _c := byte(0, mload(p_)) - } - - let n := mload(s) - let end := add(add(s, n), 0x1f) - if iszero(and(gt(n, 1), eq(0x2222, or(and(0xff00, mload(add(s, 2))), chr(end))))) { - fail() // Fail if not double-quoted. - } - let out := add(mload(0x40), 0x20) - for { let curr := add(s, 0x21) } iszero(eq(curr, end)) {} { - let c := chr(curr) - curr := add(curr, 1) - // Not '\\'. - if iszero(eq(c, 92)) { - // Not '"'. - if iszero(eq(c, 34)) { - mstore8(out, c) - out := add(out, 1) - continue - } - curr := end - } - if iszero(eq(curr, end)) { - let escape := chr(curr) - curr := add(curr, 1) - // '"', '/', '\\'. - if and(shr(escape, 0x100000000000800400000000), 1) { - mstore8(out, escape) - out := add(out, 1) - continue - } - // 'u'. - if eq(escape, 117) { - escape, curr := decodeUnicodeCodePoint(curr, end) - out := appendCodePointAsUTF8(out, escape) - continue - } - // `{'b':'\b', 'f':'\f', 'n':'\n', 'r':'\r', 't':'\t'}`. - escape := byte(sub(escape, 85), 0x080000000c000000000000000a0000000d0009) - if escape { - mstore8(out, escape) - out := add(out, 1) - continue - } - } - fail() - break - } - mstore(out, 0) // Zeroize the last slot. - result := mload(0x40) - mstore(result, sub(out, add(result, 0x20))) // Store the length. - mstore(0x40, add(out, 0x20)) // Allocate the memory. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Performs a query on the input with the given mode. - function _query(bytes32 input, uint256 mode) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - function fail() { - mstore(0x00, 0x10182796) // `ParsingFailed()`. - revert(0x1c, 0x04) - } - - function chr(p_) -> _c { - _c := byte(0, mload(p_)) - } - - function skipWhitespace(pIn_, end_) -> _pOut { - for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } { - if iszero(and(shr(chr(_pOut), 0x100002600), 1)) { leave } // Not in ' \n\r\t'. - } - } - - function setP(packed_, bitpos_, p_) -> _packed { - // Perform an out-of-gas revert if `p_` exceeds `_BITMASK_POINTER`. - returndatacopy(returndatasize(), returndatasize(), gt(p_, _BITMASK_POINTER)) - _packed := or(and(not(shl(bitpos_, _BITMASK_POINTER)), packed_), shl(bitpos_, p_)) - } - - function getP(packed_, bitpos_) -> _p { - _p := and(_BITMASK_POINTER, shr(bitpos_, packed_)) - } - - function mallocItem(s_, packed_, pStart_, pCurr_, type_) -> _item { - _item := mload(0x40) - // forgefmt: disable-next-item - packed_ := setP(setP(packed_, _BITPOS_VALUE, sub(pStart_, add(s_, 0x20))), - _BITPOS_VALUE_LENGTH, sub(pCurr_, pStart_)) - mstore(_item, or(packed_, type_)) - mstore(0x40, add(_item, 0x20)) // Allocate memory. - } - - function parseValue(s_, sibling_, pIn_, end_) -> _item, _pOut { - let packed_ := setP(mload(0x00), _BITPOS_SIBLING_OR_PARENT, sibling_) - _pOut := skipWhitespace(pIn_, end_) - if iszero(lt(_pOut, end_)) { leave } - for { let c_ := chr(_pOut) } 1 {} { - // If starts with '"'. - if eq(c_, 34) { - let pStart_ := _pOut - _pOut := parseStringSub(s_, packed_, _pOut, end_) - _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_STRING) - break - } - // If starts with '['. - if eq(c_, 91) { - _item, _pOut := parseArray(s_, packed_, _pOut, end_) - break - } - // If starts with '{'. - if eq(c_, 123) { - _item, _pOut := parseObject(s_, packed_, _pOut, end_) - break - } - // If starts with any in '0123456789-'. - if and(shr(c_, shl(45, 0x1ff9)), 1) { - _item, _pOut := parseNumber(s_, packed_, _pOut, end_) - break - } - if iszero(gt(add(_pOut, 4), end_)) { - let pStart_ := _pOut - let w_ := shr(224, mload(_pOut)) - // 'true' in hex format. - if eq(w_, 0x74727565) { - _pOut := add(_pOut, 4) - _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN) - break - } - // 'null' in hex format. - if eq(w_, 0x6e756c6c) { - _pOut := add(_pOut, 4) - _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_NULL) - break - } - } - if iszero(gt(add(_pOut, 5), end_)) { - let pStart_ := _pOut - let w_ := shr(216, mload(_pOut)) - // 'false' in hex format. - if eq(w_, 0x66616c7365) { - _pOut := add(_pOut, 5) - _item := mallocItem(s_, packed_, pStart_, _pOut, TYPE_BOOLEAN) - break - } - } - fail() - break - } - _pOut := skipWhitespace(_pOut, end_) - } - - function parseArray(s_, packed_, pIn_, end_) -> _item, _pOut { - let j_ := 0 - for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } { - if iszero(lt(_pOut, end_)) { fail() } - if iszero(_item) { - _pOut := skipWhitespace(_pOut, end_) - if eq(chr(_pOut), 93) { break } // ']'. - } - _item, _pOut := parseValue(s_, _item, _pOut, end_) - if _item { - // forgefmt: disable-next-item - mstore(_item, setP(or(_PARENT_IS_ARRAY, mload(_item)), - _BITPOS_KEY, j_)) - j_ := add(j_, 1) - let c_ := chr(_pOut) - if eq(c_, 93) { break } // ']'. - if eq(c_, 44) { continue } // ','. - } - _pOut := end_ - } - _pOut := add(_pOut, 1) - packed_ := setP(packed_, _BITPOS_CHILD, _item) - _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_ARRAY) - } - - function parseObject(s_, packed_, pIn_, end_) -> _item, _pOut { - for { _pOut := add(pIn_, 1) } 1 { _pOut := add(_pOut, 1) } { - if iszero(lt(_pOut, end_)) { fail() } - if iszero(_item) { - _pOut := skipWhitespace(_pOut, end_) - if eq(chr(_pOut), 125) { break } // '}'. - } - _pOut := skipWhitespace(_pOut, end_) - let pKeyStart_ := _pOut - let pKeyEnd_ := parseStringSub(s_, _item, _pOut, end_) - _pOut := skipWhitespace(pKeyEnd_, end_) - // If ':'. - if eq(chr(_pOut), 58) { - _item, _pOut := parseValue(s_, _item, add(_pOut, 1), end_) - if _item { - // forgefmt: disable-next-item - mstore(_item, setP(setP(or(_PARENT_IS_OBJECT, mload(_item)), - _BITPOS_KEY_LENGTH, sub(pKeyEnd_, pKeyStart_)), - _BITPOS_KEY, sub(pKeyStart_, add(s_, 0x20)))) - let c_ := chr(_pOut) - if eq(c_, 125) { break } // '}'. - if eq(c_, 44) { continue } // ','. - } - } - _pOut := end_ - } - _pOut := add(_pOut, 1) - packed_ := setP(packed_, _BITPOS_CHILD, _item) - _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_OBJECT) - } - - function checkStringU(p_, o_) { - // If not in '0123456789abcdefABCDEF', revert. - if iszero(and(shr(sub(chr(add(p_, o_)), 48), 0x7e0000007e03ff), 1)) { fail() } - if iszero(eq(o_, 5)) { checkStringU(p_, add(o_, 1)) } - } - - function parseStringSub(s_, packed_, pIn_, end_) -> _pOut { - if iszero(lt(pIn_, end_)) { fail() } - for { _pOut := add(pIn_, 1) } 1 {} { - let c_ := chr(_pOut) - if eq(c_, 34) { break } // '"'. - // Not '\'. - if iszero(eq(c_, 92)) { - _pOut := add(_pOut, 1) - continue - } - c_ := chr(add(_pOut, 1)) - // '"', '\', '//', 'b', 'f', 'n', 'r', 't'. - if and(shr(sub(c_, 34), 0x510110400000000002001), 1) { - _pOut := add(_pOut, 2) - continue - } - // 'u'. - if eq(c_, 117) { - checkStringU(_pOut, 2) - _pOut := add(_pOut, 6) - continue - } - _pOut := end_ - break - } - if iszero(lt(_pOut, end_)) { fail() } - _pOut := add(_pOut, 1) - } - - function skip0To9s(pIn_, end_, atLeastOne_) -> _pOut { - for { _pOut := pIn_ } 1 { _pOut := add(_pOut, 1) } { - if iszero(lt(sub(chr(_pOut), 48), 10)) { break } // Not '0'..'9'. - } - if and(atLeastOne_, eq(pIn_, _pOut)) { fail() } - } - - function parseNumber(s_, packed_, pIn_, end_) -> _item, _pOut { - _pOut := pIn_ - if eq(chr(_pOut), 45) { _pOut := add(_pOut, 1) } // '-'. - if iszero(lt(sub(chr(_pOut), 48), 10)) { fail() } // Not '0'..'9'. - let c_ := chr(_pOut) - _pOut := add(_pOut, 1) - if iszero(eq(c_, 48)) { _pOut := skip0To9s(_pOut, end_, 0) } // Not '0'. - if eq(chr(_pOut), 46) { _pOut := skip0To9s(add(_pOut, 1), end_, 1) } // '.'. - let t_ := mload(_pOut) - // 'E', 'e'. - if eq(or(0x20, byte(0, t_)), 101) { - // forgefmt: disable-next-item - _pOut := skip0To9s(add(byte(sub(byte(1, t_), 14), 0x010001), // '+', '-'. - add(_pOut, 1)), end_, 1) - } - _item := mallocItem(s_, packed_, pIn_, _pOut, TYPE_NUMBER) - } - - function copyStr(s_, offset_, len_) -> _sCopy { - _sCopy := mload(0x40) - s_ := add(s_, offset_) - let w_ := not(0x1f) - for { let i_ := and(add(len_, 0x1f), w_) } 1 {} { - mstore(add(_sCopy, i_), mload(add(s_, i_))) - i_ := add(i_, w_) // `sub(i_, 0x20)`. - if iszero(i_) { break } - } - mstore(_sCopy, len_) // Copy the length. - mstore(add(add(_sCopy, 0x20), len_), 0) // Zeroize the last slot. - mstore(0x40, add(add(_sCopy, 0x40), len_)) // Allocate memory. - } - - function value(item_) -> _value { - let packed_ := mload(item_) - _value := getP(packed_, _BITPOS_VALUE) // The offset in the string. - if iszero(and(_VALUE_INITED, packed_)) { - let s_ := getP(packed_, _BITPOS_STRING) - _value := copyStr(s_, _value, getP(packed_, _BITPOS_VALUE_LENGTH)) - packed_ := setP(packed_, _BITPOS_VALUE, _value) - mstore(s_, or(_VALUE_INITED, packed_)) - } - } - - function children(item_) -> _arr { - _arr := 0x60 // Initialize to the zero pointer. - let packed_ := mload(item_) - for {} iszero(gt(and(_BITMASK_TYPE, packed_), TYPE_OBJECT)) {} { - if or(iszero(packed_), iszero(item_)) { break } - if and(packed_, _CHILDREN_INITED) { - _arr := getP(packed_, _BITPOS_CHILD) - break - } - _arr := mload(0x40) - let o_ := add(_arr, 0x20) - for { let h_ := getP(packed_, _BITPOS_CHILD) } h_ {} { - mstore(o_, h_) - let q_ := mload(h_) - let y_ := getP(q_, _BITPOS_SIBLING_OR_PARENT) - mstore(h_, setP(q_, _BITPOS_SIBLING_OR_PARENT, item_)) - h_ := y_ - o_ := add(o_, 0x20) - } - let w_ := not(0x1f) - let n_ := add(w_, sub(o_, _arr)) - mstore(_arr, shr(5, n_)) - mstore(0x40, o_) // Allocate memory. - packed_ := setP(packed_, _BITPOS_CHILD, _arr) - mstore(item_, or(_CHILDREN_INITED, packed_)) - // Reverse the array. - if iszero(lt(n_, 0x40)) { - let lo_ := add(_arr, 0x20) - let hi_ := add(_arr, n_) - for {} 1 {} { - let temp_ := mload(lo_) - mstore(lo_, mload(hi_)) - mstore(hi_, temp_) - hi_ := add(hi_, w_) - lo_ := add(lo_, 0x20) - if iszero(lt(lo_, hi_)) { break } - } - } - break - } - } - - function getStr(item_, bitpos_, bitposLength_, bitmaskInited_) -> _result { - _result := 0x60 // Initialize to the zero pointer. - let packed_ := mload(item_) - if or(iszero(item_), iszero(packed_)) { leave } - _result := getP(packed_, bitpos_) - if iszero(and(bitmaskInited_, packed_)) { - let s_ := getP(packed_, _BITPOS_STRING) - _result := copyStr(s_, _result, getP(packed_, bitposLength_)) - mstore(item_, or(bitmaskInited_, setP(packed_, bitpos_, _result))) - } - } - - switch mode - // Get value. - case 0 { result := getStr(input, _BITPOS_VALUE, _BITPOS_VALUE_LENGTH, _VALUE_INITED) } - // Get key. - case 1 { result := getStr(input, _BITPOS_KEY, _BITPOS_KEY_LENGTH, _KEY_INITED) } - // Get children. - case 3 { result := children(input) } - // Parse. - default { - let p := add(input, 0x20) - let e := add(p, mload(input)) - if iszero(eq(p, e)) { - let c := chr(e) - mstore8(e, 34) // Place a '"' at the end to speed up parsing. - // The `34 << 248` makes `mallocItem` preserve '"' at the end. - mstore(0x00, setP(shl(248, 34), _BITPOS_STRING, input)) - result, p := parseValue(input, 0, p, e) - mstore8(e, c) // Restore the original char at the end. - } - if or(lt(p, e), iszero(result)) { fail() } - } - } - } - - /// @dev Casts the input to a bytes32. - function _toInput(string memory input) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - result := input - } - } - - /// @dev Casts the input to a bytes32. - function _toInput(Item memory input) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - result := input - } - } -} diff --git a/lib/solady/src/utils/LibBit.sol b/lib/solady/src/utils/LibBit.sol deleted file mode 100644 index 0c7dba5..0000000 --- a/lib/solady/src/utils/LibBit.sol +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for bit twiddling and boolean operations. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol) -/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html) -library LibBit { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* BIT TWIDDLING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Find last set. - /// Returns the index of the most significant bit of `x`, - /// counting from the least significant bit position. - /// If `x` is zero, returns 256. - function fls(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x))) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - // forgefmt: disable-next-item - r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), - 0x0706060506020504060203020504030106050205030304010505030400000000)) - } - } - - /// @dev Count leading zeros. - /// Returns the number of zeros preceding the most significant one bit. - /// If `x` is zero, returns 256. - function clz(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - // forgefmt: disable-next-item - r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)), - 0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x)) - } - } - - /// @dev Find first set. - /// Returns the index of the least significant bit of `x`, - /// counting from the least significant bit position. - /// If `x` is zero, returns 256. - /// Equivalent to `ctz` (count trailing zeros), which gives - /// the number of zeros following the least significant one bit. - function ffs(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Isolate the least significant bit. - let b := and(x, add(not(x), 1)) - - r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b))) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, b)))) - - // For the remaining 32 bits, use a De Bruijn lookup. - // forgefmt: disable-next-item - r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f), - 0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405)) - } - } - - /// @dev Returns the number of set bits in `x`. - function popCount(uint256 x) internal pure returns (uint256 c) { - /// @solidity memory-safe-assembly - assembly { - let max := not(0) - let isMax := eq(x, max) - x := sub(x, and(shr(1, x), div(max, 3))) - x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5))) - x := and(add(x, shr(4, x)), div(max, 17)) - c := or(shl(8, isMax), shr(248, mul(x, div(max, 255)))) - } - } - - /// @dev Returns whether `x` is a power of 2. - function isPo2(uint256 x) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to `x && !(x & (x - 1))`. - result := iszero(add(and(x, sub(x, 1)), iszero(x))) - } - } - - /// @dev Returns `x` reversed at the bit level. - function reverseBits(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Computing masks on-the-fly reduces bytecode size by about 500 bytes. - let m := not(0) - r := x - for { let s := 128 } 1 {} { - m := xor(m, shl(s, m)) - r := or(and(shr(s, r), m), and(shl(s, r), not(m))) - s := shr(1, s) - if iszero(s) { break } - } - } - } - - /// @dev Returns `x` reversed at the byte level. - function reverseBytes(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Computing masks on-the-fly reduces bytecode size by about 200 bytes. - let m := not(0) - r := x - for { let s := 128 } 1 {} { - m := xor(m, shl(s, m)) - r := or(and(shr(s, r), m), and(shl(s, r), not(m))) - s := shr(1, s) - if eq(s, 4) { break } - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* BOOLEAN OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // A Solidity bool on the stack or memory is represented as a 256-bit word. - // Non-zero values are true, zero is false. - // A clean bool is either 0 (false) or 1 (true) under the hood. - // Usually, if not always, the bool result of a regular Solidity expression, - // or the argument of a public/external function will be a clean bool. - // You can usually use the raw variants for more performance. - // If uncertain, test (best with exact compiler settings). - // Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s). - - /// @dev Returns `x & y`. Inputs must be clean. - function rawAnd(bool x, bool y) internal pure returns (bool z) { - /// @solidity memory-safe-assembly - assembly { - z := and(x, y) - } - } - - /// @dev Returns `x & y`. - function and(bool x, bool y) internal pure returns (bool z) { - /// @solidity memory-safe-assembly - assembly { - z := and(iszero(iszero(x)), iszero(iszero(y))) - } - } - - /// @dev Returns `x | y`. Inputs must be clean. - function rawOr(bool x, bool y) internal pure returns (bool z) { - /// @solidity memory-safe-assembly - assembly { - z := or(x, y) - } - } - - /// @dev Returns `x | y`. - function or(bool x, bool y) internal pure returns (bool z) { - /// @solidity memory-safe-assembly - assembly { - z := or(iszero(iszero(x)), iszero(iszero(y))) - } - } - - /// @dev Returns 1 if `b` is true, else 0. Input must be clean. - function rawToUint(bool b) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := b - } - } - - /// @dev Returns 1 if `b` is true, else 0. - function toUint(bool b) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := iszero(iszero(b)) - } - } -} diff --git a/lib/solady/src/utils/LibBitmap.sol b/lib/solady/src/utils/LibBitmap.sol deleted file mode 100644 index a333b5b..0000000 --- a/lib/solady/src/utils/LibBitmap.sol +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {LibBit} from "./LibBit.sol"; - -/// @notice Library for storage of packed unsigned booleans. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBitmap.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibBitmap.sol) -/// @author Modified from Solidity-Bits (https://github.com/estarriolvetch/solidity-bits/blob/main/contracts/BitMaps.sol) -library LibBitmap { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The constant returned when a bitmap scan does not find a result. - uint256 internal constant NOT_FOUND = type(uint256).max; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A bitmap in storage. - struct Bitmap { - mapping(uint256 => uint256) map; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the boolean value of the bit at `index` in `bitmap`. - function get(Bitmap storage bitmap, uint256 index) internal view returns (bool isSet) { - // It is better to set `isSet` to either 0 or 1, than zero vs non-zero. - // Both cost the same amount of gas, but the former allows the returned value - // to be reused without cleaning the upper bits. - uint256 b = (bitmap.map[index >> 8] >> (index & 0xff)) & 1; - /// @solidity memory-safe-assembly - assembly { - isSet := b - } - } - - /// @dev Updates the bit at `index` in `bitmap` to true. - function set(Bitmap storage bitmap, uint256 index) internal { - bitmap.map[index >> 8] |= (1 << (index & 0xff)); - } - - /// @dev Updates the bit at `index` in `bitmap` to false. - function unset(Bitmap storage bitmap, uint256 index) internal { - bitmap.map[index >> 8] &= ~(1 << (index & 0xff)); - } - - /// @dev Flips the bit at `index` in `bitmap`. - /// Returns the boolean result of the flipped bit. - function toggle(Bitmap storage bitmap, uint256 index) internal returns (bool newIsSet) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, bitmap.slot) - mstore(0x00, shr(8, index)) - let storageSlot := keccak256(0x00, 0x40) - let shift := and(index, 0xff) - let storageValue := xor(sload(storageSlot), shl(shift, 1)) - // It makes sense to return the `newIsSet`, - // as it allow us to skip an additional warm `sload`, - // and it costs minimal gas (about 15), - // which may be optimized away if the returned value is unused. - newIsSet := and(1, shr(shift, storageValue)) - sstore(storageSlot, storageValue) - } - } - - /// @dev Updates the bit at `index` in `bitmap` to `shouldSet`. - function setTo(Bitmap storage bitmap, uint256 index, bool shouldSet) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, bitmap.slot) - mstore(0x00, shr(8, index)) - let storageSlot := keccak256(0x00, 0x40) - let storageValue := sload(storageSlot) - let shift := and(index, 0xff) - sstore( - storageSlot, - // Unsets the bit at `shift` via `and`, then sets its new value via `or`. - or(and(storageValue, not(shl(shift, 1))), shl(shift, iszero(iszero(shouldSet)))) - ) - } - } - - /// @dev Consecutively sets `amount` of bits starting from the bit at `start`. - function setBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - let max := not(0) - let shift := and(start, 0xff) - mstore(0x20, bitmap.slot) - mstore(0x00, shr(8, start)) - if iszero(lt(add(shift, amount), 257)) { - let storageSlot := keccak256(0x00, 0x40) - sstore(storageSlot, or(sload(storageSlot), shl(shift, max))) - let bucket := add(mload(0x00), 1) - let bucketEnd := add(mload(0x00), shr(8, add(amount, shift))) - amount := and(add(amount, shift), 0xff) - shift := 0 - for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } { - mstore(0x00, bucket) - sstore(keccak256(0x00, 0x40), max) - } - mstore(0x00, bucket) - } - let storageSlot := keccak256(0x00, 0x40) - sstore(storageSlot, or(sload(storageSlot), shl(shift, shr(sub(256, amount), max)))) - } - } - - /// @dev Consecutively unsets `amount` of bits starting from the bit at `start`. - function unsetBatch(Bitmap storage bitmap, uint256 start, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - let shift := and(start, 0xff) - mstore(0x20, bitmap.slot) - mstore(0x00, shr(8, start)) - if iszero(lt(add(shift, amount), 257)) { - let storageSlot := keccak256(0x00, 0x40) - sstore(storageSlot, and(sload(storageSlot), not(shl(shift, not(0))))) - let bucket := add(mload(0x00), 1) - let bucketEnd := add(mload(0x00), shr(8, add(amount, shift))) - amount := and(add(amount, shift), 0xff) - shift := 0 - for {} iszero(eq(bucket, bucketEnd)) { bucket := add(bucket, 1) } { - mstore(0x00, bucket) - sstore(keccak256(0x00, 0x40), 0) - } - mstore(0x00, bucket) - } - let storageSlot := keccak256(0x00, 0x40) - sstore( - storageSlot, and(sload(storageSlot), not(shl(shift, shr(sub(256, amount), not(0))))) - ) - } - } - - /// @dev Returns number of set bits within a range by - /// scanning `amount` of bits starting from the bit at `start`. - function popCount(Bitmap storage bitmap, uint256 start, uint256 amount) - internal - view - returns (uint256 count) - { - unchecked { - uint256 bucket = start >> 8; - uint256 shift = start & 0xff; - if (!(amount + shift < 257)) { - count = LibBit.popCount(bitmap.map[bucket] >> shift); - uint256 bucketEnd = bucket + ((amount + shift) >> 8); - amount = (amount + shift) & 0xff; - shift = 0; - for (++bucket; bucket != bucketEnd; ++bucket) { - count += LibBit.popCount(bitmap.map[bucket]); - } - } - count += LibBit.popCount((bitmap.map[bucket] >> shift) << (256 - amount)); - } - } - - /// @dev Returns the index of the most significant set bit before the bit at `before`. - /// If no set bit is found, returns `NOT_FOUND`. - function findLastSet(Bitmap storage bitmap, uint256 before) - internal - view - returns (uint256 setBitIndex) - { - uint256 bucket; - uint256 bucketBits; - /// @solidity memory-safe-assembly - assembly { - setBitIndex := not(0) - bucket := shr(8, before) - mstore(0x00, bucket) - mstore(0x20, bitmap.slot) - let offset := and(0xff, not(before)) // `256 - (255 & before) - 1`. - bucketBits := shr(offset, shl(offset, sload(keccak256(0x00, 0x40)))) - if iszero(or(bucketBits, iszero(bucket))) { - for {} 1 {} { - bucket := add(bucket, setBitIndex) // `sub(bucket, 1)`. - mstore(0x00, bucket) - bucketBits := sload(keccak256(0x00, 0x40)) - if or(bucketBits, iszero(bucket)) { break } - } - } - } - if (bucketBits != 0) { - setBitIndex = (bucket << 8) | LibBit.fls(bucketBits); - /// @solidity memory-safe-assembly - assembly { - setBitIndex := or(setBitIndex, sub(0, gt(setBitIndex, before))) - } - } - } -} diff --git a/lib/solady/src/utils/LibClone.sol b/lib/solady/src/utils/LibClone.sol deleted file mode 100644 index abf3fbc..0000000 --- a/lib/solady/src/utils/LibClone.sol +++ /dev/null @@ -1,836 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Minimal proxy library. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol) -/// @author Minimal proxy by 0age (https://github.com/0age) -/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie -/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) -/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy) -/// -/// @dev Minimal proxy: -/// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime, -/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern, -/// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode. -/// -/// @dev Minimal proxy (PUSH0 variant): -/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai. -/// It is optimized first for minimal runtime gas, then for minimal bytecode. -/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as -/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai. -/// Please use with caution. -/// -/// @dev Clones with immutable args (CWIA): -/// The implementation of CWIA here implements a `receive()` method that emits the -/// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata, -/// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards -/// composability. The minimal proxy implementation does not offer this feature. -/// -/// @dev Minimal ERC1967 proxy: -/// An minimal ERC1967 proxy, intended to be upgraded with UUPS. -/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. -library LibClone { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unable to deploy the clone. - error DeploymentFailed(); - - /// @dev The salt must start with either the zero address or `by`. - error SaltDoesNotStartWith(); - - /// @dev The ETH transfer has failed. - error ETHTransferFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MINIMAL PROXY OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deploys a clone of `implementation`. - function clone(address implementation) internal returns (address instance) { - instance = clone(0, implementation); - } - - /// @dev Deploys a clone of `implementation`. - function clone(uint256 value, address implementation) internal returns (address instance) { - /// @solidity memory-safe-assembly - assembly { - /** - * --------------------------------------------------------------------------+ - * CREATION (9 bytes) | - * --------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * --------------------------------------------------------------------------| - * 60 runSize | PUSH1 runSize | r | | - * 3d | RETURNDATASIZE | 0 r | | - * 81 | DUP2 | r 0 r | | - * 60 offset | PUSH1 offset | o r 0 r | | - * 3d | RETURNDATASIZE | 0 o r 0 r | | - * 39 | CODECOPY | 0 r | [0..runSize): runtime code | - * f3 | RETURN | | [0..runSize): runtime code | - * --------------------------------------------------------------------------| - * RUNTIME (44 bytes) | - * --------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * --------------------------------------------------------------------------| - * | - * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | 0 | | - * 3d | RETURNDATASIZE | 0 0 | | - * 3d | RETURNDATASIZE | 0 0 0 | | - * 3d | RETURNDATASIZE | 0 0 0 0 | | - * | - * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 0 0 | | - * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | | - * 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | | - * 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata | - * | - * ::: delegate call to the implementation contract :::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata | - * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | - * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | - * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | - * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | - * | - * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata | - * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata | - * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata | - * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata | - * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | - * | - * 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata | - * 57 | JUMPI | 0 rds | [0..rds): returndata | - * | - * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * fd | REVERT | | [0..rds): returndata | - * | - * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | 0 rds | [0..rds): returndata | - * f3 | RETURN | | [0..rds): returndata | - * --------------------------------------------------------------------------+ - */ - - mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) - mstore(0x14, implementation) - mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) - instance := create(value, 0x0c, 0x35) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Deploys a deterministic clone of `implementation` with `salt`. - function cloneDeterministic(address implementation, bytes32 salt) - internal - returns (address instance) - { - instance = cloneDeterministic(0, implementation, salt); - } - - /// @dev Deploys a deterministic clone of `implementation` with `salt`. - function cloneDeterministic(uint256 value, address implementation, bytes32 salt) - internal - returns (address instance) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) - mstore(0x14, implementation) - mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) - instance := create2(value, 0x0c, 0x35, salt) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Returns the initialization code hash of the clone of `implementation`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash(address implementation) internal pure returns (bytes32 hash) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) - mstore(0x14, implementation) - mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) - hash := keccak256(0x0c, 0x35) - mstore(0x21, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Returns the address of the deterministic clone of `implementation`, - /// with `salt` by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) - internal - pure - returns (address predicted) - { - bytes32 hash = initCodeHash(implementation); - predicted = predictDeterministicAddress(hash, salt, deployer); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Deploys a PUSH0 clone of `implementation`. - function clone_PUSH0(address implementation) internal returns (address instance) { - instance = clone_PUSH0(0, implementation); - } - - /// @dev Deploys a PUSH0 clone of `implementation`. - function clone_PUSH0(uint256 value, address implementation) - internal - returns (address instance) - { - /// @solidity memory-safe-assembly - assembly { - /** - * --------------------------------------------------------------------------+ - * CREATION (9 bytes) | - * --------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * --------------------------------------------------------------------------| - * 60 runSize | PUSH1 runSize | r | | - * 5f | PUSH0 | 0 r | | - * 81 | DUP2 | r 0 r | | - * 60 offset | PUSH1 offset | o r 0 r | | - * 5f | PUSH0 | 0 o r 0 r | | - * 39 | CODECOPY | 0 r | [0..runSize): runtime code | - * f3 | RETURN | | [0..runSize): runtime code | - * --------------------------------------------------------------------------| - * RUNTIME (45 bytes) | - * --------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * --------------------------------------------------------------------------| - * | - * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | - * 5f | PUSH0 | 0 | | - * 5f | PUSH0 | 0 0 | | - * | - * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 | | - * 5f | PUSH0 | 0 cds 0 0 | | - * 5f | PUSH0 | 0 0 cds 0 0 | | - * 37 | CALLDATACOPY | 0 0 | [0..cds): calldata | - * | - * ::: delegate call to the implementation contract :::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata | - * 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata | - * 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata | - * 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata | - * f4 | DELEGATECALL | success | [0..cds): calldata | - * | - * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds success | [0..cds): calldata | - * 5f | PUSH0 | 0 rds success | [0..cds): calldata | - * 5f | PUSH0 | 0 0 rds success | [0..cds): calldata | - * 3e | RETURNDATACOPY | success | [0..rds): returndata | - * | - * 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata | - * 57 | JUMPI | | [0..rds): returndata | - * | - * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds | [0..rds): returndata | - * 5f | PUSH0 | 0 rds | [0..rds): returndata | - * fd | REVERT | | [0..rds): returndata | - * | - * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | | [0..rds): returndata | - * 3d | RETURNDATASIZE | rds | [0..rds): returndata | - * 5f | PUSH0 | 0 rds | [0..rds): returndata | - * f3 | RETURN | | [0..rds): returndata | - * --------------------------------------------------------------------------+ - */ - - mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 - mstore(0x14, implementation) // 20 - mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 - instance := create(value, 0x0e, 0x36) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. - function cloneDeterministic_PUSH0(address implementation, bytes32 salt) - internal - returns (address instance) - { - instance = cloneDeterministic_PUSH0(0, implementation, salt); - } - - /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. - function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt) - internal - returns (address instance) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 - mstore(0x14, implementation) // 20 - mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 - instance := create2(value, 0x0e, 0x36, salt) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 - mstore(0x14, implementation) // 20 - mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 - hash := keccak256(0x0e, 0x36) - mstore(0x24, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Returns the address of the deterministic PUSH0 clone of `implementation`, - /// with `salt` by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress_PUSH0( - address implementation, - bytes32 salt, - address deployer - ) internal pure returns (address predicted) { - bytes32 hash = initCodeHash_PUSH0(implementation); - predicted = predictDeterministicAddress(hash, salt, deployer); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CLONES WITH IMMUTABLE ARGS OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Note: This implementation of CWIA differs from the original implementation. - // If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`. - - /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`. - function clone(address implementation, bytes memory data) internal returns (address instance) { - instance = clone(0, implementation, data); - } - - /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`. - function clone(uint256 value, address implementation, bytes memory data) - internal - returns (address instance) - { - assembly { - // Compute the boundaries of the data and cache the memory slots around it. - let mBefore3 := mload(sub(data, 0x60)) - let mBefore2 := mload(sub(data, 0x40)) - let mBefore1 := mload(sub(data, 0x20)) - let dataLength := mload(data) - let dataEnd := add(add(data, 0x20), dataLength) - let mAfter1 := mload(dataEnd) - - // +2 bytes for telling how much data there is appended to the call. - let extraLength := add(dataLength, 2) - // The `creationSize` is `extraLength + 108` - // The `runSize` is `creationSize - 10`. - - /** - * ---------------------------------------------------------------------------------------------------+ - * CREATION (10 bytes) | - * ---------------------------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * ---------------------------------------------------------------------------------------------------| - * 61 runSize | PUSH2 runSize | r | | - * 3d | RETURNDATASIZE | 0 r | | - * 81 | DUP2 | r 0 r | | - * 60 offset | PUSH1 offset | o r 0 r | | - * 3d | RETURNDATASIZE | 0 o r 0 r | | - * 39 | CODECOPY | 0 r | [0..runSize): runtime code | - * f3 | RETURN | | [0..runSize): runtime code | - * ---------------------------------------------------------------------------------------------------| - * RUNTIME (98 bytes + extraLength) | - * ---------------------------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * ---------------------------------------------------------------------------------------------------| - * | - * ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds | | - * 60 0x2c | PUSH1 0x2c | 0x2c cds | | - * 57 | JUMPI | | | - * 34 | CALLVALUE | cv | | - * 3d | RETURNDATASIZE | 0 cv | | - * 52 | MSTORE | | [0..0x20): callvalue | - * 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue | - * 59 | MSIZE | 0x20 sig | [0..0x20): callvalue | - * 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue | - * a1 | LOG1 | | [0..0x20): callvalue | - * 00 | STOP | | [0..0x20): callvalue | - * 5b | JUMPDEST | | | - * | - * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds | | - * 3d | RETURNDATASIZE | 0 cds | | - * 3d | RETURNDATASIZE | 0 0 cds | | - * 37 | CALLDATACOPY | | [0..cds): calldata | - * | - * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | 0 | [0..cds): calldata | - * 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata | - * 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata | - * 3d | RETURNDATASIZE | 0 0 0 0 | [0..cds): calldata | - * 61 extra | PUSH2 extra | e 0 0 0 0 | [0..cds): calldata | - * | - * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 80 | DUP1 | e e 0 0 0 0 | [0..cds): calldata | - * 60 0x62 | PUSH1 0x62 | 0x62 e e 0 0 0 0 | [0..cds): calldata | - * 36 | CALLDATASIZE | cds 0x62 e e 0 0 0 0 | [0..cds): calldata | - * 39 | CODECOPY | e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * | - * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 01 | ADD | cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 5a | GAS | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * | - * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | - * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | - * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | - * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | - * | - * 60 0x60 | PUSH1 0x60 | 0x60 success 0 rds | [0..rds): returndata | - * 57 | JUMPI | 0 rds | [0..rds): returndata | - * | - * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * fd | REVERT | | [0..rds): returndata | - * | - * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | 0 rds | [0..rds): returndata | - * f3 | RETURN | | [0..rds): returndata | - * ---------------------------------------------------------------------------------------------------+ - */ - - mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. - mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. - // Write the rest of the bytecode. - mstore( - sub(data, 0x21), - or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) - ) - // `keccak256("ReceiveETH(uint256)")` - mstore( - sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff - ) - mstore( - // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. - // The actual EVM limit may be smaller and may change over time. - sub(data, add(0x59, lt(extraLength, 0xff9e))), - or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) - ) - mstore(dataEnd, shl(0xf0, extraLength)) - - instance := create(value, sub(data, 0x4c), add(extraLength, 0x6c)) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - - // Restore the overwritten memory surrounding `data`. - mstore(dataEnd, mAfter1) - mstore(data, dataLength) - mstore(sub(data, 0x20), mBefore1) - mstore(sub(data, 0x40), mBefore2) - mstore(sub(data, 0x60), mBefore3) - } - } - - /// @dev Deploys a deterministic clone of `implementation` - /// with immutable arguments encoded in `data` and `salt`. - function cloneDeterministic(address implementation, bytes memory data, bytes32 salt) - internal - returns (address instance) - { - instance = cloneDeterministic(0, implementation, data, salt); - } - - /// @dev Deploys a deterministic clone of `implementation` - /// with immutable arguments encoded in `data` and `salt`. - function cloneDeterministic( - uint256 value, - address implementation, - bytes memory data, - bytes32 salt - ) internal returns (address instance) { - assembly { - // Compute the boundaries of the data and cache the memory slots around it. - let mBefore3 := mload(sub(data, 0x60)) - let mBefore2 := mload(sub(data, 0x40)) - let mBefore1 := mload(sub(data, 0x20)) - let dataLength := mload(data) - let dataEnd := add(add(data, 0x20), dataLength) - let mAfter1 := mload(dataEnd) - - // +2 bytes for telling how much data there is appended to the call. - let extraLength := add(dataLength, 2) - - mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. - mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. - // Write the rest of the bytecode. - mstore( - sub(data, 0x21), - or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) - ) - // `keccak256("ReceiveETH(uint256)")` - mstore( - sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff - ) - mstore( - // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. - // The actual EVM limit may be smaller and may change over time. - sub(data, add(0x59, lt(extraLength, 0xff9e))), - or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) - ) - mstore(dataEnd, shl(0xf0, extraLength)) - - instance := create2(value, sub(data, 0x4c), add(extraLength, 0x6c), salt) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - - // Restore the overwritten memory surrounding `data`. - mstore(dataEnd, mAfter1) - mstore(data, dataLength) - mstore(sub(data, 0x20), mBefore1) - mstore(sub(data, 0x40), mBefore2) - mstore(sub(data, 0x60), mBefore3) - } - } - - /// @dev Returns the initialization code hash of the clone of `implementation` - /// using immutable arguments encoded in `data`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash(address implementation, bytes memory data) - internal - pure - returns (bytes32 hash) - { - assembly { - // Compute the boundaries of the data and cache the memory slots around it. - let mBefore3 := mload(sub(data, 0x60)) - let mBefore2 := mload(sub(data, 0x40)) - let mBefore1 := mload(sub(data, 0x20)) - let dataLength := mload(data) - let dataEnd := add(add(data, 0x20), dataLength) - let mAfter1 := mload(dataEnd) - - // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b. - // The actual EVM limit may be smaller and may change over time. - returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b)) - - // +2 bytes for telling how much data there is appended to the call. - let extraLength := add(dataLength, 2) - - mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data. - mstore(sub(data, 0x0d), implementation) // Write the address of the implementation. - // Write the rest of the bytecode. - mstore( - sub(data, 0x21), - or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) - ) - // `keccak256("ReceiveETH(uint256)")` - mstore( - sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff - ) - mstore( - sub(data, 0x5a), - or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) - ) - mstore(dataEnd, shl(0xf0, extraLength)) - - hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c)) - - // Restore the overwritten memory surrounding `data`. - mstore(dataEnd, mAfter1) - mstore(data, dataLength) - mstore(sub(data, 0x20), mBefore1) - mstore(sub(data, 0x40), mBefore2) - mstore(sub(data, 0x60), mBefore3) - } - } - - /// @dev Returns the address of the deterministic clone of - /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress( - address implementation, - bytes memory data, - bytes32 salt, - address deployer - ) internal pure returns (address predicted) { - bytes32 hash = initCodeHash(implementation, data); - predicted = predictDeterministicAddress(hash, salt, deployer); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MINIMAL ERC1967 PROXY OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Note: The ERC1967 proxy here is intended to be upgraded with UUPS. - // This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic. - - /// @dev Deploys a minimal ERC1967 proxy with `implementation`. - function deployERC1967(address implementation) internal returns (address instance) { - instance = deployERC1967(0, implementation); - } - - /// @dev Deploys a minimal ERC1967 proxy with `implementation`. - function deployERC1967(uint256 value, address implementation) - internal - returns (address instance) - { - /// @solidity memory-safe-assembly - assembly { - /** - * ---------------------------------------------------------------------------------+ - * CREATION (34 bytes) | - * ---------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * ---------------------------------------------------------------------------------| - * 60 runSize | PUSH1 runSize | r | | - * 3d | RETURNDATASIZE | 0 r | | - * 81 | DUP2 | r 0 r | | - * 60 offset | PUSH1 offset | o r 0 r | | - * 3d | RETURNDATASIZE | 0 o r 0 r | | - * 39 | CODECOPY | 0 r | [0..runSize): runtime code | - * 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code | - * 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code | - * 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code | - * 55 | SSTORE | 0 r | [0..runSize): runtime code | - * f3 | RETURN | | [0..runSize): runtime code | - * ---------------------------------------------------------------------------------| - * RUNTIME (62 bytes) | - * ---------------------------------------------------------------------------------| - * Opcode | Mnemonic | Stack | Memory | - * ---------------------------------------------------------------------------------| - * | - * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: | - * 36 | CALLDATASIZE | cds | | - * 3d | RETURNDATASIZE | 0 cds | | - * 3d | RETURNDATASIZE | 0 0 cds | | - * 37 | CALLDATACOPY | | [0..calldatasize): calldata | - * | - * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | 0 | | - * 3d | RETURNDATASIZE | 0 0 | | - * 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata | - * 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata | - * 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata | - * 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata | - * 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata | - * f4 | DELEGATECALL | succ | [0..calldatasize): calldata | - * | - * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata | - * 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata | - * 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata | - * 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata | - * | - * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: | - * 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata | - * 57 | JUMPI | | [0..returndatasize): returndata | - * | - * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: | - * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | - * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | - * fd | REVERT | | [0..returndatasize): returndata | - * | - * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: | - * 5b | JUMPDEST | | [0..returndatasize): returndata | - * 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata | - * 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata | - * f3 | RETURN | | [0..returndatasize): returndata | - * ---------------------------------------------------------------------------------+ - */ - - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) - mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) - mstore(0x20, 0x6009) - mstore(0x1e, implementation) - mstore(0x0a, 0x603d3d8160223d3973) - instance := create(value, 0x21, 0x5f) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x40, m) // Restore the free memory pointer. - mstore(0x60, 0) // Restore the zero slot. - } - } - - /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. - function deployDeterministicERC1967(address implementation, bytes32 salt) - internal - returns (address instance) - { - instance = deployDeterministicERC1967(0, implementation, salt); - } - - /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`. - function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt) - internal - returns (address instance) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) - mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) - mstore(0x20, 0x6009) - mstore(0x1e, implementation) - mstore(0x0a, 0x603d3d8160223d3973) - instance := create2(value, 0x21, 0x5f, salt) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - mstore(0x40, m) // Restore the free memory pointer. - mstore(0x60, 0) // Restore the zero slot. - } - } - - /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. - /// Note: This method is intended for use in ERC4337 factories, - /// which are expected to NOT revert if the proxy is already deployed. - function createDeterministicERC1967(address implementation, bytes32 salt) - internal - returns (bool alreadyDeployed, address instance) - { - return createDeterministicERC1967(0, implementation, salt); - } - - /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`. - /// Note: This method is intended for use in ERC4337 factories, - /// which are expected to NOT revert if the proxy is already deployed. - function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt) - internal - returns (bool alreadyDeployed, address instance) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) - mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) - mstore(0x20, 0x6009) - mstore(0x1e, implementation) - mstore(0x0a, 0x603d3d8160223d3973) - // Compute and store the bytecode hash. - mstore(add(m, 0x35), keccak256(0x21, 0x5f)) - mstore(m, shl(88, address())) - mstore8(m, 0xff) // Write the prefix. - mstore(add(m, 0x15), salt) - instance := keccak256(m, 0x55) - for {} 1 {} { - if iszero(extcodesize(instance)) { - instance := create2(value, 0x21, 0x5f, salt) - if iszero(instance) { - mstore(0x00, 0x30116425) // `DeploymentFailed()`. - revert(0x1c, 0x04) - } - break - } - alreadyDeployed := 1 - if iszero(value) { break } - if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - break - } - mstore(0x40, m) // Restore the free memory pointer. - mstore(0x60, 0) // Restore the zero slot. - } - } - - /// @dev Returns the initialization code hash of the clone of `implementation` - /// using immutable arguments encoded in `data`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3) - mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076) - mstore(0x20, 0x6009) - mstore(0x1e, implementation) - mstore(0x0a, 0x603d3d8160223d3973) - hash := keccak256(0x21, 0x5f) - mstore(0x40, m) // Restore the free memory pointer. - mstore(0x60, 0) // Restore the zero slot. - } - } - - /// @dev Returns the address of the deterministic clone of - /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddressERC1967( - address implementation, - bytes32 salt, - address deployer - ) internal pure returns (address predicted) { - bytes32 hash = initCodeHashERC1967(implementation); - predicted = predictDeterministicAddress(hash, salt, deployer); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OTHER OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the address when a contract with initialization code hash, - /// `hash`, is deployed with `salt`, by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer) - internal - pure - returns (address predicted) - { - /// @solidity memory-safe-assembly - assembly { - // Compute and store the bytecode hash. - mstore8(0x00, 0xff) // Write the prefix. - mstore(0x35, hash) - mstore(0x01, shl(96, deployer)) - mstore(0x15, salt) - predicted := keccak256(0x00, 0x55) - mstore(0x35, 0) // Restore the overwritten part of the free memory pointer. - } - } - - /// @dev Requires that `salt` starts with either the zero address or `by`. - function checkStartsWith(bytes32 salt, address by) internal pure { - /// @solidity memory-safe-assembly - assembly { - // If the salt does not start with the zero address or `by`. - if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) { - mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`. - revert(0x1c, 0x04) - } - } - } -} diff --git a/lib/solady/src/utils/LibMap.sol b/lib/solady/src/utils/LibMap.sol deleted file mode 100644 index edac97a..0000000 --- a/lib/solady/src/utils/LibMap.sol +++ /dev/null @@ -1,309 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for storage of packed unsigned integers. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibMap.sol) -library LibMap { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A uint8 map in storage. - struct Uint8Map { - mapping(uint256 => uint256) map; - } - - /// @dev A uint16 map in storage. - struct Uint16Map { - mapping(uint256 => uint256) map; - } - - /// @dev A uint32 map in storage. - struct Uint32Map { - mapping(uint256 => uint256) map; - } - - /// @dev A uint40 map in storage. Useful for storing timestamps up to 34841 A.D. - struct Uint40Map { - mapping(uint256 => uint256) map; - } - - /// @dev A uint64 map in storage. - struct Uint64Map { - mapping(uint256 => uint256) map; - } - - /// @dev A uint128 map in storage. - struct Uint128Map { - mapping(uint256 => uint256) map; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* GETTERS / SETTERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the uint8 value at `index` in `map`. - function get(Uint8Map storage map, uint256 index) internal view returns (uint8 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(5, index)) - result := byte(and(31, not(index)), sload(keccak256(0x00, 0x40))) - } - } - - /// @dev Updates the uint8 value at `index` in `map`. - function set(Uint8Map storage map, uint256 index, uint8 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(5, index)) - let s := keccak256(0x00, 0x40) // Storage slot. - mstore(0x00, sload(s)) - mstore8(and(31, not(index)), value) - sstore(s, mload(0x00)) - } - } - - /// @dev Returns the uint16 value at `index` in `map`. - function get(Uint16Map storage map, uint256 index) internal view returns (uint16 result) { - result = uint16(map.map[index >> 4] >> ((index & 15) << 4)); - } - - /// @dev Updates the uint16 value at `index` in `map`. - function set(Uint16Map storage map, uint256 index, uint16 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(4, index)) - let s := keccak256(0x00, 0x40) // Storage slot. - let o := shl(4, and(index, 15)) // Storage slot offset (bits). - let v := sload(s) // Storage slot value. - let m := 0xffff // Value mask. - sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value))))) - } - } - - /// @dev Returns the uint32 value at `index` in `map`. - function get(Uint32Map storage map, uint256 index) internal view returns (uint32 result) { - result = uint32(map.map[index >> 3] >> ((index & 7) << 5)); - } - - /// @dev Updates the uint32 value at `index` in `map`. - function set(Uint32Map storage map, uint256 index, uint32 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(3, index)) - let s := keccak256(0x00, 0x40) // Storage slot. - let o := shl(5, and(index, 7)) // Storage slot offset (bits). - let v := sload(s) // Storage slot value. - let m := 0xffffffff // Value mask. - sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value))))) - } - } - - /// @dev Returns the uint40 value at `index` in `map`. - function get(Uint40Map storage map, uint256 index) internal view returns (uint40 result) { - unchecked { - result = uint40(map.map[index / 6] >> ((index % 6) * 40)); - } - } - - /// @dev Updates the uint40 value at `index` in `map`. - function set(Uint40Map storage map, uint256 index, uint40 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, div(index, 6)) - let s := keccak256(0x00, 0x40) // Storage slot. - let o := mul(40, mod(index, 6)) // Storage slot offset (bits). - let v := sload(s) // Storage slot value. - let m := 0xffffffffff // Value mask. - sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value))))) - } - } - - /// @dev Returns the uint64 value at `index` in `map`. - function get(Uint64Map storage map, uint256 index) internal view returns (uint64 result) { - result = uint64(map.map[index >> 2] >> ((index & 3) << 6)); - } - - /// @dev Updates the uint64 value at `index` in `map`. - function set(Uint64Map storage map, uint256 index, uint64 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(2, index)) - let s := keccak256(0x00, 0x40) // Storage slot. - let o := shl(6, and(index, 3)) // Storage slot offset (bits). - let v := sload(s) // Storage slot value. - let m := 0xffffffffffffffff // Value mask. - sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value))))) - } - } - - /// @dev Returns the uint128 value at `index` in `map`. - function get(Uint128Map storage map, uint256 index) internal view returns (uint128 result) { - result = uint128(map.map[index >> 1] >> ((index & 1) << 7)); - } - - /// @dev Updates the uint128 value at `index` in `map`. - function set(Uint128Map storage map, uint256 index, uint128 value) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, map.slot) - mstore(0x00, shr(1, index)) - let s := keccak256(0x00, 0x40) // Storage slot. - let o := shl(7, and(index, 1)) // Storage slot offset (bits). - let v := sload(s) // Storage slot value. - let m := 0xffffffffffffffffffffffffffffffff // Value mask. - sstore(s, xor(v, shl(o, and(m, xor(shr(o, v), value))))) - } - } - - /// @dev Returns the value at `index` in `map`. - function get(mapping(uint256 => uint256) storage map, uint256 index, uint256 bitWidth) - internal - view - returns (uint256 result) - { - unchecked { - uint256 d = _rawDiv(256, bitWidth); // Bucket size. - uint256 m = (1 << bitWidth) - 1; // Value mask. - result = (map[_rawDiv(index, d)] >> (_rawMod(index, d) * bitWidth)) & m; - } - } - - /// @dev Updates the value at `index` in `map`. - function set( - mapping(uint256 => uint256) storage map, - uint256 index, - uint256 value, - uint256 bitWidth - ) internal { - unchecked { - uint256 d = _rawDiv(256, bitWidth); // Bucket size. - uint256 m = (1 << bitWidth) - 1; // Value mask. - uint256 o = _rawMod(index, d) * bitWidth; // Storage slot offset (bits). - map[_rawDiv(index, d)] ^= (((map[_rawDiv(index, d)] >> o) ^ value) & m) << o; - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* BINARY SEARCH */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // The following functions search in the range of [`start`, `end`) - // (i.e. `start <= index < end`). - // The range must be sorted in ascending order. - // `index` precedence: equal to > nearest before > nearest after. - // An invalid search range will simply return `(found = false, index = start)`. - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint8Map storage map, uint8 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 8); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint16Map storage map, uint16 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 16); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint32Map storage map, uint32 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 32); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint40Map storage map, uint40 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 40); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint64Map storage map, uint64 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 64); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted(Uint128Map storage map, uint128 needle, uint256 start, uint256 end) - internal - view - returns (bool found, uint256 index) - { - return searchSorted(map.map, needle, start, end, 128); - } - - /// @dev Returns whether `map` contains `needle`, and the index of `needle`. - function searchSorted( - mapping(uint256 => uint256) storage map, - uint256 needle, - uint256 start, - uint256 end, - uint256 bitWidth - ) internal view returns (bool found, uint256 index) { - unchecked { - if (start >= end) end = start; - uint256 t; - uint256 o = start - 1; // Offset to derive the actual index. - uint256 l = 1; // Low. - uint256 d = _rawDiv(256, bitWidth); // Bucket size. - uint256 m = (1 << bitWidth) - 1; // Value mask. - uint256 h = end - start; // High. - while (true) { - index = (l & h) + ((l ^ h) >> 1); - if (l > h) break; - t = (map[_rawDiv(index + o, d)] >> (_rawMod(index + o, d) * bitWidth)) & m; - if (t == needle) break; - if (needle <= t) h = index - 1; - else l = index + 1; - } - /// @solidity memory-safe-assembly - assembly { - m := or(iszero(index), iszero(bitWidth)) - found := iszero(or(xor(t, needle), m)) - index := add(o, xor(index, mul(xor(index, 1), m))) - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns `x / y`, returning 0 if `y` is zero. - function _rawDiv(uint256 x, uint256 y) private pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := div(x, y) - } - } - - /// @dev Returns `x % y`, returning 0 if `y` is zero. - function _rawMod(uint256 x, uint256 y) private pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - z := mod(x, y) - } - } -} diff --git a/lib/solady/src/utils/LibPRNG.sol b/lib/solady/src/utils/LibPRNG.sol deleted file mode 100644 index 6e1d9da..0000000 --- a/lib/solady/src/utils/LibPRNG.sol +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for generating pseudorandom numbers. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibPRNG.sol) -library LibPRNG { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A pseudorandom number state in memory. - struct PRNG { - uint256 state; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Seeds the `prng` with `state`. - function seed(PRNG memory prng, uint256 state) internal pure { - /// @solidity memory-safe-assembly - assembly { - mstore(prng, state) - } - } - - /// @dev Returns the next pseudorandom uint256. - /// All bits of the returned uint256 pass the NIST Statistical Test Suite. - function next(PRNG memory prng) internal pure returns (uint256 result) { - // We simply use `keccak256` for a great balance between - // runtime gas costs, bytecode size, and statistical properties. - // - // A high-quality LCG with a 32-byte state - // is only about 30% more gas efficient during runtime, - // but requires a 32-byte multiplier, which can cause bytecode bloat - // when this function is inlined. - // - // Using this method is about 2x more efficient than - // `nextRandomness = uint256(keccak256(abi.encode(randomness)))`. - /// @solidity memory-safe-assembly - assembly { - result := keccak256(prng, 0x20) - mstore(prng, result) - } - } - - /// @dev Returns a pseudorandom uint256, uniformly distributed - /// between 0 (inclusive) and `upper` (exclusive). - /// If your modulus is big, this method is recommended - /// for uniform sampling to avoid modulo bias. - /// For uniform sampling across all uint256 values, - /// or for small enough moduli such that the bias is neligible, - /// use {next} instead. - function uniform(PRNG memory prng, uint256 upper) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - for {} 1 {} { - result := keccak256(prng, 0x20) - mstore(prng, result) - if iszero(lt(result, mod(sub(0, upper), upper))) { break } - } - result := mod(result, upper) - } - } - - /// @dev Shuffles the array in-place with Fisher-Yates shuffle. - function shuffle(PRNG memory prng, uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let n := mload(a) - let w := not(0) - let mask := shr(128, w) - if n { - for { a := add(a, 0x20) } 1 {} { - // We can just directly use `keccak256`, cuz - // the other approaches don't save much. - let r := keccak256(prng, 0x20) - mstore(prng, r) - - // Note that there will be a very tiny modulo bias - // if the length of the array is not a power of 2. - // For all practical purposes, it is negligible - // and will not be a fairness or security concern. - { - let j := add(a, shl(5, mod(shr(128, r), n))) - n := add(n, w) // `sub(n, 1)`. - if iszero(n) { break } - - let i := add(a, shl(5, n)) - let t := mload(i) - mstore(i, mload(j)) - mstore(j, t) - } - - { - let j := add(a, shl(5, mod(and(r, mask), n))) - n := add(n, w) // `sub(n, 1)`. - if iszero(n) { break } - - let i := add(a, shl(5, n)) - let t := mload(i) - mstore(i, mload(j)) - mstore(j, t) - } - } - } - } - } - - /// @dev Shuffles the bytes in-place with Fisher-Yates shuffle. - function shuffle(PRNG memory prng, bytes memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let n := mload(a) - let w := not(0) - let mask := shr(128, w) - if n { - let b := add(a, 0x01) - for { a := add(a, 0x20) } 1 {} { - // We can just directly use `keccak256`, cuz - // the other approaches don't save much. - let r := keccak256(prng, 0x20) - mstore(prng, r) - - // Note that there will be a very tiny modulo bias - // if the length of the array is not a power of 2. - // For all practical purposes, it is negligible - // and will not be a fairness or security concern. - { - let o := mod(shr(128, r), n) - n := add(n, w) // `sub(n, 1)`. - if iszero(n) { break } - - let t := mload(add(b, n)) - mstore8(add(a, n), mload(add(b, o))) - mstore8(add(a, o), t) - } - - { - let o := mod(and(r, mask), n) - n := add(n, w) // `sub(n, 1)`. - if iszero(n) { break } - - let t := mload(add(b, n)) - mstore8(add(a, n), mload(add(b, o))) - mstore8(add(a, o), t) - } - } - } - } - } -} diff --git a/lib/solady/src/utils/LibRLP.sol b/lib/solady/src/utils/LibRLP.sol deleted file mode 100644 index 4f489c8..0000000 --- a/lib/solady/src/utils/LibRLP.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for computing contract addresses from their deployer and nonce. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibRLP.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibRLP.sol) -library LibRLP { - /// @dev Returns the address where a contract will be stored if deployed via - /// `deployer` with `nonce` using the `CREATE` opcode. - /// For the specification of the Recursive Length Prefix (RLP) - /// encoding scheme, please refer to p. 19 of the Ethereum Yellow Paper - /// (https://ethereum.github.io/yellowpaper/paper.pdf) - /// and the Ethereum Wiki (https://eth.wiki/fundamentals/rlp). - /// - /// Based on the EIP-161 (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md) - /// specification, all contract accounts on the Ethereum mainnet are initiated with - /// `nonce = 1`. Thus, the first contract address created by another contract - /// is calculated with a non-zero nonce. - /// - /// The theoretical allowed limit, based on EIP-2681 - /// (https://eips.ethereum.org/EIPS/eip-2681), for an account nonce is 2**64-2. - /// - /// Caution! This function will NOT check that the nonce is within the theoretical range. - /// This is for performance, as exceeding the range is extremely impractical. - /// It is the user's responsibility to ensure that the nonce is valid - /// (e.g. no dirty bits after packing / unpacking). - /// - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function computeAddress(address deployer, uint256 nonce) - internal - pure - returns (address deployed) - { - /// @solidity memory-safe-assembly - assembly { - for {} 1 {} { - // The integer zero is treated as an empty byte string, - // and as a result it only has a length prefix, 0x80, - // computed via `0x80 + 0`. - - // A one-byte integer in the [0x00, 0x7f] range uses its - // own value as a length prefix, - // there is no additional `0x80 + length` prefix that precedes it. - if iszero(gt(nonce, 0x7f)) { - mstore(0x00, deployer) - // Using `mstore8` instead of `or` naturally cleans - // any dirty upper bits of `deployer`. - mstore8(0x0b, 0x94) - mstore8(0x0a, 0xd6) - // `shl` 7 is equivalent to multiplying by 0x80. - mstore8(0x20, or(shl(7, iszero(nonce)), nonce)) - deployed := keccak256(0x0a, 0x17) - break - } - let i := 8 - // Just use a loop to generalize all the way with minimal bytecode size. - for {} shr(i, nonce) { i := add(i, 8) } {} - // `shr` 3 is equivalent to dividing by 8. - i := shr(3, i) - // Store in descending slot sequence to overlap the values correctly. - mstore(i, nonce) - mstore(0x00, shl(8, deployer)) - mstore8(0x1f, add(0x80, i)) - mstore8(0x0a, 0x94) - mstore8(0x09, add(0xd6, i)) - deployed := keccak256(0x09, add(0x17, i)) - break - } - } - } -} diff --git a/lib/solady/src/utils/LibSort.sol b/lib/solady/src/utils/LibSort.sol deleted file mode 100644 index 97789bf..0000000 --- a/lib/solady/src/utils/LibSort.sol +++ /dev/null @@ -1,699 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Optimized sorts and operations for sorted arrays. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Sort.sol) -library LibSort { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INSERTION SORT */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // - Faster on small arrays (32 or lesser elements). - // - Faster on almost sorted arrays. - // - Smaller bytecode. - // - May be suitable for view functions intended for off-chain querying. - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let n := mload(a) // Length of `a`. - mstore(a, 0) // For insertion sort's inner loop to terminate. - let h := add(a, shl(5, n)) // High slot. - let s := 0x20 - let w := not(0x1f) - for { let i := add(a, s) } 1 {} { - i := add(i, s) - if gt(i, h) { break } - let k := mload(i) // Key. - let j := add(i, w) // The slot before the current slot. - let v := mload(j) // The value of `j`. - if iszero(gt(v, k)) { continue } - for {} 1 {} { - mstore(add(j, s), v) - j := add(j, w) // `sub(j, 0x20)`. - v := mload(j) - if iszero(gt(v, k)) { break } - } - mstore(add(j, s), k) - } - mstore(a, n) // Restore the length of `a`. - } - } - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(int256[] memory a) internal pure { - _flipSign(a); - insertionSort(_toUints(a)); - _flipSign(a); - } - - /// @dev Sorts the array in-place with insertion sort. - function insertionSort(address[] memory a) internal pure { - insertionSort(_toUints(a)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* INTRO-QUICKSORT */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // - Faster on larger arrays (more than 32 elements). - // - Robust performance. - // - Larger bytecode. - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - let w := not(0x1f) - let s := 0x20 - let n := mload(a) // Length of `a`. - mstore(a, 0) // For insertion sort's inner loop to terminate. - - // Let the stack be the start of the free memory. - let stack := mload(0x40) - - for {} iszero(lt(n, 2)) {} { - // Push `l` and `h` to the stack. - // The `shl` by 5 is equivalent to multiplying by `0x20`. - let l := add(a, s) - let h := add(a, shl(5, n)) - - let j := l - // forgefmt: disable-next-item - for {} iszero(or(eq(j, h), gt(mload(j), mload(add(j, s))))) {} { - j := add(j, s) - } - // If the array is already sorted. - if eq(j, h) { break } - - j := h - // forgefmt: disable-next-item - for {} iszero(gt(mload(j), mload(add(j, w)))) {} { - j := add(j, w) // `sub(j, 0x20)`. - } - // If the array is reversed sorted. - if eq(j, l) { - for {} 1 {} { - let t := mload(l) - mstore(l, mload(h)) - mstore(h, t) - h := add(h, w) // `sub(h, 0x20)`. - l := add(l, s) - if iszero(lt(l, h)) { break } - } - break - } - - // Push `l` and `h` onto the stack. - mstore(stack, l) - mstore(add(stack, s), h) - stack := add(stack, 0x40) - break - } - - for { let stackBottom := mload(0x40) } iszero(eq(stack, stackBottom)) {} { - // Pop `l` and `h` from the stack. - stack := sub(stack, 0x40) - let l := mload(stack) - let h := mload(add(stack, s)) - - // Do insertion sort if `h - l <= 0x20 * 12`. - // Threshold is fine-tuned via trial and error. - if iszero(gt(sub(h, l), 0x180)) { - // Hardcode sort the first 2 elements. - let i := add(l, s) - if iszero(lt(mload(l), mload(i))) { - let t := mload(i) - mstore(i, mload(l)) - mstore(l, t) - } - for {} 1 {} { - i := add(i, s) - if gt(i, h) { break } - let k := mload(i) // Key. - let j := add(i, w) // The slot before the current slot. - let v := mload(j) // The value of `j`. - if iszero(gt(v, k)) { continue } - for {} 1 {} { - mstore(add(j, s), v) - j := add(j, w) - v := mload(j) - if iszero(gt(v, k)) { break } - } - mstore(add(j, s), k) - } - continue - } - // Pivot slot is the average of `l` and `h`. - let p := add(shl(5, shr(6, add(l, h))), and(31, l)) - // Median of 3 with sorting. - { - function swap(a_, b_) -> _b, _a { - _b := a_ - _a := b_ - } - let e0 := mload(l) - let e1 := mload(h) - if iszero(lt(e0, e1)) { e1, e0 := swap(e0, e1) } - let e2 := mload(p) - if iszero(lt(e2, e1)) { e2, e1 := swap(e1, e2) } - if iszero(lt(e0, e2)) { e2, e0 := swap(e0, e2) } - mstore(p, e2) - mstore(h, e1) - mstore(l, e0) - } - // Hoare's partition. - { - // The value of the pivot slot. - let x := mload(p) - p := h - for { let i := l } 1 {} { - for {} 1 {} { - i := add(i, s) - if iszero(gt(x, mload(i))) { break } - } - let j := p - for {} 1 {} { - j := add(j, w) - if iszero(lt(x, mload(j))) { break } - } - p := j - if iszero(lt(i, p)) { break } - // Swap slots `i` and `p`. - let t := mload(i) - mstore(i, mload(p)) - mstore(p, t) - } - } - // If slice on right of pivot is non-empty, push onto stack. - { - mstore(stack, add(p, s)) - // Skip `mstore(add(stack, 0x20), h)`, as it is already on the stack. - stack := add(stack, shl(6, lt(add(p, s), h))) - } - // If slice on left of pivot is non-empty, push onto stack. - { - mstore(stack, l) - mstore(add(stack, s), p) - stack := add(stack, shl(6, gt(p, l))) - } - } - mstore(a, n) // Restore the length of `a`. - } - } - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(int256[] memory a) internal pure { - _flipSign(a); - sort(_toUints(a)); - _flipSign(a); - } - - /// @dev Sorts the array in-place with intro-quicksort. - function sort(address[] memory a) internal pure { - sort(_toUints(a)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OTHER USEFUL OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // For performance, the `uniquifySorted` methods will not revert if the - // array is not sorted -- it will simply remove consecutive duplicate elements. - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - // If the length of `a` is greater than 1. - if iszero(lt(mload(a), 2)) { - let x := add(a, 0x20) - let y := add(a, 0x40) - let end := add(a, shl(5, add(mload(a), 1))) - for {} 1 {} { - if iszero(eq(mload(x), mload(y))) { - x := add(x, 0x20) - mstore(x, mload(y)) - } - y := add(y, 0x20) - if eq(y, end) { break } - } - mstore(a, shr(5, sub(x, a))) - } - } - } - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(int256[] memory a) internal pure { - uniquifySorted(_toUints(a)); - } - - /// @dev Removes duplicate elements from a ascendingly sorted memory array. - function uniquifySorted(address[] memory a) internal pure { - uniquifySorted(_toUints(a)); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(uint256[] memory a, uint256 needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(a, needle, 0); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(int256[] memory a, int256 needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(_toUints(a), uint256(needle), 1 << 255); - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function searchSorted(address[] memory a, address needle) - internal - pure - returns (bool found, uint256 index) - { - (found, index) = _searchSorted(_toUints(a), uint256(uint160(needle)), 0); - } - - /// @dev Reverses the array in-place. - function reverse(uint256[] memory a) internal pure { - /// @solidity memory-safe-assembly - assembly { - if iszero(lt(mload(a), 2)) { - let s := 0x20 - let w := not(0x1f) - let h := add(a, shl(5, mload(a))) - for { a := add(a, s) } 1 {} { - let t := mload(a) - mstore(a, mload(h)) - mstore(h, t) - h := add(h, w) - a := add(a, s) - if iszero(lt(a, h)) { break } - } - } - } - } - - /// @dev Reverses the array in-place. - function reverse(int256[] memory a) internal pure { - reverse(_toUints(a)); - } - - /// @dev Reverses the array in-place. - function reverse(address[] memory a) internal pure { - reverse(_toUints(a)); - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(uint256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := iszero(gt(p, mload(a))) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(int256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := iszero(sgt(p, mload(a))) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is sorted in ascending order. - function isSorted(address[] memory a) internal pure returns (bool result) { - result = isSorted(_toUints(a)); - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(uint256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := lt(p, mload(a)) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(int256[] memory a) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - if iszero(lt(mload(a), 2)) { - let end := add(a, shl(5, mload(a))) - for { a := add(a, 0x20) } 1 {} { - let p := mload(a) - a := add(a, 0x20) - result := slt(p, mload(a)) - if iszero(mul(result, xor(a, end))) { break } - } - } - } - } - - /// @dev Returns whether the array is strictly ascending (sorted and uniquified). - function isSortedAndUniquified(address[] memory a) internal pure returns (bool result) { - result = isSortedAndUniquified(_toUints(a)); - } - - /// @dev Returns the sorted set difference of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _difference(a, b, 0); - } - - /// @dev Returns the sorted set difference between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_difference(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set difference between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function difference(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_difference(_toUints(a), _toUints(b), 0)); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _intersection(a, b, 0); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_intersection(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function intersection(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_intersection(_toUints(a), _toUints(b), 0)); - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(uint256[] memory a, uint256[] memory b) - internal - pure - returns (uint256[] memory c) - { - c = _union(a, b, 0); - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(int256[] memory a, int256[] memory b) - internal - pure - returns (int256[] memory c) - { - c = _toInts(_union(_toUints(a), _toUints(b), 1 << 255)); - } - - /// @dev Returns the sorted set union between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function union(address[] memory a, address[] memory b) - internal - pure - returns (address[] memory c) - { - c = _toAddresses(_union(_toUints(a), _toUints(b), 0)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Reinterpret cast to an uint256 array. - function _toUints(int256[] memory a) private pure returns (uint256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Reinterpret cast to an uint256 array. - function _toUints(address[] memory a) private pure returns (uint256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - // As any address written to memory will have the upper 96 bits - // of the word zeroized (as per Solidity spec), we can directly - // compare these addresses as if they are whole uint256 words. - casted := a - } - } - - /// @dev Reinterpret cast to an int array. - function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Reinterpret cast to an address array. - function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) { - /// @solidity memory-safe-assembly - assembly { - casted := a - } - } - - /// @dev Converts an array of signed integers to unsigned - /// integers suitable for sorting or vice versa. - function _flipSign(int256[] memory a) private pure { - /// @solidity memory-safe-assembly - assembly { - let w := shl(255, 1) - for { let end := add(a, shl(5, mload(a))) } iszero(eq(a, end)) {} { - a := add(a, 0x20) - mstore(a, add(mload(a), w)) - } - } - } - - /// @dev Returns whether `a` contains `needle`, and the index of `needle`. - /// `index` precedence: equal to > nearest before > nearest after. - function _searchSorted(uint256[] memory a, uint256 needle, uint256 signed) - private - pure - returns (bool found, uint256 index) - { - /// @solidity memory-safe-assembly - assembly { - let w := not(0) - let l := 1 - let h := mload(a) - let t := 0 - for { needle := add(signed, needle) } 1 {} { - index := shr(1, add(l, h)) - t := add(signed, mload(add(a, shl(5, index)))) - if or(gt(l, h), eq(t, needle)) { break } - // Decide whether to search the left or right half. - if iszero(gt(needle, t)) { - h := add(index, w) - continue - } - l := add(index, 1) - } - // `index` will be zero in the case of an empty array, - // or when the value is less than the smallest value in the array. - found := eq(t, needle) - t := iszero(iszero(index)) - index := mul(add(index, w), t) - found := and(found, t) - } - } - - /// @dev Returns the sorted set difference of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _difference(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - b := add(b, s) - continue - } - k := add(k, s) - mstore(k, u) - a := add(a, s) - } - for {} iszero(gt(a, aEnd)) {} { - k := add(k, s) - mstore(k, mload(a)) - a := add(a, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } - - /// @dev Returns the sorted set intersection between `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _intersection(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - k := add(k, s) - mstore(k, u) - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - b := add(b, s) - continue - } - a := add(a, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } - - /// @dev Returns the sorted set union of `a` and `b`. - /// Note: Behaviour is undefined if inputs are not sorted and uniquified. - function _union(uint256[] memory a, uint256[] memory b, uint256 signed) - private - pure - returns (uint256[] memory c) - { - /// @solidity memory-safe-assembly - assembly { - let s := 0x20 - let aEnd := add(a, shl(5, mload(a))) - let bEnd := add(b, shl(5, mload(b))) - c := mload(0x40) // Set `c` to the free memory pointer. - a := add(a, s) - b := add(b, s) - let k := c - for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} { - let u := mload(a) - let v := mload(b) - if iszero(xor(u, v)) { - k := add(k, s) - mstore(k, u) - a := add(a, s) - b := add(b, s) - continue - } - if iszero(lt(add(u, signed), add(v, signed))) { - k := add(k, s) - mstore(k, v) - b := add(b, s) - continue - } - k := add(k, s) - mstore(k, u) - a := add(a, s) - } - for {} iszero(gt(a, aEnd)) {} { - k := add(k, s) - mstore(k, mload(a)) - a := add(a, s) - } - for {} iszero(gt(b, bEnd)) {} { - k := add(k, s) - mstore(k, mload(b)) - b := add(b, s) - } - mstore(c, shr(5, sub(k, c))) // Store the length of `c`. - mstore(0x40, add(k, s)) // Allocate the memory for `c`. - } - } -} diff --git a/lib/solady/src/utils/LibString.sol b/lib/solady/src/utils/LibString.sol deleted file mode 100644 index 7f5cb60..0000000 --- a/lib/solady/src/utils/LibString.sol +++ /dev/null @@ -1,1163 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for converting numbers into strings and other string operations. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) -library LibString { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The `length` of the output is too small to contain all the hex digits. - error HexLengthInsufficient(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The constant returned when the `search` is not found in the string. - uint256 internal constant NOT_FOUND = type(uint256).max; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* DECIMAL OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the base 10 decimal representation of `value`. - function toString(uint256 value) internal pure returns (string memory str) { - /// @solidity memory-safe-assembly - assembly { - // The maximum value of a uint256 contains 78 digits (1 byte per digit), but - // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. - // We will need 1 word for the trailing zeros padding, 1 word for the length, - // and 3 words for a maximum of 78 digits. - str := add(mload(0x40), 0x80) - // Update the free memory pointer to allocate. - mstore(0x40, add(str, 0x20)) - // Zeroize the slot after the string. - mstore(str, 0) - - // Cache the end of the memory to calculate the length later. - let end := str - - let w := not(0) // Tsk. - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - for { let temp := value } 1 {} { - str := add(str, w) // `sub(str, 1)`. - // Write the character to the pointer. - // The ASCII index of the '0' character is 48. - mstore8(str, add(48, mod(temp, 10))) - // Keep dividing `temp` until zero. - temp := div(temp, 10) - if iszero(temp) { break } - } - - let length := sub(end, str) - // Move the pointer 32 bytes leftwards to make room for the length. - str := sub(str, 0x20) - // Store the length. - mstore(str, length) - } - } - - /// @dev Returns the base 10 decimal representation of `value`. - function toString(int256 value) internal pure returns (string memory str) { - if (value >= 0) { - return toString(uint256(value)); - } - unchecked { - str = toString(uint256(-value)); - } - /// @solidity memory-safe-assembly - assembly { - // We still have some spare memory space on the left, - // as we have allocated 3 words (96 bytes) for up to 78 digits. - let length := mload(str) // Load the string length. - mstore(str, 0x2d) // Store the '-' character. - str := sub(str, 1) // Move back the string pointer by a byte. - mstore(str, add(length, 1)) // Update the string length. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HEXADECIMAL OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the hexadecimal representation of `value`, - /// left-padded to an input length of `length` bytes. - /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, - /// giving a total length of `length * 2 + 2` bytes. - /// Reverts if `length` is too small for the output to contain all the digits. - function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { - str = toHexStringNoPrefix(value, length); - /// @solidity memory-safe-assembly - assembly { - let strLength := add(mload(str), 2) // Compute the length. - mstore(str, 0x3078) // Write the "0x" prefix. - str := sub(str, 2) // Move the pointer. - mstore(str, strLength) // Write the length. - } - } - - /// @dev Returns the hexadecimal representation of `value`, - /// left-padded to an input length of `length` bytes. - /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, - /// giving a total length of `length * 2` bytes. - /// Reverts if `length` is too small for the output to contain all the digits. - function toHexStringNoPrefix(uint256 value, uint256 length) - internal - pure - returns (string memory str) - { - /// @solidity memory-safe-assembly - assembly { - // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes - // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. - // We add 0x20 to the total and round down to a multiple of 0x20. - // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. - str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) - // Allocate the memory. - mstore(0x40, add(str, 0x20)) - // Zeroize the slot after the string. - mstore(str, 0) - - // Cache the end to calculate the length later. - let end := str - // Store "0123456789abcdef" in scratch space. - mstore(0x0f, 0x30313233343536373839616263646566) - - let start := sub(str, add(length, length)) - let w := not(1) // Tsk. - let temp := value - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - for {} 1 {} { - str := add(str, w) // `sub(str, 2)`. - mstore8(add(str, 1), mload(and(temp, 15))) - mstore8(str, mload(and(shr(4, temp), 15))) - temp := shr(8, temp) - if iszero(xor(str, start)) { break } - } - - if temp { - // Store the function selector of `HexLengthInsufficient()`. - mstore(0x00, 0x2194895a) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Compute the string's length. - let strLength := sub(end, str) - // Move the pointer and write the length. - str := sub(str, 0x20) - mstore(str, strLength) - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. - /// As address are 20 bytes long, the output will left-padded to have - /// a length of `20 * 2 + 2` bytes. - function toHexString(uint256 value) internal pure returns (string memory str) { - str = toHexStringNoPrefix(value); - /// @solidity memory-safe-assembly - assembly { - let strLength := add(mload(str), 2) // Compute the length. - mstore(str, 0x3078) // Write the "0x" prefix. - str := sub(str, 2) // Move the pointer. - mstore(str, strLength) // Write the length. - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is prefixed with "0x". - /// The output excludes leading "0" from the `toHexString` output. - /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. - function toMinimalHexString(uint256 value) internal pure returns (string memory str) { - str = toHexStringNoPrefix(value); - /// @solidity memory-safe-assembly - assembly { - let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. - let strLength := add(mload(str), 2) // Compute the length. - mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero. - str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero. - mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output excludes leading "0" from the `toHexStringNoPrefix` output. - /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. - function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { - str = toHexStringNoPrefix(value); - /// @solidity memory-safe-assembly - assembly { - let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. - let strLength := mload(str) // Get the length. - str := add(str, o) // Move the pointer, accounting for leading zero. - mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is encoded using 2 hexadecimal digits per byte. - /// As address are 20 bytes long, the output will left-padded to have - /// a length of `20 * 2` bytes. - function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { - /// @solidity memory-safe-assembly - assembly { - // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, - // 0x02 bytes for the prefix, and 0x40 bytes for the digits. - // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. - str := add(mload(0x40), 0x80) - // Allocate the memory. - mstore(0x40, add(str, 0x20)) - // Zeroize the slot after the string. - mstore(str, 0) - - // Cache the end to calculate the length later. - let end := str - // Store "0123456789abcdef" in scratch space. - mstore(0x0f, 0x30313233343536373839616263646566) - - let w := not(1) // Tsk. - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - for { let temp := value } 1 {} { - str := add(str, w) // `sub(str, 2)`. - mstore8(add(str, 1), mload(and(temp, 15))) - mstore8(str, mload(and(shr(4, temp), 15))) - temp := shr(8, temp) - if iszero(temp) { break } - } - - // Compute the string's length. - let strLength := sub(end, str) - // Move the pointer and write the length. - str := sub(str, 0x20) - mstore(str, strLength) - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, - /// and the alphabets are capitalized conditionally according to - /// https://eips.ethereum.org/EIPS/eip-55 - function toHexStringChecksummed(address value) internal pure returns (string memory str) { - str = toHexString(value); - /// @solidity memory-safe-assembly - assembly { - let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` - let o := add(str, 0x22) - let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` - let t := shl(240, 136) // `0b10001000 << 240` - for { let i := 0 } 1 {} { - mstore(add(i, i), mul(t, byte(i, hashed))) - i := add(i, 1) - if eq(i, 20) { break } - } - mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) - o := add(o, 0x20) - mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. - function toHexString(address value) internal pure returns (string memory str) { - str = toHexStringNoPrefix(value); - /// @solidity memory-safe-assembly - assembly { - let strLength := add(mload(str), 2) // Compute the length. - mstore(str, 0x3078) // Write the "0x" prefix. - str := sub(str, 2) // Move the pointer. - mstore(str, strLength) // Write the length. - } - } - - /// @dev Returns the hexadecimal representation of `value`. - /// The output is encoded using 2 hexadecimal digits per byte. - function toHexStringNoPrefix(address value) internal pure returns (string memory str) { - /// @solidity memory-safe-assembly - assembly { - str := mload(0x40) - - // Allocate the memory. - // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, - // 0x02 bytes for the prefix, and 0x28 bytes for the digits. - // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. - mstore(0x40, add(str, 0x80)) - - // Store "0123456789abcdef" in scratch space. - mstore(0x0f, 0x30313233343536373839616263646566) - - str := add(str, 2) - mstore(str, 40) - - let o := add(str, 0x20) - mstore(add(o, 40), 0) - - value := shl(96, value) - - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - for { let i := 0 } 1 {} { - let p := add(o, add(i, i)) - let temp := byte(i, value) - mstore8(add(p, 1), mload(and(temp, 15))) - mstore8(p, mload(shr(4, temp))) - i := add(i, 1) - if eq(i, 20) { break } - } - } - } - - /// @dev Returns the hex encoded string from the raw bytes. - /// The output is encoded using 2 hexadecimal digits per byte. - function toHexString(bytes memory raw) internal pure returns (string memory str) { - str = toHexStringNoPrefix(raw); - /// @solidity memory-safe-assembly - assembly { - let strLength := add(mload(str), 2) // Compute the length. - mstore(str, 0x3078) // Write the "0x" prefix. - str := sub(str, 2) // Move the pointer. - mstore(str, strLength) // Write the length. - } - } - - /// @dev Returns the hex encoded string from the raw bytes. - /// The output is encoded using 2 hexadecimal digits per byte. - function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { - /// @solidity memory-safe-assembly - assembly { - let length := mload(raw) - str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. - mstore(str, add(length, length)) // Store the length of the output. - - // Store "0123456789abcdef" in scratch space. - mstore(0x0f, 0x30313233343536373839616263646566) - - let o := add(str, 0x20) - let end := add(raw, length) - - for {} iszero(eq(raw, end)) {} { - raw := add(raw, 1) - mstore8(add(o, 1), mload(and(mload(raw), 15))) - mstore8(o, mload(and(shr(4, mload(raw)), 15))) - o := add(o, 2) - } - mstore(o, 0) // Zeroize the slot after the string. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* RUNE STRING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the number of UTF characters in the string. - function runeCount(string memory s) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - if mload(s) { - mstore(0x00, div(not(0), 255)) - mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) - let o := add(s, 0x20) - let end := add(o, mload(s)) - for { result := 1 } 1 { result := add(result, 1) } { - o := add(o, byte(0, mload(shr(250, mload(o))))) - if iszero(lt(o, end)) { break } - } - } - } - } - - /// @dev Returns if this string is a 7-bit ASCII string. - /// (i.e. all characters codes are in [0..127]) - function is7BitASCII(string memory s) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - let mask := shl(7, div(not(0), 255)) - result := 1 - let n := mload(s) - if n { - let o := add(s, 0x20) - let end := add(o, n) - let last := mload(end) - mstore(end, 0) - for {} 1 {} { - if and(mask, mload(o)) { - result := 0 - break - } - o := add(o, 0x20) - if iszero(lt(o, end)) { break } - } - mstore(end, last) - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* BYTE STRING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // For performance and bytecode compactness, all indices of the following operations - // are byte (ASCII) offsets, not UTF character offsets. - - /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. - function replace(string memory subject, string memory search, string memory replacement) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let subjectLength := mload(subject) - let searchLength := mload(search) - let replacementLength := mload(replacement) - - subject := add(subject, 0x20) - search := add(search, 0x20) - replacement := add(replacement, 0x20) - result := add(mload(0x40), 0x20) - - let subjectEnd := add(subject, subjectLength) - if iszero(gt(searchLength, subjectLength)) { - let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) - let h := 0 - if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } - let m := shl(3, sub(0x20, and(searchLength, 0x1f))) - let s := mload(search) - for {} 1 {} { - let t := mload(subject) - // Whether the first `searchLength % 32` bytes of - // `subject` and `search` matches. - if iszero(shr(m, xor(t, s))) { - if h { - if iszero(eq(keccak256(subject, searchLength), h)) { - mstore(result, t) - result := add(result, 1) - subject := add(subject, 1) - if iszero(lt(subject, subjectSearchEnd)) { break } - continue - } - } - // Copy the `replacement` one word at a time. - for { let o := 0 } 1 {} { - mstore(add(result, o), mload(add(replacement, o))) - o := add(o, 0x20) - if iszero(lt(o, replacementLength)) { break } - } - result := add(result, replacementLength) - subject := add(subject, searchLength) - if searchLength { - if iszero(lt(subject, subjectSearchEnd)) { break } - continue - } - } - mstore(result, t) - result := add(result, 1) - subject := add(subject, 1) - if iszero(lt(subject, subjectSearchEnd)) { break } - } - } - - let resultRemainder := result - result := add(mload(0x40), 0x20) - let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) - // Copy the rest of the string one word at a time. - for {} lt(subject, subjectEnd) {} { - mstore(resultRemainder, mload(subject)) - resultRemainder := add(resultRemainder, 0x20) - subject := add(subject, 0x20) - } - result := sub(result, 0x20) - let last := add(add(result, 0x20), k) // Zeroize the slot after the string. - mstore(last, 0) - mstore(0x40, add(last, 0x20)) // Allocate the memory. - mstore(result, k) // Store the length. - } - } - - /// @dev Returns the byte index of the first location of `search` in `subject`, - /// searching from left to right, starting from `from`. - /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. - function indexOf(string memory subject, string memory search, uint256 from) - internal - pure - returns (uint256 result) - { - /// @solidity memory-safe-assembly - assembly { - for { let subjectLength := mload(subject) } 1 {} { - if iszero(mload(search)) { - if iszero(gt(from, subjectLength)) { - result := from - break - } - result := subjectLength - break - } - let searchLength := mload(search) - let subjectStart := add(subject, 0x20) - - result := not(0) // Initialize to `NOT_FOUND`. - - subject := add(subjectStart, from) - let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) - - let m := shl(3, sub(0x20, and(searchLength, 0x1f))) - let s := mload(add(search, 0x20)) - - if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } - - if iszero(lt(searchLength, 0x20)) { - for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { - if iszero(shr(m, xor(mload(subject), s))) { - if eq(keccak256(subject, searchLength), h) { - result := sub(subject, subjectStart) - break - } - } - subject := add(subject, 1) - if iszero(lt(subject, end)) { break } - } - break - } - for {} 1 {} { - if iszero(shr(m, xor(mload(subject), s))) { - result := sub(subject, subjectStart) - break - } - subject := add(subject, 1) - if iszero(lt(subject, end)) { break } - } - break - } - } - } - - /// @dev Returns the byte index of the first location of `search` in `subject`, - /// searching from left to right. - /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. - function indexOf(string memory subject, string memory search) - internal - pure - returns (uint256 result) - { - result = indexOf(subject, search, 0); - } - - /// @dev Returns the byte index of the first location of `search` in `subject`, - /// searching from right to left, starting from `from`. - /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. - function lastIndexOf(string memory subject, string memory search, uint256 from) - internal - pure - returns (uint256 result) - { - /// @solidity memory-safe-assembly - assembly { - for {} 1 {} { - result := not(0) // Initialize to `NOT_FOUND`. - let searchLength := mload(search) - if gt(searchLength, mload(subject)) { break } - let w := result - - let fromMax := sub(mload(subject), searchLength) - if iszero(gt(fromMax, from)) { from := fromMax } - - let end := add(add(subject, 0x20), w) - subject := add(add(subject, 0x20), from) - if iszero(gt(subject, end)) { break } - // As this function is not too often used, - // we shall simply use keccak256 for smaller bytecode size. - for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { - if eq(keccak256(subject, searchLength), h) { - result := sub(subject, add(end, 1)) - break - } - subject := add(subject, w) // `sub(subject, 1)`. - if iszero(gt(subject, end)) { break } - } - break - } - } - } - - /// @dev Returns the byte index of the first location of `search` in `subject`, - /// searching from right to left. - /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. - function lastIndexOf(string memory subject, string memory search) - internal - pure - returns (uint256 result) - { - result = lastIndexOf(subject, search, uint256(int256(-1))); - } - - /// @dev Returns true if `search` is found in `subject`, false otherwise. - function contains(string memory subject, string memory search) internal pure returns (bool) { - return indexOf(subject, search) != NOT_FOUND; - } - - /// @dev Returns whether `subject` starts with `search`. - function startsWith(string memory subject, string memory search) - internal - pure - returns (bool result) - { - /// @solidity memory-safe-assembly - assembly { - let searchLength := mload(search) - // Just using keccak256 directly is actually cheaper. - // forgefmt: disable-next-item - result := and( - iszero(gt(searchLength, mload(subject))), - eq( - keccak256(add(subject, 0x20), searchLength), - keccak256(add(search, 0x20), searchLength) - ) - ) - } - } - - /// @dev Returns whether `subject` ends with `search`. - function endsWith(string memory subject, string memory search) - internal - pure - returns (bool result) - { - /// @solidity memory-safe-assembly - assembly { - let searchLength := mload(search) - let subjectLength := mload(subject) - // Whether `search` is not longer than `subject`. - let withinRange := iszero(gt(searchLength, subjectLength)) - // Just using keccak256 directly is actually cheaper. - // forgefmt: disable-next-item - result := and( - withinRange, - eq( - keccak256( - // `subject + 0x20 + max(subjectLength - searchLength, 0)`. - add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), - searchLength - ), - keccak256(add(search, 0x20), searchLength) - ) - ) - } - } - - /// @dev Returns `subject` repeated `times`. - function repeat(string memory subject, uint256 times) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let subjectLength := mload(subject) - if iszero(or(iszero(times), iszero(subjectLength))) { - subject := add(subject, 0x20) - result := mload(0x40) - let output := add(result, 0x20) - for {} 1 {} { - // Copy the `subject` one word at a time. - for { let o := 0 } 1 {} { - mstore(add(output, o), mload(add(subject, o))) - o := add(o, 0x20) - if iszero(lt(o, subjectLength)) { break } - } - output := add(output, subjectLength) - times := sub(times, 1) - if iszero(times) { break } - } - mstore(output, 0) // Zeroize the slot after the string. - let resultLength := sub(output, add(result, 0x20)) - mstore(result, resultLength) // Store the length. - // Allocate the memory. - mstore(0x40, add(result, add(resultLength, 0x20))) - } - } - } - - /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). - /// `start` and `end` are byte offsets. - function slice(string memory subject, uint256 start, uint256 end) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let subjectLength := mload(subject) - if iszero(gt(subjectLength, end)) { end := subjectLength } - if iszero(gt(subjectLength, start)) { start := subjectLength } - if lt(start, end) { - result := mload(0x40) - let resultLength := sub(end, start) - mstore(result, resultLength) - subject := add(subject, start) - let w := not(0x1f) - // Copy the `subject` one word at a time, backwards. - for { let o := and(add(resultLength, 0x1f), w) } 1 {} { - mstore(add(result, o), mload(add(subject, o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - // Zeroize the slot after the string. - mstore(add(add(result, 0x20), resultLength), 0) - // Allocate memory for the length and the bytes, - // rounded up to a multiple of 32. - mstore(0x40, add(result, and(add(resultLength, 0x3f), w))) - } - } - } - - /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. - /// `start` is a byte offset. - function slice(string memory subject, uint256 start) - internal - pure - returns (string memory result) - { - result = slice(subject, start, uint256(int256(-1))); - } - - /// @dev Returns all the indices of `search` in `subject`. - /// The indices are byte offsets. - function indicesOf(string memory subject, string memory search) - internal - pure - returns (uint256[] memory result) - { - /// @solidity memory-safe-assembly - assembly { - let subjectLength := mload(subject) - let searchLength := mload(search) - - if iszero(gt(searchLength, subjectLength)) { - subject := add(subject, 0x20) - search := add(search, 0x20) - result := add(mload(0x40), 0x20) - - let subjectStart := subject - let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) - let h := 0 - if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } - let m := shl(3, sub(0x20, and(searchLength, 0x1f))) - let s := mload(search) - for {} 1 {} { - let t := mload(subject) - // Whether the first `searchLength % 32` bytes of - // `subject` and `search` matches. - if iszero(shr(m, xor(t, s))) { - if h { - if iszero(eq(keccak256(subject, searchLength), h)) { - subject := add(subject, 1) - if iszero(lt(subject, subjectSearchEnd)) { break } - continue - } - } - // Append to `result`. - mstore(result, sub(subject, subjectStart)) - result := add(result, 0x20) - // Advance `subject` by `searchLength`. - subject := add(subject, searchLength) - if searchLength { - if iszero(lt(subject, subjectSearchEnd)) { break } - continue - } - } - subject := add(subject, 1) - if iszero(lt(subject, subjectSearchEnd)) { break } - } - let resultEnd := result - // Assign `result` to the free memory pointer. - result := mload(0x40) - // Store the length of `result`. - mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) - // Allocate memory for result. - // We allocate one more word, so this array can be recycled for {split}. - mstore(0x40, add(resultEnd, 0x20)) - } - } - } - - /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. - function split(string memory subject, string memory delimiter) - internal - pure - returns (string[] memory result) - { - uint256[] memory indices = indicesOf(subject, delimiter); - /// @solidity memory-safe-assembly - assembly { - let w := not(0x1f) - let indexPtr := add(indices, 0x20) - let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) - mstore(add(indicesEnd, w), mload(subject)) - mstore(indices, add(mload(indices), 1)) - let prevIndex := 0 - for {} 1 {} { - let index := mload(indexPtr) - mstore(indexPtr, 0x60) - if iszero(eq(index, prevIndex)) { - let element := mload(0x40) - let elementLength := sub(index, prevIndex) - mstore(element, elementLength) - // Copy the `subject` one word at a time, backwards. - for { let o := and(add(elementLength, 0x1f), w) } 1 {} { - mstore(add(element, o), mload(add(add(subject, prevIndex), o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - // Zeroize the slot after the string. - mstore(add(add(element, 0x20), elementLength), 0) - // Allocate memory for the length and the bytes, - // rounded up to a multiple of 32. - mstore(0x40, add(element, and(add(elementLength, 0x3f), w))) - // Store the `element` into the array. - mstore(indexPtr, element) - } - prevIndex := add(index, mload(delimiter)) - indexPtr := add(indexPtr, 0x20) - if iszero(lt(indexPtr, indicesEnd)) { break } - } - result := indices - if iszero(mload(delimiter)) { - result := add(indices, 0x20) - mstore(result, sub(mload(indices), 2)) - } - } - } - - /// @dev Returns a concatenated string of `a` and `b`. - /// Cheaper than `string.concat()` and does not de-align the free memory pointer. - function concat(string memory a, string memory b) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let w := not(0x1f) - result := mload(0x40) - let aLength := mload(a) - // Copy `a` one word at a time, backwards. - for { let o := and(add(aLength, 0x20), w) } 1 {} { - mstore(add(result, o), mload(add(a, o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - let bLength := mload(b) - let output := add(result, aLength) - // Copy `b` one word at a time, backwards. - for { let o := and(add(bLength, 0x20), w) } 1 {} { - mstore(add(output, o), mload(add(b, o))) - o := add(o, w) // `sub(o, 0x20)`. - if iszero(o) { break } - } - let totalLength := add(aLength, bLength) - let last := add(add(result, 0x20), totalLength) - // Zeroize the slot after the string. - mstore(last, 0) - // Stores the length. - mstore(result, totalLength) - // Allocate memory for the length and the bytes, - // rounded up to a multiple of 32. - mstore(0x40, and(add(last, 0x1f), w)) - } - } - - /// @dev Returns a copy of the string in either lowercase or UPPERCASE. - /// WARNING! This function is only compatible with 7-bit ASCII strings. - function toCase(string memory subject, bool toUpper) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let length := mload(subject) - if length { - result := add(mload(0x40), 0x20) - subject := add(subject, 1) - let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) - let w := not(0) - for { let o := length } 1 {} { - o := add(o, w) - let b := and(0xff, mload(add(subject, o))) - mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) - if iszero(o) { break } - } - result := mload(0x40) - mstore(result, length) // Store the length. - let last := add(add(result, 0x20), length) - mstore(last, 0) // Zeroize the slot after the string. - mstore(0x40, add(last, 0x20)) // Allocate the memory. - } - } - } - - /// @dev Returns a string from a small bytes32 string. - /// `smallString` must be null terminated, or behavior will be undefined. - function fromSmallString(bytes32 smallString) internal pure returns (string memory result) { - if (smallString == bytes32(0)) return result; - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - let n := 0 - for {} 1 {} { - n := add(n, 1) - if iszero(byte(n, smallString)) { break } // Scan for '\0'. - } - mstore(result, n) - let o := add(result, 0x20) - mstore(o, smallString) - mstore(add(o, n), 0) - mstore(0x40, add(result, 0x40)) - } - } - - /// @dev Returns a lowercased copy of the string. - /// WARNING! This function is only compatible with 7-bit ASCII strings. - function lower(string memory subject) internal pure returns (string memory result) { - result = toCase(subject, false); - } - - /// @dev Returns an UPPERCASED copy of the string. - /// WARNING! This function is only compatible with 7-bit ASCII strings. - function upper(string memory subject) internal pure returns (string memory result) { - result = toCase(subject, true); - } - - /// @dev Escapes the string to be used within HTML tags. - function escapeHTML(string memory s) internal pure returns (string memory result) { - /// @solidity memory-safe-assembly - assembly { - let end := add(s, mload(s)) - result := add(mload(0x40), 0x20) - // Store the bytes of the packed offsets and strides into the scratch space. - // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. - mstore(0x1f, 0x900094) - mstore(0x08, 0xc0000000a6ab) - // Store ""&'<>" into the scratch space. - mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) - for {} iszero(eq(s, end)) {} { - s := add(s, 1) - let c := and(mload(s), 0xff) - // Not in `["\"","'","&","<",">"]`. - if iszero(and(shl(c, 1), 0x500000c400000000)) { - mstore8(result, c) - result := add(result, 1) - continue - } - let t := shr(248, mload(c)) - mstore(result, mload(and(t, 0x1f))) - result := add(result, shr(5, t)) - } - let last := result - mstore(last, 0) // Zeroize the slot after the string. - result := mload(0x40) - mstore(result, sub(last, add(result, 0x20))) // Store the length. - mstore(0x40, add(last, 0x20)) // Allocate the memory. - } - } - - /// @dev Escapes the string to be used within double-quotes in a JSON. - /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. - function escapeJSON(string memory s, bool addDoubleQuotes) - internal - pure - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - let end := add(s, mload(s)) - result := add(mload(0x40), 0x20) - if addDoubleQuotes { - mstore8(result, 34) - result := add(1, result) - } - // Store "\\u0000" in scratch space. - // Store "0123456789abcdef" in scratch space. - // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. - // into the scratch space. - mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) - // Bitmask for detecting `["\"","\\"]`. - let e := or(shl(0x22, 1), shl(0x5c, 1)) - for {} iszero(eq(s, end)) {} { - s := add(s, 1) - let c := and(mload(s), 0xff) - if iszero(lt(c, 0x20)) { - if iszero(and(shl(c, 1), e)) { - // Not in `["\"","\\"]`. - mstore8(result, c) - result := add(result, 1) - continue - } - mstore8(result, 0x5c) // "\\". - mstore8(add(result, 1), c) - result := add(result, 2) - continue - } - if iszero(and(shl(c, 1), 0x3700)) { - // Not in `["\b","\t","\n","\f","\d"]`. - mstore8(0x1d, mload(shr(4, c))) // Hex value. - mstore8(0x1e, mload(and(c, 15))) // Hex value. - mstore(result, mload(0x19)) // "\\u00XX". - result := add(result, 6) - continue - } - mstore8(result, 0x5c) // "\\". - mstore8(add(result, 1), mload(add(c, 8))) - result := add(result, 2) - } - if addDoubleQuotes { - mstore8(result, 34) - result := add(1, result) - } - let last := result - mstore(last, 0) // Zeroize the slot after the string. - result := mload(0x40) - mstore(result, sub(last, add(result, 0x20))) // Store the length. - mstore(0x40, add(last, 0x20)) // Allocate the memory. - } - } - - /// @dev Escapes the string to be used within double-quotes in a JSON. - function escapeJSON(string memory s) internal pure returns (string memory result) { - result = escapeJSON(s, false); - } - - /// @dev Returns whether `a` equals `b`. - function eq(string memory a, string memory b) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) - } - } - - /// @dev Returns whether `a` equals `b`. For small strings up to 32 bytes. - /// `b` must be null terminated, or behavior will be undefined. - function eqs(string memory a, bytes32 b) internal pure returns (bool result) { - /// @solidity memory-safe-assembly - assembly { - // These should be evaluated on compile time, as far as possible. - let x := and(b, add(not(b), 1)) - let r := or(shl(8, iszero(b)), shl(7, iszero(iszero(shr(128, x))))) - r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - result := gt(eq(mload(a), sub(32, shr(3, r))), shr(r, xor(b, mload(add(a, 0x20))))) - } - } - - /// @dev Packs a single string with its length into a single word. - /// Returns `bytes32(0)` if the length is zero or greater than 31. - function packOne(string memory a) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - // We don't need to zero right pad the string, - // since this is our own custom non-standard packing scheme. - result := - mul( - // Load the length and the bytes. - mload(add(a, 0x1f)), - // `length != 0 && length < 32`. Abuses underflow. - // Assumes that the length is valid and within the block gas limit. - lt(sub(mload(a), 1), 0x1f) - ) - } - } - - /// @dev Unpacks a string packed using {packOne}. - /// Returns the empty string if `packed` is `bytes32(0)`. - /// If `packed` is not an output of {packOne}, the output behavior is undefined. - function unpackOne(bytes32 packed) internal pure returns (string memory result) { - /// @solidity memory-safe-assembly - assembly { - // Grab the free memory pointer. - result := mload(0x40) - // Allocate 2 words (1 for the length, 1 for the bytes). - mstore(0x40, add(result, 0x40)) - // Zeroize the length slot. - mstore(result, 0) - // Store the length and bytes. - mstore(add(result, 0x1f), packed) - // Right pad with zeroes. - mstore(add(add(result, 0x20), mload(result)), 0) - } - } - - /// @dev Packs two strings with their lengths into a single word. - /// Returns `bytes32(0)` if combined length is zero or greater than 30. - function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let aLength := mload(a) - // We don't need to zero right pad the strings, - // since this is our own custom non-standard packing scheme. - result := - mul( - // Load the length and the bytes of `a` and `b`. - or( - shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), - mload(sub(add(b, 0x1e), aLength)) - ), - // `totalLength != 0 && totalLength < 31`. Abuses underflow. - // Assumes that the lengths are valid and within the block gas limit. - lt(sub(add(aLength, mload(b)), 1), 0x1e) - ) - } - } - - /// @dev Unpacks strings packed using {packTwo}. - /// Returns the empty strings if `packed` is `bytes32(0)`. - /// If `packed` is not an output of {packTwo}, the output behavior is undefined. - function unpackTwo(bytes32 packed) - internal - pure - returns (string memory resultA, string memory resultB) - { - /// @solidity memory-safe-assembly - assembly { - // Grab the free memory pointer. - resultA := mload(0x40) - resultB := add(resultA, 0x40) - // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. - mstore(0x40, add(resultB, 0x40)) - // Zeroize the length slots. - mstore(resultA, 0) - mstore(resultB, 0) - // Store the lengths and bytes. - mstore(add(resultA, 0x1f), packed) - mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) - // Right pad with zeroes. - mstore(add(add(resultA, 0x20), mload(resultA)), 0) - mstore(add(add(resultB, 0x20), mload(resultB)), 0) - } - } - - /// @dev Directly returns `a` without copying. - function directReturn(string memory a) internal pure { - assembly { - // Assumes that the string does not start from the scratch space. - let retStart := sub(a, 0x20) - let retSize := add(mload(a), 0x40) - // Right pad with zeroes. Just in case the string is produced - // by a method that doesn't zero right pad. - mstore(add(retStart, retSize), 0) - // Store the return offset. - mstore(retStart, 0x20) - // End the transaction, returning the string. - return(retStart, retSize) - } - } -} diff --git a/lib/solady/src/utils/LibZip.sol b/lib/solady/src/utils/LibZip.sol deleted file mode 100644 index 0fe8d82..0000000 --- a/lib/solady/src/utils/LibZip.sol +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for compressing and decompressing bytes. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibZip.sol) -/// @author Calldata compression by clabby (https://github.com/clabby/op-kompressor) -/// @author FastLZ by ariya (https://github.com/ariya/FastLZ) -/// -/// @dev Note: -/// The accompanying solady.js library includes implementations of -/// FastLZ and calldata operations for convenience. -library LibZip { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* FAST LZ OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // LZ77 implementation based on FastLZ. - // Equivalent to level 1 compression and decompression at the following commit: - // https://github.com/ariya/FastLZ/commit/344eb4025f9ae866ebf7a2ec48850f7113a97a42 - // Decompression is backwards compatible. - - /// @dev Returns the compressed `data`. - function flzCompress(bytes memory data) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - function ms8(d_, v_) -> _d { - mstore8(d_, v_) - _d := add(d_, 1) - } - function u24(p_) -> _u { - let w := mload(p_) - _u := or(shl(16, byte(2, w)), or(shl(8, byte(1, w)), byte(0, w))) - } - function cmp(p_, q_, e_) -> _l { - for { e_ := sub(e_, q_) } lt(_l, e_) { _l := add(_l, 1) } { - e_ := mul(iszero(byte(0, xor(mload(add(p_, _l)), mload(add(q_, _l))))), e_) - } - } - function literals(runs_, src_, dest_) -> _o { - for { _o := dest_ } iszero(lt(runs_, 0x20)) { runs_ := sub(runs_, 0x20) } { - mstore(ms8(_o, 31), mload(src_)) - _o := add(_o, 0x21) - src_ := add(src_, 0x20) - } - if iszero(runs_) { leave } - mstore(ms8(_o, sub(runs_, 1)), mload(src_)) - _o := add(1, add(_o, runs_)) - } - function match(l_, d_, o_) -> _o { - for { d_ := sub(d_, 1) } iszero(lt(l_, 263)) { l_ := sub(l_, 262) } { - o_ := ms8(ms8(ms8(o_, add(224, shr(8, d_))), 253), and(0xff, d_)) - } - if iszero(lt(l_, 7)) { - _o := ms8(ms8(ms8(o_, add(224, shr(8, d_))), sub(l_, 7)), and(0xff, d_)) - leave - } - _o := ms8(ms8(o_, add(shl(5, l_), shr(8, d_))), and(0xff, d_)) - } - function setHash(i_, v_) { - let p := add(mload(0x40), shl(2, i_)) - mstore(p, xor(mload(p), shl(224, xor(shr(224, mload(p)), v_)))) - } - function getHash(i_) -> _h { - _h := shr(224, mload(add(mload(0x40), shl(2, i_)))) - } - function hash(v_) -> _r { - _r := and(shr(19, mul(2654435769, v_)), 0x1fff) - } - function setNextHash(ip_, ipStart_) -> _ip { - setHash(hash(u24(ip_)), sub(ip_, ipStart_)) - _ip := add(ip_, 1) - } - codecopy(mload(0x40), codesize(), 0x8000) // Zeroize the hashmap. - let op := add(mload(0x40), 0x8000) - let a := add(data, 0x20) - let ipStart := a - let ipLimit := sub(add(ipStart, mload(data)), 13) - for { let ip := add(2, a) } lt(ip, ipLimit) {} { - let r := 0 - let d := 0 - for {} 1 {} { - let s := u24(ip) - let h := hash(s) - r := add(ipStart, getHash(h)) - setHash(h, sub(ip, ipStart)) - d := sub(ip, r) - if iszero(lt(ip, ipLimit)) { break } - ip := add(ip, 1) - if iszero(gt(d, 0x1fff)) { if eq(s, u24(r)) { break } } - } - if iszero(lt(ip, ipLimit)) { break } - ip := sub(ip, 1) - if gt(ip, a) { op := literals(sub(ip, a), a, op) } - let l := cmp(add(r, 3), add(ip, 3), add(ipLimit, 9)) - op := match(l, d, op) - ip := setNextHash(setNextHash(add(ip, l), ipStart), ipStart) - a := ip - } - op := literals(sub(add(ipStart, mload(data)), a), a, op) - result := mload(0x40) - let t := add(result, 0x8000) - let n := sub(op, t) - mstore(result, n) // Store the length. - // Copy the result to compact the memory, overwriting the hashmap. - let o := add(result, 0x20) - for { let i } lt(i, n) { i := add(i, 0x20) } { mstore(add(o, i), mload(add(t, i))) } - mstore(add(o, n), 0) // Zeroize the slot after the string. - mstore(0x40, add(add(o, n), 0x20)) // Allocate the memory. - } - } - - /// @dev Returns the decompressed `data`. - function flzDecompress(bytes memory data) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - let n := 0 - let end := add(add(data, 0x20), mload(data)) - result := mload(0x40) - let op := add(result, 0x20) - for { data := add(data, 0x20) } lt(data, end) {} { - let w := mload(data) - let c := byte(0, w) - let t := shr(5, c) - if iszero(t) { - mstore(add(op, n), mload(add(data, 1))) - data := add(data, add(2, c)) - n := add(n, add(1, c)) - continue - } - let g := eq(t, 7) - let l := add(2, xor(t, mul(g, xor(t, add(7, byte(1, w)))))) - for { - let s := add(add(shl(8, and(0x1f, c)), byte(add(1, g), w)), 1) - let r := add(op, sub(n, s)) - let o := add(op, n) - let f := xor(s, mul(gt(s, 0x20), xor(s, 0x20))) - let j := 0 - } 1 {} { - mstore(add(o, j), mload(add(r, j))) - j := add(j, f) - if iszero(lt(j, l)) { break } - } - data := add(data, add(2, g)) - n := add(n, l) - } - mstore(result, n) // Store the length. - let o := add(add(result, 0x20), n) - mstore(o, 0) // Zeroize the slot after the string. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CALLDATA OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Calldata compression and decompression using selective run length encoding: - // - Sequences of 0x00 (up to 128 consecutive). - // - Sequences of 0xff (up to 32 consecutive). - // - // A run length encoded block consists of two bytes: - // (0) 0x00 - // (1) A control byte with the following bit layout: - // - [7] `0: 0x00, 1: 0xff`. - // - [0..6] `runLength - 1`. - // - // The first 4 bytes are bitwise negated so that the compressed calldata - // can be dispatched into the `fallback` and `receive` functions. - - /// @dev Returns the compressed `data`. - function cdCompress(bytes memory data) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - function rle(v_, o_, d_) -> _o, _d { - mstore(o_, shl(240, or(and(0xff, add(d_, 0xff)), and(0x80, v_)))) - _o := add(o_, 2) - } - result := mload(0x40) - let o := add(result, 0x20) - let z := 0 // Number of consecutive 0x00. - let y := 0 // Number of consecutive 0xff. - for { let end := add(data, mload(data)) } iszero(eq(data, end)) {} { - data := add(data, 1) - let c := byte(31, mload(data)) - if iszero(c) { - if y { o, y := rle(0xff, o, y) } - z := add(z, 1) - if eq(z, 0x80) { o, z := rle(0x00, o, 0x80) } - continue - } - if eq(c, 0xff) { - if z { o, z := rle(0x00, o, z) } - y := add(y, 1) - if eq(y, 0x20) { o, y := rle(0xff, o, 0x20) } - continue - } - if y { o, y := rle(0xff, o, y) } - if z { o, z := rle(0x00, o, z) } - mstore8(o, c) - o := add(o, 1) - } - if y { o, y := rle(0xff, o, y) } - if z { o, z := rle(0x00, o, z) } - // Bitwise negate the first 4 bytes. - mstore(add(result, 4), not(mload(add(result, 4)))) - mstore(result, sub(o, add(result, 0x20))) // Store the length. - mstore(o, 0) // Zeroize the slot after the string. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - - /// @dev Returns the decompressed `data`. - function cdDecompress(bytes memory data) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - if mload(data) { - result := mload(0x40) - let o := add(result, 0x20) - let s := add(data, 4) - let v := mload(s) - let end := add(data, mload(data)) - mstore(s, not(v)) // Bitwise negate the first 4 bytes. - for {} lt(data, end) {} { - data := add(data, 1) - let c := byte(31, mload(data)) - if iszero(c) { - data := add(data, 1) - let d := byte(31, mload(data)) - // Fill with either 0xff or 0x00. - mstore(o, not(0)) - if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) } - o := add(o, add(and(d, 0x7f), 1)) - continue - } - mstore8(o, c) - o := add(o, 1) - } - mstore(s, v) // Restore the first 4 bytes. - mstore(result, sub(o, add(result, 0x20))) // Store the length. - mstore(o, 0) // Zeroize the slot after the string. - mstore(0x40, add(o, 0x20)) // Allocate the memory. - } - } - } - - /// @dev To be called in the `receive` and `fallback` functions. - /// ``` - /// receive() external payable { LibZip.cdFallback(); } - /// fallback() external payable { LibZip.cdFallback(); } - /// ``` - /// For efficiency, this function will directly return the results, terminating the context. - /// If called internally, it must be called at the end of the function. - function cdFallback() internal { - assembly { - if iszero(calldatasize()) { return(calldatasize(), calldatasize()) } - let o := 0 - let f := not(3) // For negating the first 4 bytes. - for { let i := 0 } lt(i, calldatasize()) {} { - let c := byte(0, xor(add(i, f), calldataload(i))) - i := add(i, 1) - if iszero(c) { - let d := byte(0, xor(add(i, f), calldataload(i))) - i := add(i, 1) - // Fill with either 0xff or 0x00. - mstore(o, not(0)) - if iszero(gt(d, 0x7f)) { codecopy(o, codesize(), add(d, 1)) } - o := add(o, add(and(d, 0x7f), 1)) - continue - } - mstore8(o, c) - o := add(o, 1) - } - let success := delegatecall(gas(), address(), 0x00, o, codesize(), 0x00) - returndatacopy(0x00, 0x00, returndatasize()) - if iszero(success) { revert(0x00, returndatasize()) } - return(0x00, returndatasize()) - } - } -} diff --git a/lib/solady/src/utils/MerkleProofLib.sol b/lib/solady/src/utils/MerkleProofLib.sol deleted file mode 100644 index 967eb98..0000000 --- a/lib/solady/src/utils/MerkleProofLib.sol +++ /dev/null @@ -1,309 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) -library MerkleProofLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* MERKLE PROOF VERIFICATION OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. - function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) - internal - pure - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - if mload(proof) { - // Initialize `offset` to the offset of `proof` elements in memory. - let offset := add(proof, 0x20) - // Left shift by 5 is equivalent to multiplying by 0x20. - let end := add(offset, shl(5, mload(proof))) - // Iterate over proof elements to compute root hash. - for {} 1 {} { - // Slot of `leaf` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, gt(leaf, mload(offset))) - // Store elements to hash contiguously in scratch space. - // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. - mstore(scratch, leaf) - mstore(xor(scratch, 0x20), mload(offset)) - // Reuse `leaf` to store the hash to reduce stack operations. - leaf := keccak256(0x00, 0x40) - offset := add(offset, 0x20) - if iszero(lt(offset, end)) { break } - } - } - isValid := eq(leaf, root) - } - } - - /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. - function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) - internal - pure - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - if proof.length { - // Left shift by 5 is equivalent to multiplying by 0x20. - let end := add(proof.offset, shl(5, proof.length)) - // Initialize `offset` to the offset of `proof` in the calldata. - let offset := proof.offset - // Iterate over proof elements to compute root hash. - for {} 1 {} { - // Slot of `leaf` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, gt(leaf, calldataload(offset))) - // Store elements to hash contiguously in scratch space. - // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. - mstore(scratch, leaf) - mstore(xor(scratch, 0x20), calldataload(offset)) - // Reuse `leaf` to store the hash to reduce stack operations. - leaf := keccak256(0x00, 0x40) - offset := add(offset, 0x20) - if iszero(lt(offset, end)) { break } - } - } - isValid := eq(leaf, root) - } - } - - /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, - /// given `proof` and `flags`. - /// - /// Note: - /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` - /// will always return false. - /// - The sum of the lengths of `proof` and `leaves` must never overflow. - /// - Any non-zero word in the `flags` array is treated as true. - /// - The memory offset of `proof` must be non-zero - /// (i.e. `proof` is not pointing to the scratch space). - function verifyMultiProof( - bytes32[] memory proof, - bytes32 root, - bytes32[] memory leaves, - bool[] memory flags - ) internal pure returns (bool isValid) { - // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leaves` array, and goes into a `hashes` array. - // After the process, the last element on the queue is verified - // to be equal to the `root`. - // - // The `flags` array denotes whether the sibling - // should be popped from the queue (`flag == true`), or - // should be popped from the `proof` (`flag == false`). - /// @solidity memory-safe-assembly - assembly { - // Cache the lengths of the arrays. - let leavesLength := mload(leaves) - let proofLength := mload(proof) - let flagsLength := mload(flags) - - // Advance the pointers of the arrays to point to the data. - leaves := add(0x20, leaves) - proof := add(0x20, proof) - flags := add(0x20, flags) - - // If the number of flags is correct. - for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} { - // For the case where `proof.length + leaves.length == 1`. - if iszero(flagsLength) { - // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. - isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root) - break - } - - // The required final proof offset if `flagsLength` is not zero, otherwise zero. - let proofEnd := add(proof, shl(5, proofLength)) - // We can use the free memory space for the queue. - // We don't need to allocate, since the queue is temporary. - let hashesFront := mload(0x40) - // Copy the leaves into the hashes. - // Sometimes, a little memory expansion costs less than branching. - // Should cost less, even with a high free memory offset of 0x7d00. - leavesLength := shl(5, leavesLength) - for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } { - mstore(add(hashesFront, i), mload(add(leaves, i))) - } - // Compute the back of the hashes. - let hashesBack := add(hashesFront, leavesLength) - // This is the end of the memory for the queue. - // We recycle `flagsLength` to save on stack variables (sometimes save gas). - flagsLength := add(hashesBack, shl(5, flagsLength)) - - for {} 1 {} { - // Pop from `hashes`. - let a := mload(hashesFront) - // Pop from `hashes`. - let b := mload(add(hashesFront, 0x20)) - hashesFront := add(hashesFront, 0x40) - - // If the flag is false, load the next proof, - // else, pops from the queue. - if iszero(mload(flags)) { - // Loads the next proof. - b := mload(proof) - proof := add(proof, 0x20) - // Unpop from `hashes`. - hashesFront := sub(hashesFront, 0x20) - } - - // Advance to the next flag. - flags := add(flags, 0x20) - - // Slot of `a` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, gt(a, b)) - // Hash the scratch space and push the result onto the queue. - mstore(scratch, a) - mstore(xor(scratch, 0x20), b) - mstore(hashesBack, keccak256(0x00, 0x40)) - hashesBack := add(hashesBack, 0x20) - if iszero(lt(hashesBack, flagsLength)) { break } - } - isValid := - and( - // Checks if the last value in the queue is same as the root. - eq(mload(sub(hashesBack, 0x20)), root), - // And whether all the proofs are used, if required. - eq(proofEnd, proof) - ) - break - } - } - } - - /// @dev Returns whether all `leaves` exist in the Merkle tree with `root`, - /// given `proof` and `flags`. - /// - /// Note: - /// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length` - /// will always return false. - /// - Any non-zero word in the `flags` array is treated as true. - /// - The calldata offset of `proof` must be non-zero - /// (i.e. `proof` is from a regular Solidity function with a 4-byte selector). - function verifyMultiProofCalldata( - bytes32[] calldata proof, - bytes32 root, - bytes32[] calldata leaves, - bool[] calldata flags - ) internal pure returns (bool isValid) { - // Rebuilds the root by consuming and producing values on a queue. - // The queue starts with the `leaves` array, and goes into a `hashes` array. - // After the process, the last element on the queue is verified - // to be equal to the `root`. - // - // The `flags` array denotes whether the sibling - // should be popped from the queue (`flag == true`), or - // should be popped from the `proof` (`flag == false`). - /// @solidity memory-safe-assembly - assembly { - // If the number of flags is correct. - for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} { - // For the case where `proof.length + leaves.length == 1`. - if iszero(flags.length) { - // `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`. - // forgefmt: disable-next-item - isValid := eq( - calldataload( - xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length)) - ), - root - ) - break - } - - // The required final proof offset if `flagsLength` is not zero, otherwise zero. - let proofEnd := add(proof.offset, shl(5, proof.length)) - // We can use the free memory space for the queue. - // We don't need to allocate, since the queue is temporary. - let hashesFront := mload(0x40) - // Copy the leaves into the hashes. - // Sometimes, a little memory expansion costs less than branching. - // Should cost less, even with a high free memory offset of 0x7d00. - calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length)) - // Compute the back of the hashes. - let hashesBack := add(hashesFront, shl(5, leaves.length)) - // This is the end of the memory for the queue. - // We recycle `flagsLength` to save on stack variables (sometimes save gas). - flags.length := add(hashesBack, shl(5, flags.length)) - - // We don't need to make a copy of `proof.offset` or `flags.offset`, - // as they are pass-by-value (this trick may not always save gas). - - for {} 1 {} { - // Pop from `hashes`. - let a := mload(hashesFront) - // Pop from `hashes`. - let b := mload(add(hashesFront, 0x20)) - hashesFront := add(hashesFront, 0x40) - - // If the flag is false, load the next proof, - // else, pops from the queue. - if iszero(calldataload(flags.offset)) { - // Loads the next proof. - b := calldataload(proof.offset) - proof.offset := add(proof.offset, 0x20) - // Unpop from `hashes`. - hashesFront := sub(hashesFront, 0x20) - } - - // Advance to the next flag offset. - flags.offset := add(flags.offset, 0x20) - - // Slot of `a` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl(5, gt(a, b)) - // Hash the scratch space and push the result onto the queue. - mstore(scratch, a) - mstore(xor(scratch, 0x20), b) - mstore(hashesBack, keccak256(0x00, 0x40)) - hashesBack := add(hashesBack, 0x20) - if iszero(lt(hashesBack, flags.length)) { break } - } - isValid := - and( - // Checks if the last value in the queue is same as the root. - eq(mload(sub(hashesBack, 0x20)), root), - // And whether all the proofs are used, if required. - eq(proofEnd, proof.offset) - ) - break - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EMPTY CALLDATA HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an empty calldata bytes32 array. - function emptyProof() internal pure returns (bytes32[] calldata proof) { - /// @solidity memory-safe-assembly - assembly { - proof.length := 0 - } - } - - /// @dev Returns an empty calldata bytes32 array. - function emptyLeaves() internal pure returns (bytes32[] calldata leaves) { - /// @solidity memory-safe-assembly - assembly { - leaves.length := 0 - } - } - - /// @dev Returns an empty calldata bool array. - function emptyFlags() internal pure returns (bool[] calldata flags) { - /// @solidity memory-safe-assembly - assembly { - flags.length := 0 - } - } -} diff --git a/lib/solady/src/utils/MetadataReaderLib.sol b/lib/solady/src/utils/MetadataReaderLib.sol deleted file mode 100644 index e44c249..0000000 --- a/lib/solady/src/utils/MetadataReaderLib.sol +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for reading contract metadata robustly. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MetadataReaderLib.sol) -library MetadataReaderLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* METADATA READING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Best-effort string reading operations. - // Should NOT revert as long as sufficient gas is provided. - // - // Performs the following in order: - // 1. Returns the empty string for the following cases: - // - Reverts. - // - No returndata (e.g. function returns nothing, EOA). - // - Returns empty string. - // 2. Attempts to `abi.decode` the returndata into a string. - // 3. With any remaining gas, scans the returndata from start to end for the - // null byte '\0', to interpret the returndata as a null-terminated string. - - /// @dev Equivalent to `readString(abi.encodeWithSignature("name()"), limit)`. - function readName(address target, uint256 limit) internal view returns (string memory) { - return _string(target, _ptr(0x06fdde03), limit); - } - - /// @dev Equivalent to `readString(abi.encodeWithSignature("symbol()"), limit)`. - function readSymbol(address target, uint256 limit) internal view returns (string memory) { - return _string(target, _ptr(0x95d89b41), limit); - } - - /// @dev Performs a best-effort string query on `target` with `data` as the calldata. - /// The string will be truncated to `limit` bytes. - function readString(address target, bytes memory data, uint256 limit) - internal - view - returns (string memory) - { - return _string(target, _ptr(data), limit); - } - - // Best-effort unsigned integer reading operations. - // Should NOT revert as long as sufficient gas is provided. - // - // Performs the following in order: - // 1. Attempts to `abi.decode` the result into a uint256 - // (equivalent across all Solidity uint types, downcast as needed). - // 2. Returns zero for the following cases: - // - Reverts. - // - No returndata (e.g. function returns nothing, EOA). - // - Returns zero. - // - `abi.decode` failure. - - /// @dev Equivalent to `uint8(readUint(abi.encodeWithSignature("decimal()")))`. - function readDecimals(address target) internal view returns (uint8) { - return uint8(_uint(target, _ptr(0x313ce567))); - } - - /// @dev Performs a best-effort uint query on `target` with `data` as the calldata. - function readUint(address target, bytes memory data) internal view returns (uint256) { - return _uint(target, _ptr(data)); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Attempts to read and return a string at `target`. - function _string(address target, bytes32 ptr, uint256 limit) - private - view - returns (string memory result) - { - /// @solidity memory-safe-assembly - assembly { - function min(x_, y_) -> _z { - _z := xor(x_, mul(xor(x_, y_), lt(y_, x_))) - } - for {} staticcall(gas(), target, add(ptr, 0x20), mload(ptr), 0x00, 0x20) {} { - let m := mload(0x40) // Grab the free memory pointer. - let s := add(0x20, m) // Start of the string's bytes in memory. - // Attempt to `abi.decode` if the returndatasize is greater or equal to 64. - if iszero(lt(returndatasize(), 0x40)) { - let o := mload(0x00) // Load the string's offset in the returndata. - // If the string's offset is within bounds. - if iszero(gt(o, sub(returndatasize(), 0x20))) { - returndatacopy(m, o, 0x20) // Copy the string's length. - // If the full string's end is within bounds. - // Note: If the full string doesn't fit, the `abi.decode` must be aborted - // for compliance purposes, regardless if the truncated string can fit. - if iszero(gt(mload(m), sub(returndatasize(), add(o, 0x20)))) { - let n := min(mload(m), limit) // Truncate if needed. - mstore(m, n) // Overwrite the length. - returndatacopy(s, add(o, 0x20), n) // Copy the string's bytes. - mstore(add(s, n), 0) // Zeroize the slot after the string. - mstore(0x40, add(0x20, add(s, n))) // Allocate memory for the string. - result := m - break - } - } - } - // Try interpreting as a null-terminated string. - let n := min(returndatasize(), limit) // Truncate if needed. - returndatacopy(s, 0, n) // Copy the string's bytes. - mstore8(add(s, n), 0) // Place a '\0' at the end. - let i := s // Pointer to the next byte to scan. - for {} byte(0, mload(i)) { i := add(i, 1) } {} // Scan for '\0'. - mstore(m, sub(i, s)) // Store the string's length. - mstore(i, 0) // Zeroize the slot after the string. - mstore(0x40, add(0x20, i)) // Allocate memory for the string. - result := m - break - } - } - } - - /// @dev Attempts to read and return a uint at `target`. - function _uint(address target, bytes32 ptr) private view returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - result := - mul( - mload(0x20), - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), target, add(ptr, 0x20), mload(ptr), 0x20, 0x20) - ) - ) - } - } - - /// @dev Casts the function selector `s` into a pointer. - function _ptr(uint256 s) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - // Layout the calldata in the scratch space for temporary usage. - mstore(0x04, s) // Store the function selector. - mstore(result, 4) // Store the length. - } - } - - /// @dev Casts the `data` into a pointer. - function _ptr(bytes memory data) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - result := data - } - } -} diff --git a/lib/solady/src/utils/MinHeapLib.sol b/lib/solady/src/utils/MinHeapLib.sol deleted file mode 100644 index 6facfa9..0000000 --- a/lib/solady/src/utils/MinHeapLib.sol +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for managing a min-heap in storage. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MinHeapLib.sol) -library MinHeapLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The heap is empty. - error HeapIsEmpty(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A heap in storage. - struct Heap { - uint256[] data; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // Tips: - // - To use as a max-map, negate the values. - // - If use on tuples, pack the tuple values into a single integer. - // - To use on signed integers, convert the signed integers into - // their ordered unsigned counterparts via `uint256(x) + (1 << 255)`. - - /// @dev Returns the minimum value of the heap. - /// Reverts if the heap is empty. - function root(Heap storage heap) internal view returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - if iszero(sload(heap.slot)) { - mstore(0x00, 0xa6ca772e) // Store the function selector of `HeapIsEmpty()`. - revert(0x1c, 0x04) // Revert with (offset, size). - } - mstore(0x00, heap.slot) - result := sload(keccak256(0x00, 0x20)) - } - } - - /// @dev Returns the number of items in the heap. - function length(Heap storage heap) internal view returns (uint256) { - return heap.data.length; - } - - /// @dev Pushes the `value` onto the min-heap. - function push(Heap storage heap, uint256 value) internal { - _set(heap, value, 0, 4); - } - - /// @dev Pops the minimum value from the min-heap. - /// Reverts if the heap is empty. - function pop(Heap storage heap) internal returns (uint256 popped) { - (,, popped) = _set(heap, 0, 0, 3); - } - - /// @dev Pushes the `value` onto the min-heap, and pops the minimum value. - function pushPop(Heap storage heap, uint256 value) internal returns (uint256 popped) { - (,, popped) = _set(heap, value, 0, 2); - } - - /// @dev Pops the minimum value, and pushes the new `value` onto the min-heap. - /// Reverts if the heap is empty. - function replace(Heap storage heap, uint256 value) internal returns (uint256 popped) { - (,, popped) = _set(heap, value, 0, 1); - } - - /// @dev Pushes the `value` onto the min-heap, and pops the minimum value - /// if the length of the heap exceeds `maxLength`. - /// - /// Reverts if `maxLength` is zero. - /// - /// - If the queue is not full: - /// (`success` = true, `hasPopped` = false, `popped` = 0) - /// - If the queue is full, and `value` is not greater than the minimum value: - /// (`success` = false, `hasPopped` = false, `popped` = 0) - /// - If the queue is full, and `value` is greater than the minimum value: - /// (`success` = true, `hasPopped` = true, `popped` = ) - /// - /// Useful for implementing a bounded priority queue. - function enqueue(Heap storage heap, uint256 value, uint256 maxLength) - internal - returns (bool success, bool hasPopped, uint256 popped) - { - (success, hasPopped, popped) = _set(heap, value, maxLength, 0); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Helper function for heap operations. - /// Designed for code conciseness, bytecode compactness, and decent performance. - function _set(Heap storage heap, uint256 value, uint256 maxLength, uint256 mode) - private - returns (bool success, bool hasPopped, uint256 popped) - { - /// @solidity memory-safe-assembly - assembly { - let n := sload(heap.slot) - // Compute the array storage slot offset. - mstore(0x00, heap.slot) - let sOffset := keccak256(0x00, 0x20) - - let pos := 0 - let childPos := not(0) - // Operations are ordered from most likely usage to least likely usage. - for {} 1 { - mstore(0x00, 0xa6ca772e) // Store the function selector of `HeapIsEmpty()`. - revert(0x1c, 0x04) // Revert with (offset, size). - } { - // `enqueue`. - if iszero(mode) { - if iszero(maxLength) { continue } - if iszero(eq(n, maxLength)) { - // If queue is not full. - success := 1 - // Increment and update the length. - pos := n - sstore(heap.slot, add(pos, 1)) - childPos := add(childPos, childPos) - break - } - let r := sload(sOffset) - if iszero(lt(r, value)) { break } - success := 1 - hasPopped := 1 - childPos := 1 - popped := r - break - } - // `replace`. - if eq(mode, 1) { - if iszero(n) { continue } - popped := sload(sOffset) - childPos := 1 - break - } - // `pushPop`. - if eq(mode, 2) { - popped := value - if iszero(n) { break } - let r := sload(sOffset) - if iszero(lt(r, value)) { break } - popped := r - childPos := 1 - break - } - // `pop`. - if eq(mode, 3) { - if iszero(n) { continue } - // Decrement and update the length. - n := sub(n, 1) - sstore(heap.slot, n) - // Set the `value` to the last item. - value := sload(add(sOffset, n)) - popped := value - if iszero(n) { break } - popped := sload(sOffset) - childPos := 1 - break - } - // `push`. - { - // Increment and update the length. - pos := n - sstore(heap.slot, add(pos, 1)) - childPos := add(childPos, childPos) - break - } - } - - for {} lt(childPos, n) {} { - let child := sload(add(sOffset, childPos)) - let rightPos := add(childPos, 1) - let right := sload(add(sOffset, rightPos)) - if iszero(and(lt(rightPos, n), iszero(lt(child, right)))) { - right := child - rightPos := childPos - } - sstore(add(sOffset, pos), right) - pos := rightPos - childPos := add(shl(1, pos), 1) - } - - for {} pos {} { - let parentPos := shr(1, sub(pos, 1)) - let parent := sload(add(sOffset, parentPos)) - if iszero(lt(value, parent)) { break } - sstore(add(sOffset, pos), parent) - pos := parentPos - } - - // If `childPos` is not `not(0)`. - if add(childPos, 1) { sstore(add(sOffset, pos), value) } - } - } -} diff --git a/lib/solady/src/utils/Multicallable.sol b/lib/solady/src/utils/Multicallable.sol deleted file mode 100644 index 1c0a5c4..0000000 --- a/lib/solady/src/utils/Multicallable.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Contract that enables a single call to call multiple methods on itself. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol) -abstract contract Multicallable { - /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`, - /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`. - /// If any of the `DELEGATECALL`s reverts, the entire context is reverted, - /// and the error is bubbled up. - /// - /// This function is deliberately made non-payable to guard against double-spending. - /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong) - /// - /// For efficiency, this function will directly return the results, terminating the context. - /// If called internally, it must be called at the end of a function - /// that returns `(bytes[] memory)`. - function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) { - assembly { - mstore(0x00, 0x20) - mstore(0x20, data.length) // Store `data.length` into `results`. - // Early return if no data. - if iszero(data.length) { return(0x00, 0x40) } - - let results := 0x40 - // `shl` 5 is equivalent to multiplying by 0x20. - let end := shl(5, data.length) - // Copy the offsets from calldata into memory. - calldatacopy(0x40, data.offset, end) - // Offset into `results`. - let resultsOffset := end - // Pointer to the end of `results`. - end := add(results, end) - - for {} 1 {} { - // The offset of the current bytes in the calldata. - let o := add(data.offset, mload(results)) - let m := add(resultsOffset, 0x40) - // Copy the current bytes from calldata to the memory. - calldatacopy( - m, - add(o, 0x20), // The offset of the current bytes' bytes. - calldataload(o) // The length of the current bytes. - ) - if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) { - // Bubble up the revert if the delegatecall reverts. - returndatacopy(0x00, 0x00, returndatasize()) - revert(0x00, returndatasize()) - } - // Append the current `resultsOffset` into `results`. - mstore(results, resultsOffset) - results := add(results, 0x20) - // Append the `returndatasize()`, and the return data. - mstore(m, returndatasize()) - returndatacopy(add(m, 0x20), 0x00, returndatasize()) - // Advance the `resultsOffset` by `returndatasize() + 0x20`, - // rounded up to the next multiple of 32. - resultsOffset := - and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0) - if iszero(lt(results, end)) { break } - } - return(0x00, add(resultsOffset, 0x40)) - } - } -} diff --git a/lib/solady/src/utils/RedBlackTreeLib.sol b/lib/solady/src/utils/RedBlackTreeLib.sol deleted file mode 100644 index 6ed7dfb..0000000 --- a/lib/solady/src/utils/RedBlackTreeLib.sol +++ /dev/null @@ -1,729 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Library for managing a red-black-tree in storage. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/RedBlackTreeLib.sol) -/// @author Modified from BokkyPooBahsRedBlackTreeLibrary -/// (https://github.com/bokkypoobah/BokkyPooBahsRedBlackTreeLibrary) -/// @dev This red-black-tree does not support the zero (i.e. empty) value. -library RedBlackTreeLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The value cannot be zero. - error ValueIsEmpty(); - - /// @dev Cannot insert a value that already exists. - error ValueAlreadyExists(); - - /// @dev Cannot remove a value that does not exist. - error ValueDoesNotExist(); - - /// @dev The pointer is out of bounds. - error PointerOutOfBounds(); - - /// @dev The tree is full. - error TreeIsFull(); - - /// @dev `bytes4(keccak256(bytes("ValueAlreadyExists()")))`. - uint256 internal constant ERROR_VALUE_ALREADY_EXISTS = 0xbb33e6ac; - - /// @dev `bytes4(keccak256(bytes("ValueDoesNotExist()")))`. - uint256 internal constant ERROR_VALUE_DOES_NOT_EXISTS = 0xb113638a; - - /// @dev `bytes4(keccak256(bytes("PointerOutOfBounds()")))`. - uint256 internal constant ERROR_POINTER_OUT_OF_BOUNDS = 0xccd52fbc; - - /// @dev `bytes4(keccak256(bytes("TreeIsFull()")))`. - uint256 internal constant ERROR_TREE_IS_FULL = 0xed732d0c; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STRUCTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev A red-black-tree in storage. - struct Tree { - uint256 _spacer; - } - - // Custom storage: - // ``` - // mstore(0x20, tree.slot) - // mstore(0x00, _NODES_SLOT_SEED) - // let nodes := shl(_NODES_SLOT_SHIFT, keccak256(0x00, 0x40)) - // - // let root := shr(128, sload(nodes)) - // let totalNodes := and(sload(nodes), _BITMASK_KEY) - // - // let nodePacked := sload(or(nodes, nodeIndex)) - // let nodeLeft := and(nodePacked, _BITMASK_KEY) - // let nodeRight := and(shr(_BITPOS_RIGHT, nodePacked), _BITMASK_KEY) - // let nodeParent := and(shr(_BITPOS_PARENT, nodePacked), _BITMASK_KEY) - // let nodeRed := and(shr(_BITPOS_RED, nodePacked), 1) - // - // let nodeValue := shr(_BITPOS_PACKED_VALUE, nodePacked) - // if iszero(nodeValue) { - // nodeValue := sload(or(_BIT_FULL_VALUE_SLOT, or(nodes, nodeIndex))) - // } - // ``` - - uint256 private constant _NODES_SLOT_SEED = 0x1dc27bb5462fdadcb; - uint256 private constant _NODES_SLOT_SHIFT = 32; - uint256 private constant _BITMASK_KEY = (1 << 31) - 1; - uint256 private constant _BITPOS_LEFT = 0; - uint256 private constant _BITPOS_RIGHT = 31; - uint256 private constant _BITPOS_PARENT = 31 * 2; - uint256 private constant _BITPOS_RED = 31 * 3; - uint256 private constant _BITMASK_RED = 1 << (31 * 3); - uint256 private constant _BITPOS_PACKED_VALUE = 96; - uint256 private constant _BITMASK_PACKED_VALUE = (1 << 160) - 1; - uint256 private constant _BIT_FULL_VALUE_SLOT = 1 << 31; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns the number of unique values in the tree. - function size(Tree storage tree) internal view returns (uint256 result) { - uint256 nodes = _nodes(tree); - /// @solidity memory-safe-assembly - assembly { - result := and(sload(nodes), _BITMASK_KEY) - } - } - - /// @dev Returns a pointer to the value `x`. - /// If the value `x` is not in the tree, the returned pointer will be empty. - function find(Tree storage tree, uint256 x) internal view returns (bytes32 result) { - (uint256 nodes,, uint256 key) = _find(tree, x); - result = _pack(nodes, key); - } - - /// @dev Returns a pointer to the nearest value to `x`. - /// In a tie-breaker, the returned pointer will point to the smaller value. - /// If the tree is empty, the returned pointer will be empty. - function nearest(Tree storage tree, uint256 x) internal view returns (bytes32 result) { - (uint256 nodes, uint256 cursor, uint256 key) = _find(tree, x); - unchecked { - if (cursor == 0) return result; // Nothing found -- empty tree. - if (key != 0) return _pack(nodes, key); // Exact match. - bytes32 a = _pack(nodes, cursor); - uint256 aValue = value(a); - bytes32 b = x < aValue ? prev(a) : next(a); - if (b == bytes32(0)) return a; // Only node found. - uint256 bValue = value(b); - uint256 aDist = x < aValue ? aValue - x : x - aValue; - uint256 bDist = x < bValue ? bValue - x : x - bValue; - if (aDist == bDist) return aValue < bValue ? a : b; // Tie-breaker. - return aDist < bDist ? a : b; - } - } - - /// @dev Returns a pointer to the nearest value lesser or equal to `x`. - /// If there is no value lesser or equal to `x`, the returned pointer will be empty. - function nearestBefore(Tree storage tree, uint256 x) internal view returns (bytes32 result) { - (uint256 nodes, uint256 cursor, uint256 key) = _find(tree, x); - if (cursor == 0) return result; // Nothing found -- empty tree. - if (key != 0) return _pack(nodes, key); // Exact match. - bytes32 a = _pack(nodes, cursor); - return value(a) < x ? a : prev(a); - } - - /// @dev Returns a pointer to the nearest value greater or equal to `x`. - /// If there is no value greater or equal to `x`, the returned pointer will be empty. - function nearestAfter(Tree storage tree, uint256 x) internal view returns (bytes32 result) { - (uint256 nodes, uint256 cursor, uint256 key) = _find(tree, x); - if (cursor == 0) return result; // Nothing found -- empty tree. - if (key != 0) return _pack(nodes, key); // Exact match. - bytes32 a = _pack(nodes, cursor); - return value(a) > x ? a : next(a); - } - - /// @dev Returns whether the value `x` exists. - function exists(Tree storage tree, uint256 x) internal view returns (bool result) { - (,, uint256 key) = _find(tree, x); - result = key != 0; - } - - /// @dev Inserts the value `x` into the tree. - /// Reverts if the value `x` already exists. - function insert(Tree storage tree, uint256 x) internal { - uint256 err = tryInsert(tree, x); - if (err != 0) _revert(err); - } - - /// @dev Inserts the value `x` into the tree. - /// Returns a non-zero error code upon failure instead of reverting - /// (except for reverting if `x` is an empty value). - function tryInsert(Tree storage tree, uint256 x) internal returns (uint256 err) { - (uint256 nodes, uint256 cursor, uint256 key) = _find(tree, x); - err = _update(nodes, cursor, key, x, 0); - } - - /// @dev Removes the value `x` from the tree. - /// Reverts if the value does not exist. - function remove(Tree storage tree, uint256 x) internal { - uint256 err = tryRemove(tree, x); - if (err != 0) _revert(err); - } - - /// @dev Removes the value `x` from the tree. - /// Returns a non-zero error code upon failure instead of reverting - /// (except for reverting if `x` is an empty value). - function tryRemove(Tree storage tree, uint256 x) internal returns (uint256 err) { - (uint256 nodes,, uint256 key) = _find(tree, x); - err = _update(nodes, 0, key, 0, 1); - } - - /// @dev Removes the value at pointer `ptr` from the tree. - /// Reverts if `ptr` is empty (i.e. value does not exist), - /// or if `ptr` is out of bounds. - /// After removal, `ptr` may point to another existing value. - /// For safety, do not reuse `ptr` after calling remove on it. - function remove(bytes32 ptr) internal { - uint256 err = tryRemove(ptr); - if (err != 0) _revert(err); - } - - /// @dev Removes the value at pointer `ptr` from the tree. - /// Returns a non-zero error code upon failure instead of reverting. - function tryRemove(bytes32 ptr) internal returns (uint256 err) { - (uint256 nodes, uint256 key) = _unpack(ptr); - err = _update(nodes, 0, key, 0, 1); - } - - /// @dev Clears the entire tree. All data will be deleted from storage. - function clear(Tree storage tree) internal { - uint256 nodes = _nodes(tree); - /// @solidity memory-safe-assembly - assembly { - let totalNodes := and(sload(nodes), _BITMASK_KEY) - for { let i := 1 } iszero(gt(i, totalNodes)) { i := add(i, 1) } { - let ptr := or(nodes, i) - if iszero(shr(_BITPOS_PACKED_VALUE, sload(ptr))) { - sstore(or(ptr, _BIT_FULL_VALUE_SLOT), 0) - } - sstore(ptr, 0) - } - sstore(nodes, 0) - } - } - - /// @dev Returns the value at pointer `ptr`. - /// If `ptr` is empty, the result will be zero. - function value(bytes32 ptr) internal view returns (uint256 result) { - if (ptr == bytes32(0)) return result; - /// @solidity memory-safe-assembly - assembly { - let packed := sload(ptr) - result := shr(_BITPOS_PACKED_VALUE, packed) - if iszero(result) { result := sload(or(ptr, _BIT_FULL_VALUE_SLOT)) } - } - } - - /// @dev Returns a pointer to the smallest value in the tree. - /// If the tree is empty, the returned pointer will be empty. - function first(Tree storage tree) internal view returns (bytes32 result) { - result = _end(tree, _BITPOS_LEFT); - } - - /// @dev Returns a pointer to the largest value in the tree. - /// If the tree is empty, the returned pointer will be empty. - function last(Tree storage tree) internal view returns (bytes32 result) { - result = _end(tree, _BITPOS_RIGHT); - } - - /// @dev Returns the pointer to the next largest value. - /// If there is no next value, or if `ptr` is empty, - /// the returned pointer will be empty. - function next(bytes32 ptr) internal view returns (bytes32 result) { - result = _step(ptr, _BITPOS_LEFT, _BITPOS_RIGHT); - } - - /// @dev Returns the pointer to the next smallest value. - /// If there is no previous value, or if `ptr` is empty, - /// the returned pointer will be empty. - function prev(bytes32 ptr) internal view returns (bytes32 result) { - result = _step(ptr, _BITPOS_RIGHT, _BITPOS_LEFT); - } - - /// @dev Returns whether the pointer is empty. - function isEmpty(bytes32 ptr) internal pure returns (bool result) { - result = ptr == bytes32(0); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unpacks the pointer `ptr` to its components. - function _unpack(bytes32 ptr) private pure returns (uint256 nodes, uint256 key) { - /// @solidity memory-safe-assembly - assembly { - nodes := shl(_NODES_SLOT_SHIFT, shr(_NODES_SLOT_SHIFT, ptr)) - key := and(_BITMASK_KEY, ptr) - } - } - - /// @dev Packs `nodes` and `key` into a single pointer. - function _pack(uint256 nodes, uint256 key) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - result := mul(or(nodes, key), iszero(iszero(key))) - } - } - - /// @dev Returns the pointer to either end of the tree. - function _end(Tree storage tree, uint256 L) private view returns (bytes32 result) { - uint256 nodes = _nodes(tree); - /// @solidity memory-safe-assembly - assembly { - result := shr(128, sload(nodes)) - if result { - for {} 1 {} { - let packed := sload(or(nodes, result)) - let left := and(shr(L, packed), _BITMASK_KEY) - if iszero(left) { break } - result := left - } - } - } - result = _pack(nodes, uint256(result)); - } - - /// @dev Step the pointer `ptr` forwards or backwards. - function _step(bytes32 ptr, uint256 L, uint256 R) private view returns (bytes32 result) { - if (ptr == bytes32(0)) return ptr; - (uint256 nodes, uint256 target) = _unpack(ptr); - /// @solidity memory-safe-assembly - assembly { - let packed := sload(ptr) - for { result := and(shr(R, packed), _BITMASK_KEY) } 1 {} { - if iszero(result) { - result := and(shr(_BITPOS_PARENT, packed), _BITMASK_KEY) - for {} 1 {} { - if iszero(result) { break } - packed := sload(or(nodes, result)) - if iszero(eq(target, and(shr(R, packed), _BITMASK_KEY))) { break } - target := result - result := and(shr(_BITPOS_PARENT, packed), _BITMASK_KEY) - } - break - } - for {} 1 {} { - packed := sload(or(nodes, result)) - let left := and(shr(L, packed), _BITMASK_KEY) - if iszero(left) { break } - result := left - } - break - } - } - result = _pack(nodes, uint256(result)); - } - - /// @dev Inserts or delete the value `x` from the tree. - function _update(uint256 nodes, uint256 cursor, uint256 key, uint256 x, uint256 mode) - private - returns (uint256 err) - { - /// @solidity memory-safe-assembly - assembly { - function getKey(packed_, bitpos_) -> index_ { - index_ := and(shr(bitpos_, packed_), _BITMASK_KEY) - } - - function setKey(packed_, bitpos_, key_) -> result_ { - result_ := or(and(not(shl(bitpos_, _BITMASK_KEY)), packed_), shl(bitpos_, key_)) - } - - function setRed(packed_, red_) -> result_ { - result_ := or(and(not(_BITMASK_RED), packed_), shl(_BITPOS_RED, red_)) - } - - function isRed(packed_) -> red_ { - red_ := and(_BITMASK_RED, packed_) - } - - function copyRed(packed_, fromPacked_) -> result_ { - result_ := or(and(not(_BITMASK_RED), packed_), and(_BITMASK_RED, fromPacked_)) - } - - function rotate(nodes_, key_, L, R) { - let packed_ := sload(or(nodes_, key_)) - let cursor_ := getKey(packed_, R) - let parent_ := getKey(packed_, _BITPOS_PARENT) - let cursorPacked_ := sload(or(nodes_, cursor_)) - let cursorLeft_ := getKey(cursorPacked_, L) - - if cursorLeft_ { - let s_ := or(nodes_, cursorLeft_) - sstore(s_, setKey(sload(s_), _BITPOS_PARENT, key_)) - } - - for {} 1 {} { - if iszero(parent_) { - mstore(0x00, cursor_) - break - } - let s_ := or(nodes_, parent_) - let parentPacked_ := sload(s_) - if eq(key_, getKey(parentPacked_, L)) { - sstore(s_, setKey(parentPacked_, L, cursor_)) - break - } - sstore(s_, setKey(parentPacked_, R, cursor_)) - break - } - packed_ := setKey(packed_, R, cursorLeft_) - sstore(or(nodes_, key_), setKey(packed_, _BITPOS_PARENT, cursor_)) - cursorPacked_ := setKey(cursorPacked_, _BITPOS_PARENT, parent_) - sstore(or(nodes_, cursor_), setKey(cursorPacked_, L, key_)) - } - - function insertFixup(nodes_, key_) { - for {} 1 {} { - if eq(key_, mload(0x00)) { break } - let packed_ := sload(or(nodes_, key_)) - let parent_ := getKey(packed_, _BITPOS_PARENT) - let parentPacked_ := sload(or(nodes_, parent_)) - if iszero(isRed(parentPacked_)) { break } - - let grandParent_ := getKey(parentPacked_, _BITPOS_PARENT) - let grandParentPacked_ := sload(or(nodes_, grandParent_)) - - let R := mul(eq(parent_, getKey(grandParentPacked_, 0)), _BITPOS_RIGHT) - let L := xor(R, _BITPOS_RIGHT) - - let cursor_ := getKey(grandParentPacked_, R) - let cursorPacked_ := sload(or(nodes_, cursor_)) - if iszero(isRed(cursorPacked_)) { - if eq(key_, getKey(parentPacked_, R)) { - key_ := parent_ - rotate(nodes_, key_, L, R) - } - parent_ := getKey(sload(or(nodes_, key_)), _BITPOS_PARENT) - parentPacked_ := sload(or(nodes_, parent_)) - sstore(or(nodes_, parent_), setRed(parentPacked_, 0)) - grandParent_ := getKey(parentPacked_, _BITPOS_PARENT) - let s_ := or(nodes_, grandParent_) - sstore(s_, setRed(sload(s_), 1)) - rotate(nodes_, grandParent_, R, L) - continue - } - sstore(or(nodes_, parent_), setRed(parentPacked_, 0)) - sstore(or(nodes_, cursor_), setRed(cursorPacked_, 0)) - sstore(or(nodes_, grandParent_), setRed(grandParentPacked_, 1)) - key_ := grandParent_ - } - let root_ := mload(0x00) - sstore(or(nodes_, root_), setRed(sload(or(nodes_, root_)), 0)) - } - - function insert(nodes_, cursor_, key_, x_) -> err_ { - if key_ { - err_ := ERROR_VALUE_ALREADY_EXISTS - leave - } - - let totalNodes_ := add(shr(128, mload(0x20)), 1) - - if gt(totalNodes_, _BITMASK_KEY) { - err_ := ERROR_TREE_IS_FULL - leave - } - - mstore(0x20, shl(128, totalNodes_)) - - let packed_ := or(_BITMASK_RED, shl(_BITPOS_PARENT, cursor_)) - let nodePointer_ := or(nodes_, totalNodes_) - - for {} 1 {} { - if iszero(gt(x_, _BITMASK_PACKED_VALUE)) { - packed_ := or(shl(_BITPOS_PACKED_VALUE, x_), packed_) - break - } - sstore(or(nodePointer_, _BIT_FULL_VALUE_SLOT), x_) - break - } - sstore(nodePointer_, packed_) - - for {} 1 {} { - if iszero(cursor_) { - mstore(0x00, totalNodes_) - break - } - let s_ := or(nodes_, cursor_) - let cursorPacked_ := sload(s_) - let cursorValue_ := shr(_BITPOS_PACKED_VALUE, cursorPacked_) - if iszero(cursorValue_) { cursorValue_ := sload(or(s_, _BIT_FULL_VALUE_SLOT)) } - if iszero(lt(x_, cursorValue_)) { - sstore(s_, setKey(cursorPacked_, _BITPOS_RIGHT, totalNodes_)) - break - } - sstore(s_, setKey(cursorPacked_, _BITPOS_LEFT, totalNodes_)) - break - } - insertFixup(nodes_, totalNodes_) - } - - function removeFixup(nodes_, key_) { - for {} 1 {} { - if eq(key_, mload(0x00)) { break } - let packed_ := sload(or(nodes_, key_)) - if isRed(packed_) { break } - - let parent_ := getKey(packed_, _BITPOS_PARENT) - let parentPacked_ := sload(or(nodes_, parent_)) - - let R := mul(eq(key_, getKey(parentPacked_, 0)), _BITPOS_RIGHT) - let L := xor(R, _BITPOS_RIGHT) - - let cursor_ := getKey(parentPacked_, R) - let cursorPacked_ := sload(or(nodes_, cursor_)) - - if isRed(cursorPacked_) { - sstore(or(nodes_, cursor_), setRed(cursorPacked_, 0)) - sstore(or(nodes_, parent_), setRed(parentPacked_, 1)) - rotate(nodes_, parent_, L, R) - cursor_ := getKey(sload(or(nodes_, parent_)), R) - cursorPacked_ := sload(or(nodes_, cursor_)) - } - - let cursorLeft_ := getKey(cursorPacked_, L) - let cursorLeftPacked_ := sload(or(nodes_, cursorLeft_)) - let cursorRight_ := getKey(cursorPacked_, R) - let cursorRightPacked_ := sload(or(nodes_, cursorRight_)) - - if iszero(or(isRed(cursorLeftPacked_), isRed(cursorRightPacked_))) { - sstore(or(nodes_, cursor_), setRed(cursorPacked_, 1)) - key_ := parent_ - continue - } - - if iszero(isRed(cursorRightPacked_)) { - sstore(or(nodes_, cursorLeft_), setRed(cursorLeftPacked_, 0)) - sstore(or(nodes_, cursor_), setRed(cursorPacked_, 1)) - rotate(nodes_, cursor_, R, L) - cursor_ := getKey(sload(or(nodes_, parent_)), R) - cursorPacked_ := sload(or(nodes_, cursor_)) - cursorRight_ := getKey(cursorPacked_, R) - cursorRightPacked_ := sload(or(nodes_, cursorRight_)) - } - - parentPacked_ := sload(or(nodes_, parent_)) - sstore(or(nodes_, cursor_), copyRed(cursorPacked_, parentPacked_)) - sstore(or(nodes_, parent_), setRed(parentPacked_, 0)) - sstore(or(nodes_, cursorRight_), setRed(cursorRightPacked_, 0)) - rotate(nodes_, parent_, L, R) - break - } - sstore(or(nodes_, key_), setRed(sload(or(nodes_, key_)), 0)) - } - - function removeLast(nodes_, cursor_) { - let last_ := shr(128, mload(0x20)) - let lastPacked_ := sload(or(nodes_, last_)) - let lastValue_ := shr(_BITPOS_PACKED_VALUE, lastPacked_) - let lastFullValue_ := 0 - if iszero(lastValue_) { - lastValue_ := sload(or(_BIT_FULL_VALUE_SLOT, or(nodes_, last_))) - lastFullValue_ := lastValue_ - } - - let cursorPacked_ := sload(or(nodes_, cursor_)) - let cursorValue_ := shr(_BITPOS_PACKED_VALUE, cursorPacked_) - let cursorFullValue_ := 0 - if iszero(cursorValue_) { - cursorValue_ := sload(or(_BIT_FULL_VALUE_SLOT, or(nodes_, cursor_))) - cursorFullValue_ := cursorValue_ - } - - if iszero(eq(lastValue_, cursorValue_)) { - sstore(or(nodes_, cursor_), lastPacked_) - if iszero(eq(lastFullValue_, cursorFullValue_)) { - sstore(or(_BIT_FULL_VALUE_SLOT, or(nodes_, cursor_)), lastFullValue_) - } - for { let lastParent_ := getKey(lastPacked_, _BITPOS_PARENT) } 1 {} { - if iszero(lastParent_) { - mstore(0x00, cursor_) - break - } - let s_ := or(nodes_, lastParent_) - let p_ := sload(s_) - let t_ := iszero(eq(last_, getKey(p_, _BITPOS_LEFT))) - sstore(s_, setKey(p_, mul(t_, _BITPOS_RIGHT), cursor_)) - break - } - let lastRight_ := getKey(lastPacked_, _BITPOS_RIGHT) - if lastRight_ { - let s_ := or(nodes_, lastRight_) - sstore(s_, setKey(sload(s_), _BITPOS_PARENT, cursor_)) - } - let lastLeft_ := getKey(lastPacked_, _BITPOS_LEFT) - if lastLeft_ { - let s_ := or(nodes_, lastLeft_) - sstore(s_, setKey(sload(s_), _BITPOS_PARENT, cursor_)) - } - } - sstore(or(nodes_, last_), 0) - if lastFullValue_ { sstore(or(_BIT_FULL_VALUE_SLOT, or(nodes_, last_)), 0) } - - mstore(0x20, shl(128, sub(last_, 1))) - } - - function remove(nodes_, key_) -> err_ { - let last_ := shr(128, mload(0x20)) - - if gt(key_, last_) { - err_ := ERROR_POINTER_OUT_OF_BOUNDS - leave - } - if iszero(key_) { - err_ := ERROR_VALUE_DOES_NOT_EXISTS - leave - } - - let cursor_ := 0 - - for {} 1 {} { - let packed_ := sload(or(nodes_, key_)) - let left_ := getKey(packed_, _BITPOS_LEFT) - let right_ := getKey(packed_, _BITPOS_RIGHT) - if iszero(mul(left_, right_)) { - cursor_ := key_ - break - } - cursor_ := right_ - for {} 1 {} { - let cursorLeft_ := getKey(sload(or(nodes_, cursor_)), _BITPOS_LEFT) - if iszero(cursorLeft_) { break } - cursor_ := cursorLeft_ - } - break - } - - let cursorPacked_ := sload(or(nodes_, cursor_)) - let probe_ := getKey(cursorPacked_, _BITPOS_LEFT) - if iszero(probe_) { probe_ := getKey(cursorPacked_, _BITPOS_RIGHT) } - - for { let yParent_ := getKey(cursorPacked_, _BITPOS_PARENT) } 1 {} { - let probeSlot_ := or(nodes_, probe_) - sstore(probeSlot_, setKey(sload(probeSlot_), _BITPOS_PARENT, yParent_)) - - if iszero(yParent_) { - mstore(0x00, probe_) - break - } - let s_ := or(nodes_, yParent_) - let p_ := sload(s_) - let t_ := iszero(eq(cursor_, getKey(p_, _BITPOS_LEFT))) - sstore(s_, setKey(p_, mul(t_, _BITPOS_RIGHT), probe_)) - break - } - - let skipFixup_ := isRed(cursorPacked_) - - if iszero(eq(cursor_, key_)) { - let packed_ := sload(or(nodes_, key_)) - let parent_ := getKey(packed_, _BITPOS_PARENT) - for {} 1 {} { - if iszero(parent_) { - mstore(0x00, cursor_) - break - } - let s_ := or(nodes_, parent_) - let p_ := sload(s_) - let t_ := iszero(eq(key_, getKey(p_, _BITPOS_LEFT))) - sstore(s_, setKey(p_, mul(t_, _BITPOS_RIGHT), cursor_)) - break - } - - let left_ := getKey(packed_, _BITPOS_LEFT) - let leftSlot_ := or(nodes_, left_) - sstore(leftSlot_, setKey(sload(leftSlot_), _BITPOS_PARENT, cursor_)) - - let right_ := getKey(packed_, _BITPOS_RIGHT) - let rightSlot_ := or(nodes_, right_) - sstore(rightSlot_, setKey(sload(rightSlot_), _BITPOS_PARENT, cursor_)) - - let m_ := sub(shl(_BITPOS_PACKED_VALUE, 1), 1) - sstore( - or(nodes_, cursor_), - xor(cursorPacked_, and(xor(packed_, cursorPacked_), m_)) - ) - - let t_ := cursor_ - cursor_ := key_ - key_ := t_ - } - if iszero(skipFixup_) { removeFixup(nodes_, probe_) } - - removeLast(nodes_, cursor_) - } - - mstore(0x10, sload(nodes)) - - for {} 1 {} { - if iszero(mode) { - err := insert(nodes, cursor, key, x) - break - } - err := remove(nodes, key) - break - } - - sstore(nodes, mload(0x10)) - } - } - - /// @dev Returns the pointer to the `nodes` for the tree. - function _nodes(Tree storage tree) private pure returns (uint256 nodes) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, tree.slot) - mstore(0x00, _NODES_SLOT_SEED) - nodes := shl(_NODES_SLOT_SHIFT, keccak256(0x00, 0x40)) - } - } - - /// @dev Finds `x` in `tree`. The `key` will be zero if `x` is not found. - function _find(Tree storage tree, uint256 x) - private - view - returns (uint256 nodes, uint256 cursor, uint256 key) - { - if (x == 0) _revert(0xc94f1877); // `ValueIsEmpty()`. - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, tree.slot) - mstore(0x00, _NODES_SLOT_SEED) - nodes := shl(_NODES_SLOT_SHIFT, keccak256(0x00, 0x40)) - - mstore(0x01, _BITPOS_RIGHT) - for { let probe := shr(128, sload(nodes)) } probe {} { - cursor := probe - let nodePacked := sload(or(nodes, probe)) - let nodeValue := shr(_BITPOS_PACKED_VALUE, nodePacked) - if iszero(nodeValue) { - nodeValue := sload(or(or(nodes, probe), _BIT_FULL_VALUE_SLOT)) - } - if eq(nodeValue, x) { - key := cursor - break - } - probe := and(shr(mload(gt(x, nodeValue)), nodePacked), _BITMASK_KEY) - } - } - } - - /// @dev Helper to revert `err` efficiently. - function _revert(uint256 err) private pure { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, err) - revert(0x1c, 0x04) - } - } -} diff --git a/lib/solady/src/utils/SSTORE2.sol b/lib/solady/src/utils/SSTORE2.sol deleted file mode 100644 index bc22e66..0000000 --- a/lib/solady/src/utils/SSTORE2.sol +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Read and write to persistent storage at a fraction of the cost. -/// @author Solady (https://github.com/vectorized/solmady/blob/main/src/utils/SSTORE2.sol) -/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) -library SSTORE2 { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev We skip the first byte as it's a STOP opcode, - /// which ensures the contract can't be called. - uint256 internal constant DATA_OFFSET = 1; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Unable to deploy the storage contract. - error DeploymentFailed(); - - /// @dev The storage contract address is invalid. - error InvalidPointer(); - - /// @dev Attempt to read outside of the storage contract's bytecode bounds. - error ReadOutOfBounds(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* WRITE LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Writes `data` into the bytecode of a storage contract and returns its address. - function write(bytes memory data) internal returns (address pointer) { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - - // Add 1 to data size since we are prefixing it with a STOP opcode. - let dataSize := add(originalDataLength, DATA_OFFSET) - - /** - * ------------------------------------------------------------------------------+ - * Opcode | Mnemonic | Stack | Memory | - * ------------------------------------------------------------------------------| - * 61 dataSize | PUSH2 dataSize | dataSize | | - * 80 | DUP1 | dataSize dataSize | | - * 60 0xa | PUSH1 0xa | 0xa dataSize dataSize | | - * 3D | RETURNDATASIZE | 0 0xa dataSize dataSize | | - * 39 | CODECOPY | dataSize | [0..dataSize): code | - * 3D | RETURNDATASIZE | 0 dataSize | [0..dataSize): code | - * F3 | RETURN | | [0..dataSize): code | - * 00 | STOP | | | - * ------------------------------------------------------------------------------+ - * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called. - * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16. - */ - mstore( - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - add(data, gt(dataSize, 0xffff)), - // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. - or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) - ) - - // Deploy a new contract with the generated creation code. - pointer := create(0, add(data, 0x15), add(dataSize, 0xa)) - - // If `pointer` is zero, revert. - if iszero(pointer) { - // Store the function selector of `DeploymentFailed()`. - mstore(0x00, 0x30116425) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Writes `data` into the bytecode of a storage contract with `salt` - /// and returns its deterministic address. - function writeDeterministic(bytes memory data, bytes32 salt) - internal - returns (address pointer) - { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - let dataSize := add(originalDataLength, DATA_OFFSET) - - mstore( - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - add(data, gt(dataSize, 0xffff)), - // Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2. - or(0xfd61000080600a3d393df300, shl(0x40, dataSize)) - ) - - // Deploy a new contract with the generated creation code. - pointer := create2(0, add(data, 0x15), add(dataSize, 0xa), salt) - - // If `pointer` is zero, revert. - if iszero(pointer) { - // Store the function selector of `DeploymentFailed()`. - mstore(0x00, 0x30116425) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Returns the initialization code hash of the storage contract for `data`. - /// Used for mining vanity addresses with create2crunch. - function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) { - /// @solidity memory-safe-assembly - assembly { - let originalDataLength := mload(data) - let dataSize := add(originalDataLength, DATA_OFFSET) - - // Do a out-of-gas revert if `dataSize` is more than 2 bytes. - // The actual EVM limit may be smaller and may change over time. - returndatacopy(returndatasize(), returndatasize(), shr(16, dataSize)) - - mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize))) - - hash := keccak256(add(data, 0x15), add(dataSize, 0xa)) - - // Restore original length of the variable size `data`. - mstore(data, originalDataLength) - } - } - - /// @dev Returns the address of the storage contract for `data` - /// deployed with `salt` by `deployer`. - /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. - function predictDeterministicAddress(bytes memory data, bytes32 salt, address deployer) - internal - pure - returns (address predicted) - { - bytes32 hash = initCodeHash(data); - /// @solidity memory-safe-assembly - assembly { - // Compute and store the bytecode hash. - mstore8(0x00, 0xff) // Write the prefix. - mstore(0x35, hash) - mstore(0x01, shl(96, deployer)) - mstore(0x15, salt) - predicted := keccak256(0x00, 0x55) - // Restore the part of the free memory pointer that has been overwritten. - mstore(0x35, 0) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* READ LOGIC */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns all the `data` from the bytecode of the storage contract at `pointer`. - function read(address pointer) internal view returns (bytes memory data) { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - // Offset all indices by 1 to skip the STOP opcode. - let size := sub(pointerCodesize, DATA_OFFSET) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), DATA_OFFSET, size) - } - } - - /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, - /// from the byte at `start`, to the end of the data stored. - function read(address pointer, uint256 start) internal view returns (bytes memory data) { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // If `!(pointer.code.size > start)`, reverts. - // This also handles the case where `start + DATA_OFFSET` overflows. - if iszero(gt(pointerCodesize, start)) { - // Store the function selector of `ReadOutOfBounds()`. - mstore(0x00, 0x84eb0dd1) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - let size := sub(pointerCodesize, add(start, DATA_OFFSET)) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) - } - } - - /// @dev Returns the `data` from the bytecode of the storage contract at `pointer`, - /// from the byte at `start`, to the byte at `end` (exclusive) of the data stored. - function read(address pointer, uint256 start, uint256 end) - internal - view - returns (bytes memory data) - { - /// @solidity memory-safe-assembly - assembly { - let pointerCodesize := extcodesize(pointer) - if iszero(pointerCodesize) { - // Store the function selector of `InvalidPointer()`. - mstore(0x00, 0x11052bb4) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - - // If `!(pointer.code.size > end) || (start > end)`, revert. - // This also handles the cases where - // `end + DATA_OFFSET` or `start + DATA_OFFSET` overflows. - if iszero( - and( - gt(pointerCodesize, end), // Within bounds. - iszero(gt(start, end)) // Valid range. - ) - ) { - // Store the function selector of `ReadOutOfBounds()`. - mstore(0x00, 0x84eb0dd1) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - let size := sub(end, start) - - // Get the pointer to the free memory and allocate - // enough 32-byte words for the data and the length of the data, - // then copy the code to the allocated memory. - // Masking with 0xffe0 will suffice, since contract size is less than 16 bits. - data := mload(0x40) - mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0))) - mstore(data, size) - mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot. - extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size) - } - } -} diff --git a/lib/solady/src/utils/SafeCastLib.sol b/lib/solady/src/utils/SafeCastLib.sol deleted file mode 100644 index 8129629..0000000 --- a/lib/solady/src/utils/SafeCastLib.sol +++ /dev/null @@ -1,390 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Safe integer casting library that reverts on overflow. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) -library SafeCastLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - error Overflow(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* UNSIGNED INTEGER SAFE CASTING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function toUint8(uint256 x) internal pure returns (uint8) { - if (x >= 1 << 8) _revertOverflow(); - return uint8(x); - } - - function toUint16(uint256 x) internal pure returns (uint16) { - if (x >= 1 << 16) _revertOverflow(); - return uint16(x); - } - - function toUint24(uint256 x) internal pure returns (uint24) { - if (x >= 1 << 24) _revertOverflow(); - return uint24(x); - } - - function toUint32(uint256 x) internal pure returns (uint32) { - if (x >= 1 << 32) _revertOverflow(); - return uint32(x); - } - - function toUint40(uint256 x) internal pure returns (uint40) { - if (x >= 1 << 40) _revertOverflow(); - return uint40(x); - } - - function toUint48(uint256 x) internal pure returns (uint48) { - if (x >= 1 << 48) _revertOverflow(); - return uint48(x); - } - - function toUint56(uint256 x) internal pure returns (uint56) { - if (x >= 1 << 56) _revertOverflow(); - return uint56(x); - } - - function toUint64(uint256 x) internal pure returns (uint64) { - if (x >= 1 << 64) _revertOverflow(); - return uint64(x); - } - - function toUint72(uint256 x) internal pure returns (uint72) { - if (x >= 1 << 72) _revertOverflow(); - return uint72(x); - } - - function toUint80(uint256 x) internal pure returns (uint80) { - if (x >= 1 << 80) _revertOverflow(); - return uint80(x); - } - - function toUint88(uint256 x) internal pure returns (uint88) { - if (x >= 1 << 88) _revertOverflow(); - return uint88(x); - } - - function toUint96(uint256 x) internal pure returns (uint96) { - if (x >= 1 << 96) _revertOverflow(); - return uint96(x); - } - - function toUint104(uint256 x) internal pure returns (uint104) { - if (x >= 1 << 104) _revertOverflow(); - return uint104(x); - } - - function toUint112(uint256 x) internal pure returns (uint112) { - if (x >= 1 << 112) _revertOverflow(); - return uint112(x); - } - - function toUint120(uint256 x) internal pure returns (uint120) { - if (x >= 1 << 120) _revertOverflow(); - return uint120(x); - } - - function toUint128(uint256 x) internal pure returns (uint128) { - if (x >= 1 << 128) _revertOverflow(); - return uint128(x); - } - - function toUint136(uint256 x) internal pure returns (uint136) { - if (x >= 1 << 136) _revertOverflow(); - return uint136(x); - } - - function toUint144(uint256 x) internal pure returns (uint144) { - if (x >= 1 << 144) _revertOverflow(); - return uint144(x); - } - - function toUint152(uint256 x) internal pure returns (uint152) { - if (x >= 1 << 152) _revertOverflow(); - return uint152(x); - } - - function toUint160(uint256 x) internal pure returns (uint160) { - if (x >= 1 << 160) _revertOverflow(); - return uint160(x); - } - - function toUint168(uint256 x) internal pure returns (uint168) { - if (x >= 1 << 168) _revertOverflow(); - return uint168(x); - } - - function toUint176(uint256 x) internal pure returns (uint176) { - if (x >= 1 << 176) _revertOverflow(); - return uint176(x); - } - - function toUint184(uint256 x) internal pure returns (uint184) { - if (x >= 1 << 184) _revertOverflow(); - return uint184(x); - } - - function toUint192(uint256 x) internal pure returns (uint192) { - if (x >= 1 << 192) _revertOverflow(); - return uint192(x); - } - - function toUint200(uint256 x) internal pure returns (uint200) { - if (x >= 1 << 200) _revertOverflow(); - return uint200(x); - } - - function toUint208(uint256 x) internal pure returns (uint208) { - if (x >= 1 << 208) _revertOverflow(); - return uint208(x); - } - - function toUint216(uint256 x) internal pure returns (uint216) { - if (x >= 1 << 216) _revertOverflow(); - return uint216(x); - } - - function toUint224(uint256 x) internal pure returns (uint224) { - if (x >= 1 << 224) _revertOverflow(); - return uint224(x); - } - - function toUint232(uint256 x) internal pure returns (uint232) { - if (x >= 1 << 232) _revertOverflow(); - return uint232(x); - } - - function toUint240(uint256 x) internal pure returns (uint240) { - if (x >= 1 << 240) _revertOverflow(); - return uint240(x); - } - - function toUint248(uint256 x) internal pure returns (uint248) { - if (x >= 1 << 248) _revertOverflow(); - return uint248(x); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* SIGNED INTEGER SAFE CASTING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function toInt8(int256 x) internal pure returns (int8) { - int8 y = int8(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt16(int256 x) internal pure returns (int16) { - int16 y = int16(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt24(int256 x) internal pure returns (int24) { - int24 y = int24(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt32(int256 x) internal pure returns (int32) { - int32 y = int32(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt40(int256 x) internal pure returns (int40) { - int40 y = int40(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt48(int256 x) internal pure returns (int48) { - int48 y = int48(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt56(int256 x) internal pure returns (int56) { - int56 y = int56(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt64(int256 x) internal pure returns (int64) { - int64 y = int64(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt72(int256 x) internal pure returns (int72) { - int72 y = int72(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt80(int256 x) internal pure returns (int80) { - int80 y = int80(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt88(int256 x) internal pure returns (int88) { - int88 y = int88(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt96(int256 x) internal pure returns (int96) { - int96 y = int96(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt104(int256 x) internal pure returns (int104) { - int104 y = int104(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt112(int256 x) internal pure returns (int112) { - int112 y = int112(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt120(int256 x) internal pure returns (int120) { - int120 y = int120(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt128(int256 x) internal pure returns (int128) { - int128 y = int128(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt136(int256 x) internal pure returns (int136) { - int136 y = int136(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt144(int256 x) internal pure returns (int144) { - int144 y = int144(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt152(int256 x) internal pure returns (int152) { - int152 y = int152(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt160(int256 x) internal pure returns (int160) { - int160 y = int160(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt168(int256 x) internal pure returns (int168) { - int168 y = int168(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt176(int256 x) internal pure returns (int176) { - int176 y = int176(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt184(int256 x) internal pure returns (int184) { - int184 y = int184(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt192(int256 x) internal pure returns (int192) { - int192 y = int192(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt200(int256 x) internal pure returns (int200) { - int200 y = int200(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt208(int256 x) internal pure returns (int208) { - int208 y = int208(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt216(int256 x) internal pure returns (int216) { - int216 y = int216(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt224(int256 x) internal pure returns (int224) { - int224 y = int224(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt232(int256 x) internal pure returns (int232) { - int232 y = int232(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt240(int256 x) internal pure returns (int240) { - int240 y = int240(x); - if (x != y) _revertOverflow(); - return y; - } - - function toInt248(int256 x) internal pure returns (int248) { - int248 y = int248(x); - if (x != y) _revertOverflow(); - return y; - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* OTHER SAFE CASTING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function toInt256(uint256 x) internal pure returns (int256) { - if (x >= 1 << 255) _revertOverflow(); - return int256(x); - } - - function toUint256(int256 x) internal pure returns (uint256) { - if (x < 0) _revertOverflow(); - return uint256(x); - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* PRIVATE HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - function _revertOverflow() private pure { - /// @solidity memory-safe-assembly - assembly { - // Store the function selector of `Overflow()`. - mstore(0x00, 0x35278d12) - // Revert with (offset, size). - revert(0x1c, 0x04) - } - } -} diff --git a/lib/solady/src/utils/SafeTransferLib.sol b/lib/solady/src/utils/SafeTransferLib.sol deleted file mode 100644 index 6d107dd..0000000 --- a/lib/solady/src/utils/SafeTransferLib.sol +++ /dev/null @@ -1,374 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) -/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) -/// -/// @dev Note: -/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. -/// - For ERC20s, this implementation won't check that a token has code, -/// responsibility is delegated to the caller. -library SafeTransferLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ETH transfer has failed. - error ETHTransferFailed(); - - /// @dev The ERC20 `transferFrom` has failed. - error TransferFromFailed(); - - /// @dev The ERC20 `transfer` has failed. - error TransferFailed(); - - /// @dev The ERC20 `approve` has failed. - error ApproveFailed(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CONSTANTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. - uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; - - /// @dev Suggested gas stipend for contract receiving ETH to perform a few - /// storage reads and writes, but low enough to prevent griefing. - uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ETH OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. - // - // The regular variants: - // - Forwards all remaining gas to the target. - // - Reverts if the target reverts. - // - Reverts if the current contract has insufficient balance. - // - // The force variants: - // - Forwards with an optional gas stipend - // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). - // - If the target reverts, or if the gas stipend is exhausted, - // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. - // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. - // - Reverts if the current contract has insufficient balance. - // - // The try variants: - // - Forwards with a mandatory gas stipend. - // - Instead of reverting, returns whether the transfer succeeded. - - /// @dev Sends `amount` (in wei) ETH to `to`. - function safeTransferETH(address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Sends all the ETH in the current contract to `to`. - function safeTransferAllETH(address to) internal { - /// @solidity memory-safe-assembly - assembly { - // Transfer all the ETH and check if it succeeded or not. - if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - } - } - - /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. - function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { - /// @solidity memory-safe-assembly - assembly { - if lt(selfbalance(), amount) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. - function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { - /// @solidity memory-safe-assembly - assembly { - if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. - function forceSafeTransferETH(address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - if lt(selfbalance(), amount) { - mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. - revert(0x1c, 0x04) - } - if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. - function forceSafeTransferAllETH(address to) internal { - /// @solidity memory-safe-assembly - assembly { - // forgefmt: disable-next-item - if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) { - mstore(0x00, to) // Store the address in scratch space. - mstore8(0x0b, 0x73) // Opcode `PUSH20`. - mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. - if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation. - } - } - } - - /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. - function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) - internal - returns (bool success) - { - /// @solidity memory-safe-assembly - assembly { - success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00) - } - } - - /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. - function trySafeTransferAllETH(address to, uint256 gasStipend) - internal - returns (bool success) - { - /// @solidity memory-safe-assembly - assembly { - success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC20 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. - /// Reverts upon failure. - /// - /// The `from` account must have at least `amount` approved for - /// the current contract to manage. - function safeTransferFrom(address token, address from, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x60, amount) // Store the `amount` argument. - mstore(0x40, to) // Store the `to` argument. - mstore(0x2c, shl(96, from)) // Store the `from` argument. - mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot to zero. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Sends all of ERC20 `token` from `from` to `to`. - /// Reverts upon failure. - /// - /// The `from` account must have their entire balance approved for - /// the current contract to manage. - function safeTransferAllFrom(address token, address from, address to) - internal - returns (uint256 amount) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) // Cache the free memory pointer. - mstore(0x40, to) // Store the `to` argument. - mstore(0x2c, shl(96, from)) // Store the `from` argument. - mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. - // Read the balance, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. - amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x7939f424) // `TransferFromFailed()`. - revert(0x1c, 0x04) - } - mstore(0x60, 0) // Restore the zero slot to zero. - mstore(0x40, m) // Restore the free memory pointer. - } - } - - /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. - /// Reverts upon failure. - function safeTransfer(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sends all of ERC20 `token` from the current contract to `to`. - /// Reverts upon failure. - function safeTransferAll(address token, address to) internal returns (uint256 amount) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. - mstore(0x20, address()) // Store the address of the current contract. - // Read the balance, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x14, to) // Store the `to` argument. - amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. - mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. - // Perform the transfer, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x90b8ec18) // `TransferFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. - /// Reverts upon failure. - function safeApprove(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - // Perform the approval, reverting upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. - revert(0x1c, 0x04) - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. - /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, - /// then retries the approval again (some tokens, e.g. USDT, requires this). - /// Reverts upon failure. - function safeApproveWithRetry(address token, address to, uint256 amount) internal { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, to) // Store the `to` argument. - mstore(0x34, amount) // Store the `amount` argument. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - // Perform the approval, retrying upon failure. - if iszero( - and( // The arguments of `and` are evaluated from right to left. - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x34, 0) // Store 0 for the `amount`. - mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. - pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval. - mstore(0x34, amount) // Store back the original `amount`. - // Retry the approval, reverting upon failure. - if iszero( - and( - or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. - call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) - ) - ) { - mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. - revert(0x1c, 0x04) - } - } - mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. - } - } - - /// @dev Returns the amount of ERC20 `token` owned by `account`. - /// Returns zero if the `token` does not exist. - function balanceOf(address token, address account) internal view returns (uint256 amount) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x14, account) // Store the `account` argument. - mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. - amount := - mul( - mload(0x20), - and( // The arguments of `and` are evaluated from right to left. - gt(returndatasize(), 0x1f), // At least 32 bytes returned. - staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) - ) - ) - } - } -} diff --git a/lib/solady/src/utils/SignatureCheckerLib.sol b/lib/solady/src/utils/SignatureCheckerLib.sol deleted file mode 100644 index 6f9350f..0000000 --- a/lib/solady/src/utils/SignatureCheckerLib.sol +++ /dev/null @@ -1,545 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice Signature verification helper that supports both ECDSA signatures from EOAs -/// and ERC1271 signatures from smart contract wallets like Argent and Gnosis safe. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SignatureCheckerLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol) -/// -/// @dev Note: -/// - The signature checking functions use the ecrecover precompile (0x1). -/// - The `bytes memory signature` variants use the identity precompile (0x4) -/// to copy memory internally. -/// - Unlike ECDSA signatures, contract signatures are revocable. -/// - As of Solady version 0.0.134, all `bytes signature` variants accept both -/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures. -/// See: https://eips.ethereum.org/EIPS/eip-2098 -/// This is for calldata efficiency on smart accounts prevalent on L2s. -/// -/// WARNING! Do NOT use signatures as unique identifiers: -/// - Use a nonce in the digest to prevent replay attacks on the same contract. -/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts. -/// EIP-712 also enables readable signing of typed data for better user safety. -/// This implementation does NOT check if a signature is non-malleable. -library SignatureCheckerLib { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* SIGNATURE CHECKING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `signature` is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x40, mload(add(signature, 0x20))) // `r`. - if eq(mload(signature), 64) { - let vs := mload(add(signature, 0x40)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - if eq(mload(signature), 65) { - mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`. - mstore(0x60, mload(add(signature, 0x40))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - // Copy the `signature` over. - let n := add(0x20, mload(signature)) - pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(returndatasize(), 0x44), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - break - } - } - } - - /// @dev Returns whether `signature` is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - if eq(signature.length, 64) { - let vs := calldataload(add(signature.offset, 0x20)) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, calldataload(signature.offset)) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - if eq(signature.length, 65) { - mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`. - calldatacopy(0x40, signature.offset, 0x40) // `r`, `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), signature.length) - // Copy the `signature` over. - calldatacopy(add(m, 0x64), signature.offset, signature.length) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(signature.length, 0x64), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - break - } - } - } - - /// @dev Returns whether the signature (`r`, `vs`) is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x20, add(shr(255, vs), 27)) // `v`. - mstore(0x40, r) // `r`. - mstore(0x60, shr(1, shl(1, vs))) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), mload(0x60)) // `s`. - mstore8(add(m, 0xa4), mload(0x20)) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - } - - /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `signer` and `hash`. - /// If `signer` is a smart contract, the signature is validated with ERC1271. - /// Otherwise, the signature is validated with `ECDSA.recover`. - function isValidSignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - // Clean the upper 96 bits of `signer` in case they are dirty. - for { signer := shr(96, shl(96, signer)) } signer {} { - let m := mload(0x40) - mstore(0x00, hash) - mstore(0x20, and(v, 0xff)) // `v`. - mstore(0x40, r) // `r`. - mstore(0x60, s) // `s`. - let t := - staticcall( - gas(), // Amount of gas left for the transaction. - 1, // Address of `ecrecover`. - 0x00, // Start of input. - 0x80, // Size of input. - 0x01, // Start of output. - 0x20 // Size of output. - ) - // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise. - if iszero(or(iszero(returndatasize()), xor(signer, mload(t)))) { - isValid := 1 - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), s) // `s`. - mstore8(add(m, 0xa4), v) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - mstore(0x60, 0) // Restore the zero slot. - mstore(0x40, m) // Restore the free memory pointer. - break - } - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* ERC1271 OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes memory signature) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - // Copy the `signature` over. - let n := add(0x20, mload(signature)) - pop(staticcall(gas(), 4, signature, n, add(m, 0x44), n)) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(returndatasize(), 0x44), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether `signature` is valid for `hash` for an ERC1271 `signer` contract. - function isValidERC1271SignatureNowCalldata( - address signer, - bytes32 hash, - bytes calldata signature - ) internal view returns (bool isValid) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), signature.length) - // Copy the `signature` over. - calldatacopy(add(m, 0x64), signature.offset, signature.length) - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - add(signature.length, 0x64), // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether the signature (`r`, `vs`) is valid for `hash` - /// for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes32 r, bytes32 vs) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), shr(1, shl(1, vs))) // `s`. - mstore8(add(m, 0xa4), add(shr(255, vs), 27)) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /// @dev Returns whether the signature (`v`, `r`, `s`) is valid for `hash` - /// for an ERC1271 `signer` contract. - function isValidERC1271SignatureNow(address signer, bytes32 hash, uint8 v, bytes32 r, bytes32 s) - internal - view - returns (bool isValid) - { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let f := shl(224, 0x1626ba7e) - mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`. - mstore(add(m, 0x04), hash) - let d := add(m, 0x24) - mstore(d, 0x40) // The offset of the `signature` in the calldata. - mstore(add(m, 0x44), 65) // Length of the signature. - mstore(add(m, 0x64), r) // `r`. - mstore(add(m, 0x84), s) // `s`. - mstore8(add(m, 0xa4), v) // `v`. - // forgefmt: disable-next-item - isValid := and( - // Whether the returndata is the magic value `0x1626ba7e` (left-aligned). - eq(mload(d), f), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - signer, // The `signer` address. - m, // Offset of calldata in memory. - 0xa5, // Length of calldata in memory. - d, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* HASHING OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an Ethereum Signed Message, created from a `hash`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, hash) // Store into scratch space for keccak256. - mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes. - result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. - } - } - - /// @dev Returns an Ethereum Signed Message, created from `s`. - /// This produces a hash corresponding to the one signed with the - /// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign) - /// JSON-RPC method as part of EIP-191. - /// Note: Supports lengths of `s` up to 999999 bytes. - function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let sLength := mload(s) - let o := 0x20 - mstore(o, "\x19Ethereum Signed Message:\n") // 26 bytes, zero-right-padded. - mstore(0x00, 0x00) - // Convert the `s.length` to ASCII decimal representation: `base10(s.length)`. - for { let temp := sLength } 1 {} { - o := sub(o, 1) - mstore8(o, add(48, mod(temp, 10))) - temp := div(temp, 10) - if iszero(temp) { break } - } - let n := sub(0x3a, o) // Header length: `26 + 32 - o`. - // Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes. - returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20)) - mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header. - result := keccak256(add(s, sub(0x20, n)), add(n, sLength)) - mstore(s, sLength) // Restore the length. - } - } - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EMPTY CALLDATA HELPERS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Returns an empty calldata bytes. - function emptySignature() internal pure returns (bytes calldata signature) { - /// @solidity memory-safe-assembly - assembly { - signature.length := 0 - } - } -} diff --git a/lib/solady/src/utils/UUPSUpgradeable.sol b/lib/solady/src/utils/UUPSUpgradeable.sol deleted file mode 100644 index 368699b..0000000 --- a/lib/solady/src/utils/UUPSUpgradeable.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @notice UUPS proxy mixin. -/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/UUPSUpgradeable.sol) -/// @author Modified from OpenZeppelin -/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/UUPSUpgradeable.sol) -/// -/// Note: -/// - This implementation is intended to be used with ERC1967 proxies. -/// See: `LibClone.deployERC1967` and related functions. -/// - This implementation is NOT compatible with legacy OpenZeppelin proxies -/// which do not store the implementation at `_ERC1967_IMPLEMENTATION_SLOT`. -abstract contract UUPSUpgradeable { - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* CUSTOM ERRORS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The upgrade failed. - error UpgradeFailed(); - - /// @dev The call is from an unauthorized call context. - error UnauthorizedCallContext(); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* IMMUTABLES */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev For checking if the context is a delegate call. - uint256 private immutable __self = uint256(uint160(address(this))); - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* EVENTS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Emitted when the proxy's implementation is upgraded. - event Upgraded(address indexed implementation); - - /// @dev `keccak256(bytes("Upgraded(address)"))`. - uint256 private constant _UPGRADED_EVENT_SIGNATURE = - 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev The ERC-1967 storage slot for the implementation in the proxy. - /// `uint256(keccak256("eip1967.proxy.implementation")) - 1`. - bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* UUPS OPERATIONS */ - /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - - /// @dev Please override this function to check if `msg.sender` is authorized - /// to upgrade the proxy to `newImplementation`, reverting if not. - /// ``` - /// function _authorizeUpgrade(address) internal override onlyOwner {} - /// ``` - function _authorizeUpgrade(address newImplementation) internal virtual; - - /// @dev Returns the storage slot used by the implementation, - /// as specified in [ERC1822](https://eips.ethereum.org/EIPS/eip-1822). - /// - /// Note: The `notDelegated` modifier prevents accidental upgrades to - /// an implementation that is a proxy contract. - function proxiableUUID() public view virtual notDelegated returns (bytes32) { - // This function must always return `_ERC1967_IMPLEMENTATION_SLOT` to comply with ERC1967. - return _ERC1967_IMPLEMENTATION_SLOT; - } - - /// @dev Upgrades the proxy's implementation to `newImplementation`. - /// Emits a {Upgraded} event. - /// - /// Note: Passing in empty `data` skips the delegatecall to `newImplementation`. - function upgradeToAndCall(address newImplementation, bytes calldata data) - public - payable - virtual - onlyProxy - { - _authorizeUpgrade(newImplementation); - /// @solidity memory-safe-assembly - assembly { - newImplementation := shr(96, shl(96, newImplementation)) // Clears upper 96 bits. - mstore(0x01, 0x52d1902d) // `proxiableUUID()`. - let s := _ERC1967_IMPLEMENTATION_SLOT - // Check if `newImplementation` implements `proxiableUUID` correctly. - if iszero(eq(mload(staticcall(gas(), newImplementation, 0x1d, 0x04, 0x01, 0x20)), s)) { - mstore(0x01, 0x55299b49) // `UpgradeFailed()`. - revert(0x1d, 0x04) - } - // Emit the {Upgraded} event. - log2(codesize(), 0x00, _UPGRADED_EVENT_SIGNATURE, newImplementation) - sstore(s, newImplementation) // Updates the implementation. - - // Perform a delegatecall to `newImplementation` if `data` is non-empty. - if data.length { - // Forwards the `data` to `newImplementation` via delegatecall. - let m := mload(0x40) - calldatacopy(m, data.offset, data.length) - if iszero(delegatecall(gas(), newImplementation, m, data.length, codesize(), 0x00)) - { - // Bubble up the revert if the call reverts. - returndatacopy(m, 0x00, returndatasize()) - revert(m, returndatasize()) - } - } - } - } - - /// @dev Requires that the execution is performed through a proxy. - modifier onlyProxy() { - uint256 s = __self; - /// @solidity memory-safe-assembly - assembly { - // To enable use cases with an immutable default implementation in the bytecode, - // (see: ERC6551Proxy), we don't require that the proxy address must match the - // value stored in the implementation slot, which may not be initialized. - if eq(s, address()) { - mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. - revert(0x1c, 0x04) - } - } - _; - } - - /// @dev Requires that the execution is NOT performed via delegatecall. - /// This is the opposite of `onlyProxy`. - modifier notDelegated() { - uint256 s = __self; - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(s, address())) { - mstore(0x00, 0x9f03a026) // `UnauthorizedCallContext()`. - revert(0x1c, 0x04) - } - } - _; - } -} diff --git a/lib/solady/test/Base64.t.sol b/lib/solady/test/Base64.t.sol deleted file mode 100644 index 87c62da..0000000 --- a/lib/solady/test/Base64.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {Base64} from "../src/utils/Base64.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract Base64Test is SoladyTest { - function testBase64EncodeEmptyString() public { - _testBase64Encode("", ""); - } - - function testBase64EncodeShortStrings() public { - _testBase64Encode("M", "TQ=="); - _testBase64Encode("Mi", "TWk="); - _testBase64Encode("Mil", "TWls"); - _testBase64Encode("Mila", "TWlsYQ=="); - _testBase64Encode("Milad", "TWlsYWQ="); - _testBase64Encode("Milady", "TWlsYWR5"); - } - - function testBase64EncodeToStringWithDoublePadding() public { - _testBase64Encode("test", "dGVzdA=="); - } - - function testBase64EncodeToStringWithSinglePadding() public { - _testBase64Encode("test1", "dGVzdDE="); - } - - function testBase64EncodeToStringWithNoPadding() public { - _testBase64Encode("test12", "dGVzdDEy"); - } - - function testBase64EncodeSentence() public { - _testBase64Encode( - "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=" - ); - } - - function testBase64WordBoundary() public { - // Base64.encode allocates memory in multiples of 32 bytes. - // This checks if the amount of memory allocated is enough. - _testBase64Encode("012345678901234567890", "MDEyMzQ1Njc4OTAxMjM0NTY3ODkw"); - _testBase64Encode("0123456789012345678901", "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMQ=="); - _testBase64Encode("01234567890123456789012", "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="); - _testBase64Encode("012345678901234567890123", "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIz"); - _testBase64Encode("0123456789012345678901234", "MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNA=="); - } - - function _testBase64Encode(string memory input, string memory output) private { - assertEq(Base64.encode(bytes(input)), output); - } - - function testBase64EncodeDecode(bytes memory input) public { - string memory encoded = Base64.encode(input); - bytes memory decoded = Base64.decode(encoded); - - assertEq(input, decoded); - } - - function testBase64DecodeShortStringGas() public { - assertEq(Base64.decode("TWlsYWR5").length, 6); - } - - function testBase64DecodeSentenceGas() public { - assertEq( - Base64.decode( - "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=" - ).length, - 56 - ); - } - - function testBase64EncodeDecodeAltModes(bytes memory input) public brutalizeMemory { - for (uint256 i; i < 2; ++i) { - _misalignFreeMemoryPointer(); - string memory encoded = Base64.encode(input); - _checkMemory(encoded); - - if (_random() & 1 == 0) { - encoded = LibString.replace(encoded, "=", ""); - } - if (_random() & 1 == 0) { - encoded = LibString.replace(encoded, "/", ","); - } - if (_random() & 1 == 0) { - encoded = LibString.replace(encoded, "/", "_"); - } - if (_random() & 1 == 0) { - encoded = LibString.replace(encoded, "+", "-"); - } - - _misalignFreeMemoryPointer(); - bytes memory decoded = Base64.decode(encoded); - _checkMemory(decoded); - - assertEq(input, decoded); - - input = abi.encode(encoded); - } - } - - function testBase64EncodeFileSafeAndNoPadding(bytes memory input, bool fileSafe, bool noPadding) - public - { - string memory expectedEncoded = Base64.encode(input); - - if (fileSafe) { - expectedEncoded = LibString.replace(expectedEncoded, "+", "-"); - expectedEncoded = LibString.replace(expectedEncoded, "/", "_"); - } - if (noPadding) { - expectedEncoded = LibString.replace(expectedEncoded, "=", ""); - } - - assertEq(Base64.encode(input, fileSafe, noPadding), expectedEncoded); - } -} diff --git a/lib/solady/test/CREATE3.t.sol b/lib/solady/test/CREATE3.t.sol deleted file mode 100644 index 82693bd..0000000 --- a/lib/solady/test/CREATE3.t.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {WETH} from "../src/tokens/WETH.sol"; -import {MockERC20} from "./utils/mocks/MockERC20.sol"; -import {MockCd} from "./utils/mocks/MockCd.sol"; - -import {CREATE3} from "../src/utils/CREATE3.sol"; - -import "./utils/SoladyTest.sol"; - -contract CREATE3Test is SoladyTest { - function testDeployERC20() public { - bytes32 salt = keccak256(bytes("A salt!")); - - MockERC20 deployed = MockERC20( - this.deploy( - salt, - abi.encodePacked(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)), - 0 - ) - ); - - assertEq(address(deployed), CREATE3.getDeployed(salt)); - - assertEq(deployed.name(), "Mock Token"); - assertEq(deployed.symbol(), "MOCK"); - assertEq(deployed.decimals(), 18); - } - - function testDeployedUpperBitsSafeForPlainSolidity() public { - bytes32 salt = keccak256(bytes("A salt!")); - address deployed = CREATE3.getDeployed(salt); - uint256 someNumber = 123456789; - uint256 packed = (someNumber << 160) | uint160(deployed); - uint256 someNumberUnpacked = packed >> 160; - assertEq(someNumber, someNumberUnpacked); - } - - function testDoubleDeploySameBytecodeReverts() public { - bytes32 salt = keccak256(bytes("Salty...")); - - this.deploy(salt, type(MockCd).creationCode, 0); - vm.expectRevert(CREATE3.DeploymentFailed.selector); - this.deploy(salt, type(MockCd).creationCode, 0); - } - - function testDoubleDeployDifferentBytecodeReverts() public { - bytes32 salt = keccak256(bytes("and sweet!")); - - this.deploy(salt, type(WETH).creationCode, 0); - vm.expectRevert(CREATE3.DeploymentFailed.selector); - this.deploy(salt, type(MockCd).creationCode, 0); - } - - function testDeployERC20( - bytes32 salt, - string calldata name, - string calldata symbol, - uint8 decimals - ) public { - MockERC20 deployed = MockERC20( - this.deploy( - salt, - abi.encodePacked(type(MockERC20).creationCode, abi.encode(name, symbol, decimals)), - 0 - ) - ); - - assertEq(address(deployed), CREATE3.getDeployed(salt)); - - assertEq(deployed.name(), name); - assertEq(deployed.symbol(), symbol); - assertEq(deployed.decimals(), decimals); - } - - function testDoubleDeploySameBytecodeReverts(bytes32 salt, bytes calldata bytecode) public { - bytes memory creationCode = _creationCode(bytecode); - this.deploy(salt, creationCode, 0); - vm.expectRevert(CREATE3.DeploymentFailed.selector); - this.deploy(salt, creationCode, 0); - } - - function testDoubleDeployDifferentBytecodeReverts( - bytes32 salt, - bytes memory bytecode1, - bytes memory bytecode2 - ) public { - this.deploy(salt, _creationCode(bytecode1), 0); - vm.expectRevert(CREATE3.DeploymentFailed.selector); - this.deploy(salt, _creationCode(bytecode2), 0); - } - - function deploy(bytes32 salt, bytes calldata creationCode, uint256 value) - external - returns (address) - { - return CREATE3.deploy(salt, creationCode, value); - } - - function _creationCode(bytes memory bytecode) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - // Trim the length if needed. - let length := mload(bytecode) - let maxLength := 24566 // `24576 - 0xa`. - if iszero(lt(length, maxLength)) { mstore(bytecode, maxLength) } - // The following snippet is from SSTORE2. - result := mload(0x40) - length := mload(bytecode) - let dataSize := add(length, 1) - mstore(0x40, and(add(add(result, dataSize), 0x60), not(0x1f))) - mstore(add(result, 0x0b), or(0x61000080600a3d393df300, shl(0x40, dataSize))) - mstore(result, add(dataSize, 0xa)) // Store the length of result. - // Copy the bytes over. - for { let i := 0 } lt(i, length) { i := add(i, 0x20) } { - mstore(add(add(bytecode, 0x20), i), mload(add(add(result, 0x2b), i))) - } - } - } -} diff --git a/lib/solady/test/DateTimeLib.t.sol b/lib/solady/test/DateTimeLib.t.sol deleted file mode 100644 index 099412a..0000000 --- a/lib/solady/test/DateTimeLib.t.sol +++ /dev/null @@ -1,796 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../test/utils/SoladyTest.sol"; -import {DateTimeLib} from "../src/utils/DateTimeLib.sol"; - -contract DateTimeLibTest is SoladyTest { - struct DateTime { - uint256 year; - uint256 month; - uint256 day; - uint256 hour; - uint256 minute; - uint256 second; - } - - function testDateTimeMaxSupported() public { - DateTime memory d; - assertEq( - DateTimeLib.dateToEpochDay(DateTimeLib.MAX_SUPPORTED_YEAR, 12, 31), - DateTimeLib.MAX_SUPPORTED_EPOCH_DAY - ); - assertEq( - DateTimeLib.dateToTimestamp(DateTimeLib.MAX_SUPPORTED_YEAR, 12, 31) + 86400 - 1, - DateTimeLib.MAX_SUPPORTED_TIMESTAMP - ); - (d.year, d.month, d.day) = DateTimeLib.timestampToDate(DateTimeLib.MAX_SUPPORTED_TIMESTAMP); - assertTrue(d.year == DateTimeLib.MAX_SUPPORTED_YEAR && d.month == 12 && d.day == 31); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(DateTimeLib.MAX_SUPPORTED_EPOCH_DAY); - assertTrue(d.year == DateTimeLib.MAX_SUPPORTED_YEAR && d.month == 12 && d.day == 31); - (d.year, d.month, d.day) = - DateTimeLib.timestampToDate(DateTimeLib.MAX_SUPPORTED_TIMESTAMP + 1); - assertFalse(d.year == DateTimeLib.MAX_SUPPORTED_YEAR && d.month == 12 && d.day == 31); - (d.year, d.month, d.day) = - DateTimeLib.epochDayToDate(DateTimeLib.MAX_SUPPORTED_EPOCH_DAY + 1); - assertFalse(d.year == DateTimeLib.MAX_SUPPORTED_YEAR && d.month == 12 && d.day == 31); - } - - function testDateToEpochDay() public { - assertEq(DateTimeLib.dateToEpochDay(1970, 1, 1), 0); - assertEq(DateTimeLib.dateToEpochDay(1970, 1, 2), 1); - assertEq(DateTimeLib.dateToEpochDay(1970, 2, 1), 31); - assertEq(DateTimeLib.dateToEpochDay(1970, 3, 1), 59); - assertEq(DateTimeLib.dateToEpochDay(1970, 4, 1), 90); - assertEq(DateTimeLib.dateToEpochDay(1970, 5, 1), 120); - assertEq(DateTimeLib.dateToEpochDay(1970, 6, 1), 151); - assertEq(DateTimeLib.dateToEpochDay(1970, 7, 1), 181); - assertEq(DateTimeLib.dateToEpochDay(1970, 8, 1), 212); - assertEq(DateTimeLib.dateToEpochDay(1970, 9, 1), 243); - assertEq(DateTimeLib.dateToEpochDay(1970, 10, 1), 273); - assertEq(DateTimeLib.dateToEpochDay(1970, 11, 1), 304); - assertEq(DateTimeLib.dateToEpochDay(1970, 12, 1), 334); - assertEq(DateTimeLib.dateToEpochDay(1970, 12, 31), 364); - assertEq(DateTimeLib.dateToEpochDay(1971, 1, 1), 365); - assertEq(DateTimeLib.dateToEpochDay(1980, 11, 3), 3959); - assertEq(DateTimeLib.dateToEpochDay(2000, 3, 1), 11017); - assertEq(DateTimeLib.dateToEpochDay(2355, 12, 31), 140982); - assertEq(DateTimeLib.dateToEpochDay(99999, 12, 31), 35804721); - assertEq(DateTimeLib.dateToEpochDay(100000, 12, 31), 35805087); - assertEq(DateTimeLib.dateToEpochDay(604800, 2, 29), 220179195); - assertEq(DateTimeLib.dateToEpochDay(1667347200, 2, 29), 608985340227); - assertEq(DateTimeLib.dateToEpochDay(1667952000, 2, 29), 609206238891); - } - - function testDateToEpochDayGas() public { - unchecked { - uint256 sum; - for (uint256 i; i < 256; ++i) { - uint256 year = _bound(_random(), 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - uint256 month = _bound(_random(), 1, 12); - uint256 md = DateTimeLib.daysInMonth(year, month); - uint256 day = _bound(_random(), 1, md); - uint256 epochDay = DateTimeLib.dateToEpochDay(year, month, day); - sum += epochDay; - } - assertTrue(sum != 0); - } - } - - function testDateToEpochDayGas2() public { - unchecked { - uint256 sum; - for (uint256 i; i < 256; ++i) { - uint256 year = _bound(_random(), 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - uint256 month = _bound(_random(), 1, 12); - uint256 md = DateTimeLib.daysInMonth(year, month); - uint256 day = _bound(_random(), 1, md); - uint256 epochDay = _dateToEpochDayOriginal2(year, month, day); - sum += epochDay; - } - assertTrue(sum != 0); - } - } - - function testEpochDayToDateGas() public { - unchecked { - uint256 sum; - for (uint256 i; i < 256; ++i) { - uint256 epochDay = _bound(_random(), 0, DateTimeLib.MAX_SUPPORTED_EPOCH_DAY); - (uint256 year, uint256 month, uint256 day) = DateTimeLib.epochDayToDate(epochDay); - sum += year + month + day; - } - assertTrue(sum != 0); - } - } - - function testEpochDayToDateGas2() public { - unchecked { - uint256 sum; - for (uint256 i; i < 256; ++i) { - uint256 epochDay = _bound(_random(), 0, DateTimeLib.MAX_SUPPORTED_EPOCH_DAY); - (uint256 year, uint256 month, uint256 day) = _epochDayToDateOriginal2(epochDay); - sum += year + month + day; - } - assertTrue(sum != 0); - } - } - - function testDateToEpochDayDifferential(DateTime memory d) public { - d.year = _bound(d.year, 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - d.month = _bound(d.month, 1, 12); - d.day = _bound(d.day, 1, DateTimeLib.daysInMonth(d.year, d.month)); - uint256 expectedResult = _dateToEpochDayOriginal(d.year, d.month, d.day); - assertEq(DateTimeLib.dateToEpochDay(d.year, d.month, d.day), expectedResult); - } - - function testDateToEpochDayDifferential2(DateTime memory d) public { - d.year = _bound(d.year, 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - d.month = _bound(d.month, 1, 12); - d.day = _bound(d.day, 1, DateTimeLib.daysInMonth(d.year, d.month)); - uint256 expectedResult = _dateToEpochDayOriginal2(d.year, d.month, d.day); - assertEq(DateTimeLib.dateToEpochDay(d.year, d.month, d.day), expectedResult); - } - - function testEpochDayToDateDifferential(uint256 timestamp) public { - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day) = _epochDayToDateOriginal(timestamp); - (b.year, b.month, b.day) = DateTimeLib.epochDayToDate(timestamp); - assertTrue(a.year == b.year && a.month == b.month && a.day == b.day); - } - - function testEpochDayToDateDifferential2(uint256 timestamp) public { - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day) = _epochDayToDateOriginal2(timestamp); - (b.year, b.month, b.day) = DateTimeLib.epochDayToDate(timestamp); - assertTrue(a.year == b.year && a.month == b.month && a.day == b.day); - } - - function testDaysToDate() public { - DateTime memory d; - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(0); - assertTrue(d.year == 1970 && d.month == 1 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(31); - assertTrue(d.year == 1970 && d.month == 2 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(59); - assertTrue(d.year == 1970 && d.month == 3 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(90); - assertTrue(d.year == 1970 && d.month == 4 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(120); - assertTrue(d.year == 1970 && d.month == 5 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(151); - assertTrue(d.year == 1970 && d.month == 6 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(181); - assertTrue(d.year == 1970 && d.month == 7 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(212); - assertTrue(d.year == 1970 && d.month == 8 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(243); - assertTrue(d.year == 1970 && d.month == 9 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(273); - assertTrue(d.year == 1970 && d.month == 10 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(304); - assertTrue(d.year == 1970 && d.month == 11 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(334); - assertTrue(d.year == 1970 && d.month == 12 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(365); - assertTrue(d.year == 1971 && d.month == 1 && d.day == 1); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(10987); - assertTrue(d.year == 2000 && d.month == 1 && d.day == 31); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(18321); - assertTrue(d.year == 2020 && d.month == 2 && d.day == 29); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(156468); - assertTrue(d.year == 2398 && d.month == 5 && d.day == 25); - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(35805087); - assertTrue(d.year == 100000 && d.month == 12 && d.day == 31); - } - - function testEpochDayToDate(uint256 epochDay) public { - DateTime memory d; - (d.year, d.month, d.day) = DateTimeLib.epochDayToDate(epochDay); - assertEq(epochDay, DateTimeLib.dateToEpochDay(d.year, d.month, d.day)); - } - - function testDateToAndFroEpochDay(DateTime memory a) public { - a.year = _bound(a.year, 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - a.month = _bound(a.month, 1, 12); - uint256 md = DateTimeLib.daysInMonth(a.year, a.month); - a.day = _bound(a.day, 1, md); - uint256 epochDay = DateTimeLib.dateToEpochDay(a.year, a.month, a.day); - DateTime memory b; - (b.year, b.month, b.day) = DateTimeLib.epochDayToDate(epochDay); - assertTrue(a.year == b.year && a.month == b.month && a.day == b.day); - } - - function testDateTimeToAndFroTimestamp(DateTime memory a) public { - a.year = _bound(a.year, 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - a.month = _bound(a.month, 1, 12); - uint256 md = DateTimeLib.daysInMonth(a.year, a.month); - a.day = _bound(a.day, 1, md); - a.hour = _bound(a.hour, 0, 23); - a.minute = _bound(a.minute, 0, 59); - a.second = _bound(a.second, 0, 59); - uint256 timestamp = - DateTimeLib.dateTimeToTimestamp(a.year, a.month, a.day, a.hour, a.minute, a.second); - DateTime memory b; - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(timestamp); - assertTrue(a.year == b.year && a.month == b.month && a.day == b.day); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testDateToAndFroEpochDay() public { - unchecked { - for (uint256 i; i < 256; ++i) { - uint256 year = _bound(_random(), 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - uint256 month = _bound(_random(), 1, 12); - uint256 md = DateTimeLib.daysInMonth(year, month); - uint256 day = _bound(_random(), 1, md); - uint256 epochDay = DateTimeLib.dateToEpochDay(year, month, day); - (uint256 y, uint256 m, uint256 d) = DateTimeLib.epochDayToDate(epochDay); - assertTrue(year == y && month == m && day == d); - } - } - } - - function testDateToAndFroTimestamp() public { - unchecked { - for (uint256 i; i < 256; ++i) { - uint256 year = _bound(_random(), 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - uint256 month = _bound(_random(), 1, 12); - uint256 md = DateTimeLib.daysInMonth(year, month); - uint256 day = _bound(_random(), 1, md); - uint256 timestamp = DateTimeLib.dateToTimestamp(year, month, day); - assertEq(timestamp, DateTimeLib.dateToEpochDay(year, month, day) * 86400); - (uint256 y, uint256 m, uint256 d) = DateTimeLib.timestampToDate(timestamp); - assertTrue(year == y && month == m && day == d); - } - } - } - - function testIsLeapYear() public { - assertTrue(DateTimeLib.isLeapYear(2000)); - assertTrue(DateTimeLib.isLeapYear(2024)); - assertTrue(DateTimeLib.isLeapYear(2048)); - assertTrue(DateTimeLib.isLeapYear(2072)); - assertTrue(DateTimeLib.isLeapYear(2104)); - assertTrue(DateTimeLib.isLeapYear(2128)); - assertTrue(DateTimeLib.isLeapYear(10032)); - assertTrue(DateTimeLib.isLeapYear(10124)); - assertTrue(DateTimeLib.isLeapYear(10296)); - assertTrue(DateTimeLib.isLeapYear(10400)); - assertTrue(DateTimeLib.isLeapYear(10916)); - } - - function testIsLeapYear(uint256 year) public { - assertEq( - DateTimeLib.isLeapYear(year), (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0) - ); - } - - function testDaysInMonth() public { - assertEq(DateTimeLib.daysInMonth(2022, 1), 31); - assertEq(DateTimeLib.daysInMonth(2022, 2), 28); - assertEq(DateTimeLib.daysInMonth(2022, 3), 31); - assertEq(DateTimeLib.daysInMonth(2022, 4), 30); - assertEq(DateTimeLib.daysInMonth(2022, 5), 31); - assertEq(DateTimeLib.daysInMonth(2022, 6), 30); - assertEq(DateTimeLib.daysInMonth(2022, 7), 31); - assertEq(DateTimeLib.daysInMonth(2022, 8), 31); - assertEq(DateTimeLib.daysInMonth(2022, 9), 30); - assertEq(DateTimeLib.daysInMonth(2022, 10), 31); - assertEq(DateTimeLib.daysInMonth(2022, 11), 30); - assertEq(DateTimeLib.daysInMonth(2022, 12), 31); - assertEq(DateTimeLib.daysInMonth(2024, 1), 31); - assertEq(DateTimeLib.daysInMonth(2024, 2), 29); - assertEq(DateTimeLib.daysInMonth(1900, 2), 28); - } - - function testDaysInMonth(uint256 year, uint256 month) public { - month = _bound(month, 1, 12); - if (DateTimeLib.isLeapYear(year) && month == 2) { - assertEq(DateTimeLib.daysInMonth(year, month), 29); - } else if ( - month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 - || month == 12 - ) { - assertEq(DateTimeLib.daysInMonth(year, month), 31); - } else if (month == 2) { - assertEq(DateTimeLib.daysInMonth(year, month), 28); - } else { - assertEq(DateTimeLib.daysInMonth(year, month), 30); - } - } - - function testWeekday() public { - assertEq(DateTimeLib.weekday(1), 4); - assertEq(DateTimeLib.weekday(86400), 5); - assertEq(DateTimeLib.weekday(86401), 5); - assertEq(DateTimeLib.weekday(172800), 6); - assertEq(DateTimeLib.weekday(259200), 7); - assertEq(DateTimeLib.weekday(345600), 1); - assertEq(DateTimeLib.weekday(432000), 2); - assertEq(DateTimeLib.weekday(518400), 3); - } - - function testDayOfWeek() public { - uint256 timestamp = 0; - uint256 weekday = 3; - unchecked { - for (uint256 i = 0; i < 1000; ++i) { - assertEq(DateTimeLib.weekday(timestamp) - 1, weekday); - timestamp += 86400; - weekday = (weekday + 1) % 7; - } - } - } - - function testIsSupportedDateTrue() public { - assertTrue(DateTimeLib.isSupportedDate(1970, 1, 1)); - assertTrue(DateTimeLib.isSupportedDate(1971, 5, 31)); - assertTrue(DateTimeLib.isSupportedDate(1971, 6, 30)); - assertTrue(DateTimeLib.isSupportedDate(1971, 12, 31)); - assertTrue(DateTimeLib.isSupportedDate(1972, 2, 28)); - assertTrue(DateTimeLib.isSupportedDate(1972, 4, 30)); - assertTrue(DateTimeLib.isSupportedDate(1972, 5, 31)); - assertTrue(DateTimeLib.isSupportedDate(2000, 2, 29)); - assertTrue(DateTimeLib.isSupportedDate(DateTimeLib.MAX_SUPPORTED_YEAR, 5, 31)); - } - - function testIsSupportedDateFalse() public { - assertFalse(DateTimeLib.isSupportedDate(0, 0, 0)); - assertFalse(DateTimeLib.isSupportedDate(1970, 0, 0)); - assertFalse(DateTimeLib.isSupportedDate(1970, 1, 0)); - assertFalse(DateTimeLib.isSupportedDate(1969, 1, 1)); - assertFalse(DateTimeLib.isSupportedDate(1800, 1, 1)); - assertFalse(DateTimeLib.isSupportedDate(1970, 13, 1)); - assertFalse(DateTimeLib.isSupportedDate(1700, 13, 1)); - assertFalse(DateTimeLib.isSupportedDate(1970, 15, 32)); - assertFalse(DateTimeLib.isSupportedDate(1970, 1, 32)); - assertFalse(DateTimeLib.isSupportedDate(1970, 13, 1)); - assertFalse(DateTimeLib.isSupportedDate(1879, 1, 1)); - assertFalse(DateTimeLib.isSupportedDate(1970, 4, 31)); - assertFalse(DateTimeLib.isSupportedDate(1970, 6, 31)); - assertFalse(DateTimeLib.isSupportedDate(1970, 7, 32)); - assertFalse(DateTimeLib.isSupportedDate(2000, 2, 30)); - assertFalse(DateTimeLib.isSupportedDate(DateTimeLib.MAX_SUPPORTED_YEAR + 1, 5, 31)); - assertFalse(DateTimeLib.isSupportedDate(type(uint256).max, 5, 31)); - } - - function testIsSupportedDateTime(DateTime memory a) public { - a.month = _bound(a.month, 0, 20); - a.day = _bound(a.day, 0, 50); - a.hour = _bound(a.hour, 0, 50); - a.minute = _bound(a.minute, 0, 100); - a.second = _bound(a.second, 0, 100); - bool isSupported = (1970 <= a.year && a.year <= DateTimeLib.MAX_SUPPORTED_YEAR) - && (1 <= a.month && a.month <= 12) - && (1 <= a.day && a.day <= DateTimeLib.daysInMonth(a.year, a.month)) && (a.hour < 24) - && (a.minute < 60) && (a.second < 60); - assertEq( - DateTimeLib.isSupportedDateTime(a.year, a.month, a.day, a.hour, a.minute, a.second), - isSupported - ); - } - - function testIsSupportedEpochDayTrue() public { - assertTrue(DateTimeLib.isSupportedEpochDay(0)); - assertTrue(DateTimeLib.isSupportedEpochDay(DateTimeLib.MAX_SUPPORTED_EPOCH_DAY)); - } - - function testIsSupportedEpochDayFalse() public { - assertFalse(DateTimeLib.isSupportedEpochDay(DateTimeLib.MAX_SUPPORTED_EPOCH_DAY + 1)); - assertFalse(DateTimeLib.isSupportedEpochDay(DateTimeLib.MAX_SUPPORTED_EPOCH_DAY + 2)); - } - - function testIsSupportedTimestampTrue() public { - assertTrue(DateTimeLib.isSupportedTimestamp(0)); - assertTrue(DateTimeLib.isSupportedTimestamp(DateTimeLib.MAX_SUPPORTED_TIMESTAMP)); - } - - function testIsSupportedTimestampFalse() public { - assertFalse(DateTimeLib.isSupportedTimestamp(DateTimeLib.MAX_SUPPORTED_TIMESTAMP + 1)); - assertFalse(DateTimeLib.isSupportedTimestamp(DateTimeLib.MAX_SUPPORTED_TIMESTAMP + 2)); - } - - function testNthWeekdayInMonthOfYearTimestamp() public { - uint256 wd; - // 1st 2nd 3rd 4th monday in November 2022. - wd = DateTimeLib.MON; - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 1, wd), 1667779200); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 2, wd), 1668384000); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 3, wd), 1668988800); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 4, wd), 1669593600); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 5, wd), 0); - - // 1st... 5th Wednesday in November 2022. - wd = DateTimeLib.WED; - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 1, wd), 1667347200); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 2, wd), 1667952000); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 3, wd), 1668556800); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 4, wd), 1669161600); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 5, wd), 1669766400); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 11, 6, wd), 0); - - // 1st... 5th Friday in December 2022. - wd = DateTimeLib.FRI; - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 1, wd), 1669939200); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 2, wd), 1670544000); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 3, wd), 1671148800); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 4, wd), 1671753600); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 5, wd), 1672358400); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2022, 12, 6, wd), 0); - - // 1st... 5th Sunday in January 2023. - wd = DateTimeLib.SUN; - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 1, wd), 1672531200); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 2, wd), 1673136000); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 3, wd), 1673740800); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 4, wd), 1674345600); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 5, wd), 1674950400); - assertEq(DateTimeLib.nthWeekdayInMonthOfYearTimestamp(2023, 1, 6, wd), 0); - } - - function testNthWeekdayInMonthOfYearTimestamp( - uint256 year, - uint256 month, - uint256 n, - uint256 weekday - ) public { - unchecked { - year = _bound(year, 1970, DateTimeLib.MAX_SUPPORTED_YEAR); - month = _bound(month, 1, 12); - n = _bound(n, 1, 10); - weekday = _bound(weekday, 1, 7); - // Count number of weekdays for the month in the year. - uint256 md = DateTimeLib.daysInMonth(year, month); - uint256 timestamp = DateTimeLib.dateToTimestamp(year, month, 1); - uint256 m; - uint256 found; - for (uint256 i; i < md;) { - if (DateTimeLib.weekday(timestamp) == weekday) { - if (++m == n) { - found = 1; - break; - } - } - if (m == 0) { - timestamp += 86400; - i += 1; - } else { - timestamp += 86400 * 7; - i += 7; - } - } - assertEq( - DateTimeLib.nthWeekdayInMonthOfYearTimestamp(year, month, n, weekday), - found * timestamp - ); - } - } - - function testMondayTimestamp() public { - // Thursday 01 January 1970 -> 0 - assertEq(DateTimeLib.mondayTimestamp(0), 0); - // Friday 02 January 1970 -> 86400 - assertEq(DateTimeLib.mondayTimestamp(86400), 0); - // Saturday 03 January 1970 -> 172800 - assertEq(DateTimeLib.mondayTimestamp(172800), 0); - // Sunday 04 January 1970 -> 259200 - assertEq(DateTimeLib.mondayTimestamp(259200), 0); - // Monday 05 January 19700 -> 345600 - assertEq(DateTimeLib.mondayTimestamp(345600), 345600); - // Monday 07 November 2022 -> 1667779200 - assertEq(DateTimeLib.mondayTimestamp(1667779200), 1667779200); - // Sunday 06 November 2022 -> 1667692800 - assertEq(DateTimeLib.mondayTimestamp(1667692800), 1667174400); - // Saturday 05 November 2022 -> 1667606400 - assertEq(DateTimeLib.mondayTimestamp(1667606400), 1667174400); - // Friday 04 November 2022 -> 1667520000 - assertEq(DateTimeLib.mondayTimestamp(1667520000), 1667174400); - // Thursday 03 November 2022 -> 1667433600 - assertEq(DateTimeLib.mondayTimestamp(1667433600), 1667174400); - // Wednesday 02 November 2022 -> 1667347200 - assertEq(DateTimeLib.mondayTimestamp(1667347200), 1667174400); - // Tuesday 01 November 2022 -> 1667260800 - assertEq(DateTimeLib.mondayTimestamp(1667260800), 1667174400); - // Monday 01 November 2022 -> 1667260800 - assertEq(DateTimeLib.mondayTimestamp(1667174400), 1667174400); - } - - function testMondayTimestamp(uint256 timestamp) public { - uint256 day = timestamp / 86400; - uint256 weekday = (day + 3) % 7; - assertEq( - DateTimeLib.mondayTimestamp(timestamp), timestamp > 345599 ? (day - weekday) * 86400 : 0 - ); - } - - function testIsWeekEnd(uint256 timestamp) public { - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP); - uint256 weekday = DateTimeLib.weekday(timestamp); - assertEq( - DateTimeLib.isWeekEnd(timestamp), - weekday == DateTimeLib.SAT || weekday == DateTimeLib.SUN - ); - } - - function testAddSubDiffYears(uint256 timestamp, uint256 numYears) public { - uint256 maxNumYears = 1000000; - numYears = _bound(numYears, 0, maxNumYears); - timestamp = - _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumYears * 366 * 86400); - uint256 result = DateTimeLib.addYears(timestamp, numYears); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numYears != 0) assertTrue(a.year != b.year); - if (a.day <= 28) assertEq(a.day, b.day); - assertTrue(a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - uint256 diff = DateTimeLib.diffYears(timestamp, result); - assertTrue(diff == numYears); - result = DateTimeLib.subYears(result, numYears); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testDateTimeArithmeticReverts() public { - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addYears(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subYears(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffYears(2 ** 128 - 1, 2 ** 127 - 1); - - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addMonths(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subMonths(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffMonths(2 ** 128 - 1, 2 ** 127 - 1); - - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addDays(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subDays(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffDays(2 ** 128 - 1, 2 ** 127 - 1); - - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addHours(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subHours(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffHours(2 ** 128 - 1, 2 ** 127 - 1); - - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addMinutes(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subMinutes(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffMinutes(2 ** 128 - 1, 2 ** 127 - 1); - - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.addSeconds(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.subSeconds(2 ** 128 - 1, 2 ** 255 - 1); - vm.expectRevert(stdError.arithmeticError); - DateTimeLib.diffSeconds(2 ** 128 - 1, 2 ** 127 - 1); - } - - function testAddSubDiffMonths(uint256 timestamp, uint256 numMonths) public { - uint256 maxNumMonths = 1000000; - numMonths = _bound(numMonths, 0, maxNumMonths); - timestamp = - _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumMonths * 32 * 86400); - uint256 result = DateTimeLib.addMonths(timestamp, numMonths); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numMonths != 0) assertTrue(a.year != b.year || a.month != b.month); - if (a.day <= 28) assertEq(a.day, b.day); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - uint256 diff = DateTimeLib.diffMonths(timestamp, result); - assertTrue(diff == numMonths); - result = DateTimeLib.subMonths(result, numMonths); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testAddSubDiffDays(uint256 timestamp, uint256 numDays) public { - uint256 maxNumDays = 100000000; - numDays = _bound(numDays, 0, maxNumDays); - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumDays * 86400); - uint256 result = DateTimeLib.addDays(timestamp, numDays); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numDays != 0) { - assertTrue(a.year != b.year || a.month != b.month || a.day != b.day); - } - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - uint256 diff = DateTimeLib.diffDays(timestamp, result); - assertTrue(diff == numDays); - result = DateTimeLib.subDays(result, numDays); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testAddSubDiffHours(uint256 timestamp, uint256 numHours) public { - uint256 maxNumHours = 10000000000; - numHours = _bound(numHours, 0, maxNumHours); - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumHours * 3600); - uint256 result = DateTimeLib.addHours(timestamp, numHours); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numHours != 0) { - assertTrue(a.year != b.year || a.month != b.month || a.day != b.day || a.hour != b.hour); - } - assertTrue(a.minute == b.minute && a.second == b.second); - uint256 diff = DateTimeLib.diffHours(timestamp, result); - assertTrue(diff == numHours); - result = DateTimeLib.subHours(result, numHours); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testAddSubDiffMinutes(uint256 timestamp, uint256 numMinutes) public { - uint256 maxNumMinutes = 10000000000; - numMinutes = _bound(numMinutes, 0, maxNumMinutes); - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumMinutes * 60); - uint256 result = DateTimeLib.addMinutes(timestamp, numMinutes); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numMinutes != 0) { - assertTrue( - (a.year != b.year || a.month != b.month || a.day != b.day) - || (a.hour != b.hour || a.minute != b.minute) - ); - } - assertTrue(a.second == b.second); - uint256 diff = DateTimeLib.diffMinutes(timestamp, result); - assertTrue(diff == numMinutes); - result = DateTimeLib.subMinutes(result, numMinutes); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function testAddSubDiffSeconds(uint256 timestamp, uint256 numSeconds) public { - uint256 maxNumSeconds = 1000000000000; - numSeconds = _bound(numSeconds, 0, maxNumSeconds); - timestamp = _bound(timestamp, 0, DateTimeLib.MAX_SUPPORTED_TIMESTAMP - maxNumSeconds); - uint256 result = DateTimeLib.addSeconds(timestamp, numSeconds); - DateTime memory a; - DateTime memory b; - (a.year, a.month, a.day, a.hour, a.minute, a.second) = - DateTimeLib.timestampToDateTime(timestamp); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - if (numSeconds != 0) { - assertTrue( - (a.year != b.year || a.month != b.month || a.day != b.day) - || (a.hour != b.hour || a.minute != b.minute || a.second != b.second) - ); - } - uint256 diff = DateTimeLib.diffSeconds(timestamp, result); - assertTrue(diff == numSeconds); - result = DateTimeLib.subSeconds(result, numSeconds); - (b.year, b.month, b.day, b.hour, b.minute, b.second) = - DateTimeLib.timestampToDateTime(result); - assertTrue(a.year == b.year && a.month == b.month); - assertTrue(a.hour == b.hour && a.minute == b.minute && a.second == b.second); - } - - function _dateToEpochDayOriginal(uint256 year, uint256 month, uint256 day) - internal - pure - returns (uint256) - { - unchecked { - if (month <= 2) { - year -= 1; - } - uint256 era = year / 400; - uint256 yoe = year - era * 400; - uint256 doy = (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1; - uint256 doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - return era * 146097 + doe - 719468; - } - } - - function _dateToEpochDayOriginal2(uint256 year, uint256 month, uint256 day) - internal - pure - returns (uint256 _days) - { - unchecked { - int256 _year = int256(year); - int256 _month = int256(month); - int256 _day = int256(day); - - int256 _m = (_month - 14) / 12; - int256 __days = _day - 32075 + ((1461 * (_year + 4800 + _m)) / 4) - + ((367 * (_month - 2 - _m * 12)) / 12) - ((3 * ((_year + 4900 + _m) / 100)) / 4) - - 2440588; - - _days = uint256(__days); - } - } - - function _epochDayToDateOriginal(uint256 timestamp) - internal - pure - returns (uint256 year, uint256 month, uint256 day) - { - unchecked { - timestamp += 719468; - uint256 era = timestamp / 146097; - uint256 doe = timestamp - era * 146097; - uint256 yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; - year = yoe + era * 400; - uint256 doy = doe - (365 * yoe + yoe / 4 - yoe / 100); - uint256 mp = (5 * doy + 2) / 153; - day = doy - (153 * mp + 2) / 5 + 1; - month = mp < 10 ? mp + 3 : mp - 9; - if (month <= 2) { - year += 1; - } - } - } - - function _epochDayToDateOriginal2(uint256 _days) - internal - pure - returns (uint256 year, uint256 month, uint256 day) - { - unchecked { - int256 __days = int256(_days); - - int256 L = __days + 68569 + 2440588; - int256 N = (4 * L) / 146097; - L = L - (146097 * N + 3) / 4; - int256 _year = (4000 * (L + 1)) / 1461001; - L = L - (1461 * _year) / 4 + 31; - int256 _month = (80 * L) / 2447; - int256 _day = L - (2447 * _month) / 80; - L = _month / 11; - _month = _month + 2 - 12 * L; - _year = 100 * (N - 49) + _year + L; - - year = uint256(_year); - month = uint256(_month); - day = uint256(_day); - } - } -} diff --git a/lib/solady/test/DynamicBufferLib.t.sol b/lib/solady/test/DynamicBufferLib.t.sol deleted file mode 100644 index 281553f..0000000 --- a/lib/solady/test/DynamicBufferLib.t.sol +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {DynamicBufferLib} from "../src/utils/DynamicBufferLib.sol"; - -contract DynamicBufferLibTest is SoladyTest { - using DynamicBufferLib for DynamicBufferLib.DynamicBuffer; - - function testClear(uint256) public { - DynamicBufferLib.DynamicBuffer memory buffer; - bytes memory b0 = _generateRandomBytes(128, _random()); - bytes memory b1 = _generateRandomBytes(256, _random()); - bytes memory emptyBytes; - assertEq(buffer.data.length, 0); - assertEq(emptyBytes.length, 0); - if (_random() & 1 == 0) buffer.clear(); - assertEq(buffer.data.length, 0); - assertEq(emptyBytes.length, 0); - buffer.clear().p(b0); - assertEq(buffer.data, b0); - assertEq(emptyBytes.length, 0); - uint256 n0 = _bound(_random(), 0, 1024); - uint256 n1 = _bound(_random(), 0, 4096); - buffer.reserve(n0).p(b1).clear().reserve(n1); - assertEq(buffer.data.length, 0); - assertEq(emptyBytes.length, 0); - buffer.p(b1); - assertEq(buffer.data, b1); - assertEq(emptyBytes.length, 0); - buffer.p(b0); - assertEq(buffer.data, abi.encodePacked(b1, b0)); - assertEq(emptyBytes.length, 0); - buffer.clear(); - } - - function testDynamicBufferReserveFromEmpty() public { - uint256 m = _freeMemoryPointer(); - DynamicBufferLib.DynamicBuffer memory buffer; - assertEq(_freeMemoryPointer(), m + 0x20); - buffer.reserve(0x200); - assertTrue(_freeMemoryPointer() > m + 0x20); - assertTrue(_freeMemoryPointer() < 0xffff); - m = _freeMemoryPointer(); - buffer.reserve(0x200); - assertEq(_freeMemoryPointer(), m); - buffer.reserve(0x200); - assertEq(_freeMemoryPointer(), m); - } - - function testDynamicBufferReserveFromEmpty2() public { - DynamicBufferLib.DynamicBuffer memory buffer; - _incrementFreeMemoryPointer(); - buffer.reserve(0x200); - uint256 m = _freeMemoryPointer(); - buffer.reserve(0x200); - assertEq(_freeMemoryPointer(), m); - buffer.reserve(0x200); - assertEq(_freeMemoryPointer(), m); - } - - function testDynamicBufferReserveFromEmpty3(bytes calldata b, uint256 t) public { - DynamicBufferLib.DynamicBuffer memory buffer; - if (t & 1 == 0) _incrementFreeMemoryPointer(); - if (t & 2 == 0) buffer.p(_generateRandomBytes((t >> 32) & 0xff, 1)); - if (t & 4 == 0) buffer.p(b); - assertTrue(_freeMemoryPointer() < 0xffffff); - uint256 r = t >> 240; - buffer.reserve(r); - assertTrue(_freeMemoryPointer() < 0xffffff); - uint256 m = _freeMemoryPointer(); - buffer.reserve(r); - assertEq(_freeMemoryPointer(), m); - buffer.reserve(r); - assertEq(_freeMemoryPointer(), m); - } - - function _incrementFreeMemoryPointer() internal pure { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, add(mload(0x40), 0x20)) - } - } - - function _freeMemoryPointer() internal pure returns (uint256 m) { - /// @solidity memory-safe-assembly - assembly { - m := mload(0x40) - } - } - - function _bufferLocation(DynamicBufferLib.DynamicBuffer memory buffer) - internal - pure - returns (uint256 result) - { - /// @solidity memory-safe-assembly - assembly { - result := mload(buffer) - } - } - - function testDynamicBuffer(uint256) public brutalizeMemory { - unchecked { - if (_random() & 7 == 0) _misalignFreeMemoryPointer(); - DynamicBufferLib.DynamicBuffer memory bufferA; - DynamicBufferLib.DynamicBuffer memory bufferB; - uint256 z = _bound(_random(), 32, 4096); - if (_random() & 7 == 0) bufferA.reserve(_random() % z); - if (_random() & 7 == 0) bufferB.reserve(_random() % z); - uint256 r = _random() % 3; - uint256 o = _bound(_random(), 0, 32); - uint256 n = _bound(_random(), 5, _random() & 7 == 0 ? 64 : 8); - z = z + z; - - if (r == 0) { - for (uint256 i; i != n; ++i) { - if (_random() & 7 == 0) bufferA.reserve(_random() % z); - bufferA.p(_generateRandomBytes(i + o, i + z)); - } - for (uint256 i; i != n; ++i) { - if (_random() & 7 == 0) bufferB.reserve(_random() % z); - bufferB.p(_generateRandomBytes(i + o, i + z)); - } - } else if (r == 1) { - for (uint256 i; i != n; ++i) { - if (_random() & 7 == 0) bufferB.reserve(_random() % z); - bufferB.p(_generateRandomBytes(i + o, i + z)); - } - for (uint256 i; i != n; ++i) { - if (_random() & 7 == 0) bufferA.reserve(_random() % z); - bufferA.p(_generateRandomBytes(i + o, i + z)); - } - } else { - uint256 mode; - for (uint256 i; i != n; ++i) { - if (_random() & 7 == 0) mode ^= 1; - if (mode == 0) { - if (_random() & 7 == 0) bufferA.reserve(_random() % z); - bufferA.p(_generateRandomBytes(i + o, i + z)); - if (_random() & 7 == 0) bufferB.reserve(_random() % z); - bufferB.p(_generateRandomBytes(i + o, i + z)); - } else { - if (_random() & 7 == 0) bufferB.reserve(_random() % z); - bufferB.p(_generateRandomBytes(i + o, i + z)); - if (_random() & 7 == 0) bufferA.reserve(_random() % z); - bufferA.p(_generateRandomBytes(i + o, i + z)); - } - } - } - - bytes memory expected; - for (uint256 i; i != n; ++i) { - expected = bytes.concat(expected, _generateRandomBytes(i + o, i + z)); - } - assertEq(bufferA.data, expected); - assertEq(bufferB.data, expected); - } - } - - function _generateRandomBytes(uint256 n, uint256 seed) - internal - pure - returns (bytes memory result) - { - /// @solidity memory-safe-assembly - assembly { - if n { - result := mload(0x40) - mstore(result, n) - mstore(0x00, seed) - for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { - mstore(0x20, i) - mstore(add(add(result, 0x20), i), keccak256(0x00, 0x40)) - } - mstore(0x40, add(add(result, 0x20), n)) - } - } - } - - function testDynamicBuffer(bytes[] memory inputs, uint256 t) public brutalizeMemory { - _boundInputs(inputs); - - uint256 sharedLocation; - if ((t >> 128) & 1 == 0) { - bytes memory first = _generateRandomBytes((t & 0xff | 1), t); - bytes memory expectedResult = first; - for (uint256 i; i < inputs.length; ++i) { - expectedResult = bytes.concat(expectedResult, inputs[i]); - } - DynamicBufferLib.DynamicBuffer memory buffer; - buffer.p(first); - uint256 location = _bufferLocation(buffer); - for (uint256 i; i < inputs.length; ++i) { - buffer.p(inputs[i]); - assertEq(_bufferLocation(buffer), location); - _checkMemory(buffer.data); - } - assertEq(buffer.data, expectedResult); - sharedLocation = _bufferLocation(buffer); - } - - if ((t >> 129) & 1 == 0) { - if ((t >> 16) & 7 == 0) _misalignFreeMemoryPointer(); - DynamicBufferLib.DynamicBuffer memory buffer; - if ((t >> 160) & 3 == 0) _incrementFreeMemoryPointer(); - if ((t >> 130) & 1 == 0 && sharedLocation != 0) { - /// @solidity memory-safe-assembly - assembly { - mstore(buffer, sharedLocation) - } - buffer.clear(); - } - if ((t >> 162) & 3 == 0) _incrementFreeMemoryPointer(); - if ((t >> 32) & 3 == 0) { - buffer.reserve((t >> 128) % 1024); - } - - unchecked { - uint256 expectedLength; - uint256 start; - if (t & 1 == 0) { - if (inputs.length > 0) { - expectedLength = inputs[0].length; - buffer.data = inputs[0]; - start = 1; - } - } - for (uint256 i = start; i < inputs.length; ++i) { - expectedLength += inputs[i].length; - // Manually store the t in the next free memory word, - // and then check if p will corrupt it - // (in the case of insufficient memory allocation). - uint256 corruptCheckSlot; - /// @solidity memory-safe-assembly - assembly { - corruptCheckSlot := mload(0x40) - mstore(corruptCheckSlot, t) - mstore(0x40, add(corruptCheckSlot, 0x20)) - } - buffer.p(inputs[i]); - if ((t >> 48) & 7 == 0 && expectedLength != 0) { - buffer.reserve((t >> 160) % (expectedLength * 2)); - } - assertEq(buffer.data.length, expectedLength); - _checkMemory(buffer.data); - bool isCorrupted; - /// @solidity memory-safe-assembly - assembly { - isCorrupted := iszero(eq(t, mload(corruptCheckSlot))) - } - assertFalse(isCorrupted); - } - } - - bytes memory expectedResult; - unchecked { - for (uint256 i; i < inputs.length; ++i) { - expectedResult = bytes.concat(expectedResult, inputs[i]); - } - } - - assertEq(keccak256(buffer.data), keccak256(expectedResult)); - } - } - - function testJoinWithConcat() public { - bytes memory expectedResult; - (bytes[] memory chunks, bytes32 joinedHash) = _getChunks(); - unchecked { - for (uint256 i; i < chunks.length; ++i) { - expectedResult = bytes.concat(expectedResult, chunks[i]); - } - } - assertEq(keccak256(expectedResult), joinedHash); - } - - function testJoinWithDynamicBuffer() public { - DynamicBufferLib.DynamicBuffer memory buffer; - (bytes[] memory chunks, bytes32 joinedHash) = _getChunks(); - unchecked { - for (uint256 i; i < chunks.length; ++i) { - buffer.p(chunks[i]); - } - } - assertEq(keccak256(buffer.data), joinedHash); - } - - function testDynamicBufferChaining() public { - DynamicBufferLib.DynamicBuffer memory bufferA; - DynamicBufferLib.DynamicBuffer memory bufferB; - bufferA = bufferB.p("0", "1"); - _checkSamePointers(bufferA, bufferB); - bufferA = bufferB.p("0", "1", "2"); - _checkSamePointers(bufferA, bufferB); - bufferA = bufferB.p("0", "1", "2", "3"); - _checkSamePointers(bufferA, bufferB); - bufferA = bufferB.p("0", "1", "2", "3", "4"); - _checkSamePointers(bufferA, bufferB); - bufferA = bufferB.p("0", "1", "2", "3", "4", "5"); - _checkSamePointers(bufferA, bufferB); - bufferA = bufferB.p("0", "1", "2", "3", "4", "5", "6"); - _checkSamePointers(bufferA, bufferB); - assertEq(bufferA.data, "010120123012340123450123456"); - assertEq(bufferB.data, "010120123012340123450123456"); - } - - function _checkSamePointers( - DynamicBufferLib.DynamicBuffer memory a, - DynamicBufferLib.DynamicBuffer memory b - ) internal { - bool isSamePointer; - assembly { - isSamePointer := eq(a, b) - } - assertTrue(isSamePointer); - } - - function _getChunks() internal pure returns (bytes[] memory chunks, bytes32 joinedHash) { - chunks = new bytes[](20); - chunks[0] = bytes( - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." - ); - chunks[1] = bytes("Vitae suscipit tellus mauris a diam maecenas sed enim ut."); - chunks[2] = bytes("Nisl nisi scelerisque eu ultrices vitae auctor eu augue."); - chunks[3] = bytes("Et pharetra pharetra massa massa ultricies mi quis."); - chunks[4] = bytes("Ullamcorper malesuada proin libero nunc."); - chunks[5] = bytes("Tempus imperdiet nulla malesuada pellentesque."); - chunks[6] = bytes("Nunc congue nisi vitae suscipit tellus mauris."); - chunks[7] = bytes("Eu augue ut lectus arcu."); - chunks[8] = bytes("Natoque penatibus et magnis dis parturient montes nascetur."); - chunks[9] = bytes("Convallis posuere morbi leo urna."); - - chunks[15] = bytes("Hehe"); - - joinedHash = 0x166b0e99fea53034ed188896344996efc141b922127f90922905e478cb26b312; - } - - function _boundInputs(bytes[] memory inputs) internal pure { - // Limit the total number of inputs. - /// @solidity memory-safe-assembly - assembly { - if gt(mload(inputs), 16) { mstore(inputs, 16) } - } - unchecked { - // Limit the lengths of the inputs. - for (uint256 i; i < inputs.length; ++i) { - bytes memory x = inputs[i]; - /// @solidity memory-safe-assembly - assembly { - if gt(mload(x), 128) { mstore(x, 128) } - } - } - } - } -} diff --git a/lib/solady/test/ECDSA.t.sol b/lib/solady/test/ECDSA.t.sol deleted file mode 100644 index 3f6307f..0000000 --- a/lib/solady/test/ECDSA.t.sol +++ /dev/null @@ -1,452 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {ECDSA} from "../src/utils/ECDSA.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract ECDSATest is SoladyTest { - using ECDSA for bytes32; - using ECDSA for bytes; - - bytes32 constant TEST_MESSAGE = - 0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d; - - bytes32 constant WRONG_MESSAGE = - 0x2d0828dd7c97cff316356da3c16c68ba2316886a0e05ebafb8291939310d51a3; - - address constant SIGNER = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; - - address constant V0_SIGNER = 0x2cc1166f6212628A0deEf2B33BEFB2187D35b86c; - - address constant V1_SIGNER = 0x1E318623aB09Fe6de3C9b8672098464Aeda9100E; - - function testTryRecoverWithInvalidShortSignatureReturnsZero() public { - bytes memory signature = hex"1234"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithInvalidLongSignatureReturnsZero() public { - bytes memory signature = - hex"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithValidSignature() public { - bytes memory signature = - hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - assertTrue(this.tryRecover(TEST_MESSAGE.toEthSignedMessageHash(), signature) == SIGNER); - } - - function testTryRecoverWithWrongSigner() public { - bytes memory signature = - hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - assertTrue(this.tryRecover(WRONG_MESSAGE.toEthSignedMessageHash(), signature) != SIGNER); - } - - function testTryRecoverWithInvalidSignature() public { - bytes memory signature = - hex"332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; - assertTrue(this.tryRecover(TEST_MESSAGE.toEthSignedMessageHash(), signature) != SIGNER); - } - - function testTryRecoverWithV0SignatureWithVersion00ReturnsZero() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithV0SignatureWithVersion27() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be8921b"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == V0_SIGNER); - } - - function testTryRecoverWithV0SignatureWithWrongVersionReturnsZero() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89202"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithV0SignatureWithShortEIP2098Format() public { - bytes32 r = 0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f; - bytes32 vs = 0x3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892; - assertTrue(this.tryRecover(TEST_MESSAGE, r, vs) == V0_SIGNER); - } - - function testTryRecoverWithV0SignatureWithShortEIP2098FormatAsCalldata() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == V0_SIGNER); - } - - function testTryRecoverWithV1SignatureWithVersion01ReturnsZero() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithV1SignatureWithVersion28() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == V1_SIGNER); - } - - function testTryRecoverWithV1SignatureWithWrongVersionReturnsZero() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e002"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == address(0)); - } - - function testTryRecoverWithV1SignatureWithShortEIP2098Format() public { - bytes32 r = 0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff; - bytes32 vs = 0xc8e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0; - assertTrue(this.tryRecover(TEST_MESSAGE, r, vs) == V1_SIGNER); - } - - function testTryRecoverWithV1SignatureWithShortEIP2098FormatAsCalldata() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feffc8e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0"; - assertTrue(this.tryRecover(TEST_MESSAGE, signature) == V1_SIGNER); - } - - function testRecoverWithInvalidShortSignatureReturnsZero() public { - bytes memory signature = hex"1234"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithInvalidLongSignatureReverts() public { - bytes memory signature = - hex"01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithValidSignature() public { - bytes memory signature = - hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - address recovered = this.recover(TEST_MESSAGE.toEthSignedMessageHash(), signature); - assertTrue(recovered == SIGNER); - } - - function testRecoverWithWrongSigner() public { - bytes memory signature = - hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - assertTrue(this.recover(WRONG_MESSAGE.toEthSignedMessageHash(), signature) != SIGNER); - } - - function testRecoverWithInvalidSignatureReverts() public { - bytes memory signature = - hex"332ce75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE.toEthSignedMessageHash(), signature); - } - - function testRecoverWithV0SignatureWithVersion00Reverts() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89200"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithV0SignatureWithVersion27() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be8921b"; - assertTrue(this.recover(TEST_MESSAGE, signature) == V0_SIGNER); - } - - function testRecoverWithV0SignatureWithWrongVersionReverts() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be89202"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithV0SignatureWithShortEIP2098Format() public { - bytes32 r = 0x5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f; - bytes32 vs = 0x3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892; - assertTrue(this.recover(TEST_MESSAGE, r, vs) == V0_SIGNER); - } - - function testRecoverWithV0SignatureWithShortEIP2098FormatAsCalldata() public { - bytes memory signature = - hex"5d99b6f7f6d1f73d1a26497f2b1c89b24c0993913f86e9a2d02cd69887d9c94f3c880358579d811b21dd1b7fd9bb01c1d81d10e69f0384e675c32b39643be892"; - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithV1SignatureWithVersion01Reverts() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e001"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithV1SignatureWithVersion28() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e01c"; - assertTrue(this.recover(TEST_MESSAGE, signature) == V1_SIGNER); - } - - function testRecoverWithV1SignatureWithWrongVersionReverts() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff48e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e002"; - vm.expectRevert(ECDSA.InvalidSignature.selector); - this.recover(TEST_MESSAGE, signature); - } - - function testRecoverWithV1SignatureWithShortEIP2098Format() public { - bytes32 r = 0x331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feff; - bytes32 vs = 0xc8e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0; - assertTrue(this.recover(TEST_MESSAGE, r, vs) == V1_SIGNER); - } - - function testRecoverWithV1SignatureWithShortEIP2098FormatAsCalldata() public { - bytes memory signature = - hex"331fe75a821c982f9127538858900d87d3ec1f9f737338ad67cad133fa48feffc8e6fa0c18abc62e42820f05943e47af3e9fbe306ce74d64094bdf1691ee53e0"; - this.recover(TEST_MESSAGE, signature); - } - - struct _CheckSignatureTestTemps { - bytes argsSignature; - bytes encodedCalldataArgs; - address signer; - bool expected; - bool[2] success; - bytes[2] result; - bytes4 s; - address recovered; - } - - function _checkSignature( - address signer, - bytes32 digest, - uint8 v, - bytes32 r, - bytes32 s, - bool expected - ) internal { - _CheckSignatureTestTemps memory t; - t.signer = signer; - t.expected = expected; - - t.argsSignature = "(bytes32,uint8,bytes32,bytes32)"; - t.encodedCalldataArgs = abi.encode(digest, v, r, s); - _checkSignature(t); - - if (v == 27 || v == 28) { - bytes32 vs = bytes32((v == 28 ? 1 << 255 : 0) | uint256(s)); - t.argsSignature = "(bytes32,bytes32,bytes32)"; - t.encodedCalldataArgs = abi.encode(digest, r, vs); - _checkSignature(t); - } - - if (_random() & 1 == 0) { - t.argsSignature = "(bytes32,bytes)"; - t.encodedCalldataArgs = abi.encode(digest, abi.encodePacked(r, s, v)); - _checkSignature(t); - } - } - - function _checkSignature(_CheckSignatureTestTemps memory t) internal { - t.s = bytes4(keccak256(abi.encodePacked("tryRecover", t.argsSignature))); - (t.success[0], t.result[0]) = - address(this).call(abi.encodePacked(t.s, t.encodedCalldataArgs)); - t.recovered = t.success[0] ? abi.decode(t.result[0], (address)) : address(0); - assertEq(t.recovered == t.signer, t.expected); - - t.s = bytes4(keccak256(abi.encodePacked("tryRecoverBrutalized", t.argsSignature))); - (t.success[1], t.result[1]) = - address(this).call(abi.encodePacked(t.s, t.encodedCalldataArgs)); - t.recovered = t.success[1] ? abi.decode(t.result[1], (address)) : address(0); - assertEq(t.recovered == t.signer, t.expected); - - t.s = bytes4(keccak256(abi.encodePacked("recover", t.argsSignature))); - (t.success[0], t.result[0]) = - address(this).call(abi.encodePacked(t.s, t.encodedCalldataArgs)); - - t.s = bytes4(keccak256(abi.encodePacked("recoverBrutalized", t.argsSignature))); - (t.success[1], t.result[1]) = - address(this).call(abi.encodePacked(t.s, t.encodedCalldataArgs)); - - assertEq(t.success[0], t.success[1]); - assertEq(t.result[0], t.result[1]); - - if (t.success[0]) { - t.recovered = abi.decode(t.result[0], (address)); - assertEq(t.recovered == t.signer, t.expected); - } - } - - function testRecoverAndTryRecover(bytes32 digest) public { - (address signer, uint256 privateKey) = _randomSigner(); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - if (_random() & 7 == 0) { - _checkSignature(signer, digest, v, r, s, true); - } - - uint8 vc = v ^ uint8(_random() & 0xff); - bytes32 rc = bytes32(uint256(r) ^ _random()); - bytes32 sc = bytes32(uint256(s) ^ _random()); - bool anyCorrupted = vc != v || rc != r || sc != s; - _checkSignature(signer, digest, vc, rc, sc, !anyCorrupted); - } - - function testBytes32ToEthSignedMessageHash() public { - assertEq( - TEST_MESSAGE.toEthSignedMessageHash(), - bytes32(0x7d768af957ef8cbf6219a37e743d5546d911dae3e46449d8a5810522db2ef65e) - ); - } - - function testBytesToEthSignedMessageHashShort() public { - bytes memory message = hex"61626364"; - assertEq( - message.toEthSignedMessageHash(), - bytes32(0xefd0b51a9c4e5f3449f4eeacb195bf48659fbc00d2f4001bf4c088ba0779fb33) - ); - } - - function testBytesToEthSignedMessageHashEmpty() public { - bytes memory message = hex""; - assertEq( - message.toEthSignedMessageHash(), - bytes32(0x5f35dce98ba4fba25530a026ed80b2cecdaa31091ba4958b99b52ea1d068adad) - ); - } - - function testBytesToEthSignedMessageHashLong() public { - bytes memory message = - hex"4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a3031323334353637383921402324255e262a28292d3d5b5d7b7d"; - assertEq( - message.toEthSignedMessageHash(), - bytes32(0xa46dbedd405cff161b6e80c17c8567597621d9f4c087204201097cb34448e71b) - ); - } - - function testBytesToEthSignedMessageHash() public { - _testBytesToEthSignedMessageHash(999999); - _testBytesToEthSignedMessageHash(135790); - _testBytesToEthSignedMessageHash(99999); - _testBytesToEthSignedMessageHash(88888); - _testBytesToEthSignedMessageHash(3210); - _testBytesToEthSignedMessageHash(111); - _testBytesToEthSignedMessageHash(22); - _testBytesToEthSignedMessageHash(1); - _testBytesToEthSignedMessageHash(0); - } - - function testBytesToEthSignedMessageHashExceedsMaxLengthReverts() public { - vm.expectRevert(); - _testBytesToEthSignedMessageHash(999999 + 1); - } - - function _testBytesToEthSignedMessageHash(uint256 n) internal brutalizeMemory { - bytes memory message; - /// @solidity memory-safe-assembly - assembly { - message := mload(0x40) - mstore(message, n) - mstore(0x40, add(add(message, 0x20), n)) - } - assertEq( - message.toEthSignedMessageHash(), - keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n", LibString.toString(n), message) - ) - ); - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, message) - } - } - - function tryRecover(bytes32 hash, bytes calldata signature) external returns (address result) { - result = ECDSA.tryRecoverCalldata(hash, signature); - assertEq(ECDSA.tryRecover(hash, signature), result); - } - - function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - external - view - returns (address) - { - return ECDSA.tryRecover(hash, v, r, s); - } - - function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) external view returns (address) { - return ECDSA.tryRecover(hash, r, vs); - } - - function tryRecoverBrutalized(bytes32 hash, bytes calldata signature) - external - brutalizeMemory - returns (address result) - { - result = ECDSA.tryRecoverCalldata(hash, signature); - assertEq(ECDSA.tryRecover(hash, signature), result); - } - - function tryRecoverBrutalized(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - external - view - brutalizeMemory - returns (address) - { - return ECDSA.tryRecover(hash, v, r, s); - } - - function tryRecoverBrutalized(bytes32 hash, bytes32 r, bytes32 vs) - external - view - brutalizeMemory - returns (address) - { - return ECDSA.tryRecover(hash, r, vs); - } - - function recover(bytes32 hash, bytes calldata signature) external returns (address result) { - result = ECDSA.recoverCalldata(hash, signature); - assertEq(ECDSA.recover(hash, signature), result); - } - - function recover(bytes32 hash, bytes32 r, bytes32 vs) external view returns (address) { - return ECDSA.recover(hash, r, vs); - } - - function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) external view returns (address) { - return ECDSA.recover(hash, v, r, s); - } - - function recoverBrutalized(bytes32 hash, bytes calldata signature) - external - brutalizeMemory - returns (address result) - { - result = ECDSA.recoverCalldata(hash, signature); - assertEq(ECDSA.recover(hash, signature), result); - } - - function recoverBrutalized(bytes32 hash, bytes32 r, bytes32 vs) - external - view - brutalizeMemory - returns (address) - { - return ECDSA.recover(hash, r, vs); - } - - function recoverBrutalized(bytes32 hash, uint8 v, bytes32 r, bytes32 s) - external - view - brutalizeMemory - returns (address) - { - return ECDSA.recover(hash, v, r, s); - } - - function testEmptyCalldataHelpers() public { - assertFalse(ECDSA.tryRecover(bytes32(0), ECDSA.emptySignature()) == address(1)); - } -} diff --git a/lib/solady/test/EIP712.t.sol b/lib/solady/test/EIP712.t.sol deleted file mode 100644 index d0f69af..0000000 --- a/lib/solady/test/EIP712.t.sol +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MockEIP712} from "./utils/mocks/MockEIP712.sol"; -import {MockEIP712Dynamic} from "./utils/mocks/MockEIP712Dynamic.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; - -contract EIP712Test is SoladyTest { - MockEIP712 mock; - MockEIP712 mockClone; - MockEIP712Dynamic mockDynamic; - MockEIP712Dynamic mockDynamicClone; - - function setUp() public { - mock = new MockEIP712(); - mockClone = MockEIP712(LibClone.clone(address(mock))); - mockDynamic = new MockEIP712Dynamic("Milady", "1"); - mockDynamicClone = MockEIP712Dynamic(LibClone.clone(address(mockDynamic))); - } - - function testHashTypedData() public { - _testHashTypedDataOnClone(mock); - } - - function testHashTypedDataOnClone() public { - _testHashTypedDataOnClone(mockClone); - } - - function testHashTypedDataOnDynamic() public { - _testHashTypedDataOnClone(MockEIP712(address(mockDynamic))); - } - - function testHashTypedDataOnCloneDynamic() public { - _testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone))); - } - - function testHashTypedDataWithChaindIdChange() public { - _testHashTypedDataOnClone(mock); - vm.chainId(32123); - _testHashTypedDataOnClone(mock); - } - - function testHashTypedDataOnCloneWithChaindIdChange() public { - _testHashTypedDataOnClone(mockClone); - vm.chainId(32123); - _testHashTypedDataOnClone(mockClone); - } - - function testHashTypedDataOnDynamicWithChaindIdChange() public { - _testHashTypedDataOnClone(MockEIP712(address(mockDynamic))); - vm.chainId(32123); - _testHashTypedDataOnClone(MockEIP712(address(mockDynamic))); - } - - function testHashTypedDataOnCloneDynamicWithChaindIdChange() public { - _testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone))); - vm.chainId(32123); - _testHashTypedDataOnClone(MockEIP712(address(mockDynamicClone))); - } - - function _testHashTypedDataOnClone(MockEIP712 mockToTest) internal { - (address signer, uint256 privateKey) = _randomSigner(); - - (address to,) = _randomSigner(); - - string memory message = "Hello Milady!"; - - bytes32 structHash = - keccak256(abi.encode("Message(address to,string message)", to, message)); - bytes32 expectedDigest = - keccak256(abi.encodePacked("\x19\x01", mockToTest.DOMAIN_SEPARATOR(), structHash)); - - assertEq(mockToTest.hashTypedData(structHash), expectedDigest); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, expectedDigest); - - address recoveredAddress = ecrecover(expectedDigest, v, r, s); - - assertEq(recoveredAddress, signer); - } - - function testDomainSeparator() public { - _testDomainSeparator(mock); - } - - function testDomainSeparatorOnClone() public { - _testDomainSeparator(mockClone); - } - - function testDomainSeparatorWithChainIdChange() public { - _testDomainSeparator(mock); - vm.chainId(32123); - _testDomainSeparator(mock); - } - - function testDomainSeparatorOnCloneWithChainIdChange() public { - _testDomainSeparator(mockClone); - vm.chainId(32123); - _testDomainSeparator(mockClone); - } - - function testDomainSeparatorOnDynamicWithChainIdChange() public { - _testDomainSeparator(MockEIP712(address(mockDynamic))); - vm.chainId(32123); - _testDomainSeparator(MockEIP712(address(mockDynamic))); - mockDynamic.setDomainNameAndVersion("Remilio", "2"); - _testDomainSeparator(MockEIP712(address(mockDynamic)), "Remilio", "2"); - } - - function testDomainSeparatorOnCloneDynamicWithChainIdChange() public { - mockDynamicClone.setDomainNameAndVersion("Milady", "1"); - _testDomainSeparator(MockEIP712(address(mockDynamicClone))); - vm.chainId(32123); - _testDomainSeparator(MockEIP712(address(mockDynamicClone))); - mockDynamicClone.setDomainNameAndVersion("Remilio", "2"); - _testDomainSeparator(MockEIP712(address(mockDynamicClone)), "Remilio", "2"); - } - - function _testDomainSeparator(MockEIP712 mockToTest, bytes memory name, bytes memory version) - internal - { - bytes32 expectedDomainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(name), - keccak256(version), - block.chainid, - address(mockToTest) - ) - ); - - assertEq(mockToTest.DOMAIN_SEPARATOR(), expectedDomainSeparator); - } - - function _testDomainSeparator(MockEIP712 mockToTest) internal { - _testDomainSeparator(mockToTest, "Milady", "1"); - } - - function testEIP5267() public { - _testEIP5267(mock); - _testEIP5267(mockClone); - vm.chainId(32123); - _testEIP5267(mock); - _testEIP5267(mockClone); - } - - struct _testEIP5267Variables { - bytes1 fields; - string name; - string version; - uint256 chainId; - address verifyingContract; - bytes32 salt; - uint256[] extensions; - } - - function _testEIP5267(MockEIP712 mockToTest) public { - _testEIP5267Variables memory t; - (t.fields, t.name, t.version, t.chainId, t.verifyingContract, t.salt, t.extensions) = - mockToTest.eip712Domain(); - - assertEq(t.fields, hex"0f"); - assertEq(t.name, "Milady"); - assertEq(t.version, "1"); - assertEq(t.chainId, block.chainid); - assertEq(t.verifyingContract, address(mockToTest)); - assertEq(t.salt, bytes32(0)); - assertEq(t.extensions, new uint256[](0)); - } -} diff --git a/lib/solady/test/ERC1155.t.sol b/lib/solady/test/ERC1155.t.sol deleted file mode 100644 index f4e9434..0000000 --- a/lib/solady/test/ERC1155.t.sol +++ /dev/null @@ -1,1234 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; - -import {ERC1155, MockERC1155} from "./utils/mocks/MockERC1155.sol"; - -abstract contract ERC1155TokenReceiver { - function onERC1155Received(address, address, uint256, uint256, bytes calldata) - external - virtual - returns (bytes4) - { - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external virtual returns (bytes4) { - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } -} - -contract ERC1155Recipient is ERC1155TokenReceiver { - address public operator; - address public from; - uint256 public id; - uint256 public amount; - bytes public mintData; - - function onERC1155Received( - address _operator, - address _from, - uint256 _id, - uint256 _amount, - bytes calldata _data - ) public override returns (bytes4) { - operator = _operator; - from = _from; - id = _id; - amount = _amount; - mintData = _data; - - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - address public batchOperator; - address public batchFrom; - uint256[] internal _batchIds; - uint256[] internal _batchAmounts; - bytes public batchData; - - function batchIds() external view returns (uint256[] memory) { - return _batchIds; - } - - function batchAmounts() external view returns (uint256[] memory) { - return _batchAmounts; - } - - function onERC1155BatchReceived( - address _operator, - address _from, - uint256[] calldata _ids, - uint256[] calldata _amounts, - bytes calldata _data - ) external override returns (bytes4) { - batchOperator = _operator; - batchFrom = _from; - _batchIds = _ids; - _batchAmounts = _amounts; - batchData = _data; - - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } -} - -contract RevertingERC1155Recipient is ERC1155TokenReceiver { - function onERC1155Received(address, address, uint256, uint256, bytes calldata) - public - pure - override - returns (bytes4) - { - revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector))); - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector))); - } -} - -contract WrongReturnDataERC1155Recipient is ERC1155TokenReceiver { - function onERC1155Received(address, address, uint256, uint256, bytes calldata) - public - pure - override - returns (bytes4) - { - return 0xCAFEBEEF; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - return 0xCAFEBEEF; - } -} - -contract NonERC1155Recipient {} - -contract MockERC1155WithHooks is MockERC1155 { - uint256 public beforeCounter; - uint256 public afterCounter; - - function _useBeforeTokenTransfer() internal view virtual override returns (bool) { - return true; - } - - function _useAfterTokenTransfer() internal view virtual override returns (bool) { - return true; - } - - function _beforeTokenTransfer( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) internal virtual override { - beforeCounter++; - } - - function _afterTokenTransfer(address, address, uint256[] memory, uint256[] memory, bytes memory) - internal - virtual - override - { - afterCounter++; - } -} - -contract ERC1155HooksTest is SoladyTest, ERC1155TokenReceiver { - uint256 public expectedBeforeCounter; - uint256 public expectedAfterCounter; - - function _checkCounters() internal view { - require( - expectedBeforeCounter == MockERC1155WithHooks(msg.sender).beforeCounter(), - "Before counter mismatch." - ); - require( - expectedAfterCounter == MockERC1155WithHooks(msg.sender).afterCounter(), - "After counter mismatch." - ); - } - - function onERC1155Received(address, address, uint256, uint256, bytes calldata) - external - virtual - override - returns (bytes4) - { - _checkCounters(); - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external virtual override returns (bytes4) { - _checkCounters(); - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } - - function _testHooks(MockERC1155WithHooks token) internal { - address from = _randomNonZeroAddress(); - expectedBeforeCounter++; - expectedAfterCounter++; - token.mint(address(this), 1, 1000, ""); - - expectedBeforeCounter++; - expectedAfterCounter++; - token.safeTransferFrom(address(this), from, 1, 1000, ""); - - vm.prank(from); - expectedBeforeCounter++; - expectedAfterCounter++; - token.safeTransferFrom(from, address(this), 1, 1, ""); - - vm.prank(from); - expectedBeforeCounter++; - expectedAfterCounter++; - token.directSafeTransferFrom(from, address(this), 1, 1, ""); - - uint256[] memory ids = new uint256[](1); - uint256[] memory amounts = new uint256[](1); - ids[0] = 1; - amounts[0] = 1; - - vm.prank(from); - expectedBeforeCounter++; - expectedAfterCounter++; - token.safeBatchTransferFrom(from, address(this), ids, amounts, ""); - - vm.prank(from); - expectedBeforeCounter++; - expectedAfterCounter++; - token.directSafeBatchTransferFrom(from, address(this), ids, amounts, ""); - } - - function testERC1155Hooks() public { - MockERC1155WithHooks token = new MockERC1155WithHooks(); - - for (uint256 i; i < 32; ++i) { - _testHooks(token); - } - } -} - -contract ERC1155Test is SoladyTest, ERC1155TokenReceiver { - MockERC1155 token; - - event TransferSingle( - address indexed operator, - address indexed from, - address indexed to, - uint256 id, - uint256 amount - ); - - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] amounts - ); - - event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved); - - mapping(address => mapping(uint256 => uint256)) public userMintAmounts; - mapping(address => mapping(uint256 => uint256)) public userTransferOrBurnAmounts; - - struct _TestTemps { - address from; - address to; - uint256 n; - uint256[] ids; - uint256[] mintAmounts; - uint256[] transferAmounts; - uint256[] burnAmounts; - uint256 id; - uint256 mintAmount; - uint256 transferAmount; - uint256 burnAmount; - bytes mintData; - bytes burnData; - bytes transferData; - } - - function _randomBytes() internal returns (bytes memory b) { - uint256 r = _random(); - /// @solidity memory-safe-assembly - assembly { - b := mload(0x40) - mstore(b, mod(r, 65)) - let t := add(b, 0x20) - mstore(t, r) - mstore(add(b, 0x40), keccak256(t, 0x20)) - mstore(0x40, add(b, 0x60)) - } - } - - function _randomArray(uint256 n) internal returns (uint256[] memory a) { - /// @solidity memory-safe-assembly - assembly { - a := mload(0x40) - mstore(a, n) - mstore(0x40, add(add(a, 0x20), shl(5, n))) - } - unchecked { - for (uint256 i; i != n; ++i) { - a[i] = _random(); - } - } - } - - function _testTemps() internal returns (_TestTemps memory t) { - unchecked { - t.from = _randomNonZeroAddress(); - do { - t.to = _randomNonZeroAddress(); - } while (t.from == t.to); - uint256 n = _random() % 4; - t.n = n; - t.ids = _randomArray(n); - t.mintAmounts = _randomArray(n); - t.transferAmounts = _randomArray(n); - t.burnAmounts = _randomArray(n); - t.mintData = _randomBytes(); - t.burnData = _randomBytes(); - t.transferData = _randomBytes(); - t.id = _random(); - t.transferAmount = _random(); - t.burnAmount = _random(); - t.mintAmount = _random(); - } - } - - function _safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal { - if (_random() % 2 == 0) { - token.safeTransferFrom(from, to, id, amount, data); - } else { - token.directSafeTransferFrom(from, to, id, amount, data); - } - } - - function _safeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal { - if (_random() % 2 == 0) { - token.safeBatchTransferFrom(from, to, ids, amounts, data); - } else { - token.directSafeBatchTransferFrom(from, to, ids, amounts, data); - } - } - - function _setApprovalForAll(address operator, bool approved) internal { - if (_random() % 2 == 0) { - token.setApprovalForAll(operator, approved); - } else { - token.directSetApprovalForAll(operator, approved); - } - } - - function _expectMintEvent(address to, uint256 id, uint256 amount) internal { - _expectMintEvent(address(this), to, id, amount); - } - - function _expectMintEvent(address operator, address to, uint256 id, uint256 amount) internal { - _expectTransferEvent(operator, address(0), to, id, amount); - } - - function _expectBurnEvent(address from, uint256 id, uint256 amount) internal { - _expectBurnEvent(address(this), from, id, amount); - } - - function _expectBurnEvent(address operator, address from, uint256 id, uint256 amount) - internal - { - _expectTransferEvent(operator, from, address(0), id, amount); - } - - function _expectTransferEvent(address from, address to, uint256 id, uint256 amount) internal { - _expectTransferEvent(address(this), from, to, id, amount); - } - - function _expectTransferEvent( - address operator, - address from, - address to, - uint256 id, - uint256 amount - ) internal { - vm.expectEmit(true, true, true, true); - emit TransferSingle(operator, from, to, id, amount); - } - - function _expectMintEvent(address to, uint256[] memory ids, uint256[] memory amounts) - internal - { - _expectMintEvent(address(this), to, ids, amounts); - } - - function _expectMintEvent( - address operator, - address to, - uint256[] memory ids, - uint256[] memory amounts - ) internal { - _expectTransferEvent(operator, address(0), to, ids, amounts); - } - - function _expectBurnEvent(address from, uint256[] memory ids, uint256[] memory amounts) - internal - { - _expectBurnEvent(address(this), from, ids, amounts); - } - - function _expectBurnEvent( - address operator, - address from, - uint256[] memory ids, - uint256[] memory amounts - ) internal { - _expectTransferEvent(operator, from, address(0), ids, amounts); - } - - function _expectTransferEvent( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts - ) internal { - _expectTransferEvent(address(this), from, to, ids, amounts); - } - - function _expectTransferEvent( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts - ) internal { - vm.expectEmit(true, true, true, true); - emit TransferBatch(operator, from, to, ids, amounts); - } - - function _expectApprovalForAllEvent(address operator, bool isApproved) internal { - _expectApprovalForAllEvent(address(this), operator, isApproved); - } - - function _expectApprovalForAllEvent(address owner, address operator, bool isApproved) - internal - { - vm.expectEmit(true, true, true, true); - emit ApprovalForAll(owner, operator, isApproved); - } - - function setUp() public { - token = new MockERC1155(); - } - - function testDirectSetApprovalForAll(address by, address operator, bool approved) public { - _expectApprovalForAllEvent(by, operator, approved); - vm.prank(by); - token.directSetApprovalForAll(operator, approved); - } - - function testAuthorizedEquivalence(address by, address from, bool isApprovedAccount) public { - bool a = true; - bool b = true; - /// @solidity memory-safe-assembly - assembly { - if by { if iszero(eq(by, from)) { a := isApprovedAccount } } - if iszero(or(iszero(by), eq(by, from))) { b := isApprovedAccount } - } - assertEq(a, b); - } - - function testMintToEOA(uint256) public { - _TestTemps memory t = _testTemps(); - - _expectMintEvent(t.to, t.id, t.mintAmount); - token.mint(t.to, t.id, t.mintAmount, t.mintData); - - assertEq(token.balanceOf(t.to, t.id), t.mintAmount); - } - - function testMintToERC1155Recipient(uint256) public { - _TestTemps memory t = _testTemps(); - - ERC1155Recipient to = new ERC1155Recipient(); - - _expectMintEvent(address(to), t.id, t.mintAmount); - token.mint(address(to), t.id, t.mintAmount, t.mintData); - - assertEq(token.balanceOf(address(to), t.id), t.mintAmount); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), t.id); - assertEq(to.mintData(), t.mintData); - } - - function testBatchMintToEOA(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.to][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[t.to][id] += mintAmount; - } - - _expectMintEvent(t.to, t.ids, t.mintAmounts); - token.batchMint(t.to, t.ids, t.mintAmounts, t.mintData); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - assertEq(token.balanceOf(t.to, id), userMintAmounts[t.to][id]); - } - } - - function testBatchMintToERC1155Recipient(uint256) public { - _TestTemps memory t = _testTemps(); - - ERC1155Recipient to = new ERC1155Recipient(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - _expectMintEvent(address(to), t.ids, t.mintAmounts); - token.batchMint(address(to), t.ids, t.mintAmounts, t.mintData); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), address(0)); - assertEq(to.batchIds(), t.ids); - assertEq(to.batchAmounts(), t.mintAmounts); - assertEq(to.batchData(), t.mintData); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - assertEq(token.balanceOf(address(to), id), userMintAmounts[address(to)][id]); - } - } - - function testBurn(uint256) public { - _TestTemps memory t = _testTemps(); - - t.burnAmount = _bound(t.burnAmount, 0, t.mintAmount); - - _expectMintEvent(t.to, t.id, t.mintAmount); - token.mint(t.to, t.id, t.mintAmount, t.mintData); - - if (_random() % 2 == 0) { - _expectBurnEvent(t.to, t.id, t.burnAmount); - token.uncheckedBurn(t.to, t.id, t.burnAmount); - } else if (_random() % 8 == 0) { - vm.expectRevert(ERC1155.NotOwnerNorApproved.selector); - token.burn(t.to, t.id, t.burnAmount); - return; - } else { - vm.prank(t.to); - _setApprovalForAll(address(this), true); - - _expectBurnEvent(t.to, t.id, t.burnAmount); - token.burn(t.to, t.id, t.burnAmount); - } - - assertEq(token.balanceOf(t.to, t.id), t.mintAmount - t.burnAmount); - } - - function testBatchBurn(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.to][id]; - - t.mintAmounts[i] = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - t.burnAmounts[i] = _bound(t.burnAmounts[i], 0, t.mintAmounts[i]); - - userMintAmounts[t.to][id] += t.mintAmounts[i]; - userTransferOrBurnAmounts[t.to][id] += t.burnAmounts[i]; - } - - _expectMintEvent(t.to, t.ids, t.mintAmounts); - token.batchMint(t.to, t.ids, t.mintAmounts, t.mintData); - - if (_random() % 2 == 0) { - _expectBurnEvent(t.to, t.ids, t.burnAmounts); - token.uncheckedBatchBurn(t.to, t.ids, t.burnAmounts); - } else if (_random() % 8 == 0) { - vm.expectRevert(ERC1155.NotOwnerNorApproved.selector); - token.batchBurn(t.to, t.ids, t.burnAmounts); - return; - } else { - vm.prank(t.to); - _setApprovalForAll(address(this), true); - - _expectBurnEvent(t.to, t.ids, t.burnAmounts); - token.batchBurn(t.to, t.ids, t.burnAmounts); - } - - for (uint256 i = 0; i < t.ids.length; i++) { - uint256 id = t.ids[i]; - - assertEq( - token.balanceOf(t.to, id), - userMintAmounts[t.to][id] - userTransferOrBurnAmounts[t.to][id] - ); - } - } - - function testApproveAll(address to, bool approved) public { - _expectApprovalForAllEvent(to, approved); - _setApprovalForAll(to, approved); - assertEq(token.isApprovedForAll(address(this), to), approved); - } - - function testSafeTransferFromToEOA(uint256) public { - _TestTemps memory t = _testTemps(); - - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - _expectMintEvent(t.from, t.id, t.mintAmount); - token.mint(t.from, t.id, t.mintAmount, t.mintData); - - if (_random() % 2 == 0) { - _expectTransferEvent(t.from, t.to, t.id, t.transferAmount); - token.uncheckedSafeTransferFrom(t.from, t.to, t.id, t.transferAmount, t.transferData); - } else if (_random() % 8 == 0) { - vm.expectRevert(ERC1155.NotOwnerNorApproved.selector); - _safeTransferFrom(t.from, t.to, t.id, t.transferAmount, t.transferData); - return; - } else { - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - _expectTransferEvent(t.from, t.to, t.id, t.transferAmount); - _safeTransferFrom(t.from, t.to, t.id, t.transferAmount, t.transferData); - } - - if (t.to == t.from) { - assertEq(token.balanceOf(t.to, t.id), t.mintAmount); - } else { - assertEq(token.balanceOf(t.to, t.id), t.transferAmount); - assertEq(token.balanceOf(t.from, t.id), t.mintAmount - t.transferAmount); - } - } - - function testSafeTransferFromToERC1155Recipient(uint256) public { - _TestTemps memory t = _testTemps(); - ERC1155Recipient to = new ERC1155Recipient(); - - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - _expectMintEvent(t.from, t.id, t.mintAmount); - token.mint(t.from, t.id, t.mintAmount, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - _expectTransferEvent(t.from, address(to), t.id, t.transferAmount); - _safeTransferFrom(t.from, address(to), t.id, t.transferAmount, t.transferData); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), t.from); - assertEq(to.id(), t.id); - assertEq(to.mintData(), t.transferData); - - assertEq(token.balanceOf(address(to), t.id), t.transferAmount); - assertEq(token.balanceOf(t.from, t.id), t.mintAmount - t.transferAmount); - } - - function testSafeTransferFromSelf(uint256) public { - _TestTemps memory t = _testTemps(); - - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - _expectMintEvent(address(this), t.id, t.mintAmount); - token.mint(address(this), t.id, t.mintAmount, t.mintData); - - _expectTransferEvent(address(this), t.to, t.id, t.transferAmount); - _safeTransferFrom(address(this), t.to, t.id, t.transferAmount, t.transferData); - - assertEq(token.balanceOf(t.to, t.id), t.transferAmount); - assertEq(token.balanceOf(address(this), t.id), t.mintAmount - t.transferAmount); - } - - function testSafeBatchTransfer() public { - for (uint256 i; i != 8; ++i) { - testSafeTransferFromToEOA(_random()); - testSafeBatchTransferFromToERC1155Recipient(_random()); - } - } - - function testSafeBatchTransferFromToEOA(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - userTransferOrBurnAmounts[t.from][id] += transferAmount; - } - _expectMintEvent(t.from, t.ids, t.mintAmounts); - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - if (_random() % 2 == 0) { - _expectTransferEvent(t.from, t.to, t.ids, t.transferAmounts); - token.uncheckedSafeBatchTransferFrom( - t.from, t.to, t.ids, t.transferAmounts, t.transferData - ); - } else if (_random() % 8 == 0) { - vm.expectRevert(ERC1155.NotOwnerNorApproved.selector); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - return; - } else { - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - _expectTransferEvent(t.from, t.to, t.ids, t.transferAmounts); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - } - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - assertEq(token.balanceOf(t.to, id), userTransferOrBurnAmounts[t.from][id]); - assertEq( - token.balanceOf(t.from, id), - userMintAmounts[t.from][id] - userTransferOrBurnAmounts[t.from][id] - ); - } - } - - function testSafeBatchTransferFromToERC1155Recipient(uint256) public { - _TestTemps memory t = _testTemps(); - - ERC1155Recipient to = new ERC1155Recipient(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - userTransferOrBurnAmounts[t.from][id] += transferAmount; - } - - _expectMintEvent(t.from, t.ids, t.mintAmounts); - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - _expectTransferEvent(t.from, address(to), t.ids, t.transferAmounts); - _safeBatchTransferFrom(t.from, address(to), t.ids, t.transferAmounts, t.transferData); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), t.from); - assertEq(to.batchIds(), t.ids); - assertEq(to.batchAmounts(), t.transferAmounts); - assertEq(to.batchData(), t.transferData); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - uint256 transferAmount = userTransferOrBurnAmounts[t.from][id]; - - assertEq(token.balanceOf(address(to), id), transferAmount); - assertEq(token.balanceOf(t.from, id), userMintAmounts[t.from][id] - transferAmount); - } - } - - function testBatchBalanceOf(uint256) public { - _TestTemps memory t = _testTemps(); - - address[] memory tos = new address[](t.n); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - address to = _randomNonZeroAddress(); - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; - - tos[i] = to; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - token.mint(to, id, mintAmount, t.mintData); - - userMintAmounts[to][id] += mintAmount; - } - - uint256[] memory balances = token.balanceOfBatch(tos, t.ids); - - for (uint256 i = 0; i != t.n; i++) { - assertEq(balances[i], token.balanceOf(tos[i], t.ids[i])); - } - } - - function testMintToZeroReverts(uint256) public { - vm.expectRevert(ERC1155.TransferToZeroAddress.selector); - token.mint(address(0), _random(), _random(), _randomBytes()); - } - - function testMintToNonERC155RecipientReverts(uint256) public { - address to = address(new NonERC1155Recipient()); - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - token.mint(to, _random(), _random(), _randomBytes()); - } - - function testMintToRevertingERC155RecipientReverts(uint256) public { - address to = address(new RevertingERC1155Recipient()); - vm.expectRevert(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector)); - token.mint(to, _random(), _random(), _randomBytes()); - } - - function testMintToWrongReturnDataERC155RecipientReverts(uint256) public { - address to = address(new WrongReturnDataERC1155Recipient()); - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - token.mint(to, _random(), _random(), _randomBytes()); - } - - function testBurnInsufficientBalanceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - while (t.mintAmount == type(uint256).max) t.mintAmount = _random(); - t.burnAmount = _bound(t.burnAmount, t.mintAmount + 1, type(uint256).max); - - token.mint(t.to, t.id, t.mintAmount, t.mintData); - - vm.prank(t.to); - _setApprovalForAll(address(this), true); - - vm.expectRevert(ERC1155.InsufficientBalance.selector); - token.burn(t.to, t.id, t.burnAmount); - } - - function testSafeTransferFromInsufficientBalanceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - while (t.mintAmount == type(uint256).max) t.mintAmount = _random(); - - t.transferAmount = _bound(t.transferAmount, t.mintAmount + 1, type(uint256).max); - - token.mint(t.from, t.id, t.mintAmount, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - vm.expectRevert(ERC1155.InsufficientBalance.selector); - _safeTransferFrom(t.from, t.to, t.id, t.transferAmount, t.transferData); - } - - function testSafeTransferFromSelfInsufficientBalanceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - while (t.mintAmount == type(uint256).max) t.mintAmount = _random(); - - t.transferAmount = _bound(t.transferAmount, t.mintAmount + 1, type(uint256).max); - - token.mint(address(this), t.id, t.mintAmount, t.mintData); - - vm.expectRevert(ERC1155.InsufficientBalance.selector); - _safeTransferFrom(address(this), t.to, t.id, t.transferAmount, t.transferData); - } - - function testSafeTransferFromToZeroReverts(uint256) public { - _TestTemps memory t = _testTemps(); - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - token.mint(address(this), t.id, t.mintAmount, t.mintData); - - vm.expectRevert(ERC1155.TransferToZeroAddress.selector); - _safeTransferFrom(address(this), address(0), t.id, t.transferAmount, t.transferData); - } - - function testSafeTransferFromToNonERC155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - token.mint(address(this), t.id, t.mintAmount, t.mintData); - t.to = address(new NonERC1155Recipient()); - - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - _safeTransferFrom(address(this), t.to, t.id, t.transferAmount, t.transferData); - } - - function testSafeTransferFromToRevertingERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - token.mint(address(this), t.id, t.mintAmount, t.mintData); - t.to = address(new RevertingERC1155Recipient()); - - vm.expectRevert(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector)); - _safeTransferFrom(address(this), t.to, t.id, t.transferAmount, t.transferData); - } - - function testSafeTransferFromToWrongReturnDataERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - t.transferAmount = _bound(t.transferAmount, 0, t.mintAmount); - - token.mint(address(this), t.id, t.mintAmount, t.mintData); - t.to = address(new WrongReturnDataERC1155Recipient()); - - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - _safeTransferFrom(address(this), t.to, t.id, t.transferAmount, t.transferData); - } - - function testSafeBatchTransferInsufficientBalanceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - while (t.n == 0) t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - if (mintAmount == type(uint256).max) return; - uint256 transferAmount = _bound(t.transferAmounts[i], mintAmount + 1, type(uint256).max); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - } - - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - vm.expectRevert(ERC1155.InsufficientBalance.selector); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - } - - function testSafeBatchTransferFromToZeroReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - } - - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - vm.expectRevert(ERC1155.TransferToZeroAddress.selector); - _safeBatchTransferFrom(t.from, address(0), t.ids, t.transferAmounts, t.transferData); - } - - function testSafeBatchTransferFromToNonERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - } - - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - t.to = address(new NonERC1155Recipient()); - - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - } - - function testSafeBatchTransferFromToRevertingERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - } - - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - t.to = address(new RevertingERC1155Recipient()); - vm.expectRevert(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector)); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - } - - function testSafeBatchTransferFromToWrongReturnDataERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.from][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = _bound(t.transferAmounts[i], 0, mintAmount); - - t.mintAmounts[i] = mintAmount; - t.transferAmounts[i] = transferAmount; - - userMintAmounts[t.from][id] += mintAmount; - } - - token.batchMint(t.from, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.from); - _setApprovalForAll(address(this), true); - - t.to = address(new WrongReturnDataERC1155Recipient()); - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - _safeBatchTransferFrom(t.from, t.to, t.ids, t.transferAmounts, t.transferData); - } - - function testSafeBatchTransferFromWithArrayLengthMismatchReverts(uint256) public { - uint256[] memory ids = new uint256[](_random() % 4); - uint256[] memory mintAmounts = new uint256[](_random() % 4); - - if (ids.length == mintAmounts.length) return; - - address from = address(0xABCD); - - vm.expectRevert(ERC1155.ArrayLengthsMismatch.selector); - token.batchMint(from, ids, mintAmounts, _randomBytes()); - - uint256[] memory transferAmounts = new uint256[](_random() % 4); - if (ids.length == transferAmounts.length) return; - - vm.prank(from); - _setApprovalForAll(address(this), true); - - address to = _randomNonZeroAddress(); - - vm.expectRevert(ERC1155.ArrayLengthsMismatch.selector); - _safeBatchTransferFrom(from, to, ids, transferAmounts, _randomBytes()); - } - - function testBatchMintToZeroReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(0)][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[address(0)][id] += mintAmount; - } - - vm.expectRevert(ERC1155.TransferToZeroAddress.selector); - token.batchMint(address(0), t.ids, t.mintAmounts, t.mintData); - } - - function testBatchMintToNonERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - NonERC1155Recipient to = new NonERC1155Recipient(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - token.batchMint(address(to), t.ids, t.mintAmounts, t.mintData); - } - - function testBatchMintToRevertingERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - RevertingERC1155Recipient to = new RevertingERC1155Recipient(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - vm.expectRevert(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector)); - token.batchMint(address(to), t.ids, t.mintAmounts, t.mintData); - } - - function testBatchMintToWrongReturnDataERC1155RecipientReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - WrongReturnDataERC1155Recipient to = new WrongReturnDataERC1155Recipient(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - - t.mintAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - vm.expectRevert(ERC1155.TransferToNonERC1155ReceiverImplementer.selector); - token.batchMint(address(to), t.ids, t.mintAmounts, t.mintData); - } - - function testBatchMintWithArrayMismatchReverts(uint256) public { - uint256[] memory ids = new uint256[](_random() % 4); - uint256[] memory amounts = new uint256[](_random() % 4); - - if (ids.length == amounts.length) return; - - address to = _randomNonZeroAddress(); - - vm.expectRevert(ERC1155.ArrayLengthsMismatch.selector); - token.batchMint(to, ids, amounts, _randomBytes()); - } - - function testBatchBurnInsufficientBalanceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - while (t.n == 0) t = _testTemps(); - - for (uint256 i = 0; i != t.n; i++) { - uint256 id = t.ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[t.to][id]; - - t.mintAmounts[i] = _bound(t.mintAmounts[i], 0, remainingMintAmountForId); - if (t.mintAmounts[i] == type(uint256).max) return; - t.burnAmounts[i] = _bound(t.burnAmounts[i], t.mintAmounts[i] + 1, type(uint256).max); - - userMintAmounts[t.to][id] += t.mintAmounts[i]; - } - - token.batchMint(t.to, t.ids, t.mintAmounts, t.mintData); - - vm.prank(t.to); - _setApprovalForAll(address(this), true); - - vm.expectRevert(ERC1155.InsufficientBalance.selector); - token.batchBurn(t.to, t.ids, t.burnAmounts); - } - - function testBatchBurnWithArrayLengthMismatchReverts(uint256) public { - _TestTemps memory t = _testTemps(); - - if (t.ids.length == t.burnAmounts.length) t.burnAmounts = _randomArray(t.n + 1); - - vm.expectRevert(ERC1155.ArrayLengthsMismatch.selector); - token.batchBurn(t.to, t.ids, t.burnAmounts); - } - - function testBalanceOfBatchWithArrayMismatchReverts(uint256) public { - address[] memory tos = new address[](_random() % 4); - uint256[] memory ids = new uint256[](_random() % 4); - if (tos.length == ids.length) return; - - vm.expectRevert(ERC1155.ArrayLengthsMismatch.selector); - token.balanceOfBatch(tos, ids); - } -} diff --git a/lib/solady/test/ERC1967Factory.t.sol b/lib/solady/test/ERC1967Factory.t.sol deleted file mode 100644 index a19ea56..0000000 --- a/lib/solady/test/ERC1967Factory.t.sol +++ /dev/null @@ -1,302 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MockImplementation} from "./utils/mocks/MockImplementation.sol"; -import {ERC1967Factory} from "../src/utils/ERC1967Factory.sol"; -import {ERC1967FactoryConstants} from "../src/utils/ERC1967FactoryConstants.sol"; - -contract ERC1967FactoryTest is SoladyTest { - event AdminChanged(address indexed proxy, address indexed admin); - - event Upgraded(address indexed proxy, address indexed implementation); - - event Deployed(address indexed proxy, address indexed implementation, address indexed admin); - - ERC1967Factory factory; - address implementation0; - address implementation1; - - struct _TestTemps { - uint256 key; - uint256 value; - uint256 msgValue; - bytes32 salt; - address predictedProxy; - address proxy; - } - - function _testTemps() internal returns (_TestTemps memory t) { - t.key = _random(); - t.value = _random(); - t.msgValue = _bound(_random(), 0, uint256(type(uint96).max)); - t.salt = bytes32(_random() & uint256(type(uint96).max)); - } - - function setUp() public { - factory = new ERC1967Factory(); - implementation0 = address(new MockImplementation()); - implementation1 = address(new MockImplementation()); - } - - modifier withFactories() { - _; - { - address minedFactoryAddress = 0x0000000000001122334455667788990011223344; - vm.etch(minedFactoryAddress, address(factory).code); - factory = ERC1967Factory(minedFactoryAddress); - } - _; - } - - function testDeploy() public withFactories { - (address admin,) = _randomSigner(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - _checkProxyBytecode(proxy); - - assertEq(factory.adminOf(proxy), admin); - assertTrue(proxy != address(0)); - assertTrue(proxy.code.length > 0); - _checkImplementationSlot(proxy, implementation0); - } - - function testDeployBrutalized(uint256) public withFactories { - (address admin,) = _randomSigner(); - address implementation = implementation0; - bool brutalized; - bool success; - address f = address(factory); - /// @solidity memory-safe-assembly - assembly { - calldatacopy(0x00, 0x00, 0x40) - brutalized := eq(and(mload(0x00), 1), 0) - if brutalized { - // Extremely unlikely that all 96 upper bits will be zero. - admin := or(shl(160, keccak256(0x00, 0x20)), admin) - implementation := or(shl(160, keccak256(0x00, 0x40)), implementation) - } - let m := mload(0x40) - mstore(m, 0x545e7c61) // `deploy(address, address)`. - mstore(add(m, 0x20), implementation) - mstore(add(m, 0x40), admin) - mstore(0x00, 0) - // Basically, we want to demonstrate that Solidity has checks - // to reject dirty upper bits for addresses. - success := call(gas(), f, 0, add(m, 0x1c), 0x44, 0x00, 0x20) - // If the call is successful, there will be a deployment. - if and(success, iszero(mload(0x00))) { revert(0, 0) } - } - assertEq(brutalized, !success); - } - - function testDeployAndCall(uint256) public withFactories { - (address admin,) = _randomSigner(); - _TestTemps memory t = _testTemps(); - - bytes memory data = abi.encodeWithSignature("setValue(uint256,uint256)", t.key, t.value); - vm.deal(admin, type(uint128).max); - vm.prank(admin); - address proxy = factory.deployAndCall{value: t.msgValue}(implementation0, admin, data); - - assertEq(factory.adminOf(proxy), admin); - assertTrue(proxy != address(0)); - assertTrue(proxy.code.length > 0); - _checkImplementationSlot(proxy, implementation0); - assertEq(MockImplementation(proxy).getValue(t.key), t.value); - assertEq(proxy.balance, t.msgValue); - } - - function testDeployDeterministicAndCall(uint256) public withFactories { - (address admin,) = _randomSigner(); - _TestTemps memory t = _testTemps(); - - t.predictedProxy = factory.predictDeterministicAddress(t.salt); - bytes memory data = abi.encodeWithSignature("setValue(uint256,uint256)", t.key, t.value); - vm.deal(admin, type(uint128).max); - vm.prank(admin); - if (_random() % 8 == 0) { - t.salt = keccak256(abi.encode(_random())); - vm.expectRevert(ERC1967Factory.SaltDoesNotStartWithCaller.selector); - t.proxy = factory.deployDeterministicAndCall{value: t.msgValue}( - implementation0, admin, t.salt, data - ); - return; - } else { - vm.expectEmit(true, true, true, true); - emit Deployed(t.predictedProxy, implementation0, admin); - t.proxy = factory.deployDeterministicAndCall{value: t.msgValue}( - implementation0, admin, t.salt, data - ); - assertEq(t.proxy, t.predictedProxy); - } - - assertEq(factory.adminOf(t.proxy), admin); - assertTrue(t.proxy != address(0)); - assertTrue(t.proxy.code.length > 0); - _checkImplementationSlot(t.proxy, implementation0); - assertEq(MockImplementation(t.proxy).getValue(t.key), t.value); - assertEq(t.proxy.balance, t.msgValue); - } - - function testDeployAndCallWithRevert() public withFactories { - (address admin,) = _randomSigner(); - - bytes memory data = abi.encodeWithSignature("fails()"); - vm.expectRevert(MockImplementation.Fail.selector); - factory.deployAndCall(implementation0, admin, data); - } - - function testProxySucceeds() public withFactories { - (address admin,) = _randomSigner(); - uint256 a = 1; - - MockImplementation proxy = MockImplementation(factory.deploy(implementation0, admin)); - - assertEq(proxy.succeeds(a), a); - } - - function testProxyFails() public withFactories { - (address admin,) = _randomSigner(); - - address proxy = factory.deploy(implementation0, admin); - - vm.expectRevert(MockImplementation.Fail.selector); - MockImplementation(proxy).fails(); - } - - function testChangeAdmin() public withFactories { - (address admin, address newAdmin) = _randomAccounts(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectEmit(true, true, true, true, address(factory)); - emit AdminChanged(proxy, newAdmin); - - vm.prank(admin); - factory.changeAdmin(proxy, newAdmin); - - assertEq(factory.adminOf(proxy), newAdmin); - } - - function testChangeAdminUnauthorized() public withFactories { - (address admin, address sussyAccount) = _randomAccounts(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectRevert(ERC1967Factory.Unauthorized.selector); - - vm.prank(sussyAccount); - factory.changeAdmin(proxy, sussyAccount); - } - - function testUpgrade() public withFactories { - (address admin,) = _randomSigner(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectEmit(true, true, true, true, address(factory)); - emit Upgraded(proxy, implementation1); - - vm.prank(admin); - factory.upgrade(proxy, implementation1); - - _checkImplementationSlot(proxy, implementation1); - } - - function testUpgradeAndCall() public withFactories { - (address admin,) = _randomSigner(); - _TestTemps memory t = _testTemps(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectEmit(true, true, true, true, address(factory)); - emit Upgraded(proxy, implementation1); - - vm.prank(admin); - vm.deal(admin, type(uint128).max); - bytes memory data = abi.encodeWithSignature("setValue(uint256,uint256)", t.key, t.value); - factory.upgradeAndCall{value: t.msgValue}(proxy, implementation1, data); - - _checkImplementationSlot(proxy, implementation1); - uint256 gasBefore = gasleft(); - uint256 storedValue = MockImplementation(proxy).getValue(t.key); - unchecked { - console.log(gasBefore - gasleft()); - } - assertEq(storedValue, t.value); - assertEq(proxy.balance, t.msgValue); - } - - function testUpgradeAndCallWithRevert() public withFactories { - (address admin,) = _randomSigner(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.prank(admin); - vm.expectRevert(MockImplementation.Fail.selector); - factory.upgradeAndCall(proxy, implementation1, abi.encodeWithSignature("fails()")); - } - - function testUpgradeUnauthorized() public withFactories { - (address admin, address sussyAccount) = _randomAccounts(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectRevert(ERC1967Factory.Unauthorized.selector); - vm.prank(sussyAccount); - factory.upgrade(proxy, implementation1); - - vm.expectRevert(ERC1967Factory.Unauthorized.selector); - vm.prank(address(uint160(admin) ^ 1)); - factory.upgrade(proxy, implementation1); - - vm.prank(admin); - factory.upgrade(proxy, implementation1); - } - - function testUpgradeWithCorruptedProxy() public withFactories { - (address admin,) = _randomSigner(); - - vm.prank(admin); - address proxy = factory.deploy(implementation0, admin); - - vm.expectRevert(ERC1967Factory.Unauthorized.selector); - vm.prank(admin); - factory.upgrade(address(uint160(proxy) ^ 1), implementation1); - - _checkImplementationSlot(proxy, implementation0); - } - - function testFactoryDeployment() public { - address deployment = - _safeCreate2(ERC1967FactoryConstants.SALT, ERC1967FactoryConstants.INITCODE); - assertEq(deployment, ERC1967FactoryConstants.ADDRESS); - assertEq(deployment.code, ERC1967FactoryConstants.BYTECODE); - } - - function _randomAccounts() internal returns (address a, address b) { - (a,) = _randomSigner(); - do { - (b,) = _randomSigner(); - } while (a == b); - } - - function _checkImplementationSlot(address proxy, address implementation) internal { - bytes32 slot = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1); - assertEq(vm.load(proxy, slot), bytes32(uint256(uint160(implementation)))); - } - - function _checkProxyBytecode(address proxy) internal { - bytes memory code = address(proxy).code; - assertEq(uint8(bytes1(code[code.length - 1])), 0xfd); - assertTrue(code.length == 127 || code.length == 121); - } -} diff --git a/lib/solady/test/ERC20.t.sol b/lib/solady/test/ERC20.t.sol deleted file mode 100644 index 9c69c4d..0000000 --- a/lib/solady/test/ERC20.t.sol +++ /dev/null @@ -1,482 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import "./utils/InvariantTest.sol"; - -import {ERC20, MockERC20} from "./utils/mocks/MockERC20.sol"; - -contract ERC20Test is SoladyTest { - MockERC20 token; - - bytes32 constant PERMIT_TYPEHASH = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - struct _TestTemps { - address owner; - address to; - uint256 amount; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - uint256 privateKey; - uint256 nonce; - } - - function _testTemps() internal returns (_TestTemps memory t) { - (t.owner, t.privateKey) = _randomSigner(); - t.to = _randomNonZeroAddress(); - t.amount = _random(); - t.deadline = _random(); - } - - function setUp() public { - token = new MockERC20("Token", "TKN", 18); - } - - function testMetadata() public { - assertEq(token.name(), "Token"); - assertEq(token.symbol(), "TKN"); - assertEq(token.decimals(), 18); - } - - function testMint() public { - vm.expectEmit(true, true, true, true); - emit Transfer(address(0), address(0xBEEF), 1e18); - token.mint(address(0xBEEF), 1e18); - - assertEq(token.totalSupply(), 1e18); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(0xBEEF), address(0), 0.9e18); - token.burn(address(0xBEEF), 0.9e18); - - assertEq(token.totalSupply(), 1e18 - 0.9e18); - assertEq(token.balanceOf(address(0xBEEF)), 0.1e18); - } - - function testApprove() public { - vm.expectEmit(true, true, true, true); - emit Approval(address(this), address(0xBEEF), 1e18); - assertTrue(token.approve(address(0xBEEF), 1e18)); - - assertEq(token.allowance(address(this), address(0xBEEF)), 1e18); - } - - function testTransfer() public { - token.mint(address(this), 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(0xBEEF), 1e18); - assertTrue(token.transfer(address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - vm.prank(from); - token.approve(address(this), 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(from, address(0xBEEF), 1e18); - assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.allowance(from, address(this)), 0); - - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testInfiniteApproveTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - vm.prank(from); - token.approve(address(this), type(uint256).max); - - assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.allowance(from, address(this)), type(uint256).max); - - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testPermit() public { - _TestTemps memory t = _testTemps(); - t.deadline = block.timestamp; - - _signPermit(t); - - _expectPermitEmitApproval(t); - _permit(t); - - _checkAllowanceAndNonce(t); - } - - function testMintOverMaxUintReverts() public { - token.mint(address(this), type(uint256).max); - vm.expectRevert(ERC20.TotalSupplyOverflow.selector); - token.mint(address(this), 1); - } - - function testTransferInsufficientBalanceReverts() public { - token.mint(address(this), 0.9e18); - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.transfer(address(0xBEEF), 1e18); - } - - function testTransferFromInsufficientAllowanceReverts() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - vm.prank(from); - token.approve(address(this), 0.9e18); - - vm.expectRevert(ERC20.InsufficientAllowance.selector); - token.transferFrom(from, address(0xBEEF), 1e18); - } - - function testTransferFromInsufficientBalanceReverts() public { - address from = address(0xABCD); - - token.mint(from, 0.9e18); - - vm.prank(from); - token.approve(address(this), 1e18); - - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.transferFrom(from, address(0xBEEF), 1e18); - } - - function testMint(address to, uint256 amount) public { - vm.expectEmit(true, true, true, true); - emit Transfer(address(0), to, amount); - token.mint(to, amount); - - assertEq(token.totalSupply(), amount); - assertEq(token.balanceOf(to), amount); - } - - function testBurn(address from, uint256 mintAmount, uint256 burnAmount) public { - burnAmount = _bound(burnAmount, 0, mintAmount); - - token.mint(from, mintAmount); - vm.expectEmit(true, true, true, true); - emit Transfer(from, address(0), burnAmount); - token.burn(from, burnAmount); - - assertEq(token.totalSupply(), mintAmount - burnAmount); - assertEq(token.balanceOf(from), mintAmount - burnAmount); - } - - function testApprove(address to, uint256 amount) public { - assertTrue(token.approve(to, amount)); - - assertEq(token.allowance(address(this), to), amount); - } - - function testTransfer(address to, uint256 amount) public { - token.mint(address(this), amount); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), to, amount); - assertTrue(token.transfer(to, amount)); - assertEq(token.totalSupply(), amount); - - if (address(this) == to) { - assertEq(token.balanceOf(address(this)), amount); - } else { - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.balanceOf(to), amount); - } - } - - function testTransferFrom( - address spender, - address from, - address to, - uint256 approval, - uint256 amount - ) public { - amount = _bound(amount, 0, approval); - - token.mint(from, amount); - assertEq(token.balanceOf(from), amount); - - vm.prank(from); - token.approve(spender, approval); - - vm.expectEmit(true, true, true, true); - emit Transfer(from, to, amount); - vm.prank(spender); - assertTrue(token.transferFrom(from, to, amount)); - assertEq(token.totalSupply(), amount); - - if (approval == type(uint256).max) { - assertEq(token.allowance(from, spender), approval); - } else { - assertEq(token.allowance(from, spender), approval - amount); - } - - if (from == to) { - assertEq(token.balanceOf(from), amount); - } else { - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(to), amount); - } - } - - function testDirectTransfer(uint256) public { - _TestTemps memory t = _testTemps(); - while (t.owner == t.to) (t.to,) = _randomSigner(); - - uint256 totalSupply = _random(); - token.mint(t.owner, totalSupply); - assertEq(token.balanceOf(t.owner), totalSupply); - assertEq(token.balanceOf(t.to), 0); - if (t.amount > totalSupply) { - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.directTransfer(t.owner, t.to, t.amount); - } else { - vm.expectEmit(true, true, true, true); - emit Transfer(t.owner, t.to, t.amount); - token.directTransfer(t.owner, t.to, t.amount); - assertEq(token.balanceOf(t.owner), totalSupply - t.amount); - assertEq(token.balanceOf(t.to), t.amount); - } - } - - function testDirectSpendAllowance(uint256) public { - _TestTemps memory t = _testTemps(); - uint256 allowance = _random(); - vm.prank(t.owner); - token.approve(t.to, allowance); - assertEq(token.allowance(t.owner, t.to), allowance); - if (allowance == type(uint256).max) { - token.directSpendAllowance(t.owner, t.to, t.amount); - assertEq(token.allowance(t.owner, t.to), allowance); - } else if (t.amount > allowance) { - vm.expectRevert(ERC20.InsufficientAllowance.selector); - token.directSpendAllowance(t.owner, t.to, t.amount); - } else { - token.directSpendAllowance(t.owner, t.to, t.amount); - assertEq(token.allowance(t.owner, t.to), allowance - t.amount); - } - } - - function testPermit(uint256) public { - _TestTemps memory t = _testTemps(); - if (t.deadline < block.timestamp) t.deadline = block.timestamp; - - _signPermit(t); - - _expectPermitEmitApproval(t); - _permit(t); - - _checkAllowanceAndNonce(t); - } - - function _checkAllowanceAndNonce(_TestTemps memory t) internal { - assertEq(token.allowance(t.owner, t.to), t.amount); - assertEq(token.nonces(t.owner), t.nonce + 1); - } - - function testBurnInsufficientBalanceReverts(address to, uint256 mintAmount, uint256 burnAmount) - public - { - if (mintAmount == type(uint256).max) mintAmount--; - burnAmount = _bound(burnAmount, mintAmount + 1, type(uint256).max); - - token.mint(to, mintAmount); - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.burn(to, burnAmount); - } - - function testTransferInsufficientBalanceReverts( - address to, - uint256 mintAmount, - uint256 sendAmount - ) public { - if (mintAmount == type(uint256).max) mintAmount--; - sendAmount = _bound(sendAmount, mintAmount + 1, type(uint256).max); - - token.mint(address(this), mintAmount); - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.transfer(to, sendAmount); - } - - function testTransferFromInsufficientAllowanceReverts( - address to, - uint256 approval, - uint256 amount - ) public { - if (approval == type(uint256).max) approval--; - amount = _bound(amount, approval + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, amount); - - vm.prank(from); - token.approve(address(this), approval); - - vm.expectRevert(ERC20.InsufficientAllowance.selector); - token.transferFrom(from, to, amount); - } - - function testTransferFromInsufficientBalanceReverts( - address to, - uint256 mintAmount, - uint256 sendAmount - ) public { - if (mintAmount == type(uint256).max) mintAmount--; - sendAmount = _bound(sendAmount, mintAmount + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, mintAmount); - - vm.prank(from); - token.approve(address(this), sendAmount); - - vm.expectRevert(ERC20.InsufficientBalance.selector); - token.transferFrom(from, to, sendAmount); - } - - function testPermitBadNonceReverts(uint256) public { - _TestTemps memory t = _testTemps(); - if (t.deadline < block.timestamp) t.deadline = block.timestamp; - while (t.nonce == 0) t.nonce = _random(); - - _signPermit(t); - - vm.expectRevert(ERC20.InvalidPermit.selector); - _permit(t); - } - - function testPermitBadDeadlineReverts(uint256) public { - _TestTemps memory t = _testTemps(); - if (t.deadline == type(uint256).max) t.deadline--; - if (t.deadline < block.timestamp) t.deadline = block.timestamp; - - _signPermit(t); - - vm.expectRevert(ERC20.InvalidPermit.selector); - t.deadline += 1; - _permit(t); - } - - function testPermitPastDeadlineReverts(uint256) public { - _TestTemps memory t = _testTemps(); - t.deadline = _bound(t.deadline, 0, block.timestamp - 1); - - _signPermit(t); - - vm.expectRevert(ERC20.PermitExpired.selector); - _permit(t); - } - - function testPermitReplayReverts(uint256) public { - _TestTemps memory t = _testTemps(); - if (t.deadline < block.timestamp) t.deadline = block.timestamp; - - _signPermit(t); - - _expectPermitEmitApproval(t); - _permit(t); - vm.expectRevert(ERC20.InvalidPermit.selector); - _permit(t); - } - - function _signPermit(_TestTemps memory t) internal view { - bytes32 innerHash = - keccak256(abi.encode(PERMIT_TYPEHASH, t.owner, t.to, t.amount, t.nonce, t.deadline)); - bytes32 domainSeparator = token.DOMAIN_SEPARATOR(); - bytes32 outerHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, innerHash)); - (t.v, t.r, t.s) = vm.sign(t.privateKey, outerHash); - } - - function _expectPermitEmitApproval(_TestTemps memory t) internal { - vm.expectEmit(true, true, true, true); - emit Approval(t.owner, t.to, t.amount); - } - - function _permit(_TestTemps memory t) internal { - address token_ = address(token); - /// @solidity memory-safe-assembly - assembly { - let m := mload(sub(t, 0x20)) - mstore(sub(t, 0x20), 0xd505accf) - pop(call(gas(), token_, 0, sub(t, 0x04), 0xe4, 0x00, 0x00)) - mstore(sub(t, 0x20), m) - } - } -} - -contract ERC20Invariants is SoladyTest, InvariantTest { - BalanceSum balanceSum; - MockERC20 token; - - function setUp() public { - token = new MockERC20("Token", "TKN", 18); - balanceSum = new BalanceSum(token); - _addTargetContract(address(balanceSum)); - } - - function invariantBalanceSum() public { - assertEq(token.totalSupply(), balanceSum.sum()); - } -} - -contract BalanceSum { - MockERC20 token; - uint256 public sum; - - constructor(MockERC20 _token) { - token = _token; - } - - function mint(address from, uint256 amount) public { - token.mint(from, amount); - sum += amount; - } - - function burn(address from, uint256 amount) public { - token.burn(from, amount); - sum -= amount; - } - - function approve(address to, uint256 amount) public { - token.approve(to, amount); - } - - function transferFrom(address from, address to, uint256 amount) public { - token.transferFrom(from, to, amount); - } - - function transfer(address to, uint256 amount) public { - token.transfer(to, amount); - } -} diff --git a/lib/solady/test/ERC2981.t.sol b/lib/solady/test/ERC2981.t.sol deleted file mode 100644 index 83340dc..0000000 --- a/lib/solady/test/ERC2981.t.sol +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; - -import {ERC2981, MockERC2981} from "./utils/mocks/MockERC2981.sol"; - -contract ERC2981Test is SoladyTest { - MockERC2981 token; - - function setUp() public { - token = new MockERC2981(); - } - - struct _TestTemps { - uint256 feeDenominator; - address[2] receivers; - uint256[2] tokenIds; - uint256[2] salePrices; - uint256[2] royaltyFractions; - address defaultReceiver; - uint256 defaultRoyaltyFraction; - } - - function _testTemps() internal returns (_TestTemps memory t) { - t.feeDenominator = token.feeDenominator(); - t.tokenIds[0] = _random(); - do { - t.tokenIds[1] = _random(); - } while (t.tokenIds[0] == t.tokenIds[1]); - t.receivers[0] = _randomNonZeroAddress(); - do { - t.receivers[1] = _randomNonZeroAddress(); - } while (t.receivers[0] == t.receivers[1]); - t.salePrices[0] = _bound(_random(), 0, type(uint160).max); - t.salePrices[1] = _bound(_random(), 0, type(uint160).max); - t.defaultReceiver = _randomNonZeroAddress(); - t.defaultRoyaltyFraction = _bound(_random(), 0, t.feeDenominator); - t.royaltyFractions[0] = _bound(_random(), 0, t.feeDenominator); - t.royaltyFractions[1] = _bound(_random(), 0, t.feeDenominator); - } - - function testRoyaltyOverflowCheckDifferential(uint256 x, uint256 y) public { - unchecked { - bool expected = x != 0 && (x * y) / x != y; - bool computed; - /// @solidity memory-safe-assembly - assembly { - computed := mul(y, gt(x, div(not(0), y))) - } - assertEq(computed, expected); - } - } - - function testSetAndGetRoyaltyInfo(uint256) public { - _TestTemps memory t = _testTemps(); - - if (_random() % 16 == 0) _checkReverts(t); - - _checkRoyaltyInfoIsZero(t); - - token.setDefaultRoyalty(t.defaultReceiver, uint96(t.defaultRoyaltyFraction)); - _checkRoyaltyInfoIsDefault(t, 0); - _checkRoyaltyInfoIsDefault(t, 1); - - token.setTokenRoyalty(t.tokenIds[0], t.receivers[0], uint96(t.royaltyFractions[0])); - _checkRoyaltyInfo(t, 0); - _checkRoyaltyInfoIsDefault(t, 1); - token.setTokenRoyalty(t.tokenIds[1], t.receivers[1], uint96(t.royaltyFractions[1])); - _checkRoyaltyInfo(t, 0); - _checkRoyaltyInfo(t, 1); - - if (_random() % 16 == 0) _checkReverts(t); - - token.resetTokenRoyalty(t.tokenIds[0]); - _checkRoyaltyInfoIsDefault(t, 0); - _checkRoyaltyInfo(t, 1); - token.resetTokenRoyalty(t.tokenIds[1]); - _checkRoyaltyInfoIsDefault(t, 0); - _checkRoyaltyInfoIsDefault(t, 1); - - if (_random() % 16 == 0) _checkReverts(t); - - token.deleteDefaultRoyalty(); - - _checkRoyaltyInfoIsZero(t); - - if (_random() % 16 == 0) _checkReverts(t); - } - - function _getInvalidFeeNumerator(_TestTemps memory t) internal returns (uint96 r) { - while (true) { - r = uint96(_random()); - if (r > t.feeDenominator) break; - } - } - - function _checkReverts(_TestTemps memory t) internal { - vm.expectRevert(ERC2981.RoyaltyReceiverIsZeroAddress.selector); - token.setDefaultRoyalty(address(0), 1); - vm.expectRevert(ERC2981.RoyaltyOverflow.selector); - token.setDefaultRoyalty(t.defaultReceiver, _getInvalidFeeNumerator(t)); - - vm.expectRevert(ERC2981.RoyaltyReceiverIsZeroAddress.selector); - token.setTokenRoyalty(t.tokenIds[0], address(0), 1); - vm.expectRevert(ERC2981.RoyaltyOverflow.selector); - token.setTokenRoyalty(t.tokenIds[0], t.receivers[0], _getInvalidFeeNumerator(t)); - - vm.expectRevert(ERC2981.RoyaltyReceiverIsZeroAddress.selector); - token.setTokenRoyalty(t.tokenIds[1], address(0), 1); - vm.expectRevert(ERC2981.RoyaltyOverflow.selector); - token.setTokenRoyalty(t.tokenIds[1], t.receivers[1], _getInvalidFeeNumerator(t)); - } - - function _checkRoyaltyInfoIsZero(_TestTemps memory t) internal { - _checkRoyaltyInfo(t, 0, address(0), 0); - _checkRoyaltyInfo(t, 1, address(0), 0); - } - - function _checkRoyaltyInfoIsDefault(_TestTemps memory t, uint256 i) internal { - uint256 expected = t.salePrices[i] * t.defaultRoyaltyFraction / t.feeDenominator; - _checkRoyaltyInfo(t, i, t.defaultReceiver, expected); - } - - function _checkRoyaltyInfo(_TestTemps memory t, uint256 i) internal { - uint256 expected = t.salePrices[i] * t.royaltyFractions[i] / t.feeDenominator; - _checkRoyaltyInfo(t, i, t.receivers[i], expected); - } - - function _checkRoyaltyInfo( - _TestTemps memory t, - uint256 i, - address expectedReceiver, - uint256 expectedAmount - ) internal { - (address receiver, uint256 amount) = token.royaltyInfo(t.tokenIds[i], t.salePrices[i]); - assertEq(receiver, expectedReceiver); - assertEq(amount, expectedAmount); - } -} diff --git a/lib/solady/test/ERC4337.t.sol b/lib/solady/test/ERC4337.t.sol deleted file mode 100644 index a54370b..0000000 --- a/lib/solady/test/ERC4337.t.sol +++ /dev/null @@ -1,555 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {Ownable, SignatureCheckerLib} from "../src/accounts/ERC4337.sol"; -import {ERC4337, MockERC4337} from "./utils/mocks/MockERC4337.sol"; -import {MockEntryPoint} from "./utils/mocks/MockEntryPoint.sol"; -import {MockERC721} from "./utils/mocks/MockERC721.sol"; -import {MockERC1155} from "./utils/mocks/MockERC1155.sol"; -import {MockERC1271Wallet} from "./utils/mocks/MockERC1271Wallet.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; -import {LibString} from "../src/utils/LibString.sol"; -import {LibZip} from "../src/utils/LibZip.sol"; - -contract Target { - error TargetError(bytes data); - - bytes32 public datahash; - - bytes public data; - - function setData(bytes memory data_) public payable returns (bytes memory) { - data = data_; - datahash = keccak256(data_); - return data_; - } - - function revertWithTargetError(bytes memory data_) public payable { - revert TargetError(data_); - } - - function changeOwnerSlotValue(bool change) public payable { - /// @solidity memory-safe-assembly - assembly { - if change { sstore(not(0x8b78c6d8), 0x112233) } - } - } -} - -contract ERC4337Test is SoladyTest { - event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); - - // By right, this should be the keccak256 of some long-ass string: - // (e.g. `keccak256("Parent(bytes32 childHash,Mail child)Mail(Person from,Person to,string contents)Person(string name,address wallet)")`). - // But I'm lazy and will use something randomish here. - bytes32 internal constant _PARENT_TYPEHASH = - 0xd61db970ec8a2edc5f9fd31d876abe01b785909acb16dcd4baaf3b434b4c439b; - - // By right, this should be a proper domain separator, but I'm lazy. - bytes32 internal constant _DOMAIN_SEP_B = - 0xa1a044077d7677adbbfa892ded5390979b33993e0e2a457e3f974bbcda53821b; - - address internal constant _ENTRY_POINT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; - - address erc4337; - - MockERC4337 account; - - function setUp() public { - // Etch something onto `_ENTRY_POINT` such that we can deploy the account implementation. - vm.etch(_ENTRY_POINT, hex"00"); - erc4337 = address(new MockERC4337()); - account = MockERC4337(payable(LibClone.deployERC1967(erc4337))); - } - - function testInitializer() public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(0), address(this)); - account.initialize(address(this)); - assertEq(account.owner(), address(this)); - vm.expectRevert(Ownable.AlreadyInitialized.selector); - account.initialize(address(this)); - - address newOwner = _randomNonZeroAddress(); - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), newOwner); - account.transferOwnership(newOwner); - assertEq(account.owner(), newOwner); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(newOwner, address(this)); - vm.prank(newOwner); - account.transferOwnership(address(this)); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), address(0)); - account.renounceOwnership(); - assertEq(account.owner(), address(0)); - - vm.expectRevert(Ownable.AlreadyInitialized.selector); - account.initialize(address(this)); - assertEq(account.owner(), address(0)); - - account = MockERC4337(payable(LibClone.deployERC1967(erc4337))); - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(0), address(0)); - account.initialize(address(0)); - assertEq(account.owner(), address(0)); - - vm.expectRevert(Ownable.AlreadyInitialized.selector); - account.initialize(address(this)); - assertEq(account.owner(), address(0)); - - account = MockERC4337(payable(LibClone.deployERC1967(erc4337))); - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(0), address(1)); - account.initialize(address(1)); - assertEq(account.owner(), address(1)); - - vm.expectRevert(Ownable.AlreadyInitialized.selector); - account.initialize(address(this)); - assertEq(account.owner(), address(1)); - } - - function testExecute() public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - address target = address(new Target()); - bytes memory data = _randomBytes(111); - account.execute(target, 123, abi.encodeWithSignature("setData(bytes)", data)); - assertEq(Target(target).datahash(), keccak256(data)); - assertEq(target.balance, 123); - - vm.prank(_randomNonZeroAddress()); - vm.expectRevert(Ownable.Unauthorized.selector); - account.execute(target, 123, abi.encodeWithSignature("setData(bytes)", data)); - - vm.expectRevert(abi.encodeWithSignature("TargetError(bytes)", data)); - account.execute(target, 123, abi.encodeWithSignature("revertWithTargetError(bytes)", data)); - } - - function testExecuteBatch() public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - ERC4337.Call[] memory calls = new ERC4337.Call[](2); - calls[0].target = address(new Target()); - calls[1].target = address(new Target()); - calls[0].value = 123; - calls[1].value = 456; - calls[0].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(111)); - calls[1].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(222)); - - account.executeBatch(calls); - assertEq(Target(calls[0].target).datahash(), keccak256(_randomBytes(111))); - assertEq(Target(calls[1].target).datahash(), keccak256(_randomBytes(222))); - assertEq(calls[0].target.balance, 123); - assertEq(calls[1].target.balance, 456); - - calls[1].data = abi.encodeWithSignature("revertWithTargetError(bytes)", _randomBytes(111)); - vm.expectRevert(abi.encodeWithSignature("TargetError(bytes)", _randomBytes(111))); - account.executeBatch(calls); - } - - function testExecuteBatch(uint256 r) public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - unchecked { - uint256 n = r & 3; - ERC4337.Call[] memory calls = new ERC4337.Call[](n); - - for (uint256 i; i != n; ++i) { - uint256 v = _random() & 0xff; - calls[i].target = address(new Target()); - calls[i].value = v; - calls[i].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(v)); - } - - bytes[] memory results; - if (_random() & 1 == 0) { - results = account.executeBatch(_random(), calls); - } else { - results = account.executeBatch(calls); - } - - assertEq(results.length, n); - for (uint256 i; i != n; ++i) { - uint256 v = calls[i].value; - assertEq(Target(calls[i].target).datahash(), keccak256(_randomBytes(v))); - assertEq(calls[i].target.balance, v); - assertEq(abi.decode(results[i], (bytes)), _randomBytes(v)); - } - } - } - - function testDelegateExecute() public { - testDelegateExecute(123); - } - - function testDelegateExecute(uint256 r) public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - address delegate = address(new Target()); - - bytes memory data; - data = abi.encodeWithSignature("setData(bytes)", _randomBytes(r)); - data = account.delegateExecute(delegate, data); - assertEq(abi.decode(data, (bytes)), _randomBytes(r)); - data = account.delegateExecute(delegate, abi.encodeWithSignature("datahash()")); - assertEq(abi.decode(data, (bytes32)), keccak256(_randomBytes(r))); - data = account.delegateExecute(delegate, abi.encodeWithSignature("data()")); - assertEq(abi.decode(data, (bytes)), _randomBytes(r)); - } - - function testDelegateExecuteRevertsIfOwnerSlotValueChanged() public { - account.initialize(address(this)); - - address delegate = address(new Target()); - - bytes memory data; - data = abi.encodeWithSignature("changeOwnerSlotValue(bool)", false); - account.delegateExecute(delegate, data); - vm.expectRevert(); - data = abi.encodeWithSignature("changeOwnerSlotValue(bool)", true); - account.delegateExecute(delegate, data); - data = abi.encodeWithSignature("changeOwnerSlotValue(bool)", false); - account.delegateExecute(delegate, data); - } - - function testDepositFunctions() public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - vm.etch(account.entryPoint(), address(new MockEntryPoint()).code); - assertEq(account.getDeposit(), 0); - account.addDeposit{value: 123}(); - assertEq(account.getDeposit(), 123); - address to = _randomNonZeroAddress(); - assertEq(to.balance, 0); - account.withdrawDepositTo(to, 12); - assertEq(to.balance, 12); - assertEq(account.getDeposit(), 123 - 12); - } - - function testCdFallback() public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - vm.etch(account.entryPoint(), address(new MockEntryPoint()).code); - assertEq(account.getDeposit(), 0); - - bytes memory data = LibZip.cdCompress(abi.encodeWithSignature("addDeposit()")); - (bool success,) = address(account).call{value: 123}(data); - assertTrue(success); - assertEq(account.getDeposit(), 123); - } - - function testCdFallback2() public { - vm.deal(address(account), 1 ether); - account.initialize(address(this)); - - vm.etch(account.entryPoint(), address(new MockEntryPoint()).code); - assertEq(account.getDeposit(), 0); - - ERC4337.Call[] memory calls = new ERC4337.Call[](2); - calls[0].target = address(new Target()); - calls[1].target = address(new Target()); - calls[0].value = 123; - calls[1].value = 456; - calls[0].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(111)); - calls[1].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(222)); - - bytes memory data = LibZip.cdCompress( - abi.encodeWithSignature("executeBatch((address,uint256,bytes)[])", calls) - ); - (bool success,) = address(account).call(data); - assertTrue(success); - assertEq(Target(calls[0].target).datahash(), keccak256(_randomBytes(111))); - assertEq(Target(calls[1].target).datahash(), keccak256(_randomBytes(222))); - assertEq(calls[0].target.balance, 123); - assertEq(calls[1].target.balance, 456); - } - - struct _TestTemps { - bytes32 userOpHash; - bytes32 hash; - address signer; - uint256 privateKey; - uint8 v; - bytes32 r; - bytes32 s; - uint256 missingAccountFunds; - } - - function testValidateUserOp() public { - _TestTemps memory t; - t.userOpHash = keccak256("123"); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = - vm.sign(t.privateKey, SignatureCheckerLib.toEthSignedMessageHash(t.userOpHash)); - t.missingAccountFunds = 456; - vm.deal(address(account), 1 ether); - assertEq(address(account).balance, 1 ether); - - account.initialize(t.signer); - - vm.etch(account.entryPoint(), address(new MockEntryPoint()).code); - MockEntryPoint ep = MockEntryPoint(payable(account.entryPoint())); - - ERC4337.UserOperation memory userOp; - // Success returns 0. - userOp.signature = abi.encodePacked(t.r, t.s, t.v); - assertEq( - ep.validateUserOp(address(account), userOp, t.userOpHash, t.missingAccountFunds), 0 - ); - assertEq(address(ep).balance, t.missingAccountFunds); - // Failure returns 1. - userOp.signature = abi.encodePacked(t.r, bytes32(uint256(t.s) ^ 1), t.v); - assertEq( - ep.validateUserOp(address(account), userOp, t.userOpHash, t.missingAccountFunds), 1 - ); - assertEq(address(ep).balance, t.missingAccountFunds * 2); - // Not entry point reverts. - vm.expectRevert(Ownable.Unauthorized.selector); - account.validateUserOp(userOp, t.userOpHash, t.missingAccountFunds); - } - - function testIsValidSignature() public { - _TestTemps memory t; - t.hash = keccak256("123"); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = vm.sign(t.privateKey, _toERC1271Hash(t.hash)); - - account.initialize(t.signer); - - bytes memory signature = - abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH, _DOMAIN_SEP_B, t.hash); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0x1626ba7e)); - - unchecked { - uint256 vs = uint256(t.s) | uint256(t.v - 27) << 255; - signature = abi.encodePacked(t.r, vs, _PARENT_TYPEHASH, _DOMAIN_SEP_B, t.hash); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0x1626ba7e)); - } - - signature = - abi.encodePacked(t.r, t.s, t.v, uint256(_PARENT_TYPEHASH) ^ 1, _DOMAIN_SEP_B, t.hash); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0xffffffff)); - - signature = - abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH, uint256(_DOMAIN_SEP_B) ^ 1, t.hash); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0xffffffff)); - - signature = - abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH, _DOMAIN_SEP_B, uint256(t.hash) ^ 1); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, t.s, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = ""; - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - } - - function testIsValidSignaturePersonalSign() public { - _TestTemps memory t; - t.hash = keccak256("123"); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = vm.sign(t.privateKey, _toERC1271HashPersonalSign(t.hash)); - - account.initialize(t.signer); - - bytes memory signature = abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0x1626ba7e)); - - unchecked { - uint256 vs = uint256(t.s) | uint256(t.v - 27) << 255; - signature = abi.encodePacked(t.r, vs, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0x1626ba7e)); - } - - signature = abi.encodePacked(t.r, t.s, _PARENT_TYPEHASH, _DOMAIN_SEP_B, t.hash); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, t.s, _PARENT_TYPEHASH, _DOMAIN_SEP_B); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, t.s, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = abi.encodePacked(t.r, _PARENT_TYPEHASH); - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - - signature = ""; - assertEq(account.isValidSignature(t.hash, signature), bytes4(0xffffffff)); - } - - function testIsValidSignatureWrapped() public { - _TestTemps memory t; - t.hash = keccak256("123"); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = vm.sign(t.privateKey, _toERC1271Hash(t.hash)); - - MockERC1271Wallet wrappedSigner = new MockERC1271Wallet(t.signer); - account.initialize(address(wrappedSigner)); - - bytes memory signature = - abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH, _DOMAIN_SEP_B, t.hash); - assertEq(account.isValidSignature(_toChildHash(t.hash), signature), bytes4(0x1626ba7e)); - } - - function _toERC1271Hash(bytes32 child) internal view returns (bytes32) { - bytes32 domainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256("Milady"), - keccak256("1"), - block.chainid, - address(account) - ) - ); - bytes32 parentStructHash = - keccak256(abi.encode(_PARENT_TYPEHASH, _toChildHash(child), child)); - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, parentStructHash)); - } - - function _toChildHash(bytes32 child) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(hex"1901", _DOMAIN_SEP_B, child)); - } - - function _toERC1271HashPersonalSign(bytes32 childHash) internal view returns (bytes32) { - bytes32 domainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256("Milady"), - keccak256("1"), - block.chainid, - address(account) - ) - ); - bytes32 parentStructHash = keccak256(abi.encode(_PARENT_TYPEHASH, childHash)); - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, parentStructHash)); - } - - function testETHReceived() public { - (bool success,) = address(account).call{value: 1 ether}(""); - assertTrue(success); - } - - function testOnERC721Received() public { - address alice = _randomNonZeroAddress(); - MockERC721 erc721 = new MockERC721(); - erc721.mint(alice, 1); - vm.prank(alice); - erc721.safeTransferFrom(alice, address(account), 1); - } - - function testOnERC1155Received() public { - address alice = _randomNonZeroAddress(); - MockERC1155 erc1155 = new MockERC1155(); - erc1155.mint(alice, 1, 1, ""); - vm.prank(alice); - erc1155.safeTransferFrom(alice, address(account), 1, 1, ""); - } - - function testOnERC1155BatchReceived() public { - address alice = _randomNonZeroAddress(); - MockERC1155 erc1155 = new MockERC1155(); - erc1155.mint(alice, 1, 1, ""); - uint256[] memory ids = new uint256[](1); - ids[0] = 1; - uint256[] memory amts = new uint256[](1); - amts[0] = 1; - vm.prank(alice); - erc1155.safeBatchTransferFrom(alice, address(account), ids, amts, ""); - } - - function testDirectStorage() public { - bytes32 storageSlot = bytes32(uint256(123)); - bytes32 storageValue = bytes32(uint256(456)); - - vm.expectRevert(Ownable.Unauthorized.selector); - account.storageStore(storageSlot, storageValue); - - account.initialize(address(this)); - assertEq(account.storageLoad(storageSlot), bytes32(0)); - account.storageStore(storageSlot, storageValue); - assertEq(account.storageLoad(storageSlot), storageValue); - } - - function testOwnerRecovery() public { - ERC4337.UserOperation memory userOp; - - userOp.sender = address(account); - userOp.nonce = 4337; - - // `bob` is set as recovery. - address bob = address(0xb); - userOp.callData = abi.encodeWithSelector( - ERC4337.execute.selector, - address(account), - 0, - abi.encodeWithSelector(Ownable.completeOwnershipHandover.selector, bob) - ); - - // `bob` must accept recovery. - // IRL this would follow need. - vm.prank(bob); - account.requestOwnershipHandover(); - - _TestTemps memory t; - t.userOpHash = keccak256(abi.encode(userOp)); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = - vm.sign(t.privateKey, SignatureCheckerLib.toEthSignedMessageHash(t.userOpHash)); - - t.missingAccountFunds = 456; - vm.deal(address(account), 1 ether); - - account.initialize(t.signer); - assertEq(account.owner(), t.signer); - - vm.etch(account.entryPoint(), address(new MockEntryPoint()).code); - MockEntryPoint ep = MockEntryPoint(payable(account.entryPoint())); - - // Success returns 0. - userOp.signature = abi.encodePacked(t.r, t.s, t.v); - assertEq( - ep.validateUserOp(address(account), userOp, t.userOpHash, t.missingAccountFunds), 0 - ); - // Check recovery to `bob`. - vm.prank(address(ep)); - (bool success,) = address(account).call(userOp.callData); - assertTrue(success); - assertEq(account.owner(), bob); - } - - function _randomBytes(uint256 seed) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, seed) - let r := keccak256(0x00, 0x20) - if lt(byte(2, r), 0x20) { - result := mload(0x40) - let n := and(r, 0x7f) - mstore(result, n) - codecopy(add(result, 0x20), byte(1, r), add(n, 0x40)) - mstore(0x40, add(add(result, 0x40), n)) - } - } - } -} diff --git a/lib/solady/test/ERC4337Factory.t.sol b/lib/solady/test/ERC4337Factory.t.sol deleted file mode 100644 index 33eb422..0000000 --- a/lib/solady/test/ERC4337Factory.t.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MockERC4337} from "./utils/mocks/MockERC4337.sol"; -import {ERC4337Factory} from "../src/accounts/ERC4337Factory.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; - -contract ERC4337FactoryTest is SoladyTest { - address internal constant _ENTRY_POINT = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; - - ERC4337Factory factory; - - MockERC4337 erc4337; - - function setUp() public { - // Etch something onto `_ENTRY_POINT` such that we can deploy the account implementation. - vm.etch(_ENTRY_POINT, hex"00"); - erc4337 = new MockERC4337(); - factory = new ERC4337Factory(address(erc4337)); - } - - function testDeployDeterministic(uint256) public { - vm.deal(address(this), 100 ether); - address owner = _randomNonZeroAddress(); - uint256 initialValue = _random() % 100 ether; - bytes32 salt = _random() % 8 == 0 ? bytes32(_random()) : bytes32(uint256(uint96(_random()))); - address account; - if (uint256(salt) >> 96 != uint160(owner) && uint256(salt) >> 96 != 0) { - vm.expectRevert(LibClone.SaltDoesNotStartWith.selector); - account = factory.createAccount{value: initialValue}(owner, salt); - return; - } else { - account = factory.createAccount{value: initialValue}(owner, salt); - } - assertEq(address(account).balance, initialValue); - assertEq(MockERC4337(payable(account)).owner(), owner); - _checkImplementationSlot(account, address(erc4337)); - } - - function testCreateAccountRepeatedDeployment() public { - bytes32 salt = bytes32(_random() & uint256(type(uint96).max)); - address expectedInstance = factory.getAddress(salt); - address instance = factory.createAccount{value: 123}(address(0xABCD), salt); - assertEq(instance.balance, 123); - assertEq(factory.createAccount{value: 456}(address(0xABCD), salt), instance); - assertEq(factory.createAccount(address(0xABCD), salt), instance); - assertEq(instance.balance, 123 + 456); - assertEq(expectedInstance, instance); - } - - function testCreateAccountRepeatedDeployment(uint256) public { - address owner = _randomNonZeroAddress(); - bytes32 salt = - bytes32((_random() & uint256(type(uint96).max)) | (uint256(uint160(owner)) << 96)); - address expectedInstance = factory.getAddress(salt); - address notOwner = _randomNonZeroAddress(); - while (owner == notOwner) notOwner = _randomNonZeroAddress(); - vm.expectRevert(LibClone.SaltDoesNotStartWith.selector); - factory.createAccount{value: 123}(notOwner, salt); - address instance = factory.createAccount{value: 123}(owner, salt); - assertEq(instance.balance, 123); - vm.expectRevert(LibClone.SaltDoesNotStartWith.selector); - factory.createAccount{value: 123}(notOwner, salt); - assertEq(factory.createAccount{value: 456}(owner, salt), instance); - assertEq(factory.createAccount(owner, salt), instance); - assertEq(instance.balance, 123 + 456); - assertEq(expectedInstance, instance); - } - - function _checkImplementationSlot(address proxy, address implementation_) internal { - bytes32 slot = bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1); - assertEq(vm.load(proxy, slot), bytes32(uint256(uint160(implementation_)))); - } -} diff --git a/lib/solady/test/ERC4626.t.sol b/lib/solady/test/ERC4626.t.sol deleted file mode 100644 index c84a0cb..0000000 --- a/lib/solady/test/ERC4626.t.sol +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; - -import {ERC20, MockERC20} from "./utils/mocks/MockERC20.sol"; -import {ERC4626, MockERC4626} from "./utils/mocks/MockERC4626.sol"; -import {SafeTransferLib} from "../src/utils/SafeTransferLib.sol"; -import {FixedPointMathLib} from "../src/utils/FixedPointMathLib.sol"; - -contract ERC4626Test is SoladyTest { - MockERC20 underlying; - MockERC4626 vault; - - event Deposit(address indexed by, address indexed owner, uint256 assets, uint256 shares); - - event Withdraw( - address indexed by, - address indexed to, - address indexed owner, - uint256 assets, - uint256 shares - ); - - function setUp() public { - underlying = new MockERC20("Mock Token", "TKN", 18); - vault = new MockERC4626(address(underlying), "Mock Token Vault", "vwTKN", false, 0); - } - - function _replaceWithVirtualSharesVault(uint8 decimalsOffset) internal { - vault = new MockERC4626(address(underlying), "VSV", "VSVTKN", true, decimalsOffset); - } - - function _replaceWithVirtualSharesVault() internal { - _replaceWithVirtualSharesVault(0); - } - - function testDifferentialFullMulDiv(uint256 x, uint256 y, uint256 d) public { - d = type(uint256).max - d % 4; - (bool success0,) = address(this).call( - abi.encodeWithSignature("fullMulDivChecked(uint256,uint256,uint256)", x, y, d) - ); - (bool success1,) = address(this).call( - abi.encodeWithSignature("fullMulDivUnchecked(uint256,uint256,uint256)", x, y, d) - ); - if (d == type(uint256).max) { - assertFalse(success0); - assertFalse(success1); - } - assertEq(success0, success1); - } - - function fullMulDivChecked(uint256 x, uint256 y, uint256 d) public pure { - FixedPointMathLib.fullMulDiv(x, y, d + 1); - } - - function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d) public pure { - unchecked { - FixedPointMathLib.fullMulDiv(x, y, d + 1); - } - } - - function testMetadata() public { - assertEq(vault.name(), "Mock Token Vault"); - assertEq(vault.symbol(), "vwTKN"); - assertEq(vault.decimals(), 18); - } - - function testUseVirtualShares() public { - assertEq(vault.useVirtualShares(), false); - _replaceWithVirtualSharesVault(); - assertEq(vault.useVirtualShares(), true); - assertEq(vault.decimals(), 18); - _replaceWithVirtualSharesVault(1); - assertEq(vault.decimals(), 19); - } - - function testTryGetAssetDecimals() public { - unchecked { - for (uint256 i = 0; i < 5; ++i) { - _testTryGetAssetDecimals(uint8(i)); - } - for (uint256 i = 125; i < 130; ++i) { - _testTryGetAssetDecimals(uint8(i)); - } - for (uint256 i = 250; i < 256; ++i) { - _testTryGetAssetDecimals(uint8(i)); - } - } - vault = new MockERC4626(address(this), "", "", false, 0); - assertEq(vault.decimals(), 18); - } - - function _testTryGetAssetDecimals(uint8 i) internal { - underlying = new MockERC20("", "", i); - assertEq(underlying.decimals(), i); - vault = new MockERC4626(address(underlying), "", "", false, 0); - assertEq(vault.decimals(), i); - } - - function testSingleDepositWithdraw(uint128 amount) public { - if (amount == 0) amount = 1; - - uint256 aliceUnderlyingAmount = amount; - - address alice = address(0xABCD); - - underlying.mint(alice, aliceUnderlyingAmount); - - vm.prank(alice); - underlying.approve(address(vault), aliceUnderlyingAmount); - assertEq(underlying.allowance(alice, address(vault)), aliceUnderlyingAmount); - - uint256 alicePreDepositBal = underlying.balanceOf(alice); - - vm.prank(alice); - uint256 aliceShareAmount = vault.deposit(aliceUnderlyingAmount, alice); - - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect exchange rate to be 1:1 on initial deposit. - assertEq(aliceUnderlyingAmount, aliceShareAmount); - assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); - assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); - assertEq(vault.totalSupply(), aliceShareAmount); - assertEq(vault.totalAssets(), aliceUnderlyingAmount); - assertEq(vault.balanceOf(alice), aliceShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); - assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); - - vm.prank(alice); - vault.withdraw(aliceUnderlyingAmount, alice, alice); - - assertEq(vault.beforeWithdrawHookCalledCounter(), 1); - - assertEq(vault.totalAssets(), 0); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(underlying.balanceOf(alice), alicePreDepositBal); - } - - function testSingleMintRedeem(uint128 amount) public { - if (amount == 0) amount = 1; - - uint256 aliceShareAmount = amount; - - address alice = address(0xABCD); - - underlying.mint(alice, aliceShareAmount); - - vm.prank(alice); - underlying.approve(address(vault), aliceShareAmount); - assertEq(underlying.allowance(alice, address(vault)), aliceShareAmount); - - uint256 alicePreDepositBal = underlying.balanceOf(alice); - - vm.prank(alice); - uint256 aliceUnderlyingAmount = vault.mint(aliceShareAmount, alice); - - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect exchange rate to be 1:1 on initial mint. - assertEq(aliceShareAmount, aliceUnderlyingAmount); - assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); - assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); - assertEq(vault.totalSupply(), aliceShareAmount); - assertEq(vault.totalAssets(), aliceUnderlyingAmount); - assertEq(vault.balanceOf(alice), aliceUnderlyingAmount); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); - assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); - - vm.prank(alice); - vault.redeem(aliceShareAmount, alice, alice); - - assertEq(vault.beforeWithdrawHookCalledCounter(), 1); - - assertEq(vault.totalAssets(), 0); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(underlying.balanceOf(alice), alicePreDepositBal); - } - - function testMultipleMintDepositRedeemWithdraw() public { - _testMultipleMintDepositRedeemWithdraw(0); - } - - function testVirtualSharesMultipleMintDepositRedeemWithdraw() public { - _replaceWithVirtualSharesVault(); - _testMultipleMintDepositRedeemWithdraw(1); - } - - struct _TestTemps { - address alice; - address bob; - uint256 mutationUnderlyingAmount; - uint256 aliceUnderlyingAmount; - uint256 aliceShareAmount; - uint256 bobShareAmount; - uint256 bobUnderlyingAmount; - uint256 preMutationShareBal; - uint256 preMutationBal; - } - - function _testMultipleMintDepositRedeemWithdraw(uint256 slippage) public { - // Scenario: - // A = Alice, B = Bob - // ________________________________________________________ - // | Vault shares | A share | A assets | B share | B assets | - // |========================================================| - // | 1. Alice mints 2000 shares (costs 2000 tokens) | - // |--------------|---------|----------|---------|----------| - // | 2000 | 2000 | 2000 | 0 | 0 | - // |--------------|---------|----------|---------|----------| - // | 2. Bob deposits 4000 tokens (mints 4000 shares) | - // |--------------|---------|----------|---------|----------| - // | 6000 | 2000 | 2000 | 4000 | 4000 | - // |--------------|---------|----------|---------|----------| - // | 3. Vault mutates by +3000 tokens... | - // | (simulated yield returned from strategy)... | - // |--------------|---------|----------|---------|----------| - // | 6000 | 2000 | 3000 | 4000 | 6000 | - // |--------------|---------|----------|---------|----------| - // | 4. Alice deposits 2000 tokens (mints 1333 shares) | - // |--------------|---------|----------|---------|----------| - // | 7333 | 3333 | 4999 | 4000 | 6000 | - // |--------------|---------|----------|---------|----------| - // | 5. Bob mints 2000 shares (costs 3001 assets) | - // | NOTE: Bob's assets spent got rounded up | - // | NOTE: Alice's vault assets got rounded up | - // |--------------|---------|----------|---------|----------| - // | 9333 | 3333 | 5000 | 6000 | 9000 | - // |--------------|---------|----------|---------|----------| - // | 6. Vault mutates by +3000 tokens... | - // | (simulated yield returned from strategy) | - // | NOTE: Vault holds 17001 tokens, but sum of | - // | assetsOf() is 17000. | - // |--------------|---------|----------|---------|----------| - // | 9333 | 3333 | 6071 | 6000 | 10929 | - // |--------------|---------|----------|---------|----------| - // | 7. Alice redeem 1333 shares (2428 assets) | - // |--------------|---------|----------|---------|----------| - // | 8000 | 2000 | 3643 | 6000 | 10929 | - // |--------------|---------|----------|---------|----------| - // | 8. Bob withdraws 2928 assets (1608 shares) | - // |--------------|---------|----------|---------|----------| - // | 6392 | 2000 | 3643 | 4392 | 8000 | - // |--------------|---------|----------|---------|----------| - // | 9. Alice withdraws 3643 assets (2000 shares) | - // | NOTE: Bob's assets have been rounded back up | - // |--------------|---------|----------|---------|----------| - // | 4392 | 0 | 0 | 4392 | 8001 | - // |--------------|---------|----------|---------|----------| - // | 10. Bob redeem 4392 shares (8001 tokens) | - // |--------------|---------|----------|---------|----------| - // | 0 | 0 | 0 | 0 | 0 | - // |______________|_________|__________|_________|__________| - - _TestTemps memory t; - t.alice = address(0x9988776655443322110000112233445566778899); - t.bob = address(0x1122334455667788990000998877665544332211); - - t.mutationUnderlyingAmount = 3000; - - underlying.mint(t.alice, 4000); - - vm.prank(t.alice); - underlying.approve(address(vault), 4000); - - assertEq(underlying.allowance(t.alice, address(vault)), 4000); - - underlying.mint(t.bob, 7001); - - vm.prank(t.bob); - underlying.approve(address(vault), 7001); - - assertEq(underlying.allowance(t.bob, address(vault)), 7001); - - // 1. Alice mints 2000 shares (costs 2000 tokens) - vm.prank(t.alice); - vm.expectEmit(true, true, true, true); - emit Deposit(t.alice, t.alice, 2000, 2000); - t.aliceUnderlyingAmount = vault.mint(2000, t.alice); - - t.aliceShareAmount = vault.previewDeposit(t.aliceUnderlyingAmount); - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect to have received the requested mint amount. - assertEq(t.aliceShareAmount, 2000); - assertEq(vault.balanceOf(t.alice), t.aliceShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), t.aliceUnderlyingAmount); - assertEq(vault.convertToShares(t.aliceUnderlyingAmount), vault.balanceOf(t.alice)); - - // Expect a 1:1 ratio before mutation. - assertEq(t.aliceUnderlyingAmount, 2000); - - // Sanity check. - assertEq(vault.totalSupply(), t.aliceShareAmount); - assertEq(vault.totalAssets(), t.aliceUnderlyingAmount); - - // 2. Bob deposits 4000 tokens (mints 4000 shares) - vm.prank(t.bob); - vm.expectEmit(true, true, true, true); - emit Deposit(t.bob, t.bob, 4000, 4000); - t.bobShareAmount = vault.deposit(4000, t.bob); - t.bobUnderlyingAmount = vault.previewWithdraw(t.bobShareAmount); - assertEq(vault.afterDepositHookCalledCounter(), 2); - - // Expect to have received the requested underlying amount. - assertEq(t.bobUnderlyingAmount, 4000); - assertEq(vault.balanceOf(t.bob), t.bobShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), t.bobUnderlyingAmount); - assertEq(vault.convertToShares(t.bobUnderlyingAmount), vault.balanceOf(t.bob)); - - // Expect a 1:1 ratio before mutation. - assertEq(t.bobShareAmount, t.bobUnderlyingAmount); - - // Sanity check. - t.preMutationShareBal = t.aliceShareAmount + t.bobShareAmount; - t.preMutationBal = t.aliceUnderlyingAmount + t.bobUnderlyingAmount; - assertEq(vault.totalSupply(), t.preMutationShareBal); - assertEq(vault.totalAssets(), t.preMutationBal); - assertEq(vault.totalSupply(), 6000); - assertEq(vault.totalAssets(), 6000); - - // 3. Vault mutates by +3000 tokens... | - // (simulated yield returned from strategy)... - // The Vault now contains more tokens than deposited which causes the exchange rate to change. - // Alice share is 33.33% of the Vault, Bob 66.66% of the Vault. - // Alice's share count stays the same but the underlying amount changes from 2000 to 3000. - // Bob's share count stays the same but the underlying amount changes from 4000 to 6000. - underlying.mint(address(vault), t.mutationUnderlyingAmount); - assertEq(vault.totalSupply(), t.preMutationShareBal); - assertEq(vault.totalAssets(), t.preMutationBal + t.mutationUnderlyingAmount); - assertEq(vault.balanceOf(t.alice), t.aliceShareAmount); - assertEq( - vault.convertToAssets(vault.balanceOf(t.alice)), - t.aliceUnderlyingAmount + (t.mutationUnderlyingAmount / 3) * 1 - slippage - ); - assertEq(vault.balanceOf(t.bob), t.bobShareAmount); - assertEq( - vault.convertToAssets(vault.balanceOf(t.bob)), - t.bobUnderlyingAmount + (t.mutationUnderlyingAmount / 3) * 2 - slippage - ); - - // 4. Alice deposits 2000 tokens (mints 1333 shares) - vm.prank(t.alice); - vault.deposit(2000, t.alice); - - assertEq(vault.totalSupply(), 7333); - assertEq(vault.balanceOf(t.alice), 3333); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 4999); - assertEq(vault.balanceOf(t.bob), 4000); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 6000); - - // 5. Bob mints 2000 shares (costs 3001 assets) - // NOTE: Bob's assets spent got rounded up - // NOTE: Alices's vault assets got rounded up - vm.prank(t.bob); - vault.mint(2000, t.bob); - - assertEq(vault.totalSupply(), 9333); - assertEq(vault.balanceOf(t.alice), 3333); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 5000 - slippage); - assertEq(vault.balanceOf(t.bob), 6000); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 9000); - - // Sanity checks: - // Alice and t.bob should have spent all their tokens now - assertEq(underlying.balanceOf(t.alice), 0); - assertEq(underlying.balanceOf(t.bob) - slippage, 0); - // Assets in vault: 4k (t.alice) + 7k (t.bob) + 3k (yield) + 1 (round up) - assertEq(vault.totalAssets(), 14001 - slippage); - - // 6. Vault mutates by +3000 tokens - // NOTE: Vault holds 17001 tokens, but sum of assetsOf() is 17000. - underlying.mint(address(vault), t.mutationUnderlyingAmount); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 6071 - slippage); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 10929 - slippage); - assertEq(vault.totalSupply(), 9333); - assertEq(vault.totalAssets(), 17001 - slippage); - - // 7. Alice redeem 1333 shares (2428 assets) - vm.prank(t.alice); - vault.redeem(1333, t.alice, t.alice); - - assertEq(underlying.balanceOf(t.alice), 2428 - slippage); - assertEq(vault.totalSupply(), 8000); - assertEq(vault.totalAssets(), 14573); - assertEq(vault.balanceOf(t.alice), 2000); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 3643); - assertEq(vault.balanceOf(t.bob), 6000); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 10929); - - // 8. Bob withdraws 2929 assets (1608 shares) - vm.prank(t.bob); - vault.withdraw(2929, t.bob, t.bob); - - assertEq(underlying.balanceOf(t.bob) - slippage, 2929); - assertEq(vault.totalSupply(), 6392); - assertEq(vault.totalAssets(), 11644); - assertEq(vault.balanceOf(t.alice), 2000); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 3643); - assertEq(vault.balanceOf(t.bob), 4392); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 8000); - - // 9. Alice withdraws 3643 assets (2000 shares) - // NOTE: Bob's assets have been rounded back up - vm.prank(t.alice); - vm.expectEmit(true, true, true, true); - emit Withdraw(t.alice, t.alice, t.alice, 3643, 2000); - vault.withdraw(3643, t.alice, t.alice); - assertEq(underlying.balanceOf(t.alice), 6071 - slippage); - assertEq(vault.totalSupply(), 4392); - assertEq(vault.totalAssets(), 8001); - assertEq(vault.balanceOf(t.alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 0); - assertEq(vault.balanceOf(t.bob), 4392); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 8001 - slippage); - - // 10. Bob redeem 4392 shares (8001 tokens) - vm.prank(t.bob); - vm.expectEmit(true, true, true, true); - emit Withdraw(t.bob, t.bob, t.bob, 8001 - slippage, 4392); - vault.redeem(4392, t.bob, t.bob); - assertEq(underlying.balanceOf(t.bob), 10930); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets() - slippage, 0); - assertEq(vault.balanceOf(t.alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(t.alice)), 0); - assertEq(vault.balanceOf(t.bob), 0); - assertEq(vault.convertToAssets(vault.balanceOf(t.bob)), 0); - - // Sanity check - assertEq(underlying.balanceOf(address(vault)) - slippage, 0); - } - - function testDepositWithNotEnoughApprovalReverts() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - assertEq(underlying.allowance(address(this), address(vault)), 0.5e18); - - vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); - vault.deposit(1e18, address(this)); - } - - function testWithdrawWithNotEnoughUnderlyingAmountReverts() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - - vault.deposit(0.5e18, address(this)); - - vm.expectRevert(ERC4626.WithdrawMoreThanMax.selector); - vault.withdraw(1e18, address(this), address(this)); - } - - function testRedeemWithNotEnoughShareAmountReverts() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - - vault.deposit(0.5e18, address(this)); - - vm.expectRevert(ERC4626.RedeemMoreThanMax.selector); - vault.redeem(1e18, address(this), address(this)); - } - - function testWithdrawWithNoUnderlyingAmountReverts() public { - vm.expectRevert(ERC4626.WithdrawMoreThanMax.selector); - vault.withdraw(1e18, address(this), address(this)); - } - - function testRedeemWithNoShareAmountReverts() public { - vm.expectRevert(ERC4626.RedeemMoreThanMax.selector); - vault.redeem(1e18, address(this), address(this)); - } - - function testDepositWithNoApprovalReverts() public { - vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); - vault.deposit(1e18, address(this)); - } - - function testMintWithNoApprovalReverts() public { - vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); - vault.mint(1e18, address(this)); - } - - function testMintZero() public { - vault.mint(0, address(this)); - - assertEq(vault.balanceOf(address(this)), 0); - assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets(), 0); - } - - function testWithdrawZero() public { - vault.withdraw(0, address(this), address(this)); - - assertEq(vault.balanceOf(address(this)), 0); - assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets(), 0); - } - - function testVaultInteractionsForSomeoneElse() public { - // init 2 users with a 1e18 balance - address alice = address(0xABCD); - address bob = address(0xDCBA); - underlying.mint(alice, 1e18); - underlying.mint(bob, 1e18); - - vm.prank(alice); - underlying.approve(address(vault), 1e18); - - vm.prank(bob); - underlying.approve(address(vault), 1e18); - - // alice deposits 1e18 for bob - vm.prank(alice); - vm.expectEmit(true, true, true, true); - emit Deposit(alice, bob, 1e18, 1e18); - vault.deposit(1e18, bob); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(alice), 0); - - // bob mint 1e18 for alice - vm.prank(bob); - vm.expectEmit(true, true, true, true); - emit Deposit(bob, alice, 1e18, 1e18); - vault.mint(1e18, alice); - assertEq(vault.balanceOf(alice), 1e18); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(bob), 0); - - // alice redeem 1e18 for bob - vm.prank(alice); - vm.expectEmit(true, true, true, true); - emit Withdraw(alice, bob, alice, 1e18, 1e18); - vault.redeem(1e18, bob, alice); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(bob), 1e18); - - // bob withdraw 1e18 for alice - vm.prank(bob); - vm.expectEmit(true, true, true, true); - emit Withdraw(bob, alice, bob, 1e18, 1e18); - vault.withdraw(1e18, alice, bob); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 0); - assertEq(underlying.balanceOf(alice), 1e18); - } -} diff --git a/lib/solady/test/ERC6551.t.sol b/lib/solady/test/ERC6551.t.sol deleted file mode 100644 index 5173008..0000000 --- a/lib/solady/test/ERC6551.t.sol +++ /dev/null @@ -1,415 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {SignatureCheckerLib} from "../src/utils/SignatureCheckerLib.sol"; -import {ERC6551Proxy} from "../src/accounts/ERC6551Proxy.sol"; -import {ERC6551, MockERC6551, MockERC6551V2} from "./utils/mocks/MockERC6551.sol"; -import {MockERC6551Registry} from "./utils/mocks/MockERC6551Registry.sol"; -import {MockERC721} from "./utils/mocks/MockERC721.sol"; -import {MockERC1155} from "./utils/mocks/MockERC1155.sol"; -import {LibZip} from "../src/utils/LibZip.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract Target { - error TargetError(bytes data); - - bytes32 public datahash; - - bytes public data; - - function setData(bytes memory data_) public payable returns (bytes memory) { - data = data_; - datahash = keccak256(data_); - return data_; - } - - function revertWithTargetError(bytes memory data_) public payable { - revert TargetError(data_); - } -} - -contract ERC6551Test is SoladyTest { - MockERC6551Registry internal _registry; - - address internal _erc6551; - - address internal _erc721; - - address internal _proxy; - - // By right, this should be the keccak256 of some long-ass string: - // (e.g. `keccak256("Parent(bytes32 childHash,Mail child)Mail(Person from,Person to,string contents)Person(string name,address wallet)")`). - // But I'm lazy and will use something randomish here. - bytes32 internal constant _PARENT_TYPEHASH = - 0xd61db970ec8a2edc5f9fd31d876abe01b785909acb16dcd4baaf3b434b4c439b; - - // By right, this should be a proper domain separator, but I'm lazy. - bytes32 internal constant _DOMAIN_SEP_B = - 0xa1a044077d7677adbbfa892ded5390979b33993e0e2a457e3f974bbcda53821b; - - bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - struct _TestTemps { - address owner; - uint256 chainId; - uint256 tokenId; - bytes32 salt; - MockERC6551 account; - address signer; - uint256 privateKey; - uint8 v; - bytes32 r; - bytes32 s; - } - - function setUp() public { - _registry = new MockERC6551Registry(); - _erc6551 = address(new MockERC6551()); - _erc721 = address(new MockERC721()); - _proxy = address(new ERC6551Proxy(_erc6551)); - } - - function _testTempsMint(address owner) internal returns (uint256 tokenId) { - while (true) { - tokenId = _random() % 8 == 0 ? _random() % 32 : _random(); - (bool success,) = - _erc721.call(abi.encodeWithSignature("mint(address,uint256)", owner, tokenId)); - if (success) return tokenId; - } - } - - function _testTemps() internal returns (_TestTemps memory t) { - t.owner = _randomNonZeroAddress(); - t.tokenId = _testTempsMint(t.owner); - t.chainId = block.chainid; - t.salt = bytes32(_random()); - address account = _registry.createAccount(_proxy, t.salt, t.chainId, _erc721, t.tokenId); - t.account = MockERC6551(payable(account)); - } - - function testDeployERC6551Proxy() public { - console.log(LibString.toHexString(address(new ERC6551Proxy(_erc6551)).code)); - } - - function testInitializeERC6551ProxyImplementation() public { - address account = address(_testTemps().account); - (bool success,) = account.call(""); - assertTrue(success); - bytes32 implementationSlotValue = bytes32(uint256(uint160(_erc6551))); - assertEq(vm.load(account, _ERC1967_IMPLEMENTATION_SLOT), implementationSlotValue); - } - - function testDeployERC6551(uint256) public { - _TestTemps memory t = _testTemps(); - (uint256 chainId, address tokenContract, uint256 tokenId) = t.account.token(); - assertEq(chainId, t.chainId); - assertEq(tokenContract, _erc721); - assertEq(tokenId, t.tokenId); - address owner = t.account.owner(); - assertEq(owner, t.owner); - if (_random() % 8 == 0) { - vm.prank(owner); - address newOnwer = _randomNonZeroAddress(); - MockERC721(_erc721).transferFrom(owner, newOnwer, t.tokenId); - assertEq(t.account.owner(), newOnwer); - } - } - - function testOnERC721ReceivedCycles() public { - unchecked { - uint256 n = 8; - _TestTemps[] memory t = new _TestTemps[](n); - for (uint256 i; i != n; ++i) { - t[i] = _testTemps(); - if (i != 0) { - vm.prank(t[i].owner); - MockERC721(_erc721).safeTransferFrom( - t[i].owner, address(t[i - 1].account), t[i].tokenId - ); - t[i].owner = address(t[i - 1].account); - } - } - for (uint256 i; i != n; ++i) { - for (uint256 j = i; j != n; ++j) { - vm.prank(t[i].owner); - vm.expectRevert(ERC6551.SelfOwnDetected.selector); - MockERC721(_erc721).safeTransferFrom( - t[i].owner, address(t[j].account), t[i].tokenId - ); - } - } - - _TestTemps memory u = _testTemps(); - vm.prank(u.owner); - MockERC721(_erc721).safeTransferFrom(u.owner, address(t[n - 1].account), u.tokenId); - } - } - - function testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) public { - _TestTemps[] memory t = new _TestTemps[](3); - unchecked { - for (uint256 i; i != 3; ++i) { - vm.chainId(i); - t[i] = _testTemps(); - if (i != 0) { - vm.prank(t[i].owner); - MockERC721(_erc721).safeTransferFrom( - t[i].owner, address(t[i - 1].account), t[i].tokenId - ); - t[i].owner = address(t[i - 1].account); - } - } - } - unchecked { - vm.chainId(_random() % 3); - uint256 i = _random() % 3; - uint256 j = _random() % 3; - while (j == i) j = _random() % 3; - vm.prank(t[i].owner); - MockERC721(_erc721).safeTransferFrom(t[i].owner, address(t[j].account), t[i].tokenId); - } - } - - function testOnERC721Received() public { - _TestTemps memory t = _testTemps(); - address alice = _randomNonZeroAddress(); - MockERC721 erc721 = new MockERC721(); - erc721.mint(alice, 1); - vm.prank(alice); - if (alice != address(t.account)) { - erc721.safeTransferFrom(alice, address(t.account), 1); - } - } - - function testOnERC1155Received() public { - _TestTemps memory t = _testTemps(); - address alice = _randomNonZeroAddress(); - MockERC1155 erc1155 = new MockERC1155(); - erc1155.mint(alice, 1, 1, ""); - vm.prank(alice); - erc1155.safeTransferFrom(alice, address(t.account), 1, 1, ""); - } - - function testOnERC1155BatchReceived() public { - _TestTemps memory t = _testTemps(); - address alice = _randomNonZeroAddress(); - MockERC1155 erc1155 = new MockERC1155(); - erc1155.mint(alice, 1, 1, ""); - uint256[] memory ids = new uint256[](1); - ids[0] = 1; - uint256[] memory amts = new uint256[](1); - amts[0] = 1; - vm.prank(alice); - erc1155.safeBatchTransferFrom(alice, address(t.account), ids, amts, ""); - } - - function testExecute() public { - _TestTemps memory t = _testTemps(); - vm.deal(address(t.account), 1 ether); - - address target = address(new Target()); - bytes memory data = _randomBytes(111); - - assertEq(t.account.state(), 0); - - vm.prank(t.owner); - t.account.execute(target, 123, abi.encodeWithSignature("setData(bytes)", data), 0); - assertEq(Target(target).datahash(), keccak256(data)); - assertEq(target.balance, 123); - - vm.prank(_randomNonZeroAddress()); - vm.expectRevert(ERC6551.Unauthorized.selector); - t.account.execute(target, 123, abi.encodeWithSignature("setData(bytes)", data), 0); - - vm.prank(t.owner); - vm.expectRevert(abi.encodeWithSignature("TargetError(bytes)", data)); - t.account.execute( - target, 123, abi.encodeWithSignature("revertWithTargetError(bytes)", data), 0 - ); - - vm.prank(t.owner); - vm.expectRevert(ERC6551.OperationNotSupported.selector); - t.account.execute( - target, 123, abi.encodeWithSignature("revertWithTargetError(bytes)", data), 1 - ); - - assertEq(t.account.state(), 1); - } - - function testExecuteBatch() public { - _TestTemps memory t = _testTemps(); - vm.deal(address(t.account), 1 ether); - - ERC6551.Call[] memory calls = new ERC6551.Call[](2); - calls[0].target = address(new Target()); - calls[1].target = address(new Target()); - calls[0].value = 123; - calls[1].value = 456; - calls[0].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(111)); - calls[1].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(222)); - - assertEq(t.account.state(), 0); - - vm.prank(t.owner); - t.account.executeBatch(calls, 0); - assertEq(Target(calls[0].target).datahash(), keccak256(_randomBytes(111))); - assertEq(Target(calls[1].target).datahash(), keccak256(_randomBytes(222))); - assertEq(calls[0].target.balance, 123); - assertEq(calls[1].target.balance, 456); - - calls[1].data = abi.encodeWithSignature("revertWithTargetError(bytes)", _randomBytes(111)); - vm.expectRevert(abi.encodeWithSignature("TargetError(bytes)", _randomBytes(111))); - vm.prank(t.owner); - t.account.executeBatch(calls, 0); - - vm.prank(t.owner); - vm.expectRevert(ERC6551.OperationNotSupported.selector); - t.account.executeBatch(calls, 1); - - assertEq(t.account.state(), 1); - } - - function testExecuteBatch(uint256 r) public { - _TestTemps memory t = _testTemps(); - vm.deal(address(t.account), 1 ether); - - assertEq(t.account.state(), 0); - - unchecked { - uint256 n = r & 3; - ERC6551.Call[] memory calls = new ERC6551.Call[](n); - - for (uint256 i; i != n; ++i) { - uint256 v = _random() & 0xff; - calls[i].target = address(new Target()); - calls[i].value = v; - calls[i].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(v)); - } - - bytes[] memory results; - if (_random() & 1 == 0) { - vm.prank(t.owner); - results = t.account.executeBatch(_random(), calls, 0); - } else { - vm.prank(t.owner); - results = t.account.executeBatch(calls, 0); - } - - assertEq(results.length, n); - for (uint256 i; i != n; ++i) { - uint256 v = calls[i].value; - assertEq(Target(calls[i].target).datahash(), keccak256(_randomBytes(v))); - assertEq(calls[i].target.balance, v); - assertEq(abi.decode(results[i], (bytes)), _randomBytes(v)); - } - } - - assertEq(t.account.state(), 1); - } - - function testUpgrade() public { - _TestTemps memory t = _testTemps(); - address anotherImplementation = address(new MockERC6551V2()); - vm.expectRevert(ERC6551.Unauthorized.selector); - t.account.upgradeToAndCall(anotherImplementation, bytes("")); - assertEq(t.account.state(), 0); - assertEq(t.account.mockId(), "1"); - - vm.prank(t.owner); - t.account.upgradeToAndCall(anotherImplementation, bytes("")); - assertEq(t.account.state(), 1); - assertEq(t.account.mockId(), "2"); - - vm.prank(t.owner); - t.account.upgradeToAndCall(_erc6551, bytes("")); - assertEq(t.account.state(), 2); - assertEq(t.account.mockId(), "1"); - } - - function testSupportsInterface() public { - _TestTemps memory t = _testTemps(); - assertTrue(t.account.supportsInterface(0x01ffc9a7)); - assertTrue(t.account.supportsInterface(0x6faff5f1)); - assertTrue(t.account.supportsInterface(0x74420f4c)); - assertFalse(t.account.supportsInterface(0x00000001)); - } - - function testCdFallback() public { - _TestTemps memory t = _testTemps(); - vm.deal(t.owner, 1 ether); - - ERC6551.Call[] memory calls = new ERC6551.Call[](2); - calls[0].target = address(new Target()); - calls[1].target = address(new Target()); - calls[0].value = 123; - calls[1].value = 456; - calls[0].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(111)); - calls[1].data = abi.encodeWithSignature("setData(bytes)", _randomBytes(222)); - - bytes memory data = LibZip.cdCompress( - abi.encodeWithSignature("executeBatch((address,uint256,bytes)[],uint8)", calls, 0) - ); - vm.prank(t.owner); - (bool success,) = address(t.account).call{value: 1 ether}(data); - assertTrue(success); - assertEq(Target(calls[0].target).datahash(), keccak256(_randomBytes(111))); - assertEq(Target(calls[1].target).datahash(), keccak256(_randomBytes(222))); - assertEq(calls[0].target.balance, 123); - assertEq(calls[1].target.balance, 456); - } - - function testIsValidSignature() public { - _TestTemps memory t = _testTemps(); - (t.signer, t.privateKey) = _randomSigner(); - (t.v, t.r, t.s) = - vm.sign(t.privateKey, _toERC1271Hash(address(t.account), keccak256("123"))); - - vm.prank(t.owner); - MockERC721(_erc721).safeTransferFrom(t.owner, t.signer, t.tokenId); - - bytes32 hash = keccak256("123"); - bytes memory signature = - abi.encodePacked(t.r, t.s, t.v, _PARENT_TYPEHASH, _DOMAIN_SEP_B, hash); - // Success returns `0x1626ba7e`. - assertEq(t.account.isValidSignature(_toChildHash(hash), signature), bytes4(0x1626ba7e)); - } - - function _toERC1271Hash(address account, bytes32 child) internal view returns (bytes32) { - bytes32 domainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256("Milady"), - keccak256("1"), - block.chainid, - address(account) - ) - ); - bytes32 parentStructHash = - keccak256(abi.encode(_PARENT_TYPEHASH, _toChildHash(child), child)); - return keccak256(abi.encodePacked("\x19\x01", domainSeparator, parentStructHash)); - } - - function _toChildHash(bytes32 child) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(hex"1901", _DOMAIN_SEP_B, child)); - } - - function _randomBytes(uint256 seed) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, seed) - let r := keccak256(0x00, 0x20) - if lt(byte(2, r), 0x20) { - result := mload(0x40) - let n := and(r, 0x7f) - mstore(result, n) - codecopy(add(result, 0x20), byte(1, r), add(n, 0x40)) - mstore(0x40, add(add(result, 0x40), n)) - } - } - } -} diff --git a/lib/solady/test/ERC6909.t.sol b/lib/solady/test/ERC6909.t.sol deleted file mode 100644 index df94587..0000000 --- a/lib/solady/test/ERC6909.t.sol +++ /dev/null @@ -1,535 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; - -import {ERC6909, MockERC6909} from "./utils/mocks/MockERC6909.sol"; - -contract ERC6909Test is SoladyTest { - MockERC6909 token; - - event Transfer( - address by, address indexed from, address indexed to, uint256 indexed id, uint256 amount - ); - - event OperatorSet(address indexed owner, address indexed spender, bool approved); - - event Approval( - address indexed owner, address indexed spender, uint256 indexed id, uint256 amount - ); - - function setUp() public { - token = new MockERC6909(); - } - - function testMetadata() public { - assertEq(token.name(), "Solady Token"); - assertEq(token.symbol(), "ST"); - } - - function testMint() public { - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(0), address(0xBEEF), 1, 1e18); - - token.mint(address(0xBEEF), 1, 1e18); - assertEq(token.balanceOf(address(0xBEEF), 1), 1e18); - } - - function testDecimals() public { - assertEq(token.decimals(1), 18); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1, 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(0xBEEF), address(0), 1, 0.9e18); - token.burn(address(0xBEEF), 1, 0.9e18); - - assertEq(token.balanceOf(address(0xBEEF), 1), 0.1e18); - } - - function testApprove() public { - vm.expectEmit(true, true, true, true); - emit Approval(address(this), address(0xBEEF), 1, 1e18); - assertTrue(token.approve(address(0xBEEF), 1, 1e18)); - - assertEq(token.allowance(address(this), address(0xBEEF), 1), 1e18); - } - - function testTransfer() public { - token.mint(address(this), 1, 1e18); - - assertEq(token.balanceOf(address(this), 1), 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(this), address(0xBEEF), 1, 1e18); - assertTrue(token.transfer(address(0xBEEF), 1, 1e18)); - assertEq(token.balanceOf(address(this), 1), 0); - assertEq(token.balanceOf(address(0xBEEF), 1), 1e18); - } - - function testTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1, 1e18); - - _approve(from, address(this), 1, 1e18); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), from, address(0xBEEF), 1, 1e18); - assertTrue(token.transferFrom(from, address(0xBEEF), 1, 1e18)); - - assertEq(token.allowance(from, address(this), 1), 0); - - assertEq(token.balanceOf(from, 1), 0); - assertEq(token.balanceOf(address(0xBEEF), 1), 1e18); - } - - function testInfiniteApproveTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1, 1e18); - - _approve(from, address(this), 1, type(uint256).max); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), from, address(0xBEEF), 1, 1e18); - assertTrue(token.transferFrom(from, address(0xBEEF), 1, 1e18)); - - assertEq(token.allowance(from, address(this), 1), type(uint256).max); - - assertEq(token.balanceOf(from, 1), 0); - assertEq(token.balanceOf(address(0xBEEF), 1), 1e18); - } - - function testOperatorTransferFrom() public { - address from = address(0xABcD); - - token.mint(from, 1, 1e18); - - _setOperator(from, address(this), true); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), from, address(0xBEEF), 1, 1e18); - assertTrue(token.transferFrom(from, address(0xBEEF), 1, 1e18)); - - assertEq(token.balanceOf(from, 1), 0); - assertEq(token.balanceOf(address(0xBEEF), 1), 1e18); - } - - function testSetOperator() public { - assertEq(token.isOperator(address(this), address(0xBEEF)), false); - - vm.expectEmit(true, true, true, true); - emit OperatorSet(address(this), address(0xBEEF), true); - token.setOperator(address(0xBEEF), true); - assertEq(token.isOperator(address(this), address(0xBEEF)), true); - } - - function testTokenURI() public { - token.mint(address(0xBEEF), 1, 1e18); - assertEq(token.tokenURI(1), "http://solady.org/1"); - } - - function testMintOverMaxUintReverts() public { - token.mint(address(this), 1, type(uint256).max); - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.mint(address(this), 1, 1); - } - - function testTransferOverMaxUintReverts() public { - token.mint(address(this), 1, type(uint256).max); - token.transfer(address(0xBEEF), 1, type(uint256).max); - token.mint(address(this), 1, 1); - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.transfer(address(0xBEEF), 1, 1); - } - - function testTransferFromOverMaxUintReverts() public { - address from = address(0xABCD); - - _approve(from, address(this), 1, type(uint256).max); - - token.mint(from, 1, type(uint256).max); - token.transferFrom(from, address(0xBEEF), 1, type(uint256).max); - - token.mint(from, 1, 1); - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.transferFrom(from, address(0xBEEF), 1, 1); - } - - function testTransferInsufficientBalanceReverts() public { - token.mint(address(this), 1, 0.9e18); - _expectInsufficientBalanceRevert(); - token.transfer(address(0xBEEF), 1, 1e18); - } - - function testTransferFromInsufficientPermission() public { - address from = address(0xABCD); - - token.mint(from, 1, 1e18); - - _approve(from, address(this), 1, 0.9e18); - - _expectInsufficientPermissionRevert(); - token.transferFrom(from, address(0xBEEF), 1, 1e18); - } - - function testTransferFromInsufficientBalanceReverts() public { - address from = address(0xABCD); - - token.mint(from, 1, 0.9e18); - - _approve(from, address(this), 1, 1e18); - - _expectInsufficientBalanceRevert(); - token.transferFrom(from, address(0xBEEF), 1, 1e18); - } - - function testMint(address to, uint256 id, uint256 amount) public { - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(0), to, id, amount); - token.mint(to, id, amount); - - assertEq(token.balanceOf(to, id), amount); - } - - function testBurn(address from, uint256 id, uint256 mintAmount, uint256 burnAmount) public { - burnAmount = _bound(burnAmount, 0, mintAmount); - - token.mint(from, id, mintAmount); - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), from, address(0), id, burnAmount); - token.burn(from, id, burnAmount); - - assertEq(token.balanceOf(from, id), mintAmount - burnAmount); - } - - function testApprove(address to, uint256 id, uint256 amount) public { - _approve(address(this), to, id, amount); - } - - function testTransfer(address to, uint256 id, uint256 amount) public { - token.mint(address(this), id, amount); - - vm.expectEmit(true, true, true, true); - emit Transfer(address(this), address(this), to, id, amount); - assertTrue(token.transfer(to, id, amount)); - - if (address(this) == to) { - assertEq(token.balanceOf(address(this), id), amount); - } else { - assertEq(token.balanceOf(address(this), id), 0); - assertEq(token.balanceOf(to, id), amount); - } - } - - function testTransferFrom( - address spender, - address from, - address to, - uint256 id, - uint256 approval, - uint256 amount - ) public { - amount = _bound(amount, 0, approval); - - token.mint(from, id, amount); - assertEq(token.balanceOf(from, id), amount); - - _approve(from, spender, id, approval); - - vm.expectEmit(true, true, true, true); - emit Transfer(spender, from, to, id, amount); - vm.prank(spender); - assertTrue(token.transferFrom(from, to, id, amount)); - - if (approval == type(uint256).max) { - assertEq(token.allowance(from, spender, id), approval); - } else { - assertEq(token.allowance(from, spender, id), approval - amount); - } - - if (from == to) { - assertEq(token.balanceOf(from, id), amount); - } else { - assertEq(token.balanceOf(from, id), 0); - assertEq(token.balanceOf(to, id), amount); - } - } - - function testSetOperator(address owner, address spender, bool approved) public { - _setOperator(owner, spender, approved); - } - - function testMintOverMaxUintReverts(address to, uint256 id, uint256 amount0, uint256 amount1) - public - { - amount0 = _bound(amount0, 1, type(uint256).max); - amount1 = _bound(amount1, type(uint256).max - amount0 + 1, type(uint256).max); - token.mint(to, id, amount0); - - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.mint(to, id, amount1); - } - - function testBurnInsufficientBalanceReverts( - address to, - uint256 mintAmount, - uint256 id, - uint256 burnAmount - ) public { - if (mintAmount == type(uint256).max) mintAmount--; - burnAmount = _bound(burnAmount, mintAmount + 1, type(uint256).max); - - token.mint(to, id, mintAmount); - - _expectInsufficientBalanceRevert(); - token.burn(to, id, burnAmount); - } - - function testTransferOverMaxUintReverts( - address to, - uint256 id, - uint256 amount0, - uint256 amount1 - ) public { - amount0 = _bound(amount0, 1, type(uint256).max); - amount1 = _bound(amount1, type(uint256).max - amount0 + 1, type(uint256).max); - - token.mint(address(this), id, amount0); - token.transfer(to, id, amount0); - token.mint(address(this), id, amount1); - - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.transfer(to, id, amount1); - } - - function testTransferInsufficientBalanceReverts( - address to, - uint256 id, - uint256 mintAmount, - uint256 sendAmount - ) public { - if (mintAmount == type(uint256).max) mintAmount--; - sendAmount = _bound(sendAmount, mintAmount + 1, type(uint256).max); - - token.mint(address(this), id, mintAmount); - - _expectInsufficientBalanceRevert(); - token.transfer(to, id, sendAmount); - } - - function testTransferFromOverMaxUintReverts( - address to, - uint256 id, - uint256 amount0, - uint256 amount1 - ) public { - amount0 = _bound(amount0, 1, type(uint256).max); - amount1 = _bound(amount1, type(uint256).max - amount0 + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, id, amount0); - _approve(from, address(this), id, amount0); - - token.transferFrom(from, to, id, amount0); - - token.mint(from, id, amount1); - _approve(from, address(this), id, amount1); - - vm.expectRevert(ERC6909.BalanceOverflow.selector); - token.transferFrom(from, to, id, amount1); - } - - function testTransferFromInsufficientAllowanceReverts( - address to, - uint256 id, - uint256 approval, - uint256 amount - ) public { - if (approval == type(uint256).max) approval--; - amount = _bound(amount, approval + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, amount, id); - - _approve(from, address(this), id, approval); - - _expectInsufficientPermissionRevert(); - token.transferFrom(from, to, id, amount); - } - - function testTransferFromInsufficientBalanceReverts( - address to, - uint256 id, - uint256 mintAmount, - uint256 sendAmount - ) public { - if (mintAmount == type(uint256).max) mintAmount--; - sendAmount = _bound(sendAmount, mintAmount + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, id, mintAmount); - - _approve(from, address(this), id, sendAmount); - - _expectInsufficientBalanceRevert(); - token.transferFrom(from, to, id, sendAmount); - } - - function testTransferFromCallerIsNotOperator(address to, uint256 id, uint256 amount) public { - amount = _bound(amount, 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, id, amount); - - _expectInsufficientPermissionRevert(); - token.transferFrom(from, to, id, amount); - } - - struct _TestTemps { - uint256 id; - uint256 allowance; - bool isOperator; - uint256 balance; - uint256 amount; - address by; - address from; - address to; - bool success; - } - - function testDirectSetOperator() public { - _directSetOperator(address(1), address(2), true); - } - - function testDirectApprove() public { - _directApprove(address(1), address(2), 1, 123); - } - - function testDirectTransfer() public { - token.mint(address(2), 1, 1); - vm.prank(address(2)); - token.approve(address(1), 1, 1); - token.directTransferFrom(address(1), address(2), address(3), 1, 1); - } - - function testDirectFunctions(uint256) public { - _TestTemps memory t; - t.id = _random(); - t.by = _random() % 16 == 0 ? address(0) : _randomAddress(); - t.from = _randomAddress(); - t.to = _randomAddress(); - - for (uint256 q; q != 2; ++q) { - t.success = false; - t.allowance = _random(); - t.balance = _random(); - t.amount = _random(); - t.isOperator = _random() % 4 == 0; - t.id ^= 1; - - token.mint(t.from, t.id, t.balance); - if (_random() % 2 == 0) { - _directSetOperator(t.from, t.by, t.isOperator); - _directApprove(t.from, t.by, t.id, t.allowance); - } else { - _setOperator(t.from, t.by, t.isOperator); - _directApprove(t.from, t.by, t.id, t.allowance); - } - - if (t.balance >= t.amount) { - if (t.by == address(0) || t.isOperator || t.allowance >= t.amount) { - t.success = true; - } else { - _expectInsufficientPermissionRevert(); - } - } else { - if (t.by == address(0) || t.isOperator || t.allowance >= t.amount) { - _expectInsufficientBalanceRevert(); - } else { - _expectInsufficientPermissionRevert(); - } - } - - if (t.by == address(0) && _random() % 4 == 0) { - if (t.success) { - vm.expectEmit(true, true, true, true); - emit Transfer(t.from, t.from, t.to, t.id, t.amount); - } - vm.prank(t.from); - token.transfer(t.to, t.id, t.amount); - } else if (t.by != address(0) && _random() % 4 == 0) { - if (t.success) { - vm.expectEmit(true, true, true, true); - emit Transfer(t.by, t.from, t.to, t.id, t.amount); - } - vm.prank(t.by); - token.transferFrom(t.from, t.to, t.id, t.amount); - } else { - if (t.success) { - vm.expectEmit(true, true, true, true); - emit Transfer(t.by, t.from, t.to, t.id, t.amount); - } - token.directTransferFrom(t.by, t.from, t.to, t.id, t.amount); - } - - if (t.by == address(0) || t.isOperator || t.allowance == type(uint256).max) { - assertEq(token.allowance(t.from, t.by, t.id), t.allowance); - } - - if (t.success) { - if (t.to == t.from) { - assertEq(token.balanceOf(t.to, t.id), t.balance); - } else { - assertEq(token.balanceOf(t.from, t.id), t.balance - t.amount); - assertEq(token.balanceOf(t.to, t.id), t.amount); - } - } - } - } - - function _expectInsufficientBalanceRevert() internal { - vm.expectRevert(ERC6909.InsufficientBalance.selector); - } - - function _expectInsufficientPermissionRevert() internal { - vm.expectRevert(ERC6909.InsufficientPermission.selector); - } - - function _approve(address owner, address spender, uint256 id, uint256 amount) internal { - vm.prank(owner); - vm.expectEmit(true, true, true, true); - emit Approval(owner, spender, id, amount); - token.approve(spender, id, amount); - assertEq(token.allowance(owner, spender, id), amount); - } - - function _setOperator(address owner, address operator, bool approved) internal { - vm.prank(owner); - vm.expectEmit(true, true, true, true); - emit OperatorSet(owner, operator, approved); - token.directSetOperator(owner, operator, approved); - assertEq(token.isOperator(owner, operator), approved); - } - - function _directApprove(address owner, address spender, uint256 id, uint256 amount) internal { - vm.expectEmit(true, true, true, true); - emit Approval(owner, spender, id, amount); - token.directApprove(owner, spender, id, amount); - assertEq(token.allowance(owner, spender, id), amount); - } - - function _directSetOperator(address owner, address operator, bool approved) internal { - vm.expectEmit(true, true, true, true); - emit OperatorSet(owner, operator, approved); - token.directSetOperator(owner, operator, approved); - assertEq(token.isOperator(owner, operator), approved); - } -} diff --git a/lib/solady/test/ERC721.t.sol b/lib/solady/test/ERC721.t.sol deleted file mode 100644 index 5591b2b..0000000 --- a/lib/solady/test/ERC721.t.sol +++ /dev/null @@ -1,976 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; - -import {ERC721, MockERC721} from "./utils/mocks/MockERC721.sol"; - -abstract contract ERC721TokenReceiver { - function onERC721Received(address, address, uint256, bytes calldata) - external - virtual - returns (bytes4) - { - return ERC721TokenReceiver.onERC721Received.selector; - } -} - -contract ERC721Recipient is ERC721TokenReceiver { - address public operator; - address public from; - uint256 public id; - bytes public data; - - function onERC721Received(address _operator, address _from, uint256 _id, bytes calldata _data) - public - virtual - override - returns (bytes4) - { - operator = _operator; - from = _from; - id = _id; - data = _data; - - return ERC721TokenReceiver.onERC721Received.selector; - } -} - -contract RevertingERC721Recipient is ERC721TokenReceiver { - function onERC721Received(address, address, uint256, bytes calldata) - public - virtual - override - returns (bytes4) - { - revert(string(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector))); - } -} - -contract WrongReturnDataERC721Recipient is ERC721TokenReceiver { - function onERC721Received(address, address, uint256, bytes calldata) - public - virtual - override - returns (bytes4) - { - return 0xCAFEBEEF; - } -} - -contract NonERC721Recipient {} - -contract MockERC721WithHooks is MockERC721 { - uint256 public beforeCounter; - uint256 public afterCounter; - - function _beforeTokenTransfer(address, address, uint256) internal virtual override { - beforeCounter++; - } - - function _afterTokenTransfer(address, address, uint256) internal virtual override { - afterCounter++; - } -} - -contract ERC721HooksTest is SoladyTest, ERC721TokenReceiver { - uint256 public expectedBeforeCounter; - uint256 public expectedAfterCounter; - uint256 public ticker; - - function _checkCounters() internal view { - require( - expectedBeforeCounter == MockERC721WithHooks(msg.sender).beforeCounter(), - "Before counter mismatch." - ); - require( - expectedAfterCounter == MockERC721WithHooks(msg.sender).afterCounter(), - "After counter mismatch." - ); - } - - function onERC721Received(address, address, uint256, bytes calldata) - external - virtual - override - returns (bytes4) - { - _checkCounters(); - return ERC721TokenReceiver.onERC721Received.selector; - } - - function _testHooks(MockERC721WithHooks token) internal { - address from = _randomNonZeroAddress(); - uint256 tokenId = - uint256(keccak256(abi.encode(expectedBeforeCounter, expectedAfterCounter))); - expectedBeforeCounter++; - expectedAfterCounter++; - token.mint(address(this), tokenId); - - expectedBeforeCounter++; - expectedAfterCounter++; - token.transferFrom(address(this), from, tokenId); - - expectedBeforeCounter++; - expectedAfterCounter++; - uint256 r = ticker < 4 ? ticker : _random() % 4; - vm.prank(from); - if (r == 0) { - token.safeTransferFrom(from, address(this), tokenId); - } else if (r == 1) { - token.safeTransferFrom(from, address(this), tokenId, ""); - } else if (r == 2) { - token.directSafeTransferFrom(from, address(this), tokenId); - } else if (r == 3) { - token.directSafeTransferFrom(from, address(this), tokenId, ""); - } else { - revert(); - } - } - - function testERC721Hooks() public { - MockERC721WithHooks token = new MockERC721WithHooks(); - - for (uint256 i; i < 32; ++i) { - _testHooks(token); - } - } -} - -contract ERC721Test is SoladyTest { - MockERC721 token; - - uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192; - - event Transfer(address indexed from, address indexed to, uint256 indexed id); - - event Approval(address indexed owner, address indexed approved, uint256 indexed id); - - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - function setUp() public { - token = new MockERC721(); - } - - function _expectMintEvent(address to, uint256 id) internal { - _expectTransferEvent(address(0), to, id); - } - - function _expectBurnEvent(address from, uint256 id) internal { - _expectTransferEvent(from, address(0), id); - } - - function _expectTransferEvent(address from, address to, uint256 id) internal { - vm.expectEmit(true, true, true, true); - emit Transfer(from, to, id); - } - - function _expectApprovalEvent(address owner, address approved, uint256 id) internal { - vm.expectEmit(true, true, true, true); - emit Approval(owner, approved, id); - } - - function _expectApprovalForAllEvent(address owner, address operator, bool approved) internal { - vm.expectEmit(true, true, true, true); - emit ApprovalForAll(owner, operator, approved); - } - - function _aux(address owner) internal pure returns (uint224 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, owner) - result := shr(32, shl(32, keccak256(0x0c, 0x14))) - } - } - - function _extraData(uint256 id) internal pure returns (uint96 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id) - result := shr(160, shl(160, keccak256(0x00, 0x20))) - } - } - - function _transferFrom(address from, address to, uint256 id) internal { - if (_random() % 2 == 0) { - token.transferFrom(from, to, id); - } else { - token.directTransferFrom(from, to, id); - } - } - - function _safeTransferFrom(address from, address to, uint256 id) internal { - if (_random() % 2 == 0) { - token.safeTransferFrom(from, to, id); - } else { - token.directSafeTransferFrom(from, to, id); - } - } - - function _safeTransferFrom(address from, address to, uint256 id, bytes memory data) internal { - if (_random() % 2 == 0) { - token.safeTransferFrom(from, to, id, data); - } else { - token.directSafeTransferFrom(from, to, id, data); - } - } - - function _approve(address spender, uint256 id) internal { - if (_random() % 2 == 0) { - token.approve(spender, id); - } else { - token.directApprove(spender, id); - } - } - - function _setApprovalForAll(address operator, bool approved) internal { - if (_random() % 2 == 0) { - token.setApprovalForAll(operator, approved); - } else { - token.directSetApprovalForAll(operator, approved); - } - } - - function _ownerOf(uint256 id) internal returns (address) { - if (_random() % 2 == 0) { - return token.ownerOf(id); - } else { - return token.directOwnerOf(id); - } - } - - function _getApproved(uint256 id) internal returns (address) { - if (_random() % 2 == 0) { - return token.getApproved(id); - } else { - return token.directGetApproved(id); - } - } - - function _owners() internal returns (address a, address b) { - a = _randomNonZeroAddress(); - b = _randomNonZeroAddress(); - while (a == b) b = _randomNonZeroAddress(); - } - - function testSafetyOfCustomStorage(uint256 id0, uint256 id1) public { - bool safe; - while (id0 == id1) id1 = _random(); - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, id0) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let slot0 := add(id0, add(id0, keccak256(0x00, 0x20))) - let slot2 := add(1, slot0) - mstore(0x00, id1) - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let slot1 := add(id1, add(id1, keccak256(0x00, 0x20))) - let slot3 := add(1, slot1) - safe := 1 - if eq(slot0, slot1) { safe := 0 } - if eq(slot0, slot2) { safe := 0 } - if eq(slot0, slot3) { safe := 0 } - if eq(slot1, slot2) { safe := 0 } - if eq(slot1, slot3) { safe := 0 } - if eq(slot2, slot3) { safe := 0 } - } - require(safe, "Custom storage not safe"); - } - - function testAuthorizedEquivalence(address by, bool isOwnerOrOperator, bool isApprovedAccount) - public - { - bool a = true; - bool b = true; - /// @solidity memory-safe-assembly - assembly { - if by { if iszero(isOwnerOrOperator) { a := isApprovedAccount } } - if iszero(or(iszero(by), isOwnerOrOperator)) { b := isApprovedAccount } - } - assertEq(a, b); - } - - function testCannotExceedMaxBalance() public { - bytes32 balanceSlot; - (address owner0, address owner1) = _owners(); - - /// @solidity memory-safe-assembly - assembly { - mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - mstore(0x00, owner0) - balanceSlot := keccak256(0x0c, 0x1c) - } - - vm.store(address(token), balanceSlot, bytes32(uint256(0xfffffffe))); - token.setAux(owner0, type(uint224).max); - assertEq(token.balanceOf(owner0), 0xfffffffe); - assertEq(token.getAux(owner0), type(uint224).max); - token.mint(owner0, 0); - assertEq(token.balanceOf(owner0), 0xffffffff); - - vm.expectRevert(ERC721.AccountBalanceOverflow.selector); - token.mint(owner0, 1); - - token.uncheckedBurn(0); - assertEq(token.balanceOf(owner0), 0xfffffffe); - - token.mint(owner1, 0); - vm.prank(owner1); - _transferFrom(owner1, owner0, 0); - - token.mint(owner1, 1); - vm.expectRevert(ERC721.AccountBalanceOverflow.selector); - vm.prank(owner1); - _transferFrom(owner1, owner0, 1); - assertEq(token.getAux(owner0), type(uint224).max); - } - - function testMint(uint256 id) public { - address owner = _randomNonZeroAddress(); - - _expectMintEvent(owner, id); - token.mint(owner, id); - - assertEq(token.balanceOf(owner), 1); - assertEq(_ownerOf(id), owner); - } - - function testBurn(uint256 id) public { - address owner = _randomNonZeroAddress(); - - _expectMintEvent(owner, id); - token.mint(owner, id); - - if (_random() % 2 == 0) { - _expectBurnEvent(owner, id); - token.uncheckedBurn(id); - } else { - vm.expectRevert(ERC721.NotOwnerNorApproved.selector); - token.burn(id); - uint256 r = _random() % 3; - if (r == 0) { - vm.prank(owner); - _transferFrom(owner, address(this), id); - _expectBurnEvent(address(this), id); - token.burn(id); - } - if (r == 1) { - vm.prank(owner); - _setApprovalForAll(address(this), true); - _expectBurnEvent(owner, id); - token.burn(id); - } - if (r == 2) { - vm.prank(owner); - _approve(address(this), id); - _expectBurnEvent(owner, id); - token.burn(id); - } - } - - assertEq(token.balanceOf(owner), 0); - - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _ownerOf(id); - } - - function testTransferFrom() public { - address owner = _randomNonZeroAddress(); - token.mint(owner, 0); - vm.prank(owner); - token.transferFrom(owner, address(this), 0); - } - - function testEverything(uint256) public { - address[2] memory owners; - uint256[][2] memory tokens; - - unchecked { - (owners[0], owners[1]) = _owners(); - for (uint256 j; j != 2; ++j) { - tokens[j] = new uint256[](_random() % 3); - } - - for (uint256 j; j != 2; ++j) { - token.setAux(owners[j], _aux(owners[j])); - for (uint256 i; i != tokens[j].length;) { - uint256 id = _random(); - if (!token.exists(id)) { - tokens[j][i++] = id; - _expectMintEvent(owners[j], id); - token.mint(owners[j], id); - token.setExtraData(id, _extraData(id)); - } - } - } - for (uint256 j; j != 2; ++j) { - assertEq(token.balanceOf(owners[j]), tokens[j].length); - for (uint256 i; i != tokens[j].length; ++i) { - vm.prank(owners[j]); - _expectApprovalEvent(owners[j], address(this), tokens[j][i]); - _approve(address(this), tokens[j][i]); - } - } - for (uint256 j; j != 2; ++j) { - for (uint256 i; i != tokens[j].length; ++i) { - assertEq(_getApproved(tokens[j][i]), address(this)); - uint256 fromBalanceBefore = token.balanceOf(owners[j]); - uint256 toBalanceBefore = token.balanceOf(owners[j ^ 1]); - _expectTransferEvent(owners[j], owners[j ^ 1], tokens[j][i]); - _transferFrom(owners[j], owners[j ^ 1], tokens[j][i]); - assertEq(token.balanceOf(owners[j]), fromBalanceBefore - 1); - assertEq(token.balanceOf(owners[j ^ 1]), toBalanceBefore + 1); - assertEq(_getApproved(tokens[j][i]), address(0)); - } - } - for (uint256 j; j != 2; ++j) { - for (uint256 i; i != tokens[j].length; ++i) { - assertEq(_ownerOf(tokens[j][i]), owners[j ^ 1]); - assertEq(token.getExtraData(tokens[j][i]), _extraData(tokens[j][i])); - } - } - if (_random() % 2 == 0) { - for (uint256 j; j != 2; ++j) { - for (uint256 i; i != tokens[j].length; ++i) { - vm.expectRevert(ERC721.NotOwnerNorApproved.selector); - _transferFrom(owners[j ^ 1], owners[j], tokens[j][i]); - vm.prank(owners[j ^ 1]); - _expectApprovalEvent(owners[j ^ 1], address(this), tokens[j][i]); - _approve(address(this), tokens[j][i]); - _expectTransferEvent(owners[j ^ 1], owners[j], tokens[j][i]); - _transferFrom(owners[j ^ 1], owners[j], tokens[j][i]); - } - } - } else { - for (uint256 j; j != 2; ++j) { - vm.prank(owners[j ^ 1]); - _expectApprovalForAllEvent(owners[j ^ 1], address(this), true); - token.setApprovalForAll(address(this), true); - for (uint256 i; i != tokens[j].length; ++i) { - _expectTransferEvent(owners[j ^ 1], owners[j], tokens[j][i]); - _transferFrom(owners[j ^ 1], owners[j], tokens[j][i]); - } - } - } - for (uint256 j; j != 2; ++j) { - assertEq(token.getAux(owners[j]), _aux(owners[j])); - for (uint256 i; i != tokens[j].length; ++i) { - assertEq(_ownerOf(tokens[j][i]), owners[j]); - assertEq(token.getExtraData(tokens[j][i]), _extraData(tokens[j][i])); - } - } - for (uint256 j; j != 2; ++j) { - for (uint256 i; i != tokens[j].length; ++i) { - token.uncheckedBurn(tokens[j][i]); - } - } - for (uint256 j; j != 2; ++j) { - assertEq(token.balanceOf(owners[j]), 0); - for (uint256 i; i != tokens[j].length; ++i) { - assertEq(token.getExtraData(tokens[j][i]), _extraData(tokens[j][i])); - } - } - } - } - - function testIsApprovedOrOwner(uint256 id) public { - (address owner0, address owner1) = _owners(); - - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - token.isApprovedOrOwner(owner0, id); - - token.mint(owner0, id); - assertEq(token.isApprovedOrOwner(owner0, id), true); - - vm.prank(owner0); - _transferFrom(owner0, owner1, id); - assertEq(token.isApprovedOrOwner(owner0, id), false); - - vm.prank(owner1); - _setApprovalForAll(owner0, true); - assertEq(token.isApprovedOrOwner(owner0, id), true); - - vm.prank(owner1); - _setApprovalForAll(owner0, false); - assertEq(token.isApprovedOrOwner(owner0, id), false); - - vm.prank(owner1); - _approve(owner0, id); - assertEq(token.isApprovedOrOwner(owner0, id), true); - } - - function testExtraData(uint256 id) public { - (address owner0, address owner1) = _owners(); - - bool setExtraData = _random() % 2 == 0; - uint96 extraData = uint96(_bound(_random(), 0, type(uint96).max)); - if (setExtraData) { - token.setExtraData(id, extraData); - } - _expectMintEvent(owner0, id); - token.mint(owner0, id); - if (setExtraData) { - assertEq(token.getExtraData(id), extraData); - } else { - assertEq(token.getExtraData(id), 0); - } - - vm.prank(owner0); - _expectTransferEvent(owner0, owner1, id); - _transferFrom(owner0, owner1, id); - if (setExtraData) { - assertEq(token.getExtraData(id), extraData); - } else { - assertEq(token.getExtraData(id), 0); - } - assertEq(_ownerOf(id), owner1); - - if (_random() % 2 == 0) { - extraData = uint96(_bound(_random(), 0, type(uint96).max)); - token.setExtraData(id, extraData); - setExtraData = true; - } - - _expectBurnEvent(owner1, id); - token.uncheckedBurn(id); - if (setExtraData) { - assertEq(token.getExtraData(id), extraData); - } else { - assertEq(token.getExtraData(id), 0); - } - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _ownerOf(id); - } - - function testExtraData2(uint256 id0, uint256 id1) public { - while (id0 == id1) id1 = _random(); - token.setExtraData(id0, _extraData(id0)); - token.setExtraData(id1, _extraData(id1)); - assertEq(token.getExtraData(id0), _extraData(id0)); - assertEq(token.getExtraData(id1), _extraData(id1)); - } - - function testAux(uint256) public { - (address owner0, address owner1) = _owners(); - - bool setAux = _random() % 2 == 0; - if (setAux) { - token.setAux(owner0, _aux(owner0)); - token.setAux(owner1, _aux(owner1)); - } - - for (uint256 i; i < 2; ++i) { - _expectMintEvent(owner0, i * 2 + 0); - token.mint(owner0, i * 2 + 0); - assertEq(token.balanceOf(owner0), i + 1); - - _expectMintEvent(owner1, i * 2 + 1); - token.mint(owner1, i * 2 + 1); - assertEq(token.balanceOf(owner1), i + 1); - - if (setAux) { - assertEq(token.getAux(owner0), _aux(owner0)); - assertEq(token.getAux(owner1), _aux(owner1)); - } else { - assertEq(token.getAux(owner0), 0); - assertEq(token.getAux(owner1), 0); - } - } - - for (uint256 i; i < 2; ++i) { - _expectBurnEvent(owner0, i * 2 + 0); - token.uncheckedBurn(i * 2 + 0); - assertEq(token.balanceOf(owner0), 1 - i); - - _expectBurnEvent(owner1, i * 2 + 1); - token.uncheckedBurn(i * 2 + 1); - assertEq(token.balanceOf(owner1), 1 - i); - - if (setAux) { - assertEq(token.getAux(owner0), _aux(owner0)); - assertEq(token.getAux(owner1), _aux(owner1)); - } else { - assertEq(token.getAux(owner0), 0); - assertEq(token.getAux(owner1), 0); - } - } - } - - function testApprove(uint256 id) public { - (address spender,) = _randomSigner(); - - token.mint(address(this), id); - - _expectApprovalEvent(address(this), spender, id); - _approve(spender, id); - assertEq(_getApproved(id), spender); - } - - function testApproveBurn(uint256 id) public { - (address spender,) = _randomSigner(); - - token.mint(address(this), id); - - _approve(spender, id); - - token.uncheckedBurn(id); - - assertEq(token.balanceOf(address(this)), 0); - - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _getApproved(id); - - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _ownerOf(id); - } - - function testApproveAll(uint256) public { - (address operator,) = _randomSigner(); - bool approved = _random() % 2 == 0; - _expectApprovalForAllEvent(address(this), operator, approved); - _setApprovalForAll(operator, approved); - assertEq(token.isApprovedForAll(address(this), operator), approved); - } - - function testTransferFrom(uint256 id) public { - (address from, address to) = _owners(); - - token.mint(from, id); - - if (_random() % 2 == 0) { - uint256 r = _random() % 3; - if (r == 0) { - vm.prank(from); - _approve(address(this), id); - _expectTransferEvent(from, to, id); - _transferFrom(from, to, id); - } - if (r == 1) { - vm.prank(from); - _setApprovalForAll(address(this), true); - _expectTransferEvent(from, to, id); - _transferFrom(from, to, id); - } - if (r == 2) { - vm.prank(from); - _expectTransferEvent(from, address(this), id); - _transferFrom(from, address(this), id); - _expectTransferEvent(address(this), to, id); - _transferFrom(address(this), to, id); - } - } else { - (address temp,) = _randomSigner(); - while (temp == from || temp == to) (temp,) = _randomSigner(); - if (_random() % 2 == 0) { - _expectTransferEvent(from, temp, id); - token.uncheckedTransferFrom(from, temp, id); - } else { - vm.prank(from); - _expectTransferEvent(from, temp, id); - _transferFrom(from, temp, id); - } - _expectTransferEvent(temp, to, id); - token.uncheckedTransferFrom(temp, to, id); - } - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testTransferFromSelf(uint256 id) public { - (address to,) = _randomSigner(); - - token.mint(address(this), id); - - _transferFrom(address(this), to, id); - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(address(this)), 0); - } - - function testTransferFromApproveAll(uint256 id) public { - (address from, address to) = _owners(); - - token.mint(from, id); - - vm.prank(from); - _setApprovalForAll(address(this), true); - - _transferFrom(from, to, id); - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToEOA(uint256 id) public { - (address from, address to) = _owners(); - - token.mint(from, id); - - vm.prank(from); - _setApprovalForAll(address(this), true); - - _safeTransferFrom(from, to, id); - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToERC721Recipient(uint256 id) public { - (address from,) = _randomSigner(); - - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, id); - - vm.prank(from); - _setApprovalForAll(address(this), true); - - _safeTransferFrom(from, address(recipient), id); - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - assertEq(recipient.id(), id); - assertEq(recipient.data(), ""); - } - - function testSafeTransferFromToERC721RecipientWithData(uint256 id, bytes memory data) public { - (address from,) = _randomSigner(); - - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, id); - - vm.prank(from); - _setApprovalForAll(address(this), true); - - _safeTransferFrom(from, address(recipient), id, data); - - assertEq(recipient.data(), data); - assertEq(recipient.id(), id); - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - - assertEq(_getApproved(id), address(0)); - assertEq(_ownerOf(id), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeMintToEOA(uint256 id) public { - (address to,) = _randomSigner(); - - token.safeMint(to, id); - - assertEq(_ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - } - - function testSafeMintToERC721Recipient(uint256 id) public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), id); - - assertEq(_ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), id); - assertEq(to.data(), ""); - } - - function testSafeMintToERC721RecipientWithData(uint256 id, bytes memory data) public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), id, data); - - assertEq(_ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), id); - assertEq(to.data(), data); - } - - function testMintToZeroReverts(uint256 id) public { - vm.expectRevert(ERC721.TransferToZeroAddress.selector); - token.mint(address(0), id); - } - - function testDoubleMintReverts(uint256 id) public { - (address to,) = _randomSigner(); - - token.mint(to, id); - vm.expectRevert(ERC721.TokenAlreadyExists.selector); - token.mint(to, id); - } - - function testBurnNonExistentReverts(uint256 id) public { - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - token.uncheckedBurn(id); - } - - function testDoubleBurnReverts(uint256 id) public { - (address to,) = _randomSigner(); - - token.mint(to, id); - - token.uncheckedBurn(id); - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - token.uncheckedBurn(id); - } - - function testApproveNonExistentReverts(uint256 id, address to) public { - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _approve(to, id); - } - - function testApproveUnauthorizedReverts(uint256 id) public { - (address owner, address to) = _owners(); - - token.mint(owner, id); - vm.expectRevert(ERC721.NotOwnerNorApproved.selector); - _approve(to, id); - } - - function testTransferFromNotExistentReverts(address from, address to, uint256 id) public { - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _transferFrom(from, to, id); - } - - function testTransferFromWrongFromReverts(address to, uint256 id) public { - (address owner, address from) = _owners(); - - token.mint(owner, id); - vm.expectRevert(ERC721.TransferFromIncorrectOwner.selector); - _transferFrom(from, to, id); - } - - function testTransferFromToZeroReverts(uint256 id) public { - token.mint(address(this), id); - - vm.expectRevert(ERC721.TransferToZeroAddress.selector); - _transferFrom(address(this), address(0), id); - } - - function testTransferFromNotOwner(uint256 id) public { - (address from, address to) = _owners(); - - token.mint(from, id); - - vm.expectRevert(ERC721.NotOwnerNorApproved.selector); - _transferFrom(from, to, id); - } - - function testSafeTransferFromToNonERC721RecipientReverts(uint256 id) public { - token.mint(address(this), id); - address to = address(new NonERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - _safeTransferFrom(address(this), address(to), id); - } - - function testSafeTransferFromToNonERC721RecipientWithDataReverts(uint256 id, bytes memory data) - public - { - token.mint(address(this), id); - address to = address(new NonERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - _safeTransferFrom(address(this), to, id, data); - } - - function testSafeTransferFromToRevertingERC721RecipientReverts(uint256 id) public { - token.mint(address(this), id); - address to = address(new RevertingERC721Recipient()); - vm.expectRevert(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector)); - _safeTransferFrom(address(this), to, id); - } - - function testSafeTransferFromToRevertingERC721RecipientWithDataReverts( - uint256 id, - bytes memory data - ) public { - token.mint(address(this), id); - address to = address(new RevertingERC721Recipient()); - vm.expectRevert(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector)); - _safeTransferFrom(address(this), to, id, data); - } - - function testSafeTransferFromToERC721RecipientWithWrongReturnDataReverts(uint256 id) public { - token.mint(address(this), id); - address to = address(new WrongReturnDataERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - _safeTransferFrom(address(this), to, id); - } - - function testSafeTransferFromToERC721RecipientWithWrongReturnDataWithDataReverts( - uint256 id, - bytes memory data - ) public { - token.mint(address(this), id); - address to = address(new WrongReturnDataERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - _safeTransferFrom(address(this), to, id, data); - } - - function testSafeMintToNonERC721RecipientReverts(uint256 id) public { - address to = address(new NonERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - token.safeMint(to, id); - } - - function testSafeMintToNonERC721RecipientWithDataReverts(uint256 id, bytes memory data) - public - { - address to = address(new NonERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - token.safeMint(to, id, data); - } - - function testSafeMintToRevertingERC721RecipientReverts(uint256 id) public { - address to = address(new RevertingERC721Recipient()); - vm.expectRevert(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector)); - token.safeMint(to, id); - } - - function testSafeMintToRevertingERC721RecipientWithDataReverts(uint256 id, bytes memory data) - public - { - address to = address(new RevertingERC721Recipient()); - vm.expectRevert(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector)); - token.safeMint(to, id, data); - } - - function testSafeMintToERC721RecipientWithWrongReturnData(uint256 id) public { - address to = address(new WrongReturnDataERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - token.safeMint(to, id); - } - - function testSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes memory data) - public - { - address to = address(new WrongReturnDataERC721Recipient()); - vm.expectRevert(ERC721.TransferToNonERC721ReceiverImplementer.selector); - token.safeMint(to, id, data); - } - - function testOwnerOfNonExistent(uint256 id) public { - vm.expectRevert(ERC721.TokenDoesNotExist.selector); - _ownerOf(id); - } -} diff --git a/lib/solady/test/FixedPointMathLib.t.sol b/lib/solady/test/FixedPointMathLib.t.sol deleted file mode 100644 index 62f8bc3..0000000 --- a/lib/solady/test/FixedPointMathLib.t.sol +++ /dev/null @@ -1,1750 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {FixedPointMathLib} from "../src/utils/FixedPointMathLib.sol"; - -contract FixedPointMathLibTest is SoladyTest { - function testExpWad() public { - assertEq(FixedPointMathLib.expWad(-42139678854452767551), 0); - - assertEq(FixedPointMathLib.expWad(-3e18), 49787068367863942); - assertEq(FixedPointMathLib.expWad(-2e18), 135335283236612691); - assertEq(FixedPointMathLib.expWad(-1e18), 367879441171442321); - - assertEq(FixedPointMathLib.expWad(-0.5e18), 606530659712633423); - assertEq(FixedPointMathLib.expWad(-0.3e18), 740818220681717866); - - assertEq(FixedPointMathLib.expWad(0), 1000000000000000000); - - assertEq(FixedPointMathLib.expWad(0.3e18), 1349858807576003103); - assertEq(FixedPointMathLib.expWad(0.5e18), 1648721270700128146); - - assertEq(FixedPointMathLib.expWad(1e18), 2718281828459045235); - assertEq(FixedPointMathLib.expWad(2e18), 7389056098930650227); - assertEq(FixedPointMathLib.expWad(3e18), 20085536923187667741); - // True value: 20085536923187667740.92 - - assertEq(FixedPointMathLib.expWad(10e18), 220264657948067165169_80); - // True value: 22026465794806716516957.90 - // Relative error 9.987984547746668e-22 - - assertEq(FixedPointMathLib.expWad(50e18), 5184705528587072464_148529318587763226117); - // True value: 5184705528587072464_087453322933485384827.47 - // Relative error: 1.1780031733243328e-20 - - assertEq( - FixedPointMathLib.expWad(100e18), - 268811714181613544841_34666106240937146178367581647816351662017 - ); - // True value: 268811714181613544841_26255515800135873611118773741922415191608 - // Relative error: 3.128803544297531e-22 - - assertEq( - FixedPointMathLib.expWad(135305999368893231588), - 578960446186580976_50144101621524338577433870140581303254786265309376407432913 - ); - // True value: 578960446186580976_49816762928942336782129491980154662247847962410455084893091 - // Relative error: 5.653904247484822e-21 - } - - // Notes on lambertW0Wad: - // - // If you want to attempt finding a better approximation, look at - // https://github.com/recmo/experiment-solexp/blob/main/approximate_mpmath.ipynb - // I somehow can't get it to reproduce the approximation constants for `lnWad`. - // Let me know if you can get the code to reproduce the approximation constants for `lnWad`. - - event TestingLambertW0WadMonotonicallyIncreasing( - int256 a, int256 b, int256 w0a, int256 w0b, bool success, uint256 gasUsed - ); - - event LogUint(string name, uint256 value); - event LogInt(string name, int256 value); - - int256 internal constant _ONE_DIV_EXP = 367879441171442321; - int256 internal constant _LAMBERT_W0_MIN = -367879441171442321; - int256 internal constant _EXP = 2718281828459045235; - int256 internal constant _WAD = 10 ** 18; - - function testLambertW0WadKnownValues() public { - _checkLambertW0Wad(0, 0); - _checkLambertW0Wad(1, 1); - _checkLambertW0Wad(2, 2); - _checkLambertW0Wad(3, 2); - _checkLambertW0Wad(131071, 131070); - _checkLambertW0Wad(17179869183, 17179868887); - _checkLambertW0Wad(1000000000000000000, 567143290409783872); - _checkLambertW0Wad(-3678794411715, -3678807945318); - _checkLambertW0Wad(_LAMBERT_W0_MIN, -999999999741585709); - // These are exact values. - _checkLambertW0Wad(2 ** 255 - 1, 130435123404408416612); - _checkLambertW0Wad(2 ** 254 - 1, 129747263755102316133); - _checkLambertW0Wad(2 ** 253 - 1, 129059431996357330139); - _checkLambertW0Wad(2 ** 252 - 1, 128371628422812486425); - _checkLambertW0Wad(2 ** 251 - 1, 127683853333788079721); - _checkLambertW0Wad(2 ** 250 - 1, 126996107033385166927); - _checkLambertW0Wad(2 ** 249 - 1, 126308389830587715420); - _checkLambertW0Wad(2 ** 248 - 1, 125620702039367489656); - _checkLambertW0Wad(2 ** 247 - 1, 124933043978791764502); - _checkLambertW0Wad(2 ** 246 - 1, 124245415973133957088); - _checkLambertW0Wad(2 ** 245 - 1, 123557818351987272451); - _checkLambertW0Wad(2 ** 244 - 1, 122870251450381461880); - _checkLambertW0Wad(2 ** 243 - 1, 122182715608902796703); - _checkLambertW0Wad(2 ** 242 - 1, 121495211173817364188); - _checkLambertW0Wad(2 ** 241 - 1, 120807738497197796422); - _checkLambertW0Wad(2 ** 240 - 1, 120120297937053547320); - _checkLambertW0Wad(2 ** 239 - 1, 119432889857464837488); - _checkLambertW0Wad(2 ** 238 - 1, 118745514628720391363); - _checkLambertW0Wad(2 ** 237 - 1, 118058172627459096009); - _checkLambertW0Wad(2 ** 236 - 1, 117370864236815716134); - _checkLambertW0Wad(2 ** 235 - 1, 116683589846570805279); - _checkLambertW0Wad(2 ** 234 - 1, 115996349853304958814); - _checkLambertW0Wad(2 ** 233 - 1, 115309144660557560280); - _checkLambertW0Wad(2 ** 232 - 1, 114621974678990178815); - _checkLambertW0Wad(2 ** 231 - 1, 113934840326554781918); - _checkLambertW0Wad(2 ** 230 - 1, 113247742028666934564); - _checkLambertW0Wad(2 ** 229 - 1, 112560680218384162820); - _checkLambertW0Wad(2 ** 228 - 1, 111873655336589667598); - _checkLambertW0Wad(2 ** 227 - 1, 111186667832181581935); - _checkLambertW0Wad(2 ** 226 - 1, 110499718162267973459); - _checkLambertW0Wad(2 ** 225 - 1, 109812806792367802251); - _checkLambertW0Wad(2 ** 224 - 1, 109125934196618053331); - _checkLambertW0Wad(2 ** 223 - 1, 108439100857987272488); - _checkLambertW0Wad(2 ** 222 - 1, 107752307268495744067); - _checkLambertW0Wad(2 ** 221 - 1, 107065553929442559763); - _checkLambertW0Wad(2 ** 220 - 1, 106378841351639838444); - _checkLambertW0Wad(2 ** 219 - 1, 105692170055654368478); - _checkLambertW0Wad(2 ** 218 - 1, 105005540572056956171); - _checkLambertW0Wad(2 ** 217 - 1, 104318953441679776592); - _checkLambertW0Wad(2 ** 216 - 1, 103632409215882036434); - _checkLambertW0Wad(2 ** 215 - 1, 102945908456824272609); - _checkLambertW0Wad(2 ** 214 - 1, 102259451737751625038); - _checkLambertW0Wad(2 ** 213 - 1, 101573039643286437675); - _checkLambertW0Wad(2 ** 212 - 1, 100886672769730558166); - _checkLambertW0Wad(2 ** 211 - 1, 100200351725377723788); - _checkLambertW0Wad(2 ** 210 - 1, 99514077130836439501); - _checkLambertW0Wad(2 ** 209 - 1, 98827849619363773067); - _checkLambertW0Wad(2 ** 208 - 1, 98141669837210512407); - _checkLambertW0Wad(2 ** 207 - 1, 97455538443978151616); - _checkLambertW0Wad(2 ** 206 - 1, 96769456112988194563); - _checkLambertW0Wad(2 ** 205 - 1, 96083423531664288650); - _checkLambertW0Wad(2 ** 204 - 1, 95397441401927726359); - _checkLambertW0Wad(2 ** 203 - 1, 94711510440606878644); - _checkLambertW0Wad(2 ** 202 - 1, 94025631379861152095); - _checkLambertW0Wad(2 ** 201 - 1, 93339804967620091367); - _checkLambertW0Wad(2 ** 200 - 1, 92654031968038279517); - _checkLambertW0Wad(2 ** 199 - 1, 91968313161966721893); - _checkLambertW0Wad(2 ** 198 - 1, 91282649347441434152); - _checkLambertW0Wad(2 ** 197 - 1, 90597041340189991908); - _checkLambertW0Wad(2 ** 196 - 1, 89911489974156838659); - _checkLambertW0Wad(2 ** 195 - 1, 89225996102048190100); - _checkLambertW0Wad(2 ** 194 - 1, 88540560595897416858); - _checkLambertW0Wad(2 ** 193 - 1, 87855184347651834275); - _checkLambertW0Wad(2 ** 192 - 1, 87169868269781877263); - _checkLambertW0Wad(2 ** 191 - 1, 86484613295913690725); - _checkLambertW0Wad(2 ** 190 - 1, 85799420381486221653); - _checkLambertW0Wad(2 ** 189 - 1, 85114290504433958190); - _checkLambertW0Wad(2 ** 188 - 1, 84429224665896523735); - _checkLambertW0Wad(2 ** 187 - 1, 83744223890956400983); - _checkLambertW0Wad(2 ** 186 - 1, 83059289229406131801); - _checkLambertW0Wad(2 ** 185 - 1, 82374421756546414467); - _checkLambertW0Wad(2 ** 184 - 1, 81689622574016600237); - _checkLambertW0Wad(2 ** 183 - 1, 81004892810659176931); - _checkLambertW0Wad(2 ** 182 - 1, 80320233623419918558); - _checkLambertW0Wad(2 ** 181 - 1, 79635646198285477393); - _checkLambertW0Wad(2 ** 180 - 1, 78951131751260298782); - _checkLambertW0Wad(2 ** 179 - 1, 78266691529384849812); - _checkLambertW0Wad(2 ** 178 - 1, 77582326811797271395); - _checkLambertW0Wad(2 ** 177 - 1, 76898038910840689756); - _checkLambertW0Wad(2 ** 176 - 1, 76213829173218558571); - _checkLambertW0Wad(2 ** 175 - 1, 75529698981200547567); - _checkLambertW0Wad(2 ** 174 - 1, 74845649753881648207); - _checkLambertW0Wad(2 ** 173 - 1, 74161682948497332759); - _checkLambertW0Wad(2 ** 172 - 1, 73477800061797780656); - _checkLambertW0Wad(2 ** 171 - 1, 72794002631484376331); - _checkLambertW0Wad(2 ** 170 - 1, 72110292237711886966); - _checkLambertW0Wad(2 ** 169 - 1, 71426670504659947705); - _checkLambertW0Wad(2 ** 168 - 1, 70743139102177717275); - _checkLambertW0Wad(2 ** 167 - 1, 70059699747505819935); - _checkLambertW0Wad(2 ** 166 - 1, 69376354207079961679); - _checkLambertW0Wad(2 ** 165 - 1, 68693104298420901379); - _checkLambertW0Wad(2 ** 164 - 1, 68009951892115772747); - _checkLambertW0Wad(2 ** 163 - 1, 67326898913896092682); - _checkLambertW0Wad(2 ** 162 - 1, 66643947346818157796); - _checkLambertW0Wad(2 ** 161 - 1, 65961099233551926143); - _checkLambertW0Wad(2 ** 160 - 1, 65278356678784907905); - _checkLambertW0Wad(2 ** 159 - 1, 64595721851748049983); - _checkLambertW0Wad(2 ** 158 - 1, 63913196988871098107); - _checkLambertW0Wad(2 ** 157 - 1, 63230784396575459844); - _checkLambertW0Wad(2 ** 156 - 1, 62548486454213176429); - _checkLambertW0Wad(2 ** 155 - 1, 61866305617161244980); - _checkLambertW0Wad(2 ** 154 - 1, 61184244420081220067); - _checkLambertW0Wad(2 ** 153 - 1, 60502305480354769865); - _checkLambertW0Wad(2 ** 152 - 1, 59820491501706673077); - _checkLambertW0Wad(2 ** 151 - 1, 59138805278027624755); - _checkLambertW0Wad(2 ** 150 - 1, 58457249697410179101); - _checkLambertW0Wad(2 ** 149 - 1, 57775827746412203235); - _checkLambertW0Wad(2 ** 148 - 1, 57094542514563356374); - _checkLambertW0Wad(2 ** 147 - 1, 56413397199131353678); - _checkLambertW0Wad(2 ** 146 - 1, 55732395110166133991); - _checkLambertW0Wad(2 ** 145 - 1, 55051539675841537897); - _checkLambertW0Wad(2 ** 144 - 1, 54370834448115730535); - _checkLambertW0Wad(2 ** 143 - 1, 53690283108733387465); - _checkLambertW0Wad(2 ** 142 - 1, 53009889475594618649); - _checkLambertW0Wad(2 ** 141 - 1, 52329657509517754228); - _checkLambertW0Wad(2 ** 140 - 1, 51649591321425477661); - _checkLambertW0Wad(2 ** 139 - 1, 50969695179986390948); - _checkLambertW0Wad(2 ** 138 - 1, 50289973519746960243); - _checkLambertW0Wad(2 ** 137 - 1, 49610430949791948630); - _checkLambertW0Wad(2 ** 136 - 1, 48931072262974930811); - _checkLambertW0Wad(2 ** 135 - 1, 48251902445764340905); - _checkLambertW0Wad(2 ** 134 - 1, 47572926688754773801); - _checkLambertW0Wad(2 ** 133 - 1, 46894150397897992742); - _checkLambertW0Wad(2 ** 132 - 1, 46215579206513348095); - _checkLambertW0Wad(2 ** 131 - 1, 45537218988143149666); - _checkLambertW0Wad(2 ** 130 - 1, 44859075870325031417); - _checkLambertW0Wad(2 ** 129 - 1, 44181156249360587882); - _checkLambertW0Wad(2 ** 128 - 1, 43503466806167642613); - _checkLambertW0Wad(2 ** 127 - 1, 42826014523312541917); - _checkLambertW0Wad(2 ** 126 - 1, 42148806703328979292); - _checkLambertW0Wad(2 ** 125 - 1, 41471850988441194251); - _checkLambertW0Wad(2 ** 124 - 1, 40795155381822122767); - _checkLambertW0Wad(2 ** 123 - 1, 40118728270531400808); - _checkLambertW0Wad(2 ** 122 - 1, 39442578450294263667); - _checkLambertW0Wad(2 ** 121 - 1, 38766715152300604375); - _checkLambertW0Wad(2 ** 120 - 1, 38091148072224059569); - _checkLambertW0Wad(2 ** 119 - 1, 37415887401684336100); - _checkLambertW0Wad(2 ** 118 - 1, 36740943862402491609); - _checkLambertW0Wad(2 ** 117 - 1, 36066328743329022902); - _checkLambertW0Wad(2 ** 116 - 1, 35392053941058967434); - _checkLambertW0Wad(2 ** 115 - 1, 34718132003887455986); - _checkLambertW0Wad(2 ** 114 - 1, 34044576179904059477); - _checkLambertW0Wad(2 ** 113 - 1, 33371400469575784902); - _checkLambertW0Wad(2 ** 112 - 1, 32698619683327803297); - _checkLambertW0Wad(2 ** 111 - 1, 32026249504699254799); - _checkLambertW0Wad(2 ** 110 - 1, 31354306559730344521); - _checkLambertW0Wad(2 ** 109 - 1, 30682808493328298780); - _checkLambertW0Wad(2 ** 108 - 1, 30011774053465850808); - _checkLambertW0Wad(2 ** 107 - 1, 29341223184189485097); - _checkLambertW0Wad(2 ** 106 - 1, 28671177128558970924); - _checkLambertW0Wad(2 ** 105 - 1, 28001658542808735364); - _checkLambertW0Wad(2 ** 104 - 1, 27332691623220201135); - _checkLambertW0Wad(2 ** 103 - 1, 26664302247428250682); - _checkLambertW0Wad(2 ** 102 - 1, 25996518132161712657); - _checkLambertW0Wad(2 ** 101 - 1, 25329369009746106264); - _checkLambertW0Wad(2 ** 100 - 1, 24662886826087826761); - _checkLambertW0Wad(2 ** 99 - 1, 23997105963326166352); - _checkLambertW0Wad(2 ** 98 - 1, 23332063490900058530); - _checkLambertW0Wad(2 ** 97 - 1, 22667799449451523321); - _checkLambertW0Wad(2 ** 96 - 1, 22004357172804292983); - _checkLambertW0Wad(2 ** 95 - 1, 21341783654247925671); - _checkLambertW0Wad(2 ** 94 - 1, 20680129964567978803); - _checkLambertW0Wad(2 ** 93 - 1, 20019451730746615034); - _checkLambertW0Wad(2 ** 92 - 1, 19359809686086176343); - _checkLambertW0Wad(2 ** 91 - 1, 18701270304772358157); - _checkLambertW0Wad(2 ** 90 - 1, 18043906536712772323); - _checkLambertW0Wad(2 ** 89 - 1, 17387798662016868795); - _checkLambertW0Wad(2 ** 88 - 1, 16733035288929945451); - _checkLambertW0Wad(2 ** 87 - 1, 16079714524670107222); - _checkLambertW0Wad(2 ** 86 - 1, 15427945355807184379); - _checkLambertW0Wad(2 ** 85 - 1, 14777849284057868231); - _checkLambertW0Wad(2 ** 84 - 1, 14129562275318189632); - _checkLambertW0Wad(2 ** 83 - 1, 13483237095324880705); - _checkLambertW0Wad(2 ** 82 - 1, 12839046125789215063); - _checkLambertW0Wad(2 ** 81 - 1, 12197184781931118579); - _checkLambertW0Wad(2 ** 80 - 1, 11557875688514566228 - 1); - _checkLambertW0Wad(2 ** 79 - 1, 10921373820226202580); - _checkLambertW0Wad(2 ** 78 - 1, 10287972878516218499); - _checkLambertW0Wad(2 ** 77 - 1, 9658013267990184319); - _checkLambertW0Wad(2 ** 76 - 1, 9031892161491509531); - _checkLambertW0Wad(2 ** 75 - 1, 8410076319328428686); - _checkLambertW0Wad(2 ** 74 - 1, 7793118576966979948); - _checkLambertW0Wad(2 ** 73 - 1, 7181679269695846234); - _checkLambertW0Wad(2 ** 72 - 1, 6576554370186862926); - _checkLambertW0Wad(2 ** 71 - 1, 5978712844468804878); - _checkLambertW0Wad(2 ** 70 - 1, 5389346779005776683); - _checkLambertW0Wad(2 ** 69 - 1, 4809939316762921936); - _checkLambertW0Wad(2 ** 68 - 1, 4242357480017482271); - _checkLambertW0Wad(2 ** 67 - 1, 3688979548845126287); - _checkLambertW0Wad(2 ** 66 - 1, 3152869312105232629); - _checkLambertW0Wad(2 ** 65 - 1, 2638010157689274059); - _checkLambertW0Wad(2 ** 64 - 1, 2149604165721149566); - _checkLambertW0Wad(2 ** 63 - 1, 1694407549795038335); - _checkLambertW0Wad(2 ** 62 - 1, 1280973323147500590); - _checkLambertW0Wad(2 ** 61 - 1, 919438481612859603); - _checkLambertW0Wad(2 ** 60 - 1, 620128202996354327); - _checkLambertW0Wad(2 ** 59 - 1, 390213425026895126); - _checkLambertW0Wad(2 ** 58 - 1, 229193491169149614); - _checkLambertW0Wad(2 ** 57 - 1, 126935310044982397); - _checkLambertW0Wad(2 ** 56 - 1, 67363429834711483); - _checkLambertW0Wad(2 ** 55 - 1, 34796675828817814); - _checkLambertW0Wad(2 ** 54 - 1, 17698377658513340); - _checkLambertW0Wad(2 ** 53 - 1, 8927148493627578); - _checkLambertW0Wad(2 ** 52 - 1, 4483453146102402); - _checkLambertW0Wad(2 ** 51 - 1, 2246746269994097); - _checkLambertW0Wad(2 ** 50 - 1, 1124634392838166); - _checkLambertW0Wad(2 ** 49 - 1, 562633308112667); - _checkLambertW0Wad(2 ** 48 - 1, 281395781982528); - _checkLambertW0Wad(2 ** 47 - 1, 140717685495042); - _checkLambertW0Wad(2 ** 46 - 1, 70363792940114); - _checkLambertW0Wad(2 ** 45 - 1, 35183134214121); - _checkLambertW0Wad(2 ** 44 - 1, 17591876567571); - _checkLambertW0Wad(2 ** 43 - 1, 8796015651975); - _checkLambertW0Wad(2 ** 42 - 1, 4398027168417); - _checkLambertW0Wad(2 ** 41 - 1, 2199018419863); - _checkLambertW0Wad(2 ** 40 - 1, 1099510418851); - _checkLambertW0Wad(2 ** 39 - 1, 549755511655); - _checkLambertW0Wad(2 ** 38 - 1, 274877831385); - _checkLambertW0Wad(2 ** 37 - 1, 137438934581); - _checkLambertW0Wad(2 ** 36 - 1, 68719472012); - _checkLambertW0Wad(2 ** 35 - 1, 34359737186); - _checkLambertW0Wad(2 ** 34 - 1, 17179868887); - _checkLambertW0Wad(2 ** 33 - 1, 8589934517); - _checkLambertW0Wad(2 ** 32 - 1, 4294967276); - _checkLambertW0Wad(2 ** 31 - 1, 2147483642); - _checkLambertW0Wad(2 ** 30 - 1, 1073741821); - _checkLambertW0Wad(2 ** 29 - 1, 536870910); - _checkLambertW0Wad(2 ** 28 - 1, 268435454); - _checkLambertW0Wad(2 ** 27 - 1, 134217726); - _checkLambertW0Wad(2 ** 26 - 1, 67108862); - _checkLambertW0Wad(2 ** 25 - 1, 33554430); - _checkLambertW0Wad(2 ** 24 - 1, 16777214); - _checkLambertW0Wad(2 ** 23 - 1, 8388606); - _checkLambertW0Wad(2 ** 22 - 1, 4194302); - _checkLambertW0Wad(2 ** 21 - 1, 2097150); - _checkLambertW0Wad(2 ** 20 - 1, 1048574); - _checkLambertW0Wad(2 ** 19 - 1, 524286); - _checkLambertW0Wad(2 ** 18 - 1, 262142); - _checkLambertW0Wad(2 ** 17 - 1, 131070); - _checkLambertW0Wad(2 ** 16 - 1, 65534); - _checkLambertW0Wad(2 ** 15 - 1, 32766); - _checkLambertW0Wad(2 ** 14 - 1, 16382); - _checkLambertW0Wad(2 ** 13 - 1, 8190); - _checkLambertW0Wad(2 ** 12 - 1, 4094); - _checkLambertW0Wad(2 ** 11 - 1, 2046); - _checkLambertW0Wad(2 ** 10 - 1, 1022); - _checkLambertW0Wad(2 ** 9 - 1, 510); - _checkLambertW0Wad(2 ** 8 - 1, 254); - } - - function testLambertW0WadRevertsForOutOfDomain() public { - FixedPointMathLib.lambertW0Wad(_LAMBERT_W0_MIN); - for (int256 i = 0; i <= 10; ++i) { - vm.expectRevert(FixedPointMathLib.OutOfDomain.selector); - FixedPointMathLib.lambertW0Wad(_LAMBERT_W0_MIN - 1 - i); - } - vm.expectRevert(FixedPointMathLib.OutOfDomain.selector); - FixedPointMathLib.lambertW0Wad(-type(int256).max); - } - - function _checkLambertW0Wad(int256 x, int256 expected) internal { - unchecked { - uint256 gasBefore = gasleft(); - int256 w = FixedPointMathLib.lambertW0Wad(x); - uint256 gasUsed = gasBefore - gasleft(); - emit LogInt("x", x); - emit LogUint("gasUsed", gasUsed); - assertEq(w, expected); - } - } - - function testLambertW0WadAccuracy() public { - testLambertW0WadAccuracy(uint184(int184(_testLamberW0WadAccuracyThres()))); - testLambertW0WadAccuracy(2 ** 184 - 1); - } - - function testLambertW0WadAccuracy(uint184 a) public { - int256 x = int256(int184(a)); - if (x >= _testLamberW0WadAccuracyThres()) { - int256 l = FixedPointMathLib.lnWad(x); - int256 r = x * l / _WAD; - int256 w = FixedPointMathLib.lambertW0Wad(r); - assertLt(FixedPointMathLib.abs(l - w), 0xff); - } - } - - function _testLamberW0WadAccuracyThres() internal pure returns (int256) { - unchecked { - return _ONE_DIV_EXP + _ONE_DIV_EXP * 0.01 ether / 1 ether; - } - } - - function testLambertW0WadWithinBounds(int256 x) public { - if (x <= 0) x = _boundLambertW0WadInput(x); - int256 w = FixedPointMathLib.lambertW0Wad(x); - assertTrue(w <= x); - unchecked { - if (x > _EXP) { - int256 l = FixedPointMathLib.lnWad(x); - assertGt(l, 0); - int256 ll = FixedPointMathLib.lnWad(l); - int256 q = ll * _WAD; - int256 lower = l - ll + q / (2 * l); - assertLt(lower, w + 1); - int256 upper = l - ll + (q * _EXP) / (l * (_EXP - _WAD)) + 1; - assertLt(w, upper); - } - } - } - - function testLambertW0WadWithinBounds() public { - testLambertW0WadWithinBounds(_EXP - 1); - testLambertW0WadWithinBounds(_EXP); - testLambertW0WadWithinBounds(_EXP + 1); - testLambertW0WadWithinBounds(type(int256).max); - } - - function testLambertW0WadMonotonicallyIncreasing() public { - unchecked { - for (uint256 i; i <= 256; ++i) { - uint256 x = 1 << i; - testLambertW0WadMonotonicallyIncreasingAround(int256(x)); - testLambertW0WadMonotonicallyIncreasingAround(int256(x - 1)); - } - for (uint256 i; i <= 57; ++i) { - uint256 x = 1 << i; - testLambertW0WadMonotonicallyIncreasingAround(-int256(x)); - testLambertW0WadMonotonicallyIncreasingAround(-int256(x - 1)); - } - } - } - - function testLambertW0WadMonotonicallyIncreasing2() public { - // These are some problematic values gathered over the attempts. - // Some might not be problematic now. - _testLambertW0WadMonoAround(0x598cdf77327d789dc); - _testLambertW0WadMonoAround(0x3c8d97dfe4afb1b05); - _testLambertW0WadMonoAround(0x56a147b480c03cc22); - _testLambertW0WadMonoAround(0x3136f439c231d0bb9); - _testLambertW0WadMonoAround(0x2ae7cff17ef2469a1); - _testLambertW0WadMonoAround(0x1de668fd7afcf61cc); - _testLambertW0WadMonoAround(0x15024b2a35f2cdd95); - _testLambertW0WadMonoAround(0x11a65ae94b59590f9); - _testLambertW0WadMonoAround(0xf0c2c82174dffb7e); - _testLambertW0WadMonoAround(0xed3e56938cb11626); - _testLambertW0WadMonoAround(0xecf5c4e511142439); - _testLambertW0WadMonoAround(0xc0755fa2b4033cb0); - _testLambertW0WadMonoAround(0xa235db282ea4edc6); - _testLambertW0WadMonoAround(0x9ff2ec5c26eec112); - _testLambertW0WadMonoAround(0xa0c3c4e36f4415f1); - _testLambertW0WadMonoAround(0x9b9f0e8d61287782); - _testLambertW0WadMonoAround(0x7df719d1a4a7b8ad); - _testLambertW0WadMonoAround(0x7c881679a1464d25); - _testLambertW0WadMonoAround(0x7bec47487071495a); - _testLambertW0WadMonoAround(0x7be31c75fc717f9f); - _testLambertW0WadMonoAround(0x7bbb4e0716eeca53); - _testLambertW0WadMonoAround(0x78e59d40a92b443b); - _testLambertW0WadMonoAround(0x77658c4ad3af717d); - _testLambertW0WadMonoAround(0x75ae9afa425919fe); - _testLambertW0WadMonoAround(0x7526092d05bef41f); - _testLambertW0WadMonoAround(0x52896fe82be03dfe); - _testLambertW0WadMonoAround(0x4f05b0ddf3b71a19); - _testLambertW0WadMonoAround(0x3094b0feb93943fd); - _testLambertW0WadMonoAround(0x2ef215ae6701c40e); - _testLambertW0WadMonoAround(0x2ebd1c82095d6a92); - _testLambertW0WadMonoAround(0x2e520a4e670d52bb); - _testLambertW0WadMonoAround(0xfc2f004412e5ce69); - _testLambertW0WadMonoAround(0x158bc0b201103a7fc); - _testLambertW0WadMonoAround(0x39280df60945c436b); - _testLambertW0WadMonoAround(0x47256e5d374b35f74); - _testLambertW0WadMonoAround(0x2b9568ffb08c155a4); - _testLambertW0WadMonoAround(0x1b60b07806956f34d); - _testLambertW0WadMonoAround(0x21902755d1eee824c); - _testLambertW0WadMonoAround(0x6e15c8a6ee6e4fca4); - _testLambertW0WadMonoAround(0x5b13067d92d8e49c6); - _testLambertW0WadMonoAround(0x2826ebc1fce90cf6e); - _testLambertW0WadMonoAround(0x215eb5aa1041510a4); - _testLambertW0WadMonoAround(0x47b20347b57504c32); - _testLambertW0WadMonoAround(0x75e8fd53f8c90f95a); - _testLambertW0WadMonoAround(0x43e8d80f9af282627); - _testLambertW0WadMonoAround(0x3cf555b5fd4f20615); - _testLambertW0WadMonoAround(0xaff4b8b52f8355e6e); - _testLambertW0WadMonoAround(0x529e89e77ae046255); - _testLambertW0WadMonoAround(0x1f0289433f07cbf53b); - _testLambertW0WadMonoAround(0xc1f6e56c2001d9432); - _testLambertW0WadMonoAround(0x5e4117305c6e33ebc); - _testLambertW0WadMonoAround(0x2b416472dce2ea26d); - _testLambertW0WadMonoAround(0x71f55956ef3326067); - _testLambertW0WadMonoAround(0x35d9d57c965eb82c6); - _testLambertW0WadMonoAround(0x184f520f19335f25d); - _testLambertW0WadMonoAround(0x3c4bb8f445abe21a7); - _testLambertW0WadMonoAround(0x573e3b3e06e208201); - _testLambertW0WadMonoAround(0x184f520f19335f25d); - _testLambertW0WadMonoAround(0x573e3b3e06e208201); - _testLambertW0WadMonoAround(0x61e511ba00db632a4); - _testLambertW0WadMonoAround(0x12731b97bde57933d); - _testLambertW0WadMonoAround(0x79c29b05cf39be374); - _testLambertW0WadMonoAround(0x390fcd4186ac250b3); - _testLambertW0WadMonoAround(0x69c74b5975fd4832a); - _testLambertW0WadMonoAround(0x59db219a7048121bd); - _testLambertW0WadMonoAround(0x28f2adc4fab331d251); - _testLambertW0WadMonoAround(0x7be91527cc31769c); - _testLambertW0WadMonoAround(0x2ef215ae6701c40f); - _testLambertW0WadMonoAround(0x1240541334cfadd81); - _testLambertW0WadMonoAround(0x2a79eccb3d5f4faaed); - _testLambertW0WadMonoAround(0x7470d50c23bfd30e0); - _testLambertW0WadMonoAround(0x313386f14a7f95af9); - _testLambertW0WadMonoAround(0x2a60f3b64c57088e9); - _testLambertW0WadMonoAround(0x381298f7aa53edfe0); - _testLambertW0WadMonoAround(0x5cbfac5d7a1770806); - _testLambertW0WadMonoAround(0x19e46d1b5e6aba57e); - _testLambertW0WadMonoAround(0x19ff86906ae47c70a); - _testLambertW0WadMonoAround(0x164684654d9ca54ea1); - _testLambertW0WadMonoAround(0x99337fa75e803139); - _testLambertW0WadMonoAround(0x6fa0a50fcb8a95b97e); - _testLambertW0WadMonoAround(0xa117a195e06c3fd531); - _testLambertW0WadMonoAround(0x305da7073093bd8a07); - _testLambertW0WadMonoAround(0x98582b07fd3c6b64); - _testLambertW0WadMonoAround(0x1e824d2a367d9ce65); - _testLambertW0WadMonoAround(0x7bea796d633b386a); - _testLambertW0WadMonoAround(0x2fff5c38c6b2a2cd); - _testLambertW0WadMonoAround(0x198af4e7ffee1df7627); - _testLambertW0WadMonoAround(0x8ea8a7b6f7c7424d8d); - _testLambertW0WadMonoAround(0x11e504fa805e54e2ed8); - _testLambertW0WadMonoAround(0x3e5f2a7801badcdabd); - _testLambertW0WadMonoAround(0x1b7aaad69ac8770a3be); - _testLambertW0WadMonoAround(0x658acb00d525f3d345); - _testLambertW0WadMonoAround(0xd994d6447146880183f); - _testLambertW0WadMonoAround(0x2e07a342d7b1bc1a5ae); - } - - function testLambertW0WadMonoDebug() public { - unchecked { - for (int256 i = -9; i <= 9; ++i) { - _testLambertW0WadMonoAround(0x2e07a342d7b1bc1a5ae + i); - } - } - } - - function _testLambertW0WadMonoAround(int256 x) internal { - emit LogInt("x", x); - emit LogUint("log2(x)", FixedPointMathLib.log2(uint256(x))); - testLambertW0WadMonotonicallyIncreasingAround(x); - } - - function testLambertW0WadMonotonicallyIncreasingAround2(uint96 t) public { - int256 x = int256(uint256(t)); - testLambertW0WadMonotonicallyIncreasingAround(x); - if (t & 0xff == 0xab) { - _testLambertW0WadMonoFocus(x, 0, 0x1ffffffffffff, 0xffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 1, 0x1fffffffffffff, 0xffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 2, 0xfffffffffffffff, 0xffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 3, 0xffffffffffffffff, 0xfffffffffffffffff); - _testLambertW0WadMonoFocus(x, 4, 0xffffffffffffffff, 0xfffffffffffffffff); - _testLambertW0WadMonoFocus(x, 5, 0xffffffffffffffff, 0xffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 6, 0xffffffffffffffff, 0xffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 7, 0xffffffffffffffff, 0xfffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 8, 0xffffffffffffffff, 0xfffffffffffffffffff); - _testLambertW0WadMonoFocus(x, 9, 0xffffffffffffffff, 0xffffffffffffffffffff); - } - } - - function _testLambertW0WadMonoFocus(int256 t, int256 i, int256 low, int256 mask) internal { - int256 x; - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, t) - mstore(0x20, i) - x := and(keccak256(0x00, 0x40), mask) - } - do { - testLambertW0WadMonotonicallyIncreasingAround(x); - x >>= 1; - } while (x >= low); - } - - function testLambertW0WadMonotonicallyIncreasingAround(int256 t) public { - if (t < _LAMBERT_W0_MIN) t = _boundLambertW0WadInput(t); - unchecked { - int256 end = t + 2; - for (int256 x = t - 2; x != end; ++x) { - testLambertW0WadMonotonicallyIncreasing(x, x + 1); - } - } - } - - function testLambertW0WadMonotonicallyIncreasing(int256 a, int256 b) public { - if (a < _LAMBERT_W0_MIN) a = _boundLambertW0WadInput(a); - if (b < _LAMBERT_W0_MIN) b = _boundLambertW0WadInput(b); - if (a > b) { - int256 t = b; - b = a; - a = t; - } - unchecked { - uint256 gasBefore = gasleft(); - int256 w0a = FixedPointMathLib.lambertW0Wad(a); - uint256 gasUsed = gasBefore - gasleft(); - int256 w0b = FixedPointMathLib.lambertW0Wad(b); - bool success = w0a <= w0b; - emit TestingLambertW0WadMonotonicallyIncreasing(a, b, w0a, w0b, success, gasUsed); - if (!success) { - emit LogUint("log2(a)", FixedPointMathLib.log2(uint256(a))); - emit LogUint("log2(b)", FixedPointMathLib.log2(uint256(b))); - emit LogUint("log2(w0a)", FixedPointMathLib.log2(uint256(w0a))); - emit LogUint("log2(w0b)", FixedPointMathLib.log2(uint256(w0b))); - assertTrue(success); - } - } - } - - function _boundLambertW0WadInput(int256 x) internal pure returns (int256 result) { - /// @solidity memory-safe-assembly - assembly { - result := shr(1, shl(1, not(x))) - } - } - - function testMulWad() public { - assertEq(FixedPointMathLib.mulWad(2.5e18, 0.5e18), 1.25e18); - assertEq(FixedPointMathLib.mulWad(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.mulWad(369, 271), 0); - } - - function testMulWadEdgeCases() public { - assertEq(FixedPointMathLib.mulWad(0, 1e18), 0); - assertEq(FixedPointMathLib.mulWad(1e18, 0), 0); - assertEq(FixedPointMathLib.mulWad(0, 0), 0); - } - - function testMulWadUp() public { - assertEq(FixedPointMathLib.mulWadUp(2.5e18, 0.5e18), 1.25e18); - assertEq(FixedPointMathLib.mulWadUp(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.mulWadUp(369, 271), 1); - } - - function testMulWadUpEdgeCases() public { - assertEq(FixedPointMathLib.mulWadUp(0, 1e18), 0); - assertEq(FixedPointMathLib.mulWadUp(1e18, 0), 0); - assertEq(FixedPointMathLib.mulWadUp(0, 0), 0); - } - - function testDivWad() public { - assertEq(FixedPointMathLib.divWad(1.25e18, 0.5e18), 2.5e18); - assertEq(FixedPointMathLib.divWad(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.divWad(2, 100000000000000e18), 0); - } - - function testDivWadEdgeCases() public { - assertEq(FixedPointMathLib.divWad(0, 1e18), 0); - } - - function testDivWadZeroDenominatorReverts() public { - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWad(1e18, 0); - } - - function testDivWadUp() public { - assertEq(FixedPointMathLib.divWadUp(1.25e18, 0.5e18), 2.5e18); - assertEq(FixedPointMathLib.divWadUp(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.divWadUp(2, 100000000000000e18), 1); - unchecked { - for (uint256 i; i < 10; ++i) { - assertEq(FixedPointMathLib.divWadUp(2, 100000000000000e18), 1); - } - } - } - - function testDivWadUpEdgeCases() public { - assertEq(FixedPointMathLib.divWadUp(0, 1e18), 0); - } - - function testDivWadUpZeroDenominatorReverts() public { - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWadUp(1e18, 0); - } - - function testMulDiv() public { - assertEq(FixedPointMathLib.mulDiv(2.5e27, 0.5e27, 1e27), 1.25e27); - assertEq(FixedPointMathLib.mulDiv(2.5e18, 0.5e18, 1e18), 1.25e18); - assertEq(FixedPointMathLib.mulDiv(2.5e8, 0.5e8, 1e8), 1.25e8); - assertEq(FixedPointMathLib.mulDiv(369, 271, 1e2), 999); - - assertEq(FixedPointMathLib.mulDiv(1e27, 1e27, 2e27), 0.5e27); - assertEq(FixedPointMathLib.mulDiv(1e18, 1e18, 2e18), 0.5e18); - assertEq(FixedPointMathLib.mulDiv(1e8, 1e8, 2e8), 0.5e8); - - assertEq(FixedPointMathLib.mulDiv(2e27, 3e27, 2e27), 3e27); - assertEq(FixedPointMathLib.mulDiv(3e18, 2e18, 3e18), 2e18); - assertEq(FixedPointMathLib.mulDiv(2e8, 3e8, 2e8), 3e8); - } - - function testMulDivEdgeCases() public { - assertEq(FixedPointMathLib.mulDiv(0, 1e18, 1e18), 0); - assertEq(FixedPointMathLib.mulDiv(1e18, 0, 1e18), 0); - assertEq(FixedPointMathLib.mulDiv(0, 0, 1e18), 0); - } - - function testMulDivZeroDenominatorReverts() public { - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDiv(1e18, 1e18, 0); - } - - function testMulDivUp() public { - assertEq(FixedPointMathLib.mulDivUp(2.5e27, 0.5e27, 1e27), 1.25e27); - assertEq(FixedPointMathLib.mulDivUp(2.5e18, 0.5e18, 1e18), 1.25e18); - assertEq(FixedPointMathLib.mulDivUp(2.5e8, 0.5e8, 1e8), 1.25e8); - assertEq(FixedPointMathLib.mulDivUp(369, 271, 1e2), 1000); - - assertEq(FixedPointMathLib.mulDivUp(1e27, 1e27, 2e27), 0.5e27); - assertEq(FixedPointMathLib.mulDivUp(1e18, 1e18, 2e18), 0.5e18); - assertEq(FixedPointMathLib.mulDivUp(1e8, 1e8, 2e8), 0.5e8); - - assertEq(FixedPointMathLib.mulDivUp(2e27, 3e27, 2e27), 3e27); - assertEq(FixedPointMathLib.mulDivUp(3e18, 2e18, 3e18), 2e18); - assertEq(FixedPointMathLib.mulDivUp(2e8, 3e8, 2e8), 3e8); - } - - function testMulDivUpEdgeCases() public { - assertEq(FixedPointMathLib.mulDivUp(0, 1e18, 1e18), 0); - assertEq(FixedPointMathLib.mulDivUp(1e18, 0, 1e18), 0); - assertEq(FixedPointMathLib.mulDivUp(0, 0, 1e18), 0); - } - - function testMulDivUpZeroDenominator() public { - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDivUp(1e18, 1e18, 0); - } - - function testLnWad() public { - assertEq(FixedPointMathLib.lnWad(1e18), 0); - - // Actual: 999999999999999999.8674576… - assertEq(FixedPointMathLib.lnWad(2718281828459045235), 999999999999999999); - - // Actual: 2461607324344817917.963296… - assertEq(FixedPointMathLib.lnWad(11723640096265400935), 2461607324344817918); - } - - function testLnWadSmall() public { - // Actual: -41446531673892822312.3238461… - assertEq(FixedPointMathLib.lnWad(1), -41446531673892822313); - - // Actual: -37708862055609454006.40601608… - assertEq(FixedPointMathLib.lnWad(42), -37708862055609454007); - - // Actual: -32236191301916639576.251880365581… - assertEq(FixedPointMathLib.lnWad(1e4), -32236191301916639577); - - // Actual: -20723265836946411156.161923092… - assertEq(FixedPointMathLib.lnWad(1e9), -20723265836946411157); - } - - function testLnWadBig() public { - // Actual: 135305999368893231589.070344787… - assertEq(FixedPointMathLib.lnWad(2 ** 255 - 1), 135305999368893231589); - - // Actual: 76388489021297880288.605614463571… - assertEq(FixedPointMathLib.lnWad(2 ** 170), 76388489021297880288); - - // Actual: 47276307437780177293.081865… - assertEq(FixedPointMathLib.lnWad(2 ** 128), 47276307437780177293); - } - - function testLnWadNegativeReverts() public { - vm.expectRevert(FixedPointMathLib.LnWadUndefined.selector); - FixedPointMathLib.lnWad(-1); - FixedPointMathLib.lnWad(-2 ** 255); - } - - function testLnWadOverflowReverts() public { - vm.expectRevert(FixedPointMathLib.LnWadUndefined.selector); - FixedPointMathLib.lnWad(0); - } - - function testRPow() public { - assertEq(FixedPointMathLib.rpow(0, 0, 0), 0); - assertEq(FixedPointMathLib.rpow(1, 0, 0), 0); - assertEq(FixedPointMathLib.rpow(0, 1, 0), 0); - assertEq(FixedPointMathLib.rpow(0, 0, 1), 1); - assertEq(FixedPointMathLib.rpow(1, 1, 0), 1); - assertEq(FixedPointMathLib.rpow(1, 1, 1), 1); - assertEq(FixedPointMathLib.rpow(2e27, 0, 1e27), 1e27); - assertEq(FixedPointMathLib.rpow(2e27, 2, 1e27), 4e27); - assertEq(FixedPointMathLib.rpow(2e18, 2, 1e18), 4e18); - assertEq(FixedPointMathLib.rpow(2e8, 2, 1e8), 4e8); - assertEq(FixedPointMathLib.rpow(8, 3, 1), 512); - } - - function testRPowOverflowReverts() public { - vm.expectRevert(FixedPointMathLib.RPowOverflow.selector); - FixedPointMathLib.rpow(2, type(uint128).max, 1); - FixedPointMathLib.rpow(type(uint128).max, 3, 1); - } - - function testSqrt() public { - assertEq(FixedPointMathLib.sqrt(0), 0); - assertEq(FixedPointMathLib.sqrt(1), 1); - assertEq(FixedPointMathLib.sqrt(2704), 52); - assertEq(FixedPointMathLib.sqrt(110889), 333); - assertEq(FixedPointMathLib.sqrt(32239684), 5678); - unchecked { - for (uint256 i = 100; i < 200; ++i) { - assertEq(FixedPointMathLib.sqrt(i * i), i); - } - } - } - - function testSqrtWad() public { - assertEq(FixedPointMathLib.sqrtWad(0), 0); - assertEq(FixedPointMathLib.sqrtWad(1), 10 ** 9); - assertEq(FixedPointMathLib.sqrtWad(2), 1414213562); - assertEq(FixedPointMathLib.sqrtWad(4), 2000000000); - assertEq(FixedPointMathLib.sqrtWad(8), 2828427124); - assertEq(FixedPointMathLib.sqrtWad(16), 4000000000); - assertEq(FixedPointMathLib.sqrtWad(32), 5656854249); - assertEq(FixedPointMathLib.sqrtWad(64), 8000000000); - assertEq(FixedPointMathLib.sqrtWad(10 ** 18), 10 ** 18); - assertEq(FixedPointMathLib.sqrtWad(4 * 10 ** 18), 2 * 10 ** 18); - assertEq(FixedPointMathLib.sqrtWad(type(uint8).max), 15968719422); - assertEq(FixedPointMathLib.sqrtWad(type(uint16).max), 255998046867); - assertEq(FixedPointMathLib.sqrtWad(type(uint32).max), 65535999992370); - assertEq(FixedPointMathLib.sqrtWad(type(uint64).max), 4294967295999999999); - assertEq(FixedPointMathLib.sqrtWad(type(uint128).max), 18446744073709551615999999999); - assertEq( - FixedPointMathLib.sqrtWad(type(uint256).max), - 340282366920938463463374607431768211455000000000 - ); - } - - function testCbrt() public { - assertEq(FixedPointMathLib.cbrt(0), 0); - assertEq(FixedPointMathLib.cbrt(1), 1); - assertEq(FixedPointMathLib.cbrt(2), 1); - assertEq(FixedPointMathLib.cbrt(3), 1); - assertEq(FixedPointMathLib.cbrt(9), 2); - assertEq(FixedPointMathLib.cbrt(27), 3); - assertEq(FixedPointMathLib.cbrt(80), 4); - assertEq(FixedPointMathLib.cbrt(81), 4); - assertEq(FixedPointMathLib.cbrt(10 ** 18), 10 ** 6); - assertEq(FixedPointMathLib.cbrt(8 * 10 ** 18), 2 * 10 ** 6); - assertEq(FixedPointMathLib.cbrt(9 * 10 ** 18), 2080083); - assertEq(FixedPointMathLib.cbrt(type(uint8).max), 6); - assertEq(FixedPointMathLib.cbrt(type(uint16).max), 40); - assertEq(FixedPointMathLib.cbrt(type(uint32).max), 1625); - assertEq(FixedPointMathLib.cbrt(type(uint64).max), 2642245); - assertEq(FixedPointMathLib.cbrt(type(uint128).max), 6981463658331); - assertEq(FixedPointMathLib.cbrt(type(uint256).max), 48740834812604276470692694); - } - - function testCbrtWad() public { - assertEq(FixedPointMathLib.cbrtWad(0), 0); - assertEq(FixedPointMathLib.cbrtWad(1), 10 ** 12); - assertEq(FixedPointMathLib.cbrtWad(2), 1259921049894); - assertEq(FixedPointMathLib.cbrtWad(3), 1442249570307); - assertEq(FixedPointMathLib.cbrtWad(9), 2080083823051); - assertEq(FixedPointMathLib.cbrtWad(27), 3000000000000); - assertEq(FixedPointMathLib.cbrtWad(80), 4308869380063); - assertEq(FixedPointMathLib.cbrtWad(81), 4326748710922); - assertEq(FixedPointMathLib.cbrtWad(10 ** 18), 10 ** 18); - assertEq(FixedPointMathLib.cbrtWad(8 * 10 ** 18), 2 * 10 ** 18); - assertEq(FixedPointMathLib.cbrtWad(9 * 10 ** 18), 2080083823051904114); - assertEq(FixedPointMathLib.cbrtWad(type(uint8).max), 6341325705384); - assertEq(FixedPointMathLib.cbrtWad(type(uint16).max), 40317268530317); - assertEq(FixedPointMathLib.cbrtWad(type(uint32).max), 1625498677089280); - assertEq(FixedPointMathLib.cbrtWad(type(uint64).max), 2642245949629133047); - assertEq(FixedPointMathLib.cbrtWad(type(uint128).max), 6981463658331559092288464); - assertEq( - FixedPointMathLib.cbrtWad(type(uint256).max), 48740834812604276470692694000000000000 - ); - } - - function testLog2() public { - assertEq(FixedPointMathLib.log2(0), 0); - assertEq(FixedPointMathLib.log2(2), 1); - assertEq(FixedPointMathLib.log2(4), 2); - assertEq(FixedPointMathLib.log2(1024), 10); - assertEq(FixedPointMathLib.log2(1048576), 20); - assertEq(FixedPointMathLib.log2(1073741824), 30); - for (uint256 i = 1; i < 255; i++) { - assertEq(FixedPointMathLib.log2((1 << i) - 1), i - 1); - assertEq(FixedPointMathLib.log2((1 << i)), i); - assertEq(FixedPointMathLib.log2((1 << i) + 1), i); - } - } - - function testLog2Differential(uint256 x) public { - assertEq(FixedPointMathLib.log2(x), _log2Original(x)); - } - - function _log2Original(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - unchecked { - if (value >> 128 > 0) { - value >>= 128; - result += 128; - } - if (value >> 64 > 0) { - value >>= 64; - result += 64; - } - if (value >> 32 > 0) { - value >>= 32; - result += 32; - } - if (value >> 16 > 0) { - value >>= 16; - result += 16; - } - if (value >> 8 > 0) { - value >>= 8; - result += 8; - } - if (value >> 4 > 0) { - value >>= 4; - result += 4; - } - if (value >> 2 > 0) { - value >>= 2; - result += 2; - } - if (value >> 1 > 0) { - result += 1; - } - } - return result; - } - - function testLog2Up() public { - assertEq(FixedPointMathLib.log2Up(0), 0); - assertEq(FixedPointMathLib.log2Up(1), 0); - assertEq(FixedPointMathLib.log2Up(2), 1); - assertEq(FixedPointMathLib.log2Up(2 + 1), 2); - assertEq(FixedPointMathLib.log2Up(4), 2); - assertEq(FixedPointMathLib.log2Up(4 + 1), 3); - assertEq(FixedPointMathLib.log2Up(4 + 2), 3); - assertEq(FixedPointMathLib.log2Up(1024), 10); - assertEq(FixedPointMathLib.log2Up(1024 + 1), 11); - assertEq(FixedPointMathLib.log2Up(1048576), 20); - assertEq(FixedPointMathLib.log2Up(1048576 + 1), 21); - assertEq(FixedPointMathLib.log2Up(1073741824), 30); - assertEq(FixedPointMathLib.log2Up(1073741824 + 1), 31); - for (uint256 i = 2; i < 255; i++) { - assertEq(FixedPointMathLib.log2Up((1 << i) - 1), i); - assertEq(FixedPointMathLib.log2Up((1 << i)), i); - assertEq(FixedPointMathLib.log2Up((1 << i) + 1), i + 1); - } - } - - function testAvg() public { - assertEq(FixedPointMathLib.avg(uint256(5), uint256(6)), uint256(5)); - assertEq(FixedPointMathLib.avg(uint256(0), uint256(1)), uint256(0)); - assertEq(FixedPointMathLib.avg(uint256(45645465), uint256(4846513)), uint256(25245989)); - } - - function testAvgSigned() public { - assertEq(FixedPointMathLib.avg(int256(5), int256(6)), int256(5)); - assertEq(FixedPointMathLib.avg(int256(0), int256(1)), int256(0)); - assertEq(FixedPointMathLib.avg(int256(45645465), int256(4846513)), int256(25245989)); - - assertEq(FixedPointMathLib.avg(int256(5), int256(-6)), int256(-1)); - assertEq(FixedPointMathLib.avg(int256(0), int256(-1)), int256(-1)); - assertEq(FixedPointMathLib.avg(int256(45645465), int256(-4846513)), int256(20399476)); - } - - function testAvgEdgeCase() public { - assertEq(FixedPointMathLib.avg(uint256(2 ** 256 - 1), uint256(1)), uint256(2 ** 255)); - assertEq(FixedPointMathLib.avg(uint256(2 ** 256 - 1), uint256(10)), uint256(2 ** 255 + 4)); - assertEq( - FixedPointMathLib.avg(uint256(2 ** 256 - 1), uint256(2 ** 256 - 1)), - uint256(2 ** 256 - 1) - ); - } - - function testAbs() public { - assertEq(FixedPointMathLib.abs(0), 0); - assertEq(FixedPointMathLib.abs(-5), 5); - assertEq(FixedPointMathLib.abs(5), 5); - assertEq(FixedPointMathLib.abs(-1155656654), 1155656654); - assertEq(FixedPointMathLib.abs(621356166516546561651), 621356166516546561651); - } - - function testDist() public { - assertEq(FixedPointMathLib.dist(0, 0), 0); - assertEq(FixedPointMathLib.dist(-5, -4), 1); - assertEq(FixedPointMathLib.dist(5, 46), 41); - assertEq(FixedPointMathLib.dist(46, 5), 41); - assertEq(FixedPointMathLib.dist(-1155656654, 6544844), 1162201498); - assertEq(FixedPointMathLib.dist(-848877, -8447631456), 8446782579); - } - - function testDistEdgeCases() public { - assertEq(FixedPointMathLib.dist(type(int256).min, type(int256).max), type(uint256).max); - assertEq( - FixedPointMathLib.dist(type(int256).min, 0), - 0x8000000000000000000000000000000000000000000000000000000000000000 - ); - assertEq( - FixedPointMathLib.dist(type(int256).max, 5), - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa - ); - assertEq( - FixedPointMathLib.dist(type(int256).min, -5), - 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb - ); - } - - function testAbsEdgeCases() public { - assertEq(FixedPointMathLib.abs(-(2 ** 255 - 1)), (2 ** 255 - 1)); - assertEq(FixedPointMathLib.abs((2 ** 255 - 1)), (2 ** 255 - 1)); - } - - function testGcd() public { - assertEq(FixedPointMathLib.gcd(0, 0), 0); - assertEq(FixedPointMathLib.gcd(85, 0), 85); - assertEq(FixedPointMathLib.gcd(0, 2), 2); - assertEq(FixedPointMathLib.gcd(56, 45), 1); - assertEq(FixedPointMathLib.gcd(12, 28), 4); - assertEq(FixedPointMathLib.gcd(12, 1), 1); - assertEq(FixedPointMathLib.gcd(486516589451122, 48656), 2); - assertEq(FixedPointMathLib.gcd(2 ** 254 - 4, 2 ** 128 - 1), 15); - assertEq(FixedPointMathLib.gcd(3, 26017198113384995722614372765093167890), 1); - unchecked { - for (uint256 i = 2; i < 10; ++i) { - assertEq(FixedPointMathLib.gcd(31 * (1 << i), 31), 31); - } - } - } - - function testFullMulDiv() public { - assertEq(FixedPointMathLib.fullMulDiv(0, 0, 1), 0); - assertEq(FixedPointMathLib.fullMulDiv(4, 4, 2), 8); - assertEq(FixedPointMathLib.fullMulDiv(2 ** 200, 2 ** 200, 2 ** 200), 2 ** 200); - } - - function testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase1() public { - vm.expectRevert(FixedPointMathLib.FullMulDivFailed.selector); - FixedPointMathLib.fullMulDivUp( - 535006138814359, 432862656469423142931042426214547535783388063929571229938474969, 2 - ); - } - - function testFullMulDivUpRevertsIfRoundedUpResultOverflowsCase2() public { - vm.expectRevert(FixedPointMathLib.FullMulDivFailed.selector); - FixedPointMathLib.fullMulDivUp( - 115792089237316195423570985008687907853269984659341747863450311749907997002549, - 115792089237316195423570985008687907853269984659341747863450311749907997002550, - 115792089237316195423570985008687907853269984653042931687443039491902864365164 - ); - } - - function testFullMulDiv(uint256 a, uint256 b, uint256 d) public returns (uint256 result) { - if (d == 0) { - vm.expectRevert(FixedPointMathLib.FullMulDivFailed.selector); - FixedPointMathLib.fullMulDiv(a, b, d); - return 0; - } - - // Compute a * b in Chinese Remainder Basis - uint256 expectedA; - uint256 expectedB; - unchecked { - expectedA = a * b; - expectedB = mulmod(a, b, 2 ** 256 - 1); - } - - // Construct a * b - uint256 prod0; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(a, b, not(0)) - prod0 := mul(a, b) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } - if (prod1 >= d) { - vm.expectRevert(FixedPointMathLib.FullMulDivFailed.selector); - FixedPointMathLib.fullMulDiv(a, b, d); - return 0; - } - - uint256 q = FixedPointMathLib.fullMulDiv(a, b, d); - uint256 r = mulmod(a, b, d); - - // Compute q * d + r in Chinese Remainder Basis - uint256 actualA; - uint256 actualB; - unchecked { - actualA = q * d + r; - actualB = addmod(mulmod(q, d, 2 ** 256 - 1), r, 2 ** 256 - 1); - } - - assertEq(actualA, expectedA); - assertEq(actualB, expectedB); - return q; - } - - function testFullMulDivUp(uint256 a, uint256 b, uint256 d) public { - uint256 fullMulDivResult = testFullMulDiv(a, b, d); - if (fullMulDivResult != 0) { - uint256 expectedResult = fullMulDivResult; - if (mulmod(a, b, d) > 0) { - if (!(fullMulDivResult < type(uint256).max)) { - vm.expectRevert(FixedPointMathLib.FullMulDivFailed.selector); - FixedPointMathLib.fullMulDivUp(a, b, d); - return; - } - expectedResult++; - } - assertEq(FixedPointMathLib.fullMulDivUp(a, b, d), expectedResult); - } - } - - function testMulWad(uint256 x, uint256 y) public { - // Ignore cases where x * y overflows. - unchecked { - if (x != 0 && (x * y) / x != y) return; - } - - assertEq(FixedPointMathLib.mulWad(x, y), (x * y) / 1e18); - } - - function testMulWadOverflowReverts(uint256 x, uint256 y) public { - // Ignore cases where x * y does not overflow. - unchecked { - vm.assume(x != 0 && (x * y) / x != y); - } - vm.expectRevert(FixedPointMathLib.MulWadFailed.selector); - FixedPointMathLib.mulWad(x, y); - } - - function testMulWadUp(uint256 x, uint256 y) public { - // Ignore cases where x * y overflows. - unchecked { - if (x != 0 && (x * y) / x != y) return; - } - - assertEq(FixedPointMathLib.mulWadUp(x, y), x * y == 0 ? 0 : (x * y - 1) / 1e18 + 1); - } - - function testMulWadUpOverflowReverts(uint256 x, uint256 y) public { - // Ignore cases where x * y does not overflow. - unchecked { - vm.assume(x != 0 && !((x * y) / x == y)); - } - vm.expectRevert(FixedPointMathLib.MulWadFailed.selector); - FixedPointMathLib.mulWadUp(x, y); - } - - function testDivWad(uint256 x, uint256 y) public { - // Ignore cases where x * WAD overflows or y is 0. - unchecked { - if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; - } - - assertEq(FixedPointMathLib.divWad(x, y), (x * 1e18) / y); - } - - function testDivWadOverflowReverts(uint256 x, uint256 y) public { - // Ignore cases where x * WAD does not overflow or y is 0. - unchecked { - vm.assume(y != 0 && (x * 1e18) / 1e18 != x); - } - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWad(x, y); - } - - function testDivWadZeroDenominatorReverts(uint256 x) public { - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWad(x, 0); - } - - function testDivWadUp(uint256 x, uint256 y) public { - // Ignore cases where x * WAD overflows or y is 0. - unchecked { - if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; - } - - assertEq(FixedPointMathLib.divWadUp(x, y), x == 0 ? 0 : (x * 1e18 - 1) / y + 1); - } - - function testDivWadUpOverflowReverts(uint256 x, uint256 y) public { - // Ignore cases where x * WAD does not overflow or y is 0. - unchecked { - vm.assume(y != 0 && (x * 1e18) / 1e18 != x); - } - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWadUp(x, y); - } - - function testDivWadUpZeroDenominatorReverts(uint256 x) public { - vm.expectRevert(FixedPointMathLib.DivWadFailed.selector); - FixedPointMathLib.divWadUp(x, 0); - } - - function testMulDiv(uint256 x, uint256 y, uint256 denominator) public { - // Ignore cases where x * y overflows or denominator is 0. - unchecked { - if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; - } - - assertEq(FixedPointMathLib.mulDiv(x, y, denominator), (x * y) / denominator); - } - - function testMulDivOverflowReverts(uint256 x, uint256 y, uint256 denominator) public { - // Ignore cases where x * y does not overflow or denominator is 0. - unchecked { - vm.assume(denominator != 0 && x != 0 && (x * y) / x != y); - } - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDiv(x, y, denominator); - } - - function testMulDivZeroDenominatorReverts(uint256 x, uint256 y) public { - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDiv(x, y, 0); - } - - function testMulDivUp(uint256 x, uint256 y, uint256 denominator) public { - // Ignore cases where x * y overflows or denominator is 0. - unchecked { - if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; - } - - assertEq( - FixedPointMathLib.mulDivUp(x, y, denominator), - x * y == 0 ? 0 : (x * y - 1) / denominator + 1 - ); - } - - function testMulDivUpOverflowReverts(uint256 x, uint256 y, uint256 denominator) public { - // Ignore cases where x * y does not overflow or denominator is 0. - unchecked { - vm.assume(denominator != 0 && x != 0 && (x * y) / x != y); - } - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDivUp(x, y, denominator); - } - - function testMulDivUpZeroDenominatorReverts(uint256 x, uint256 y) public { - vm.expectRevert(FixedPointMathLib.MulDivFailed.selector); - FixedPointMathLib.mulDivUp(x, y, 0); - } - - function testCbrt(uint256 x) public { - uint256 root = FixedPointMathLib.cbrt(x); - uint256 next = root + 1; - - // Ignore cases where `next * next * next` or `next * next` overflows. - unchecked { - if (next * next * next < next * next) return; - if (next * next < next) return; - } - - assertTrue(root * root * root <= x && next * next * next > x); - } - - function testCbrtWad(uint256 x) public { - uint256 result = FixedPointMathLib.cbrtWad(x); - uint256 floor = FixedPointMathLib.cbrt(x); - assertTrue(result >= floor * 10 ** 12 && result <= (floor + 1) * 10 ** 12); - assertEq(result / 10 ** 12, floor); - } - - function testCbrtBack(uint256 x) public { - unchecked { - x = _bound(x, 0, 48740834812604276470692694); - while (x != 0) { - assertEq(FixedPointMathLib.cbrt(x * x * x), x); - x >>= 1; - } - } - } - - function testSqrt(uint256 x) public { - uint256 root = FixedPointMathLib.sqrt(x); - uint256 next = root + 1; - - // Ignore cases where `next * next` overflows. - unchecked { - if (next * next < next) return; - } - - assertTrue(root * root <= x && next * next > x); - } - - function testSqrtWad(uint256 x) public { - uint256 result = FixedPointMathLib.sqrtWad(x); - uint256 floor = FixedPointMathLib.sqrt(x); - assertTrue(result >= floor * 10 ** 9 && result <= (floor + 1) * 10 ** 9); - assertEq(result / 10 ** 9, floor); - } - - function testSqrtBack(uint256 x) public { - unchecked { - x >>= 128; - while (x != 0) { - assertEq(FixedPointMathLib.sqrt(x * x), x); - x >>= 1; - } - } - } - - function testSqrtHashed(uint256 x) public { - testSqrtBack(uint256(keccak256(abi.encode(x)))); - } - - function testSqrtHashedSingle() public { - testSqrtHashed(123); - } - - function testMin(uint256 x, uint256 y) public { - uint256 z = x < y ? x : y; - assertEq(FixedPointMathLib.min(x, y), z); - } - - function testMinBrutalized(uint256 x, uint256 y) public { - uint32 xCasted; - uint32 yCasted; - /// @solidity memory-safe-assembly - assembly { - xCasted := x - yCasted := y - } - uint256 expected = xCasted < yCasted ? xCasted : yCasted; - assertEq(FixedPointMathLib.min(xCasted, yCasted), expected); - assertEq(FixedPointMathLib.min(uint32(x), uint32(y)), expected); - expected = uint32(x) < uint32(y) ? uint32(x) : uint32(y); - assertEq(FixedPointMathLib.min(xCasted, yCasted), expected); - } - - function testMinSigned(int256 x, int256 y) public { - int256 z = x < y ? x : y; - assertEq(FixedPointMathLib.min(x, y), z); - } - - function testMax(uint256 x, uint256 y) public { - uint256 z = x > y ? x : y; - assertEq(FixedPointMathLib.max(x, y), z); - } - - function testMaxSigned(int256 x, int256 y) public { - int256 z = x > y ? x : y; - assertEq(FixedPointMathLib.max(x, y), z); - } - - function testMaxCasted(uint32 x, uint32 y, uint256 brutalizer) public { - uint32 z = x > y ? x : y; - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, brutalizer) - mstore(0x20, 1) - x := or(shl(32, keccak256(0x00, 0x40)), x) - mstore(0x20, 2) - y := or(shl(32, keccak256(0x00, 0x40)), y) - } - assertTrue(FixedPointMathLib.max(x, y) == z); - } - - function testZeroFloorSub(uint256 x, uint256 y) public { - uint256 z = x > y ? x - y : 0; - assertEq(FixedPointMathLib.zeroFloorSub(x, y), z); - } - - function testZeroFloorSubCasted(uint32 x, uint32 y, uint256 brutalizer) public { - uint256 z = x > y ? x - y : 0; - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, brutalizer) - mstore(0x20, 1) - x := or(shl(32, keccak256(0x00, 0x40)), x) - mstore(0x20, 2) - y := or(shl(32, keccak256(0x00, 0x40)), y) - } - assertTrue(FixedPointMathLib.zeroFloorSub(x, y) == z); - } - - function testDist(int256 x, int256 y) public { - uint256 z; - unchecked { - if (x > y) { - z = uint256(x - y); - } else { - z = uint256(y - x); - } - } - assertEq(FixedPointMathLib.dist(x, y), z); - } - - function testAbs(int256 x) public { - uint256 z = uint256(x); - if (x < 0) { - if (x == type(int256).min) { - z = uint256(type(int256).max) + 1; - } else { - z = uint256(-x); - } - } - assertEq(FixedPointMathLib.abs(x), z); - } - - function testGcd(uint256 x, uint256 y) public { - assertEq(FixedPointMathLib.gcd(x, y), _gcd(x, y)); - } - - function testClamp(uint256 x, uint256 minValue, uint256 maxValue) public { - uint256 clamped = x; - if (clamped < minValue) { - clamped = minValue; - } - if (clamped > maxValue) { - clamped = maxValue; - } - assertEq(FixedPointMathLib.clamp(x, minValue, maxValue), clamped); - } - - function testClampSigned(int256 x, int256 minValue, int256 maxValue) public { - int256 clamped = x; - if (clamped < minValue) { - clamped = minValue; - } - if (clamped > maxValue) { - clamped = maxValue; - } - assertEq(FixedPointMathLib.clamp(x, minValue, maxValue), clamped); - } - - function testFactorial() public { - uint256 result = 1; - assertEq(FixedPointMathLib.factorial(0), result); - unchecked { - for (uint256 i = 1; i != 58; ++i) { - result = result * i; - assertEq(FixedPointMathLib.factorial(i), result); - } - } - vm.expectRevert(FixedPointMathLib.FactorialOverflow.selector); - FixedPointMathLib.factorial(58); - } - - function testFactorialOriginal() public { - uint256 result = 1; - assertEq(_factorialOriginal(0), result); - unchecked { - for (uint256 i = 1; i != 58; ++i) { - result = result * i; - assertEq(_factorialOriginal(i), result); - } - } - } - - function _factorialOriginal(uint256 x) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - result := 1 - for {} x {} { - result := mul(result, x) - x := sub(x, 1) - } - } - } - - function _gcd(uint256 x, uint256 y) internal pure returns (uint256 result) { - if (y == 0) { - return x; - } else { - return _gcd(y, x % y); - } - } - - function testRawAdd(uint256 x, uint256 y) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := add(x, y) - } - assertEq(FixedPointMathLib.rawAdd(x, y), z); - } - - function testRawAdd(int256 x, int256 y) public { - int256 z; - /// @solidity memory-safe-assembly - assembly { - z := add(x, y) - } - assertEq(FixedPointMathLib.rawAdd(x, y), z); - } - - function testRawSub(uint256 x, uint256 y) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := sub(x, y) - } - assertEq(FixedPointMathLib.rawSub(x, y), z); - } - - function testRawSub(int256 x, int256 y) public { - int256 z; - /// @solidity memory-safe-assembly - assembly { - z := sub(x, y) - } - assertEq(FixedPointMathLib.rawSub(x, y), z); - } - - function testRawMul(uint256 x, uint256 y) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := mul(x, y) - } - assertEq(FixedPointMathLib.rawMul(x, y), z); - } - - function testRawMul(int256 x, int256 y) public { - int256 z; - /// @solidity memory-safe-assembly - assembly { - z := mul(x, y) - } - assertEq(FixedPointMathLib.rawMul(x, y), z); - } - - function testRawDiv(uint256 x, uint256 y) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := div(x, y) - } - assertEq(FixedPointMathLib.rawDiv(x, y), z); - } - - function testRawSDiv(int256 x, int256 y) public { - int256 z; - /// @solidity memory-safe-assembly - assembly { - z := sdiv(x, y) - } - assertEq(FixedPointMathLib.rawSDiv(x, y), z); - } - - function testRawMod(uint256 x, uint256 y) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := mod(x, y) - } - assertEq(FixedPointMathLib.rawMod(x, y), z); - } - - function testRawSMod(int256 x, int256 y) public { - int256 z; - /// @solidity memory-safe-assembly - assembly { - z := smod(x, y) - } - assertEq(FixedPointMathLib.rawSMod(x, y), z); - } - - function testRawAddMod(uint256 x, uint256 y, uint256 denominator) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := addmod(x, y, denominator) - } - assertEq(FixedPointMathLib.rawAddMod(x, y, denominator), z); - } - - function testRawMulMod(uint256 x, uint256 y, uint256 denominator) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := mulmod(x, y, denominator) - } - assertEq(FixedPointMathLib.rawMulMod(x, y, denominator), z); - } - - function testLog10() public { - assertEq(FixedPointMathLib.log10(0), 0); - assertEq(FixedPointMathLib.log10(1), 0); - assertEq(FixedPointMathLib.log10(type(uint256).max), 77); - unchecked { - for (uint256 i = 1; i <= 77; ++i) { - uint256 x = 10 ** i; - assertEq(FixedPointMathLib.log10(x), i); - assertEq(FixedPointMathLib.log10(x - 1), i - 1); - assertEq(FixedPointMathLib.log10(x + 1), i); - } - } - } - - function testLog10(uint256 i, uint256 j) public { - i = _bound(i, 0, 77); - uint256 low = 10 ** i; - uint256 high = i == 77 ? type(uint256).max : (10 ** (i + 1)) - 1; - uint256 x = _bound(j, low, high); - assertEq(FixedPointMathLib.log10(x), i); - } - - function testLog10Up() public { - assertEq(FixedPointMathLib.log10Up(0), 0); - assertEq(FixedPointMathLib.log10Up(1), 0); - assertEq(FixedPointMathLib.log10Up(9), 1); - assertEq(FixedPointMathLib.log10Up(10), 1); - assertEq(FixedPointMathLib.log10Up(99), 2); - assertEq(FixedPointMathLib.log10Up(100), 2); - assertEq(FixedPointMathLib.log10Up(999), 3); - assertEq(FixedPointMathLib.log10Up(1000), 3); - assertEq(FixedPointMathLib.log10Up(10 ** 77), 77); - assertEq(FixedPointMathLib.log10Up(10 ** 77 + 1), 78); - assertEq(FixedPointMathLib.log10Up(type(uint256).max), 78); - } - - function testLog256() public { - assertEq(FixedPointMathLib.log256(0), 0); - assertEq(FixedPointMathLib.log256(1), 0); - assertEq(FixedPointMathLib.log256(256), 1); - assertEq(FixedPointMathLib.log256(type(uint256).max), 31); - unchecked { - for (uint256 i = 1; i <= 31; ++i) { - uint256 x = 256 ** i; - assertEq(FixedPointMathLib.log256(x), i); - assertEq(FixedPointMathLib.log256(x - 1), i - 1); - assertEq(FixedPointMathLib.log256(x + 1), i); - } - } - } - - function testLog256(uint256 i, uint256 j) public { - i = _bound(i, 0, 31); - uint256 low = 256 ** i; - uint256 high = i == 31 ? type(uint256).max : (256 ** (i + 1)) - 1; - uint256 x = _bound(j, low, high); - assertEq(FixedPointMathLib.log256(x), i); - } - - function testLog256Up() public { - assertEq(FixedPointMathLib.log256Up(0), 0); - assertEq(FixedPointMathLib.log256Up(0x01), 0); - assertEq(FixedPointMathLib.log256Up(0x02), 1); - assertEq(FixedPointMathLib.log256Up(0xff), 1); - assertEq(FixedPointMathLib.log256Up(0x0100), 1); - assertEq(FixedPointMathLib.log256Up(0x0101), 2); - assertEq(FixedPointMathLib.log256Up(0xffff), 2); - assertEq(FixedPointMathLib.log256Up(0x010000), 2); - assertEq(FixedPointMathLib.log256Up(0x010001), 3); - assertEq(FixedPointMathLib.log256Up(type(uint256).max - 1), 32); - assertEq(FixedPointMathLib.log256Up(type(uint256).max), 32); - } - - function testSci() public { - _testSci(0, 0, 0); - _testSci(1, 1, 0); - _testSci(13, 13, 0); - _testSci(130, 13, 1); - _testSci(1300, 13, 2); - unchecked { - uint256 a = 103; - uint256 exponent = 0; - uint256 m = 1; - uint256 n = 78 - FixedPointMathLib.log10Up(a); - for (uint256 i; i < n; ++i) { - _testSci(a * m, a, exponent); - exponent += 1; - m *= 10; - } - } - _testSci(10 ** 77, 1, 77); - _testSci(2 * (10 ** 76), 2, 76); - _testSci(9 * (10 ** 76), 9, 76); - unchecked { - for (uint256 i; i < 32; ++i) { - testSci(11 + i * i * 100); - } - for (uint256 i; i < 500; ++i) { - _testSci(0, 0, 0); - } - } - unchecked { - uint256 x = 30000000000000000000000000000000000000000000000001; - _testSci(x, x, 0); - } - } - - function testSci(uint256 a) public { - unchecked { - while (a % 10 == 0) a = _random(); - uint256 exponent = 0; - uint256 m = 1; - uint256 n = 78 - FixedPointMathLib.log10Up(a); - for (uint256 i; i < n; ++i) { - _testSci(a * m, a, exponent); - uint256 x = a * 10 ** exponent; - assertEq(x, a * m); - exponent += 1; - m *= 10; - } - } - } - - function testSci2(uint256 x) public { - unchecked { - (uint256 mantissa, uint256 exponent) = FixedPointMathLib.sci(x); - assertEq(x % 10 ** exponent, 0); - if (x != 0) { - assertTrue(x % 10 ** (exponent + 1) > 0); - assertTrue(mantissa % 10 != 0); - } else { - assertEq(mantissa, 0); - assertEq(exponent, 0); - } - } - } - - function _testSci(uint256 x, uint256 expectedMantissa, uint256 expectedExponent) internal { - (uint256 mantissa, uint256 exponent) = FixedPointMathLib.sci(x); - assertEq(mantissa, expectedMantissa); - assertEq(exponent, expectedExponent); - } - - function testPackUnpackSci(uint256) public { - unchecked { - uint256 x = (_random() & 0x1) * 10 ** (_random() % 70); - uint8 packed = uint8(FixedPointMathLib.packSci(x)); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - unchecked { - uint256 x = (_random() & 0x1ff) * 10 ** (_random() % 70); - uint16 packed = uint16(FixedPointMathLib.packSci(x)); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - unchecked { - uint256 x = (_random() & 0x1ffffff) * 10 ** (_random() % 70); - uint32 packed = uint32(FixedPointMathLib.packSci(x)); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - unchecked { - uint256 x = (_random() & 0x1ffffffffffffff) * 10 ** (_random() % 60); - uint64 packed = uint64(FixedPointMathLib.packSci(x)); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - unchecked { - uint256 x = (_random() * 10 ** (_random() % 78)) & ((1 << 249) - 1); - uint256 packed = FixedPointMathLib.packSci(x); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - } - - function testPackUnpackSci() public { - uint256 mantissaSize = 249; - unchecked { - for (uint256 i; i <= mantissaSize; ++i) { - uint256 x = (1 << i) - 1; - uint256 packed = FixedPointMathLib.packSci(x); - uint256 unpacked = FixedPointMathLib.unpackSci(packed); - assertEq(unpacked, x); - } - } - unchecked { - uint256 x = (1 << (mantissaSize + 1)) - 1; - vm.expectRevert(FixedPointMathLib.MantissaOverflow.selector); - FixedPointMathLib.packSci(x); - } - } -} diff --git a/lib/solady/test/GasBurnerLib.t.sol b/lib/solady/test/GasBurnerLib.t.sol deleted file mode 100644 index aa92057..0000000 --- a/lib/solady/test/GasBurnerLib.t.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {GasBurnerLib} from "../src/utils/GasBurnerLib.sol"; - -contract GasBurnerLibTest is SoladyTest { - event LogGasBurn(uint256 required, uint256 actual); - - function testBurnGas() public { - _testBurnGas(0); - _testBurnGas(1); - _testBurnGas(110); - _testBurnGas(119); - _testBurnGas(120); - _testBurnGas(121); - _testBurnGas(300); - for (uint256 x = 300; x < 9000; x += 32) { - _testBurnGas(x); - } - } - - function _testBurnGas(uint256 x) internal { - unchecked { - uint256 gasBefore = gasleft(); - GasBurnerLib.burn(x); - uint256 gasAfter = gasleft(); - emit LogGasBurn(x, gasBefore - gasAfter); - } - } -} diff --git a/lib/solady/test/JSONParserLib.t.sol b/lib/solady/test/JSONParserLib.t.sol deleted file mode 100644 index e6adf97..0000000 --- a/lib/solady/test/JSONParserLib.t.sol +++ /dev/null @@ -1,788 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {JSONParserLib} from "../src/utils/JSONParserLib.sol"; -import {LibString} from "../src/utils/LibString.sol"; -import {Base64} from "../src/utils/Base64.sol"; - -contract JSONParserLibTest is SoladyTest { - using JSONParserLib for *; - - function testParseInvalidReverts() public { - _checkParseReverts(""); - _checkParseReverts("e"); - _checkParseReverts("abc"); - _checkParseReverts("1,2"); - _checkParseReverts("["); - _checkParseReverts("]"); - _checkParseReverts("{"); - _checkParseReverts("}"); - _checkParseReverts("[[]"); - _checkParseReverts("[]["); - _checkParseReverts("[][]"); - _checkParseReverts("[],[]"); - _checkParseReverts("[1,2"); - _checkParseReverts("1,2]"); - _checkParseReverts("[1"); - _checkParseReverts("1]"); - _checkParseReverts("[1,"); - _checkParseReverts("{}{"); - _checkParseReverts("{}{}"); - _checkParseReverts("{},{}"); - _checkParseReverts("{]"); - _checkParseReverts("{{}"); - _checkParseReverts("{}}"); - _checkParseReverts("[,]"); - _checkParseReverts("[0,]"); - _checkParseReverts("[0,1,]"); - _checkParseReverts("[0,,]"); - _checkParseReverts("[0,,1]"); - _checkParseReverts("[,0]"); - _checkParseReverts("{,}"); - _checkParseReverts('{"a"}'); - _checkParseReverts('{"a":"A",}'); - _checkParseReverts('{"a":"A","b":"B",}'); - _checkParseReverts('{"a":"A"b":"B"}'); - _checkParseReverts('{"a":"A",,"b":"B"}'); - _checkParseReverts('{,"a":"A","b":"B"}'); - _checkParseReverts('{"a"::"A"}'); - _checkParseReverts('{"a","A"}'); - _checkParseReverts("{1}"); - _checkParseReverts("{:}"); - } - - function testParseInvalidNumberReverts() public { - _checkParseReverts("01234567890"); - _checkParseReverts("-1.234567890e-a"); - _checkParseReverts("-1.234567890e-"); - _checkParseReverts("-1.234567890e+a"); - _checkParseReverts("-1.234567890e+"); - _checkParseReverts("-1.234567890z"); - _checkParseReverts("-1.234567890e"); - _checkParseReverts("-00.234567890"); - _checkParseReverts("-.234567890"); - _checkParseReverts("-00"); - _checkParseReverts("--0"); - _checkParseReverts("00"); - _checkParseReverts("0."); - _checkParseReverts("0..12"); - _checkParseReverts("0.0e"); - _checkParseReverts("."); - _checkParseReverts("-"); - _checkParseReverts("+"); - _checkParseReverts("e"); - _checkParseReverts("+123"); - _checkParseReverts(".123"); - _checkParseReverts("e123"); - _checkParseReverts("1 e 1"); - _checkParseReverts("-1.e+0"); - _checkParseReverts("0x"); - } - - function _checkParseReverts(string memory trimmed) internal { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.parsedValue(trimmed); - string memory s = trimmed; - for (uint256 i; i != 4; ++i) { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.parsedValue(_padWhiteSpace(s, i)); - } - } - - function parsedValue(string memory s) public view miniBrutalizeMemory returns (string memory) { - s = s.parse().value(); - _checkMemory(s); - return s; - } - - function testParseNumber() public { - _checkParseNumber("0"); - _checkParseNumber("-0"); - _checkParseNumber("-1.2e+0"); - _checkParseNumber("-1.2e+00"); - _checkParseNumber("-1.2e+001"); - _checkParseNumber("-1.2e+22"); - _checkParseNumber("-1.2e-22"); - _checkParseNumber("-1.2e22"); - _checkParseNumber("0.1"); - _checkParseNumber("0.123"); - _checkParseNumber("12345678901234567890123456789012345678901234567890"); - _checkParseNumber("12345e12345678901234567890123456789012345678901234567890"); - _checkParseNumber("1234567890"); - _checkParseNumber("123"); - _checkParseNumber("1"); - } - - function _checkParseNumber(string memory trimmed) internal { - _checkSoloNumber(trimmed.parse(), trimmed); - string memory s = trimmed; - for (uint256 i; i != 4; ++i) { - _checkSoloNumber(_padWhiteSpace(s, i).parse(), trimmed); - } - } - - function _checkSoloNumber(JSONParserLib.Item memory item, string memory trimmed) internal { - for (uint256 i; i != 2; ++i) { - assertEq(item.getType(), JSONParserLib.TYPE_NUMBER); - assertEq(item.isNumber(), true); - assertEq(item.value(), trimmed); - _checkItemIsSolo(item); - } - } - - function testParseEmptyArrays() public { - _checkParseEmptyArray("[]"); - _checkParseEmptyArray("[ ]"); - _checkParseEmptyArray("[ ]"); - } - - function _checkParseEmptyArray(string memory trimmed) internal { - _checkSoloEmptyArray(trimmed.parse(), trimmed); - string memory s = trimmed; - for (uint256 i; i != 16; ++i) { - _checkSoloEmptyArray(_padWhiteSpace(s, i).parse(), trimmed); - } - } - - function _checkSoloEmptyArray(JSONParserLib.Item memory item, string memory trimmed) internal { - for (uint256 i; i != 2; ++i) { - assertEq(item.getType(), JSONParserLib.TYPE_ARRAY); - assertEq(item.isArray(), true); - assertEq(item.value(), trimmed); - _checkItemIsSolo(item); - } - } - - function testParseEmptyObjects() public { - _checkParseEmptyObject("{}"); - _checkParseEmptyObject("{ }"); - _checkParseEmptyObject("{ }"); - } - - function _checkParseEmptyObject(string memory trimmed) internal { - _checkSoloEmptyObject(trimmed.parse(), trimmed); - string memory s = trimmed; - for (uint256 i; i != 16; ++i) { - _checkSoloEmptyObject(_padWhiteSpace(s, i).parse(), trimmed); - } - } - - function _checkSoloEmptyObject(JSONParserLib.Item memory item, string memory trimmed) - internal - { - for (uint256 i; i != 2; ++i) { - assertEq(item.getType(), JSONParserLib.TYPE_OBJECT); - assertEq(item.isObject(), true); - assertEq(item.value(), trimmed); - _checkItemIsSolo(item); - } - } - - function _padWhiteSpace(string memory s, uint256 r) internal pure returns (string memory) { - unchecked { - uint256 q = r; - r = r % 3; - string memory p = r == 0 ? " " : r == 1 ? "\t" : r == 2 ? "\r" : "\n"; - q = 1 + q / 3; - for (uint256 i; i != q; ++i) { - s = string(abi.encodePacked(p, s, p)); - } - return s; - } - } - - function testParseSimpleUintArray() public { - string memory s; - JSONParserLib.Item memory item; - - for (uint256 k; k != 9; ++k) { - uint256 o = k == 0 ? 0 : 1 << (17 * k); - string memory p = _padWhiteSpace("", k); - for (uint256 j; j != 5; ++j) { - s = "["; - for (uint256 i; i != j; ++i) { - string memory x = LibString.toString(o + i); - if (i == 0) { - s = string(abi.encodePacked(s, p, x)); - } else { - s = string(abi.encodePacked(s, p, ",", p, x)); - } - } - s = string(abi.encodePacked(s, "]")); - item = s.parse(); - assertEq(item.isArray(), true); - assertEq(item.size(), j); - for (uint256 i; i != j; ++i) { - string memory x = LibString.toString(o + i); - assertEq(item.children()[i].value(), x); - assertEq(item.children()[i].parent()._data, item._data); - assertEq(item.children()[i].parent().isArray(), true); - assertEq(item.at(i)._data, item.children()[i]._data); - assertEq(item.at(LibString.toString(i))._data, 0); - } - } - } - } - - function testEmptyItem() public { - JSONParserLib.Item memory item; - assertEq(item.value(), ""); - assertEq(item.isUndefined(), true); - assertEq(item.parent().isUndefined(), true); - assertEq(item.parent().parent().isUndefined(), true); - assertEq(item.key(), ""); - assertEq(item.at(0).isUndefined(), true); - assertEq(item.at(0).at(0).isUndefined(), true); - } - - function testParseSimpleArray() public { - string memory s = '["hehe",12,"haha"]'; - JSONParserLib.Item memory item = s.parse(); - - assertEq(item.isArray(), true); - assertEq(item.size(), 3); - _checkItemHasNoParent(item); - - assertEq(item.children()[0].value(), '"hehe"'); - assertEq(item.children()[0].index(), 0); - assertEq(item.children()[0].getType(), JSONParserLib.TYPE_STRING); - assertEq(item.children()[0].key(), ""); - assertEq(item.children()[0].parent()._data, item._data); - assertEq(item.children()[0].parent().isArray(), true); - - assertEq(item.children()[1].value(), "12"); - assertEq(item.children()[1].index(), 1); - assertEq(item.children()[1].key(), ""); - assertEq(item.children()[1].getType(), JSONParserLib.TYPE_NUMBER); - assertEq(item.children()[1].parent()._data, item._data); - assertEq(item.children()[1].parent().isArray(), true); - - assertEq(item.children()[2].value(), '"haha"'); - assertEq(item.children()[2].index(), 2); - assertEq(item.children()[2].getType(), JSONParserLib.TYPE_STRING); - assertEq(item.children()[2].key(), ""); - assertEq(item.children()[2].parent()._data, item._data); - assertEq(item.children()[2].parent().isArray(), true); - - assertEq(item.at(0)._data, item.children()[0]._data); - assertEq(item.at(1)._data, item.children()[1]._data); - assertEq(item.at(2)._data, item.children()[2]._data); - assertEq(item.at(3)._data, 0); - } - - function testParseSpecials() public miniBrutalizeMemory { - string memory s; - JSONParserLib.Item memory item; - - for (uint256 k; k < 9; ++k) { - s = _padWhiteSpace("true", k); - item = s.parse(); - assertEq(item.getType(), JSONParserLib.TYPE_BOOLEAN); - assertEq(item.isBoolean(), true); - assertEq(item.isNull(), false); - assertEq(item.value(), "true"); - assertEq(item.parent().isUndefined(), true); - _checkItemIsSolo(item); - - s = _padWhiteSpace("false", k); - item = s.parse(); - assertEq(item.getType(), JSONParserLib.TYPE_BOOLEAN); - assertEq(item.isBoolean(), true); - assertEq(item.isNull(), false); - assertEq(item.value(), "false"); - _checkItemIsSolo(item); - - s = _padWhiteSpace("null", k); - item = s.parse(); - assertEq(item.getType(), JSONParserLib.TYPE_NULL); - assertEq(item.isBoolean(), false); - assertEq(item.isNull(), true); - assertEq(item.value(), "null"); - _checkItemIsSolo(item); - } - - for (uint256 k; k != 4; ++k) { - if (k == 0) s = "[true,false,null]"; - if (k == 1) s = "[ true , false , null ]"; - if (k == 2) s = '{"A":true,"B":false,"C":null}'; - if (k == 3) s = '{ "A" : true , "B" : false , "C" : null }'; - item = s.parse(); - assertEq(item.size(), 3); - assertEq(item.children()[0].getType(), JSONParserLib.TYPE_BOOLEAN); - assertEq(item.children()[0].value(), "true"); - assertEq(item.children()[1].getType(), JSONParserLib.TYPE_BOOLEAN); - assertEq(item.children()[1].value(), "false"); - assertEq(item.children()[2].getType(), JSONParserLib.TYPE_NULL); - assertEq(item.children()[2].value(), "null"); - if (k == 0 || k == 1) { - for (uint256 i; i != 3; ++i) { - assertEq(item.children()[i].parent()._data, item._data); - assertEq(item.children()[i].parent().isArray(), true); - assertEq(item.children()[i].parent().isArray(), true); - assertEq(item.children()[i].index(), i); - assertEq(item.children()[i].key(), ""); - } - } - if (k == 2 || k == 3) { - for (uint256 i; i != 3; ++i) { - assertEq(item.children()[i].parent()._data, item._data); - assertEq(item.children()[i].parent().isObject(), true); - assertEq(item.children()[i].index(), 0); - } - assertEq(item.children()[0].key(), '"A"'); - assertEq(item.children()[1].key(), '"B"'); - assertEq(item.children()[2].key(), '"C"'); - } - } - } - - function testParseObject() public { - string memory s; - JSONParserLib.Item memory item; - - s = '{"b": "B", "_": "x", "hehe": "HEHE", "_": "y", "v": 12345, "_": "z"}'; - item = s.parse(); - - assertEq(item.isObject(), true); - assertEq(item.size(), 6); - - for (uint256 i; i < item.size(); ++i) { - assertEq(item.at(i).isUndefined(), true); - assertEq(item.children()[i].parent()._data, item._data); - } - assertEq(item.at('"_"').value(), '"z"'); - assertEq(item.at('"b"').value(), '"B"'); - assertEq(item.at('"v"').value(), "12345"); - assertEq(item.at('"hehe"').value(), '"HEHE"'); - assertEq(item.at('"m"').value(), ""); - assertEq(item.at('"m"').isUndefined(), true); - } - - function testParseValidObjectDoesNotRevert(string memory key, string memory value) public { - _limitStringLength(key); - _limitStringLength(value); - string memory s = string( - abi.encodePacked( - '{"', LibString.escapeJSON(key), '":"', LibString.escapeJSON(value), '"}' - ) - ); - this.parsedValue(s); - } - - function testParseRecursiveObject() public miniBrutalizeMemory { - string memory s; - JSONParserLib.Item memory item; - - s = '{ "": [1,2, {"m": "M"}, {},[]], "X": {"hehe": "1", "h": [true,false, null], "": 0} }'; - item = s.parse(); - - assertEq(item.isObject(), true); - assertEq(item.children()[0].key(), '""'); - assertEq(item.children()[0].index(), 0); - assertEq(item.children()[0].children()[0].value(), "1"); - assertEq(item.children()[0].children()[1].value(), "2"); - assertEq(item.children()[0].children()[2].value(), '{"m": "M"}'); - assertEq(item.children()[0].children()[2].children()[0].key(), '"m"'); - assertEq(item.children()[0].children()[2].children()[0].value(), '"M"'); - - JSONParserLib.Item memory c = item.children()[0].children()[2].children()[0]; - assertEq(c.parent().parent().parent()._data, item._data); - assertEq(c.parent().parent().parent().value(), item.value()); - assertEq(c.parent().parent().parent().parent().isUndefined(), true); - - assertEq(item.children()[1].key(), '"X"'); - assertEq(item.children()[1].index(), 0); - assertEq(item.children()[1].value(), '{"hehe": "1", "h": [true,false, null], "": 0}'); - assertEq(item.children()[1].children()[0].key(), '"hehe"'); - assertEq(item.children()[1].children()[0].value(), '"1"'); - - assertEq(item.children()[1].children()[1].key(), '"h"'); - assertEq(item.children()[1].children()[1].value(), "[true,false, null]"); - assertEq(item.children()[1].children()[1].children()[0].value(), "true"); - assertEq(item.children()[1].children()[1].children()[0].isBoolean(), true); - assertEq(item.children()[1].children()[1].children()[1].value(), "false"); - assertEq(item.children()[1].children()[1].children()[1].isBoolean(), true); - assertEq(item.children()[1].children()[1].children()[2].value(), "null"); - assertEq(item.children()[1].children()[1].children()[2].isNull(), true); - - assertEq(item.children()[1].children()[2].key(), '""'); - assertEq(item.children()[1].children()[2].value(), "0"); - assertEq(item.children()[1].children()[2].size(), 0); - - s = "[[[[[[[]]]]]]]"; - item = s.parse(); - assertEq(item.isArray(), true); - - s = '{"a":[[[{"z":"Z"}]]]}'; - item = s.parse(); - assertEq(item.isObject(), true); - } - - function testParseString() public { - _checkParseString('""'); - _checkParseString('"a"'); - _checkParseString('"ab"'); - _checkParseString('"012345678901234567890123456789"'); - _checkParseString('"0123456789012345678901234567890"'); - _checkParseString('"01234567890123456789012345678901"'); - _checkParseString('"012345678901234567890123456789012"'); - _checkParseString('"0123456789012345678901234567890123"'); - _checkParseString('" d"'); - _checkParseString('"d "'); - _checkParseString('" d "'); - _checkParseString('"\\""'); - _checkParseString('"\\\\"'); - _checkParseString('"\\/"'); - _checkParseString('"\\b"'); - _checkParseString('"\\f"'); - _checkParseString('"\\n"'); - _checkParseString('"\\r"'); - _checkParseString('"\\t"'); - _checkParseString('" \\u1234 \\"\\"\\\\ \\b\\f \\n\\r "'); - _checkParseString('"\\u1234"'); - _checkParseString('"\\uabcd"'); - _checkParseString('"\\uABCD"'); - _checkParseString('"\\uef00"'); - _checkParseString('"\\u00EF"'); - _checkParseString('"\\u1234 "'); - _checkParseString('"\\uabcd "'); - _checkParseString('"\\uABCD "'); - _checkParseString('"\\uef00 "'); - _checkParseString('"\\u00EF "'); - } - - function _checkParseString(string memory s) internal { - JSONParserLib.Item memory item; - assertEq(this.parsedValue(s), s); - for (uint256 k; k != 4; ++k) { - item = _padWhiteSpace(s, k).parse(); - assertEq(item.value(), s); - assertEq(item.isString(), true); - assertEq(item.value(), s); - _checkItemIsSolo(item); - } - } - - function testParseInvalidStringReverts() public { - _checkParseReverts('"'); - _checkParseReverts('"""'); - _checkParseReverts('""""'); - _checkParseReverts('"""""'); - _checkParseReverts('"abc" "'); - _checkParseReverts('"abc" ""'); - _checkParseReverts('"abc""abc"'); - _checkParseReverts('"\\"'); - _checkParseReverts('"\\\\\\"'); - _checkParseReverts('"\\u"'); - _checkParseReverts('"\\u1"'); - _checkParseReverts('"\\u12"'); - _checkParseReverts('"\\u123"'); - _checkParseReverts('"\\uxxxx"'); - _checkParseReverts('"\\u012g"'); - _checkParseReverts('"\\u1234'); - } - - function _checkItemIsSolo(JSONParserLib.Item memory item) internal { - _checkItemHasNoParent(item); - assertEq(item.size(), 0); - } - - function _checkItemHasNoParent(JSONParserLib.Item memory item) internal { - assertEq(item.parent().isUndefined(), true); - assertEq(item.parent()._data, 0); - assertEq(item.key(), ""); - assertEq(item.index(), 0); - assertEq(item.parent().isObject(), false); - assertEq(item.parent().isArray(), false); - assertEq(item.isUndefined(), false); - } - - function testParseGas() public { - string memory s = - '{"animation_url":"","artist":"Daniel Allan","artwork":{"mimeType":"image/gif","uri":"ar://J5NZ-e2NUcQj1OuuhpTjAKtdW_nqwnwo5FypF_a6dE4","nft":null},"attributes":[{"trait_type":"Criteria","value":"Song Edition"}],"bpm":null,"credits":null,"description":"Criteria is an 8-track project between Daniel Allan and Reo Cragun.\\n\\nA fusion of electronic music and hip-hop - Criteria brings together the best of both worlds and is meant to bring web3 music to a wider audience.\\n\\nThe collection consists of 2500 editions with activations across Sound, Bonfire, OnCyber, Spinamp and Arpeggi.","duration":105,"external_url":"https://www.sound.xyz/danielallan/criteria","genre":"Pop","image":"ar://J5NZ-e2NUcQj1OuuhpTjAKtdW_nqwnwo5FypF_a6dE4","isrc":null,"key":null,"license":null,"locationCreated":null,"losslessAudio":"","lyrics":null,"mimeType":"audio/wave","nftSerialNumber":11,"name":"Criteria #11","originalReleaseDate":null,"project":null,"publisher":null,"recordLabel":null,"tags":null,"title":"Criteria","trackNumber":1,"version":"sound-edition-20220930","visualizer":null}'; - JSONParserLib.Item memory item = s.parse(); - assertEq(item.isObject(), true); - bytes32 expectedHash = 0x6c3276c7005f50c82624fb28f9748f0fb6f0b364234e4823178f964315b41567; - assertEq(keccak256(bytes(item.at('"description"').value())), expectedHash); - } - - function testParseUintFromHex() public { - unchecked { - for (uint256 i; i != 9; ++i) { - _checkParseUintFromHex(LibString.toString(i), i); - } - } - _checkParseUintFromHex("a", 0xa); - _checkParseUintFromHex("f", 0xf); - _checkParseUintFromHex("ff", 0xff); - _checkParseUintFromHex("fff", 0xfff); - _checkParseUintFromHex( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", type(uint256).max - ); - _checkParseUintFromHex( - "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", - 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef - ); - _checkParseUintFromHex( - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ); - _checkParseUintFromHex( - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", - 0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - ); - } - - function _checkParseUintFromHex(string memory s, uint256 x) internal { - _checkParseUintFromHexSub(LibString.lower(s), x); - _checkParseUintFromHexSub(LibString.upper(s), x); - } - - function _checkParseUintFromHexSub(string memory s, uint256 x) internal { - assertEq(this.parseUintFromHex(s), x); - assertEq(this.parseUintFromHex(LibString.concat("0x", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("0x0", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("0X00", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("0x000", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("0X", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("0", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("00", s)), x); - assertEq(this.parseUintFromHex(LibString.concat("000", s)), x); - } - - function parseUintFromHex(string memory s) public pure returns (uint256) { - return s.parseUintFromHex(); - } - - function testParseInvalidUintFromHexReverts() public { - _checkParseInvalidUintFromHexReverts(""); - _checkParseInvalidUintFromHexReverts("+"); - _checkParseInvalidUintFromHexReverts(" 0"); - _checkParseInvalidUintFromHexReverts("0 "); - _checkParseInvalidUintFromHexReverts(" 12"); - _checkParseInvalidUintFromHexReverts("00x12"); - _checkParseInvalidUintFromHexReverts(" 0x12"); - _checkParseInvalidUintFromHexReverts("-0x12"); - _checkParseInvalidUintFromHexReverts("0x123g"); - _checkParseInvalidUintFromHexReverts("123g"); - _checkParseInvalidUintFromHexReverts("z"); - _checkParseInvalidUintFromHexReverts( - "1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - _checkParseInvalidUintFromHexReverts( - "10000000000000000000000000000000000000000000000000000000000000000" - ); - _checkParseInvalidUintFromHexReverts( - "ff0000000000000000000000000000000000000000000000000000000000000000" - ); - } - - function _checkParseInvalidUintFromHexReverts(string memory s) internal { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.parseUintFromHex(s); - } - - function testParseUint() public { - assertEq(this.parseUint("0"), 0); - assertEq(this.parseUint("1"), 1); - assertEq(this.parseUint("123"), 123); - assertEq(this.parseUint("0123"), 123); - assertEq(this.parseUint("000123"), 123); - assertEq(this.parseUint("12345678901234567890"), 12345678901234567890); - string memory s; - s = "115792089237316195423570985008687907853269984665640564039457584007913129639935"; - assertEq(this.parseUint(s), type(uint256).max); - } - - function testParseInvalidUintReverts() public { - _checkParseInvalidUintReverts(""); - _checkParseInvalidUintReverts("-"); - _checkParseInvalidUintReverts("a"); - _checkParseInvalidUintReverts(" "); - _checkParseInvalidUintReverts(" 123 "); - _checkParseInvalidUintReverts("123:"); - _checkParseInvalidUintReverts(":"); - string memory s; - s = "115792089237316195423570985008687907853269984665640564039457584007913129639936"; - _checkParseInvalidUintReverts(s); - s = "115792089237316195423570985008687907853269984665640564039457584007913129639937"; - _checkParseInvalidUintReverts(s); - s = "115792089237316195423570985008687907853269984665640564039457584007913129639999"; - _checkParseInvalidUintReverts(s); - s = "115792089237316195423570985008687907853269984665640564039457584007913129640001"; - _checkParseInvalidUintReverts(s); - s = "115792089237316195423570985008687907853269984665640564039457584007913129640035"; - _checkParseInvalidUintReverts(s); - s = "215792089237316195423570985008687907853269984665640564039457584007913129639935"; - _checkParseInvalidUintReverts(s); - s = "222222222222222222222222222222222222222222222222222222222222222222222222222222"; - _checkParseInvalidUintReverts(s); - s = "1215792089237316195423570985008687907853269984665640564039457584007913129639935"; - _checkParseInvalidUintReverts(s); - } - - function _checkParseInvalidUintReverts(string memory s) internal { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.parseUint(s); - } - - function parseUint(string memory s) public view miniBrutalizeMemory returns (uint256) { - return s.parseUint(); - } - - function testParseInt() public { - _checkParseInt("0", 0); - _checkParseInt("1", 1); - _checkParseInt("+1", 1); - _checkParseInt("+01", 1); - _checkParseInt("+001", 1); - _checkParseInt("+0", 0); - _checkParseInt("+1", 1); - _checkParseInt("+12", 12); - _checkParseInt("-12", -12); - string memory s; - s = "-57896044618658097711785492504343953926634992332820282019728792003956564819967"; - _checkParseInt(s, -type(int256).max); - s = "+57896044618658097711785492504343953926634992332820282019728792003956564819967"; - _checkParseInt(s, type(int256).max); - s = "57896044618658097711785492504343953926634992332820282019728792003956564819967"; - _checkParseInt(s, type(int256).max); - } - - function testParseInvalidIntReverts() public { - _checkParseInvalidIntReverts(""); - _checkParseInvalidIntReverts("-"); - _checkParseInvalidIntReverts("+"); - _checkParseInvalidIntReverts("--"); - _checkParseInvalidIntReverts("++"); - _checkParseInvalidIntReverts("a"); - _checkParseInvalidIntReverts(" "); - _checkParseInvalidIntReverts(" 123 "); - _checkParseInvalidIntReverts("123:"); - _checkParseInvalidIntReverts(":"); - _checkParseInvalidIntReverts(":123"); - string memory s; - s = "-57896044618658097711785492504343953926634992332820282019728792003956564819968"; - _checkParseInvalidIntReverts(s); - s = "+57896044618658097711785492504343953926634992332820282019728792003956564819968"; - _checkParseInvalidIntReverts(s); - } - - function _checkParseInt(string memory s, int256 x) internal { - bytes32 hashBefore = keccak256(bytes(s)); - assertEq(this.parseInt(s), x); - assertEq(keccak256(bytes(s)), hashBefore); - } - - function _checkParseInvalidIntReverts(string memory s) internal { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.parseInt(s); - } - - function parseInt(string memory s) public view miniBrutalizeMemory returns (int256) { - return s.parseInt(); - } - - function testDecodeString() public { - assertEq(this.decodeString('""'), ""); - assertEq(this.decodeString('"abc"'), "abc"); - assertEq(this.decodeString('" abc "'), " abc "); - assertEq(this.decodeString('"\\""'), '"'); - assertEq(this.decodeString('"\\/"'), "/"); - assertEq(this.decodeString('"\\\\"'), "\\"); - assertEq(this.decodeString('"\\b"'), hex"08"); - assertEq(this.decodeString('"\\f"'), hex"0c"); - assertEq(this.decodeString('"\\n"'), "\n"); - assertEq(this.decodeString('"\\r"'), "\r"); - assertEq(this.decodeString('"\\t"'), "\t"); - assertEq(this.decodeString('"\\u0020"'), " "); - bytes32 expectedHash; - expectedHash = 0x40b2b6558413427ef2da03b1452640d701458e0ce57114db6b7423ae3b5fe857; - assertEq(keccak256(bytes(this.decodeString('"\\u039e"'))), expectedHash); // Greek uppercase Xi. - expectedHash = 0xecab436111d5a82d983bd4630c03c83f424d2a2dd8465c31fd950b9ec8d005fb; - assertEq(keccak256(bytes(this.decodeString('"\\u2661"'))), expectedHash); // Heart. - expectedHash = 0x367c272ea502ac6e9f085c1baddc52d0ac0224f1b7d1e8621202620efa3ba084; - assertEq(keccak256(bytes(this.decodeString('"\\uD83D\\ude00"'))), expectedHash); // Smiley emoji. - } - - function testDecodeEncodedStringDoesNotRevert(string memory s) public { - _limitStringLength(s); - s = string(abi.encodePacked('"', LibString.escapeJSON(s), '"')); - this.decodeString(s); - assertEq(this.parsedValue(s), s); - } - - function _limitStringLength(string memory s) internal { - uint256 r = _random(); - /// @solidity memory-safe-assembly - assembly { - let limit := 16 - if eq(1, and(r, 3)) { limit := 80 } - let n := mload(s) - if gt(n, limit) { mstore(s, limit) } - } - } - - function testDecodeInvalidStringReverts() public { - _checkDecodeInvalidStringReverts(""); - _checkDecodeInvalidStringReverts('"'); - _checkDecodeInvalidStringReverts(' "" '); - _checkDecodeInvalidStringReverts(' ""'); - _checkDecodeInvalidStringReverts('"" '); - _checkDecodeInvalidStringReverts('"\\z"'); - _checkDecodeInvalidStringReverts('"\\u"'); - _checkDecodeInvalidStringReverts('"\\u1"'); - _checkDecodeInvalidStringReverts('"\\u111"'); - _checkDecodeInvalidStringReverts('"\\uxxxx"'); - _checkDecodeInvalidStringReverts('"\\uD83D"'); // Only half of a Smiley emoji. - } - - function _checkDecodeInvalidStringReverts(string memory s) internal { - vm.expectRevert(JSONParserLib.ParsingFailed.selector); - this.decodeString(s); - } - - function decodeString(string memory s) - public - view - miniBrutalizeMemory - returns (string memory) - { - return JSONParserLib.decodeString(s); - } - - function testParseUint(uint256 x) public { - string memory s = LibString.toString(x); - assertEq(this.parsedValue(s), s); - assertEq(this.parseUint(s), x); - } - - function testParseJWTGas() public { - string memory jwt = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; - string[] memory jwtSplitted = LibString.split(jwt, "."); - JSONParserLib.Item memory header = - JSONParserLib.parse(string(Base64.decode(jwtSplitted[0]))); - JSONParserLib.Item memory payload = - JSONParserLib.parse(string(Base64.decode(jwtSplitted[1]))); - assertEq(jwtSplitted.length, 3); - assertEq(jwtSplitted[2], "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); - assertEq(header.at('"alg"').value(), '"HS256"'); - assertEq(header.at('"typ"').value(), '"JWT"'); - assertEq(payload.at('"sub"').value(), '"1234567890"'); - assertEq(payload.at('"name"').value(), '"John Doe"'); - assertEq(JSONParserLib.parseUint(payload.at('"iat"').value()), 1516239022); - } - - modifier miniBrutalizeMemory() { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, gas()) - mstore(0x00, keccak256(0x00, 0x20)) - mstore(0x20, not(mload(0x00))) - codecopy(mload(0x40), 0, codesize()) - } - _; - } -} diff --git a/lib/solady/test/LibBit.t.sol b/lib/solady/test/LibBit.t.sol deleted file mode 100644 index c235a91..0000000 --- a/lib/solady/test/LibBit.t.sol +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibBit} from "../src/utils/LibBit.sol"; - -contract LibBitTest is SoladyTest { - function testFLS() public { - assertEq(LibBit.fls(0xff << 3), 10); - for (uint256 i = 1; i < 255; i++) { - assertEq(LibBit.fls((1 << i) - 1), i - 1); - assertEq(LibBit.fls((1 << i)), i); - assertEq(LibBit.fls((1 << i) + 1), i); - } - assertEq(LibBit.fls(0), 256); - } - - function testCLZ() public { - for (uint256 i = 1; i < 255; i++) { - assertEq(LibBit.clz((1 << i) - 1), 255 - (i - 1)); - assertEq(LibBit.clz((1 << i)), 255 - i); - assertEq(LibBit.clz((1 << i) + 1), 255 - i); - } - assertEq(LibBit.clz(0), 256); - } - - function testFFS() public { - assertEq(LibBit.ffs(0xff << 3), 3); - uint256 brutalizer = uint256(keccak256(abi.encode(address(this), block.timestamp))); - for (uint256 i = 0; i < 256; i++) { - assertEq(LibBit.ffs(1 << i), i); - assertEq(LibBit.ffs(type(uint256).max << i), i); - assertEq(LibBit.ffs((brutalizer | 1) << i), i); - } - assertEq(LibBit.ffs(0), 256); - } - - function testPopCount(uint256 x) public { - uint256 c; - unchecked { - for (uint256 t = x; t != 0; c++) { - t &= t - 1; - } - } - assertEq(LibBit.popCount(x), c); - } - - function testPopCount() public { - unchecked { - for (uint256 i = 1; i < 256; ++i) { - assertEq(LibBit.popCount((1 << i) | 1), 2); - } - } - } - - function testIsPo2(uint8 a, uint8 b) public { - unchecked { - uint256 x = (1 << uint256(a)) | (1 << uint256(b)); - if (a == b) { - assertTrue(LibBit.isPo2(x)); - } else { - assertFalse(LibBit.isPo2(x)); - } - } - } - - function testIsPo2(uint256 x) public { - uint256 c; - unchecked { - for (uint256 t = x; t != 0; c++) { - t &= t - 1; - } - } - assertEq(LibBit.isPo2(x), c == 1); - } - - function testIsPo2() public { - assertFalse(LibBit.isPo2(0)); - assertFalse(LibBit.isPo2(type(uint256).max)); - unchecked { - for (uint256 i; i < 256; ++i) { - uint256 x = 1 << i; - assertTrue(LibBit.isPo2(x)); - assertFalse(LibBit.isPo2(~x)); - } - } - } - - function testAnd(bool x, bool y) public { - assertEq(LibBit.and(x, y), x && y); - assertEq(LibBit.rawAnd(x, y), LibBit.and(x, y)); - } - - function testAnd() public { - unchecked { - for (uint256 t; t != 100; ++t) { - uint256 i = _random(); - uint256 j = _random(); - uint256 k = _random(); - bool a = i < j; - bool b = j < k; - assertEq(LibBit.and(a, b), i < j && j < k); - } - } - } - - function testOr(bool x, bool y) public { - assertEq(LibBit.or(x, y), x || y); - assertEq(LibBit.rawOr(x, y), LibBit.or(x, y)); - } - - function testOr() public { - unchecked { - for (uint256 t; t != 100; ++t) { - uint256 i = _random(); - uint256 j = _random(); - uint256 k = _random(); - bool a = i < j; - bool b = j < k; - assertEq(LibBit.or(a, b), i < j || j < k); - } - } - } - - function testAutoClean(uint256 x, uint256 y) public { - bool xCasted; - bool yCasted; - /// @solidity memory-safe-assembly - assembly { - xCasted := x - yCasted := y - } - bool result = LibBit.and(true, LibBit.or(xCasted, yCasted)); - assertEq(result, xCasted || yCasted); - } - - function testReturnsBool() public { - bool result; - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, 0x40b98a2f) - mstore(0x20, 123) - pop(staticcall(gas(), address(), 0x1c, 0x24, 0x00, 0x20)) - result := eq(mload(0x00), 1) - } - assertTrue(result); - } - - function returnsBool(uint256 i) public pure returns (bool b) { - /// @solidity memory-safe-assembly - assembly { - b := i - } - } - - function testPassInBool() public { - bool result; - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, 0x59a3028a) - mstore(0x20, 1) - pop(staticcall(gas(), address(), 0x1c, 0x24, 0x00, 0x20)) - result := eq(mload(0x00), 1) - } - assertTrue(result); - } - - function acceptsBool(bool) public pure returns (bool) { - return true; - } - - function testBoolToUint(bool b) public { - uint256 z; - /// @solidity memory-safe-assembly - assembly { - z := b - } - assertEq(LibBit.toUint(b), z); - assertEq(LibBit.rawToUint(b), z); - } - - function testReverseBitsDifferential(uint256 x) public { - assertEq(LibBit.reverseBits(x), _reverseBitsOriginal(x)); - } - - function _reverseBitsOriginal(uint256 x) internal pure returns (uint256 r) { - unchecked { - for (uint256 i; i != 256; ++i) { - r = (r << 1) | ((x >> i) & 1); - } - } - } - - function testReverseBytesDifferential(uint256 x) public { - assertEq(LibBit.reverseBytes(x), _reverseBytesOriginal(x)); - } - - function _reverseBytesOriginal(uint256 x) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - for { let i := 0 } lt(i, 32) { i := add(i, 1) } { mstore8(i, byte(sub(31, i), x)) } - r := mload(0x00) - } - } -} diff --git a/lib/solady/test/LibBitmap.t.sol b/lib/solady/test/LibBitmap.t.sol deleted file mode 100644 index 1e97cf8..0000000 --- a/lib/solady/test/LibBitmap.t.sol +++ /dev/null @@ -1,350 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibBitmap} from "../src/utils/LibBitmap.sol"; -import {LibBit} from "../src/utils/LibBit.sol"; - -contract LibBitmapTest is SoladyTest { - using LibBitmap for LibBitmap.Bitmap; - - error AlreadyClaimed(); - - LibBitmap.Bitmap bitmap; - - function get(uint256 index) public view returns (bool result) { - result = bitmap.get(index); - } - - function set(uint256 index) public { - bitmap.set(index); - } - - function unset(uint256 index) public { - bitmap.unset(index); - } - - function toggle(uint256 index) public { - bitmap.toggle(index); - } - - function setTo(uint256 index, bool shouldSet) public { - bitmap.setTo(index, shouldSet); - } - - function claimWithGetSet(uint256 index) public { - if (bitmap.get(index)) { - revert AlreadyClaimed(); - } - bitmap.set(index); - } - - function claimWithToggle(uint256 index) public { - if (bitmap.toggle(index) == false) { - revert AlreadyClaimed(); - } - } - - function testBitmapGet() public { - testBitmapGet(111111); - } - - function testBitmapGet(uint256 index) public { - assertFalse(get(index)); - } - - function testBitmapSetAndGet(uint256 index) public { - set(index); - bool result = get(index); - bool resultIsOne; - /// @solidity memory-safe-assembly - assembly { - resultIsOne := eq(result, 1) - } - assertTrue(result); - assertTrue(resultIsOne); - } - - function testBitmapSet() public { - testBitmapSet(222222); - } - - function testBitmapSet(uint256 index) public { - set(index); - assertTrue(get(index)); - } - - function testBitmapUnset() public { - testBitmapSet(333333); - } - - function testBitmapUnset(uint256 index) public { - set(index); - assertTrue(get(index)); - unset(index); - assertFalse(get(index)); - } - - function testBitmapSetTo() public { - testBitmapSetTo(555555, true, 0); - testBitmapSetTo(555555, false, 0); - } - - function testBitmapSetTo(uint256 index, bool shouldSet, uint256 randomness) public { - bool shouldSetBrutalized; - /// @solidity memory-safe-assembly - assembly { - if shouldSet { shouldSetBrutalized := or(iszero(randomness), randomness) } - } - setTo(index, shouldSetBrutalized); - assertEq(get(index), shouldSet); - } - - function testBitmapSetTo(uint256 index, uint256 randomness) public { - randomness = _random(); - unchecked { - for (uint256 i; i < 5; ++i) { - bool shouldSet; - /// @solidity memory-safe-assembly - assembly { - shouldSet := and(shr(i, randomness), 1) - } - testBitmapSetTo(index, shouldSet, _random()); - } - } - } - - function testBitmapToggle() public { - testBitmapToggle(777777, true); - testBitmapToggle(777777, false); - } - - function testBitmapToggle(uint256 index, bool initialValue) public { - setTo(index, initialValue); - assertEq(get(index), initialValue); - toggle(index); - assertEq(get(index), !initialValue); - } - - function testBitmapClaimWithGetSet() public { - uint256 index = 888888; - this.claimWithGetSet(index); - vm.expectRevert(AlreadyClaimed.selector); - this.claimWithGetSet(index); - } - - function testBitmapClaimWithToggle() public { - uint256 index = 999999; - this.claimWithToggle(index); - vm.expectRevert(AlreadyClaimed.selector); - this.claimWithToggle(index); - } - - function testBitmapSetBatchWithinSingleBucket() public { - _testBitmapSetBatch(257, 30); - } - - function testBitmapSetBatchAcrossMultipleBuckets() public { - _testBitmapSetBatch(10, 512); - } - - function testBitmapSetBatch() public { - unchecked { - for (uint256 i; i < 8; ++i) { - uint256 start = _random(); - uint256 amount = _random(); - _testBitmapSetBatch(start, amount); - } - } - } - - function testBitmapUnsetBatchWithinSingleBucket() public { - _testBitmapUnsetBatch(257, 30); - } - - function testBitmapUnsetBatchAcrossMultipleBuckets() public { - _testBitmapUnsetBatch(10, 512); - } - - function testBitmapUnsetBatch() public { - unchecked { - for (uint256 i; i < 8; ++i) { - uint256 start = _random(); - uint256 amount = _random(); - _testBitmapUnsetBatch(start, amount); - } - } - } - - function testBitmapPopCountWithinSingleBucket() public { - _testBitmapPopCount(1, 150); - } - - function testBitmapPopCountAcrossMultipleBuckets() public { - _testBitmapPopCount(10, 512); - } - - function testBitmapPopCount(uint256, uint256 start, uint256 amount) public { - unchecked { - uint256 n = 1000; - uint256 expectedCount; - _resetBitmap(0, n / 256 + 1); - - (start, amount) = _boundStartAndAmount(start, amount, n); - - uint256 jPrev = 0xff + 1; - uint256 j = _random() & 0xff; - while (true) { - bitmap.set(j); - if (j != jPrev && start <= j && j < start + amount) { - expectedCount += 1; - } - if (start + amount <= j && _random() & 7 == 0) break; - jPrev = j; - j += _random() & 0xff; - } - assertEq(bitmap.popCount(start, amount), expectedCount); - } - } - - function testBitmapPopCount() public { - unchecked { - for (uint256 i; i < 8; ++i) { - uint256 start = _random(); - uint256 amount = _random(); - testBitmapPopCount(start, amount, _random()); - } - } - } - - function testBitmapFindLastSet() public { - unchecked { - bitmap.unsetBatch(0, 2000); - bitmap.set(1000); - for (uint256 i = 0; i < 1000; ++i) { - assertEq(bitmap.findLastSet(i), LibBitmap.NOT_FOUND); - } - bitmap.set(100); - bitmap.set(10); - for (uint256 i = 0; i < 10; ++i) { - assertEq(bitmap.findLastSet(i), LibBitmap.NOT_FOUND); - } - for (uint256 i = 10; i < 100; ++i) { - assertEq(bitmap.findLastSet(i), 10); - } - for (uint256 i = 100; i < 600; ++i) { - assertEq(bitmap.findLastSet(i), 100); - } - for (uint256 i = 1000; i < 1100; ++i) { - assertEq(bitmap.findLastSet(i), 1000); - } - bitmap.set(0); - for (uint256 i = 0; i < 10; ++i) { - assertEq(bitmap.findLastSet(i), 0); - } - } - } - - function testBitmapFindLastSet(uint256 before, uint256 randomness) public { - uint256 n = 1000; - unchecked { - _resetBitmap(0, n / 256 + 1); - before = before % n; - randomness = _random() % n; - } - bitmap.set(randomness); - if (randomness <= before) { - assertEq(bitmap.findLastSet(before), randomness); - uint256 nextLcg = _random(); - bitmap.set(nextLcg); - if (nextLcg <= before) { - assertEq(bitmap.findLastSet(before), (randomness < nextLcg ? nextLcg : randomness)); - } - } else { - assertEq(bitmap.findLastSet(before), LibBitmap.NOT_FOUND); - uint256 nextLcg = _random(); - bitmap.set(nextLcg); - if (nextLcg <= before) { - assertEq(bitmap.findLastSet(before), nextLcg); - } else { - assertEq(bitmap.findLastSet(before), LibBitmap.NOT_FOUND); - } - } - } - - function _testBitmapSetBatch(uint256 start, uint256 amount) internal { - uint256 n = 1000; - (start, amount) = _boundStartAndAmount(start, amount, n); - - unchecked { - _resetBitmap(0, n / 256 + 1); - bitmap.setBatch(start, amount); - for (uint256 i; i < n; ++i) { - if (i < start) { - assertFalse(bitmap.get(i)); - } else if (i < start + amount) { - assertTrue(bitmap.get(i)); - } else { - assertFalse(bitmap.get(i)); - } - } - } - } - - function _testBitmapUnsetBatch(uint256 start, uint256 amount) internal { - uint256 n = 1000; - (start, amount) = _boundStartAndAmount(start, amount, n); - - unchecked { - _resetBitmap(type(uint256).max, n / 256 + 1); - bitmap.unsetBatch(start, amount); - for (uint256 i; i < n; ++i) { - if (i < start) { - assertTrue(bitmap.get(i)); - } else if (i < start + amount) { - assertFalse(bitmap.get(i)); - } else { - assertTrue(bitmap.get(i)); - } - } - } - } - - function _testBitmapPopCount(uint256 start, uint256 amount) internal { - uint256 n = 1000; - (start, amount) = _boundStartAndAmount(start, amount, n); - - unchecked { - _resetBitmap(0, n / 256 + 1); - bitmap.setBatch(start, amount); - assertEq(bitmap.popCount(0, n), amount); - if (start > 0) { - assertEq(bitmap.popCount(0, start - 1), 0); - } - if (start + amount < n) { - assertEq(bitmap.popCount(start + amount, n - (start + amount)), 0); - } - } - } - - function _boundStartAndAmount(uint256 start, uint256 amount, uint256 n) - private - pure - returns (uint256 boundedStart, uint256 boundedAmount) - { - unchecked { - boundedStart = start % n; - uint256 end = boundedStart + (amount % n); - if (end > n) end = n; - boundedAmount = end - boundedStart; - } - } - - function _resetBitmap(uint256 bucketValue, uint256 bucketEnd) private { - unchecked { - for (uint256 i; i < bucketEnd; ++i) { - bitmap.map[i] = bucketValue; - } - } - } -} diff --git a/lib/solady/test/LibClone.t.sol b/lib/solady/test/LibClone.t.sol deleted file mode 100644 index 4af3450..0000000 --- a/lib/solady/test/LibClone.t.sol +++ /dev/null @@ -1,422 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; -import {Clone} from "../src/utils/Clone.sol"; -import {SafeTransferLib} from "../src/utils/SafeTransferLib.sol"; - -contract LibCloneTest is SoladyTest, Clone { - error CustomError(uint256 currentValue); - - event ReceiveETH(uint256 amount); - - uint256 public value; - - mapping(bytes32 => bool) saltIsUsed; - - bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - function setValue(uint256 value_) public { - value = value_; - } - - function revertWithError() public view { - revert CustomError(value); - } - - function getCalldataHash() public pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - let extraLength := shr(0xf0, calldataload(sub(calldatasize(), 2))) - if iszero(lt(extraLength, 2)) { - let offset := sub(calldatasize(), extraLength) - let m := mload(0x40) - calldatacopy(m, offset, sub(extraLength, 2)) - result := keccak256(m, sub(extraLength, 2)) - } - } - } - - function _canReceiveETHCorrectly(address clone, uint256 deposit) internal { - deposit = deposit % 1 ether; - - vm.deal(address(this), deposit * 2); - - vm.expectEmit(true, true, true, true); - emit ReceiveETH(deposit); - SafeTransferLib.safeTransferETH(clone, deposit); - assertEq(clone.balance, deposit); - - vm.expectEmit(true, true, true, true); - emit ReceiveETH(deposit); - payable(clone).transfer(deposit); - assertEq(clone.balance, deposit * 2); - } - - function _shouldBehaveLikeClone(address clone, uint256 value_) internal { - assertTrue(clone != address(0)); - - uint256 thisValue = this.value(); - if (thisValue == value_) { - value_ ^= 1; - } - LibCloneTest(clone).setValue(value_); - assertEq(value_, LibCloneTest(clone).value()); - assertEq(thisValue, this.value()); - vm.expectRevert(abi.encodeWithSelector(CustomError.selector, value_)); - LibCloneTest(clone).revertWithError(); - } - - function testDeployERC1967(uint256 value_) public { - address clone = LibClone.deployERC1967(address(this)); - _shouldBehaveLikeClone(clone, value_); - assertEq( - vm.load(clone, _ERC1967_IMPLEMENTATION_SLOT), bytes32(uint256(uint160(address(this)))) - ); - } - - function testDeployERC1967() public { - testDeployERC1967(1); - } - - function testClone(uint256 value_) public { - address clone = LibClone.clone(address(this)); - _shouldBehaveLikeClone(clone, value_); - } - - function testClone() public { - testClone(1); - } - - function testCloneDeterministic(uint256 value_, bytes32 salt) public { - if (saltIsUsed[salt]) { - vm.expectRevert(LibClone.DeploymentFailed.selector); - this.cloneDeterministic(address(this), salt); - return; - } - - address clone = this.cloneDeterministic(address(this), salt); - saltIsUsed[salt] = true; - - _shouldBehaveLikeClone(clone, value_); - - address predicted = LibClone.predictDeterministicAddress(address(this), salt, address(this)); - assertEq(clone, predicted); - } - - function cloneDeterministic(address implementation, bytes32 salt) external returns (address) { - return LibClone.cloneDeterministic(_brutalized(implementation), salt); - } - - function cloneDeterministic(address implementation, bytes calldata data, bytes32 salt) - external - returns (address) - { - return LibClone.cloneDeterministic(_brutalized(implementation), data, salt); - } - - function testCloneDeterministicRevertsIfAddressAlreadyUsed() public { - testCloneDeterministic(1, keccak256("a")); - testCloneDeterministic(1, keccak256("a")); - } - - function testCloneDeterministic() public { - testCloneDeterministic(1, keccak256("b")); - } - - function testDeployDeterministicERC1967(uint256 value_, bytes32 salt) public { - if (saltIsUsed[salt]) { - vm.expectRevert(LibClone.DeploymentFailed.selector); - this.deployDeterministicERC1967(address(this), salt); - return; - } - - address clone = this.deployDeterministicERC1967(address(this), salt); - saltIsUsed[salt] = true; - - _shouldBehaveLikeClone(clone, value_); - - address predicted = - LibClone.predictDeterministicAddressERC1967(address(this), salt, address(this)); - assertEq(clone, predicted); - - assertEq( - vm.load(clone, _ERC1967_IMPLEMENTATION_SLOT), bytes32(uint256(uint160(address(this)))) - ); - } - - function deployDeterministicERC1967(address implementation, bytes32 salt) - external - returns (address) - { - return LibClone.deployDeterministicERC1967(_brutalized(implementation), salt); - } - - function testDeployDeterministicERC1967() public { - testDeployDeterministicERC1967(1, keccak256("b")); - } - - function getArgBytes(uint256 argOffset, uint256 length) public pure returns (bytes memory) { - return _getArgBytes(argOffset, length); - } - - function getArgAddress(uint256 argOffset) public pure returns (address) { - return _getArgAddress(argOffset); - } - - function getArgUint256(uint256 argOffset) public pure returns (uint256) { - uint256 result = _getArgUint256(argOffset); - unchecked { - require(bytes32(result) == _getArgBytes32(argOffset)); - require(uint248(result) == _getArgUint248(argOffset + 1)); - require(uint240(result) == _getArgUint240(argOffset + 2)); - require(uint232(result) == _getArgUint232(argOffset + 3)); - require(uint224(result) == _getArgUint224(argOffset + 4)); - require(uint216(result) == _getArgUint216(argOffset + 5)); - require(uint208(result) == _getArgUint208(argOffset + 6)); - require(uint200(result) == _getArgUint200(argOffset + 7)); - require(uint192(result) == _getArgUint192(argOffset + 8)); - require(uint184(result) == _getArgUint184(argOffset + 9)); - require(uint176(result) == _getArgUint176(argOffset + 10)); - require(uint168(result) == _getArgUint168(argOffset + 11)); - require(uint160(result) == _getArgUint160(argOffset + 12)); - require(uint152(result) == _getArgUint152(argOffset + 13)); - require(uint144(result) == _getArgUint144(argOffset + 14)); - require(uint136(result) == _getArgUint136(argOffset + 15)); - require(uint128(result) == _getArgUint128(argOffset + 16)); - require(uint120(result) == _getArgUint120(argOffset + 17)); - require(uint112(result) == _getArgUint112(argOffset + 18)); - require(uint104(result) == _getArgUint104(argOffset + 19)); - require(uint96(result) == _getArgUint96(argOffset + 20)); - require(uint88(result) == _getArgUint88(argOffset + 21)); - require(uint80(result) == _getArgUint80(argOffset + 22)); - require(uint72(result) == _getArgUint72(argOffset + 23)); - require(uint64(result) == _getArgUint64(argOffset + 24)); - require(uint56(result) == _getArgUint56(argOffset + 25)); - require(uint48(result) == _getArgUint48(argOffset + 26)); - require(uint40(result) == _getArgUint40(argOffset + 27)); - require(uint32(result) == _getArgUint32(argOffset + 28)); - require(uint24(result) == _getArgUint24(argOffset + 29)); - require(uint16(result) == _getArgUint16(argOffset + 30)); - require(uint8(result) == _getArgUint8(argOffset + 31)); - } - return result; - } - - function getArgUint256Array(uint256 argOffset, uint256 length) - public - pure - returns (uint256[] memory) - { - uint256[] memory result = _getArgUint256Array(argOffset, length); - bytes32 hash = keccak256(abi.encode(_getArgBytes32Array(argOffset, length))); - require(keccak256(abi.encode(result)) == hash); - return result; - } - - function getArgUint64(uint256 argOffset) public pure returns (uint64) { - return _getArgUint64(argOffset); - } - - function getArgUint8(uint256 argOffset) public pure returns (uint8) { - return _getArgUint8(argOffset); - } - - function testCloneWithImmutableArgs( - uint256 value_, - address argAddress, - uint256 argUint256, - uint256[] memory argUint256Array, - uint64 argUint64, - uint8 argUint8 - ) public { - bytes memory data = - abi.encodePacked(argAddress, argUint256, argUint256Array, argUint64, argUint8); - LibCloneTest clone = LibCloneTest(LibClone.clone(address(this), data)); - _shouldBehaveLikeClone(address(clone), value_); - - // For avoiding stack too deep. Also, no risk of overflow. - unchecked { - uint256 argOffset; - assertEq(clone.getArgAddress(argOffset), argAddress); - argOffset += 20; - assertEq(clone.getArgUint256(argOffset), argUint256); - argOffset += 32; - assertEq(clone.getArgUint256Array(argOffset, argUint256Array.length), argUint256Array); - argOffset += 32 * argUint256Array.length; - assertEq(clone.getArgUint64(argOffset), argUint64); - argOffset += 8; - assertEq(clone.getArgUint8(argOffset), argUint8); - } - } - - function testCloneWithImmutableArgs() public { - uint256[] memory argUint256Array = new uint256[](2); - argUint256Array[0] = 111; - argUint256Array[1] = 222; - testCloneWithImmutableArgs(1, address(uint160(0xB00Ba5)), 8, argUint256Array, 7, 6); - } - - function testCloneDeteministicWithImmutableArgs( - address argAddress, - uint256 argUint256, - uint256[] memory argUint256Array, - bytes memory argBytes, - uint64 argUint64, - uint8 argUint8, - uint256 deposit - ) public { - bytes memory data; - bytes32 salt; - - // For avoiding stack too deep. - unchecked { - // Recycle for the salt. - salt = bytes32(argUint256 + 123); - - data = abi.encodePacked( - argUint256, - argAddress, - argUint256, - argUint256Array, - argBytes, - argUint64, - argUint8, - argUint256 - ); - - bytes32 saltKey = keccak256(abi.encode(data, salt)); - if (saltIsUsed[saltKey]) { - vm.expectRevert(LibClone.DeploymentFailed.selector); - LibCloneTest(this.cloneDeterministic(address(this), data, salt)); - return; - } - saltIsUsed[saltKey] = true; - } - - bytes32 dataHashBefore = keccak256(data); - - LibCloneTest clone = LibCloneTest(this.cloneDeterministic(address(this), data, salt)); - // Check that memory management is done properly. - assertEq(keccak256(data), dataHashBefore); - - _shouldBehaveLikeClone(address(clone), argUint256); - _canReceiveETHCorrectly(address(clone), deposit); - - // For avoiding stack too deep. Also, no risk of overflow. - unchecked { - uint256 argOffset; - assertEq(clone.getArgUint256(argOffset), argUint256); - argOffset += (256 / 8); - assertEq(clone.getArgAddress(argOffset), argAddress); - argOffset += (160 / 8); - assertEq(clone.getArgUint256(argOffset), argUint256); - argOffset += (256 / 8); - assertEq(clone.getArgUint256Array(argOffset, argUint256Array.length), argUint256Array); - argOffset += (256 / 8) * argUint256Array.length; - assertEq(clone.getArgBytes(argOffset, argBytes.length), argBytes); - argOffset += (8 / 8) * argBytes.length; - assertEq(clone.getArgUint64(argOffset), argUint64); - argOffset += (64 / 8); - assertEq(clone.getArgUint8(argOffset), argUint8); - argOffset += (8 / 8); - assertEq(clone.getArgUint256(argOffset), argUint256); - } - - { - address predicted = - LibClone.predictDeterministicAddress(address(this), data, salt, address(this)); - assertEq(address(clone), predicted); - } - - // Check that memory management is done properly. - assertEq(keccak256(data), dataHashBefore); - - assertEq(clone.getCalldataHash(), dataHashBefore); - } - - function testCloneDeteministicWithImmutableArgs() public { - uint256[] memory argUint256Array = new uint256[](2); - argUint256Array[0] = uint256(keccak256("zero")); - argUint256Array[1] = uint256(keccak256("one")); - bytes memory argBytes = bytes("Teehee"); - testCloneDeteministicWithImmutableArgs( - address(uint160(uint256(keccak256("argAddress")))), - uint256(keccak256("argUint256")), - argUint256Array, - argBytes, - uint64(uint256(keccak256("argUint64"))), - uint8(uint256(keccak256("argUint8"))), - uint256(keccak256("deposit")) - ); - } - - function testStartsWith(uint256) public { - uint256 noise = _random() >> 160; - this.checkStartsWith(bytes32(noise), address(0)); - - address by = _randomNonZeroAddress(); - this.checkStartsWith(bytes32((uint256(uint160(by)) << 96) | noise), by); - - address notBy; - while (by == notBy) notBy = _randomNonZeroAddress(); - vm.expectRevert(LibClone.SaltDoesNotStartWith.selector); - this.checkStartsWith(bytes32((uint256(uint160(by)) << 96) | noise), notBy); - } - - function checkStartsWith(bytes32 salt, address by) public view { - LibClone.checkStartsWith(salt, _brutalized(by)); - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } - - function testCloneWithImmutableArgsRevertsIfDataTooBig() public { - uint256 n = 0xff9b; - bytes memory data = _dummyData(n); - - address clone = this.cloneDeterministic(address(this), data, bytes32(gasleft())); - _shouldBehaveLikeClone(clone, 1); - assertEq(LibCloneTest(clone).argBytesHash(), keccak256(data)); - - vm.expectRevert(); - this.cloneDeterministic(address(this), _dummyData(n + 1), bytes32(gasleft())); - } - - function testInitialDeposit() public { - vm.deal(address(this), 1 ether); - address t = address(this); - assertEq(LibClone.clone(123, t).balance, 123); - assertEq(LibClone.cloneDeterministic(123, t, bytes32(gasleft())).balance, 123); - assertEq(LibClone.clone(123, t, "").balance, 123); - assertEq(LibClone.cloneDeterministic(123, t, "", bytes32(gasleft())).balance, 123); - assertEq(LibClone.deployERC1967(123, t).balance, 123); - assertEq(LibClone.deployDeterministicERC1967(123, t, bytes32(gasleft())).balance, 123); - } - - function argBytesHash() public pure returns (bytes32) { - return keccak256(_getArgBytes()); - } - - function _dummyData(uint256 n) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - mstore(result, n) - mstore(0x00, n) - mstore(0x20, 1) - mstore(add(0x20, result), keccak256(0x00, 0x40)) - mstore(0x20, 2) - mstore(add(add(0x20, result), n), keccak256(0x00, 0x40)) - mstore(0x20, 3) - mstore(add(result, n), keccak256(0x00, 0x40)) - mstore(0x40, add(add(0x20, result), n)) - } - } -} diff --git a/lib/solady/test/LibMap.t.sol b/lib/solady/test/LibMap.t.sol deleted file mode 100644 index 91800ed..0000000 --- a/lib/solady/test/LibMap.t.sol +++ /dev/null @@ -1,649 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibMap} from "../src/utils/LibMap.sol"; - -contract LibMapTest is SoladyTest { - using LibMap for *; - - uint8[0xffffffffffffffff] bigUint8ArrayMap; - - LibMap.Uint8Map[2] uint8s; - - LibMap.Uint16Map[2] uint16s; - - LibMap.Uint32Map[2] uint32s; - - LibMap.Uint40Map[2] uint40s; - - LibMap.Uint64Map[2] uint64s; - - LibMap.Uint128Map[2] uint128s; - - mapping(uint256 => LibMap.Uint32Map) uint32Maps; - - mapping(uint256 => mapping(uint256 => uint256)) generalMaps; - - mapping(uint256 => uint256) filled; - - struct _TestTemps { - uint256 i0; - uint256 i1; - uint256 v0; - uint256 v1; - } - - function _testTemps() internal returns (_TestTemps memory t) { - uint256 r = _random(); - t.i0 = (r >> 8) & 31; - t.i1 = (r >> 16) & 31; - t.v0 = _random(); - t.v1 = _random(); - } - - function getUint8(uint256 index) public view returns (uint8 result) { - result = uint8s[0].get(index); - } - - function setUint8(uint256 index, uint8 value) public { - uint8s[0].set(index, value); - } - - function getUint8FromBigArray(uint256 index) public view returns (uint8 result) { - result = bigUint8ArrayMap[index]; - } - - function setUint8FromBigArray(uint256 index, uint8 value) public { - bigUint8ArrayMap[index] = value; - } - - function testMapSetUint8() public { - this.setUint8(111111, 123); - } - - function testMapGetUint8() public { - assertEq(this.getUint8(222222), uint8(0)); - } - - function testMapSetUint8FromBigArray() public { - this.setUint8FromBigArray(111111, 123); - } - - function testMapGetFromBigArray() public { - assertEq(this.getUint8FromBigArray(222222), uint8(0)); - } - - function testUint8MapSetAndGet(uint256) public { - uint8 u = uint8(_random()); - uint8s[0].set(0, u); - assertEq(uint8s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint8 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint8s[0].set(index, casted); - assertEq(uint8s[0].get(index), casted); - } - } - } - - function testUint8MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint8 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint8s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint8s[0].get(i), casted); - } - } - } - } - - function testUint8MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint8s[0].set(t.i0, uint8(t.v0)); - uint8s[1].set(t.i1, uint8(t.v1)); - assertEq(uint8s[0].get(t.i0), uint8(t.v0)); - assertEq(uint8s[1].get(t.i1), uint8(t.v1)); - } - - function testUint16MapSetAndGet(uint256) public { - uint16 u = uint16(_random()); - uint16s[0].set(0, u); - assertEq(uint16s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint16 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint16s[0].set(index, casted); - assertEq(uint16s[0].get(index), casted); - } - } - } - - function testUint16MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint16 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint16s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint16s[0].get(i), casted); - } - } - } - } - - function testUint16MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint16s[0].set(t.i0, uint16(t.v0)); - uint16s[1].set(t.i1, uint16(t.v1)); - assertEq(uint16s[0].get(t.i0), uint16(t.v0)); - assertEq(uint16s[1].get(t.i1), uint16(t.v1)); - } - - function testUint32MapSetAndGet(uint256) public { - uint32 u = uint32(_random()); - uint32s[0].set(0, u); - assertEq(uint32s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint32 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint32s[0].set(index, casted); - assertEq(uint32s[0].get(index), casted); - } - } - } - - function testUint32MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint32 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint32s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint32s[0].get(i), casted); - } - } - } - } - - function testUint32MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint32s[0].set(t.i0, uint32(t.v0)); - uint32s[1].set(t.i1, uint32(t.v1)); - assertEq(uint32s[0].get(t.i0), uint32(t.v0)); - assertEq(uint32s[1].get(t.i1), uint32(t.v1)); - } - - function testUint40MapSetAndGet(uint256) public { - uint40 u = uint40(_random()); - uint40s[0].set(0, u); - assertEq(uint40s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint40 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint40s[0].set(index, casted); - assertEq(uint40s[0].get(index), casted); - } - } - } - - function testUint40MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint40 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint40s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint40s[0].get(i), casted); - } - } - } - } - - function testUint40MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint40s[0].set(t.i0, uint40(t.v0)); - uint40s[1].set(t.i1, uint40(t.v1)); - assertEq(uint40s[0].get(t.i0), uint40(t.v0)); - assertEq(uint40s[1].get(t.i1), uint40(t.v1)); - } - - function testUint64MapSetAndGet(uint256) public { - uint64 u = uint64(_random()); - uint64s[0].set(0, u); - assertEq(uint64s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint64 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint64s[0].set(index, casted); - assertEq(uint64s[0].get(index), casted); - } - } - } - - function testUint64MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint64 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint64s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint64s[0].get(i), casted); - } - } - } - } - - function testUint64MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint64s[0].set(t.i0, uint64(t.v0)); - uint64s[1].set(t.i1, uint64(t.v1)); - assertEq(uint64s[0].get(t.i0), uint64(t.v0)); - assertEq(uint64s[1].get(t.i1), uint64(t.v1)); - } - - function testUint128MapSetAndGet(uint256) public { - uint128 u = uint128(_random()); - uint128s[0].set(0, u); - assertEq(uint128s[0].map[0], u); - unchecked { - for (uint256 t; t < 8; ++t) { - uint256 r = _random(); - uint128 casted; - /// @solidity memory-safe-assembly - assembly { - casted := r - } - uint256 index = _random() % 32; - uint128s[0].set(index, casted); - assertEq(uint128s[0].get(index), casted); - } - } - } - - function testUint128MapSetAndGet() public { - unchecked { - for (uint256 t; t < 16; ++t) { - uint256 n = 64; - uint128 casted; - uint256 r = _random(); - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - uint128s[0].set(i, casted); - } - for (uint256 i; i < n; ++i) { - /// @solidity memory-safe-assembly - assembly { - casted := or(add(mul(n, t), i), r) - } - assertEq(uint128s[0].get(i), casted); - } - } - } - } - - function testUint128MapSetAndGet2(uint256) public { - _TestTemps memory t = _testTemps(); - uint128s[0].set(t.i0, uint128(t.v0)); - uint128s[1].set(t.i1, uint128(t.v1)); - assertEq(uint128s[0].get(t.i0), uint128(t.v0)); - assertEq(uint128s[1].get(t.i1), uint128(t.v1)); - } - - function testUint32Maps(uint256) public { - unchecked { - uint256 a0 = _random(); - uint256 a1 = _random() % 2 == 0 ? a0 + _random() % 4 : a0 - _random() % 4; - uint256 b0 = _random(); - uint256 b1 = _random() % 2 == 0 ? b0 + _random() % 4 : b0 - _random() % 4; - if (a0 == a1 && b1 == b0) { - if (_random() % 2 == 0) { - if (_random() % 2 == 0) b1++; - else a0++; - } else { - if (_random() % 2 == 0) b1--; - else a0--; - } - } - uint256 c0 = _random(); - uint256 c1 = _random(); - uint32 c0Casted; - uint32 c1Casted; - /// @solidity memory-safe-assembly - assembly { - c0Casted := c0 - c1Casted := c1 - } - assertEq(uint32Maps[a0].get(b0), 0); - assertEq(uint32Maps[a1].get(b1), 0); - uint32Maps[a0].set(b0, c0Casted); - uint32Maps[a1].set(b1, c1Casted); - assertEq(uint32Maps[a0].get(b0), uint32(c0)); - assertEq(uint32Maps[a1].get(b1), uint32(c1)); - } - } - - struct _SearchSortedTestVars { - uint256 o; - uint256 n; - uint256 end; - bool found; - uint256 index; - uint256 randomIndex; - uint256 randomIndexValue; - uint256[] values; - } - - function _searchSortedTestVars(mapping(uint256 => uint256) storage map, uint256 bitWidth) - internal - returns (_SearchSortedTestVars memory t) - { - unchecked { - t.n = 1 + _random() % 7 + (_random() % 8 == 0 ? _random() % 64 : 0); - if (_random() % 2 == 0) { - t.o = type(uint256).max - t.n; - t.end = t.o + t.n; - assertEq(t.end, type(uint256).max); - } else { - t.o = _random() % 4 + (_random() % 8 == 0 ? type(uint256).max - 256 : 0); - t.end = t.o + t.n; - } - uint256 v = _random() % 4; - uint256 b = (_random() % 2) * (_random() << 7); - uint256 valueMask = (1 << bitWidth) - 1; - for (uint256 i; i != t.n; ++i) { - map.set(t.o + i, b | v, bitWidth); - filled.set((b | v) & valueMask, 1, 1); - v += 1 + _random() % 2; - } - t.randomIndex = t.o + _random() % t.n; - t.randomIndexValue = map.get(t.randomIndex, bitWidth); - - if (t.o > 0) map.set(t.o - 1, _random(), bitWidth); - if (t.end < type(uint256).max) map.set(t.end, _random(), bitWidth); - - uint256 notFoundValue = _generateNotFoundValue(t.o); - - (t.found, t.index) = map.searchSorted(notFoundValue, t.o, t.end, bitWidth); - assertFalse(t.found); - assertEq(t.index, _nearestIndexBefore(map, notFoundValue, t.o, t.n, bitWidth)); - - uint256 end = t.o - (t.o > 0 ? _random() % t.o : 0); - (t.found, t.index) = map.searchSorted(t.randomIndexValue, t.o, end, bitWidth); - assertFalse(t.found); - assertEq(t.index, t.o); - - (t.found, t.index) = map.searchSorted(t.randomIndexValue, t.o, t.end, bitWidth); - assertTrue(t.found); - assertEq(t.index, t.randomIndex); - } - } - - function _generateNotFoundValue(uint256 o) internal returns (uint256 notFoundValue) { - unchecked { - uint256 max = 32; - do { - notFoundValue = o + _random() % max; - max += 8; - } while (filled.get(notFoundValue, 1) == 1); - } - } - - function _nearestIndexBefore( - mapping(uint256 => uint256) storage map, - uint256 x, - uint256 o, - uint256 n, - uint256 bitWidth - ) internal view returns (uint256 nearestIndex) { - unchecked { - nearestIndex = o; - uint256 nearestDist = type(uint256).max; - for (uint256 i; i != n; ++i) { - uint256 y = map.get(o + i, bitWidth); - if (y > x) continue; - uint256 dist = x - y; - if (dist < nearestDist) { - nearestIndex = o + i; - nearestDist = dist; - } - } - } - } - - function testUint8MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint8Map storage m = uint8s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 8); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint8(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testUint16MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint16Map storage m = uint16s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 16); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint16(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testUint32MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint32Map storage m = uint32s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 32); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint32(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testUint40MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint40Map storage m = uint40s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 40); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint40(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testUint64MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint64Map storage m = uint64s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 64); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint64(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testUint128MapSearchSorted(uint256) public { - unchecked { - LibMap.Uint128Map storage m = uint128s[0]; - _SearchSortedTestVars memory t = _searchSortedTestVars(m.map, 128); - assertEq(m.get(t.randomIndex), t.randomIndexValue); - (bool found, uint256 index) = m.searchSorted(uint128(t.randomIndexValue), t.o, t.end); - assertTrue(found == t.found && index == t.index); - } - } - - function testGeneralMapSearchSorted(uint256) public { - unchecked { - mapping(uint256 => uint256) storage m = generalMaps[0]; - uint256 bitWidth = _bound(_random(), 8, 256); - _searchSortedTestVars(m, bitWidth); - } - } - - function testGeneralMapFunctionsWithSmallBitWidths(uint256) public { - unchecked { - uint256 bitWidth = 1 + _random() % 6; - uint256 valueMask = (1 << bitWidth) - 1; - uint256 o = _random() % 64 + (_random() % 8 == 0 ? type(uint256).max - 256 : 0); - uint256 n = _random() % 9; - for (uint256 k; k != 2; ++k) { - for (uint256 i; i != n; ++i) { - uint256 j = o + i * 2; - generalMaps[k].set(j, _hash(j), bitWidth); - } - } - for (uint256 k; k != 2; ++k) { - for (uint256 i; i != n; ++i) { - uint256 j = o + i * 2 + 1; - generalMaps[k].set(j, _hash(j), bitWidth); - } - } - for (uint256 k; k != 2; ++k) { - for (uint256 i; i != n; ++i) { - uint256 j = o + i * 2; - assertEq(generalMaps[k].get(j, bitWidth), _hash(j) & valueMask); - j = j + 1; - assertEq(generalMaps[k].get(j, bitWidth), _hash(j) & valueMask); - } - } - } - } - - function _hash(uint256 x) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, x) - result := keccak256(0x00, 0x20) - } - } - - function testGeneralMapFunctionsWithZeroBitWidth() public { - unchecked { - mapping(uint256 => uint256) storage m = generalMaps[0]; - for (uint256 j; j < 3; ++j) { - for (uint256 i; i < 3; ++i) { - m.set(i, j + 1, 0); - assertEq(m.get(i, 0), 0); - (bool found, uint256 index) = m.searchSorted(i, j, j + 2, 0); - assertFalse(found); - assertEq(index, j); - } - } - } - } - - function testGeneralMapFunctionsGas() public { - unchecked { - mapping(uint256 => uint256) storage m = generalMaps[0]; - for (uint256 i; i != 1000; ++i) { - m.set(i, i + 1, 32); - assertEq(m.get(i, 32), i + 1); - } - for (uint256 j = 1; j < 900; j += 37) { - (bool found, uint256 index) = m.searchSorted(j, 0, 1000, 32); - assertTrue(found); - assertEq(index, j - 1); - } - } - } - - function testFoundStatementDifferential(uint256 t, uint256 needle, uint256 index) public { - bool a; - bool b; - /// @solidity memory-safe-assembly - assembly { - a := and(eq(t, needle), iszero(iszero(index))) - b := iszero(or(xor(t, needle), iszero(index))) - } - assertEq(a, b); - } -} diff --git a/lib/solady/test/LibPRNG.t.sol b/lib/solady/test/LibPRNG.t.sol deleted file mode 100644 index 79bc2f6..0000000 --- a/lib/solady/test/LibPRNG.t.sol +++ /dev/null @@ -1,171 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibPRNG} from "../src/utils/LibPRNG.sol"; -import {LibSort} from "../src/utils/LibSort.sol"; - -contract LibPRNGTest is SoladyTest { - using LibPRNG for *; - - function testPRNGNext() public { - unchecked { - // Super unlikely to fail. - for (uint256 i; i < 32; ++i) { - LibPRNG.PRNG memory prng; - prng.seed(i); - uint256 r0 = prng.next(); - uint256 r1 = prng.next(); - uint256 r2 = prng.next(); - assertTrue(r0 != r1); - assertTrue(r1 != r2); - prng.seed(i * 2); - uint256 r3 = prng.next(); - assertTrue(r2 != r3); - } - } - } - - function testPRNGUniform() public { - unchecked { - LibPRNG.PRNG memory prng; - for (uint256 i = 1; i < 32; ++i) { - for (uint256 j; j < 32; ++j) { - assertTrue(prng.uniform(i) < i); - } - } - for (uint256 i; i < 32; ++i) { - assertTrue(prng.uniform(0) == 0); - } - // Super unlikely to fail. - uint256 previous; - for (uint256 i = 128; i < 256; ++i) { - uint256 n = 1 << i; - for (uint256 j; j < 8; ++j) { - uint256 r = prng.uniform(n); - assertTrue(r < n); - assertTrue(r != previous); - previous = r; - } - } - } - } - - function testPRNGShuffleGas() public pure { - unchecked { - uint256[] memory a = new uint256[](10000); - LibPRNG.PRNG memory prng; - prng.shuffle(a); - } - } - - function testPRNGShuffleBytesGas() public pure { - unchecked { - bytes memory a = new bytes(10000); - LibPRNG.PRNG memory prng; - prng.shuffle(a); - } - } - - function testPRNGShuffle() public { - unchecked { - LibPRNG.PRNG memory prng; - for (uint256 s = 1; s < 9; ++s) { - uint256 n = 1 << s; // 2, 4, 8, 16, ... - uint256[] memory a = new uint256[](n); - for (uint256 i; i < n; ++i) { - a[i] = i; - } - bytes32 hashBefore = keccak256(abi.encode(a)); - for (;;) { - prng.shuffle(a); - bytes32 hashAfterShuffle = keccak256(abi.encode(a)); - LibSort.sort(a); - bytes32 hashAfterSort = keccak256(abi.encode(a)); - assertTrue(hashBefore == hashAfterSort); - if (hashBefore != hashAfterShuffle) break; - } - } - // Checking that we won't crash. - for (uint256 n = 0; n < 2; ++n) { - uint256[] memory a = new uint256[](n); - prng.shuffle(a); - } - } - } - - function testPRNGShuffleBytes() public { - unchecked { - LibPRNG.PRNG memory prng; - for (uint256 s = 1; s < 9; ++s) { - uint256 n = 1 << s; // 2, 4, 8, 16, ... - bytes memory a = new bytes(n); - for (uint256 i; i < n; ++i) { - a[i] = bytes1(uint8(i & 0xff)); - } - bytes32 hashBefore = keccak256(abi.encode(a)); - uint256 checksumBefore = _bytesOrderAgnosticChecksum(a); - for (uint256 i; i < 30; ++i) { - prng.shuffle(a); - assertEq(_bytesOrderAgnosticChecksum(a), checksumBefore); - bytes32 hashAfterShuffle = keccak256(abi.encode(a)); - if (hashBefore != hashAfterShuffle) break; - } - } - // Checking that we won't crash. - for (uint256 n = 0; n < 2; ++n) { - uint256[] memory a = new uint256[](n); - prng.shuffle(a); - } - } - } - - function testLCGGas() public { - unchecked { - uint256 randomness; - for (uint256 i; i < 256; i++) { - randomness = _stepLCG(randomness); - } - assertTrue(randomness != 0); - } - } - - function testPRNGGas() public { - unchecked { - LibPRNG.PRNG memory prng; - uint256 randomness; - for (uint256 i; i < 256; i++) { - randomness = prng.next(); - } - assertTrue(randomness != 0); - } - } - - function _bytesOrderAgnosticChecksum(bytes memory a) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - for { let n := mload(a) } n { n := sub(n, 1) } { - result := add(result, and(mload(add(a, n)), 0xff)) - } - } - } - - // This is for demonstrating that the gas savings - // over the `keccak256` approach isn't that much. - // The multiplier and the increment are chosen for good enough - // statistical test results. - // - // See: https://github.com/stevenang/randomness_testsuite - // See: https://www.pcg-random.org/posts/does-it-beat-the-minimal-standard.html - // - // The xorshift is required because the raw 128 lower bits - // of the LCG alone will not pass the tests. - function _stepLCG(uint256 state) private pure returns (uint256 randomness) { - /// @solidity memory-safe-assembly - assembly { - let a := 0xd6aad120322a96acae4ccfaf5fcd4bbfda3f2f3001db6837c0981639faa68d8d - state := add(mul(state, a), 83) - randomness := xor(state, shr(128, state)) - } - } -} diff --git a/lib/solady/test/LibRLP.t.sol b/lib/solady/test/LibRLP.t.sol deleted file mode 100644 index f01551f..0000000 --- a/lib/solady/test/LibRLP.t.sol +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibRLP} from "../src/utils/LibRLP.sol"; - -contract LibRLPTest is SoladyTest { - function testComputeAddressDifferential(address deployer, uint256 nonce) public { - assertEq(LibRLP.computeAddress(deployer, nonce), computeAddressOriginal(deployer, nonce)); - } - - function testComputeAddressForSmallNonces() public { - address deployer = address(1); - assertTrue(LibRLP.computeAddress(deployer, 1) != address(0)); - assertTrue(LibRLP.computeAddress(deployer, 0x7f) != address(0)); - assertTrue(LibRLP.computeAddress(deployer, 0xff) != address(0)); - } - - function testComputeAddressOriginalForSmallNonces() public { - address deployer = address(1); - assertTrue(computeAddressOriginal(deployer, 1) != address(0)); - assertTrue(computeAddressOriginal(deployer, 0x7f) != address(0)); - assertTrue(computeAddressOriginal(deployer, 0xff) != address(0)); - } - - function testComputeAddressForLargeNonces() public { - address deployer = address(1); - assertTrue(LibRLP.computeAddress(deployer, 0xffffffff) != address(0)); - assertTrue(LibRLP.computeAddress(deployer, 0xffffffffffffff) != address(0)); - assertTrue(LibRLP.computeAddress(deployer, 0xffffffffffffffff) != address(0)); - } - - function testComputeAddressOriginalForLargeNonces() public { - address deployer = address(1); - assertTrue(computeAddressOriginal(deployer, 0xffffffff) != address(0)); - assertTrue(computeAddressOriginal(deployer, 0xffffffffffffff) != address(0)); - assertTrue(computeAddressOriginal(deployer, 0xffffffffffffffff) != address(0)); - } - - function computeAddressOriginal(address deployer, uint256 nonce) - internal - pure - returns (address) - { - // Although the theoretical allowed limit, based on EIP-2681, - // for an account nonce is 2**64-2: https://eips.ethereum.org/EIPS/eip-2681, - // we just test all the way to 2**256-1 to ensure that the computeAddress function does not revert - // for whatever nonce we provide. - // forgefmt: disable-next-item - if (nonce == 0x00) { - return address( - uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, bytes1(0x80))))) - ); - } - // forgefmt: disable-next-item - if (nonce <= 0x7f) { - return address( - uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployer, uint8(nonce))))) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint8).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployer, bytes1(0x81), uint8(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint16).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployer, bytes1(0x82), uint16(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint24).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployer, bytes1(0x83), uint24(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint32).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), deployer, bytes1(0x84), uint32(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint40).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdb), bytes1(0x94), deployer, bytes1(0x85), uint40(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint48).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdc), bytes1(0x94), deployer, bytes1(0x86), uint48(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint56).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdd), bytes1(0x94), deployer, bytes1(0x87), uint56(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint64).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xde), bytes1(0x94), deployer, bytes1(0x88), uint64(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint72).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xdf), bytes1(0x94), deployer, bytes1(0x89), uint72(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint80).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe0), bytes1(0x94), deployer, bytes1(0x8a), uint80(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint88).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe1), bytes1(0x94), deployer, bytes1(0x8b), uint88(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint96).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe2), bytes1(0x94), deployer, bytes1(0x8c), uint96(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint104).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe3), bytes1(0x94), deployer, bytes1(0x8d), uint104(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint112).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe4), bytes1(0x94), deployer, bytes1(0x8e), uint112(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint120).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe5), bytes1(0x94), deployer, bytes1(0x8f), uint120(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint128).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe6), bytes1(0x94), deployer, bytes1(0x90), uint128(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint136).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe7), bytes1(0x94), deployer, bytes1(0x91), uint136(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint144).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe8), bytes1(0x94), deployer, bytes1(0x92), uint144(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint152).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xe9), bytes1(0x94), deployer, bytes1(0x93), uint152(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint160).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xea), bytes1(0x94), deployer, bytes1(0x94), uint160(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint168).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xeb), bytes1(0x94), deployer, bytes1(0x95), uint168(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint176).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xec), bytes1(0x94), deployer, bytes1(0x96), uint176(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint184).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xed), bytes1(0x94), deployer, bytes1(0x97), uint184(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint192).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xee), bytes1(0x94), deployer, bytes1(0x98), uint192(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint200).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xef), bytes1(0x94), deployer, bytes1(0x99), uint200(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint208).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf0), bytes1(0x94), deployer, bytes1(0x9a), uint208(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint216).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf1), bytes1(0x94), deployer, bytes1(0x9b), uint216(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint224).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf2), bytes1(0x94), deployer, bytes1(0x9c), uint224(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint232).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf3), bytes1(0x94), deployer, bytes1(0x9d), uint232(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint240).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf4), bytes1(0x94), deployer, bytes1(0x9e), uint240(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint248).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf5), bytes1(0x94), deployer, bytes1(0x9f), uint248(nonce))) - ) - ) - ); - } - // forgefmt: disable-next-item - if (nonce <= type(uint256).max) { - return address( - uint160( - uint256( - keccak256(abi.encodePacked(bytes1(0xf6), bytes1(0x94), deployer, bytes1(0xa0), uint256(nonce))) - ) - ) - ); - } - revert(); - } -} diff --git a/lib/solady/test/LibSort.t.sol b/lib/solady/test/LibSort.t.sol deleted file mode 100644 index 9dd557a..0000000 --- a/lib/solady/test/LibSort.t.sol +++ /dev/null @@ -1,1250 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import "src/utils/LibSort.sol"; - -contract LibSortTest is SoladyTest { - function testInsertionSortAddressesDifferential(uint256) public { - unchecked { - uint256 n = _random() % 32 == 0 ? _randomArrayLength() : _random() % 4; - address[] memory a = _randomAddresses(n); - // Make a copy of the `a` and perform insertion sort on it. - address[] memory aCopy = _copy(a); - for (uint256 i = 1; i < n; ++i) { - address key = aCopy[i]; - uint256 j = i; - while (j != 0 && aCopy[j - 1] > key) { - aCopy[j] = aCopy[j - 1]; - --j; - } - aCopy[j] = key; - } - LibSort.insertionSort(a); - assertEq(a, aCopy); - } - } - - function testInsertionSortPsuedorandom(uint256) public { - unchecked { - uint256[] memory a = _randomUints(32); - LibSort.insertionSort(a); - assertTrue(_isSorted(a)); - } - } - - function testInsertionSortPsuedorandom() public { - testInsertionSortPsuedorandom(123456789); - } - - function testSortChecksumed(uint256) public { - unchecked { - uint256 n = _randomArrayLength(); - uint256[] memory a = _randomUints(n); - uint256 checksum; - for (uint256 i = 0; i != n; ++i) { - checksum += a[i]; - } - LibSort.sort(a); - uint256 checksumAfterSort; - for (uint256 i = 0; i != n; ++i) { - checksumAfterSort += a[i]; - } - assertEq(checksum, checksumAfterSort); - assertTrue(_isSorted(a)); - } - } - - function testSortDifferential(uint256) public { - unchecked { - uint256[] memory a = _randomUints(_randomArrayLength()); - // Make a copy of the `a` and perform insertion sort on it. - uint256[] memory aCopy = _copy(a); - LibSort.insertionSort(aCopy); - LibSort.sort(a); - assertEq(a, aCopy); - } - } - - function testSort(uint256) public { - unchecked { - uint256[] memory a = _randomUints(_randomArrayLength()); - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortBasicCase() public { - unchecked { - uint256[] memory a = new uint256[](2); - a[0] = 3; - a[1] = 0; - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortPsuedorandom(uint256) public { - unchecked { - uint256[] memory a = _randomUints(100); - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortPsuedorandom() public { - testSortPsuedorandom(123456789); - } - - function testSortPsuedorandomNonuniform(uint256) public { - unchecked { - uint256[] memory a = new uint256[](100); - for (uint256 i; i < a.length; ++i) { - a[i] = _random() << (i & 8 == 0 ? 128 : 0); - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortPsuedorandomNonuniform() public { - testSortPsuedorandomNonuniform(123456789); - } - - function testSortSorted() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = i; - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortReversed() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = 999 - i; - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortMostlySame() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = i % 8 == 0 ? i : 0; - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortTestOverhead() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - uint256 mask = (1 << 128) - 1; - for (uint256 i; i != n; ++i) { - a[i] = (i << 128) | (_random() & mask); - } - assertTrue(_isSorted(a)); - } - } - - function testSortAddressesPsuedorandomBrutalizeUpperBits() public { - unchecked { - uint256 n = 100; - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - address addr = address(uint160(_random())); - uint256 randomness = _random(); - /// @solidity memory-safe-assembly - assembly { - addr := or(addr, shl(160, randomness)) - } - a[i] = addr; - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortAddressesDifferential(uint256) public { - unchecked { - uint256 n = _randomArrayLength(); - uint256[] memory aRaw = _randomUints(n); - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - address addr; - uint256 addrRaw = aRaw[i]; - /// @solidity memory-safe-assembly - assembly { - addr := addrRaw - } - a[i] = addr; - } - // Make a copy of the `a` and perform insertion sort on it. - address[] memory aCopy = _copy(a); - LibSort.insertionSort(aCopy); - LibSort.sort(a); - assertEq(a, aCopy); - } - } - - function testSortAddressesPsuedorandom(uint256) public { - unchecked { - address[] memory a = _randomAddresses(100); - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortAddressesPsuedorandom() public { - testSortAddressesPsuedorandom(123456789); - } - - function testSortAddressesSorted() public { - unchecked { - uint256 n = 100; - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - a[i] = address(uint160(i)); - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortAddressesReversed() public { - unchecked { - uint256 n = 100; - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - a[i] = address(uint160(999 - i)); - } - LibSort.sort(a); - assertTrue(_isSorted(a)); - } - } - - function testSortOriginalPsuedorandom(uint256) public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = _random(); - } - _sortOriginal(a); - assertTrue(_isSorted(a)); - } - } - - function testSortOriginalPsuedorandom() public { - testSortOriginalPsuedorandom(123456789); - } - - function testSortOriginalSorted() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = i; - } - _sortOriginal(a); - assertTrue(_isSorted(a)); - } - } - - function testSortOriginalReversed() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = 999 - i; - } - _sortOriginal(a); - assertTrue(_isSorted(a)); - } - } - - function testSortOriginalMostlySame() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = i % 8 == 0 ? i : 0; - } - _sortOriginal(a); - assertTrue(_isSorted(a)); - } - } - - function testUniquifySorted() public { - uint256[] memory a = new uint256[](5); - a[0] = 1; - a[1] = 1; - a[2] = 3; - a[3] = 3; - a[4] = 5; - LibSort.uniquifySorted(a); - assertTrue(_isSortedAndUniquified(a)); - assertEq(a.length, 3); - } - - function testUniquifySortedWithEmptyArray() public { - uint256[] memory a = new uint256[](0); - LibSort.uniquifySorted(a); - assertTrue(_isSortedAndUniquified(a)); - assertEq(a.length, 0); - } - - function testUniquifySortedAddress() public { - address[] memory a = new address[](10); - a[0] = address(0x1efF47bc3a10a45D4B230B5d10E37751FE6AA718); - a[1] = address(0x1efF47bc3a10a45D4B230B5d10E37751FE6AA718); - a[2] = address(0x1efF47bC3A10a45d4b630B5D10E37751FE6aA718); - a[3] = address(0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF); - a[4] = address(0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69); - a[5] = address(0x6813eb9362372Eef6200f3B1dbC3f819671cbA70); - a[6] = address(0xe1AB8145F7E55DC933d51a18c793F901A3A0b276); - a[7] = address(0xe1AB8145F7E55DC933d51a18c793F901A3A0b276); - a[8] = address(0xE1Ab8145F7e55Dc933D61a18c793f901A3a0B276); - a[9] = address(0xe1ab8145f7E55Dc933D61A18c793f901A3A0B288); - LibSort.uniquifySorted(a); - assertTrue(_isSortedAndUniquified(a)); - assertEq(a.length, 8); - } - - function testUniquifySorted(uint256) public { - uint256[] memory a = _randomUints(_randomArrayLength()); - LibSort.sort(a); - LibSort.uniquifySorted(a); - assertTrue(_isSortedAndUniquified(a)); - } - - function testUniquifySortedAddress(uint256) public { - address[] memory a = _randomAddresses(_randomArrayLength()); - LibSort.sort(a); - LibSort.uniquifySorted(a); - assertTrue(_isSortedAndUniquified(a)); - } - - function testUniquifySortedDifferential(uint256) public { - uint256 n = _randomArrayLength(); - uint256[] memory a = _randomUints(n); - LibSort.sort(a); - uint256[] memory aCopy = new uint256[](n); - for (uint256 i; i != n; ++i) { - aCopy[i] = a[i]; - } - LibSort.uniquifySorted(a); - _uniquifyOriginal(aCopy); - assertEq(a, aCopy); - } - - function testSearchSortedBasicCases() public { - uint256[] memory a = new uint256[](6); - a[0] = 0; - a[1] = 1; - a[2] = 2; - a[3] = 3; - a[4] = 4; - a[5] = 5; - (bool found, uint256 index) = LibSort.searchSorted(a, 2); - assertTrue(found); - assertEq(index, 2); - - a[0] = 0; - a[1] = 1; - a[2] = 2; - a[3] = 3; - a[4] = 4; - a[5] = 5; - (found, index) = LibSort.searchSorted(a, 5); - assertTrue(found); - assertEq(index, 5); - } - - function testSearchSortedEdgeCases() public { - uint256[] memory a = new uint256[](1); - a[0] = 2; - (bool found, uint256 index) = LibSort.searchSorted(a, 1); - assertFalse(found); - - a = new uint256[](2); - a[0] = 45; - a[1] = 46; - (found, index) = LibSort.searchSorted(a, 2); - assertFalse(found); - } - - function testSearchSortedWithEmptyArray() public { - uint256[] memory a = new uint256[](0); - (bool found, uint256 index) = LibSort.searchSorted(a, 1); - assertFalse(found); - assertEq(index, 0); - } - - function testSearchSortedElementNotInArray() public { - uint256[] memory a = new uint256[](5); - a[0] = 1; - a[1] = 2; - a[2] = 3; - a[3] = 4; - a[4] = 5; - (bool found, uint256 index) = LibSort.searchSorted(a, 0); - assertFalse(found); - assertEq(index, 0); - - a[0] = 15; - a[1] = 25; - a[2] = 35; - a[3] = 45; - a[4] = 55; - (found, index) = LibSort.searchSorted(a, 10); - assertFalse(found); - assertEq(index, 0); - (found, index) = LibSort.searchSorted(a, 20); - assertFalse(found); - assertEq(index, 0); - (found, index) = LibSort.searchSorted(a, 30); - assertFalse(found); - assertEq(index, 1); - (found, index) = LibSort.searchSorted(a, 40); - assertFalse(found); - assertEq(index, 2); - (found, index) = LibSort.searchSorted(a, 50); - assertFalse(found); - assertEq(index, 3); - (found, index) = LibSort.searchSorted(a, 60); - assertFalse(found); - assertEq(index, 4); - } - - function testSearchSortedElementInArray(uint256) public { - unchecked { - _misalignFreeMemoryPointer(); - uint256[] memory a = _randomUints(_randomNonZeroArrayLength()); - LibSort.sort(a); - if (_random() % 2 == 0) { - LibSort.uniquifySorted(a); - } - uint256 randomIndex = _random() % a.length; - uint256 value = a[randomIndex]; - (bool found, uint256 index) = LibSort.searchSorted(a, value); - assertTrue(found); - assertEq(a[index], value); - } - } - - function testSearchSortedElementNotInArray(uint256) public { - unchecked { - _misalignFreeMemoryPointer(); - uint256[] memory a = _randomUints(_randomNonZeroArrayLength()); - LibSort.sort(a); - if (_random() % 2 == 0) { - LibSort.uniquifySorted(a); - } - uint256 randomIndex = _random() % a.length; - uint256 missingValue; - if (_random() % 2 == 0) { - if (_random() % 2 == 0) { - missingValue = a[randomIndex] + 1; - if (missingValue == 0) return; - } else { - missingValue = a[randomIndex] - 1; - if (missingValue == type(uint256).max) return; - } - if (_exists(a, missingValue)) return; - (bool found, uint256 index) = LibSort.searchSorted(a, missingValue); - assertFalse(found); - assertEq(a[index], a[_nearestIndexBefore(a, missingValue)]); - } else { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, missingValue) - missingValue := keccak256(0x00, 0x20) - } - (bool found,) = LibSort.searchSorted(a, missingValue); - assertFalse(found); - } - } - } - - function _exists(uint256[] memory a, uint256 x) internal pure returns (bool result) { - unchecked { - uint256 n = a.length; - for (uint256 i; i != n; ++i) { - if (a[i] == x) { - return true; - } - } - return false; - } - } - - function _nearestIndexBefore(uint256[] memory a, uint256 x) - internal - pure - returns (uint256 nearestIndex) - { - unchecked { - uint256 nearestDist = type(uint256).max; - uint256 n = a.length; - for (uint256 i; i != n; ++i) { - uint256 y = a[i]; - if (y > x) continue; - uint256 dist = x - y; - if (dist < nearestDist) { - nearestIndex = i; - nearestDist = dist; - } - } - } - } - - function testSearchSorted() public { - unchecked { - uint256 n = 100; - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; i++) { - a[i] = i; - } - for (uint256 i; i != n; i++) { - (bool found, uint256 index) = LibSort.searchSorted(a, i); - assertTrue(found); - assertEq(index, i); - } - } - } - - function testSearchSortedDifferential(uint256) public { - uint256[] memory a = _randomUints(_randomArrayLength()); - uint256 needle = _random(); - if (_random() % 2 == 0 && a.length != 0) { - needle = a[_random() % a.length]; - } - (bool found, uint256 index) = LibSort.searchSorted(a, needle); - if (found) { - assertEq(a[index], needle); - } - LibSort.sort(a); - (found, index) = LibSort.searchSorted(a, needle); - assertEq(found, _exists(a, needle)); - if (found) { - assertEq(a[index], needle); - } - } - - function testSearchSortedInts() public { - unchecked { - uint256 n = 100; - int256[] memory a = new int256[](n); - for (uint256 i = 0; i != n; i++) { - a[i] = int256(i) - 50; - } - for (uint256 i; i != n; i++) { - (bool found, uint256 index) = LibSort.searchSorted(a, int256(i) - 50); - assertTrue(found); - assertEq(index, i); - } - } - } - - function testSearchSortedInts(int256[] memory a, int256 needle) public { - (bool found, uint256 index) = LibSort.searchSorted(a, needle); - if (found) { - assertEq(a[index], needle); - } - } - - function testSearchSortedAddresses() public { - unchecked { - uint256 n = 100; - address[] memory a = new address[](n); - for (uint256 i; i != n; i++) { - a[i] = address(uint160(i)); - } - for (uint256 i; i != n; i++) { - (bool found, uint256 index) = LibSort.searchSorted(a, address(uint160(i))); - assertTrue(found); - assertEq(index, i); - (found,) = LibSort.searchSorted(a, address(uint160(i + n))); - assertFalse(found); - } - } - } - - function testInsertionSortInts() public { - unchecked { - for (uint256 t; t != 16; ++t) { - int256[] memory a = _randomInts(_bound(_random(), 0, 8)); - LibSort.insertionSort(a); - assertTrue(_isSorted(a)); - } - } - } - - function testSortInts() public { - unchecked { - for (uint256 t; t != 16; ++t) { - int256[] memory a = _randomInts(_bound(_random(), 0, 64)); - LibSort.insertionSort(a); - assertTrue(_isSorted(a)); - } - } - } - - function testTwoComplementConversionSort(int256 a, int256 b) public { - uint256 w = 1 << 255; - /// @solidity memory-safe-assembly - assembly { - let aConverted := add(a, w) - let bConverted := add(b, w) - if iszero(lt(aConverted, bConverted)) { - let t := aConverted - aConverted := bConverted - bConverted := t - } - a := add(aConverted, w) - b := add(bConverted, w) - } - assertTrue(a <= b); - } - - function testReverse() public { - unchecked { - for (uint256 t; t != 16; ++t) { - uint256 n = _bound(_random(), 0, 8); - uint256[] memory a = new uint256[](n); - uint256[] memory reversed = new uint256[](n); - for (uint256 i; i != n; ++i) { - reversed[n - 1 - i] = (a[i] = _random()); - } - bytes32 originalHash = keccak256(abi.encode(a)); - LibSort.reverse(a); - assertEq(a, reversed); - LibSort.reverse(a); - assertEq(originalHash, keccak256(abi.encode(a))); - } - } - } - - function testSortedUnionDifferential(uint256) public { - (uint256[] memory a, uint256[] memory b) = _randomUintsPair(); - uint256[] memory c = LibSort.union(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _unionOriginal(a, b)); - } - - function testSortedUnionDifferential() public { - unchecked { - for (uint256 t; t != 16; ++t) { - testSortedUnionDifferential(t); - } - } - } - - function testSortedUnionDifferentialInt(uint256) public { - (int256[] memory a, int256[] memory b) = _randomIntsPair(); - int256[] memory c = LibSort.union(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _unionOriginal(a, b)); - } - - function testSortedIntersectionDifferential(uint256) public { - (uint256[] memory a, uint256[] memory b) = _randomUintsPair(); - uint256[] memory c = LibSort.intersection(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _intersectionOriginal(a, b)); - } - - function testSortedIntersectionDifferential() public { - unchecked { - for (uint256 t; t != 16; ++t) { - testSortedIntersectionDifferential(t); - } - } - } - - function testSortedIntersectionDifferentialInt(uint256) public { - (int256[] memory a, int256[] memory b) = _randomIntsPair(); - int256[] memory c = LibSort.intersection(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _intersectionOriginal(a, b)); - } - - function testSortedDifferenceDifferential(uint256) public { - (uint256[] memory a, uint256[] memory b) = _randomUintsPair(); - uint256[] memory c = LibSort.difference(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _differenceOriginal(a, b)); - } - - function testSortedDifferenceDifferential() public { - unchecked { - for (uint256 t; t != 16; ++t) { - testSortedDifferenceDifferential(t); - } - } - } - - function testSortedDifferenceDifferentialInt(uint256) public { - (int256[] memory a, int256[] memory b) = _randomIntsPair(); - int256[] memory c = LibSort.difference(a, b); - assertTrue(_isSorted(c)); - assertEq(c, _differenceOriginal(a, b)); - } - - function testSortedDifferenceUnionIntersection(uint256) public { - unchecked { - bool found; - (uint256[] memory a, uint256[] memory b) = _randomUintsPair(); - - uint256[] memory aSubB = LibSort.difference(a, b); - assertTrue(_isSorted(aSubB)); - for (uint256 i; i != aSubB.length; ++i) { - (found,) = LibSort.searchSorted(a, aSubB[i]); - assertTrue(found); - (found,) = LibSort.searchSorted(b, aSubB[i]); - assertFalse(found); - } - for (uint256 i; i != b.length; ++i) { - (found,) = LibSort.searchSorted(aSubB, b[i]); - assertFalse(found); - } - - uint256[] memory bSubA = LibSort.difference(b, a); - assertTrue(_isSorted(bSubA)); - for (uint256 i; i != bSubA.length; ++i) { - (found,) = LibSort.searchSorted(b, bSubA[i]); - assertTrue(found); - (found,) = LibSort.searchSorted(a, bSubA[i]); - assertFalse(found); - } - for (uint256 i; i != a.length; ++i) { - (found,) = LibSort.searchSorted(bSubA, a[i]); - assertFalse(found); - } - - uint256[] memory aIntersectionB = LibSort.intersection(a, b); - for (uint256 i; i != aIntersectionB.length; ++i) { - (found,) = LibSort.searchSorted(b, aIntersectionB[i]); - assertTrue(found); - (found,) = LibSort.searchSorted(a, aIntersectionB[i]); - assertTrue(found); - } - - uint256[] memory aUnionB = LibSort.union(a, b); - uint256[] memory aSubBUnionBSubA = LibSort.union(aSubB, bSubA); - uint256[] memory emptySet; - assertEq(emptySet, LibSort.intersection(aSubB, bSubA)); - assertEq(emptySet, LibSort.intersection(aSubBUnionBSubA, aIntersectionB)); - assertEq(a, LibSort.union(aIntersectionB, aSubB)); - assertEq(b, LibSort.union(aIntersectionB, bSubA)); - assertEq(aIntersectionB, LibSort.intersection(b, a)); - assertEq(aUnionB, LibSort.union(b, a)); - assertEq(LibSort.union(aSubB, b), LibSort.union(b, aSubB)); - assertEq(LibSort.union(bSubA, a), LibSort.union(a, bSubA)); - assertEq(aUnionB, LibSort.union(aSubBUnionBSubA, aIntersectionB)); - } - } - - function testIsSortedDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = _random() % 4; - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - LibSort.sort(a); - assertEq(LibSort.isSorted(a), _isSorted(a)); - if (n != 0) { - a[_random() % n] = 0; - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - } - } - - function testIsSortedIntsDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - int256[] memory a = new int256[](n); - for (uint256 i; i != n; ++i) { - a[i] = int256(_random() % 4); - if (_random() % 2 == 0) { - a[i] = -a[i]; - } - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - LibSort.sort(a); - assertEq(LibSort.isSorted(a), _isSorted(a)); - if (n != 0) { - a[_random() % n] = 0; - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - } - } - - function testIsSortedAddressesDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - a[i] = address(uint160(_random() % 4)); - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - LibSort.sort(a); - assertEq(LibSort.isSorted(a), _isSorted(a)); - if (n != 0) { - a[_random() % n] = address(0); - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSorted(a), _isSorted(a)); - } - } - - function testIsSortedAndUniquifiedDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - uint256[] memory a = new uint256[](n); - for (uint256 i; i != n; ++i) { - a[i] = _random() % 4; - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - LibSort.sort(a); - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - if (n != 0) { - a[_random() % n] = 0; - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - } - } - - function testIsSortedAndUniquifiedIntsDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - int256[] memory a = new int256[](n); - for (uint256 i; i != n; ++i) { - a[i] = int256(_random() % 4); - if (_random() % 2 == 0) { - a[i] = -a[i]; - } - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - LibSort.sort(a); - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - if (n != 0) { - a[_random() % n] = 0; - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - } - } - - function testIsSortedAndUniquifiedAddressesDifferential(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? _random() % 4 : _randomArrayLength(); - address[] memory a = new address[](n); - for (uint256 i; i != n; ++i) { - a[i] = address(uint160(_random() % 4)); - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - LibSort.sort(a); - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - if (n != 0) { - a[_random() % n] = address(0); - if (_random() % 2 == 0) { - a[_random() % n] = a[_random() % n]; - } - } - assertEq(LibSort.isSortedAndUniquified(a), _isSortedAndUniquified(a)); - } - } - - function _unionOriginal(uint256[] memory a, uint256[] memory b) - private - pure - returns (uint256[] memory c) - { - unchecked { - c = new uint256[](a.length + b.length); - uint256 o; - for (uint256 i; i != a.length; ++i) { - c[o++] = a[i]; - } - for (uint256 i; i != b.length; ++i) { - c[o++] = b[i]; - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _unionOriginal(int256[] memory a, int256[] memory b) - private - pure - returns (int256[] memory c) - { - unchecked { - c = new int256[](a.length + b.length); - uint256 o; - for (uint256 i; i != a.length; ++i) { - c[o++] = a[i]; - } - for (uint256 i; i != b.length; ++i) { - c[o++] = b[i]; - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _intersectionOriginal(uint256[] memory a, uint256[] memory b) - private - pure - returns (uint256[] memory c) - { - unchecked { - c = new uint256[](a.length + b.length); - uint256 o; - bool found; - for (uint256 i; i != a.length; ++i) { - (found,) = LibSort.searchSorted(b, a[i]); - if (found) c[o++] = a[i]; - } - /// @solidity memory-safe-assembly - assembly { - mstore(c, o) - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _intersectionOriginal(int256[] memory a, int256[] memory b) - private - pure - returns (int256[] memory c) - { - unchecked { - c = new int256[](a.length + b.length); - uint256 o; - bool found; - for (uint256 i; i != a.length; ++i) { - (found,) = LibSort.searchSorted(b, a[i]); - if (found) c[o++] = a[i]; - } - /// @solidity memory-safe-assembly - assembly { - mstore(c, o) - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _differenceOriginal(uint256[] memory a, uint256[] memory b) - private - pure - returns (uint256[] memory c) - { - unchecked { - c = new uint256[](a.length + b.length); - uint256 o; - bool found; - for (uint256 i; i != a.length; ++i) { - (found,) = LibSort.searchSorted(b, a[i]); - if (!found) c[o++] = a[i]; - } - /// @solidity memory-safe-assembly - assembly { - mstore(c, o) - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _differenceOriginal(int256[] memory a, int256[] memory b) - private - pure - returns (int256[] memory c) - { - unchecked { - c = new int256[](a.length + b.length); - uint256 o; - bool found; - for (uint256 i; i != a.length; ++i) { - (found,) = LibSort.searchSorted(b, a[i]); - if (!found) c[o++] = a[i]; - } - /// @solidity memory-safe-assembly - assembly { - mstore(c, o) - } - LibSort.insertionSort(c); - LibSort.uniquifySorted(c); - } - } - - function _isSorted(address[] memory a) private pure returns (bool) { - unchecked { - for (uint256 i = 1; i < a.length; ++i) { - if (a[i - 1] > a[i]) return false; - } - return true; - } - } - - function _isSorted(uint256[] memory a) private pure returns (bool) { - unchecked { - for (uint256 i = 1; i < a.length; ++i) { - if (a[i - 1] > a[i]) return false; - } - return true; - } - } - - function _isSorted(int256[] memory a) private pure returns (bool) { - unchecked { - for (uint256 i = 1; i < a.length; ++i) { - if (a[i - 1] > a[i]) return false; - } - return true; - } - } - - function _isSortedAndUniquified(uint256[] memory a) private pure returns (bool) { - if (a.length == 0) { - return true; - } - unchecked { - uint256 end = a.length - 1; - for (uint256 i = 0; i != end; ++i) { - if (a[i] >= a[i + 1]) { - return false; - } - } - return true; - } - } - - function _isSortedAndUniquified(int256[] memory a) private pure returns (bool) { - if (a.length == 0) { - return true; - } - unchecked { - uint256 end = a.length - 1; - for (uint256 i = 0; i != end; ++i) { - if (a[i] >= a[i + 1]) { - return false; - } - } - return true; - } - } - - function _isSortedAndUniquified(address[] memory a) private pure returns (bool) { - if (a.length == 0) { - return true; - } - unchecked { - uint256 end = a.length - 1; - for (uint256 i = 0; i != end; ++i) { - if (a[i] >= a[i + 1]) { - return false; - } - } - return true; - } - } - - function _sortOriginal(uint256[] memory a) internal pure { - _sortOriginal(a, 0, int256(a.length - 1)); - } - - function _sortOriginal(uint256[] memory arr, int256 left, int256 right) internal pure { - int256 i = left; - int256 j = right; - if (i == j) return; - uint256 pivot = arr[uint256(left + (right - left) / 2)]; - while (i <= j) { - while (arr[uint256(i)] < pivot) { - unchecked { - ++i; - } - } - while (pivot < arr[uint256(j)]) { - unchecked { - --j; - } - } - if (i <= j) { - (arr[uint256(i)], arr[uint256(j)]) = (arr[uint256(j)], arr[uint256(i)]); - unchecked { - ++i; - --j; - } - } - } - if (left < j) _sortOriginal(arr, left, j); - if (i < right) _sortOriginal(arr, i, right); - } - - function _copy(uint256[] memory a) private view returns (uint256[] memory b) { - /// @solidity memory-safe-assembly - assembly { - b := mload(0x40) - let n := add(shl(5, mload(a)), 0x20) - pop(staticcall(gas(), 4, a, n, b, n)) - mstore(0x40, add(b, n)) - } - } - - function _copy(int256[] memory a) private view returns (int256[] memory b) { - /// @solidity memory-safe-assembly - assembly { - b := mload(0x40) - let n := add(shl(5, mload(a)), 0x20) - pop(staticcall(gas(), 4, a, n, b, n)) - mstore(0x40, add(b, n)) - } - } - - function _copy(address[] memory a) private view returns (address[] memory b) { - /// @solidity memory-safe-assembly - assembly { - b := mload(0x40) - let n := add(shl(5, mload(a)), 0x20) - pop(staticcall(gas(), 4, a, n, b, n)) - mstore(0x40, add(b, n)) - } - } - - function _randomUints(uint256 n) private returns (uint256[] memory a) { - unchecked { - _misalignFreeMemoryPointer(); - /// @solidity memory-safe-assembly - assembly { - a := mload(0x40) - mstore(a, n) - mstore(0x40, add(add(0x20, a), shl(5, n))) - } - for (uint256 i; i != n; ++i) { - a[i] = _random(); - } - } - } - - function _randomAddresses(uint256 n) private returns (address[] memory a) { - unchecked { - _misalignFreeMemoryPointer(); - /// @solidity memory-safe-assembly - assembly { - a := mload(0x40) - mstore(a, n) - mstore(0x40, add(add(0x20, a), shl(5, n))) - } - for (uint256 i; i != n; ++i) { - a[i] = address(uint160(_random())); - } - } - } - - function _randomInts(uint256 n) private returns (int256[] memory a) { - unchecked { - uint256[] memory aRaw = _randomUints(n); - /// @solidity memory-safe-assembly - assembly { - a := aRaw - } - } - } - - function _uniquifyOriginal(uint256[] memory a) private pure { - if (a.length != 0) { - unchecked { - uint256 n = a.length; - uint256 i = 0; - for (uint256 j = 1; j < n; j++) { - if (a[i] != a[j]) { - i++; - a[i] = a[j]; - } - } - /// @solidity memory-safe-assembly - assembly { - mstore(a, add(i, 1)) - } - } - } - } - - function _randomUintsPair() private returns (uint256[] memory a, uint256[] memory b) { - uint256 r = _random(); - a = _randomUints(r & 7); - b = _randomUints((r >> 128) & 7); - LibSort.insertionSort(a); - LibSort.uniquifySorted(a); - LibSort.insertionSort(b); - LibSort.uniquifySorted(b); - } - - function _randomAddressesPair() private returns (address[] memory a, address[] memory b) { - uint256 r = _random(); - a = _randomAddresses(r & 7); - b = _randomAddresses((r >> 128) & 7); - LibSort.insertionSort(a); - LibSort.uniquifySorted(a); - LibSort.insertionSort(b); - LibSort.uniquifySorted(b); - } - - function _randomIntsPair() private returns (int256[] memory a, int256[] memory b) { - uint256 r = _random(); - a = _randomInts(r & 7); - b = _randomInts((r >> 128) & 7); - LibSort.insertionSort(a); - LibSort.uniquifySorted(a); - LibSort.insertionSort(b); - LibSort.uniquifySorted(b); - } - - function _randomArrayLength() internal returns (uint256 r) { - r = _random(); - /// @solidity memory-safe-assembly - assembly { - let m := 0x070707070707070707070707070707070f0f0f0f0f0f0f1f1f1f1f1f1f3f7fff - r := and(byte(1, r), byte(and(r, 31), m)) - } - } - - function _randomNonZeroArrayLength() internal returns (uint256 r) { - do { - r = _randomArrayLength(); - } while (r == 0); - } -} diff --git a/lib/solady/test/LibString.t.sol b/lib/solady/test/LibString.t.sol deleted file mode 100644 index c0f7ce3..0000000 --- a/lib/solady/test/LibString.t.sol +++ /dev/null @@ -1,1392 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract LibStringTest is SoladyTest { - function testToStringZero() public { - assertEq(LibString.toString(uint256(0)), "0"); - } - - function testToStringPositiveNumber() public { - assertEq(LibString.toString(uint256(4132)), "4132"); - } - - function testToStringUint256Max() public { - assertEq( - LibString.toString(type(uint256).max), - "115792089237316195423570985008687907853269984665640564039457584007913129639935" - ); - } - - function testToStringZeroBrutalized() public brutalizeMemory { - string memory s0 = LibString.toString(uint256(0)); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - string memory s1 = LibString.toString(uint256(0)); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - assertEq(s0, "0"); - assertEq(s1, "0"); - } - - function testToStringPositiveNumberBrutalized() public brutalizeMemory { - string memory s0 = LibString.toString(uint256(4132)); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - string memory s1 = LibString.toString(uint256(4132)); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - assertEq(s0, "4132"); - assertEq(s1, "4132"); - } - - function testToStringUint256MaxBrutalized() public brutalizeMemory { - string memory s0 = LibString.toString(type(uint256).max); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - string memory s1 = LibString.toString(type(uint256).max); - /// @solidity memory-safe-assembly - assembly { - mstore(mload(0x40), not(0)) - mstore(0x40, add(mload(0x40), 0x20)) - } - assertEq( - s0, "115792089237316195423570985008687907853269984665640564039457584007913129639935" - ); - assertEq( - s1, "115792089237316195423570985008687907853269984665640564039457584007913129639935" - ); - } - - function testToStringZeroRightPadded(uint256 x) public view brutalizeMemory { - _checkMemory(LibString.toString(x)); - } - - function testToStringSignedDifferential(int256 x) public brutalizeMemory { - assertEq(LibString.toString(x), _toStringSignedOriginal(x)); - } - - function testToStringSignedMemory(int256 x) public view brutalizeMemory { - _misalignFreeMemoryPointer(); - uint256 freeMemoryPointer; - /// @solidity memory-safe-assembly - assembly { - freeMemoryPointer := mload(0x40) - } - string memory str = LibString.toString(x); - /// @solidity memory-safe-assembly - assembly { - if lt(str, freeMemoryPointer) { revert(0, 0) } - } - _checkMemory(str); - } - - function testToStringSignedGas() public pure { - for (int256 x = -10; x < 10; ++x) { - LibString.toString(x); - } - } - - function testToStringSignedOriginalGas() public pure { - for (int256 x = -10; x < 10; ++x) { - _toStringSignedOriginal(x); - } - } - - function _toStringSignedOriginal(int256 x) internal pure returns (string memory) { - unchecked { - return x >= 0 - ? LibString.toString(uint256(x)) - : string(abi.encodePacked("-", LibString.toString(uint256(-x)))); - } - } - - function testToHexStringZero() public { - assertEq(LibString.toHexString(0), "0x00"); - } - - function testToHexStringPositiveNumber() public { - assertEq(LibString.toHexString(0x4132), "0x4132"); - } - - function testToHexStringUint256Max() public { - assertEq( - LibString.toHexString(type(uint256).max), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - } - - function testToHexStringFixedLengthPositiveNumberLong() public { - assertEq( - LibString.toHexString(0x4132, 32), - "0x0000000000000000000000000000000000000000000000000000000000004132" - ); - } - - function testToHexStringFixedLengthPositiveNumberShort() public { - assertEq(LibString.toHexString(0x4132, 2), "0x4132"); - } - - function testToHexStringZeroRightPadded(uint256 x) public pure { - _checkMemory(LibString.toHexString(x)); - } - - function testToHexStringFixedLengthInsufficientLength() public { - vm.expectRevert(LibString.HexLengthInsufficient.selector); - LibString.toHexString(0x4132, 1); - } - - function testToHexStringFixedLengthUint256Max() public { - assertEq( - LibString.toHexString(type(uint256).max, 32), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - } - - function testToHexStringFixedLengthZeroRightPadded(uint256 x, uint256 randomness) public pure { - uint256 minLength = (bytes(LibString.toHexString(x)).length - 2) * 2; - uint256 length = (randomness % 32) + minLength; - _checkMemory(LibString.toHexString(x, length)); - } - - function testFromAddressToHexString() public { - assertEq( - LibString.toHexString(0xA9036907dCcae6a1E0033479B12E837e5cF5a02f), - "0xa9036907dccae6a1e0033479b12e837e5cf5a02f" - ); - } - - function testAddressToHexStringZeroRightPadded(address x) public pure { - _checkMemory(LibString.toHexString(x)); - } - - function testFromAddressToHexStringWithLeadingZeros() public { - assertEq( - LibString.toHexString(0x0000E0Ca771e21bD00057F54A68C30D400000000), - "0x0000e0ca771e21bd00057f54a68c30d400000000" - ); - } - - function testToMinimalHexStringZero() public { - assertEq(LibString.toMinimalHexString(0), "0x0"); - } - - function testToMinimalHexStringPositiveNumber() public { - assertEq(LibString.toMinimalHexString(0x54132), "0x54132"); - assertEq(LibString.toMinimalHexString(0x4132), "0x4132"); - assertEq(LibString.toMinimalHexString(0x0123), "0x123"); - assertEq(LibString.toMinimalHexString(0x12), "0x12"); - assertEq(LibString.toMinimalHexString(0x1), "0x1"); - } - - function testToMinimalHexStringUint256Max() public { - assertEq( - LibString.toMinimalHexString(type(uint256).max), - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - } - - function testToMinimalHexStringZeroRightPadded(uint256 x) public pure { - _checkMemory(LibString.toMinimalHexString(x)); - } - - function testToMinimalHexStringNoPrefixZero() public { - assertEq(LibString.toMinimalHexStringNoPrefix(0), "0"); - } - - function testToMinimalHexStringNoPrefixPositiveNumber() public { - assertEq(LibString.toMinimalHexStringNoPrefix(0x54132), "54132"); - assertEq(LibString.toMinimalHexStringNoPrefix(0x4132), "4132"); - assertEq(LibString.toMinimalHexStringNoPrefix(0x0123), "123"); - assertEq(LibString.toMinimalHexStringNoPrefix(0x12), "12"); - assertEq(LibString.toMinimalHexStringNoPrefix(0x1), "1"); - } - - function testToMinimalHexStringNoPrefixUint256Max() public { - assertEq( - LibString.toMinimalHexStringNoPrefix(type(uint256).max), - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ); - } - - function testToMinimalHexStringNoPrefixZeroRightPadded(uint256 x) public pure { - _checkMemory(LibString.toMinimalHexStringNoPrefix(x)); - } - - function testFromAddressToHexStringChecksummed() public { - // All caps. - assertEq( - LibString.toHexStringChecksummed(0x52908400098527886E0F7030069857D2E4169EE7), - "0x52908400098527886E0F7030069857D2E4169EE7" - ); - assertEq( - LibString.toHexStringChecksummed(0x8617E340B3D01FA5F11F306F4090FD50E238070D), - "0x8617E340B3D01FA5F11F306F4090FD50E238070D" - ); - // All lower. - assertEq( - LibString.toHexStringChecksummed(0xde709f2102306220921060314715629080e2fb77), - "0xde709f2102306220921060314715629080e2fb77" - ); - assertEq( - LibString.toHexStringChecksummed(0x27b1fdb04752bbc536007a920d24acb045561c26), - "0x27b1fdb04752bbc536007a920d24acb045561c26" - ); - // Normal. - assertEq( - LibString.toHexStringChecksummed(0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed), - "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" - ); - assertEq( - LibString.toHexStringChecksummed(0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359), - "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359" - ); - assertEq( - LibString.toHexStringChecksummed(0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB), - "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB" - ); - assertEq( - LibString.toHexStringChecksummed(0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb), - "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb" - ); - } - - function testFromAddressToHexStringChecksummedDifferential(uint256 randomness) - public - brutalizeMemory - { - address r; - /// @solidity memory-safe-assembly - assembly { - r := randomness - } - string memory expectedResult = LibString.toHexString(r); - /// @solidity memory-safe-assembly - assembly { - let o := add(expectedResult, 0x22) - let hashed := keccak256(o, 40) - // forgefmt: disable-next-item - for { let i := 0 } iszero(eq(i, 20)) { i := add(i, 1) } { - let temp := byte(i, hashed) - let p := add(o, add(i, i)) - let c0 := byte(0, mload(p)) - let c1 := byte(1, mload(p)) - if and(gt(c1, 58), gt(and(temp, 15), 7)) { - mstore8(add(p, 1), sub(c1, 32)) - } - if and(gt(c0, 58), gt(shr(4, temp), 7)) { - mstore8(p, sub(c0, 32)) - } - } - } - string memory checksummed = LibString.toHexStringChecksummed(r); - _checkMemory(checksummed); - assertEq(checksummed, expectedResult); - } - - function testHexStringNoPrefixVariants(uint256 x, uint256 randomness) public brutalizeMemory { - string memory noPrefix = LibString.toHexStringNoPrefix(x); - _checkMemory(noPrefix); - string memory expectedResult = LibString.concat("0x", noPrefix); - string memory withPrefix = LibString.toHexString(x); - _checkMemory(withPrefix); - assertEq(withPrefix, expectedResult); - - uint256 length; - /// @solidity memory-safe-assembly - assembly { - length := add(shr(1, mload(noPrefix)), and(randomness, 63)) - } - _misalignFreeMemoryPointer(); - noPrefix = LibString.toHexStringNoPrefix(x, length); - _checkMemory(noPrefix); - expectedResult = LibString.concat("0x", noPrefix); - _misalignFreeMemoryPointer(); - withPrefix = LibString.toHexString(x, length); - _checkMemory(withPrefix); - assertEq(withPrefix, expectedResult); - - address xAddress; - /// @solidity memory-safe-assembly - assembly { - xAddress := x - } - _misalignFreeMemoryPointer(); - noPrefix = LibString.toHexStringNoPrefix(xAddress); - _checkMemory(noPrefix); - expectedResult = LibString.concat("0x", noPrefix); - _misalignFreeMemoryPointer(); - withPrefix = LibString.toHexString(xAddress); - _checkMemory(withPrefix); - assertEq(withPrefix, expectedResult); - } - - function testBytesToHexStringNoPrefix() public { - assertEq(LibString.toHexStringNoPrefix(""), ""); - assertEq(LibString.toHexStringNoPrefix("A"), "41"); - assertEq( - LibString.toHexStringNoPrefix("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), - "4142434445464748494a4b4c4d4e4f505152535455565758595a" - ); - } - - function testBytesToHexStringNoPrefix(bytes memory raw) public brutalizeMemory { - string memory converted = LibString.toHexStringNoPrefix(raw); - _checkMemory(converted); - unchecked { - bytes memory hexChars = "0123456789abcdef"; - for (uint256 i; i != raw.length; ++i) { - uint256 t = uint8(bytes1(raw[i])); - assertTrue(hexChars[t & 15] == bytes(converted)[i * 2 + 1]); - assertTrue(hexChars[(t >> 4) & 15] == bytes(converted)[i * 2]); - } - } - } - - function testBytesToHexString() public { - assertEq(LibString.toHexString(""), "0x"); - assertEq(LibString.toHexString("A"), "0x41"); - assertEq( - LibString.toHexString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), - "0x4142434445464748494a4b4c4d4e4f505152535455565758595a" - ); - } - - function testBytesToHexString(bytes memory raw) public brutalizeMemory { - string memory converted = LibString.toHexString(raw); - _checkMemory(converted); - unchecked { - bytes memory hexChars = "0123456789abcdef"; - for (uint256 i; i != raw.length; ++i) { - uint256 t = uint8(bytes1(raw[i])); - assertTrue(hexChars[t & 15] == bytes(converted)[i * 2 + 1 + 2]); - assertTrue(hexChars[(t >> 4) & 15] == bytes(converted)[i * 2 + 2]); - } - } - } - - function testStringIs7BitASCII() public { - bytes memory raw = new bytes(1); - for (uint256 i; i < 256; ++i) { - raw[0] = bytes1(uint8(i)); - assertEq(LibString.is7BitASCII(string(raw)), i < 128); - assertEq(LibString.is7BitASCII(string(raw)), _is7BitASCIIOriginal(string(raw))); - } - } - - function testStringIs7BitASCIIDifferential(bytes memory raw) public brutalizeMemory { - string memory s = string(raw); - bytes32 hashBefore = keccak256(raw); - assertEq(LibString.is7BitASCII(s), _is7BitASCIIOriginal(s)); - assertEq(keccak256(raw), hashBefore); - /// @solidity memory-safe-assembly - assembly { - mstore(add(raw, add(0x20, mload(raw))), hashBefore) - } - assertEq(LibString.is7BitASCII(s), _is7BitASCIIOriginal(s)); - assertEq(keccak256(raw), hashBefore); - /// @solidity memory-safe-assembly - assembly { - if iszero(eq(mload(add(raw, add(0x20, mload(raw)))), hashBefore)) { revert(0, 0) } - } - } - - function testStringRuneCountDifferential(string memory s) public { - assertEq(LibString.runeCount(s), _runeCountOriginal(s)); - } - - function testStringRuneCount() public { - unchecked { - string memory runes = new string(256); - for (uint256 i; i < 256; ++i) { - /// @solidity memory-safe-assembly - assembly { - mstore8(add(add(runes, 0x20), i), i) - } - } - for (uint256 i; i < 256; ++i) { - string memory s = _generateString(runes); - testStringRuneCountDifferential(s); - } - } - } - - function testStringReplaceShort() public { - assertEq(LibString.replace("abc", "", "_@"), "_@a_@b_@c_@"); - assertEq(LibString.replace("abc", "a", "_"), "_bc"); - assertEq(LibString.replace("abc", "b", "_"), "a_c"); - assertEq(LibString.replace("abc", "c", "_"), "ab_"); - assertEq(LibString.replace("abc", "ab", "_"), "_c"); - assertEq(LibString.replace("abc", "bc", "_"), "a_"); - assertEq(LibString.replace("abc", "ac", "_"), "abc"); - assertEq(LibString.replace("abc", "a", ""), "bc"); - assertEq(LibString.replace("abc", "", ""), "abc"); - assertEq(LibString.replace("abc", "d", "x"), "abc"); - } - - function testStringReplaceMedium() public { - // forgefmt: disable-next-item - string memory subject = "70708741044725766535585242414884609539555049888764130733849700923779599488691391677696419266840"; - string memory search = "46095395550498887641307338497009"; - string memory replacement = "320807383223517906783031356692334377159141"; - // forgefmt: disable-next-item - string memory expectedResult = "707087410447257665355852424148832080738322351790678303135669233437715914123779599488691391677696419266840"; - assertEq(LibString.replace(subject, search, replacement), expectedResult); - } - - function testStringReplaceLong() public { - // forgefmt: disable-next-item - string memory subject = "01234567890123456789012345678901_search_search_search_search_search_search_23456789012345678901234567890123456789_search_search_search_search_search_search"; - string memory search = "search_search_search_search_search_search"; - string memory replacement = "REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT"; - // forgefmt: disable-next-item - string memory expectedResult = "01234567890123456789012345678901_REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT_23456789012345678901234567890123456789_REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT_REPLACEMENT"; - assertEq(LibString.replace(subject, search, replacement), expectedResult); - } - - function testStringReplace(uint256) public brutalizeMemory { - string memory filler = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - string memory replacement = _generateString("0123456790_-+/=|{}<>!"); - if (bytes(search).length != 0) { - string memory subject = string( - bytes.concat( - bytes(filler), bytes(search), bytes(filler), bytes(search), bytes(filler) - ) - ); - _misalignFreeMemoryPointer(); - string memory expectedResult = string( - bytes.concat( - bytes(filler), - bytes(replacement), - bytes(filler), - bytes(replacement), - bytes(filler) - ) - ); - _misalignFreeMemoryPointer(); - string memory replaced = LibString.replace(subject, search, replacement); - _checkMemory(replaced); - assertEq(replaced, expectedResult); - } else { - string memory expectedResult = string( - bytes.concat( - bytes(replacement), - bytes(" "), - bytes(replacement), - bytes(" "), - bytes(replacement), - bytes(" "), - bytes(replacement) - ) - ); - string memory replaced = LibString.replace(" ", search, replacement); - assertEq(replaced, expectedResult); - } - } - - function testStringIndexOf(uint256) public brutalizeMemory { - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - - string memory subject = string(bytes.concat(bytes(filler0), bytes(search), bytes(filler1))); - - uint256 from = _generateFrom(subject); - - if (bytes(search).length == 0) { - if (from > bytes(subject).length) { - assertEq(LibString.indexOf(subject, search, from), bytes(subject).length); - } else { - assertEq(LibString.indexOf(subject, search, from), from); - } - } else { - if (from > bytes(filler0).length) { - assertEq(LibString.indexOf(subject, search, from), LibString.NOT_FOUND); - } else { - assertEq(LibString.indexOf(subject, search, from), bytes(filler0).length); - } - } - } - - function testStringIndexOf() public { - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.indexOf(subject, ""), 0); - assertEq(LibString.indexOf(subject, "", 16), 16); - assertEq(LibString.indexOf(subject, "", 17), 17); - assertEq(LibString.indexOf(subject, "", 52), 52); - assertEq(LibString.indexOf(subject, "", 53), 52); - assertEq(LibString.indexOf(subject, "", 555), 52); - assertEq(LibString.indexOf(subject, "abc", 0), 0); - assertEq(LibString.indexOf(subject, "abc", 1), LibString.NOT_FOUND); - assertEq(LibString.indexOf(subject, "bcd"), 1); - assertEq(LibString.indexOf(subject, "XYZ"), 49); - assertEq(LibString.indexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"), 16); - assertEq(LibString.indexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 16); - assertEq(LibString.indexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 16), 16); - assertEq( - LibString.indexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 17), - LibString.NOT_FOUND - ); - - assertEq(LibString.indexOf("abcabcabc", "abc"), 0); - assertEq(LibString.indexOf("abcabcabc", "abc", 1), 3); - - assertEq(LibString.indexOf("a", "bcd"), LibString.NOT_FOUND); - assertEq(LibString.indexOf("a", "bcd", 0), LibString.NOT_FOUND); - assertEq(LibString.indexOf("accd", "bcd"), LibString.NOT_FOUND); - assertEq(LibString.indexOf("", "bcd"), LibString.NOT_FOUND); - } - - function testStringLastIndexOf(uint256) public brutalizeMemory { - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - - string memory subject = string(bytes.concat(bytes(filler0), bytes(search), bytes(filler1))); - - uint256 from = _generateFrom(subject); - - if (bytes(search).length == 0) { - if (from > bytes(subject).length) { - assertEq(LibString.lastIndexOf(subject, search, from), bytes(subject).length); - } else { - assertEq(LibString.lastIndexOf(subject, search, from), from); - } - } else { - if (from < bytes(filler0).length) { - assertEq(LibString.lastIndexOf(subject, search, from), LibString.NOT_FOUND); - } else { - assertEq(LibString.lastIndexOf(subject, search, from), bytes(filler0).length); - } - } - } - - function testStringLastIndexOf() public { - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.lastIndexOf(subject, "", 0), 0); - assertEq(LibString.lastIndexOf(subject, "", 16), 16); - assertEq(LibString.lastIndexOf(subject, "", 17), 17); - assertEq(LibString.lastIndexOf(subject, "", 52), 52); - assertEq(LibString.lastIndexOf(subject, "", 53), 52); - assertEq(LibString.lastIndexOf(subject, "", 555), 52); - assertEq(LibString.lastIndexOf(subject, "abc"), 0); - assertEq(LibString.lastIndexOf(subject, "abc", 0), 0); - assertEq(LibString.lastIndexOf(subject, "abc", 1), 0); - assertEq(LibString.lastIndexOf(subject, "abc", 3), 0); - assertEq(LibString.lastIndexOf(subject, "bcd"), 1); - assertEq(LibString.lastIndexOf(subject, "bcd", 1), 1); - assertEq(LibString.lastIndexOf(subject, "bcd", 0), LibString.NOT_FOUND); - assertEq(LibString.lastIndexOf(subject, "XYZ"), 49); - assertEq(LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW"), 16); - assertEq(LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 16); - assertEq(LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 52), 16); - assertEq(LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 51), 16); - assertEq(LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 16), 16); - assertEq( - LibString.lastIndexOf(subject, "qrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 15), - LibString.NOT_FOUND - ); - - assertEq(LibString.lastIndexOf("abcabcabc", "abc"), 6); - assertEq(LibString.lastIndexOf("abcabcabc", "abc", 5), 3); - - assertEq(LibString.lastIndexOf("a", "bcd"), LibString.NOT_FOUND); - assertEq(LibString.lastIndexOf("a", "bcd", 0), LibString.NOT_FOUND); - assertEq(LibString.lastIndexOf("accd", "bcd"), LibString.NOT_FOUND); - assertEq(LibString.lastIndexOf("", "bcd"), LibString.NOT_FOUND); - } - - function testContains() public { - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.contains(subject, "a"), true); - assertEq(LibString.contains(subject, "abc"), true); - assertEq(LibString.contains(subject, "z"), true); - assertEq(LibString.contains(subject, "Z"), true); - assertEq(LibString.contains(subject, "az"), false); - assertEq(LibString.contains(subject, "aZ"), false); - assertEq(LibString.contains(subject, "Aa"), false); - assertEq(LibString.contains(subject, "Zz"), false); - assertEq(LibString.contains(subject, "abcZ"), false); - assertEq(LibString.contains(subject, "abcz"), false); - assertEq(LibString.contains(subject, "abcA"), false); - assertEq(LibString.contains(subject, "abcB"), false); - assertEq(LibString.contains(subject, "abcC"), false); - assertEq(LibString.contains(subject, ""), true); - assertEq(LibString.contains("", "abc"), false); - assertEq(LibString.contains("", ""), true); - } - - function testStringStartsWith(uint256) public brutalizeMemory { - string memory filler = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - - if (bytes(search).length == 0) { - string memory subject = string(bytes.concat(bytes(filler), bytes(search))); - assertEq(LibString.startsWith(subject, search), true); - } - - if (_random() & 1 == 1) { - string memory subject = string(bytes.concat(bytes(search), bytes(filler))); - assertEq(LibString.startsWith(subject, search), true); - } - - if (bytes(filler).length != 0 && bytes(search).length != 0) { - string memory subject = string(bytes.concat(bytes(filler), bytes(search))); - assertEq(LibString.startsWith(subject, search), false); - } - } - - function testStringStartsWith() public { - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.startsWith(subject, "abc"), true); - assertEq(LibString.startsWith(subject, "abcdefghijklmnopqrstuvwxyzABCDEFG"), true); - assertEq(LibString.startsWith(subject, "bcd"), false); - assertEq(LibString.startsWith(subject, "bcdefghijklmnopqrstuvwxyzABCDEFGH"), false); - - assertEq(LibString.startsWith("", ""), true); - assertEq(LibString.startsWith("bc", ""), true); - assertEq(LibString.startsWith("bc", "bc"), true); - assertEq(LibString.startsWith("bc", "abc"), false); - assertEq(LibString.startsWith("", "abc"), false); - } - - function testStringEndsWith(uint256) public brutalizeMemory { - string memory filler = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - - if (bytes(search).length == 0) { - string memory subject = string(bytes.concat(bytes(search), bytes(filler))); - assertEq(LibString.endsWith(subject, search), true); - } - - if (_random() & 1 == 1) { - string memory subject = string(bytes.concat(bytes(filler), bytes(search))); - assertEq(LibString.endsWith(subject, search), true); - } - - if (bytes(filler).length != 0 && bytes(search).length != 0) { - string memory subject = string(bytes.concat(bytes(search), bytes(filler))); - assertEq(LibString.endsWith(subject, search), false); - } - } - - function testStringEndsWith() public { - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.endsWith(subject, "XYZ"), true); - assertEq(LibString.endsWith(subject, "pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), true); - assertEq(LibString.endsWith(subject, "WXY"), false); - assertEq(LibString.endsWith(subject, "opqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"), false); - - assertEq(LibString.endsWith("", ""), true); - assertEq(LibString.endsWith("bc", ""), true); - assertEq(LibString.endsWith("bc", "bc"), true); - assertEq(LibString.endsWith("bc", "abc"), false); - assertEq(LibString.endsWith("", "abc"), false); - } - - function testStringRepeat(string memory subject, uint256 times) public brutalizeMemory { - times = times % 8; - string memory repeated = LibString.repeat(subject, times); - string memory expectedResult = _repeatOriginal(subject, times); - _checkMemory(repeated); - assertEq(repeated, expectedResult); - } - - function testStringRepeat() public { - assertEq(LibString.repeat("", 0), ""); - assertEq(LibString.repeat("", 100), ""); - assertEq(LibString.repeat("a", 0), ""); - assertEq(LibString.repeat("a", 1), "a"); - assertEq(LibString.repeat("a", 3), "aaa"); - assertEq(LibString.repeat("abc", 0), ""); - assertEq(LibString.repeat("abc", 1), "abc"); - assertEq(LibString.repeat("abc", 3), "abcabcabc"); - assertEq(LibString.repeat("efghi", 3), "efghiefghiefghi"); - } - - function testStringRepeatOriginal() public { - assertEq(_repeatOriginal("", 0), ""); - assertEq(_repeatOriginal("", 100), ""); - assertEq(_repeatOriginal("a", 0), ""); - assertEq(_repeatOriginal("a", 1), "a"); - assertEq(_repeatOriginal("a", 3), "aaa"); - assertEq(_repeatOriginal("abc", 0), ""); - assertEq(_repeatOriginal("abc", 1), "abc"); - assertEq(_repeatOriginal("abc", 3), "abcabcabc"); - assertEq(_repeatOriginal("efghi", 3), "efghiefghiefghi"); - } - - function testStringSlice(uint256) public brutalizeMemory { - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory expectedResult = _generateString("abcdefghijklmnopqrstuvwxyz"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - - string memory subject = - string(bytes.concat(bytes(filler0), bytes(expectedResult), bytes(filler1))); - - uint256 start = bytes(filler0).length; - uint256 end = start + bytes(expectedResult).length; - - _misalignFreeMemoryPointer(); - string memory slice = LibString.slice(subject, start, end); - _checkMemory(slice); - assertEq(slice, expectedResult); - } - - function testStringSlice() public { - assertEq(LibString.slice("", 0, 1), ""); - assertEq(LibString.slice("", 1, 0), ""); - assertEq(LibString.slice("", 0, 0), ""); - assertEq(LibString.slice("", 0), ""); - assertEq(LibString.slice("", 1), ""); - - assertEq(LibString.slice("a", 0), "a"); - assertEq(LibString.slice("a", 1), ""); - assertEq(LibString.slice("a", 3), ""); - - assertEq(LibString.slice("abc", 0), "abc"); - assertEq(LibString.slice("abc", 1), "bc"); - assertEq(LibString.slice("abc", 1, 2), "b"); - assertEq(LibString.slice("abc", 3), ""); - - string memory subject = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - assertEq(LibString.slice(subject, 0), subject); - assertEq(LibString.slice(subject, 1), "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - assertEq( - LibString.slice(subject, 1, 51), "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" - ); - assertEq(LibString.slice(subject, 11, 41), "lmnopqrstuvwxyzABCDEFGHIJKLMNO"); - assertEq(LibString.slice(subject, 21, 31), "vwxyzABCDE"); - assertEq(LibString.slice(subject, 31, 21), ""); - } - - function testStringIndicesOf(uint256) public brutalizeMemory { - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory search = _generateString("abcdefghijklmnopqrstuvwxyz"); - - string memory subject; - - unchecked { - uint256[] memory indices; - if (_random() & 1 == 0) { - subject = string(bytes.concat(bytes(filler0), bytes(search), bytes(filler1))); - indices = new uint256[](1); - indices[0] = bytes(filler0).length; - } else { - subject = string(bytes.concat(bytes(filler0), bytes(filler1))); - indices = new uint256[](0); - } - - if (bytes(search).length == 0) { - indices = new uint256[](bytes(subject).length + 1); - for (uint256 i; i < indices.length; ++i) { - indices[i] = i; - } - } - assertEq(LibString.indicesOf(subject, search), indices); - } - } - - function testStringIndicesOf() public { - uint256[] memory indices; - - indices = new uint256[](3); - indices[0] = 0; - indices[1] = 2; - indices[2] = 4; - assertEq(LibString.indicesOf("ababa", "a"), indices); - - indices = new uint256[](6); - indices[0] = 0; - indices[1] = 1; - indices[2] = 2; - indices[3] = 3; - indices[4] = 4; - indices[5] = 5; - assertEq(LibString.indicesOf("ababa", ""), indices); - - indices = new uint256[](2); - indices[0] = 1; - indices[1] = 3; - assertEq(LibString.indicesOf("ababa", "b"), indices); - - indices = new uint256[](2); - indices[0] = 0; - indices[1] = 2; - assertEq(LibString.indicesOf("ababa", "ab"), indices); - - indices = new uint256[](2); - indices[0] = 1; - indices[1] = 3; - assertEq(LibString.indicesOf("ababa", "ba"), indices); - - indices = new uint256[](1); - indices[0] = 1; - assertEq(LibString.indicesOf("ababa", "bab"), indices); - - indices = new uint256[](1); - indices[0] = 0; - assertEq(LibString.indicesOf("ababa", "ababa"), indices); - - indices = new uint256[](1); - indices[0] = 0; - assertEq(LibString.indicesOf("", ""), indices); - - indices = new uint256[](0); - assertEq(LibString.indicesOf("ababa", "c"), indices); - - indices = new uint256[](0); - assertEq(LibString.indicesOf("ababab", "abababa"), indices); - } - - function testStringSplit(uint256) public brutalizeMemory { - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory delimiter = _generateString("abcdefghijklmnopqrstuvwxyz"); - - string memory subject = - string(bytes.concat(bytes(filler0), bytes(delimiter), bytes(filler1))); - - unchecked { - string[] memory elements; - if (bytes(delimiter).length == 0) { - elements = new string[](bytes(subject).length); - for (uint256 i; i < elements.length; ++i) { - elements[i] = LibString.slice(subject, i, i + 1); - } - } else { - elements = new string[](2); - elements[0] = filler0; - elements[1] = filler1; - } - _misalignFreeMemoryPointer(); - string[] memory split = LibString.split(subject, delimiter); - assertTrue(_stringArraysAreSame(split, elements)); - for (uint256 i; i < split.length; ++i) { - _checkMemory(split[i]); - } - } - } - - function testStringSplit() public { - string[] memory elements; - - elements = new string[](4); - elements[0] = ""; - elements[1] = "b"; - elements[2] = "b"; - elements[3] = ""; - assertTrue(_stringArraysAreSame(LibString.split("ababa", "a"), elements)); - - elements = new string[](3); - elements[0] = "a"; - elements[1] = "a"; - elements[2] = "a"; - assertTrue(_stringArraysAreSame(LibString.split("ababa", "b"), elements)); - - elements = new string[](5); - elements[0] = "a"; - elements[1] = "b"; - elements[2] = "a"; - elements[3] = "b"; - elements[4] = "a"; - assertTrue(_stringArraysAreSame(LibString.split("ababa", ""), elements)); - - elements = new string[](2); - elements[0] = "a"; - elements[1] = "b"; - assertTrue(_stringArraysAreSame(LibString.split("ab", ""), elements)); - - elements = new string[](1); - elements[0] = "ab"; - assertTrue(_stringArraysAreSame(LibString.split("ab", " "), elements)); - - elements = new string[](1); - elements[0] = "a"; - assertTrue(_stringArraysAreSame(LibString.split("a", ""), elements)); - - elements = new string[](0); - assertTrue(_stringArraysAreSame(LibString.split("", ""), elements)); - } - - function testStringConcat(string memory a, string memory b) public brutalizeMemory { - string memory concatenated = LibString.concat(a, b); - _checkMemory(concatenated); - string memory expectedResult = string(bytes.concat(bytes(a), bytes(b))); - assertEq(concatenated, expectedResult); - } - - function testStringConcat() public { - assertEq( - LibString.concat( - "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY", - "12345678901234567890123456789012345678901234567890" - ), - "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY12345678901234567890123456789012345678901234567890" - ); - assertEq(LibString.concat("", "b"), "b"); - assertEq(LibString.concat("", "b"), "b"); - assertEq(LibString.concat("a", "b"), "ab"); - assertEq(LibString.concat("a", ""), "a"); - assertEq(LibString.concat("", ""), ""); - } - - function testStringConcatOriginal() public { - assertEq( - string( - bytes.concat( - bytes("bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"), - bytes("12345678901234567890123456789012345678901234567890") - ) - ), - "bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY12345678901234567890123456789012345678901234567890" - ); - assertEq(string(bytes.concat(bytes(""), bytes("b"))), "b"); - assertEq(string(bytes.concat(bytes(""), bytes("b"))), "b"); - assertEq(string(bytes.concat(bytes("a"), bytes("b"))), "ab"); - assertEq(string(bytes.concat(bytes("a"), bytes(""))), "a"); - assertEq(string(bytes.concat(bytes(""), bytes(""))), ""); - } - - function testStringEscapeHTML() public { - assertEq(LibString.escapeHTML(""), ""); - assertEq(LibString.escapeHTML("abc"), "abc"); - assertEq(LibString.escapeHTML('abc"_123'), "abc"_123"); - assertEq(LibString.escapeHTML("abc&_123"), "abc&_123"); - assertEq(LibString.escapeHTML("abc'_123"), "abc'_123"); - assertEq(LibString.escapeHTML("abc<_123"), "abc<_123"); - assertEq(LibString.escapeHTML("abc>_123"), "abc>_123"); - } - - function testStringEscapeHTML(uint256) public brutalizeMemory { - string[] memory originalChars = new string[](5); - originalChars[0] = '"'; - originalChars[1] = "&"; - originalChars[2] = "'"; - originalChars[3] = "<"; - originalChars[4] = ">"; - - string[] memory escapedChars = new string[](5); - escapedChars[0] = """; - escapedChars[1] = "&"; - escapedChars[2] = "'"; - escapedChars[3] = "<"; - escapedChars[4] = ">"; - - string memory filler0 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - string memory filler1 = _generateString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - - uint256 r = _random() % 5; - - string memory expectedResult = - string(bytes.concat(bytes(filler0), bytes(escapedChars[r]), bytes(filler1))); - - string memory input = - string(bytes.concat(bytes(filler0), bytes(originalChars[r]), bytes(filler1))); - - _misalignFreeMemoryPointer(); - string memory escaped = LibString.escapeHTML(input); - _checkMemory(escaped); - - assertEq(expectedResult, escaped); - } - - function testStringEscapeJSON() public { - _checkStringEscapeJSON("", ""); - _checkStringEscapeJSON("abc", "abc"); - _checkStringEscapeJSON('abc"_123', 'abc\\"_123'); - _checkStringEscapeJSON("abc\\_123", "abc\\\\_123"); - _checkStringEscapeJSON("abc\x08_123", "abc\\b_123"); - _checkStringEscapeJSON("abc\x0c_123", "abc\\f_123"); - _checkStringEscapeJSON("abc\n_123", "abc\\n_123"); - _checkStringEscapeJSON("abc\r_123", "abc\\r_123"); - _checkStringEscapeJSON("abc\t_123", "abc\\t_123"); - } - - function _checkStringEscapeJSON(string memory s, string memory expected) internal { - assertEq(LibString.escapeJSON(s), expected); - assertEq(LibString.escapeJSON(s, false), expected); - assertEq(LibString.escapeJSON(s, true), string(bytes.concat('"', bytes(expected), '"'))); - } - - function testStringEscapeJSONHexEncode() public brutalizeMemory { - unchecked { - for (uint256 i; i <= 0x1f; ++i) { - if (i != 0x8 && i != 0x9 && i != 0x0a && i != 0x0c && i != 0x0d) { - string memory input = - string(bytes.concat(bytes("abc"), bytes1(uint8(i)), bytes("_123"))); - string memory hexCode = LibString.replace(LibString.toHexString(i), "0x", "00"); - string memory expectedOutput = - string(bytes.concat(bytes("abc\\u"), bytes(hexCode), bytes("_123"))); - string memory escaped = LibString.escapeJSON(input); - _checkMemory(escaped); - assertEq(escaped, expectedOutput); - } - } - } - } - - function testStringEq(string memory a, string memory b) public { - assertEq(LibString.eq(a, b), keccak256(bytes(a)) == keccak256(bytes(b))); - } - - function checkIsSN(string memory s) public pure returns (bool) { - // You can try replacing it with - // `return keccak256(bytes(s)) == keccak256("sn");` - // and see the bytecode size increase. - // This demonstrates that `eqs` does the compile time magic. - // Note that `s` must be in memory, not calldata. - return LibString.eqs(s, "sn"); - } - - function testStringEqs() public { - assertTrue(LibString.eqs("", "")); - assertTrue(LibString.eqs("1", "1")); - assertTrue(LibString.eqs("12", "12")); - assertTrue(LibString.eqs("123", "123")); - assertTrue(LibString.eqs("Hello", "Hello")); - assertTrue( - LibString.eqs("12345678901234567890123456789012", "12345678901234567890123456789012") - ); - - assertFalse(LibString.eqs("", "x")); - assertFalse(LibString.eqs("1", "2")); - assertFalse(LibString.eqs("Hello", "Hehe")); - assertFalse(LibString.eqs("12345678901234567890123456789012", "")); - - assertTrue(checkIsSN("sn")); - assertFalse(checkIsSN("x")); - } - - function testStringPackAndUnpackOneDifferential(string memory a) public brutalizeMemory { - a = LibString.slice(a, 0); - bytes32 packed = LibString.packOne(a); - unchecked { - if (bytes(a).length < 32) { - bytes memory expectedResultBytes = abi.encodePacked(uint8(bytes(a).length), a); - bytes32 expectedResult; - /// @solidity memory-safe-assembly - assembly { - expectedResult := mload(add(expectedResultBytes, 0x20)) - } - assertEq(packed, expectedResult); - } else { - assertEq(packed, bytes32(0)); - } - } - } - - function testStringPackAndUnpackOne(string memory a) public brutalizeMemory { - _misalignFreeMemoryPointer(); - bytes32 packed = LibString.packOne(a); - string memory unpacked = LibString.unpackOne(packed); - _checkMemory(unpacked); - - if (bytes(a).length < 32) { - assertEq(unpacked, a); - } else { - assertEq(packed, bytes32(0)); - assertEq(unpacked, ""); - } - } - - function testStringPackAndUnpackOne() public { - unchecked { - testStringPackAndUnpackOne(""); - testStringPackAndUnpackOne("Hehe"); - testStringPackAndUnpackOne("abcdefghijklmnopqrstuvwxyzABCD"); - testStringPackAndUnpackOne("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - } - } - - function testStringPackAndUnpackTwoDifferential(string memory a, string memory b) - public - brutalizeMemory - { - a = LibString.slice(a, 0); - b = LibString.slice(b, 0); - bytes32 packed = LibString.packTwo(a, b); - unchecked { - if (bytes(a).length + bytes(b).length < 31) { - bytes memory expectedResultBytes = - abi.encodePacked(uint8(bytes(a).length), a, uint8(bytes(b).length), b); - bytes32 expectedResult; - /// @solidity memory-safe-assembly - assembly { - expectedResult := mload(add(expectedResultBytes, 0x20)) - } - assertEq(packed, expectedResult); - } else { - assertEq(packed, bytes32(0)); - } - } - } - - function testStringPackAndUnpackTwo(string memory a, string memory b) public brutalizeMemory { - bytes32 packed = LibString.packTwo(a, b); - _misalignFreeMemoryPointer(); - (string memory unpackedA, string memory unpackedB) = LibString.unpackTwo(packed); - _checkMemory(unpackedA); - _checkMemory(unpackedB); - - unchecked { - if (bytes(a).length + bytes(b).length < 31) { - assertEq(unpackedA, a); - assertEq(unpackedB, b); - } else { - assertEq(packed, bytes32(0)); - assertEq(unpackedA, ""); - assertEq(unpackedB, ""); - } - } - } - - function testStringPackAndUnpackTwo() public { - unchecked { - testStringPackAndUnpackTwo("", ""); - testStringPackAndUnpackTwo("", ""); - testStringPackAndUnpackTwo("a", ""); - testStringPackAndUnpackTwo("", "b"); - testStringPackAndUnpackTwo("abcdefghijklmnopqrstuvwxyzABCD", ""); - testStringPackAndUnpackTwo("The strongest community I've ever seen", "NGL"); - testStringPackAndUnpackTwo("", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - } - } - - function testStringDirectReturn(string memory a) public { - assertEq(this.returnString(a), a); - } - - function testStringDirectReturn() public { - testStringDirectReturn(""); - testStringDirectReturn("aaa"); - testStringDirectReturn("98729"); - } - - function returnString(string memory a) external pure returns (string memory) { - LibString.directReturn(a); - } - - function testStringLowerDifferential(string memory s) public { - string memory expectedResult = _lowerOriginal(s); - _misalignFreeMemoryPointer(); - string memory result = LibString.lower(s); - _checkMemory(result); - assertEq(result, expectedResult); - } - - function testStringLowerDifferential() public { - unchecked { - string memory ascii = new string(128); - for (uint256 i; i < 128; ++i) { - /// @solidity memory-safe-assembly - assembly { - mstore8(add(add(ascii, 0x20), i), i) - } - } - for (uint256 i; i < 256; ++i) { - string memory s = _generateString(ascii); - testStringLowerDifferential(s); - } - } - } - - function testStringLowerOriginal() public { - assertEq(_lowerOriginal("@AZ["), "@az["); - } - - function testStringUpperDifferential(string memory s) public { - string memory expectedResult = _upperOriginal(s); - _misalignFreeMemoryPointer(); - string memory result = LibString.upper(s); - _checkMemory(result); - assertEq(result, expectedResult); - } - - function testStringUpperDifferential() public { - unchecked { - string memory ascii = new string(128); - for (uint256 i; i < 128; ++i) { - /// @solidity memory-safe-assembly - assembly { - mstore8(add(add(ascii, 0x20), i), i) - } - } - for (uint256 i; i < 256; ++i) { - string memory s = _generateString(ascii); - testStringUpperDifferential(s); - } - } - } - - function testStringUpperOriginal() public { - assertEq(_upperOriginal("`az}"), "`AZ}"); - } - - function fromSmallString() public { - assertEq(LibString.fromSmallString(bytes32("")), ""); - assertEq(LibString.fromSmallString(bytes32("a")), "a"); - assertEq(LibString.fromSmallString(bytes32("abc")), "abc"); - assertEq(LibString.fromSmallString(bytes32("Hello world!")), "Hello world!"); - } - - function _lowerOriginal(string memory subject) internal pure returns (string memory result) { - unchecked { - uint256 n = bytes(subject).length; - result = new string(n); - for (uint256 i; i != n; ++i) { - /// @solidity memory-safe-assembly - assembly { - let b := byte(0, mload(add(add(subject, 0x20), i))) - mstore8( - add(add(result, 0x20), i), add(b, mul(0x20, and(lt(0x40, b), lt(b, 0x5b)))) - ) - } - } - } - } - - function _upperOriginal(string memory subject) internal pure returns (string memory result) { - unchecked { - uint256 n = bytes(subject).length; - result = new string(n); - for (uint256 i; i != n; ++i) { - /// @solidity memory-safe-assembly - assembly { - let b := byte(0, mload(add(add(subject, 0x20), i))) - mstore8( - add(add(result, 0x20), i), sub(b, mul(0x20, and(lt(0x60, b), lt(b, 0x7b)))) - ) - } - } - } - } - - function _is7BitASCIIOriginal(string memory s) internal pure returns (bool) { - unchecked { - bytes memory sBytes = bytes(s); - for (uint256 i; i < sBytes.length; ++i) { - if (uint8(bytes1(sBytes[i])) > 127) return false; - } - return true; - } - } - - function _runeCountOriginal(string memory s) internal pure returns (uint256) { - unchecked { - uint256 len; - uint256 i = 0; - uint256 bytelength = bytes(s).length; - for (len = 0; i < bytelength; len++) { - bytes1 b = bytes(s)[i]; - if (b < 0x80) { - i += 1; - } else if (b < 0xE0) { - i += 2; - } else if (b < 0xF0) { - i += 3; - } else if (b < 0xF8) { - i += 4; - } else if (b < 0xFC) { - i += 5; - } else { - i += 6; - } - } - return len; - } - } - - function _repeatOriginal(string memory subject, uint256 times) - internal - pure - returns (string memory) - { - unchecked { - string memory result; - if (!(times == 0 || bytes(subject).length == 0)) { - for (uint256 i; i < times; ++i) { - result = string(bytes.concat(bytes(result), bytes(subject))); - } - } - _misalignFreeMemoryPointer(); - return result; - } - } - - function _generateFrom(string memory subject) internal returns (uint256) { - unchecked { - if (_random() % 8 == 0) { - return _random(); - } - return _random() % (bytes(subject).length + 10); - } - } - - function _generateString(string memory byteChoices) internal returns (string memory result) { - uint256 randomness = _random(); - uint256 resultLength = _randomStringLength(); - /// @solidity memory-safe-assembly - assembly { - if mload(byteChoices) { - result := mload(0x40) - mstore(0x00, randomness) - mstore(0x40, and(add(add(result, 0x40), resultLength), not(31))) - mstore(result, resultLength) - - // forgefmt: disable-next-item - for { let i := 0 } lt(i, resultLength) { i := add(i, 1) } { - mstore(0x20, gas()) - mstore8( - add(add(result, 0x20), i), - mload(add(add(byteChoices, 1), mod(keccak256(0x00, 0x40), mload(byteChoices)))) - ) - } - } - } - } - - function _randomStringLength() internal returns (uint256 r) { - r = _random() % 256; - if (r < 64) return _random() % 128; - if (r < 128) return _random() % 64; - return _random() % 16; - } - - function _stringArraysAreSame(string[] memory a, string[] memory b) - internal - pure - returns (bool) - { - unchecked { - if (a.length != b.length) { - return false; - } - for (uint256 i; i < a.length; ++i) { - if (keccak256(bytes(a[i])) != keccak256(bytes(b[i]))) { - return false; - } - } - return true; - } - } -} diff --git a/lib/solady/test/LibZip.t.sol b/lib/solady/test/LibZip.t.sol deleted file mode 100644 index e6fb355..0000000 --- a/lib/solady/test/LibZip.t.sol +++ /dev/null @@ -1,276 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MockCd, MockCdFallbackDecompressor} from "./utils/mocks/MockCd.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; -import {ERC1967Factory} from "../src/utils/ERC1967Factory.sol"; -import {LibString} from "../src/utils/LibString.sol"; -import {DynamicBufferLib} from "../src/utils/DynamicBufferLib.sol"; -import {LibZip} from "../src/utils/LibZip.sol"; - -contract LibZipTest is SoladyTest { - using DynamicBufferLib for DynamicBufferLib.DynamicBuffer; - - function testFlzCompressDecompress() public brutalizeMemory { - assertEq(LibZip.flzCompress(""), ""); - assertEq(LibZip.flzDecompress(""), ""); - bytes memory compressed = - hex"1f4e65772077617665206469676974616c206172742073686f756c64206e6f74201f6265206a756467656420736f6c656c79206f6e20616573746865746963206d65017269202301757420240e737420617320696d706f7274616e74202a1b666f7220697473206162696c69747920746f20646576656c6f702061200c406d03766973692051026f662020510320576972206801616e200301626f209315616c6c20637265617465206e6574776f726b20737069207201756140500e2e20416e6369656e7420477265656b60ba04697320686520bb01696e6051036869676820ad062072656761726420cd0463617573652095c089406f0220776820db03206d79742007016f6720a801686141040061401d0272656c2119066f6e2c206d6f72607660a0017761210a0866206c6966652e2054212f016768607c20ff026c6963205fa080414e40b80073402de003ae016365613741630373756666203320c8213406646973656e676141710366726f6d208b016520201102736f722097006f208e03666c6563205e21570b6f776e2070726f647563747340840065400d03626c656d406b056d6f6465726e417621120074201ba17a006d2066401f0272617420d521f00273656c200d805620118048036265617520e920e140c00561707065617220dc21fb41d5006d21310a69636820636f6e74726173208a02616273200920972142410a40450275732ce0081720852028016f798092056e616976652f40dd02756f752044057070726563696098c07a2072076578657274696e6760f3046e617475722172015265818b20dc02736c612025016279208e006d20400068212c60c701507221de2281026e7465409a201304207769746820a200652008426906696e766f6c7665808c20bf006e21940373657061811b4082227c2312e006dc620004616363656c20580074203e0165782062214b01697380bb2072026f207020a74009017569407a072c20656d626f647940c74080c2cf42ec01756e4183214c0273636921074204027261774029006f235905747275746873e2010c02636f6c42012139223c046f73706865410f025468724178205b2111016265603c830280ea0077228a02636869237e21c20172652144201d006f214e2047002ce0078140b620da2064c20f06696e64697669642377032065676f6175036163726922b6234060ee40d4e00b9a405c210121be205701756e2294208063fc23fa201583d040c0427523aa427021cf404f80ba20d04325016f6e21186297036966756c633c82fc21800061e1004f804e20a722f9052e2049206c6f20634488024e657443870d2053706972697475616c69747921"; - bytes memory decompressed = LibZip.flzDecompress(compressed); - bytes memory expectedDecompressed = - "New wave digital art should not be judged solely on aesthetic merit but just as importantly for its ability to develop a total vision of the Wired and above all create network spirituality. Ancient Greek art is held in the highest regard because it developed a whole mythology that shaped religion, morality and way of life. Thought is implicit in the art works of Ancient Greece but not sufficiently disengaged from the sensory to reflect its own products. The problem of modernity is the development of rational self reflection. The beauty of art appears in a form which contrasts abstract thought. Thus, abstract thought destroys the naive/sensuous appreciation of art in exerting its nature. Reality is slain by comprehension. Proper interaction with the wired involves the trance separation of real abstract thought and accelerates externalisation into pure intuition, embodying the network and unselfconsciously drawing out truths from the collective noosphere. Through this being on the wired we achieve a return to naive, unselfconscious interaction. The individual ego is sacrificed into the collective noosphere, uniting us under a totalising spirit. The best art in the wired is not only beautiful but produces a network spirituality. I long for Network Spirituality!"; - assertEq(decompressed, expectedDecompressed); - assertEq(LibZip.flzCompress(decompressed), compressed); - // Check backwards compatibility with older FastLZ releases. - compressed = - hex"1f4e65772077617665206469676974616c206172742073686f756c64206e6f74201f6265206a756467656420736f6c656c79206f6e20616573746865746963206d65017269202301757420240e737420617320696d706f7274616e74202a1b666f7220697473206162696c69747920746f20646576656c6f702061200c406d03766973692051026f662020510320576972206801616e200301626f209315616c6c20637265617465206e6574776f726b20737069207201756140500e2e20416e6369656e7420477265656b60ba04697320686520bb01696e6051036869676820ad062072656761726420cd0463617573652095c089406f0220776820db03206d79742007016f6720a801686141040061401d0272656c2119066f6e2c206d6f72607660a0017761210a0866206c6966652e2054212f016768607c20ff026c6963205fa080414e40b80073402de003ae016365613741630373756666203320c8213406646973656e676141710366726f6d208b016520201102736f722097006f208e03666c6563205e21570b6f776e2070726f647563747340840065400d03626c656d406b056d6f6465726e417621124044a17a006d2066401f0272617420d521f00273656c200d805620118048036265617520e920e140c00561707065617220dc21fb41d5006d21310a69636820636f6e74726173208a02616273200920972142410a40450275732ce0081720852028016f798092056e616976652f40dd02756f752044057070726563696098c07a2072076578657274696e6760f3046e617475722172015265818b20dc02736c612025016279208e006d20400068212c60c701507221de2281026e7465409a201304207769746820a200652008426906696e766f6c7665808c20bf006e21940373657061811b4082227c2312e006dc620004616363656c20580074203e0165782062214b01697380bb2072026f207020a74009017569407a072c20656d626f647940c74080c2cf42ec01756e4183214c0273636921074204027261774029006f235905747275746873e2010c02636f6c42012139223c046f73706865410f025468724178205b2111016265603c830280ea0077228a02636869237e21c20172652144201d006f214e2047002ce0078140b620da2064c20f06696e64697669642377032065676f6175036163726922b6234060ee40d4e00b9a405c210121be205701756e2294208063fc23fa201583d040c0427523aa427021cf404f80ba20d04325016f6e21186297036966756c633c82fc21800061e1004f804e20a722f9052e2049206c6f20634488004ea4400c53706972697475616c69747921"; - assertEq(LibZip.flzDecompress(compressed), decompressed); - } - - function _expandedData(bytes memory data) internal returns (bytes memory) { - unchecked { - DynamicBufferLib.DynamicBuffer memory buffer; - bytes memory r = abi.encode(_random()); - if (_random() % 8 == 0) { - r = abi.encodePacked(r, r, r, r); - r = bytes(LibString.slice(string(r), 0, _random() % r.length)); - } - uint256 n = _random() % 16 + 1; - uint256 c = _random(); - for (uint256 i; i < n; ++i) { - buffer.p((c >> i) & 1 == 0 ? r : data); - } - return buffer.data; - } - } - - function testFlzCompressDecompress(bytes memory data) public brutalizeMemory { - if (_random() % 2 == 0) { - data = _expandedData(data); - } - bytes32 dataHash = keccak256(data); - _misalignFreeMemoryPointer(); - bytes memory compressed = LibZip.flzCompress(data); - bytes32 compressedHash = keccak256(compressed); - _checkMemory(compressed); - _misalignFreeMemoryPointer(); - bytes memory decompressed = LibZip.flzDecompress(compressed); - _checkMemory(compressed); - _checkMemory(decompressed); - assertEq(decompressed, data); - assertEq(keccak256(data), dataHash); - assertEq(keccak256(compressed), compressedHash); - } - - function testFlzCompressDecompress2() public brutalizeMemory { - bytes memory data = - "______________________________________________________________e_______8______________________________________________________________________________________________________________________12_______8______________________________________________________________________________________________________________________16_______8______________________________________________________________________________________________________________________1a_______________________________________________________________2_____________________________________________732e2_5_726f2_49__73______________________________________________________________2_____________________________________________732e2_5_726f2_49__73______________________________________________________________2_____________________________________________732e2_5_726f2_49__73______________________________________________________________2_____________________________________________732e2_5_726f2_49__73"; - bytes32 dataHash = keccak256(data); - bytes memory expectedCompressed = - hex"015f5fe033010065a03c0038a007e06600013132a070e06f7f0036e0767f0061a07fe02f00c13fe01d000f37333265325f355f37323666325f34394011e01d39e00f00e0fd7fe02e7f04395f5f3733"; - bytes memory compressed = LibZip.flzCompress(data); - assertEq(compressed, expectedCompressed); - bytes32 compressedHash = keccak256(compressed); - _checkMemory(compressed); - bytes memory decompressed = LibZip.flzDecompress(compressed); - _checkMemory(compressed); - _checkMemory(decompressed); - assertEq(decompressed, data); - assertEq(keccak256(data), dataHash); - assertEq(keccak256(compressed), compressedHash); - } - - function testCdCompressDecompress(bytes memory data) public brutalizeMemory { - if (_random() % 8 == 0) { - data = _expandedData(data); - } - bytes32 dataHash = keccak256(data); - _misalignFreeMemoryPointer(); - bytes memory compressed = LibZip.cdCompress(data); - bytes32 compressedHash = keccak256(compressed); - _checkMemory(compressed); - _misalignFreeMemoryPointer(); - bytes memory decompressed = LibZip.cdDecompress(compressed); - _checkMemory(compressed); - _checkMemory(decompressed); - assertEq(decompressed, data); - assertEq(keccak256(data), dataHash); - assertEq(keccak256(compressed), compressedHash); - } - - function _randomCd() internal returns (bytes memory data) { - uint256 n = _random() % 8 == 0 ? _random() % 2048 : _random() % 256; - data = new bytes(n); - if (_random() % 2 == 0) { - /// @solidity memory-safe-assembly - assembly { - for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { - mstore(add(add(data, 0x20), i), not(0)) - } - } - } - if (n != 0) { - uint256 m = _random() % 8; - for (uint256 j; j < m; ++j) { - data[_random() % n] = bytes1(uint8(_random())); - } - } - } - - function testCdCompressDecompress(uint256) public brutalizeMemory { - unchecked { - bytes memory data = _randomCd(); - bytes memory compressed = LibZip.cdCompress(data); - bytes memory decompressed = LibZip.cdDecompress(compressed); - assertEq(decompressed, data); - } - } - - function testCdFallbackDecompressor(bytes memory data) public { - bytes memory compressed = LibZip.cdCompress(data); - MockCdFallbackDecompressor decompressor = new MockCdFallbackDecompressor(); - (, bytes memory result) = address(decompressor).call(compressed); - assertEq(abi.decode(result, (bytes32)), keccak256(data)); - } - - function testCdFallbackDecompressor(uint256) public { - bytes memory data = _randomCd(); - bytes memory compressed = LibZip.cdCompress(data); - MockCdFallbackDecompressor decompressor = new MockCdFallbackDecompressor(); - (, bytes memory result) = address(decompressor).call(compressed); - assertEq(abi.decode(result, (bytes32)), keccak256(data)); - } - - function testCdCompress() public { - assertEq(LibZip.cdCompress(""), ""); - assertEq(LibZip.cdDecompress(""), ""); - bytes memory data = - hex"ac9650d80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000a40c49ccbe000000000000000000000000000000000000000000000000000000000005b70e00000000000000000000000000000000000000000000000000000dfc79825feb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000645c48a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fc6f7865000000000000000000000000000000000000000000000000000000000005b70e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004449404b7c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f1cdf1a632eaaab40d1c263edf49faf749010a1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064df2ab5bb0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c3160700000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f1cdf1a632eaaab40d1c263edf49faf749010a100000000000000000000000000000000000000000000000000000000"; - bytes memory expected = - hex"5369af27001e20001e04001e80001d0160001d0220001d02a0001ea40c49ccbe001c05b70e00190dfc79825feb005b645c48a7003a84fc6f7865001c05b70e002f008f000f008f003a4449404b7c002b1f1cdf1a632eaaab40d1c263edf49faf749010a1003a64df2ab5bb000b7f5c764cbc14f9669b88837ca1490cca17c31607002b1f1cdf1a632eaaab40d1c263edf49faf749010a1001b"; - assertEq(LibZip.cdCompress(data), expected); - } - - function testCdDecompressOnInvalidInput() public { - bytes memory data = hex"ffffffff00ff"; - bytes memory expected = - hex"0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - bytes memory decompressed = LibZip.cdDecompress(data); - assertEq(decompressed, expected); - } - - function testDecompressWontRevert(bytes memory data) public brutalizeMemory { - data = LibZip.cdDecompress(data); - bytes memory compressed = LibZip.cdCompress(data); - bytes memory decompressed = LibZip.cdDecompress(compressed); - assertEq(decompressed, data); - } - - function testCdFallback() public { - MockCd mockCd = new MockCd(); - _testCdFallback(mockCd); - // Check if it also works for clones. - mockCd = MockCd(payable(LibClone.clone(address(mockCd)))); - _testCdFallback(mockCd); - // Check if it also works for CWIA. - mockCd = MockCd(payable(LibClone.clone(address(mockCd), ""))); - _testCdFallback(mockCd); - // Check if it also works for ERC1967 proxies. - ERC1967Factory factory = new ERC1967Factory(); - mockCd = MockCd(payable(factory.deploy(address(mockCd), address(this)))); - _testCdFallback(mockCd); - } - - function _testCdFallback(MockCd mockCd) internal { - uint256[] memory numbers = new uint256[](100); - unchecked { - for (uint256 i; i < numbers.length; ++i) { - numbers[i] = i % 2 == 0 ? i : ~i; - } - } - assertEq(mockCd.numbersHash(), 0); - assertEq(mockCd.lastCallvalue(), 0); - assertEq(mockCd.lastCaller(), address(0)); - - uint256 callValue = 123 ether; - vm.deal(address(this), callValue * 2); - - (bool success, bytes memory result) = payable(mockCd).call{value: callValue}( - LibZip.cdCompress( - abi.encodeWithSignature("storeNumbersHash(uint256[],bool)", numbers, true) - ) - ); - - assertTrue(success); - bytes32 decodedNumbersHash = abi.decode(result, (bytes32)); - bytes32 expectedNumbersHash = keccak256(abi.encode(numbers)); - assertEq(decodedNumbersHash, expectedNumbersHash); - assertEq(mockCd.numbersHash(), expectedNumbersHash); - assertEq(mockCd.lastCallvalue(), callValue); - assertEq(mockCd.lastCaller(), address(this)); - assertEq(address(mockCd).balance, callValue); - - (success, result) = payable(mockCd).call{value: callValue}( - LibZip.cdCompress( - abi.encodeWithSignature("storeNumbersHash(uint256[],bool)", numbers, false) - ) - ); - - assertFalse(success); - assertEq(address(mockCd).balance, callValue); - assertEq(abi.encodeWithSelector(MockCd.Hash.selector, expectedNumbersHash), result); - assertEq(address(mockCd).balance, callValue); - - (success, result) = payable(mockCd).call{value: callValue}(""); - assertEq(address(mockCd).balance, callValue * 2); - assertTrue(success); - } - - function testCdFallback(bytes memory data, uint256 callValue) public brutalizeMemory { - MockCd mockCd = new MockCd(); - callValue = _bound(callValue, 0, 123 ether); - vm.deal(address(this), callValue * 2); - if (_random() % 8 == 0) { - data = _expandedData(data); - } - - (bool success, bytes memory result) = payable(mockCd).call{value: callValue}( - LibZip.cdCompress(abi.encodeWithSignature("storeDataHash(bytes,bool)", data, true)) - ); - - assertTrue(success); - bytes32 decodedDataHash = abi.decode(result, (bytes32)); - bytes32 expectedDataHash = keccak256(data); - assertEq(decodedDataHash, expectedDataHash); - assertEq(mockCd.dataHash(), expectedDataHash); - assertEq(mockCd.lastCallvalue(), callValue); - assertEq(mockCd.lastCaller(), address(this)); - assertEq(address(mockCd).balance, callValue); - - (success, result) = payable(mockCd).call{value: callValue}( - LibZip.cdCompress(abi.encodeWithSignature("storeDataHash(bytes,bool)", data, false)) - ); - - assertFalse(success); - assertEq(address(mockCd).balance, callValue); - assertEq(abi.encodeWithSelector(MockCd.Hash.selector, expectedDataHash), result); - assertEq(address(mockCd).balance, callValue); - - (success, result) = payable(mockCd).call{value: callValue}(""); - assertEq(address(mockCd).balance, callValue * 2); - assertTrue(success); - } - - function testCdFallbackMaskTrick(uint256 i, uint256 j) public { - i = _bound(i, 0, 2 ** 248 - 1); - uint256 a; - uint256 b; - /// @solidity memory-safe-assembly - assembly { - a := byte(0, xor(add(i, not(3)), j)) - b := xor(byte(i, shl(224, 0xffffffff)), byte(0, j)) - } - assertEq(a, b); - } -} diff --git a/lib/solady/test/MerkleProofLib.t.sol b/lib/solady/test/MerkleProofLib.t.sol deleted file mode 100644 index cddce6f..0000000 --- a/lib/solady/test/MerkleProofLib.t.sol +++ /dev/null @@ -1,440 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MerkleProofLib} from "../src/utils/MerkleProofLib.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract MerkleProofLibTest is SoladyTest { - function testVerifyProofForHeightOneTree( - bool hasProof, - bool nonEmptyProof, - bool nonEmptyRoot, - bool nonEmptyLeaf - ) public { - bytes32 root; - if (nonEmptyRoot) { - root = bytes32("a"); - } - bytes32 leaf; - if (nonEmptyLeaf) { - leaf = bytes32("a"); - } - bytes32[] memory proof; - if (hasProof) { - proof = new bytes32[](1); - proof[0] = nonEmptyProof ? bytes32("a") : bytes32(0); - } - bool isValid = leaf == root && proof.length == 0; - assertEq(this.verify(proof, root, leaf), isValid); - } - - function testVerifyProof(bytes32[] memory data, uint256 randomness) public brutalizeMemory { - vm.assume(data.length > 1); - uint256 nodeIndex = randomness % data.length; - bytes32 root = _getRoot(data); - bytes32[] memory proof = _getProof(data, nodeIndex); - bytes32 leaf = data[nodeIndex]; - - assertTrue(this.verify(proof, root, leaf)); - - // Checks verify with corrupted root returns false. - assertFalse(this.verify(proof, bytes32(uint256(root) ^ 1), leaf)); - - // Checks verify with corrupted proof returns false. - proof[0] = bytes32(uint256(proof[0]) ^ 1); - assertFalse(this.verify(proof, root, leaf)); - - // Checks verify with corrupted root and proof returns false. - assertFalse(this.verify(proof, bytes32(uint256(root) ^ 1), leaf)); - } - - function testVerifyProofBasicCaseIsValid() public { - testVerifyProofBasicCase(false, false, false, 0x00); - } - - function testVerifyProofBasicCaseIsInvalid() public { - testVerifyProofBasicCase(false, false, true, 0x00); - } - - function testVerifyMultiProofMalicious() public { - bytes32[] memory realLeaves = new bytes32[](2); - realLeaves[0] = bytes32("real leaf"); - realLeaves[1] = bytes32(0); - bytes32 root = _hashPair(realLeaves[0], realLeaves[1]); - - bytes32[] memory maliciousLeaves = new bytes32[](2); - maliciousLeaves[0] = bytes32("malicious"); - maliciousLeaves[1] = bytes32("leaves"); - bytes32[] memory maliciousProof = new bytes32[](2); - maliciousProof[0] = realLeaves[0]; - maliciousProof[1] = realLeaves[0]; - bool[] memory maliciousFlags = new bool[](3); - maliciousFlags[0] = true; - maliciousFlags[1] = true; - maliciousFlags[2] = false; - - assertFalse(this.verifyMultiProof(maliciousProof, root, maliciousLeaves, maliciousFlags)); - } - - function testVerifyProofBasicCase( - bool damageProof, - bool damageRoot, - bool damageLeaf, - bytes32 randomness - ) public { - bool noDamage = true; - uint256 ri; // Randomness index. - - bytes32[] memory proof = new bytes32[](2); - proof[0] = bytes32("b"); - proof[1] = _hashPair(bytes32("c"), bytes32(0)); - if (damageProof) { - noDamage = false; - uint256 i = uint256(uint8(randomness[ri++])) % proof.length; - proof[i] = bytes32(uint256(proof[i]) ^ 1); // Flip a bit. - } - - bytes32 root = - _hashPair(_hashPair(bytes32("a"), bytes32("b")), _hashPair(bytes32("c"), bytes32(0))); - - if (damageRoot) { - noDamage = false; - root = bytes32(uint256(root) ^ 1); // Flip a bit. - } - - bytes32 leaf = bytes32("a"); - if (damageLeaf) { - noDamage = false; - leaf = bytes32(uint256(leaf) ^ 1); // Flip a bit. - } - - assertEq(this.verify(proof, root, leaf), noDamage); - } - - function testVerifyMultiProofForSingleLeaf(bytes32[] memory data, uint256 randomness) - public - brutalizeMemory - { - vm.assume(data.length > 1); - uint256 nodeIndex = randomness % data.length; - bytes32 root = _getRoot(data); - bytes32[] memory proof = _getProof(data, nodeIndex); - bytes32[] memory leaves = new bytes32[](1); - leaves[0] = data[nodeIndex]; - bool[] memory flags = new bool[](proof.length); - - assertTrue(this.verifyMultiProof(proof, root, leaves, flags)); - - // Checks verify with corrupted root returns false. - assertFalse(this.verifyMultiProof(proof, bytes32(uint256(root) ^ 1), leaves, flags)); - - // Checks verify with corrupted proof returns false. - proof[0] = bytes32(uint256(proof[0]) ^ 1); - assertFalse(this.verifyMultiProof(proof, root, leaves, flags)); - - // Checks verify with corrupted root and proof returns false. - assertFalse(this.verifyMultiProof(proof, bytes32(uint256(root) ^ 1), leaves, flags)); - } - - function testVerifyMultiProofForHeightOneTree( - bool hasProof, - bool nonEmptyProof, - bool nonEmptyRoot, - bool hasLeaf, - bool nonEmptyLeaf, - bool[] memory flags - ) public { - bytes32 root; - if (nonEmptyRoot) { - root = bytes32("a"); - } - bytes32[] memory proof; - if (hasProof) { - proof = new bytes32[](1); - proof[0] = nonEmptyProof ? bytes32("a") : bytes32(0); - } - bytes32[] memory leaves; - if (hasLeaf) { - leaves = new bytes32[](1); - leaves[0] = nonEmptyLeaf ? bytes32("a") : bytes32(0); - } - bool leafSameAsRoot = leaves.length == 1 && leaves[0] == root; - bool proofSameAsRoot = proof.length == 1 && proof[0] == root; - bool isValid = flags.length == 0 && (leafSameAsRoot || proofSameAsRoot) - && (leaves.length + proof.length == 1); - assertEq(this.verifyMultiProof(proof, root, leaves, flags), isValid); - } - - function testVerifyMultiProofForHeightTwoTree( - bool allLeaves, - bool damageRoot, - bool damageLeaves, - bool damageProof, - bool damageFlags, - bytes32 randomness - ) public { - bool noDamage = true; - uint256 ri; // Randomness index. - - bytes32 root = _hashPair(bytes32("a"), bytes32("b")); - - bytes32[] memory proof; - bytes32[] memory leaves; - bool[] memory flags = new bool[](1); - flags[0] = allLeaves; - - if (allLeaves) { - leaves = new bytes32[](2); - leaves[0] = bytes32("a"); - leaves[1] = bytes32("b"); - } else { - leaves = new bytes32[](1); - leaves[0] = bytes32("a"); - proof = new bytes32[](1); - proof[0] = bytes32("b"); - } - - if (damageRoot) { - noDamage = false; - root = bytes32(uint256(root) ^ 1); // Flip a bit. - } - - if (damageFlags) { - noDamage = false; - flags[0] = !flags[0]; // Flip a bool. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete flags; - } - - if (damageLeaves) { - noDamage = false; - uint256 i = uint256(uint8(randomness[ri++])) % leaves.length; - leaves[i] = bytes32(uint256(leaves[i]) ^ 1); // Flip a bit. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete leaves; - } - - if (damageProof && proof.length != 0) { - noDamage = false; - proof[0] = bytes32(uint256(proof[0]) ^ 1); // Flip a bit. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete proof; - } - - assertEq(this.verifyMultiProof(proof, root, leaves, flags), noDamage); - } - - function testVerifyMultiProofIsValid() public { - testVerifyMultiProof(false, false, false, false, 0x00); - } - - function testVerifyMultiProofIsInvalid() public { - testVerifyMultiProof(false, false, true, false, 0x00); - } - - function testVerifyMultiProof( - bool damageRoot, - bool damageLeaves, - bool damageProof, - bool damageFlags, - bytes32 randomness - ) public brutalizeMemory { - bool noDamage = true; - uint256 ri; // Randomness index. - - bytes32 root = _hashPair( - _hashPair(_hashPair(bytes32("a"), bytes32("b")), _hashPair(bytes32("c"), bytes32("d"))), - _hashPair(bytes32("e"), bytes32("f")) - ); - - bytes32[] memory leaves = new bytes32[](3); - leaves[0] = bytes32("d"); - leaves[1] = bytes32("e"); - leaves[2] = bytes32("f"); - - bytes32[] memory proof = new bytes32[](2); - proof[0] = bytes32("c"); - proof[1] = _hashPair(bytes32("b"), bytes32("a")); - - bool[] memory flags = new bool[](4); - flags[0] = false; - flags[1] = true; - flags[2] = false; - flags[3] = true; - - if (damageRoot) { - noDamage = false; - root = bytes32(uint256(root) ^ 1); // Flip a bit. - } - - if (damageLeaves) { - noDamage = false; - uint256 i = uint256(uint8(randomness[ri++])) % leaves.length; - leaves[i] = bytes32(uint256(leaves[i]) ^ 1); // Flip a bit. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete leaves; - } - - if (damageProof) { - noDamage = false; - uint256 i = uint256(uint8(randomness[ri++])) % proof.length; - proof[i] = bytes32(uint256(proof[i]) ^ 1); // Flip a bit. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete proof; - } - - if (damageFlags) { - noDamage = false; - uint256 i = uint256(uint8(randomness[ri++])) % flags.length; - flags[i] = !flags[i]; // Flip a bool. - if (uint256(uint8(randomness[ri++])) & 1 == 0) delete flags; - } - - assertEq(this.verifyMultiProof(proof, root, leaves, flags), noDamage); - } - - function verify(bytes32[] calldata proof, bytes32 root, bytes32 leaf) - external - returns (bool result) - { - result = MerkleProofLib.verifyCalldata(proof, root, leaf); - assertEq(MerkleProofLib.verify(proof, root, leaf), result); - } - - function verifyMultiProof( - bytes32[] calldata proof, - bytes32 root, - bytes32[] calldata leaves, - bool[] calldata flags - ) external returns (bool result) { - uint256[] memory offsetsAndLengths = new uint256[](12); - - // Basically, we want to demonstrate that the `verifyMultiProof` does not - // change the offsets and lengths. - - /// @solidity memory-safe-assembly - assembly { - mstore(add(offsetsAndLengths, shl(5, add(1, 0))), proof.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 1))), leaves.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 2))), flags.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 3))), proof.length) - mstore(add(offsetsAndLengths, shl(5, add(1, 4))), leaves.length) - mstore(add(offsetsAndLengths, shl(5, add(1, 5))), flags.length) - } - - result = MerkleProofLib.verifyMultiProofCalldata(proof, root, leaves, flags); - - /// @solidity memory-safe-assembly - assembly { - mstore(add(offsetsAndLengths, shl(5, add(1, 6))), proof.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 7))), leaves.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 8))), flags.offset) - mstore(add(offsetsAndLengths, shl(5, add(1, 9))), proof.length) - mstore(add(offsetsAndLengths, shl(5, add(1, 10))), leaves.length) - mstore(add(offsetsAndLengths, shl(5, add(1, 11))), flags.length) - } - - assertEq(offsetsAndLengths[0], offsetsAndLengths[6]); - assertEq(offsetsAndLengths[1], offsetsAndLengths[7]); - assertEq(offsetsAndLengths[2], offsetsAndLengths[8]); - assertEq(offsetsAndLengths[3], offsetsAndLengths[9]); - assertEq(offsetsAndLengths[4], offsetsAndLengths[10]); - assertEq(offsetsAndLengths[5], offsetsAndLengths[11]); - - assertEq(MerkleProofLib.verifyMultiProof(proof, root, leaves, flags), result); - } - - // Following code is adapted from https://github.com/dmfxyz/murky/blob/main/src/common/MurkyBase.sol. - - function _getRoot(bytes32[] memory data) private pure returns (bytes32) { - require(data.length > 1); - while (data.length > 1) { - data = _hashLevel(data); - } - return data[0]; - } - - function _getProof(bytes32[] memory data, uint256 nodeIndex) - private - pure - returns (bytes32[] memory) - { - require(data.length > 1); - - bytes32[] memory result = new bytes32[](64); - uint256 pos; - - while (data.length > 1) { - unchecked { - if (nodeIndex & 0x1 == 1) { - result[pos] = data[nodeIndex - 1]; - } else if (nodeIndex + 1 == data.length) { - result[pos] = bytes32(0); - } else { - result[pos] = data[nodeIndex + 1]; - } - ++pos; - nodeIndex /= 2; - } - data = _hashLevel(data); - } - // Resize the length of the array to fit. - /// @solidity memory-safe-assembly - assembly { - mstore(result, pos) - } - - return result; - } - - function _hashLevel(bytes32[] memory data) private pure returns (bytes32[] memory) { - bytes32[] memory result; - unchecked { - uint256 length = data.length; - if (length & 0x1 == 1) { - result = new bytes32[](length / 2 + 1); - result[result.length - 1] = _hashPair(data[length - 1], bytes32(0)); - } else { - result = new bytes32[](length / 2); - } - uint256 pos = 0; - for (uint256 i = 0; i < length - 1; i += 2) { - result[pos] = _hashPair(data[i], data[i + 1]); - ++pos; - } - } - return result; - } - - function _hashPair(bytes32 left, bytes32 right) private pure returns (bytes32 result) { - /// @solidity memory-safe-assembly - assembly { - switch lt(left, right) - case 0 { - mstore(0x0, right) - mstore(0x20, left) - } - default { - mstore(0x0, left) - mstore(0x20, right) - } - result := keccak256(0x0, 0x40) - } - } - - function testEmptyCalldataHelpers() public { - assertFalse( - MerkleProofLib.verifyMultiProofCalldata( - MerkleProofLib.emptyProof(), - bytes32(0), - MerkleProofLib.emptyLeaves(), - MerkleProofLib.emptyFlags() - ) - ); - - assertFalse( - MerkleProofLib.verifyMultiProof( - MerkleProofLib.emptyProof(), - bytes32(0), - MerkleProofLib.emptyLeaves(), - MerkleProofLib.emptyFlags() - ) - ); - } -} diff --git a/lib/solady/test/MetadataReaderLib.t.sol b/lib/solady/test/MetadataReaderLib.t.sol deleted file mode 100644 index db7f8ac..0000000 --- a/lib/solady/test/MetadataReaderLib.t.sol +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MetadataReaderLib} from "../src/utils/MetadataReaderLib.sol"; -import {LibString} from "../src/utils/LibString.sol"; - -contract MetadataReaderLibTest is SoladyTest { - string internal _stringToReturn; - - uint256 internal _randomness; - - function returnsString() public view returns (string memory) { - uint256 r = _randomness; - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, add(0x100, mload(0x40))) - mstore(0x00, r) - } - string memory s = _stringToReturn; - /// @solidity memory-safe-assembly - assembly { - if iszero(and(r, 1)) { - if iszero(and(r, 2)) { - mstore(sub(s, 0x40), 0x40) - return(sub(s, 0x40), add(0x60, add(mload(s), byte(2, r)))) - } - mstore(sub(s, 0x20), 0x20) - return(sub(s, 0x20), add(0x40, add(mload(s), byte(2, r)))) - } - mstore(0x00, gas()) - mstore(0x20, r) - mstore(add(mload(s), add(s, 0x20)), shr(8, keccak256(0x00, 0x40))) - return(add(s, 0x20), add(mload(s), byte(2, r))) - } - } - - function returnsEmptyString() public view returns (string memory) { - uint256 r = _randomness; - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, add(0x100, mload(0x40))) - mstore(0x00, r) - } - string memory s = _stringToReturn; - /// @solidity memory-safe-assembly - assembly { - if iszero(and(r, 1)) { - if iszero(and(r, 2)) { - mstore(sub(s, 0x40), 0x41) - return(sub(s, 0x40), add(0x60, mload(s))) - } - mstore(sub(s, 0x20), 0x21) - return(sub(s, 0x20), add(0x40, mload(s))) - } - if iszero(and(r, 2)) { - let n := mload(s) - mstore(s, add(n, 1)) - if iszero(and(r, 2)) { - mstore(sub(s, 0x40), 0x40) - return(sub(s, 0x40), add(0x60, n)) - } - mstore(sub(s, 0x20), 0x20) - return(sub(s, 0x20), add(0x40, n)) - } - let m := mload(0x40) - codecopy(m, codesize(), 0x200) - mstore(m, and(63, byte(3, r))) - return(m, and(63, byte(2, r))) - } - } - - function returnsChoppedString(uint256 chop) public pure returns (string memory) { - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - mstore(add(m, 0x00), 0x20) - mstore(add(m, 0x20), 0x20) - mstore(add(m, 0x40), "112233445566778899aa112233445566") - return(add(m, 0x00), add(0x40, chop)) - } - } - - function returnsBytes32StringA() public pure returns (bytes32) { - return bytes32(hex"4d696c616479"); - } - - function returnsBytes32StringB() public pure returns (bytes32) { - return bytes32("This string has thirty two bytes"); - } - - function returnsNothing() public pure {} - - function reverts() public pure { - revert("Lorem Ipsum"); - } - - function returnsChoppedUint(uint256 v, uint256 chop) public pure returns (uint256) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, v) - return(0x00, chop) - } - } - - function name() public view returns (string memory) { - return returnsString(); - } - - function symbol() public view returns (string memory) { - return returnsString(); - } - - function returnsUint() public view returns (uint256) { - return _randomness; - } - - function decimals() public view returns (uint8) { - return uint8(_randomness); - } - - function testReadBytes32String() public brutalizeMemory { - bytes memory data; - string memory result; - data = abi.encodeWithSignature("returnsBytes32StringA()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, "Milady"); - data = abi.encodeWithSignature("returnsBytes32StringB()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, "This string has thirty two bytes"); - data = abi.encodeWithSignature("returnsNothing()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, ""); - data = abi.encodeWithSignature("reverts()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, ""); - } - - function testReadBytes32StringTruncated() public brutalizeMemory { - bytes memory data; - string memory result; - unchecked { - data = abi.encodeWithSignature("returnsBytes32StringB()"); - for (uint256 limit; limit < 39; ++limit) { - result = MetadataReaderLib.readString(address(this), data, limit); - _checkMemory(result); - assertEq(result, LibString.slice("This string has thirty two bytes", 0, limit)); - } - } - } - - function testReadStringChopped() public { - bytes memory data; - string memory result; - data = abi.encodeWithSignature("returnsChoppedString(uint256)", uint256(32)); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, "112233445566778899aa112233445566"); - - for (uint256 limit; limit < 39; limit += 5) { - for (uint256 chop; chop < 39; chop += 3) { - data = abi.encodeWithSignature("returnsChoppedString(uint256)", uint256(chop)); - result = MetadataReaderLib.readString(address(this), data, limit); - _checkMemory(result); - // As long as the returndatasize is insufficient, the `abi.decode` will fail, - // and the resultant string will be empty. - // Even if the `limit` is smaller than `chop`. - string memory expected; - if (chop >= 32) { - expected = LibString.slice("112233445566778899aa112233445566", 0, limit); - } - assertEq(result, expected); - } - } - } - - function testReadString(uint256 r) public brutalizeMemory { - bytes memory data; - string memory result; - string memory s = _generateString(); - _stringToReturn = s; - _randomness = r; - data = abi.encodeWithSignature("returnsString()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, s); - result = MetadataReaderLib.readName(address(this), type(uint256).max); - _checkMemory(result); - assertEq(result, s); - result = MetadataReaderLib.readSymbol(address(this), type(uint256).max); - _checkMemory(result); - assertEq(result, s); - data = abi.encodeWithSignature("returnsEmptyString()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, ""); - data = abi.encodeWithSignature("reverts()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, ""); - data = abi.encodeWithSignature("returnsNothing()"); - result = MetadataReaderLib.readString(address(this), data, type(uint256).max); - _checkMemory(result); - assertEq(result, ""); - } - - function testReadStringTruncated(uint256 r) public brutalizeMemory { - bytes memory data; - string memory result; - string memory s = _generateString(); - _stringToReturn = s; - _randomness = r; - unchecked { - uint256 limit = _bound(_random(), 0, bytes(s).length * 2); - data = abi.encodeWithSignature("returnsString()"); - result = MetadataReaderLib.readString(address(this), data, limit); - _checkMemory(result); - assertEq(result, LibString.slice(s, 0, limit)); - } - } - - function testReadUint(uint256 r) public { - _randomness = r; - bytes memory data = abi.encodeWithSignature("returnsUint()"); - assertEq(MetadataReaderLib.readUint(address(this), data), r); - assertEq(MetadataReaderLib.readDecimals(address(this)), uint8(r)); - } - - function testReadUint() public { - bytes memory data; - uint256 result; - data = abi.encodeWithSignature("returnsNothing()"); - result = MetadataReaderLib.readUint(address(this), data); - assertEq(result, 0); - data = abi.encodeWithSignature("reverts()"); - result = MetadataReaderLib.readUint(address(this), data); - assertEq(result, 0); - - for (uint256 j; j != 8; ++j) { - for (uint256 i; i != 70; ++i) { - uint256 k = _hash(i, j); - data = abi.encodeWithSignature("returnsChoppedUint(uint256,uint256)", k, i); - result = MetadataReaderLib.readUint(address(this), data); - assertEq(result, i < 32 ? 0 : k); - } - } - } - - function testBoundsCheckDifferential(uint256) public { - uint256 rds = _bound(_random(), 0, 128); - uint256 l = _random() % 2 == 0 ? type(uint248).max : 128; - uint256 o = _bound(_random(), 0, l); - uint256 n = _bound(_random(), 0, l); - bool result; - /// @solidity memory-safe-assembly - assembly { - if iszero(lt(rds, 0x40)) { - if iszero(gt(o, sub(rds, 0x20))) { - if iszero(gt(n, sub(rds, add(o, 0x20)))) { result := 1 } - } - } - } - bool expected = rds >= 0x40 && !(o + 0x20 > rds) && !(n + o + 0x20 > rds); - assertEq(result, expected); - } - - function _hash(uint256 i, uint256 j) internal pure returns (uint256 result) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, i) - mstore(0x20, j) - result := keccak256(0x00, 0x20) - } - } - - function _generateString() internal returns (string memory result) { - uint256 randomness = _random(); - uint256 resultLength = _randomStringLength(); - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - mstore(0x00, randomness) - mstore(0x40, and(add(add(result, 0x40), resultLength), not(31))) - mstore(result, resultLength) - - // forgefmt: disable-next-item - for { let i := 0 } lt(i, resultLength) { i := add(i, 1) } { - mstore(0x20, gas()) - let c := byte(0, keccak256(0x00, 0x40)) - mstore8(add(add(result, 0x20), i), or(c, iszero(c))) - } - } - } - - function _randomStringLength() internal returns (uint256 r) { - r = _random() % 256; - if (r < 64) return _random() % 128; - if (r < 128) return _random() % 64; - return _random() % 16; - } -} diff --git a/lib/solady/test/MinHeapLib.t.sol b/lib/solady/test/MinHeapLib.t.sol deleted file mode 100644 index 07a1efa..0000000 --- a/lib/solady/test/MinHeapLib.t.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MinHeapLib} from "../src/utils/MinHeapLib.sol"; -import {LibSort} from "../src/utils/LibSort.sol"; - -contract MinHeapLibTest is SoladyTest { - using MinHeapLib for MinHeapLib.Heap; - - MinHeapLib.Heap heap0; - - MinHeapLib.Heap heap1; - - function testHeapRoot(uint256 x) public { - vm.expectRevert(MinHeapLib.HeapIsEmpty.selector); - heap0.root(); - heap0.data.push(x); - assertEq(heap0.length(), 1); - assertEq(heap0.root(), x); - } - - function testHeapPushAndPop(uint256) public { - unchecked { - uint256 n = _random() % 8; - uint256[] memory a = new uint256[](n); - - for (uint256 i; i < n; ++i) { - uint256 r = _random(); - a[i] = r; - heap0.push(r); - } - LibSort.insertionSort(a); - for (uint256 i; i < n; ++i) { - assertEq(heap0.pop(), a[i]); - } - assertEq(heap0.length(), 0); - } - } - - function testHeapPushPop(uint256) public { - unchecked { - uint256 n = _random() % 8; - for (uint256 i; i < n; ++i) { - uint256 r = _random(); - heap0.push(r); - heap1.push(r); - } - n = _random() % 8; - for (uint256 i; i < n; ++i) { - uint256 r = _random(); - uint256 popped0 = heap0.pushPop(r); - heap1.push(r); - uint256 popped1 = heap1.pop(); - assertEq(popped0, popped1); - } - } - } - - function testHeapReplace(uint256) public { - unchecked { - uint256 n = _random() % 8 + 1; - for (uint256 i; i < n; ++i) { - uint256 r = _random(); - heap0.push(r); - heap1.push(r); - } - n = _random() % 8; - for (uint256 i; i < n; ++i) { - uint256 r = _random(); - uint256 popped0 = heap0.replace(r); - uint256 popped1 = heap1.pop(); - heap1.push(r); - assertEq(popped0, popped1); - } - } - } - - function testHeapEnqueue(uint256) public { - unchecked { - uint256 maxLength = _random() % 8 + 1; - uint256 m = _random() % 32 + maxLength; - uint256[] memory a = new uint256[](m); - uint256[] memory rejected = new uint256[](m); - uint256 numRejected; - for (uint256 i; i < m; ++i) { - uint256 r = _random(); - (bool success, bool hasPopped, uint256 popped) = heap0.enqueue(r, maxLength); - if (hasPopped) { - assertEq(heap0.length(), maxLength); - assertEq(success, true); - rejected[numRejected++] = popped; - } - if (!success) { - assertEq(heap0.length(), maxLength); - rejected[numRejected++] = r; - } - a[i] = r; - } - LibSort.insertionSort(a); - /// @solidity memory-safe-assembly - assembly { - mstore(rejected, numRejected) - } - LibSort.insertionSort(rejected); - for (uint256 i; i < maxLength; ++i) { - assertEq(a[m - maxLength + i], heap0.pop()); - } - assertEq(numRejected + maxLength, m); - for (uint256 i; i < numRejected; ++i) { - assertEq(a[i], rejected[i]); - } - } - } - - function testHeapEnqueueGas(uint256) public { - unchecked { - for (uint256 i; i < 16; ++i) { - this.enqueue(i, 8); - } - for (uint256 i; i < 16; ++i) { - this.enqueue(_random() % 16, 8); - } - } - } - - function enqueue(uint256 value, uint256 maxLength) public { - heap0.enqueue(value, maxLength); - } -} diff --git a/lib/solady/test/Multicallable.t.sol b/lib/solady/test/Multicallable.t.sol deleted file mode 100644 index bbc1de8..0000000 --- a/lib/solady/test/Multicallable.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {MockMulticallable} from "./utils/mocks/MockMulticallable.sol"; - -contract MulticallableTest is SoladyTest { - MockMulticallable multicallable; - - function setUp() public { - multicallable = new MockMulticallable(); - } - - function testMulticallableRevertWithMessage(string memory revertMessage) public { - bytes[] memory data = new bytes[](1); - data[0] = - abi.encodeWithSelector(MockMulticallable.revertsWithString.selector, revertMessage); - vm.expectRevert(bytes(revertMessage)); - multicallable.multicall(data); - } - - function testMulticallableRevertWithMessage() public { - testMulticallableRevertWithMessage("Milady"); - } - - function testMulticallableRevertWithCustomError() public { - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(MockMulticallable.revertsWithCustomError.selector); - vm.expectRevert(MockMulticallable.CustomError.selector); - multicallable.multicall(data); - } - - function testMulticallableRevertWithNothing() public { - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(MockMulticallable.revertsWithNothing.selector); - vm.expectRevert(); - multicallable.multicall(data); - } - - function testMulticallableReturnDataIsProperlyEncoded( - uint256 a0, - uint256 b0, - uint256 a1, - uint256 b1 - ) public { - bytes[] memory data = new bytes[](2); - data[0] = abi.encodeWithSelector(MockMulticallable.returnsTuple.selector, a0, b0); - data[1] = abi.encodeWithSelector(MockMulticallable.returnsTuple.selector, a1, b1); - bytes[] memory returnedData = multicallable.multicall(data); - MockMulticallable.Tuple memory t0 = abi.decode(returnedData[0], (MockMulticallable.Tuple)); - MockMulticallable.Tuple memory t1 = abi.decode(returnedData[1], (MockMulticallable.Tuple)); - assertEq(t0.a, a0); - assertEq(t0.b, b0); - assertEq(t1.a, a1); - assertEq(t1.b, b1); - } - - function testMulticallableReturnDataIsProperlyEncoded( - string memory sIn0, - string memory sIn1, - uint256 n - ) public { - n = n % 2; - bytes[] memory dataIn = new bytes[](n); - if (n > 0) { - dataIn[0] = abi.encodeWithSelector(MockMulticallable.returnsString.selector, sIn0); - } - if (n > 1) { - dataIn[1] = abi.encodeWithSelector(MockMulticallable.returnsString.selector, sIn1); - } - bytes[] memory dataOut = multicallable.multicall(dataIn); - if (n > 0) { - assertEq(abi.decode(dataOut[0], (string)), sIn0); - } - if (n > 1) { - assertEq(abi.decode(dataOut[1], (string)), sIn1); - } - } - - function testMulticallableReturnDataIsProperlyEncoded() public { - testMulticallableReturnDataIsProperlyEncoded(0, 1, 2, 3); - } - - function testMulticallableBenchmark() public { - unchecked { - bytes[] memory data = new bytes[](10); - for (uint256 i; i != data.length; ++i) { - data[i] = abi.encodeWithSelector(MockMulticallable.returnsTuple.selector, i, i + 1); - } - bytes[] memory returnedData = multicallable.multicall(data); - assertEq(returnedData.length, data.length); - } - } - - function testMulticallableOriginalBenchmark() public { - unchecked { - bytes[] memory data = new bytes[](10); - for (uint256 i; i != data.length; ++i) { - data[i] = abi.encodeWithSelector(MockMulticallable.returnsTuple.selector, i, i + 1); - } - bytes[] memory returnedData = multicallable.multicallOriginal(data); - assertEq(returnedData.length, data.length); - } - } - - function testMulticallableWithNoData() public { - bytes[] memory data = new bytes[](0); - assertEq(multicallable.multicall(data).length, 0); - } - - function testMulticallablePreservesMsgSender() public { - address caller = address(uint160(0xbeef)); - bytes[] memory data = new bytes[](1); - data[0] = abi.encodeWithSelector(MockMulticallable.returnsSender.selector); - vm.prank(caller); - address returnedAddress = abi.decode(multicallable.multicall(data)[0], (address)); - assertEq(caller, returnedAddress); - } -} diff --git a/lib/solady/test/Ownable.t.sol b/lib/solady/test/Ownable.t.sol deleted file mode 100644 index 9e14132..0000000 --- a/lib/solady/test/Ownable.t.sol +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import "./utils/mocks/MockOwnable.sol"; - -contract OwnableTest is SoladyTest { - event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); - - event OwnershipHandoverRequested(address indexed pendingOwner); - - event OwnershipHandoverCanceled(address indexed pendingOwner); - - MockOwnable mockOwnable; - - function setUp() public { - mockOwnable = new MockOwnable(); - } - - function testBytecodeSize() public { - MockOwnableBytecodeSizer mock = new MockOwnableBytecodeSizer(); - assertTrue(address(mock).code.length > 0); - assertEq(mock.owner(), address(this)); - } - - function testInitializeOwnerDirect() public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(0), address(1)); - mockOwnable.initializeOwnerDirect(address(1)); - } - - function testSetOwnerDirect(address newOwner) public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), newOwner); - mockOwnable.setOwnerDirect(newOwner); - assertEq(mockOwnable.owner(), newOwner); - } - - function testSetOwnerDirect() public { - testSetOwnerDirect(address(1)); - } - - function testRenounceOwnership() public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), address(0)); - mockOwnable.renounceOwnership(); - assertEq(mockOwnable.owner(), address(0)); - } - - function testTransferOwnership( - address newOwner, - bool setNewOwnerToZeroAddress, - bool callerIsOwner - ) public { - assertEq(mockOwnable.owner(), address(this)); - - vm.assume(newOwner != address(this)); - - if (newOwner == address(0) || setNewOwnerToZeroAddress) { - newOwner = address(0); - vm.expectRevert(Ownable.NewOwnerIsZeroAddress.selector); - } else if (callerIsOwner) { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), newOwner); - } else { - vm.prank(newOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - } - - mockOwnable.transferOwnership(newOwner); - - if (newOwner != address(0) && callerIsOwner) { - assertEq(mockOwnable.owner(), newOwner); - } - } - - function testTransferOwnership() public { - testTransferOwnership(address(1), false, true); - } - - function testOnlyOwnerModifier(address nonOwner, bool callerIsOwner) public { - vm.assume(nonOwner != address(this)); - - if (!callerIsOwner) { - vm.prank(nonOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - } - mockOwnable.updateFlagWithOnlyOwner(); - } - - function testHandoverOwnership(address pendingOwner) public { - vm.prank(pendingOwner); - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverRequested(pendingOwner); - mockOwnable.requestOwnershipHandover(); - assertTrue(mockOwnable.ownershipHandoverExpiresAt(pendingOwner) > block.timestamp); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), pendingOwner); - - mockOwnable.completeOwnershipHandover(pendingOwner); - - assertEq(mockOwnable.owner(), pendingOwner); - } - - function testHandoverOwnership() public { - testHandoverOwnership(address(1)); - } - - function testHandoverOwnershipRevertsIfCompleteIsNotOwner() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnable.requestOwnershipHandover(); - - vm.prank(pendingOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - mockOwnable.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipWithCancellation() public { - address pendingOwner = address(1); - - vm.prank(pendingOwner); - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverRequested(pendingOwner); - mockOwnable.requestOwnershipHandover(); - assertTrue(mockOwnable.ownershipHandoverExpiresAt(pendingOwner) > block.timestamp); - - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverCanceled(pendingOwner); - vm.prank(pendingOwner); - mockOwnable.cancelOwnershipHandover(); - assertEq(mockOwnable.ownershipHandoverExpiresAt(pendingOwner), 0); - vm.expectRevert(Ownable.NoHandoverRequest.selector); - - mockOwnable.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipBeforeExpiration() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnable.requestOwnershipHandover(); - - vm.warp(block.timestamp + mockOwnable.ownershipHandoverValidFor()); - - mockOwnable.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipAfterExpiration() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnable.requestOwnershipHandover(); - - vm.warp(block.timestamp + mockOwnable.ownershipHandoverValidFor() + 1); - - vm.expectRevert(Ownable.NoHandoverRequest.selector); - - mockOwnable.completeOwnershipHandover(pendingOwner); - } - - function testOwnershipHandoverValidForDefaultValue() public { - assertEq(mockOwnable.ownershipHandoverValidFor(), 48 * 3600); - } -} diff --git a/lib/solady/test/OwnableRoles.t.sol b/lib/solady/test/OwnableRoles.t.sol deleted file mode 100644 index f8fc7a4..0000000 --- a/lib/solady/test/OwnableRoles.t.sol +++ /dev/null @@ -1,377 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import "./utils/mocks/MockOwnableRoles.sol"; - -contract OwnableRolesTest is SoladyTest { - event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); - - event OwnershipHandoverRequested(address indexed pendingOwner); - - event OwnershipHandoverCanceled(address indexed pendingOwner); - - event RolesUpdated(address indexed user, uint256 indexed roles); - - MockOwnableRoles mockOwnableRoles; - - function setUp() public { - mockOwnableRoles = new MockOwnableRoles(); - } - - function testBytecodeSize() public { - MockOwnableRolesBytecodeSizer mock = new MockOwnableRolesBytecodeSizer(); - assertTrue(address(mock).code.length > 0); - assertEq(mock.owner(), address(this)); - } - - function testInitializeOwnerDirect() public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(0), address(1)); - mockOwnableRoles.initializeOwnerDirect(address(1)); - } - - function testSetOwnerDirect(address newOwner) public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), newOwner); - mockOwnableRoles.setOwnerDirect(newOwner); - assertEq(mockOwnableRoles.owner(), newOwner); - } - - function testGrantAndRemoveRolesDirect( - address user, - uint256 rolesToGrant, - uint256 rolesToRemove - ) public { - mockOwnableRoles.removeRolesDirect(user, mockOwnableRoles.rolesOf(user)); - assertEq(mockOwnableRoles.rolesOf(user), 0); - mockOwnableRoles.grantRolesDirect(user, rolesToGrant); - assertEq(mockOwnableRoles.rolesOf(user), rolesToGrant); - mockOwnableRoles.removeRolesDirect(user, rolesToRemove); - assertEq(mockOwnableRoles.rolesOf(user), rolesToGrant ^ (rolesToGrant & rolesToRemove)); - } - - function testSetRolesDirect(uint256) public { - address userA = _randomNonZeroAddress(); - address userB = _randomNonZeroAddress(); - while (userA == userB) userA = _randomNonZeroAddress(); - for (uint256 t; t != 2; ++t) { - uint256 rolesA = _random(); - uint256 rolesB = _random(); - vm.expectEmit(true, true, true, true); - emit RolesUpdated(userA, rolesA); - mockOwnableRoles.setRolesDirect(userA, rolesA); - emit RolesUpdated(userB, rolesB); - mockOwnableRoles.setRolesDirect(userB, rolesB); - assertEq(mockOwnableRoles.rolesOf(userA), rolesA); - assertEq(mockOwnableRoles.rolesOf(userB), rolesB); - } - } - - function testSetOwnerDirect() public { - testSetOwnerDirect(address(1)); - } - - function testRenounceOwnership() public { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), address(0)); - mockOwnableRoles.renounceOwnership(); - assertEq(mockOwnableRoles.owner(), address(0)); - } - - function testTransferOwnership( - address newOwner, - bool setNewOwnerToZeroAddress, - bool callerIsOwner - ) public { - assertEq(mockOwnableRoles.owner(), address(this)); - - vm.assume(newOwner != address(this)); - - if (newOwner == address(0) || setNewOwnerToZeroAddress) { - newOwner = address(0); - vm.expectRevert(Ownable.NewOwnerIsZeroAddress.selector); - } else if (callerIsOwner) { - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), newOwner); - } else { - vm.prank(newOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - } - - mockOwnableRoles.transferOwnership(newOwner); - - if (newOwner != address(0) && callerIsOwner) { - assertEq(mockOwnableRoles.owner(), newOwner); - } - } - - function testTransferOwnership() public { - testTransferOwnership(address(1), false, true); - } - - function testGrantRoles() public { - vm.expectEmit(true, true, true, true); - emit RolesUpdated(address(1), 111111); - mockOwnableRoles.grantRoles(address(1), 111111); - } - - function testGrantAndRevokeOrRenounceRoles( - address user, - bool granterIsOwner, - bool useRenounce, - bool revokerIsOwner, - uint256 rolesToGrant, - uint256 rolesToRevoke - ) public { - vm.assume(user != address(this)); - - uint256 rolesAfterRevoke = rolesToGrant ^ (rolesToGrant & rolesToRevoke); - - assertTrue(rolesAfterRevoke & rolesToRevoke == 0); - assertTrue((rolesAfterRevoke | rolesToRevoke) & rolesToGrant == rolesToGrant); - - if (granterIsOwner) { - vm.expectEmit(true, true, true, true); - emit RolesUpdated(user, rolesToGrant); - } else { - vm.prank(user); - vm.expectRevert(Ownable.Unauthorized.selector); - } - mockOwnableRoles.grantRoles(user, rolesToGrant); - - if (!granterIsOwner) return; - - assertEq(mockOwnableRoles.rolesOf(user), rolesToGrant); - - if (useRenounce) { - vm.expectEmit(true, true, true, true); - emit RolesUpdated(user, rolesAfterRevoke); - vm.prank(user); - mockOwnableRoles.renounceRoles(rolesToRevoke); - } else if (revokerIsOwner) { - vm.expectEmit(true, true, true, true); - emit RolesUpdated(user, rolesAfterRevoke); - mockOwnableRoles.revokeRoles(user, rolesToRevoke); - } else { - vm.prank(user); - vm.expectRevert(Ownable.Unauthorized.selector); - mockOwnableRoles.revokeRoles(user, rolesToRevoke); - return; - } - - assertEq(mockOwnableRoles.rolesOf(user), rolesAfterRevoke); - } - - function testHasAllRoles( - address user, - uint256 rolesToGrant, - uint256 rolesToGrantBrutalizer, - uint256 rolesToCheck, - bool useSameRoles - ) public { - if (useSameRoles) { - rolesToGrant = rolesToCheck; - } - rolesToGrant |= rolesToGrantBrutalizer; - mockOwnableRoles.grantRoles(user, rolesToGrant); - - bool hasAllRoles = (rolesToGrant & rolesToCheck) == rolesToCheck; - assertEq(mockOwnableRoles.hasAllRoles(user, rolesToCheck), hasAllRoles); - } - - function testHasAnyRole(address user, uint256 rolesToGrant, uint256 rolesToCheck) public { - mockOwnableRoles.grantRoles(user, rolesToGrant); - assertEq(mockOwnableRoles.hasAnyRole(user, rolesToCheck), rolesToGrant & rolesToCheck != 0); - } - - function testRolesFromOrdinals(uint8[] memory ordinals) public { - uint256 roles; - unchecked { - for (uint256 i; i < ordinals.length; ++i) { - roles |= 1 << uint256(ordinals[i]); - } - } - assertEq(mockOwnableRoles.rolesFromOrdinals(ordinals), roles); - } - - function testRolesFromOrdinals() public { - unchecked { - for (uint256 t; t != 32; ++t) { - uint8[] memory ordinals = new uint8[](_random() % 32); - for (uint256 i; i != ordinals.length; ++i) { - uint256 randomness = _random(); - uint8 r; - assembly { - r := randomness - } - ordinals[i] = r; - } - testRolesFromOrdinals(ordinals); - } - } - } - - function testOrdinalsFromRoles(uint256 roles) public { - uint8[] memory ordinals = new uint8[](256); - uint256 n; - unchecked { - for (uint256 i; i < 256; ++i) { - if (roles & (1 << i) != 0) ordinals[n++] = uint8(i); - } - } - uint8[] memory results = mockOwnableRoles.ordinalsFromRoles(roles); - assertEq(results.length, n); - unchecked { - for (uint256 i; i < n; ++i) { - assertEq(results[i], ordinals[i]); - } - } - } - - function testOrdinalsFromRoles() public { - unchecked { - for (uint256 t; t != 32; ++t) { - testOrdinalsFromRoles(_random()); - } - } - } - - function testOnlyOwnerModifier(address nonOwner, bool callerIsOwner) public { - vm.assume(nonOwner != address(this)); - - if (!callerIsOwner) { - vm.prank(nonOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - } - mockOwnableRoles.updateFlagWithOnlyOwner(); - } - - function testOnlyRolesModifier(address user, uint256 rolesToGrant, uint256 rolesToCheck) - public - { - mockOwnableRoles.grantRoles(user, rolesToGrant); - - if (rolesToGrant & rolesToCheck == 0) { - vm.expectRevert(Ownable.Unauthorized.selector); - } - vm.prank(user); - mockOwnableRoles.updateFlagWithOnlyRoles(rolesToCheck); - } - - function testOnlyOwnerOrRolesModifier( - address user, - bool callerIsOwner, - uint256 rolesToGrant, - uint256 rolesToCheck - ) public { - vm.assume(user != address(this)); - - mockOwnableRoles.grantRoles(user, rolesToGrant); - - if ((rolesToGrant & rolesToCheck == 0) && !callerIsOwner) { - vm.expectRevert(Ownable.Unauthorized.selector); - } - if (!callerIsOwner) { - vm.prank(user); - } - mockOwnableRoles.updateFlagWithOnlyOwnerOrRoles(rolesToCheck); - } - - function testOnlyRolesOrOwnerModifier( - address user, - bool callerIsOwner, - uint256 rolesToGrant, - uint256 rolesToCheck - ) public { - vm.assume(user != address(this)); - - mockOwnableRoles.grantRoles(user, rolesToGrant); - - if ((rolesToGrant & rolesToCheck == 0) && !callerIsOwner) { - vm.expectRevert(Ownable.Unauthorized.selector); - } - if (!callerIsOwner) { - vm.prank(user); - } - mockOwnableRoles.updateFlagWithOnlyRolesOrOwner(rolesToCheck); - } - - function testOnlyOwnerOrRolesModifier() public { - testOnlyOwnerOrRolesModifier(address(1), false, 1, 2); - } - - function testHandoverOwnership(address pendingOwner) public { - vm.prank(pendingOwner); - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverRequested(pendingOwner); - mockOwnableRoles.requestOwnershipHandover(); - assertTrue(mockOwnableRoles.ownershipHandoverExpiresAt(pendingOwner) > block.timestamp); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(address(this), pendingOwner); - - mockOwnableRoles.completeOwnershipHandover(pendingOwner); - - assertEq(mockOwnableRoles.owner(), pendingOwner); - } - - function testHandoverOwnership() public { - testHandoverOwnership(address(1)); - } - - function testHandoverOwnershipRevertsIfCompleteIsNotOwner() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnableRoles.requestOwnershipHandover(); - - vm.prank(pendingOwner); - vm.expectRevert(Ownable.Unauthorized.selector); - mockOwnableRoles.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipWithCancellation() public { - address pendingOwner = address(1); - - vm.prank(pendingOwner); - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverRequested(pendingOwner); - mockOwnableRoles.requestOwnershipHandover(); - assertTrue(mockOwnableRoles.ownershipHandoverExpiresAt(pendingOwner) > block.timestamp); - - vm.expectEmit(true, true, true, true); - emit OwnershipHandoverCanceled(pendingOwner); - vm.prank(pendingOwner); - mockOwnableRoles.cancelOwnershipHandover(); - assertEq(mockOwnableRoles.ownershipHandoverExpiresAt(pendingOwner), 0); - vm.expectRevert(Ownable.NoHandoverRequest.selector); - - mockOwnableRoles.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipBeforeExpiration() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnableRoles.requestOwnershipHandover(); - - vm.warp(block.timestamp + mockOwnableRoles.ownershipHandoverValidFor()); - - mockOwnableRoles.completeOwnershipHandover(pendingOwner); - } - - function testHandoverOwnershipAfterExpiration() public { - address pendingOwner = address(1); - vm.prank(pendingOwner); - mockOwnableRoles.requestOwnershipHandover(); - - vm.warp(block.timestamp + mockOwnableRoles.ownershipHandoverValidFor() + 1); - - vm.expectRevert(Ownable.NoHandoverRequest.selector); - - mockOwnableRoles.completeOwnershipHandover(pendingOwner); - } - - function testOwnershipHandoverValidForDefaultValue() public { - assertEq(mockOwnableRoles.ownershipHandoverValidFor(), 48 * 3600); - } -} diff --git a/lib/solady/test/README.md b/lib/solady/test/README.md deleted file mode 100644 index 17268ea..0000000 --- a/lib/solady/test/README.md +++ /dev/null @@ -1,5 +0,0 @@ -## WARNING! - -All test files are strictly intended for testing purposes only. - -Do NOT copy anything here into production code unless you really know what you are doing. \ No newline at end of file diff --git a/lib/solady/test/Receiver.t.sol b/lib/solady/test/Receiver.t.sol deleted file mode 100644 index 68fa005..0000000 --- a/lib/solady/test/Receiver.t.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {Receiver} from "../src/accounts/Receiver.sol"; -import {MockERC721} from "./utils/mocks/MockERC721.sol"; -import {MockERC1155} from "./utils/mocks/MockERC1155.sol"; -import {MockReceiver} from "./utils/mocks/MockReceiver.sol"; - -contract ReceiverTest is SoladyTest { - MockERC721 immutable erc721 = new MockERC721(); - MockERC1155 immutable erc1155 = new MockERC1155(); - MockReceiver immutable receiver = new MockReceiver(); - address immutable alice = address(bytes20("milady")); - - function setUp() public {} - - function testETHReceived() public { - payable(address(receiver)).transfer(1 ether); - } - - function testOnERC721Received() public { - erc721.mint(alice, 1); - vm.prank(alice); - erc721.safeTransferFrom(alice, address(receiver), 1); - } - - function testOnERC1155Received() public { - erc1155.mint(alice, 1, 1, ""); - vm.prank(alice); - erc1155.safeTransferFrom(alice, address(receiver), 1, 1, ""); - } - - function testOnERC1155BatchReceived() public { - erc1155.mint(alice, 1, 1, ""); - uint256[] memory ids = new uint256[](1); - ids[0] = 1; - uint256[] memory amts = new uint256[](1); - amts[0] = 1; - vm.prank(alice); - erc1155.safeBatchTransferFrom(alice, address(receiver), ids, amts, ""); - } -} diff --git a/lib/solady/test/RedBlackTree.t.sol b/lib/solady/test/RedBlackTree.t.sol deleted file mode 100644 index b3e83ea..0000000 --- a/lib/solady/test/RedBlackTree.t.sol +++ /dev/null @@ -1,570 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {LibSort} from "../src/utils/LibSort.sol"; -import {LibPRNG} from "../src/utils/LibPRNG.sol"; -import {RedBlackTreeLib} from "../src/utils/RedBlackTreeLib.sol"; - -contract RedBlackTreeLibTest is SoladyTest { - using RedBlackTreeLib for *; - using LibPRNG for *; - - RedBlackTreeLib.Tree tree; - RedBlackTreeLib.Tree tree2; - - function testRedBlackTreeInsertBenchStep() public { - unchecked { - LibPRNG.PRNG memory prng = LibPRNG.PRNG(123); - uint256 n = 128; - uint256 m = (1 << 160) - 1; - for (uint256 i; i != n; ++i) { - uint256 r = 1 | (prng.next() & m); - tree.insert(r); - } - _testIterateTree(); - } - } - - function testRedBlackTreeInsertBenchUint160() public { - unchecked { - LibPRNG.PRNG memory prng = LibPRNG.PRNG(123); - uint256 n = 128; - uint256[] memory a = _makeArray(n); - uint256 m = (1 << 160) - 1; - for (uint256 i; i != n; ++i) { - uint256 r = 1 | (prng.next() & m); - a[i] = r; - tree.insert(r); - } - } - } - - function testRedBlackTreeBenchUint160() public { - unchecked { - LibPRNG.PRNG memory prng = LibPRNG.PRNG(123); - uint256 n = 128; - uint256[] memory a = _makeArray(n); - uint256 m = (1 << 160) - 1; - for (uint256 i; i != n; ++i) { - uint256 r = 1 | (prng.next() & m); - a[i] = r; - tree.insert(r); - } - prng.shuffle(a); - for (uint256 i; i != n; ++i) { - tree.remove(a[i]); - } - assertEq(tree.size(), 0); - } - } - - function testRedBlackTreeInsertBenchUint256() public { - unchecked { - LibPRNG.PRNG memory prng = LibPRNG.PRNG(123); - uint256 n = 128; - uint256[] memory a = _makeArray(n); - for (uint256 i; i != n; ++i) { - uint256 r = 1 | prng.next(); - a[i] = r; - tree.insert(r); - } - } - } - - function testRedBlackTreeBenchUint256() public { - unchecked { - LibPRNG.PRNG memory prng = LibPRNG.PRNG(123); - uint256 n = 128; - uint256[] memory a = _makeArray(n); - for (uint256 i; i != n; ++i) { - uint256 r = 1 | prng.next(); - a[i] = r; - tree.insert(r); - } - prng.shuffle(a); - for (uint256 i; i != n; ++i) { - tree.remove(a[i]); - } - assertEq(tree.size(), 0); - } - } - - function testRedBlackTreeInsertAndRemove(uint256) public { - unchecked { - for (uint256 t; t < 2; ++t) { - _testRedBlackTreeInsertAndRemove(); - } - } - } - - function _testRemoveAndInsertBack(uint256[] memory a, uint256 n, uint256 t) internal { - unchecked { - uint256 choice = a[_random() % n]; - bytes32 ptr = tree.find(choice); - bool exists = !ptr.isEmpty(); - if (exists) { - assertEq(ptr.value(), choice); - ptr.remove(); - if (_random() % 4 == 0) tree.tryRemove(choice); - assertTrue(tree.find(choice).isEmpty()); - assertFalse(tree.exists(choice)); - } - if (t != 0) { - _testRemoveAndInsertBack(a, n, t - 1); - } - if (exists) { - tree.insert(choice); - if (_random() % 4 == 0) tree.tryInsert(choice); - assertFalse(tree.find(choice).isEmpty()); - assertTrue(tree.exists(choice)); - } - } - } - - function _testIterateTree() internal { - bytes32 ptr = tree.first(); - uint256 prevValue; - while (!ptr.isEmpty()) { - uint256 v = ptr.value(); - assertTrue(prevValue < v); - prevValue = v; - ptr = ptr.next(); - } - assertEq(ptr.next().value(), 0); - - ptr = tree.last(); - prevValue = 0; - while (!ptr.isEmpty()) { - uint256 v = ptr.value(); - assertTrue(prevValue == 0 || prevValue > v); - prevValue = v; - ptr = ptr.prev(); - } - assertEq(ptr.prev().value(), 0); - } - - function _testRedBlackTreeInsertAndRemove() internal { - uint256 n = _random() % (_random() % 128 == 0 ? 32 : 8); - uint256[] memory a = _fillTree(n); - - LibSort.sort(a); - LibSort.uniquifySorted(a); - assertEq(a.length, n); - assertEq(tree.size(), n); - - assertEq(tree2.size(), 0); - - unchecked { - uint256 i; - bytes32 ptr = tree.first(); - while (!ptr.isEmpty()) { - assertEq(a[i++], ptr.value()); - ptr = ptr.next(); - } - assertEq(ptr.next().value(), 0); - } - - unchecked { - uint256 i = n; - bytes32 ptr = tree.last(); - while (!ptr.isEmpty()) { - assertEq(a[--i], ptr.value()); - ptr = ptr.prev(); - } - assertEq(ptr.prev().value(), 0); - } - - _testIterateTree(); - - LibPRNG.PRNG memory prng = LibPRNG.PRNG(_random()); - prng.shuffle(a); - - unchecked { - uint256 m = n < 8 ? 4 : n; - for (uint256 i; i != n; ++i) { - tree.remove(a[i]); - assertEq(tree.size(), n - i - 1); - if (_random() % m == 0) { - _testIterateTree(); - } - } - } - assertEq(tree.size(), 0); - - unchecked { - if (_random() % 2 == 0) { - for (uint256 i; i != n; ++i) { - assertTrue(tree.find(a[i]).isEmpty()); - } - } - assertTrue(tree.first().isEmpty()); - assertEq(tree.first().value(), 0); - assertTrue(tree.last().isEmpty()); - assertEq(tree.last().value(), 0); - } - - assertEq(tree2.size(), 0); - } - - function testRedBlackTreeInsertAndRemove2(uint256) public { - unchecked { - uint256 n = _random() % 2 == 0 ? 16 : 32; - uint256[] memory candidates = _makeArray(n); - for (uint256 i; i != n; ++i) { - candidates[i] = _bound(_random(), 1, type(uint256).max); - } - uint256[] memory records = _makeArray(0); - uint256 mode = 0; - for (uint256 t = _random() % 32 + 1; t != 0; --t) { - uint256 r = candidates[_random() % n]; - bytes32 ptr = tree.find(r); - if (mode == 0) { - if (ptr.isEmpty()) { - tree.insert(r); - _addToArray(records, r); - } - } else { - if (!ptr.isEmpty()) { - tree.remove(r); - _removeFromArray(records, r); - } - } - if (_random() % 3 == 0) mode = _random() % 2; - } - LibSort.sort(records); - assertEq(tree.size(), records.length); - - assertEq(tree2.size(), 0); - - { - uint256 i = 0; - bytes32 ptr = tree.first(); - while (!ptr.isEmpty()) { - assertEq(records[i++], ptr.value()); - ptr = ptr.next(); - } - assertEq(ptr.next().value(), 0); - } - } - } - - function _makeArray(uint256 size, uint256 maxCap) - internal - pure - returns (uint256[] memory result) - { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - mstore(result, size) - mstore(0x40, add(result, shl(5, add(maxCap, 1)))) - } - } - - function _makeArray(uint256 size) internal pure returns (uint256[] memory result) { - require(size <= 512, "Size too big."); - result = _makeArray(size, 512); - } - - function _addToArray(uint256[] memory a, uint256 x) internal pure { - /// @solidity memory-safe-assembly - assembly { - let exists := 0 - let n := mload(a) - for { let i := 0 } lt(i, n) { i := add(i, 1) } { - let o := add(add(a, 0x20), shl(5, i)) - if eq(mload(o), x) { - exists := 1 - break - } - } - if iszero(exists) { - n := add(n, 1) - mstore(add(a, shl(5, n)), x) - mstore(a, n) - } - } - } - - function _removeFromArray(uint256[] memory a, uint256 x) internal pure { - /// @solidity memory-safe-assembly - assembly { - let n := mload(a) - for { let i := 0 } lt(i, n) { i := add(i, 1) } { - let o := add(add(a, 0x20), shl(5, i)) - if eq(mload(o), x) { - mstore(o, mload(add(a, shl(5, n)))) - mstore(a, sub(n, 1)) - break - } - } - } - } - - function testRedBlackTreeInsertAndRemove3() public { - unchecked { - uint256 m = type(uint256).max; - for (uint256 i; i < 256; ++i) { - tree.insert(m - i); - assertEq(tree.size(), i + 1); - } - for (uint256 i; i < 256; ++i) { - tree2.insert(i + 1); - assertEq(tree2.size(), i + 1); - } - for (uint256 i; i < 256; ++i) { - assertTrue(tree.exists(m - i)); - assertFalse(tree.exists(i + 1)); - assertTrue(tree2.exists(i + 1)); - assertFalse(tree2.exists(m - i)); - } - bytes32[] memory ptrs = new bytes32[](256); - for (uint256 i; i < 256; ++i) { - bytes32 ptr = tree.find(m - i); - ptr.remove(); - assertTrue(ptr.value() != m - i); - ptrs[i] = ptr; - assertEq(tree.size(), 256 - (i + 1)); - } - for (uint256 i; i < 256; ++i) { - assertEq(ptrs[i].value(), 0); - vm.expectRevert(RedBlackTreeLib.PointerOutOfBounds.selector); - ptrs[i].remove(); - } - for (uint256 i; i < 256; ++i) { - tree2.remove(i + 1); - assertEq(tree2.size(), 256 - (i + 1)); - } - } - } - - function testRedBlackTreeRejectsEmptyValue() public { - vm.expectRevert(RedBlackTreeLib.ValueIsEmpty.selector); - tree.insert(0); - vm.expectRevert(RedBlackTreeLib.ValueIsEmpty.selector); - tree.remove(0); - vm.expectRevert(RedBlackTreeLib.ValueIsEmpty.selector); - tree.find(0); - } - - function testRedBlackTreeRemoveViaPointer() public { - tree.insert(1); - tree.insert(2); - - bytes32 ptr = tree.find(1); - ptr.remove(); - ptr.remove(); - - vm.expectRevert(RedBlackTreeLib.PointerOutOfBounds.selector); - ptr.remove(); - - ptr = bytes32(0); - vm.expectRevert(RedBlackTreeLib.ValueDoesNotExist.selector); - ptr.remove(); - } - - function testRedBlackTreeTryInsertAndRemove() public { - tree.tryInsert(1); - tree.tryInsert(2); - assertEq(tree.size(), 2); - tree.tryInsert(1); - assertEq(tree.size(), 2); - tree.tryRemove(2); - assertEq(tree.size(), 1); - tree.tryRemove(2); - assertEq(tree.size(), 1); - } - - function testRedBlackTreeClear() public { - tree.tryInsert(1); - tree.tryInsert(2); - bytes32 ptr1 = tree.find(1); - bytes32 ptr2 = tree.find(2); - assertEq(tree.size(), 2); - tree.clear(); - assertEq(tree.size(), 0); - assertEq(ptr1.value(), 0); - assertEq(ptr2.value(), 0); - } - - function testRedBlackTreeClear(uint256) public { - unchecked { - uint256 n = _random() % (_random() % 128 == 0 ? 32 : 8); - uint256[] memory a = _fillTree(n); - - bytes32[] memory ptrs = new bytes32[](n); - for (uint256 i; i != n; ++i) { - ptrs[i] = tree.find(a[i]); - assertTrue(ptrs[i].value() != 0); - } - tree.clear(); - assertEq(tree.size(), 0); - for (uint256 i; i != n; ++i) { - assertEq(ptrs[i].value(), 0); - } - } - } - - function testRedBlackTreeTreeFullReverts() public { - tree.insert(1); - bytes32 ptr = tree.find(1); - /// @solidity memory-safe-assembly - assembly { - ptr := shl(32, shr(32, ptr)) - sstore(ptr, or(sload(ptr), sub(shl(31, 1), 1))) - } - vm.expectRevert(RedBlackTreeLib.TreeIsFull.selector); - tree.insert(2); - assertEq(tree.size(), 2 ** 31 - 1); - } - - function testRedBlackTreePointers() public { - assertTrue(tree.find(1).isEmpty()); - assertTrue(tree.find(2).isEmpty()); - - tree.insert(1); - tree.insert(2); - - assertFalse(tree.find(1).isEmpty()); - assertFalse(tree.find(2).isEmpty()); - - assertTrue(tree.find(1).prev().isEmpty()); - assertFalse(tree.find(1).next().isEmpty()); - - assertFalse(tree.find(2).prev().isEmpty()); - assertTrue(tree.find(2).next().isEmpty()); - - assertEq(tree.find(1).next(), tree.find(2)); - assertEq(tree.find(1), tree.find(2).prev()); - - assertTrue(tree.find(1).prev().isEmpty()); - assertTrue(tree.find(1).prev().prev().isEmpty()); - assertTrue(tree.find(1).prev().next().isEmpty()); - - assertTrue(tree.find(2).next().isEmpty()); - assertTrue(tree.find(2).next().next().isEmpty()); - assertTrue(tree.find(2).next().prev().isEmpty()); - - assertEq(tree.first(), tree.find(1)); - assertEq(tree.last(), tree.find(2)); - - assertTrue(tree.find(3).isEmpty()); - } - - function testRedBlackTreeNearest(uint256) public { - assertEq(tree.nearest(1), bytes32(0)); - uint256[] memory a = _fillTree(_random() % 8); - uint256 x = _bound(_random(), 1, type(uint256).max); - (uint256 nearestIndex, bool found) = _nearestIndex(a, x); - if (found) { - assertEq(tree.nearest(x).value(), a[nearestIndex]); - } else { - assertEq(tree.nearest(x), bytes32(0)); - } - } - - function _nearestIndex(uint256[] memory a, uint256 x) - internal - pure - returns (uint256 nearestIndex, bool found) - { - unchecked { - uint256 nearestValue = type(uint256).max; - uint256 nearestDist = type(uint256).max; - uint256 n = a.length; - for (uint256 i; i != n; ++i) { - uint256 y = a[i]; - uint256 dist = x < y ? y - x : x - y; - if (dist < nearestDist || (dist == nearestDist && y < nearestValue)) { - nearestIndex = i; - nearestValue = y; - nearestDist = dist; - found = true; - } - } - } - } - - function testRedBlackTreeNearestBefore(uint256) public { - assertEq(tree.nearestBefore(1), bytes32(0)); - uint256[] memory a = _fillTree(_random() % 8); - uint256 x = _bound(_random(), 1, type(uint256).max); - (uint256 nearestIndexBefore, bool found) = _nearestIndexBefore(a, x); - if (found) { - assertEq(tree.nearestBefore(x).value(), a[nearestIndexBefore]); - } else { - assertEq(tree.nearestBefore(x), bytes32(0)); - } - } - - function _nearestIndexBefore(uint256[] memory a, uint256 x) - internal - pure - returns (uint256 nearestIndex, bool found) - { - unchecked { - uint256 nearestDist = type(uint256).max; - uint256 n = a.length; - for (uint256 i; i != n; ++i) { - uint256 y = a[i]; - if (y > x) continue; - uint256 dist = x - y; - if (dist < nearestDist) { - nearestIndex = i; - nearestDist = dist; - found = true; - } - } - } - } - - function testRedBlackTreeNearestAfter(uint256) public { - assertEq(tree.nearestAfter(1), bytes32(0)); - uint256[] memory a = _fillTree(_random() % 8); - uint256 x = _bound(_random(), 1, type(uint256).max); - (uint256 nearestIndexAfter, bool found) = _nearestIndexAfter(a, x); - if (found) { - assertEq(tree.nearestAfter(x).value(), a[nearestIndexAfter]); - } else { - assertEq(tree.nearestAfter(x), bytes32(0)); - } - } - - function _nearestIndexAfter(uint256[] memory a, uint256 x) - internal - pure - returns (uint256 nearestIndex, bool found) - { - unchecked { - uint256 nearestDist = type(uint256).max; - uint256 n = a.length; - for (uint256 i; i != n; ++i) { - uint256 y = a[i]; - if (y < x) continue; - uint256 dist = y - x; - if (dist < nearestDist) { - nearestIndex = i; - nearestDist = dist; - found = true; - } - } - } - } - - function _fillTree(uint256 n) internal returns (uint256[] memory a) { - a = _makeArray(n); - unchecked { - for (uint256 i; i != n;) { - uint256 r = _bound(_random(), 1, type(uint256).max); - if (tree.find(r).isEmpty()) { - a[i++] = r; - tree.insert(r); - } - if (_random() % 4 == 0) { - _testRemoveAndInsertBack(a, i, (3 + i >> 2)); - } - } - } - } -} diff --git a/lib/solady/test/SSTORE2.t.sol b/lib/solady/test/SSTORE2.t.sol deleted file mode 100644 index 411f28a..0000000 --- a/lib/solady/test/SSTORE2.t.sol +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {SSTORE2} from "../src/utils/SSTORE2.sol"; - -contract SSTORE2Test is SoladyTest { - function testWriteRead() public { - bytes memory testBytes = abi.encode("this is a test"); - - address pointer = SSTORE2.write(testBytes); - - assertEq(SSTORE2.read(pointer), testBytes); - } - - function testWriteReadFullStartBound() public { - assertEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 0), hex"11223344"); - } - - function testWriteReadCustomStartBound() public { - assertEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1), hex"223344"); - } - - function testWriteReadFullBoundedRead() public { - bytes memory testBytes = abi.encode("this is a test"); - - assertEq(SSTORE2.read(SSTORE2.write(testBytes), 0, testBytes.length), testBytes); - } - - function testWriteReadCustomBounds() public { - assertEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1, 3), hex"2233"); - } - - function testWriteReadEmptyBound() public { - SSTORE2.read(SSTORE2.write(hex"11223344"), 3, 3); - } - - function testReadInvalidPointerReverts() public { - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(address(1)); - } - - function testReadInvalidPointerCustomStartBoundReverts() public { - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(address(1), 1); - } - - function testReadInvalidPointerCustomBoundsReverts() public { - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(address(1), 2, 4); - } - - function testWriteReadOutOfStartBoundReverts() public { - address pointer = SSTORE2.write(hex"11223344"); - vm.expectRevert(SSTORE2.ReadOutOfBounds.selector); - SSTORE2.read(pointer, 41000); - } - - function testWriteReadEmptyOutOfBoundsReverts() public { - address pointer = SSTORE2.write(hex"11223344"); - vm.expectRevert(SSTORE2.ReadOutOfBounds.selector); - SSTORE2.read(pointer, 42000, 42000); - } - - function testWriteReadOutOfBoundsReverts() public { - address pointer = SSTORE2.write(hex"11223344"); - vm.expectRevert(SSTORE2.ReadOutOfBounds.selector); - SSTORE2.read(pointer, 41000, 42000); - } - - function testWriteRead(bytes calldata testBytes) public brutalizeMemory { - _misalignFreeMemoryPointer(); - bytes memory readResult = SSTORE2.read(SSTORE2.write(testBytes)); - _checkMemory(readResult); - assertEq(readResult, testBytes); - } - - function testWriteReadCustomStartBound(bytes calldata testBytes, uint256 startIndex) - public - brutalizeMemory - { - if (testBytes.length == 0) return; - - startIndex = _bound(startIndex, 0, testBytes.length); - - _misalignFreeMemoryPointer(); - bytes memory readResult = SSTORE2.read(SSTORE2.write(testBytes), startIndex); - _checkMemory(readResult); - assertEq(readResult, bytes(testBytes[startIndex:])); - } - - function testWriteReadCustomBounds( - bytes calldata testBytes, - uint256 startIndex, - uint256 endIndex - ) public brutalizeMemory { - if (testBytes.length == 0) return; - - endIndex = _bound(endIndex, 0, testBytes.length); - startIndex = _bound(startIndex, 0, testBytes.length); - - if (startIndex > endIndex) return; - - _misalignFreeMemoryPointer(); - bytes memory readResult = SSTORE2.read(SSTORE2.write(testBytes), startIndex, endIndex); - _checkMemory(readResult); - assertEq(readResult, bytes(testBytes[startIndex:endIndex])); - } - - function testReadInvalidPointerRevert(address pointer) public brutalizeMemory { - if (pointer.code.length > 0) return; - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(pointer); - } - - function testReadInvalidPointerCustomStartBoundReverts(address pointer, uint256 startIndex) - public - brutalizeMemory - { - if (pointer.code.length > 0) return; - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(pointer, startIndex); - } - - function testReadInvalidPointerCustomBoundsReverts( - address pointer, - uint256 startIndex, - uint256 endIndex - ) public brutalizeMemory { - if (pointer.code.length > 0) return; - vm.expectRevert(SSTORE2.InvalidPointer.selector); - SSTORE2.read(pointer, startIndex, endIndex); - } - - function testWriteReadCustomStartBoundOutOfRangeReverts( - bytes calldata testBytes, - uint256 startIndex - ) public brutalizeMemory { - startIndex = _bound(startIndex, testBytes.length + 1, type(uint256).max); - address pointer = SSTORE2.write(testBytes); - vm.expectRevert(SSTORE2.ReadOutOfBounds.selector); - SSTORE2.read(pointer, startIndex); - } - - function testWriteReadCustomBoundsOutOfRangeReverts( - bytes calldata testBytes, - uint256 startIndex, - uint256 endIndex - ) public brutalizeMemory { - endIndex = _bound(endIndex, testBytes.length + 1, type(uint256).max); - address pointer = SSTORE2.write(testBytes); - vm.expectRevert(SSTORE2.ReadOutOfBounds.selector); - SSTORE2.read(pointer, startIndex, endIndex); - } - - function testWriteReadDeterministic(bytes calldata testBytes) public brutalizeMemory { - bytes32 salt = bytes32(_random()); - address deployer = address(this); - if (_random() % 8 == 0) { - (deployer,) = _randomSigner(); - } - vm.prank(deployer); - address deterministicPointer = SSTORE2.writeDeterministic(testBytes, salt); - assertEq(SSTORE2.read(deterministicPointer), testBytes); - assertEq( - SSTORE2.predictDeterministicAddress(testBytes, salt, deployer), deterministicPointer - ); - - address pointer = SSTORE2.write(testBytes); - assertEq(pointer.code, deterministicPointer.code); - } - - function testWriteWithTooBigDataReverts() public { - bytes memory data = _dummyData(0xfffe); - address pointer = this.write(data); - assertEq(SSTORE2.read(pointer), data); - vm.expectRevert(); - pointer = this.write(_dummyData(0xffff)); - } - - function write(bytes memory data) public returns (address) { - return SSTORE2.write(data); - } - - function _dummyData(uint256 n) internal pure returns (bytes memory result) { - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - mstore(result, n) - mstore(0x00, n) - mstore(0x20, 1) - mstore(add(0x20, result), keccak256(0x00, 0x40)) - mstore(0x20, 2) - mstore(add(add(0x20, result), n), keccak256(0x00, 0x40)) - mstore(0x20, 3) - mstore(add(result, n), keccak256(0x00, 0x40)) - mstore(0x40, add(add(0x20, result), n)) - } - } -} diff --git a/lib/solady/test/SafeCastLib.t.sol b/lib/solady/test/SafeCastLib.t.sol deleted file mode 100644 index 5b17145..0000000 --- a/lib/solady/test/SafeCastLib.t.sol +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {SafeCastLib} from "../src/utils/SafeCastLib.sol"; - -contract SafeCastLibTest is SoladyTest { - function testSafeCastToUint(uint256 x) public { - assertEq(SafeCastLib.toUint8(uint8(x)), uint8(x)); - if (x >= (1 << 8)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint8(x); - assertEq(SafeCastLib.toUint16(uint16(x)), uint16(x)); - if (x >= (1 << 16)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint16(x); - assertEq(SafeCastLib.toUint24(uint24(x)), uint24(x)); - if (x >= (1 << 24)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint24(x); - assertEq(SafeCastLib.toUint32(uint32(x)), uint32(x)); - if (x >= (1 << 32)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint32(x); - assertEq(SafeCastLib.toUint40(uint40(x)), uint40(x)); - if (x >= (1 << 40)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint40(x); - assertEq(SafeCastLib.toUint48(uint48(x)), uint48(x)); - if (x >= (1 << 48)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint48(x); - assertEq(SafeCastLib.toUint56(uint56(x)), uint56(x)); - if (x >= (1 << 56)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint56(x); - assertEq(SafeCastLib.toUint64(uint64(x)), uint64(x)); - if (x >= (1 << 64)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint64(x); - assertEq(SafeCastLib.toUint72(uint72(x)), uint72(x)); - if (x >= (1 << 72)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint72(x); - assertEq(SafeCastLib.toUint80(uint80(x)), uint80(x)); - if (x >= (1 << 80)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint80(x); - assertEq(SafeCastLib.toUint88(uint88(x)), uint88(x)); - if (x >= (1 << 88)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint88(x); - assertEq(SafeCastLib.toUint96(uint96(x)), uint96(x)); - if (x >= (1 << 96)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint96(x); - assertEq(SafeCastLib.toUint104(uint104(x)), uint104(x)); - if (x >= (1 << 104)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint104(x); - assertEq(SafeCastLib.toUint112(uint112(x)), uint112(x)); - if (x >= (1 << 112)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint112(x); - assertEq(SafeCastLib.toUint120(uint120(x)), uint120(x)); - if (x >= (1 << 120)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint120(x); - assertEq(SafeCastLib.toUint128(uint128(x)), uint128(x)); - if (x >= (1 << 128)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint128(x); - assertEq(SafeCastLib.toUint136(uint136(x)), uint136(x)); - if (x >= (1 << 136)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint136(x); - assertEq(SafeCastLib.toUint144(uint144(x)), uint144(x)); - if (x >= (1 << 144)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint144(x); - assertEq(SafeCastLib.toUint152(uint152(x)), uint152(x)); - if (x >= (1 << 152)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint152(x); - assertEq(SafeCastLib.toUint160(uint160(x)), uint160(x)); - if (x >= (1 << 160)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint160(x); - assertEq(SafeCastLib.toUint168(uint168(x)), uint168(x)); - if (x >= (1 << 168)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint168(x); - assertEq(SafeCastLib.toUint176(uint176(x)), uint176(x)); - if (x >= (1 << 176)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint176(x); - assertEq(SafeCastLib.toUint184(uint184(x)), uint184(x)); - if (x >= (1 << 184)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint184(x); - assertEq(SafeCastLib.toUint192(uint192(x)), uint192(x)); - if (x >= (1 << 192)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint192(x); - assertEq(SafeCastLib.toUint200(uint200(x)), uint200(x)); - if (x >= (1 << 200)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint200(x); - assertEq(SafeCastLib.toUint208(uint208(x)), uint208(x)); - if (x >= (1 << 208)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint208(x); - assertEq(SafeCastLib.toUint216(uint216(x)), uint216(x)); - if (x >= (1 << 216)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint216(x); - assertEq(SafeCastLib.toUint224(uint224(x)), uint224(x)); - if (x >= (1 << 224)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint224(x); - assertEq(SafeCastLib.toUint232(uint232(x)), uint232(x)); - if (x >= (1 << 232)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint232(x); - assertEq(SafeCastLib.toUint240(uint240(x)), uint240(x)); - if (x >= (1 << 240)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint240(x); - assertEq(SafeCastLib.toUint248(uint248(x)), uint248(x)); - if (x >= (1 << 248)) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint248(x); - } - - function testSafeCastToUint() public { - unchecked { - for (uint256 i; i != 256; ++i) { - testSafeCastToUint(1 << i); - testSafeCastToUint(type(uint256).max >> i); - testSafeCastToUint(_random()); - } - } - } - - function testSafeCastToUintBench() public { - unchecked { - uint256 sum; - for (uint256 i; i != 127; ++i) { - sum += uint256(SafeCastLib.toUint8(i)); - sum += uint256(SafeCastLib.toUint16(i)); - sum += uint256(SafeCastLib.toUint24(i)); - sum += uint256(SafeCastLib.toUint32(i)); - sum += uint256(SafeCastLib.toUint40(i)); - sum += uint256(SafeCastLib.toUint48(i)); - sum += uint256(SafeCastLib.toUint56(i)); - sum += uint256(SafeCastLib.toUint64(i)); - sum += uint256(SafeCastLib.toUint72(i)); - sum += uint256(SafeCastLib.toUint80(i)); - sum += uint256(SafeCastLib.toUint88(i)); - sum += uint256(SafeCastLib.toUint96(i)); - sum += uint256(SafeCastLib.toUint104(i)); - sum += uint256(SafeCastLib.toUint112(i)); - sum += uint256(SafeCastLib.toUint120(i)); - sum += uint256(SafeCastLib.toUint128(i)); - sum += uint256(SafeCastLib.toUint136(i)); - sum += uint256(SafeCastLib.toUint144(i)); - sum += uint256(SafeCastLib.toUint152(i)); - sum += uint256(SafeCastLib.toUint160(i)); - sum += uint256(SafeCastLib.toUint168(i)); - sum += uint256(SafeCastLib.toUint176(i)); - sum += uint256(SafeCastLib.toUint184(i)); - sum += uint256(SafeCastLib.toUint192(i)); - sum += uint256(SafeCastLib.toUint200(i)); - sum += uint256(SafeCastLib.toUint208(i)); - sum += uint256(SafeCastLib.toUint216(i)); - sum += uint256(SafeCastLib.toUint224(i)); - sum += uint256(SafeCastLib.toUint232(i)); - sum += uint256(SafeCastLib.toUint240(i)); - sum += uint256(SafeCastLib.toUint248(i)); - } - assertTrue(sum > 100); - } - } - - function testSafeCastToInt(int256 x) public { - assertEq(SafeCastLib.toInt8(int8(x)), int8(x)); - if (int8(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt8(x); - assertEq(SafeCastLib.toInt16(int16(x)), int16(x)); - if (int16(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt16(x); - assertEq(SafeCastLib.toInt24(int24(x)), int24(x)); - if (int24(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt24(x); - assertEq(SafeCastLib.toInt32(int32(x)), int32(x)); - if (int32(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt32(x); - assertEq(SafeCastLib.toInt40(int40(x)), int40(x)); - if (int40(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt40(x); - assertEq(SafeCastLib.toInt48(int48(x)), int48(x)); - if (int48(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt48(x); - assertEq(SafeCastLib.toInt56(int56(x)), int56(x)); - if (int56(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt56(x); - assertEq(SafeCastLib.toInt64(int64(x)), int64(x)); - if (int64(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt64(x); - assertEq(SafeCastLib.toInt72(int72(x)), int72(x)); - if (int72(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt72(x); - assertEq(SafeCastLib.toInt80(int80(x)), int80(x)); - if (int80(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt80(x); - assertEq(SafeCastLib.toInt88(int88(x)), int88(x)); - if (int88(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt88(x); - assertEq(SafeCastLib.toInt96(int96(x)), int96(x)); - if (int96(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt96(x); - assertEq(SafeCastLib.toInt104(int104(x)), int104(x)); - if (int104(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt104(x); - assertEq(SafeCastLib.toInt112(int112(x)), int112(x)); - if (int112(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt112(x); - assertEq(SafeCastLib.toInt120(int120(x)), int120(x)); - if (int120(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt120(x); - assertEq(SafeCastLib.toInt128(int128(x)), int128(x)); - if (int128(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt128(x); - assertEq(SafeCastLib.toInt136(int136(x)), int136(x)); - if (int136(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt136(x); - assertEq(SafeCastLib.toInt144(int144(x)), int144(x)); - if (int144(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt144(x); - assertEq(SafeCastLib.toInt152(int152(x)), int152(x)); - if (int152(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt152(x); - assertEq(SafeCastLib.toInt160(int160(x)), int160(x)); - if (int160(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt160(x); - assertEq(SafeCastLib.toInt168(int168(x)), int168(x)); - if (int168(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt168(x); - assertEq(SafeCastLib.toInt176(int176(x)), int176(x)); - if (int176(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt176(x); - assertEq(SafeCastLib.toInt184(int184(x)), int184(x)); - if (int184(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt184(x); - assertEq(SafeCastLib.toInt192(int192(x)), int192(x)); - if (int192(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt192(x); - assertEq(SafeCastLib.toInt200(int200(x)), int200(x)); - if (int200(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt200(x); - assertEq(SafeCastLib.toInt208(int208(x)), int208(x)); - if (int208(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt208(x); - assertEq(SafeCastLib.toInt216(int216(x)), int216(x)); - if (int216(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt216(x); - assertEq(SafeCastLib.toInt224(int224(x)), int224(x)); - if (int224(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt224(x); - assertEq(SafeCastLib.toInt232(int232(x)), int232(x)); - if (int232(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt232(x); - assertEq(SafeCastLib.toInt240(int240(x)), int240(x)); - if (int240(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt240(x); - assertEq(SafeCastLib.toInt248(int248(x)), int248(x)); - if (int248(x) != x) vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt248(x); - } - - function testSafeCastToInt256(uint256 x) public { - if (x > uint256(type(int256).max)) { - vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toInt256(x); - } else { - assertEq(SafeCastLib.toInt256(x), int256(x)); - } - } - - function testSafeCastToUint256(int256 x) public { - if (x < 0) { - vm.expectRevert(SafeCastLib.Overflow.selector); - SafeCastLib.toUint256(x); - } else { - assertEq(SafeCastLib.toUint256(x), uint256(x)); - } - } - - function testSafeCastToIntBench() public { - unchecked { - int256 sum; - for (int256 i; i != 127; ++i) { - sum += int256(SafeCastLib.toInt8(i)); - sum += int256(SafeCastLib.toInt16(i)); - sum += int256(SafeCastLib.toInt24(i)); - sum += int256(SafeCastLib.toInt32(i)); - sum += int256(SafeCastLib.toInt40(i)); - sum += int256(SafeCastLib.toInt48(i)); - sum += int256(SafeCastLib.toInt56(i)); - sum += int256(SafeCastLib.toInt64(i)); - sum += int256(SafeCastLib.toInt72(i)); - sum += int256(SafeCastLib.toInt80(i)); - sum += int256(SafeCastLib.toInt88(i)); - sum += int256(SafeCastLib.toInt96(i)); - sum += int256(SafeCastLib.toInt104(i)); - sum += int256(SafeCastLib.toInt112(i)); - sum += int256(SafeCastLib.toInt120(i)); - sum += int256(SafeCastLib.toInt128(i)); - sum += int256(SafeCastLib.toInt136(i)); - sum += int256(SafeCastLib.toInt144(i)); - sum += int256(SafeCastLib.toInt152(i)); - sum += int256(SafeCastLib.toInt160(i)); - sum += int256(SafeCastLib.toInt168(i)); - sum += int256(SafeCastLib.toInt176(i)); - sum += int256(SafeCastLib.toInt184(i)); - sum += int256(SafeCastLib.toInt192(i)); - sum += int256(SafeCastLib.toInt200(i)); - sum += int256(SafeCastLib.toInt208(i)); - sum += int256(SafeCastLib.toInt216(i)); - sum += int256(SafeCastLib.toInt224(i)); - sum += int256(SafeCastLib.toInt232(i)); - sum += int256(SafeCastLib.toInt240(i)); - sum += int256(SafeCastLib.toInt248(i)); - } - assertTrue(sum > 100); - } - } -} diff --git a/lib/solady/test/SafeTransferLib.t.sol b/lib/solady/test/SafeTransferLib.t.sol deleted file mode 100644 index 81617b8..0000000 --- a/lib/solady/test/SafeTransferLib.t.sol +++ /dev/null @@ -1,748 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {MockERC20} from "./utils/mocks/MockERC20.sol"; -import {MockERC20LikeUSDT} from "./utils/mocks/MockERC20LikeUSDT.sol"; -import {MockETHRecipient} from "./utils/mocks/MockETHRecipient.sol"; -import {RevertingToken} from "./utils/weird-tokens/RevertingToken.sol"; -import {ReturnsTwoToken} from "./utils/weird-tokens/ReturnsTwoToken.sol"; -import {ReturnsFalseToken} from "./utils/weird-tokens/ReturnsFalseToken.sol"; -import {MissingReturnToken} from "./utils/weird-tokens/MissingReturnToken.sol"; -import {ReturnsTooMuchToken} from "./utils/weird-tokens/ReturnsTooMuchToken.sol"; -import {ReturnsRawBytesToken} from "./utils/weird-tokens/ReturnsRawBytesToken.sol"; -import {ReturnsTooLittleToken} from "./utils/weird-tokens/ReturnsTooLittleToken.sol"; - -import "./utils/SoladyTest.sol"; - -import {ERC20} from "../src/tokens/ERC20.sol"; -import {SafeTransferLib} from "../src/utils/SafeTransferLib.sol"; - -contract SafeTransferLibTest is SoladyTest { - uint256 constant SUCCESS = 1; - uint256 constant REVERTS_WITH_SELECTOR = 2; - uint256 constant REVERTS_WITH_ANY = 3; - - RevertingToken reverting; - ReturnsTwoToken returnsTwo; - ReturnsFalseToken returnsFalse; - MissingReturnToken missingReturn; - ReturnsTooMuchToken returnsTooMuch; - ReturnsRawBytesToken returnsRawBytes; - ReturnsTooLittleToken returnsTooLittle; - - MockERC20 erc20; - - function setUp() public { - reverting = new RevertingToken(); - returnsTwo = new ReturnsTwoToken(); - returnsFalse = new ReturnsFalseToken(); - missingReturn = new MissingReturnToken(); - returnsTooMuch = new ReturnsTooMuchToken(); - returnsRawBytes = new ReturnsRawBytesToken(); - returnsTooLittle = new ReturnsTooLittleToken(); - - erc20 = new MockERC20("StandardToken", "ST", 18); - erc20.mint(address(this), type(uint256).max); - } - - function testTransferWithMissingReturn() public { - verifySafeTransfer(address(missingReturn), address(0xBEEF), 1e18, SUCCESS); - } - - function testTransferWithStandardERC20() public { - verifySafeTransfer(address(erc20), address(0xBEEF), 1e18, SUCCESS); - } - - function testTransferWithReturnsTooMuch() public { - verifySafeTransfer(address(returnsTooMuch), address(0xBEEF), 1e18, SUCCESS); - } - - function testTransferWithNonContract() public { - SafeTransferLib.safeTransfer(address(0xBADBEEF), address(0xBEEF), 1e18); - } - - function testTransferFromWithMissingReturn() public { - verifySafeTransferFrom( - address(missingReturn), address(0xFEED), address(0xBEEF), 1e18, SUCCESS - ); - } - - function testTransferFromWithStandardERC20() public { - verifySafeTransferFrom(address(erc20), address(0xFEED), address(0xBEEF), 1e18, SUCCESS); - } - - function testTransferFromWithReturnsTooMuch() public { - verifySafeTransferFrom( - address(returnsTooMuch), address(0xFEED), address(0xBEEF), 1e18, SUCCESS - ); - } - - function testTransferFromWithNonContract() public { - SafeTransferLib.safeTransferFrom(address(0xBADBEEF), address(0xFEED), address(0xBEEF), 1e18); - } - - function testApproveWithMissingReturn() public { - verifySafeApprove(address(missingReturn), address(0xBEEF), 1e18, SUCCESS); - } - - function testApproveWithStandardERC20() public { - verifySafeApprove(address(erc20), address(0xBEEF), 1e18, SUCCESS); - } - - function testApproveWithReturnsTooMuch() public { - verifySafeApprove(address(returnsTooMuch), address(0xBEEF), 1e18, SUCCESS); - } - - function testApproveWithNonContract() public { - SafeTransferLib.safeApprove(address(0xBADBEEF), address(0xBEEF), 1e18); - } - - function testApproveWithRetryWithNonContract() public { - SafeTransferLib.safeApproveWithRetry(address(0xBADBEEF), address(0xBEEF), 1e18); - } - - function testTransferETH() public { - SafeTransferLib.safeTransferETH(address(0xBEEF), 1e18); - } - - function testTransferAllETH() public { - SafeTransferLib.safeTransferAllETH(address(0xBEEF)); - } - - function testTryTransferETH() public { - MockETHRecipient recipient = new MockETHRecipient(false, false); - bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); - assertTrue(success); - } - - function testTryTransferAllETH() public { - MockETHRecipient recipient = new MockETHRecipient(false, false); - bool success = SafeTransferLib.trySafeTransferAllETH(address(recipient), gasleft()); - assertTrue(success); - } - - function testTryTransferETHWithNoStorageWrites() public { - MockETHRecipient recipient = new MockETHRecipient(true, false); - - { - bool success = SafeTransferLib.trySafeTransferETH( - address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES - ); - assertFalse(success); - } - - { - uint256 counterBefore = recipient.counter(); - bool success = SafeTransferLib.trySafeTransferETH( - address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_GRIEF - ); - assertTrue(success); - assertEq(recipient.counter(), counterBefore + 1); - } - - { - uint256 counterBefore = recipient.counter(); - bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); - assertTrue(success); - assertEq(recipient.counter(), counterBefore + 1); - } - } - - function testTryTransferETHWithNoGrief() public { - MockETHRecipient recipient = new MockETHRecipient(false, true); - - { - bool success = SafeTransferLib.trySafeTransferETH( - address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES - ); - assertFalse(success); - assertTrue(recipient.garbage() == 0); - } - - { - bool success = SafeTransferLib.trySafeTransferETH( - address(recipient), 1e18, SafeTransferLib.GAS_STIPEND_NO_GRIEF - ); - assertFalse(success); - assertTrue(recipient.garbage() == 0); - } - - { - bool success = SafeTransferLib.trySafeTransferETH(address(recipient), 1e18, gasleft()); - assertTrue(success); - assertTrue(recipient.garbage() != 0); - } - } - - function testForceTransferETHToGriever(uint256 amount, uint256 randomness) public { - amount = amount % 1000 ether; - uint256 originalBalance = address(this).balance; - vm.deal(address(this), amount * 2); - - MockETHRecipient recipient = new MockETHRecipient(false, true); - - { - uint256 receipientBalanceBefore = address(recipient).balance; - uint256 senderBalanceBefore = address(this).balance; - uint256 r = uint256(keccak256(abi.encode(randomness))) % 3; - // Send to a griever with a gas stipend. Should not revert. - if (r == 0) { - this.forceSafeTransferETH( - address(recipient), amount, SafeTransferLib.GAS_STIPEND_NO_STORAGE_WRITES - ); - } else if (r == 1) { - this.forceSafeTransferETH( - address(recipient), amount, SafeTransferLib.GAS_STIPEND_NO_GRIEF - ); - } else { - this.forceSafeTransferETH(address(recipient), amount); - } - assertEq(address(recipient).balance - receipientBalanceBefore, amount); - assertEq(senderBalanceBefore - address(this).balance, amount); - // We use the `SELFDESTRUCT` to send, and thus the `garbage` should NOT be updated. - assertTrue(recipient.garbage() == 0); - } - - { - uint256 receipientBalanceBefore = address(recipient).balance; - uint256 senderBalanceBefore = address(this).balance; - // Send more than remaining balance without gas stipend. Should revert. - vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); - this.forceSafeTransferETH(address(recipient), address(this).balance + 1, gasleft()); - assertEq(address(recipient).balance - receipientBalanceBefore, 0); - assertEq(senderBalanceBefore - address(this).balance, 0); - // We did not send anything, and thus the `garbage` should NOT be updated. - assertTrue(recipient.garbage() == 0); - } - - { - uint256 receipientBalanceBefore = address(recipient).balance; - uint256 senderBalanceBefore = address(this).balance; - // Send all the remaining balance without gas stipend. Should not revert. - amount = address(this).balance; - this.forceSafeTransferETH(address(recipient), amount, gasleft()); - assertEq(address(recipient).balance - receipientBalanceBefore, amount); - assertEq(senderBalanceBefore - address(this).balance, amount); - // We use the normal `CALL` to send, and thus the `garbage` should be updated. - assertTrue(recipient.garbage() != 0); - } - - vm.deal(address(this), originalBalance); - } - - function testForceTransferETHToGriever() public { - testForceTransferETHToGriever(1 ether, 0); - testForceTransferETHToGriever(1 ether, 1); - testForceTransferETHToGriever(1 ether, 2); - } - - function testTransferWithReturnsFalseReverts() public { - verifySafeTransfer(address(returnsFalse), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testTransferWithRevertingReverts() public { - verifySafeTransfer(address(reverting), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testTransferWithReturnsTooLittleReverts() public { - verifySafeTransfer(address(returnsTooLittle), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testTransferFromWithReturnsFalseReverts() public { - verifySafeTransferFrom( - address(returnsFalse), address(0xFEED), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR - ); - } - - function testTransferFromWithRevertingReverts() public { - verifySafeTransferFrom( - address(reverting), address(0xFEED), address(0xBEEF), 1e18, REVERTS_WITH_ANY - ); - } - - function testTransferFromWithReturnsTooLittleReverts() public { - verifySafeTransferFrom( - address(returnsTooLittle), address(0xFEED), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR - ); - } - - function testApproveWithReturnsFalseReverts() public { - verifySafeApprove(address(returnsFalse), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testApproveWithRevertingReverts() public { - verifySafeApprove(address(reverting), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testApproveWithReturnsTooLittleReverts() public { - verifySafeApprove(address(returnsTooLittle), address(0xBEEF), 1e18, REVERTS_WITH_SELECTOR); - } - - function testBalanceOfStandardERC20() public view { - erc20.balanceOf(address(this)); - } - - function testBalanceOfStandardERC20(address to, uint256 amount) public { - uint256 originalBalance = erc20.balanceOf(address(this)); - vm.assume(originalBalance >= amount); - vm.assume(to != address(this)); - - SafeTransferLib.safeTransfer(address(erc20), _brutalized(to), originalBalance - amount); - assertEq(SafeTransferLib.balanceOf(address(erc20), _brutalized(address(this))), amount); - } - - function testTransferAllWithStandardERC20() public { - SafeTransferLib.safeTransferAll(address(erc20), address(1)); - } - - function testTransferAllWithStandardERC20(address to, uint256 amount) public { - uint256 originalBalance = erc20.balanceOf(address(this)); - vm.assume(originalBalance >= amount); - vm.assume(to != address(this)); - - SafeTransferLib.safeTransfer(address(erc20), _brutalized(to), originalBalance - amount); - assertEq(erc20.balanceOf(address(this)), amount); - - assertEq(SafeTransferLib.safeTransferAll(address(erc20), _brutalized(to)), amount); - - assertEq(erc20.balanceOf(address(this)), 0); - assertEq(erc20.balanceOf(to), originalBalance); - } - - function testTransferAllFromWithStandardERC20() public { - forceApprove(address(erc20), address(this), address(this), type(uint256).max); - SafeTransferLib.safeTransferAllFrom(address(erc20), address(this), address(1)); - } - - function testTransferAllFromWithStandardERC20(address to, address from, uint256 amount) - public - { - SafeTransferLib.safeTransferAll(address(erc20), _brutalized(from)); - - uint256 originalBalance = erc20.balanceOf(from); - vm.assume(originalBalance >= amount); - vm.assume(to != from && to != address(this) && from != address(this)); - - forceApprove(address(erc20), from, address(this), type(uint256).max); - - SafeTransferLib.safeTransferFrom( - address(erc20), _brutalized(from), _brutalized(to), originalBalance - amount - ); - assertEq(erc20.balanceOf(from), amount); - - assertEq( - SafeTransferLib.safeTransferAllFrom(address(erc20), _brutalized(from), _brutalized(to)), - amount - ); - - assertEq(erc20.balanceOf(address(this)), 0); - assertEq(erc20.balanceOf(to), originalBalance); - } - - function testTransferWithMissingReturn(address to, uint256 amount) public { - verifySafeTransfer(address(missingReturn), to, amount, SUCCESS); - } - - function testTransferWithStandardERC20(address to, uint256 amount) public { - verifySafeTransfer(address(erc20), to, amount, SUCCESS); - } - - function testTransferWithReturnsTooMuch(address to, uint256 amount) public { - verifySafeTransfer(address(returnsTooMuch), to, amount, SUCCESS); - } - - function testTransferWithNonGarbage(address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateNonGarbage()); - - verifySafeTransfer(address(returnsRawBytes), to, amount, SUCCESS); - } - - function testTransferWithNonContract(address nonContract, address to, uint256 amount) public { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { - return; - } - - SafeTransferLib.safeTransfer(nonContract, _brutalized(to), amount); - } - - function testTransferETHToContractWithoutFallbackReverts() public { - vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); - this.safeTransferETH(address(this), 1e18); - } - - function testTransferAllETHToContractWithoutFallbackReverts() public { - vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); - this.safeTransferAllETH(address(this)); - } - - function testTransferFromWithMissingReturn(address from, address to, uint256 amount) public { - verifySafeTransferFrom(address(missingReturn), from, to, amount, SUCCESS); - } - - function testTransferFromWithStandardERC20(address from, address to, uint256 amount) public { - verifySafeTransferFrom(address(erc20), from, to, amount, SUCCESS); - } - - function testTransferFromWithReturnsTooMuch(address from, address to, uint256 amount) public { - verifySafeTransferFrom(address(returnsTooMuch), from, to, amount, SUCCESS); - } - - function testTransferFromWithNonGarbage(address from, address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateNonGarbage()); - - verifySafeTransferFrom(address(returnsRawBytes), from, to, amount, SUCCESS); - } - - function testTransferFromWithNonContract( - address nonContract, - address from, - address to, - uint256 amount - ) public { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { - return; - } - - SafeTransferLib.safeTransferFrom(nonContract, _brutalized(from), _brutalized(to), amount); - } - - function testApproveWithMissingReturn(address to, uint256 amount) public { - verifySafeApprove(address(missingReturn), to, amount, SUCCESS); - } - - function testApproveWithStandardERC20(address to, uint256 amount) public { - verifySafeApprove(address(erc20), to, amount, SUCCESS); - } - - function testApproveWithReturnsTooMuch(address to, uint256 amount) public { - verifySafeApprove(address(returnsTooMuch), to, amount, SUCCESS); - } - - function testApproveWithNonGarbage(address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateNonGarbage()); - - verifySafeApprove(address(returnsRawBytes), to, amount, SUCCESS); - } - - function testApproveWithNonContract(address nonContract, address to, uint256 amount) public { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { - return; - } - - SafeTransferLib.safeApprove(nonContract, _brutalized(to), amount); - } - - function testApproveWithRetryWithNonContract(address nonContract, address to, uint256 amount) - public - { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) { - return; - } - - SafeTransferLib.safeApproveWithRetry(nonContract, _brutalized(to), amount); - } - - function testApproveWithRetry(address to, uint256 amount0, uint256 amount1) public { - MockERC20LikeUSDT usdt = new MockERC20LikeUSDT(); - assertEq(usdt.allowance(address(this), to), 0); - SafeTransferLib.safeApproveWithRetry(address(usdt), _brutalized(to), amount0); - assertEq(usdt.allowance(address(this), to), amount0); - if (amount0 != 0 && amount1 != 0) { - verifySafeApprove(address(usdt), to, amount1, REVERTS_WITH_SELECTOR); - } - SafeTransferLib.safeApproveWithRetry(address(usdt), _brutalized(to), amount1); - assertEq(usdt.allowance(address(this), to), amount1); - } - - function testApproveWithRetry() public { - testApproveWithRetry(address(1), 123, 456); - } - - function testTransferETH(address recipient, uint256 amount) public { - // Transferring to msg.sender can fail because it's possible to overflow their ETH balance as it begins non-zero. - if ( - recipient.code.length > 0 || uint256(uint160(recipient)) <= 18 - || recipient == msg.sender - ) { - return; - } - - amount = _bound(amount, 0, address(this).balance); - - SafeTransferLib.safeTransferETH(recipient, amount); - } - - function testTransferAllETH(address recipient) public { - // Transferring to msg.sender can fail because it's possible to overflow their ETH balance as it begins non-zero. - if ( - recipient.code.length > 0 || uint256(uint160(recipient)) <= 18 - || recipient == msg.sender - ) { - return; - } - - SafeTransferLib.safeTransferAllETH(recipient); - } - - function testTransferWithReturnsFalseReverts(address to, uint256 amount) public { - verifySafeTransfer(address(returnsFalse), to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferWithRevertingReverts(address to, uint256 amount) public { - verifySafeTransfer(address(reverting), to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferWithReturnsTooLittleReverts(address to, uint256 amount) public { - verifySafeTransfer(address(returnsTooLittle), to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferWithReturnsTwoReverts(address to, uint256 amount) public { - verifySafeTransfer(address(returnsTwo), to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferWithGarbageReverts(address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateGarbage()); - - verifySafeTransfer(address(returnsRawBytes), to, amount, REVERTS_WITH_ANY); - } - - function testTransferFromWithReturnsFalseReverts(address from, address to, uint256 amount) - public - { - verifySafeTransferFrom(address(returnsFalse), from, to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferFromWithRevertingReverts(address from, address to, uint256 amount) - public - { - verifySafeTransferFrom(address(reverting), from, to, amount, REVERTS_WITH_ANY); - } - - function testTransferFromWithReturnsTooLittleReverts(address from, address to, uint256 amount) - public - { - verifySafeTransferFrom(address(returnsTooLittle), from, to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferFromWithReturnsTwoReverts(address from, address to, uint256 amount) - public - { - verifySafeTransferFrom(address(returnsTwo), from, to, amount, REVERTS_WITH_SELECTOR); - } - - function testTransferFromWithGarbageReverts(address from, address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateGarbage()); - - verifySafeTransferFrom(address(returnsRawBytes), from, to, amount, REVERTS_WITH_ANY); - } - - function testApproveWithReturnsFalseReverts(address to, uint256 amount) public { - verifySafeApprove(address(returnsFalse), to, amount, REVERTS_WITH_SELECTOR); - } - - function testApproveWithRevertingReverts(address to, uint256 amount) public { - verifySafeApprove(address(reverting), to, amount, REVERTS_WITH_SELECTOR); - } - - function testApproveWithReturnsTooLittleReverts(address to, uint256 amount) public { - verifySafeApprove(address(returnsTooLittle), to, amount, REVERTS_WITH_SELECTOR); - } - - function testApproveWithReturnsTwoReverts(address to, uint256 amount) public { - verifySafeApprove(address(returnsTwo), to, amount, REVERTS_WITH_SELECTOR); - } - - function testApproveWithGarbageReverts(address to, uint256 amount) public { - returnsRawBytes.setRawBytes(_generateGarbage()); - - verifySafeApprove(address(returnsRawBytes), to, amount, REVERTS_WITH_ANY); - } - - function testTransferETHToContractWithoutFallbackReverts(uint256 amount) public { - vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); - this.safeTransferETH(address(this), amount); - } - - function testTransferAllETHToContractWithoutFallbackReverts(uint256) public { - vm.expectRevert(SafeTransferLib.ETHTransferFailed.selector); - this.safeTransferAllETH(address(this)); - } - - function verifySafeTransfer(address token, address to, uint256 amount, uint256 mode) public { - if (mode == REVERTS_WITH_SELECTOR) { - vm.expectRevert(SafeTransferLib.TransferFailed.selector); - } else if (mode == REVERTS_WITH_ANY) { - (bool success,) = address(this).call( - abi.encodeWithSignature( - "verifySafeTransfer(address,address,uint256)", token, to, amount - ) - ); - assertFalse(success); - return; - } - this.verifySafeTransfer(token, to, amount); - } - - function verifySafeTransfer(address token, address to, uint256 amount) public brutalizeMemory { - uint256 preBal = ERC20(token).balanceOf(to); - if (amount == ERC20(token).balanceOf(address(this)) && _random() % 2 == 0) { - SafeTransferLib.safeTransferAll(address(token), _brutalized(to)); - } else { - SafeTransferLib.safeTransfer(address(token), _brutalized(to), amount); - } - - uint256 postBal = ERC20(token).balanceOf(to); - - if (to == address(this)) { - assertEq(preBal, postBal); - } else { - assertEq(postBal - preBal, amount); - } - } - - function verifySafeTransferFrom( - address token, - address from, - address to, - uint256 amount, - uint256 mode - ) public { - if (mode == REVERTS_WITH_SELECTOR) { - vm.expectRevert(SafeTransferLib.TransferFromFailed.selector); - } else if (mode == REVERTS_WITH_ANY) { - (bool success,) = address(this).call( - abi.encodeWithSignature( - "verifySafeTransferFrom(address,address,address,uint256)", - token, - from, - to, - amount - ) - ); - assertFalse(success); - return; - } - this.verifySafeTransferFrom(token, from, to, amount); - } - - function verifySafeTransferFrom(address token, address from, address to, uint256 amount) - public - brutalizeMemory - { - forceApprove(token, from, address(this), amount); - - // We cast to MissingReturnToken here because it won't check - // that there was return data, which accommodates all tokens. - MissingReturnToken(token).transfer(from, amount); - - uint256 preBal = ERC20(token).balanceOf(to); - if (amount == ERC20(token).balanceOf(from) && _random() % 2 == 0) { - SafeTransferLib.safeTransferAllFrom(address(token), _brutalized(from), _brutalized(to)); - } else { - SafeTransferLib.safeTransferFrom(token, _brutalized(from), _brutalized(to), amount); - } - uint256 postBal = ERC20(token).balanceOf(to); - - if (from == to) { - assertEq(preBal, postBal); - } else { - assertEq(postBal - preBal, amount); - } - } - - function verifySafeApprove(address token, address to, uint256 amount, uint256 mode) public { - if (mode == REVERTS_WITH_SELECTOR) { - vm.expectRevert(SafeTransferLib.ApproveFailed.selector); - } else if (mode == REVERTS_WITH_ANY) { - (bool success,) = address(this).call( - abi.encodeWithSignature( - "verifySafeApprove(address,address,uint256)", token, to, amount - ) - ); - assertFalse(success); - return; - } - this.verifySafeApprove(token, to, amount); - } - - function verifySafeApprove(address token, address to, uint256 amount) public { - SafeTransferLib.safeApprove(_brutalized(address(token)), _brutalized(to), amount); - - assertEq(ERC20(token).allowance(address(this), to), amount); - } - - function forceApprove(address token, address from, address to, uint256 amount) public { - if (token == address(erc20)) { - bytes32 allowanceSlot; - /// @solidity memory-safe-assembly - assembly { - mstore(0x20, to) - mstore(0x0c, 0x7f5e9f20) // `_ALLOWANCE_SLOT_SEED`. - mstore(0x00, from) - allowanceSlot := keccak256(0x0c, 0x34) - } - vm.store(token, allowanceSlot, bytes32(uint256(amount))); - } else { - vm.store( - token, - keccak256(abi.encode(to, keccak256(abi.encode(from, uint256(2))))), - bytes32(uint256(amount)) - ); - } - - assertEq(ERC20(token).allowance(from, to), amount, "wrong allowance"); - } - - function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) public { - SafeTransferLib.forceSafeTransferETH(to, amount, gasStipend); - } - - function forceSafeTransferETH(address to, uint256 amount) public { - SafeTransferLib.forceSafeTransferETH(to, amount); - } - - function safeTransferETH(address to, uint256 amount) public { - SafeTransferLib.safeTransferETH(to, amount); - } - - function safeTransferAllETH(address to) public { - SafeTransferLib.safeTransferAllETH(to); - } - - function _generateGarbage() internal returns (bytes memory result) { - uint256 r = _random(); - /// @solidity memory-safe-assembly - assembly { - for {} 1 {} { - mstore(0x00, r) - result := mload(0x40) - let n := and(r, 0x7f) - mstore(result, n) - r := keccak256(0x00, 0x40) - mstore(add(result, 0x20), r) - mstore(0x40, add(result, 0x100)) - if and(or(lt(n, 0x20), iszero(eq(r, 1))), gt(n, 0)) { break } - } - } - } - - function _generateNonGarbage() internal returns (bytes memory result) { - uint256 r = _random(); - /// @solidity memory-safe-assembly - assembly { - if iszero(and(r, 1)) { - result := mload(0x40) - mstore(result, 0x20) - mstore(add(result, 0x20), 1) - mstore(0x40, add(result, 0x40)) - } - } - } - - function _brutalized(address a) internal pure returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, keccak256(0x00, 0x20))) - } - } -} diff --git a/lib/solady/test/SignatureCheckerLib.t.sol b/lib/solady/test/SignatureCheckerLib.t.sol deleted file mode 100644 index 4713d22..0000000 --- a/lib/solady/test/SignatureCheckerLib.t.sol +++ /dev/null @@ -1,341 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {SignatureCheckerLib} from "../src/utils/SignatureCheckerLib.sol"; -import {ECDSA} from "../src/utils/ECDSA.sol"; -import {MockERC1271Wallet} from "./utils/mocks/MockERC1271Wallet.sol"; -import {MockERC1271Malicious} from "./utils/mocks/MockERC1271Malicious.sol"; - -contract SignatureCheckerLibTest is SoladyTest { - bytes32 constant TEST_MESSAGE = - 0x7dbaf558b0a1a5dc7a67202117ab143c1d8605a983e4a743bc06fcc03162dc0d; - - bytes32 constant WRONG_MESSAGE = - 0x2d0828dd7c97cff316356da3c16c68ba2316886a0e05ebafb8291939310d51a3; - - address constant SIGNER = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; - - address constant OTHER = address(uint160(1)); - - bytes32 constant TEST_SIGNED_MESSAGE_HASH = - 0x7d768af957ef8cbf6219a37e743d5546d911dae3e46449d8a5810522db2ef65e; - - bytes32 constant WRONG_SIGNED_MESSAGE_HASH = - 0x8cd3e659093d21364c6330514aff328218aa29c2693c5b0e96602df075561952; - - bytes constant SIGNATURE = - hex"8688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - - bytes constant INVALID_SIGNATURE = - hex"7688e590483917863a35ef230c0f839be8418aa4ee765228eddfcea7fe2652815db01c2c84b0ec746e1b74d97475c599b3d3419fa7181b4e01de62c02b721aea1b"; - - MockERC1271Wallet mockERC1271Wallet; - - MockERC1271Malicious mockERC1271Malicious; - - function setUp() public { - mockERC1271Wallet = new MockERC1271Wallet(SIGNER); - mockERC1271Malicious = new MockERC1271Malicious(); - } - - function testSignatureCheckerOnEOAWithMatchingSignerAndSignature() public { - _checkSignature(SIGNER, TEST_SIGNED_MESSAGE_HASH, SIGNATURE, true); - } - - function testSignatureCheckerOnEOAWithInvalidSigner() public { - _checkSignature(OTHER, TEST_SIGNED_MESSAGE_HASH, SIGNATURE, false); - } - - function testSignatureCheckerOnEOAWithWrongSignedMessageHash() public { - _checkSignature(SIGNER, WRONG_SIGNED_MESSAGE_HASH, SIGNATURE, false); - } - - function testSignatureCheckerOnEOAWithInvalidSignature() public { - _checkSignature(SIGNER, TEST_SIGNED_MESSAGE_HASH, INVALID_SIGNATURE, false); - } - - function testSignatureCheckerOnWalletWithMatchingSignerAndSignature() public { - _checkSignatureBothModes( - address(mockERC1271Wallet), TEST_SIGNED_MESSAGE_HASH, SIGNATURE, true - ); - } - - function testSignatureCheckerOnWalletWithInvalidSigner() public { - _checkSignatureBothModes(address(this), TEST_SIGNED_MESSAGE_HASH, SIGNATURE, false); - } - - function testSignatureCheckerOnWalletWithZeroAddressSigner() public { - _checkSignatureBothModes(address(0), TEST_SIGNED_MESSAGE_HASH, SIGNATURE, false); - } - - function testSignatureCheckerOnWalletWithWrongSignedMessageHash() public { - _checkSignatureBothModes( - address(mockERC1271Wallet), WRONG_SIGNED_MESSAGE_HASH, SIGNATURE, false - ); - } - - function testSignatureCheckerOnWalletWithInvalidSignature() public { - _checkSignatureBothModes( - address(mockERC1271Wallet), TEST_SIGNED_MESSAGE_HASH, INVALID_SIGNATURE, false - ); - } - - function testSignatureCheckerOnMaliciousWallet() public { - _checkSignatureBothModes( - address(mockERC1271Malicious), WRONG_SIGNED_MESSAGE_HASH, SIGNATURE, false - ); - } - - function testSignatureChecker(bytes32 digest) public { - (address signer, uint256 privateKey) = _randomSigner(); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - _checkSignature(signer, digest, abi.encodePacked(r, s, v), true); - - if (_random() % 8 == 0) { - assertEq( - this.isValidSignatureNowCalldata(signer, digest, abi.encodePacked(r, s, v)), true - ); - assertEq( - SignatureCheckerLib.isValidSignatureNow(signer, digest, abi.encodePacked(r, s, v)), - true - ); - assertEq( - SignatureCheckerLib.isValidSignatureNow( - signer, digest, abi.encodePacked(r, s, v + 1) - ), - false - ); - assertEq( - SignatureCheckerLib.isValidSignatureNow( - signer, digest, abi.encodePacked(r, s, v - 1) - ), - false - ); - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, digest, v, r, s), true); - } - - if (_random() % 8 == 0) { - bytes32 vs; - /// @solidity memory-safe-assembly - assembly { - vs := or(shl(255, sub(v, 27)), s) - } - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, digest, r, vs), true); - assertEq( - SignatureCheckerLib.isValidSignatureNow(signer, digest, abi.encode(r, vs)), true - ); - assertEq(this.isValidSignatureNowCalldata(signer, digest, abi.encode(r, vs)), true); - } - - if (_random() % 8 == 0) { - bytes32 vsc; // Corrupted `vs`. - /// @solidity memory-safe-assembly - assembly { - vsc := or(shl(255, xor(1, sub(v, 27))), s) - } - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, digest, r, vsc), false); - assertEq( - SignatureCheckerLib.isValidSignatureNow(signer, digest, abi.encode(r, vsc)), false - ); - assertEq(this.isValidSignatureNowCalldata(signer, digest, abi.encode(r, vsc)), false); - } - - if (_random() % 8 == 0 && r != bytes32(0) && s != bytes32(0)) { - bytes32 rc = bytes32(uint256(r) - (_random() & 1)); // Corrupted `r`. - bytes32 sc = bytes32(uint256(s) - (_random() & 1)); // Corrupted `s`. - bool anyCorrupted = rc != r || sc != s; - _checkSignature(signer, digest, abi.encodePacked(rc, sc, v), !anyCorrupted); - } - - if (_random() % 8 == 0) { - uint8 vc = uint8(_random()); // Corrupted `v`. - while (vc == 28 || vc == 27) vc = uint8(_random()); - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, digest, vc, r, s), false); - assertEq( - SignatureCheckerLib.isValidSignatureNow(signer, digest, abi.encodePacked(r, s, vc)), - false - ); - assertEq( - this.isValidSignatureNowCalldata(signer, digest, abi.encodePacked(r, s, vc)), false - ); - } - } - - function _checkSignatureBothModes( - address signer, - bytes32 hash, - bytes memory signature, - bool expectedResult - ) internal { - _checkSignature(false, signer, hash, signature, expectedResult); - _checkSignature(true, signer, hash, signature, expectedResult); - } - - function _checkSignature( - address signer, - bytes32 hash, - bytes memory signature, - bool expectedResult - ) internal { - _checkSignature(false, signer, hash, signature, expectedResult); - } - - function _checkSignature( - bool onlyERC1271, - address signer, - bytes32 hash, - bytes memory signature, - bool expectedResult - ) internal { - bool callResult; - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - - // `bytes4(keccak256("isValidSignatureNow(address,bytes32,bytes)"))`. - mstore(m, shl(224, 0x6ccea652)) - if onlyERC1271 { - // `bytes4(keccak256("isValidERC1271SignatureNow(address,bytes32,bytes)"))`. - mstore(m, shl(224, 0x3ae5d83c)) - } - mstore(add(m, 0x04), signer) - mstore(add(m, 0x24), hash) - mstore(add(m, 0x44), 0x60) // Offset of signature in calldata. - mstore(add(m, 0x64), mload(signature)) - mstore(add(m, 0x84), mload(add(signature, 0x20))) - mstore(add(m, 0xa4), mload(add(signature, 0x40))) - mstore(add(m, 0xc4), mload(add(signature, 0x60))) - // Brutalize the bytes following the 8-bit `v`. All ones will do. - mstore(add(m, 0xc5), not(0)) - - // We have to do the call in assembly to ensure that Solidity does not - // clean up the brutalized bits. - callResult := - and( - and( - // Whether the returndata is equal to 1. - eq(mload(0x00), 1), - // Whether the returndata is exactly 0x20 bytes (1 word) long . - eq(returndatasize(), 0x20) - ), - // Whether the staticcall does not revert. - // This must be placed at the end of the `and` clause, - // as the arguments are evaluated from right to left. - staticcall( - gas(), // Remaining gas. - address(), // The current contract's address. - m, // Offset of calldata in memory. - 0xe4, // Length of calldata in memory. - 0x00, // Offset of returndata. - 0x20 // Length of returndata to write. - ) - ) - } - assertEq(callResult, expectedResult); - - uint8 v; - bytes32 r; - bytes32 s; - bytes32 vs; - /// @solidity memory-safe-assembly - assembly { - // Contaminate the upper 96 bits. - signer := or(shl(160, 1), signer) - // Extract `r`, `s`, `v`. - r := mload(add(signature, 0x20)) - s := mload(add(signature, 0x40)) - v := byte(0, mload(add(signature, 0x60))) - // Pack `vs`. - vs := or(shl(255, sub(v, 27)), s) - - // Brutalize the memory. Just all ones will do. - let m := mload(0x40) - for { let i := 0 } lt(i, 30) { i := add(i, 1) } { mstore(add(m, shl(5, i)), not(0)) } - } - - if (onlyERC1271) { - assertEq( - SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, r, vs), expectedResult - ); - assertEq( - SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, v, r, s), - expectedResult - ); - } else { - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, hash, r, vs), expectedResult); - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, hash, v, r, s), expectedResult); - } - } - - function isValidSignatureNow(address signer, bytes32 hash, bytes calldata signature) - external - returns (bool result) - { - bool signatureIsBrutalized; - /// @solidity memory-safe-assembly - assembly { - // Contaminate the upper 96 bits. - signer := or(shl(160, 1), signer) - // Ensure that the bytes right after the signature is brutalized. - signatureIsBrutalized := calldataload(add(signature.offset, signature.length)) - } - if (!signatureIsBrutalized) revert("Signature is not brutalized."); - - result = SignatureCheckerLib.isValidSignatureNowCalldata(signer, hash, signature); - assertEq(SignatureCheckerLib.isValidSignatureNow(signer, hash, signature), result); - } - - function isValidERC1271SignatureNow(address signer, bytes32 hash, bytes calldata signature) - external - returns (bool result) - { - bool signatureIsBrutalized; - /// @solidity memory-safe-assembly - assembly { - // Contaminate the upper 96 bits. - signer := or(shl(160, 1), signer) - // Ensure that the bytes right after the signature is brutalized. - signatureIsBrutalized := calldataload(add(signature.offset, signature.length)) - } - if (!signatureIsBrutalized) revert("Signature is not brutalized."); - - result = SignatureCheckerLib.isValidERC1271SignatureNowCalldata(signer, hash, signature); - assertEq(SignatureCheckerLib.isValidERC1271SignatureNow(signer, hash, signature), result); - } - - function isValidSignatureNowCalldata(address signer, bytes32 hash, bytes calldata signature) - external - view - returns (bool result) - { - result = SignatureCheckerLib.isValidSignatureNowCalldata(signer, hash, signature); - } - - function isValidERC1271SignatureNowCalldata( - address signer, - bytes32 hash, - bytes calldata signature - ) external view returns (bool result) { - result = SignatureCheckerLib.isValidERC1271SignatureNowCalldata(signer, hash, signature); - } - - function testEmptyCalldataHelpers() public { - assertFalse( - SignatureCheckerLib.isValidSignatureNow( - address(1), bytes32(0), SignatureCheckerLib.emptySignature() - ) - ); - } - - function testToEthSignedMessageHashDifferential(bytes32 hash) public { - assertEq( - SignatureCheckerLib.toEthSignedMessageHash(hash), ECDSA.toEthSignedMessageHash(hash) - ); - } - - function testToEthSignedMessageHashDifferential(bytes memory s) public { - assertEq(SignatureCheckerLib.toEthSignedMessageHash(s), ECDSA.toEthSignedMessageHash(s)); - } -} diff --git a/lib/solady/test/UUPSUpgradeable.t.sol b/lib/solady/test/UUPSUpgradeable.t.sol deleted file mode 100644 index d7f1d7d..0000000 --- a/lib/solady/test/UUPSUpgradeable.t.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import {UUPSUpgradeable} from "../src/utils/UUPSUpgradeable.sol"; -import {LibClone} from "../src/utils/LibClone.sol"; -import {MockUUPSImplementation} from "../test/utils/mocks/MockUUPSImplementation.sol"; - -contract UUPSUpgradeableTest is SoladyTest { - MockUUPSImplementation impl1; - - address proxy; - - bytes32 internal constant _ERC1967_IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - event Upgraded(address indexed implementation); - - function setUp() public { - impl1 = new MockUUPSImplementation(); - proxy = LibClone.deployERC1967(address(impl1)); - MockUUPSImplementation(proxy).initialize(address(this)); - } - - function testNotDelegatedGuard() public { - assertEq(impl1.proxiableUUID(), _ERC1967_IMPLEMENTATION_SLOT); - vm.expectRevert(UUPSUpgradeable.UnauthorizedCallContext.selector); - MockUUPSImplementation(proxy).proxiableUUID(); - } - - function testOnlyProxyGuard() public { - vm.expectRevert(UUPSUpgradeable.UnauthorizedCallContext.selector); - impl1.upgradeToAndCall(address(1), bytes("")); - } - - function testUpgradeTo() public { - MockUUPSImplementation impl2 = new MockUUPSImplementation(); - vm.expectEmit(true, true, true, true); - emit Upgraded(address(impl2)); - MockUUPSImplementation(proxy).upgradeToAndCall(address(impl2), bytes("")); - bytes32 v = vm.load(proxy, _ERC1967_IMPLEMENTATION_SLOT); - assertEq(address(uint160(uint256(v))), address(impl2)); - } - - function testUpgradeToRevertWithUnauthorized() public { - vm.prank(address(0xBEEF)); - vm.expectRevert(MockUUPSImplementation.Unauthorized.selector); - MockUUPSImplementation(proxy).upgradeToAndCall(address(0xABCD), bytes("")); - } - - function testUpgradeToRevertWithUpgradeFailed() public { - vm.expectRevert(UUPSUpgradeable.UpgradeFailed.selector); - MockUUPSImplementation(proxy).upgradeToAndCall(address(0xABCD), bytes("")); - } - - function testUpgradeToAndCall() public { - MockUUPSImplementation impl2 = new MockUUPSImplementation(); - bytes memory data = abi.encodeWithSignature("setValue(uint256)", 5); - MockUUPSImplementation(proxy).upgradeToAndCall(address(impl2), data); - bytes32 v = vm.load(proxy, _ERC1967_IMPLEMENTATION_SLOT); - assertEq(address(uint160(uint256(v))), address(impl2)); - assertEq(MockUUPSImplementation(proxy).value(), 5); - } - - function testUpgradeToAndCallRevertWithUpgradeFailed() public { - vm.expectRevert(UUPSUpgradeable.UpgradeFailed.selector); - MockUUPSImplementation(proxy).upgradeToAndCall(address(0xABCD), ""); - } - - function testUpgradeToAndCallRevertWithCustomError() public { - MockUUPSImplementation impl2 = new MockUUPSImplementation(); - bytes memory data = abi.encodeWithSignature("revertWithError()"); - vm.expectRevert( - abi.encodeWithSelector(MockUUPSImplementation.CustomError.selector, address(this)) - ); - MockUUPSImplementation(proxy).upgradeToAndCall(address(impl2), data); - } - - function testUpgradeToAndCallRevertWithUnauthorized() public { - vm.prank(address(0xBEEF)); - vm.expectRevert(MockUUPSImplementation.Unauthorized.selector); - MockUUPSImplementation(proxy).upgradeToAndCall(address(0xABCD), ""); - } -} diff --git a/lib/solady/test/WETH.t.sol b/lib/solady/test/WETH.t.sol deleted file mode 100644 index e788f76..0000000 --- a/lib/solady/test/WETH.t.sol +++ /dev/null @@ -1,202 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./utils/SoladyTest.sol"; -import "./utils/InvariantTest.sol"; - -import {SafeTransferLib} from "../src/utils/SafeTransferLib.sol"; - -import {WETH} from "../src/tokens/WETH.sol"; - -contract ContractWithoutReceive {} - -contract WETHTest is SoladyTest { - WETH weth; - - event Deposit(address indexed from, uint256 amount); - - event Withdrawal(address indexed to, uint256 amount); - - function setUp() public { - weth = new WETH(); - } - - function _expectDepositEvent(address from, uint256 amount) internal { - vm.expectEmit(true, true, true, true); - emit Deposit(from, amount); - } - - function _expectDepositEvent(uint256 amount) internal { - _expectDepositEvent(address(this), amount); - } - - function _expectWithdrawalEvent(address to, uint256 amount) internal { - vm.expectEmit(true, true, true, true); - emit Withdrawal(to, amount); - } - - function _expectWithdrawalEvent(uint256 amount) internal { - _expectWithdrawalEvent(address(this), amount); - } - - function testMetadata() public { - assertEq(weth.name(), "Wrapped Ether"); - assertEq(weth.symbol(), "WETH"); - assertEq(weth.decimals(), 18); - } - - function testFallbackDeposit() public { - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - _expectDepositEvent(1 ether); - SafeTransferLib.safeTransferETH(address(weth), 1 ether); - - assertEq(weth.balanceOf(address(this)), 1 ether); - assertEq(weth.totalSupply(), 1 ether); - } - - function testDeposit() public { - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - _expectDepositEvent(1 ether); - weth.deposit{value: 1 ether}(); - - assertEq(weth.balanceOf(address(this)), 1 ether); - assertEq(weth.totalSupply(), 1 ether); - } - - function testWithdraw() public { - uint256 startingBalance = address(this).balance; - - _expectDepositEvent(1 ether); - weth.deposit{value: 1 ether}(); - - _expectWithdrawalEvent(1 ether); - weth.withdraw(1 ether); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, startingBalance); - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - } - - function testPartialWithdraw() public { - _expectDepositEvent(1 ether); - weth.deposit{value: 1 ether}(); - - uint256 balanceBeforeWithdraw = address(this).balance; - - _expectWithdrawalEvent(0.5 ether); - weth.withdraw(0.5 ether); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + 0.5 ether); - assertEq(weth.balanceOf(address(this)), 0.5 ether); - assertEq(weth.totalSupply(), 0.5 ether); - } - - function testWithdrawToContractWithoutReceiveReverts() public { - address owner = address(new ContractWithoutReceive()); - - vm.deal(owner, 1 ether); - - vm.prank(owner); - _expectDepositEvent(owner, 1 ether); - weth.deposit{value: 1 ether}(); - - assertEq(weth.balanceOf(owner), 1 ether); - - vm.expectRevert(WETH.ETHTransferFailed.selector); - vm.prank(owner); - weth.withdraw(1 ether); - } - - function testFallbackDeposit(uint256 amount) public { - amount = _bound(amount, 0, address(this).balance); - - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - _expectDepositEvent(amount); - SafeTransferLib.safeTransferETH(address(weth), amount); - - assertEq(weth.balanceOf(address(this)), amount); - assertEq(weth.totalSupply(), amount); - } - - function testDeposit(uint256 amount) public { - amount = _bound(amount, 0, address(this).balance); - - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - _expectDepositEvent(amount); - weth.deposit{value: amount}(); - - assertEq(weth.balanceOf(address(this)), amount); - assertEq(weth.totalSupply(), amount); - } - - function testWithdraw(uint256 depositAmount, uint256 withdrawAmount) public { - depositAmount = _bound(depositAmount, 0, address(this).balance); - withdrawAmount = _bound(withdrawAmount, 0, depositAmount); - - _expectDepositEvent(depositAmount); - weth.deposit{value: depositAmount}(); - - uint256 balanceBeforeWithdraw = address(this).balance; - - _expectWithdrawalEvent(withdrawAmount); - weth.withdraw(withdrawAmount); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + withdrawAmount); - assertEq(weth.balanceOf(address(this)), depositAmount - withdrawAmount); - assertEq(weth.totalSupply(), depositAmount - withdrawAmount); - } - - receive() external payable {} -} - -contract WETHInvariants is SoladyTest, InvariantTest { - WETHTester wethTester; - WETH weth; - - function setUp() public { - weth = new WETH(); - wethTester = new WETHTester{value: address(this).balance}(weth); - - _addTargetContract(address(wethTester)); - } - - function invariantTotalSupplyEqualsBalance() public { - assertEq(address(weth).balance, weth.totalSupply()); - } -} - -contract WETHTester { - WETH weth; - - constructor(WETH _weth) payable { - weth = _weth; - } - - function deposit(uint256 amount) public { - weth.deposit{value: amount}(); - } - - function fallbackDeposit(uint256 amount) public { - SafeTransferLib.safeTransferETH(address(weth), amount); - } - - function withdraw(uint256 amount) public { - weth.withdraw(amount); - } - - receive() external payable {} -} diff --git a/lib/solady/test/utils/InvariantTest.sol b/lib/solady/test/utils/InvariantTest.sol deleted file mode 100644 index 261af92..0000000 --- a/lib/solady/test/utils/InvariantTest.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract InvariantTest { - address[] private _targets; - - function targetContracts() public view virtual returns (address[] memory) { - require(_targets.length > 0, "NO_TARGET_CONTRACTS"); - return _targets; - } - - function _addTargetContract(address newTargetContract) internal virtual { - _targets.push(newTargetContract); - } -} diff --git a/lib/solady/test/utils/SoladyTest.sol b/lib/solady/test/utils/SoladyTest.sol deleted file mode 100644 index 4969db3..0000000 --- a/lib/solady/test/utils/SoladyTest.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "./forge-std/Test.sol"; -import "./TestPlus.sol"; - -contract SoladyTest is Test, TestPlus { - /// @dev Alias for `_hem`. - function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256) { - return _hem(x, min, max); - } -} diff --git a/lib/solady/test/utils/TestPlus.sol b/lib/solady/test/utils/TestPlus.sol deleted file mode 100644 index 4443f70..0000000 --- a/lib/solady/test/utils/TestPlus.sol +++ /dev/null @@ -1,351 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract TestPlus { - /// @dev `address(bytes20(uint160(uint256(keccak256("hevm cheat code")))))`. - address private constant _VM_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - - /// @dev Fills the memory with junk, for more robust testing of inline assembly - /// which reads/write to the memory. - function _brutalizeMemory() private view { - // To prevent a solidity 0.8.13 bug. - // See: https://blog.soliditylang.org/2022/06/15/inline-assembly-memory-side-effects-bug - // Basically, we need to access a solidity variable from the assembly to - // tell the compiler that this assembly block is not in isolation. - uint256 zero; - /// @solidity memory-safe-assembly - assembly { - let offset := mload(0x40) // Start the offset at the free memory pointer. - calldatacopy(offset, zero, calldatasize()) - - // Fill the 64 bytes of scratch space with garbage. - mstore(zero, add(caller(), gas())) - mstore(0x20, keccak256(offset, calldatasize())) - mstore(zero, keccak256(zero, 0x40)) - - let r0 := mload(zero) - let r1 := mload(0x20) - - let cSize := add(codesize(), iszero(codesize())) - if iszero(lt(cSize, 32)) { cSize := sub(cSize, and(mload(0x02), 0x1f)) } - let start := mod(mload(0x10), cSize) - let size := mul(sub(cSize, start), gt(cSize, start)) - let times := div(0x7ffff, cSize) - if iszero(lt(times, 128)) { times := 128 } - - // Occasionally offset the offset by a pseudorandom large amount. - // Can't be too large, or we will easily get out-of-gas errors. - offset := add(offset, mul(iszero(and(r1, 0xf)), and(r0, 0xfffff))) - - // Fill the free memory with garbage. - // prettier-ignore - for { let w := not(0) } 1 {} { - mstore(offset, r0) - mstore(add(offset, 0x20), r1) - offset := add(offset, 0x40) - // We use codecopy instead of the identity precompile - // to avoid polluting the `forge test -vvvv` output with tons of junk. - codecopy(offset, start, size) - codecopy(add(offset, size), 0, start) - offset := add(offset, cSize) - times := add(times, w) // `sub(times, 1)`. - if iszero(times) { break } - } - } - } - - /// @dev Fills the memory with junk, for more robust testing of inline assembly - /// which reads/write to the memory. - modifier brutalizeMemory() { - _brutalizeMemory(); - _; - _checkMemory(); - } - - /// @dev Returns a pseudorandom random number from [0 .. 2**256 - 1] (inclusive). - /// For usage in fuzz tests, please ensure that the function has an unnamed uint256 argument. - /// e.g. `testSomething(uint256) public`. - function _random() internal returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // This is the keccak256 of a very long string I randomly mashed on my keyboard. - let sSlot := 0xd715531fe383f818c5f158c342925dcf01b954d24678ada4d07c36af0f20e1ee - let sValue := sload(sSlot) - - mstore(0x20, sValue) - r := keccak256(0x20, 0x40) - - // If the storage is uninitialized, initialize it to the keccak256 of the calldata. - if iszero(sValue) { - sValue := sSlot - let m := mload(0x40) - calldatacopy(m, 0, calldatasize()) - r := keccak256(m, calldatasize()) - } - sstore(sSlot, add(r, 1)) - - // Do some biased sampling for more robust tests. - // prettier-ignore - for {} 1 {} { - let d := byte(0, r) - // With a 1/256 chance, randomly set `r` to any of 0,1,2. - if iszero(d) { - r := and(r, 3) - break - } - // With a 1/2 chance, set `r` to near a random power of 2. - if iszero(and(2, d)) { - // Set `t` either `not(0)` or `xor(sValue, r)`. - let t := xor(not(0), mul(iszero(and(4, d)), not(xor(sValue, r)))) - // Set `r` to `t` shifted left or right by a random multiple of 8. - switch and(8, d) - case 0 { - if iszero(and(16, d)) { t := 1 } - r := add(shl(shl(3, and(byte(3, r), 0x1f)), t), sub(and(r, 7), 3)) - } - default { - if iszero(and(16, d)) { t := shl(255, 1) } - r := add(shr(shl(3, and(byte(3, r), 0x1f)), t), sub(and(r, 7), 3)) - } - // With a 1/2 chance, negate `r`. - if iszero(and(0x20, d)) { r := not(r) } - break - } - // Otherwise, just set `r` to `xor(sValue, r)`. - r := xor(sValue, r) - break - } - } - } - - /// @dev Returns a random signer and its private key. - function _randomSigner() internal returns (address signer, uint256 privateKey) { - uint256 privateKeyMax = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140; - privateKey = _hem(_random(), 1, privateKeyMax); - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, 0xffa18649) // `addr(uint256)`. - mstore(0x20, privateKey) - if iszero(call(gas(), _VM_ADDRESS, 0, 0x1c, 0x24, 0x00, 0x20)) { revert(0, 0) } - signer := mload(0x00) - } - } - - /// @dev Returns a random address. - function _randomAddress() internal returns (address result) { - result = address(uint160(_random())); - } - - /// @dev Returns a random non-zero address. - function _randomNonZeroAddress() internal returns (address result) { - do { - result = address(uint160(_random())); - } while (result == address(0)); - } - - /// @dev Rounds up the free memory pointer to the next word boundary. - /// Sometimes, some Solidity operations cause the free memory pointer to be misaligned. - function _roundUpFreeMemoryPointer() internal pure { - // To prevent a solidity 0.8.13 bug. - // See: https://blog.soliditylang.org/2022/06/15/inline-assembly-memory-side-effects-bug - // Basically, we need to access a solidity variable from the assembly to - // tell the compiler that this assembly block is not in isolation. - uint256 twoWords = 0x40; - /// @solidity memory-safe-assembly - assembly { - mstore(twoWords, and(add(mload(twoWords), 0x1f), not(0x1f))) - } - } - - /// @dev Misaligns the free memory pointer. - /// The free memory pointer has a 1/32 chance to be aligned. - function _misalignFreeMemoryPointer() internal pure { - uint256 twoWords = 0x40; - /// @solidity memory-safe-assembly - assembly { - let m := mload(twoWords) - m := add(m, mul(and(keccak256(0x00, twoWords), 0x1f), iszero(and(m, 0x1f)))) - mstore(twoWords, m) - } - } - - /// @dev Check if the free memory pointer and the zero slot are not contaminated. - /// Useful for cases where these slots are used for temporary storage. - function _checkMemory() internal pure { - bool zeroSlotIsNotZero; - bool freeMemoryPointerOverflowed; - /// @solidity memory-safe-assembly - assembly { - // Write ones to the free memory, to make subsequent checks fail if - // insufficient memory is allocated. - mstore(mload(0x40), not(0)) - // Test at a lower, but reasonable limit for more safety room. - if gt(mload(0x40), 0xffffffff) { freeMemoryPointerOverflowed := 1 } - // Check the value of the zero slot. - zeroSlotIsNotZero := mload(0x60) - } - if (freeMemoryPointerOverflowed) revert("`0x40` overflowed!"); - if (zeroSlotIsNotZero) revert("`0x60` is not zero!"); - } - - /// @dev Check if `s`: - /// - Has sufficient memory allocated. - /// - Is zero right padded (cuz some frontends like Etherscan has issues - /// with decoding non-zero-right-padded strings). - function _checkMemory(bytes memory s) internal pure { - bool notZeroRightPadded; - bool insufficientMalloc; - /// @solidity memory-safe-assembly - assembly { - // Write ones to the free memory, to make subsequent checks fail if - // insufficient memory is allocated. - mstore(mload(0x40), not(0)) - let length := mload(s) - let lastWord := mload(add(add(s, 0x20), and(length, not(0x1f)))) - let remainder := and(length, 0x1f) - if remainder { if shl(mul(8, remainder), lastWord) { notZeroRightPadded := 1 } } - // Check if the memory allocated is sufficient. - if length { if gt(add(add(s, 0x20), length), mload(0x40)) { insufficientMalloc := 1 } } - } - if (notZeroRightPadded) revert("Not zero right padded!"); - if (insufficientMalloc) revert("Insufficient memory allocation!"); - _checkMemory(); - } - - /// @dev For checking the memory allocation for string `s`. - function _checkMemory(string memory s) internal pure { - _checkMemory(bytes(s)); - } - - /// @dev Adapted from `bound`: - /// https://github.com/foundry-rs/forge-std/blob/ff4bf7db008d096ea5a657f2c20516182252a3ed/src/StdUtils.sol#L10 - /// Differentially fuzzed tested against the original implementation. - function _hem(uint256 x, uint256 min, uint256 max) - internal - pure - virtual - returns (uint256 result) - { - require(min <= max, "Max is less than min."); - - /// @solidity memory-safe-assembly - assembly { - // prettier-ignore - for {} 1 {} { - // If `x` is between `min` and `max`, return `x` directly. - // This is to ensure that dictionary values - // do not get shifted if the min is nonzero. - // More info: https://github.com/foundry-rs/forge-std/issues/188 - if iszero(or(lt(x, min), gt(x, max))) { - result := x - break - } - - let size := add(sub(max, min), 1) - if and(iszero(gt(x, 3)), gt(size, x)) { - result := add(min, x) - break - } - - let w := not(0) - if and(iszero(lt(x, sub(0, 4))), gt(size, sub(w, x))) { - result := sub(max, sub(w, x)) - break - } - - // Otherwise, wrap x into the range [min, max], - // i.e. the range is inclusive. - if iszero(lt(x, max)) { - let d := sub(x, max) - let r := mod(d, size) - if iszero(r) { - result := max - break - } - result := add(add(min, r), w) - break - } - let d := sub(min, x) - let r := mod(d, size) - if iszero(r) { - result := min - break - } - result := add(sub(max, r), 1) - break - } - } - } - - /// @dev Deploys a contract via 0age's immutable create 2 factory for testing. - function _safeCreate2(uint256 payableAmount, bytes32 salt, bytes memory initializationCode) - internal - returns (address deploymentAddress) - { - // Canonical address of 0age's immutable create 2 factory. - address c2f = 0x0000000000FFe8B47B3e2130213B802212439497; - uint256 c2fCodeLength; - /// @solidity memory-safe-assembly - assembly { - c2fCodeLength := extcodesize(c2f) - } - if (c2fCodeLength == 0) { - bytes memory ic2fBytecode = - hex"60806040526004361061003f5760003560e01c806308508b8f1461004457806364e030871461009857806385cf97ab14610138578063a49a7c90146101bc575b600080fd5b34801561005057600080fd5b506100846004803603602081101561006757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff166101ec565b604080519115158252519081900360200190f35b61010f600480360360408110156100ae57600080fd5b813591908101906040810160208201356401000000008111156100d057600080fd5b8201836020820111156100e257600080fd5b8035906020019184600183028401116401000000008311171561010457600080fd5b509092509050610217565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b34801561014457600080fd5b5061010f6004803603604081101561015b57600080fd5b8135919081019060408101602082013564010000000081111561017d57600080fd5b82018360208201111561018f57600080fd5b803590602001918460018302840111640100000000831117156101b157600080fd5b509092509050610592565b3480156101c857600080fd5b5061010f600480360360408110156101df57600080fd5b508035906020013561069e565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205460ff1690565b600083606081901c33148061024c57507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008116155b6102a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260458152602001806107746045913960600191505060405180910390fd5b606084848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920182905250604051855195965090943094508b93508692506020918201918291908401908083835b6020831061033557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016102f8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018019909216911617905260408051929094018281037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00183528085528251928201929092207fff000000000000000000000000000000000000000000000000000000000000008383015260609890981b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602183015260358201969096526055808201979097528251808203909701875260750182525084519484019490942073ffffffffffffffffffffffffffffffffffffffff81166000908152938490529390922054929350505060ff16156104a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180610735603f913960400191505060405180910390fd5b81602001825188818334f5955050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161461053a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260468152602001806107b96046913960600191505060405180910390fd5b50505073ffffffffffffffffffffffffffffffffffffffff8116600090815260208190526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559392505050565b6000308484846040516020018083838082843760408051919093018181037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001825280845281516020928301207fff000000000000000000000000000000000000000000000000000000000000008383015260609990991b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166021820152603581019790975260558088019890985282518088039098018852607590960182525085519585019590952073ffffffffffffffffffffffffffffffffffffffff81166000908152948590529490932054939450505060ff909116159050610697575060005b9392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091523060601b6021830152603582018590526055808301859052835180840390910181526075909201835281519181019190912073ffffffffffffffffffffffffffffffffffffffff81166000908152918290529190205460ff161561072e575060005b9291505056fe496e76616c696420636f6e7472616374206372656174696f6e202d20636f6e74726163742068617320616c7265616479206265656e206465706c6f7965642e496e76616c69642073616c74202d206669727374203230206279746573206f66207468652073616c74206d757374206d617463682063616c6c696e6720616464726573732e4661696c656420746f206465706c6f7920636f6e7472616374207573696e672070726f76696465642073616c7420616e6420696e697469616c697a6174696f6e20636f64652ea265627a7a723058202bdc55310d97c4088f18acf04253db593f0914059f0c781a9df3624dcef0d1cf64736f6c634300050a0032"; - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - mstore(m, 0xb4d6c782) // `etch(address,bytes)`. - mstore(add(m, 0x20), c2f) - mstore(add(m, 0x40), 0x40) - let n := mload(ic2fBytecode) - mstore(add(m, 0x60), n) - for { let i := 0 } lt(i, n) { i := add(0x20, i) } { - mstore(add(add(m, 0x80), i), mload(add(add(ic2fBytecode, 0x20), i))) - } - if iszero(call(gas(), _VM_ADDRESS, 0, add(m, 0x1c), add(n, 0x64), 0x00, 0x00)) { - revert(0, 0) - } - } - } - /// @solidity memory-safe-assembly - assembly { - let m := mload(0x40) - let n := mload(initializationCode) - mstore(m, 0x64e03087) // `safeCreate2(bytes32,bytes)`. - mstore(add(m, 0x20), salt) - mstore(add(m, 0x40), 0x40) - mstore(add(m, 0x60), n) - // prettier-ignore - for { let i := 0 } lt(i, n) { i := add(i, 0x20) } { - mstore(add(add(m, 0x80), i), mload(add(add(initializationCode, 0x20), i))) - } - if iszero(call(gas(), c2f, payableAmount, add(m, 0x1c), add(n, 0x64), m, 0x20)) { - returndatacopy(m, m, returndatasize()) - revert(m, returndatasize()) - } - deploymentAddress := mload(m) - } - } - - /// @dev Deploys a contract via 0age's immutable create 2 factory for testing. - function _safeCreate2(bytes32 salt, bytes memory initializationCode) - internal - returns (address deploymentAddress) - { - deploymentAddress = _safeCreate2(0, salt, initializationCode); - } - - /// @dev This function will make forge's gas output display the approximate codesize of - /// the test contract as the amount of gas burnt. Useful for quick guess checking if - /// certain optimizations actually compiles to similar bytecode. - function test__codesize() external view { - /// @solidity memory-safe-assembly - assembly { - // If the caller is the contract itself (i.e. recursive call), burn all the gas. - if eq(caller(), address()) { invalid() } - mstore(0x00, 0xf09ff470) // Store the function selector of `test__codesize()`. - pop(staticcall(codesize(), address(), 0x1c, 0x04, 0x00, 0x00)) - } - } -} diff --git a/lib/solady/test/utils/forge-std/Script.sol b/lib/solady/test/utils/forge-std/Script.sol deleted file mode 100644 index 724dd85..0000000 --- a/lib/solady/test/utils/forge-std/Script.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.6.0 <0.9.0; - -import "./Vm.sol"; -import "./console.sol"; - -abstract contract Script { - bool public IS_SCRIPT = true; - address private constant VM_ADDRESS = - address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); - - Vm public constant vm = Vm(VM_ADDRESS); -} diff --git a/lib/solady/test/utils/forge-std/Test.sol b/lib/solady/test/utils/forge-std/Test.sol deleted file mode 100644 index bf92f66..0000000 --- a/lib/solady/test/utils/forge-std/Test.sol +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.6.0 <0.9.0; - -import "./Script.sol"; -import "ds-test/test.sol"; - -// Wrappers around Cheatcodes to avoid footguns -abstract contract Test is DSTest, Script { - uint256 internal constant UINT256_MAX = - 115792089237316195423570985008687907853269984665640564039457584007913129639935; - - /*////////////////////////////////////////////////////////////////////////// - STD-LOGS - //////////////////////////////////////////////////////////////////////////*/ - - event log_array(uint256[] val); - event log_array(int256[] val); - event log_array(address[] val); - event log_named_array(string key, uint256[] val); - event log_named_array(string key, int256[] val); - event log_named_array(string key, address[] val); - - /*////////////////////////////////////////////////////////////////////////// - STD-ASSERTIONS - //////////////////////////////////////////////////////////////////////////*/ - - function fail(string memory err) internal virtual { - emit log_named_string("Error", err); - fail(); - } - - function assertFalse(bool data) internal virtual { - assertTrue(!data); - } - - function assertFalse(bool data, string memory err) internal virtual { - assertTrue(!data, err); - } - - function assertEq(bool a, bool b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [bool]"); - emit log_named_string(" Expected", b ? "true" : "false"); - emit log_named_string(" Actual", a ? "true" : "false"); - fail(); - } - } - - function assertEq(bool a, bool b, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - - function assertEq(bytes memory a, bytes memory b) internal { - assertEq0(a, b); - } - - function assertEq(bytes memory a, bytes memory b, string memory err) internal { - assertEq0(a, b, err); - } - - function assertEq(uint256[] memory a, uint256[] memory b) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log("Error: a == b not satisfied [uint[]]"); - emit log_named_array(" Expected", b); - emit log_named_array(" Actual", a); - fail(); - } - } - - function assertEq(int256[] memory a, int256[] memory b) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log("Error: a == b not satisfied [int[]]"); - emit log_named_array(" Expected", b); - emit log_named_array(" Actual", a); - fail(); - } - } - - function assertEq(address[] memory a, address[] memory b) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log("Error: a == b not satisfied [address[]]"); - emit log_named_array(" Expected", b); - emit log_named_array(" Actual", a); - fail(); - } - } - - function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - - function assertEq(int256[] memory a, int256[] memory b, string memory err) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - - function assertEq(address[] memory a, address[] memory b, string memory err) internal { - bool inputsEq; - /// @solidity memory-safe-assembly - assembly { - inputsEq := - eq(keccak256(a, shl(5, add(mload(a), 1))), keccak256(b, shl(5, add(mload(b), 1)))) - } - if (!inputsEq) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } -} - -/*////////////////////////////////////////////////////////////////////////// - STD-ERRORS -//////////////////////////////////////////////////////////////////////////*/ - -library stdError { - bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01); - bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11); - bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12); - bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21); - bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22); - bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31); - bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32); - bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41); - bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51); - // DEPRECATED: Use Vm's `expectRevert` without any arguments instead - bytes public constant lowLevelError = bytes(""); // `0x` -} diff --git a/lib/solady/test/utils/forge-std/Vm.sol b/lib/solady/test/utils/forge-std/Vm.sol deleted file mode 100644 index 9d2d2f6..0000000 --- a/lib/solady/test/utils/forge-std/Vm.sol +++ /dev/null @@ -1,525 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.22 <0.9.0; - -interface VmSafe { - struct Log { - bytes32[] topics; - bytes data; - address emitter; - } - - struct Rpc { - string key; - string url; - } - - struct FsMetadata { - bool isDir; - bool isSymlink; - uint256 length; - bool readOnly; - uint256 modified; - uint256 accessed; - uint256 created; - } - - // Loads a storage slot from an address - function load(address target, bytes32 slot) external view returns (bytes32 data); - // Signs data - function sign(uint256 privateKey, bytes32 digest) - external - pure - returns (uint8 v, bytes32 r, bytes32 s); - // Gets the address for a given private key - function addr(uint256 privateKey) external pure returns (address keyAddr); - // Gets the nonce of an account - function getNonce(address account) external view returns (uint64 nonce); - // Performs a foreign function call via the terminal - function ffi(string[] calldata commandInput) external returns (bytes memory result); - // Sets environment variables - function setEnv(string calldata name, string calldata value) external; - // Reads environment variables, (name) => (value) - function envBool(string calldata name) external view returns (bool value); - function envUint(string calldata name) external view returns (uint256 value); - function envInt(string calldata name) external view returns (int256 value); - function envAddress(string calldata name) external view returns (address value); - function envBytes32(string calldata name) external view returns (bytes32 value); - function envString(string calldata name) external view returns (string memory value); - function envBytes(string calldata name) external view returns (bytes memory value); - // Reads environment variables as arrays - function envBool(string calldata name, string calldata delim) - external - view - returns (bool[] memory value); - function envUint(string calldata name, string calldata delim) - external - view - returns (uint256[] memory value); - function envInt(string calldata name, string calldata delim) - external - view - returns (int256[] memory value); - function envAddress(string calldata name, string calldata delim) - external - view - returns (address[] memory value); - function envBytes32(string calldata name, string calldata delim) - external - view - returns (bytes32[] memory value); - function envString(string calldata name, string calldata delim) - external - view - returns (string[] memory value); - function envBytes(string calldata name, string calldata delim) - external - view - returns (bytes[] memory value); - // Read environment variables with default value - function envOr(string calldata name, bool defaultValue) external returns (bool value); - function envOr(string calldata name, uint256 defaultValue) external returns (uint256 value); - function envOr(string calldata name, int256 defaultValue) external returns (int256 value); - function envOr(string calldata name, address defaultValue) external returns (address value); - function envOr(string calldata name, bytes32 defaultValue) external returns (bytes32 value); - function envOr(string calldata name, string calldata defaultValue) - external - returns (string memory value); - function envOr(string calldata name, bytes calldata defaultValue) - external - returns (bytes memory value); - // Read environment variables as arrays with default value - function envOr(string calldata name, string calldata delim, bool[] calldata defaultValue) - external - returns (bool[] memory value); - function envOr(string calldata name, string calldata delim, uint256[] calldata defaultValue) - external - returns (uint256[] memory value); - function envOr(string calldata name, string calldata delim, int256[] calldata defaultValue) - external - returns (int256[] memory value); - function envOr(string calldata name, string calldata delim, address[] calldata defaultValue) - external - returns (address[] memory value); - function envOr(string calldata name, string calldata delim, bytes32[] calldata defaultValue) - external - returns (bytes32[] memory value); - function envOr(string calldata name, string calldata delim, string[] calldata defaultValue) - external - returns (string[] memory value); - function envOr(string calldata name, string calldata delim, bytes[] calldata defaultValue) - external - returns (bytes[] memory value); - // Records all storage reads and writes - function record() external; - // Gets all accessed reads and write slot from a recording session, for a given address - function accesses(address target) - external - returns (bytes32[] memory readSlots, bytes32[] memory writeSlots); - // Gets the _creation_ bytecode from an artifact file. Takes in the relative path to the json file - function getCode(string calldata artifactPath) - external - view - returns (bytes memory creationBytecode); - // Gets the _deployed_ bytecode from an artifact file. Takes in the relative path to the json file - function getDeployedCode(string calldata artifactPath) - external - view - returns (bytes memory runtimeBytecode); - // Labels an address in call traces - function label(address account, string calldata newLabel) external; - // Using the address that calls the test contract, has the next call (at this call depth only) create a transaction that can later be signed and sent onchain - function broadcast() external; - // Has the next call (at this call depth only) create a transaction with the address provided as the sender that can later be signed and sent onchain - function broadcast(address signer) external; - // Has the next call (at this call depth only) create a transaction with the private key provided as the sender that can later be signed and sent onchain - function broadcast(uint256 privateKey) external; - // Using the address that calls the test contract, has all subsequent calls (at this call depth only) create transactions that can later be signed and sent onchain - function startBroadcast() external; - // Has all subsequent calls (at this call depth only) create transactions with the address provided that can later be signed and sent onchain - function startBroadcast(address signer) external; - // Has all subsequent calls (at this call depth only) create transactions with the private key provided that can later be signed and sent onchain - function startBroadcast(uint256 privateKey) external; - // Stops collecting onchain transactions - function stopBroadcast() external; - // Reads the entire content of file to string - function readFile(string calldata path) external view returns (string memory data); - // Reads the entire content of file as binary. Path is relative to the project root. - function readFileBinary(string calldata path) external view returns (bytes memory data); - // Get the path of the current project root - function projectRoot() external view returns (string memory path); - // Get the metadata for a file/directory - function fsMetadata(string calldata fileOrDir) external returns (FsMetadata memory metadata); - // Reads next line of file to string - function readLine(string calldata path) external view returns (string memory line); - // Writes data to file, creating a file if it does not exist, and entirely replacing its contents if it does. - function writeFile(string calldata path, string calldata data) external; - // Writes binary data to a file, creating a file if it does not exist, and entirely replacing its contents if it does. - // Path is relative to the project root. - function writeFileBinary(string calldata path, bytes calldata data) external; - // Writes line to file, creating a file if it does not exist. - function writeLine(string calldata path, string calldata data) external; - // Closes file for reading, resetting the offset and allowing to read it from beginning with readLine. - function closeFile(string calldata path) external; - // Removes file. This cheatcode will revert in the following situations, but is not limited to just these cases: - // - Path points to a directory. - // - The file doesn't exist. - // - The user lacks permissions to remove the file. - function removeFile(string calldata path) external; - // Convert values to a string - function toString(address value) external pure returns (string memory stringifiedValue); - function toString(bytes calldata value) - external - pure - returns (string memory stringifiedValue); - function toString(bytes32 value) external pure returns (string memory stringifiedValue); - function toString(bool value) external pure returns (string memory stringifiedValue); - function toString(uint256 value) external pure returns (string memory stringifiedValue); - function toString(int256 value) external pure returns (string memory stringifiedValue); - // Convert values from a string - function parseBytes(string calldata stringifiedValue) - external - pure - returns (bytes memory parsedValue); - function parseAddress(string calldata stringifiedValue) - external - pure - returns (address parsedValue); - function parseUint(string calldata stringifiedValue) - external - pure - returns (uint256 parsedValue); - function parseInt(string calldata stringifiedValue) - external - pure - returns (int256 parsedValue); - function parseBytes32(string calldata stringifiedValue) - external - pure - returns (bytes32 parsedValue); - function parseBool(string calldata stringifiedValue) external pure returns (bool parsedValue); - // Record all the transaction logs - function recordLogs() external; - // Gets all the recorded logs - function getRecordedLogs() external returns (Log[] memory logs); - // Derive a private key from a provided mnenomic string (or mnenomic file path) at the derivation path m/44'/60'/0'/0/{index} - function deriveKey(string calldata mnemonic, uint32 index) - external - pure - returns (uint256 privateKey); - // Derive a private key from a provided mnenomic string (or mnenomic file path) at {derivationPath}{index} - function deriveKey(string calldata mnemonic, string calldata derivationPath, uint32 index) - external - pure - returns (uint256 privateKey); - // Adds a private key to the local forge wallet and returns the address - function rememberKey(uint256 privateKey) external returns (address keyAddr); - // - // parseJson - // - // ---- - // In case the returned value is a JSON object, it's encoded as a ABI-encoded tuple. As JSON objects - // don't have the notion of ordered, but tuples do, they JSON object is encoded with it's fields ordered in - // ALPHABETICAL order. That means that in order to successfully decode the tuple, we need to define a tuple that - // encodes the fields in the same order, which is alphabetical. In the case of Solidity structs, they are encoded - // as tuples, with the attributes in the order in which they are defined. - // For example: json = { 'a': 1, 'b': 0xa4tb......3xs} - // a: uint256 - // b: address - // To decode that json, we need to define a struct or a tuple as follows: - // struct json = { uint256 a; address b; } - // If we defined a json struct with the opposite order, meaning placing the address b first, it would try to - // decode the tuple in that order, and thus fail. - // ---- - // Given a string of JSON, return it as ABI-encoded - function parseJson(string calldata json, string calldata key) - external - pure - returns (bytes memory abiEncodedData); - function parseJson(string calldata json) external pure returns (bytes memory abiEncodedData); - - // The following parseJson cheatcodes will do type coercion, for the type that they indicate. - // For example, parseJsonUint will coerce all values to a uint256. That includes stringified numbers '12' - // and hex numbers '0xEF'. - // Type coercion works ONLY for discrete values or arrays. That means that the key must return a value or array, not - // a JSON object. - function parseJsonUint(string calldata, string calldata) external returns (uint256); - function parseJsonUintArray(string calldata, string calldata) - external - returns (uint256[] memory); - function parseJsonInt(string calldata, string calldata) external returns (int256); - function parseJsonIntArray(string calldata, string calldata) - external - returns (int256[] memory); - function parseJsonBool(string calldata, string calldata) external returns (bool); - function parseJsonBoolArray(string calldata, string calldata) - external - returns (bool[] memory); - function parseJsonAddress(string calldata, string calldata) external returns (address); - function parseJsonAddressArray(string calldata, string calldata) - external - returns (address[] memory); - function parseJsonString(string calldata, string calldata) external returns (string memory); - function parseJsonStringArray(string calldata, string calldata) - external - returns (string[] memory); - function parseJsonBytes(string calldata, string calldata) external returns (bytes memory); - function parseJsonBytesArray(string calldata, string calldata) - external - returns (bytes[] memory); - function parseJsonBytes32(string calldata, string calldata) external returns (bytes32); - function parseJsonBytes32Array(string calldata, string calldata) - external - returns (bytes32[] memory); - - // Serialize a key and value to a JSON object stored in-memory that can be later written to a file - // It returns the stringified version of the specific JSON file up to that moment. - function serializeBool(string calldata objectKey, string calldata valueKey, bool value) - external - returns (string memory json); - function serializeUint(string calldata objectKey, string calldata valueKey, uint256 value) - external - returns (string memory json); - function serializeInt(string calldata objectKey, string calldata valueKey, int256 value) - external - returns (string memory json); - function serializeAddress(string calldata objectKey, string calldata valueKey, address value) - external - returns (string memory json); - function serializeBytes32(string calldata objectKey, string calldata valueKey, bytes32 value) - external - returns (string memory json); - function serializeString( - string calldata objectKey, - string calldata valueKey, - string calldata value - ) external returns (string memory json); - function serializeBytes( - string calldata objectKey, - string calldata valueKey, - bytes calldata value - ) external returns (string memory json); - - function serializeBool( - string calldata objectKey, - string calldata valueKey, - bool[] calldata values - ) external returns (string memory json); - function serializeUint( - string calldata objectKey, - string calldata valueKey, - uint256[] calldata values - ) external returns (string memory json); - function serializeInt( - string calldata objectKey, - string calldata valueKey, - int256[] calldata values - ) external returns (string memory json); - function serializeAddress( - string calldata objectKey, - string calldata valueKey, - address[] calldata values - ) external returns (string memory json); - function serializeBytes32( - string calldata objectKey, - string calldata valueKey, - bytes32[] calldata values - ) external returns (string memory json); - function serializeString( - string calldata objectKey, - string calldata valueKey, - string[] calldata values - ) external returns (string memory json); - function serializeBytes( - string calldata objectKey, - string calldata valueKey, - bytes[] calldata values - ) external returns (string memory json); - - // - // writeJson - // - // ---- - // Write a serialized JSON object to a file. If the file exists, it will be overwritten. - // Let's assume we want to write the following JSON to a file: - // - // { "boolean": true, "number": 342, "object": { "title": "finally json serialization" } } - // - // ``` - // string memory json1 = "some key"; - // vm.serializeBool(json1, "boolean", true); - // vm.serializeBool(json1, "number", uint256(342)); - // json2 = "some other key"; - // string memory output = vm.serializeString(json2, "title", "finally json serialization"); - // string memory finalJson = vm.serialize(json1, "object", output); - // vm.writeJson(finalJson, "./output/example.json"); - // ``` - // The critical insight is that every invocation of serialization will return the stringified version of the JSON - // up to that point. That means we can construct arbitrary JSON objects and then use the return stringified version - // to serialize them as values to another JSON object. - // - // json1 and json2 are simply keys used by the backend to keep track of the objects. So vm.serializeJson(json1,..) - // will find the object in-memory that is keyed by "some key". - function writeJson(string calldata json, string calldata path) external; - // Write a serialized JSON object to an **existing** JSON file, replacing a value with key = - // This is useful to replace a specific value of a JSON file, without having to parse the entire thing - function writeJson(string calldata json, string calldata path, string calldata valueKey) - external; - // Returns the RPC url for the given alias - function rpcUrl(string calldata rpcAlias) external view returns (string memory json); - // Returns all rpc urls and their aliases `[alias, url][]` - function rpcUrls() external view returns (string[2][] memory urls); - // Returns all rpc urls and their aliases as structs. - function rpcUrlStructs() external view returns (Rpc[] memory urls); - // If the condition is false, discard this run's fuzz inputs and generate new ones. - function assume(bool condition) external pure; - // Pauses gas metering (i.e. gas usage is not counted). Noop if already paused. - function pauseGasMetering() external; - // Resumes gas metering (i.e. gas usage is counted again). Noop if already on. - function resumeGasMetering() external; -} - -interface Vm is VmSafe { - // Sets block.timestamp - function warp(uint256 newTimestamp) external; - // Sets block.height - function roll(uint256 newHeight) external; - // Sets block.basefee - function fee(uint256 newBasefee) external; - // Sets block.difficulty - function difficulty(uint256 newDifficulty) external; - // Sets block.chainid - function chainId(uint256 newChainId) external; - // Stores a value to an address' storage slot. - function store(address target, bytes32 slot, bytes32 value) external; - // Sets the nonce of an account; must be higher than the current nonce of the account - function setNonce(address account, uint64 newNonce) external; - // Sets the *next* call's msg.sender to be the input address - function prank(address msgSender) external; - // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called - function startPrank(address msgSender) external; - // Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input - function prank(address msgSender, address txOrigin) external; - // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input - function startPrank(address msgSender, address txOrigin) external; - // Resets subsequent calls' msg.sender to be `address(this)` - function stopPrank() external; - // Sets an address' balance - function deal(address account, uint256 newBalance) external; - // Sets an address' code - function etch(address target, bytes calldata newRuntimeBytecode) external; - // Expects an error on next call - function expectRevert(bytes calldata revertData) external; - function expectRevert(bytes4 revertData) external; - function expectRevert() external; - - // Prepare an expected log with all four checks enabled. - // Call this function, then emit an event, then call a function. Internally after the call, we check if - // logs were emitted in the expected order with the expected topics and data. - // Second form also checks supplied address against emitting contract. - function expectEmit() external; - function expectEmit(address) external; - - // Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). - // Call this function, then emit an event, then call a function. Internally after the call, we check if - // logs were emitted in the expected order with the expected topics and data (as specified by the booleans). - // Second form also checks supplied address against emitting contract. - function expectEmit(bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData) - external; - function expectEmit( - bool checkTopic1, - bool checkTopic2, - bool checkTopic3, - bool checkData, - address emitter - ) external; - - // Mocks a call to an address, returning specified data. - // Calldata can either be strict or a partial match, e.g. if you only - // pass a Solidity selector to the expected calldata, then the entire Solidity - // function will be mocked. - function mockCall(address callee, bytes calldata data, bytes calldata returnData) external; - // Mocks a call to an address with a specific msg.value, returning specified data. - // Calldata match takes precedence over msg.value in case of ambiguity. - function mockCall( - address callee, - uint256 msgValue, - bytes calldata data, - bytes calldata returnData - ) external; - // Clears all mocked calls - function clearMockedCalls() external; - // Expects a call to an address with the specified calldata. - // Calldata can either be a strict or a partial match - function expectCall(address callee, bytes calldata data) external; - // Expects a call to an address with the specified msg.value and calldata - function expectCall(address callee, uint256 msgValue, bytes calldata data) external; - // Expect a call to an address with the specified msg.value, gas, and calldata. - function expectCall(address callee, uint256 msgValue, uint64 gas, bytes calldata data) - external; - // Expect a call to an address with the specified msg.value and calldata, and a *minimum* amount of gas. - function expectCallMinGas(address callee, uint256 msgValue, uint64 minGas, bytes calldata data) - external; - // Sets block.coinbase - function coinbase(address newCoinbase) external; - // Snapshot the current state of the evm. - // Returns the id of the snapshot that was created. - // To revert a snapshot use `revertTo` - function snapshot() external returns (uint256 snapshotId); - // Revert the state of the EVM to a previous snapshot - // Takes the snapshot id to revert to. - // This deletes the snapshot and all snapshots taken after the given snapshot id. - function revertTo(uint256 snapshotId) external returns (bool success); - // Creates a new fork with the given endpoint and block and returns the identifier of the fork - function createFork(string calldata urlOrAlias, uint256 blockNumber) - external - returns (uint256 forkId); - // Creates a new fork with the given endpoint and the _latest_ block and returns the identifier of the fork - function createFork(string calldata urlOrAlias) external returns (uint256 forkId); - // Creates a new fork with the given endpoint and at the block the given transaction was mined in, replays all transaction mined in the block before the transaction, - // and returns the identifier of the fork - function createFork(string calldata urlOrAlias, bytes32 txHash) - external - returns (uint256 forkId); - // Creates _and_ also selects a new fork with the given endpoint and block and returns the identifier of the fork - function createSelectFork(string calldata urlOrAlias, uint256 blockNumber) - external - returns (uint256 forkId); - // Creates _and_ also selects new fork with the given endpoint and at the block the given transaction was mined in, replays all transaction mined in the block before - // the transaction, returns the identifier of the fork - function createSelectFork(string calldata urlOrAlias, bytes32 txHash) - external - returns (uint256 forkId); - // Creates _and_ also selects a new fork with the given endpoint and the latest block and returns the identifier of the fork - function createSelectFork(string calldata urlOrAlias) external returns (uint256 forkId); - // Takes a fork identifier created by `createFork` and sets the corresponding forked state as active. - function selectFork(uint256 forkId) external; - /// Returns the identifier of the currently active fork. Reverts if no fork is currently active. - function activeFork() external view returns (uint256 forkId); - // Updates the currently active fork to given block number - // This is similar to `roll` but for the currently active fork - function rollFork(uint256 blockNumber) external; - // Updates the currently active fork to given transaction - // this will `rollFork` with the number of the block the transaction was mined in and replays all transaction mined before it in the block - function rollFork(bytes32 txHash) external; - // Updates the given fork to given block number - function rollFork(uint256 forkId, uint256 blockNumber) external; - // Updates the given fork to block number of the given transaction and replays all transaction mined before it in the block - function rollFork(uint256 forkId, bytes32 txHash) external; - // Marks that the account(s) should use persistent storage across fork swaps in a multifork setup - // Meaning, changes made to the state of this account will be kept when switching forks - function makePersistent(address account) external; - function makePersistent(address account0, address account1) external; - function makePersistent(address account0, address account1, address account2) external; - function makePersistent(address[] calldata accounts) external; - // Revokes persistent status from the address, previously added via `makePersistent` - function revokePersistent(address account) external; - function revokePersistent(address[] calldata accounts) external; - // Returns true if the account is marked as persistent - function isPersistent(address account) external view returns (bool persistent); - // In forking mode, explicitly grant the given address cheatcode access - function allowCheatcodes(address account) external; - // Fetches the given transaction from the active fork and executes it on the current state - function transact(bytes32 txHash) external; - // Fetches the given transaction from the given fork and executes it on the current state - function transact(uint256 forkId, bytes32 txHash) external; -} diff --git a/lib/solady/test/utils/forge-std/console.sol b/lib/solady/test/utils/forge-std/console.sol deleted file mode 100644 index 49850e1..0000000 --- a/lib/solady/test/utils/forge-std/console.sol +++ /dev/null @@ -1,1581 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.22 <0.9.0; - -library console { - address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - - function _sendLogPayload(bytes memory payload) private view { - uint256 payloadLength = payload.length; - address consoleAddress = CONSOLE_ADDRESS; - /// @solidity memory-safe-assembly - assembly { - let payloadStart := add(payload, 32) - let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) - } - } - - function log() internal view { - _sendLogPayload(abi.encodeWithSignature("log()")); - } - - function logInt(int256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); - } - - function logUint(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function logString(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function logBool(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function logAddress(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function logBytes(bytes memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); - } - - function logBytes1(bytes1 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); - } - - function logBytes2(bytes2 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); - } - - function logBytes3(bytes3 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); - } - - function logBytes4(bytes4 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); - } - - function logBytes5(bytes5 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); - } - - function logBytes6(bytes6 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); - } - - function logBytes7(bytes7 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); - } - - function logBytes8(bytes8 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); - } - - function logBytes9(bytes9 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); - } - - function logBytes10(bytes10 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); - } - - function logBytes11(bytes11 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); - } - - function logBytes12(bytes12 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); - } - - function logBytes13(bytes13 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); - } - - function logBytes14(bytes14 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); - } - - function logBytes15(bytes15 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); - } - - function logBytes16(bytes16 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); - } - - function logBytes17(bytes17 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); - } - - function logBytes18(bytes18 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); - } - - function logBytes19(bytes19 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); - } - - function logBytes20(bytes20 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); - } - - function logBytes21(bytes21 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); - } - - function logBytes22(bytes22 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); - } - - function logBytes23(bytes23 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); - } - - function logBytes24(bytes24 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); - } - - function logBytes25(bytes25 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); - } - - function logBytes26(bytes26 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); - } - - function logBytes27(bytes27 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); - } - - function logBytes28(bytes28 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); - } - - function logBytes29(bytes29 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); - } - - function logBytes30(bytes30 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); - } - - function logBytes31(bytes31 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); - } - - function logBytes32(bytes32 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); - } - - function log(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function log(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function log(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function log(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function log(uint256 p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); - } - - function log(uint256 p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); - } - - function log(uint256 p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); - } - - function log(uint256 p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); - } - - function log(string memory p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); - } - - function log(string memory p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); - } - - function log(string memory p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); - } - - function log(string memory p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); - } - - function log(bool p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); - } - - function log(bool p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); - } - - function log(bool p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); - } - - function log(bool p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); - } - - function log(address p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); - } - - function log(address p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); - } - - function log(address p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); - } - - function log(address p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); - } - - function log(uint256 p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); - } - - function log(string memory p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); - } - - function log(string memory p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); - } - - function log(string memory p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); - } - - function log(string memory p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); - } - - function log(bool p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); - } - - function log(bool p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); - } - - function log(bool p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); - } - - function log(bool p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); - } - - function log(bool p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); - } - - function log(bool p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); - } - - function log(bool p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); - } - - function log(bool p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); - } - - function log(address p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); - } - - function log(address p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); - } - - function log(address p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); - } - - function log(address p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); - } - - function log(address p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); - } - - function log(address p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); - } - - function log(address p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); - } - - function log(address p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); - } - - function log(address p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); - } - - function log(address p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); - } - - function log(address p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); - } - - function log(address p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, string memory p3) - internal - view - { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3) - ); - } - - function log(string memory p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3) - ); - } - - function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3) - ); - } - - function log(address p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3) - ); - } - - function log(address p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, address p2, bool p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3) - ); - } - - function log(address p0, address p1, address p2, address p3) internal view { - _sendLogPayload( - abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3) - ); - } -} diff --git a/lib/solady/test/utils/mocks/MockCd.sol b/lib/solady/test/utils/mocks/MockCd.sol deleted file mode 100644 index 8dbb4d7..0000000 --- a/lib/solady/test/utils/mocks/MockCd.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {LibZip} from "../../../src/utils/LibZip.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockCd { - error Hash(bytes32 h); - - bytes32 public dataHash; - bytes32 public numbersHash; - uint256 public lastCallvalue; - address public lastCaller; - - function storeDataHash(bytes calldata data, bool success) - external - payable - returns (bytes32 result) - { - result = keccak256(data); - if (!success) { - revert Hash(result); - } - dataHash = result; - lastCallvalue = msg.value; - lastCaller = msg.sender; - } - - function storeNumbersHash(uint256[] calldata numbers, bool success) - external - payable - returns (bytes32 result) - { - result = keccak256(abi.encode(numbers)); - if (!success) { - revert Hash(result); - } - numbersHash = result; - lastCallvalue = msg.value; - lastCaller = msg.sender; - } - - receive() external payable { - LibZip.cdFallback(); - } - - fallback() external payable { - LibZip.cdFallback(); - } -} - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockCdFallbackDecompressor { - receive() external payable { - _interceptCdFallback(); - LibZip.cdFallback(); - } - - fallback() external payable { - _interceptCdFallback(); - LibZip.cdFallback(); - } - - function _interceptCdFallback() internal { - assembly { - if iszero(calldatasize()) { - mstore(0x00, keccak256(0x00, 0x00)) - return(0x00, 0x20) - } - if sload(0) { - calldatacopy(0x00, 0x00, calldatasize()) - mstore(0x00, keccak256(0x00, calldatasize())) - return(0x00, 0x20) - } - sstore(0, 1) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockEIP712.sol b/lib/solady/test/utils/mocks/MockEIP712.sol deleted file mode 100644 index 5bf1c6b..0000000 --- a/lib/solady/test/utils/mocks/MockEIP712.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../../src/utils/EIP712.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockEIP712 is EIP712 { - function _domainNameAndVersion() - internal - pure - override - returns (string memory name, string memory version) - { - name = "Milady"; - version = "1"; - } - - function hashTypedData(bytes32 structHash) external view returns (bytes32) { - return _hashTypedData(structHash); - } - - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } -} diff --git a/lib/solady/test/utils/mocks/MockEIP712Dynamic.sol b/lib/solady/test/utils/mocks/MockEIP712Dynamic.sol deleted file mode 100644 index b9051f7..0000000 --- a/lib/solady/test/utils/mocks/MockEIP712Dynamic.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../../src/utils/EIP712.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockEIP712Dynamic is EIP712 { - string private _name; - string private _version; - - constructor(string memory name, string memory version) { - _name = name; - _version = version; - } - - function setDomainNameAndVersion(string memory name, string memory version) public { - _name = name; - _version = version; - } - - function _domainNameAndVersion() - internal - view - override - returns (string memory name, string memory version) - { - name = _name; - version = _version; - } - - function _domainNameAndVersionMayChange() internal pure override returns (bool) { - return true; - } - - function hashTypedData(bytes32 structHash) external view returns (bytes32) { - return _hashTypedData(structHash); - } - - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } -} diff --git a/lib/solady/test/utils/mocks/MockERC1155.sol b/lib/solady/test/utils/mocks/MockERC1155.sol deleted file mode 100644 index ed398d3..0000000 --- a/lib/solady/test/utils/mocks/MockERC1155.sol +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC1155} from "../../../src/tokens/ERC1155.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC1155 is ERC1155 { - function uri(uint256) public pure virtual override returns (string memory) {} - - function mint(address to, uint256 id, uint256 amount, bytes memory data) public virtual { - _mint(_brutalized(to), id, amount, data); - } - - function batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - _batchMint(_brutalized(to), ids, amounts, data); - } - - function burn(address from, uint256 id, uint256 amount) public virtual { - _burn(_brutalized(msg.sender), _brutalized(from), id, amount); - } - - function uncheckedBurn(address from, uint256 id, uint256 amount) public virtual { - _burn(_brutalized(from), id, amount); - } - - function batchBurn(address from, uint256[] memory ids, uint256[] memory amounts) - public - virtual - { - _batchBurn(_brutalized(msg.sender), _brutalized(from), ids, amounts); - } - - function uncheckedBatchBurn(address from, uint256[] memory ids, uint256[] memory amounts) - public - virtual - { - _batchBurn(_brutalized(from), ids, amounts); - } - - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes calldata data - ) public virtual override { - super.safeTransferFrom(_brutalized(from), _brutalized(to), id, amount, data); - } - - function directSafeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { - _safeTransfer(_brutalized(msg.sender), _brutalized(from), _brutalized(to), id, amount, data); - } - - function uncheckedSafeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { - _safeTransfer(_brutalized(address(0)), _brutalized(from), _brutalized(to), id, amount, data); - } - - function safeBatchTransferFrom( - address from, - address to, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) public virtual override { - super.safeBatchTransferFrom(_brutalized(from), _brutalized(to), ids, amounts, data); - } - - function directSafeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - _safeBatchTransfer( - _brutalized(msg.sender), _brutalized(from), _brutalized(to), ids, amounts, data - ); - } - - function uncheckedSafeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - _safeBatchTransfer( - _brutalized(address(0)), _brutalized(from), _brutalized(to), ids, amounts, data - ); - } - - function directSetApprovalForAll(address operator, bool approved) public virtual { - _setApprovalForAll(_brutalized(msg.sender), _brutalized(operator), approved); - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC1271Malicious.sol b/lib/solady/test/utils/mocks/MockERC1271Malicious.sol deleted file mode 100644 index 3428d1a..0000000 --- a/lib/solady/test/utils/mocks/MockERC1271Malicious.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC1271Malicious { - function isValidSignature(bytes32, bytes calldata) external pure returns (bytes4) { - /// @solidity memory-safe-assembly - assembly { - mstore(0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - return(0, 32) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC1271Wallet.sol b/lib/solady/test/utils/mocks/MockERC1271Wallet.sol deleted file mode 100644 index f4cf765..0000000 --- a/lib/solady/test/utils/mocks/MockERC1271Wallet.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../../src/utils/ECDSA.sol"; - -/// @notice A generic interface for a contract which properly accepts ERC721 tokens. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) -abstract contract ERC721TokenReceiver { - function onERC721Received(address, address, uint256, bytes calldata) - external - virtual - returns (bytes4) - { - return ERC721TokenReceiver.onERC721Received.selector; - } -} - -/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) -abstract contract ERC1155TokenReceiver { - function onERC1155Received(address, address, uint256, uint256, bytes calldata) - external - virtual - returns (bytes4) - { - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external virtual returns (bytes4) { - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } -} - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC1271Wallet is ERC721TokenReceiver, ERC1155TokenReceiver { - address signer; - - constructor(address signer_) { - signer = signer_; - } - - function isValidSignature(bytes32 hash, bytes calldata signature) - external - view - returns (bytes4) - { - return ECDSA.recover(hash, signature) == signer ? bytes4(0x1626ba7e) : bytes4(0); - } -} diff --git a/lib/solady/test/utils/mocks/MockERC20.sol b/lib/solady/test/utils/mocks/MockERC20.sol deleted file mode 100644 index e1a2765..0000000 --- a/lib/solady/test/utils/mocks/MockERC20.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC20} from "../../../src/tokens/ERC20.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC20 is ERC20 { - string internal _name; - string internal _symbol; - uint8 internal _decimals; - bytes32 internal immutable _nameHash; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) { - _name = name_; - _symbol = symbol_; - _decimals = decimals_; - _nameHash = keccak256(bytes(name_)); - } - - function _constantNameHash() internal view virtual override returns (bytes32) { - return _nameHash; - } - - function name() public view virtual override returns (string memory) { - return _name; - } - - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - function decimals() public view virtual override returns (uint8) { - return _decimals; - } - - function mint(address to, uint256 value) public virtual { - _mint(_brutalized(to), value); - } - - function burn(address from, uint256 value) public virtual { - _burn(_brutalized(from), value); - } - - function directTransfer(address from, address to, uint256 amount) public virtual { - _transfer(_brutalized(from), _brutalized(to), amount); - } - - function directSpendAllowance(address owner, address spender, uint256 amount) public virtual { - _spendAllowance(_brutalized(owner), _brutalized(spender), amount); - } - - function transfer(address to, uint256 amount) public virtual override returns (bool) { - return super.transfer(_brutalized(to), amount); - } - - function transferFrom(address from, address to, uint256 amount) - public - virtual - override - returns (bool) - { - return super.transferFrom(_brutalized(from), _brutalized(to), amount); - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC20LikeUSDT.sol b/lib/solady/test/utils/mocks/MockERC20LikeUSDT.sol deleted file mode 100644 index 2089ddf..0000000 --- a/lib/solady/test/utils/mocks/MockERC20LikeUSDT.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {MockERC20} from "./MockERC20.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC20LikeUSDT is MockERC20 { - constructor() MockERC20("Tether USD", "USDT", 6) {} - - // Replicates USDT (0xdAC17F958D2ee523a2206206994597C13D831ec7) approval behavior. - function approve(address spender, uint256 amount) public virtual override returns (bool) { - require(amount == 0 || allowance(msg.sender, spender) == 0, "USDT approval failure"); - return super.approve(spender, amount); - } -} diff --git a/lib/solady/test/utils/mocks/MockERC2981.sol b/lib/solady/test/utils/mocks/MockERC2981.sol deleted file mode 100644 index 0c38c5a..0000000 --- a/lib/solady/test/utils/mocks/MockERC2981.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC2981} from "../../../src/tokens/ERC2981.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC2981 is ERC2981 { - function feeDenominator() external pure returns (uint256) { - return _feeDenominator(); - } - - function setDefaultRoyalty(address receiver, uint96 feeNumerator) external { - _setDefaultRoyalty(_brutalized(receiver), _brutalized(feeNumerator)); - } - - function deleteDefaultRoyalty() external { - _deleteDefaultRoyalty(); - } - - function setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) external { - _setTokenRoyalty(tokenId, _brutalized(receiver), _brutalized(feeNumerator)); - } - - function resetTokenRoyalty(uint256 tokenId) external { - _resetTokenRoyalty(tokenId); - } - - function _brutalized(uint96 x) internal view returns (uint96 result) { - /// @solidity memory-safe-assembly - assembly { - result := or(x, shl(96, gas())) - } - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC4337.sol b/lib/solady/test/utils/mocks/MockERC4337.sol deleted file mode 100644 index fdef803..0000000 --- a/lib/solady/test/utils/mocks/MockERC4337.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC4337} from "../../../src/accounts/ERC4337.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC4337 is ERC4337 { - function withdrawDepositTo(address to, uint256 amount) public payable virtual override { - super.withdrawDepositTo(_brutalized(to), amount); - } - - function _brutalized(address a) private pure returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, 0x0123456789abcdeffedcba98)) - } - } - - function executeBatch(uint256 filler, Call[] calldata calls) - public - payable - virtual - onlyEntryPointOrOwner - returns (bytes[] memory results) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, add(mload(0x40), mod(filler, 0x40))) - } - return super.executeBatch(calls); - } - - function _domainNameAndVersion() - internal - pure - override - returns (string memory, string memory) - { - return ("Milady", "1"); - } - - function hashTypedData(bytes32 structHash) external view returns (bytes32) { - return _hashTypedData(structHash); - } - - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } -} diff --git a/lib/solady/test/utils/mocks/MockERC4626.sol b/lib/solady/test/utils/mocks/MockERC4626.sol deleted file mode 100644 index 82cd582..0000000 --- a/lib/solady/test/utils/mocks/MockERC4626.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC20, ERC4626} from "../../../src/tokens/ERC4626.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC4626 is ERC4626 { - bool public immutable useVirtualShares; - uint8 public immutable decimalsOffset; - - address internal immutable _underlying; - uint8 internal immutable _decimals; - - string internal _name; - string internal _symbol; - - uint256 public beforeWithdrawHookCalledCounter; - uint256 public afterDepositHookCalledCounter; - - constructor( - address underlying_, - string memory name_, - string memory symbol_, - bool useVirtualShares_, - uint8 decimalsOffset_ - ) { - _underlying = underlying_; - - (bool success, uint8 result) = _tryGetAssetDecimals(underlying_); - _decimals = success ? result : _DEFAULT_UNDERLYING_DECIMALS; - - _name = name_; - _symbol = symbol_; - - useVirtualShares = useVirtualShares_; - decimalsOffset = decimalsOffset_; - } - - function asset() public view virtual override returns (address) { - return _underlying; - } - - function name() public view virtual override returns (string memory) { - return _name; - } - - function symbol() public view virtual override returns (string memory) { - return _symbol; - } - - function _useVirtualShares() internal view virtual override returns (bool) { - return useVirtualShares; - } - - function _underlyingDecimals() internal view virtual override returns (uint8) { - return _decimals; - } - - function _decimalsOffset() internal view virtual override returns (uint8) { - return decimalsOffset; - } - - function _beforeWithdraw(uint256, uint256) internal override { - unchecked { - ++beforeWithdrawHookCalledCounter; - } - } - - function _afterDeposit(uint256, uint256) internal override { - unchecked { - ++afterDepositHookCalledCounter; - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC6551.sol b/lib/solady/test/utils/mocks/MockERC6551.sol deleted file mode 100644 index 663d897..0000000 --- a/lib/solady/test/utils/mocks/MockERC6551.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC6551} from "../../../src/accounts/ERC6551.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC6551 is ERC6551 { - function _brutalized(address a) private pure returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, 0x0123456789abcdeffedcba98)) - } - } - - function executeBatch(uint256 filler, Call[] calldata calls, uint8 operation) - public - payable - virtual - returns (bytes[] memory results) - { - /// @solidity memory-safe-assembly - assembly { - mstore(0x40, add(mload(0x40), mod(filler, 0x40))) - } - return super.executeBatch(calls, operation); - } - - function _domainNameAndVersion() - internal - pure - override - returns (string memory, string memory) - { - return ("Milady", "1"); - } - - function hashTypedData(bytes32 structHash) external view returns (bytes32) { - return _hashTypedData(structHash); - } - - function DOMAIN_SEPARATOR() external view returns (bytes32) { - return _domainSeparator(); - } - - function mockId() public pure virtual returns (string memory) { - return "1"; - } -} - -contract MockERC6551V2 is MockERC6551 { - function mockId() public pure virtual override(MockERC6551) returns (string memory) { - return "2"; - } -} diff --git a/lib/solady/test/utils/mocks/MockERC6551Registry.sol b/lib/solady/test/utils/mocks/MockERC6551Registry.sol deleted file mode 100644 index adf981d..0000000 --- a/lib/solady/test/utils/mocks/MockERC6551Registry.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC6551Registry { - function createAccount( - address implementation, - bytes32 salt, - uint256 chainId, - address tokenContract, - uint256 tokenId - ) external returns (address) { - assembly { - // Memory Layout: - // ---- - // 0x00 0xff (1 byte) - // 0x01 registry (address) (20 bytes) - // 0x15 salt (bytes32) (32 bytes) - // 0x35 Bytecode Hash (bytes32) (32 bytes) - // ---- - // 0x55 ERC-1167 Constructor + Header (20 bytes) - // 0x69 implementation (address) (20 bytes) - // 0x5D ERC-1167 Footer (15 bytes) - // 0x8C salt (uint256) (32 bytes) - // 0xAC chainId (uint256) (32 bytes) - // 0xCC tokenContract (address) (32 bytes) - // 0xEC tokenId (uint256) (32 bytes) - - // Silence unused variable warnings - pop(chainId) - - // Copy bytecode + constant data to memory - calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId - mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer - mstore(0x5d, implementation) // implementation - mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header - - // Copy create2 computation data to memory - mstore8(0x00, 0xff) // 0xFF - mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytedcode) - mstore(0x01, shl(96, address())) // registry address - mstore(0x15, salt) // salt - - // Compute account address - let computed := keccak256(0x00, 0x55) - - // If the account has not yet been deployed - if iszero(extcodesize(computed)) { - // Deploy account contract - let deployed := create2(0, 0x55, 0xb7, salt) - - // Revert if the deployment fails - if iszero(deployed) { - mstore(0x00, 0x20188a59) // `AccountCreationFailed()` - revert(0x1c, 0x04) - } - - // Store account address in memory before salt and chainId - mstore(0x6c, deployed) - - // Emit the ERC6551AccountCreated event - log4( - 0x6c, - 0x60, - // `ERC6551AccountCreated(address,address,bytes32,uint256,address,uint256)` - 0x79f19b3655ee38b1ce526556b7731a20c8f218fbda4a3990b6cc4172fdf88722, - implementation, - tokenContract, - tokenId - ) - - // Return the account address - return(0x6c, 0x20) - } - - // Otherwise, return the computed account address - mstore(0x00, shr(96, shl(96, computed))) - return(0x00, 0x20) - } - } - - function account( - address implementation, - bytes32 salt, - uint256 chainId, - address tokenContract, - uint256 tokenId - ) external view returns (address) { - assembly { - // Silence unused variable warnings - pop(chainId) - pop(tokenContract) - pop(tokenId) - - // Copy bytecode + constant data to memory - calldatacopy(0x8c, 0x24, 0x80) // salt, chainId, tokenContract, tokenId - mstore(0x6c, 0x5af43d82803e903d91602b57fd5bf3) // ERC-1167 footer - mstore(0x5d, implementation) // implementation - mstore(0x49, 0x3d60ad80600a3d3981f3363d3d373d3d3d363d73) // ERC-1167 constructor + header - - // Copy create2 computation data to memory - mstore8(0x00, 0xff) // 0xFF - mstore(0x35, keccak256(0x55, 0xb7)) // keccak256(bytedcode) - mstore(0x01, shl(96, address())) // registry address - mstore(0x15, salt) // salt - - // Store computed account address in memory - mstore(0x00, shr(96, shl(96, keccak256(0x00, 0x55)))) - - // Return computed account address - return(0x00, 0x20) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC6909.sol b/lib/solady/test/utils/mocks/MockERC6909.sol deleted file mode 100644 index dc3abf7..0000000 --- a/lib/solady/test/utils/mocks/MockERC6909.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC6909} from "../../../src/tokens/ERC6909.sol"; -import {LibString} from "../../../src/utils/LibString.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC6909 is ERC6909 { - error TokenDoesNotExist(); - - function name() public view virtual override returns (string memory) { - return "Solady Token"; - } - - function symbol() public view virtual override returns (string memory) { - return "ST"; - } - - function tokenURI(uint256 id) public view virtual override returns (string memory) { - return string(abi.encodePacked("http://solady.org/", LibString.toString(id))); - } - - function mint(address to, uint256 id, uint256 amount) public payable virtual { - _mint(_brutalized(to), id, amount); - } - - function burn(address from, uint256 id, uint256 amount) public payable virtual { - _burn(_brutalized(from), id, amount); - } - - function approve(address spender, uint256 id, uint256 amount) - public - payable - virtual - override - returns (bool) - { - return super.approve(_brutalized(spender), id, amount); - } - - function setOperator(address owner, bool approved) - public - payable - virtual - override - returns (bool) - { - /// @solidity memory-safe-assembly - assembly { - approved := mul(gas(), approved) - } - return super.setOperator(_brutalized(owner), approved); - } - - function transfer(address to, uint256 id, uint256 amount) - public - payable - virtual - override - returns (bool) - { - return super.transfer(_brutalized(to), id, amount); - } - - function transferFrom(address from, address to, uint256 id, uint256 amount) - public - payable - virtual - override - returns (bool) - { - return super.transferFrom(_brutalized(from), _brutalized(to), id, amount); - } - - function directTransferFrom(address by, address from, address to, uint256 id, uint256 amount) - public - payable - virtual - { - _transfer(_brutalized(by), _brutalized(from), _brutalized(to), id, amount); - } - - function directSetOperator(address owner, address operator, bool approved) - public - payable - virtual - { - /// @solidity memory-safe-assembly - assembly { - approved := mul(gas(), approved) - } - _setOperator(owner, operator, approved); - } - - function directApprove(address owner, address spender, uint256 id, uint256 amount) - public - payable - virtual - { - _approve(owner, spender, id, amount); - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockERC721.sol b/lib/solady/test/utils/mocks/MockERC721.sol deleted file mode 100644 index 81e8df1..0000000 --- a/lib/solady/test/utils/mocks/MockERC721.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC721} from "../../../src/tokens/ERC721.sol"; -import {LibString} from "../../../src/utils/LibString.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockERC721 is ERC721 { - function name() public view virtual override returns (string memory) { - return "TEST NFT"; - } - - function symbol() public view virtual override returns (string memory) { - return "TEST"; - } - - function tokenURI(uint256 id) public view virtual override returns (string memory) { - if (!_exists(id)) revert TokenDoesNotExist(); - return string(abi.encodePacked("https://remilio.org/remilio/json/", LibString.toString(id))); - } - - function exists(uint256 id) public view virtual returns (bool) { - return _exists(id); - } - - function mint(address to, uint256 id) public virtual { - _mint(_brutalized(to), id); - } - - function burn(uint256 id) public virtual { - _burn(msg.sender, id); - } - - function uncheckedBurn(uint256 id) public virtual { - _burn(id); - } - - function safeMint(address to, uint256 id) public virtual { - _safeMint(_brutalized(to), id); - } - - function safeMint(address to, uint256 id, bytes calldata data) public virtual { - _safeMint(_brutalized(to), id, data); - } - - function getExtraData(uint256 id) public view virtual returns (uint96) { - return _getExtraData(id); - } - - function setExtraData(uint256 id, uint96 value) public virtual { - _setExtraData(id, value); - } - - function getAux(address owner) public view virtual returns (uint224) { - return _getAux(_brutalized(owner)); - } - - function setAux(address owner, uint224 value) public virtual { - _setAux(_brutalized(owner), value); - } - - function approve(address account, uint256 id) public payable virtual override { - super.approve(_brutalized(account), id); - } - - function directApprove(address account, uint256 id) public virtual { - if (!_isApprovedOrOwner(_brutalized(msg.sender), id)) revert NotOwnerNorApproved(); - _approve(_brutalized(account), id); - } - - function setApprovalForAll(address operator, bool approved) public virtual override { - super.setApprovalForAll(_brutalized(operator), approved); - } - - function directSetApprovalForAll(address operator, bool approved) public virtual { - _setApprovalForAll(_brutalized(msg.sender), _brutalized(operator), approved); - } - - function transferFrom(address from, address to, uint256 id) public payable virtual override { - super.transferFrom(_brutalized(from), _brutalized(to), id); - } - - function uncheckedTransferFrom(address from, address to, uint256 id) public payable virtual { - _transfer(_brutalized(address(0)), _brutalized(from), _brutalized(to), id); - } - - function directTransferFrom(address from, address to, uint256 id) public virtual { - _transfer(_brutalized(msg.sender), _brutalized(from), _brutalized(to), id); - } - - function safeTransferFrom(address from, address to, uint256 id) - public - payable - virtual - override - { - super.safeTransferFrom(_brutalized(from), _brutalized(to), id); - } - - function directSafeTransferFrom(address from, address to, uint256 id) public virtual { - _safeTransfer(_brutalized(msg.sender), _brutalized(from), _brutalized(to), id); - } - - function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) - public - payable - virtual - override - { - super.safeTransferFrom(_brutalized(from), _brutalized(to), id, data); - } - - function directSafeTransferFrom(address from, address to, uint256 id, bytes calldata data) - public - virtual - { - _safeTransfer(_brutalized(msg.sender), _brutalized(from), _brutalized(to), id, data); - } - - function isApprovedOrOwner(address account, uint256 id) public view virtual returns (bool) { - return _isApprovedOrOwner(_brutalized(account), id); - } - - function directOwnerOf(uint256 id) public view virtual returns (address) { - if (!_exists(id)) revert TokenDoesNotExist(); - return _ownerOf(id); - } - - function directGetApproved(uint256 id) public view virtual returns (address) { - if (!_exists(id)) revert TokenDoesNotExist(); - return _getApproved(id); - } - - function _brutalized(address a) internal view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, gas())) - } - } -} diff --git a/lib/solady/test/utils/mocks/MockETHRecipient.sol b/lib/solady/test/utils/mocks/MockETHRecipient.sol deleted file mode 100644 index b4012a7..0000000 --- a/lib/solady/test/utils/mocks/MockETHRecipient.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockETHRecipient { - bool public immutable gasGriefUponReceiveETH; - - bool public immutable updateCounterUponReceiveETH; - - uint256 public counter; - - uint256 public garbage; - - constructor(bool updateCounterUponReceiveETH_, bool gasGriefUponReceiveETH_) { - updateCounterUponReceiveETH = updateCounterUponReceiveETH_; - gasGriefUponReceiveETH = gasGriefUponReceiveETH_; - } - - receive() external payable { - if (updateCounterUponReceiveETH) { - counter += 1; - } - if (gasGriefUponReceiveETH) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, timestamp()) - mstore(0x20, 0) - - for { let i := 0 } lt(i, 10) { i := add(i, 1) } { - let h := keccak256(0x00, 0x40) - mstore(0x00, sload(h)) - mstore(0x20, i) - sstore(add(h, 1), h) - } - sstore(garbage.slot, keccak256(0x00, 0x40)) - } - } - } -} diff --git a/lib/solady/test/utils/mocks/MockEntryPoint.sol b/lib/solady/test/utils/mocks/MockEntryPoint.sol deleted file mode 100644 index e0fc265..0000000 --- a/lib/solady/test/utils/mocks/MockEntryPoint.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {ERC4337} from "../../../src/accounts/ERC4337.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockEntryPoint { - mapping(address => uint256) public balanceOf; - - function depositTo(address to) public payable { - balanceOf[to] += msg.value; - } - - function withdrawTo(address to, uint256 amount) public payable { - balanceOf[msg.sender] -= amount; - (bool success,) = payable(to).call{value: amount}(""); - require(success); - } - - function validateUserOp( - address account, - ERC4337.UserOperation memory userOp, - bytes32 userOpHash, - uint256 missingAccountFunds - ) public payable returns (uint256 validationData) { - validationData = - ERC4337(payable(account)).validateUserOp(userOp, userOpHash, missingAccountFunds); - } - - receive() external payable { - depositTo(msg.sender); - } -} diff --git a/lib/solady/test/utils/mocks/MockImplementation.sol b/lib/solady/test/utils/mocks/MockImplementation.sol deleted file mode 100644 index 59ba351..0000000 --- a/lib/solady/test/utils/mocks/MockImplementation.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockImplementation { - error Fail(); - - mapping(uint256 => uint256) internal _values; - - function fails() external pure { - revert Fail(); - } - - function succeeds(uint256 a) external pure returns (uint256) { - return a; - } - - function setValue(uint256 key, uint256 value) external payable { - _values[key] = value; - } - - function getValue(uint256 key) external view returns (uint256) { - return _values[key]; - } -} diff --git a/lib/solady/test/utils/mocks/MockMulticallable.sol b/lib/solady/test/utils/mocks/MockMulticallable.sol deleted file mode 100644 index 1a6c197..0000000 --- a/lib/solady/test/utils/mocks/MockMulticallable.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../../src/utils/Multicallable.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockMulticallable is Multicallable { - error CustomError(); - - struct Tuple { - uint256 a; - uint256 b; - } - - function revertsWithString(string memory e) external pure { - revert(e); - } - - function revertsWithCustomError() external pure { - revert CustomError(); - } - - function revertsWithNothing() external pure { - revert(); - } - - function returnsTuple(uint256 a, uint256 b) external pure returns (Tuple memory tuple) { - tuple = Tuple({a: a, b: b}); - } - - function returnsString(string calldata s) external pure returns (string memory) { - return s; - } - - uint256 public paid; - - function pay() external payable { - paid += msg.value; - } - - function returnsSender() external view returns (address) { - return msg.sender; - } - - function multicallOriginal(bytes[] calldata data) - public - payable - returns (bytes[] memory results) - { - unchecked { - results = new bytes[](data.length); - for (uint256 i = 0; i < data.length; i++) { - (bool success, bytes memory result) = address(this).delegatecall(data[i]); - if (!success) { - // Next 5 lines from https://ethereum.stackexchange.com/a/83577 - if (result.length < 68) revert(); - /// @solidity memory-safe-assembly - assembly { - result := add(result, 0x04) - } - revert(abi.decode(result, (string))); - } - results[i] = result; - } - } - } -} diff --git a/lib/solady/test/utils/mocks/MockOwnable.sol b/lib/solady/test/utils/mocks/MockOwnable.sol deleted file mode 100644 index c132cd9..0000000 --- a/lib/solady/test/utils/mocks/MockOwnable.sol +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Ownable} from "../../../src/auth/Ownable.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockOwnable is Ownable { - bool public flag; - - constructor() payable { - _initializeOwner(msg.sender); - - // Perform the tests on the helper functions. - - address brutalizedAddress = _brutalizedAddress(address(0)); - bool brutalizedAddressIsBrutalized; - /// @solidity memory-safe-assembly - assembly { - brutalizedAddressIsBrutalized := gt(shr(160, brutalizedAddress), 0) - } - - if (!brutalizedAddressIsBrutalized) { - revert("Setup failed"); - } - } - - function initializeOwnerDirect(address newOwner) public payable { - _initializeOwner(_brutalizedAddress(newOwner)); - } - - function setOwnerDirect(address newOwner) public payable { - _setOwner(_brutalizedAddress(newOwner)); - } - - function completeOwnershipHandover(address pendingOwner) public payable virtual override { - super.completeOwnershipHandover(_brutalizedAddress(pendingOwner)); - } - - function transferOwnership(address newOwner) public payable virtual override { - super.transferOwnership(_brutalizedAddress(newOwner)); - } - - function ownershipHandoverExpiresAt(address pendingOwner) - public - view - virtual - override - returns (uint256 result) - { - result = super.ownershipHandoverExpiresAt(_brutalizedAddress(pendingOwner)); - } - - function ownershipHandoverValidFor() public view returns (uint64 result) { - result = _ownershipHandoverValidFor(); - /// @solidity memory-safe-assembly - assembly { - // Some acrobatics to make the brutalized bits pseudorandomly - // different with every call. - mstore(0x00, or(calldataload(0), mload(0x40))) - mstore(0x20, or(caller(), mload(0x00))) - // Just brutalize the upper unused bits of the result to see if it causes any issue. - result := or(shl(64, keccak256(0x00, 0x40)), result) - mstore(0x40, add(0x20, mload(0x40))) - mstore(0x00, result) - } - } - - function updateFlagWithOnlyOwner() public payable onlyOwner { - flag = true; - } - - function _brutalizedAddress(address value) private view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - // Some acrobatics to make the brutalized bits pseudorandomly - // different with every call. - mstore(0x00, or(calldataload(0), mload(0x40))) - mstore(0x20, or(caller(), mload(0x00))) - result := or(shl(160, keccak256(0x00, 0x40)), value) - mstore(0x40, add(0x20, mload(0x40))) - mstore(0x00, result) - } - } -} - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockOwnableBytecodeSizer is Ownable { - constructor() payable { - initialize(); - } - - function initialize() public payable { - _initializeOwner(msg.sender); - } -} diff --git a/lib/solady/test/utils/mocks/MockOwnableRoles.sol b/lib/solady/test/utils/mocks/MockOwnableRoles.sol deleted file mode 100644 index 8a3038f..0000000 --- a/lib/solady/test/utils/mocks/MockOwnableRoles.sol +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Ownable, OwnableRoles} from "../../../src/auth/OwnableRoles.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockOwnableRoles is OwnableRoles { - bool public flag; - - constructor() payable { - _initializeOwner(msg.sender); - - // Perform the tests on the helper functions. - - address brutalizedAddress = _brutalizedAddress(address(0)); - bool brutalizedAddressIsBrutalized; - /// @solidity memory-safe-assembly - assembly { - brutalizedAddressIsBrutalized := gt(shr(160, brutalizedAddress), 0) - } - - if (!brutalizedAddressIsBrutalized) { - revert("Setup failed"); - } - - bool badBool; - /// @solidity memory-safe-assembly - assembly { - badBool := 2 - } - - bool checkedBadBool = _checkedBool(badBool); - - if (checkedBadBool) { - revert("Setup failed"); - } - } - - function initializeOwnerDirect(address newOwner) public payable { - _initializeOwner(_brutalizedAddress(newOwner)); - } - - function setOwnerDirect(address newOwner) public payable { - _setOwner(_brutalizedAddress(newOwner)); - } - - function setRolesDirect(address user, uint256 roles) public payable { - _setRoles(_brutalizedAddress(user), roles); - } - - function grantRolesDirect(address user, uint256 roles) public payable { - _grantRoles(_brutalizedAddress(user), roles); - } - - function removeRolesDirect(address user, uint256 roles) public payable { - _removeRoles(_brutalizedAddress(user), roles); - } - - function grantRoles(address user, uint256 roles) public payable virtual override { - super.grantRoles(_brutalizedAddress(user), roles); - } - - function revokeRoles(address user, uint256 roles) public payable virtual override { - super.revokeRoles(_brutalizedAddress(user), roles); - } - - function completeOwnershipHandover(address pendingOwner) public payable virtual override { - super.completeOwnershipHandover(_brutalizedAddress(pendingOwner)); - } - - function transferOwnership(address newOwner) public payable virtual override { - super.transferOwnership(_brutalizedAddress(newOwner)); - } - - function rolesOf(address user) public view virtual override returns (uint256 result) { - result = super.rolesOf(_brutalizedAddress(user)); - } - - function ownershipHandoverExpiresAt(address pendingOwner) - public - view - virtual - override - returns (uint256 result) - { - result = super.ownershipHandoverExpiresAt(_brutalizedAddress(pendingOwner)); - } - - function ownershipHandoverValidFor() public view returns (uint64 result) { - result = _ownershipHandoverValidFor(); - /// @solidity memory-safe-assembly - assembly { - // Some acrobatics to make the brutalized bits pseudorandomly - // different with every call. - mstore(0x00, or(calldataload(0), mload(0x40))) - mstore(0x20, or(caller(), mload(0x00))) - // Just brutalize the upper unused bits of the result to see if it causes any issue. - result := or(shl(64, keccak256(0x00, 0x40)), result) - mstore(0x40, add(0x20, mload(0x40))) - mstore(0x00, result) - } - } - - function updateFlagWithOnlyOwner() public payable onlyOwner { - flag = true; - } - - function updateFlagWithOnlyRoles(uint256 roles) public payable onlyRoles(roles) { - flag = true; - } - - function updateFlagWithOnlyOwnerOrRoles(uint256 roles) public payable onlyOwnerOrRoles(roles) { - flag = true; - } - - function updateFlagWithOnlyRolesOrOwner(uint256 roles) public payable onlyRolesOrOwner(roles) { - flag = true; - } - - function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) { - roles = _rolesFromOrdinals(ordinals); - } - - function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) { - ordinals = _ordinalsFromRoles(roles); - } - - function _brutalizedAddress(address value) private view returns (address result) { - /// @solidity memory-safe-assembly - assembly { - // Some acrobatics to make the brutalized bits pseudorandomly - // different with every call. - mstore(0x00, or(calldataload(0), mload(0x40))) - mstore(0x20, or(caller(), mload(0x00))) - result := or(shl(160, keccak256(0x00, 0x40)), value) - mstore(0x40, add(0x20, mload(0x40))) - mstore(0x00, result) - } - } - - function _checkedBool(bool value) private pure returns (bool result) { - result = value; - bool resultIsOneOrZero; - /// @solidity memory-safe-assembly - assembly { - // We wanna check if the result is either 1 or 0, - // to make sure we practice good assembly politeness. - resultIsOneOrZero := lt(result, 2) - } - if (!resultIsOneOrZero) result = !result; - } -} - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockOwnableRolesBytecodeSizer is OwnableRoles { - constructor() payable { - initialize(); - } - - function initialize() public payable { - _initializeOwner(msg.sender); - } -} diff --git a/lib/solady/test/utils/mocks/MockReceiver.sol b/lib/solady/test/utils/mocks/MockReceiver.sol deleted file mode 100644 index 77cd523..0000000 --- a/lib/solady/test/utils/mocks/MockReceiver.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Receiver} from "../../../src/accounts/Receiver.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockReceiver is Receiver {} diff --git a/lib/solady/test/utils/mocks/MockUUPSImplementation.sol b/lib/solady/test/utils/mocks/MockUUPSImplementation.sol deleted file mode 100644 index c153160..0000000 --- a/lib/solady/test/utils/mocks/MockUUPSImplementation.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {UUPSUpgradeable} from "../../../src/utils/UUPSUpgradeable.sol"; - -/// @dev WARNING! This mock is strictly intended for testing purposes only. -/// Do NOT copy anything here into production code unless you really know what you are doing. -contract MockUUPSImplementation is UUPSUpgradeable { - uint256 public value; - - address owner; - - error Unauthorized(); - - error CustomError(address owner_); - - function initialize(address owner_) public { - owner = owner_; - } - - modifier onlyOwner() { - if (msg.sender != owner) revert Unauthorized(); - _; - } - - function _authorizeUpgrade(address) internal override onlyOwner {} - - function revertWithError() public view { - revert CustomError(owner); - } - - function setValue(uint256 val_) public { - value = val_; - } - - function upgradeToAndCall(address newImplementation, bytes calldata data) - public - payable - override - { - super.upgradeToAndCall(_brutalized(newImplementation), data); - } - - function _brutalized(address a) private pure returns (address result) { - /// @solidity memory-safe-assembly - assembly { - result := or(a, shl(160, 0x0123456789abcdeffedcba98)) - } - } -} diff --git a/lib/solady/test/utils/weird-tokens/MissingReturnToken.sol b/lib/solady/test/utils/weird-tokens/MissingReturnToken.sol deleted file mode 100644 index e07d8bd..0000000 --- a/lib/solady/test/utils/weird-tokens/MissingReturnToken.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract MissingReturnToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "MissingReturnToken"; - - string public constant symbol = "MRT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - } - - function transferFrom(address from, address to, uint256 amount) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) { - allowance[from][msg.sender] = allowed - amount; - } - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - } -} diff --git a/lib/solady/test/utils/weird-tokens/ReturnsFalseToken.sol b/lib/solady/test/utils/weird-tokens/ReturnsFalseToken.sol deleted file mode 100644 index 8dd01de..0000000 --- a/lib/solady/test/utils/weird-tokens/ReturnsFalseToken.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract ReturnsFalseToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsFalseToken"; - - string public constant symbol = "RFT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual returns (bool) { - return false; - } - - function transfer(address, uint256) public virtual returns (bool) { - return false; - } - - function transferFrom(address, address, uint256) public virtual returns (bool) { - return false; - } -} diff --git a/lib/solady/test/utils/weird-tokens/ReturnsRawBytesToken.sol b/lib/solady/test/utils/weird-tokens/ReturnsRawBytesToken.sol deleted file mode 100644 index 6525990..0000000 --- a/lib/solady/test/utils/weird-tokens/ReturnsRawBytesToken.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract ReturnsRawBytesToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsRawBytesToken"; - - string public constant symbol = "RGT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - MOCK STORAGE - //////////////////////////////////////////////////////////////*/ - - bytes rawBytes; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - bytes memory _rawBytes = rawBytes; - - /// @solidity memory-safe-assembly - assembly { - return(add(_rawBytes, 32), mload(_rawBytes)) - } - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - bytes memory _rawBytes = rawBytes; - - /// @solidity memory-safe-assembly - assembly { - return(add(_rawBytes, 32), mload(_rawBytes)) - } - } - - function transferFrom(address from, address to, uint256 amount) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) { - allowance[from][msg.sender] = allowed - amount; - } - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - bytes memory _rawBytes = rawBytes; - - /// @solidity memory-safe-assembly - assembly { - return(add(_rawBytes, 32), mload(_rawBytes)) - } - } - - /*/////////////////////////////////////////////////////////////// - MOCK LOGIC - //////////////////////////////////////////////////////////////*/ - - function setRawBytes(bytes memory _rawBytes) public virtual { - rawBytes = _rawBytes; - } -} diff --git a/lib/solady/test/utils/weird-tokens/ReturnsTooLittleToken.sol b/lib/solady/test/utils/weird-tokens/ReturnsTooLittleToken.sol deleted file mode 100644 index 2c624c1..0000000 --- a/lib/solady/test/utils/weird-tokens/ReturnsTooLittleToken.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract ReturnsTooLittleToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsTooLittleToken"; - - string public constant symbol = "RTLT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } - - function transfer(address, uint256) public virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } - - function transferFrom(address, address, uint256) public virtual { - /// @solidity memory-safe-assembly - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } -} diff --git a/lib/solady/test/utils/weird-tokens/ReturnsTooMuchToken.sol b/lib/solady/test/utils/weird-tokens/ReturnsTooMuchToken.sol deleted file mode 100644 index 97d16b1..0000000 --- a/lib/solady/test/utils/weird-tokens/ReturnsTooMuchToken.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract ReturnsTooMuchToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsTooMuchToken"; - - string public constant symbol = "RTMT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - /// @solidity memory-safe-assembly - assembly { - mstore(0, 1) - return(0, 4096) - } - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - /// @solidity memory-safe-assembly - assembly { - mstore(0, 1) - return(0, 4096) - } - } - - function transferFrom(address from, address to, uint256 amount) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) { - allowance[from][msg.sender] = allowed - amount; - } - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - /// @solidity memory-safe-assembly - assembly { - mstore(0, 1) - return(0, 4096) - } - } -} diff --git a/lib/solady/test/utils/weird-tokens/ReturnsTwoToken.sol b/lib/solady/test/utils/weird-tokens/ReturnsTwoToken.sol deleted file mode 100644 index fb88d0d..0000000 --- a/lib/solady/test/utils/weird-tokens/ReturnsTwoToken.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract ReturnsTwoToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsFalseToken"; - - string public constant symbol = "RTT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual returns (uint256) { - return 2; - } - - function transfer(address, uint256) public virtual returns (uint256) { - return 2; - } - - function transferFrom(address, address, uint256) public virtual returns (uint256) { - return 2; - } -} diff --git a/lib/solady/test/utils/weird-tokens/RevertingToken.sol b/lib/solady/test/utils/weird-tokens/RevertingToken.sol deleted file mode 100644 index 17aca18..0000000 --- a/lib/solady/test/utils/weird-tokens/RevertingToken.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -contract RevertingToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "RevertingToken"; - - string public constant symbol = "RT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual { - revert(); - } - - function transfer(address, uint256) public virtual { - revert(); - } - - function transferFrom(address, address, uint256) public virtual { - revert(); - } -} diff --git a/lib/solmate b/lib/solmate new file mode 160000 index 0000000..4b47a19 --- /dev/null +++ b/lib/solmate @@ -0,0 +1 @@ +Subproject commit 4b47a19038b798b4a33d9749d25e570443520647 diff --git a/lib/solmate/.gas-snapshot b/lib/solmate/.gas-snapshot deleted file mode 100644 index 13e2410..0000000 --- a/lib/solmate/.gas-snapshot +++ /dev/null @@ -1,568 +0,0 @@ -AuthTest:testCallFunctionAsOwner() (gas: 29784) -AuthTest:testCallFunctionWithPermissiveAuthority() (gas: 124292) -AuthTest:testCallFunctionWithPermissiveAuthority(address) (runs: 256, μ: 129197, ~: 129235) -AuthTest:testFailCallFunctionAsNonOwner() (gas: 15523) -AuthTest:testFailCallFunctionAsNonOwner(address) (runs: 256, μ: 15685, ~: 15685) -AuthTest:testFailCallFunctionAsOwnerWithOutOfOrderAuthority() (gas: 136021) -AuthTest:testFailCallFunctionWithRestrictiveAuthority() (gas: 129144) -AuthTest:testFailCallFunctionWithRestrictiveAuthority(address) (runs: 256, μ: 129329, ~: 129329) -AuthTest:testFailSetAuthorityAsNonOwner() (gas: 18325) -AuthTest:testFailSetAuthorityAsNonOwner(address,address) (runs: 256, μ: 18594, ~: 18594) -AuthTest:testFailSetAuthorityWithRestrictiveAuthority() (gas: 129077) -AuthTest:testFailSetAuthorityWithRestrictiveAuthority(address,address) (runs: 256, μ: 129409, ~: 129409) -AuthTest:testFailTransferOwnershipAsNonOwner() (gas: 15641) -AuthTest:testFailTransferOwnershipAsNonOwner(address,address) (runs: 256, μ: 15922, ~: 15922) -AuthTest:testFailTransferOwnershipAsOwnerWithOutOfOrderAuthority() (gas: 136159) -AuthTest:testFailTransferOwnershipAsOwnerWithOutOfOrderAuthority(address) (runs: 256, μ: 136344, ~: 136344) -AuthTest:testFailTransferOwnershipWithRestrictiveAuthority() (gas: 129338) -AuthTest:testFailTransferOwnershipWithRestrictiveAuthority(address,address) (runs: 256, μ: 129588, ~: 129588) -AuthTest:testSetAuthorityAsOwner() (gas: 32214) -AuthTest:testSetAuthorityAsOwner(address) (runs: 256, μ: 32384, ~: 32384) -AuthTest:testSetAuthorityAsOwnerWithOutOfOrderAuthority() (gas: 226419) -AuthTest:testSetAuthorityWithPermissiveAuthority() (gas: 125962) -AuthTest:testSetAuthorityWithPermissiveAuthority(address,address) (runs: 256, μ: 130862, ~: 131055) -AuthTest:testTransferOwnershipAsOwner() (gas: 15298) -AuthTest:testTransferOwnershipAsOwner(address) (runs: 256, μ: 15469, ~: 15469) -AuthTest:testTransferOwnershipWithPermissiveAuthority() (gas: 127926) -AuthTest:testTransferOwnershipWithPermissiveAuthority(address,address) (runs: 256, μ: 131000, ~: 131000) -Bytes32AddressLibTest:testFillLast12Bytes() (gas: 223) -Bytes32AddressLibTest:testFromLast20Bytes() (gas: 191) -CREATE3Test:testDeployERC20() (gas: 853207) -CREATE3Test:testDeployERC20(bytes32,string,string,uint8) (runs: 256, μ: 899408, ~: 899427) -CREATE3Test:testFailDoubleDeployDifferentBytecode() (gas: 9079256848778914176) -CREATE3Test:testFailDoubleDeployDifferentBytecode(bytes32,bytes,bytes) (runs: 256, μ: 5830806919119382084, ~: 8937393460516727580) -CREATE3Test:testFailDoubleDeploySameBytecode() (gas: 9079256848778906219) -CREATE3Test:testFailDoubleDeploySameBytecode(bytes32,bytes) (runs: 256, μ: 5516047526412694750, ~: 8937393460516728714) -CREATE3Test:testPredictDeployERC20() (gas: 2102048) -DSTestPlusTest:testBound() (gas: 14214) -DSTestPlusTest:testBound(uint256,uint256,uint256) (runs: 256, μ: 2787, ~: 2793) -DSTestPlusTest:testBrutalizeMemory() (gas: 823) -DSTestPlusTest:testFailBoundMinBiggerThanMax() (gas: 309) -DSTestPlusTest:testFailBoundMinBiggerThanMax(uint256,uint256,uint256) (runs: 256, μ: 460, ~: 460) -DSTestPlusTest:testRelApproxEqBothZeroesPasses() (gas: 425) -ERC1155Test:testApproveAll() (gas: 31009) -ERC1155Test:testApproveAll(address,bool) (runs: 256, μ: 22771, ~: 31309) -ERC1155Test:testBatchBalanceOf() (gas: 157631) -ERC1155Test:testBatchBalanceOf(address[],uint256[],uint256[],bytes) (runs: 256, μ: 3564574, ~: 2943600) -ERC1155Test:testBatchBurn() (gas: 151074) -ERC1155Test:testBatchBurn(address,uint256[],uint256[],uint256[],bytes) (runs: 256, μ: 3627558, ~: 3049938) -ERC1155Test:testBatchMintToEOA() (gas: 137337) -ERC1155Test:testBatchMintToEOA(address,uint256[],uint256[],bytes) (runs: 256, μ: 3072824, ~: 2525648) -ERC1155Test:testBatchMintToERC1155Recipient() (gas: 995703) -ERC1155Test:testBatchMintToERC1155Recipient(uint256[],uint256[],bytes) (runs: 256, μ: 7395823, ~: 6396323) -ERC1155Test:testBurn() (gas: 38598) -ERC1155Test:testBurn(address,uint256,uint256,bytes,uint256) (runs: 256, μ: 39631, ~: 42098) -ERC1155Test:testFailBalanceOfBatchWithArrayMismatch() (gas: 7933) -ERC1155Test:testFailBalanceOfBatchWithArrayMismatch(address[],uint256[]) (runs: 256, μ: 58022, ~: 58788) -ERC1155Test:testFailBatchBurnInsufficientBalance() (gas: 136156) -ERC1155Test:testFailBatchBurnInsufficientBalance(address,uint256[],uint256[],uint256[],bytes) (runs: 256, μ: 1401354, ~: 590599) -ERC1155Test:testFailBatchBurnWithArrayLengthMismatch() (gas: 135542) -ERC1155Test:testFailBatchBurnWithArrayLengthMismatch(address,uint256[],uint256[],uint256[],bytes) (runs: 256, μ: 86139, ~: 76411) -ERC1155Test:testFailBatchMintToNonERC1155Recipient() (gas: 167292) -ERC1155Test:testFailBatchMintToNonERC1155Recipient(uint256[],uint256[],bytes) (runs: 256, μ: 3190100, ~: 2673077) -ERC1155Test:testFailBatchMintToRevertingERC1155Recipient() (gas: 358811) -ERC1155Test:testFailBatchMintToRevertingERC1155Recipient(uint256[],uint256[],bytes) (runs: 256, μ: 3381638, ~: 2864613) -ERC1155Test:testFailBatchMintToWrongReturnDataERC1155Recipient() (gas: 310743) -ERC1155Test:testFailBatchMintToWrongReturnDataERC1155Recipient(uint256[],uint256[],bytes) (runs: 256, μ: 3333596, ~: 2816572) -ERC1155Test:testFailBatchMintToZero() (gas: 131737) -ERC1155Test:testFailBatchMintToZero(uint256[],uint256[],bytes) (runs: 256, μ: 3130600, ~: 2612336) -ERC1155Test:testFailBatchMintWithArrayMismatch() (gas: 9600) -ERC1155Test:testFailBatchMintWithArrayMismatch(address,uint256[],uint256[],bytes) (runs: 256, μ: 66450, ~: 66511) -ERC1155Test:testFailBurnInsufficientBalance() (gas: 34852) -ERC1155Test:testFailBurnInsufficientBalance(address,uint256,uint256,uint256,bytes) (runs: 256, μ: 35106, ~: 38209) -ERC1155Test:testFailMintToNonERC155Recipient() (gas: 68191) -ERC1155Test:testFailMintToNonERC155Recipient(uint256,uint256,bytes) (runs: 256, μ: 68507, ~: 69197) -ERC1155Test:testFailMintToRevertingERC155Recipient() (gas: 259435) -ERC1155Test:testFailMintToRevertingERC155Recipient(uint256,uint256,bytes) (runs: 256, μ: 259682, ~: 260373) -ERC1155Test:testFailMintToWrongReturnDataERC155Recipient() (gas: 259389) -ERC1155Test:testFailMintToWrongReturnDataERC155Recipient(uint256,uint256,bytes) (runs: 256, μ: 259706, ~: 260397) -ERC1155Test:testFailMintToZero() (gas: 33705) -ERC1155Test:testFailMintToZero(uint256,uint256,bytes) (runs: 256, μ: 33815, ~: 34546) -ERC1155Test:testFailSafeBatchTransferFromToNonERC1155Recipient() (gas: 321377) -ERC1155Test:testFailSafeBatchTransferFromToNonERC1155Recipient(uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 3541141, ~: 2963551) -ERC1155Test:testFailSafeBatchTransferFromToRevertingERC1155Recipient() (gas: 512956) -ERC1155Test:testFailSafeBatchTransferFromToRevertingERC1155Recipient(uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 3732678, ~: 3155082) -ERC1155Test:testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() (gas: 464847) -ERC1155Test:testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient(uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 3684595, ~: 3107003) -ERC1155Test:testFailSafeBatchTransferFromToZero() (gas: 286556) -ERC1155Test:testFailSafeBatchTransferFromToZero(uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 3505960, ~: 2928396) -ERC1155Test:testFailSafeBatchTransferFromWithArrayLengthMismatch() (gas: 162674) -ERC1155Test:testFailSafeBatchTransferFromWithArrayLengthMismatch(address,uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 79144, ~: 76711) -ERC1155Test:testFailSafeBatchTransferInsufficientBalance() (gas: 163555) -ERC1155Test:testFailSafeBatchTransferInsufficientBalance(address,uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 1808833, ~: 648144) -ERC1155Test:testFailSafeTransferFromInsufficientBalance() (gas: 63245) -ERC1155Test:testFailSafeTransferFromInsufficientBalance(address,uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 62944, ~: 67391) -ERC1155Test:testFailSafeTransferFromSelfInsufficientBalance() (gas: 34297) -ERC1155Test:testFailSafeTransferFromSelfInsufficientBalance(address,uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 35671, ~: 38477) -ERC1155Test:testFailSafeTransferFromToNonERC155Recipient() (gas: 96510) -ERC1155Test:testFailSafeTransferFromToNonERC155Recipient(uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 96657, ~: 100546) -ERC1155Test:testFailSafeTransferFromToRevertingERC1155Recipient() (gas: 287731) -ERC1155Test:testFailSafeTransferFromToRevertingERC1155Recipient(uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 287828, ~: 291719) -ERC1155Test:testFailSafeTransferFromToWrongReturnDataERC1155Recipient() (gas: 239587) -ERC1155Test:testFailSafeTransferFromToWrongReturnDataERC1155Recipient(uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 239707, ~: 243598) -ERC1155Test:testFailSafeTransferFromToZero() (gas: 62014) -ERC1155Test:testFailSafeTransferFromToZero(uint256,uint256,uint256,bytes,bytes) (runs: 256, μ: 62068, ~: 66037) -ERC1155Test:testMintToEOA() (gas: 34765) -ERC1155Test:testMintToEOA(address,uint256,uint256,bytes) (runs: 256, μ: 35273, ~: 35907) -ERC1155Test:testMintToERC1155Recipient() (gas: 661411) -ERC1155Test:testMintToERC1155Recipient(uint256,uint256,bytes) (runs: 256, μ: 691094, ~: 684374) -ERC1155Test:testSafeBatchTransferFromToEOA() (gas: 297822) -ERC1155Test:testSafeBatchTransferFromToEOA(address,uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 5017684, ~: 4010502) -ERC1155Test:testSafeBatchTransferFromToERC1155Recipient() (gas: 1175327) -ERC1155Test:testSafeBatchTransferFromToERC1155Recipient(uint256[],uint256[],uint256[],bytes,bytes) (runs: 256, μ: 7759463, ~: 6618414) -ERC1155Test:testSafeTransferFromSelf() (gas: 64177) -ERC1155Test:testSafeTransferFromSelf(uint256,uint256,bytes,uint256,address,bytes) (runs: 256, μ: 64681, ~: 68564) -ERC1155Test:testSafeTransferFromToEOA() (gas: 93167) -ERC1155Test:testSafeTransferFromToEOA(uint256,uint256,bytes,uint256,address,bytes) (runs: 256, μ: 93963, ~: 97450) -ERC1155Test:testSafeTransferFromToERC1155Recipient() (gas: 739583) -ERC1155Test:testSafeTransferFromToERC1155Recipient(uint256,uint256,bytes,uint256,bytes) (runs: 256, μ: 769591, ~: 765729) -ERC20Invariants:invariantBalanceSum() (runs: 256, calls: 3840, reverts: 2349) -ERC20Test:invariantMetadata() (runs: 256, calls: 3840, reverts: 2566) -ERC20Test:testApprove() (gas: 31058) -ERC20Test:testApprove(address,uint256) (runs: 256, μ: 30036, ~: 31280) -ERC20Test:testBurn() (gas: 56970) -ERC20Test:testBurn(address,uint256,uint256) (runs: 256, μ: 57875, ~: 59645) -ERC20Test:testFailBurnInsufficientBalance(address,uint256,uint256) (runs: 256, μ: 52089, ~: 55492) -ERC20Test:testFailPermitBadDeadline() (gas: 36935) -ERC20Test:testFailPermitBadDeadline(uint256,address,uint256,uint256) (runs: 256, μ: 31831, ~: 37218) -ERC20Test:testFailPermitBadNonce() (gas: 36874) -ERC20Test:testFailPermitBadNonce(uint256,address,uint256,uint256,uint256) (runs: 256, μ: 32158, ~: 37187) -ERC20Test:testFailPermitPastDeadline() (gas: 11191) -ERC20Test:testFailPermitPastDeadline(uint256,address,uint256,uint256) (runs: 256, μ: 11916, ~: 13101) -ERC20Test:testFailPermitReplay() (gas: 66274) -ERC20Test:testFailPermitReplay(uint256,address,uint256,uint256) (runs: 256, μ: 56082, ~: 66592) -ERC20Test:testFailTransferFromInsufficientAllowance() (gas: 80882) -ERC20Test:testFailTransferFromInsufficientAllowance(address,uint256,uint256) (runs: 256, μ: 79521, ~: 83393) -ERC20Test:testFailTransferFromInsufficientBalance() (gas: 81358) -ERC20Test:testFailTransferFromInsufficientBalance(address,uint256,uint256) (runs: 256, μ: 79323, ~: 83870) -ERC20Test:testFailTransferInsufficientBalance() (gas: 52806) -ERC20Test:testFailTransferInsufficientBalance(address,uint256,uint256) (runs: 256, μ: 51912, ~: 55310) -ERC20Test:testInfiniteApproveTransferFrom() (gas: 89793) -ERC20Test:testMetadata(string,string,uint8) (runs: 256, μ: 846655, ~: 840944) -ERC20Test:testMint() (gas: 53746) -ERC20Test:testMint(address,uint256) (runs: 256, μ: 51437, ~: 53925) -ERC20Test:testPermit() (gas: 63193) -ERC20Test:testPermit(uint248,address,uint256,uint256) (runs: 256, μ: 62506, ~: 63517) -ERC20Test:testTransfer() (gas: 60272) -ERC20Test:testTransfer(address,uint256) (runs: 256, μ: 57996, ~: 60484) -ERC20Test:testTransferFrom() (gas: 83777) -ERC20Test:testTransferFrom(address,uint256,uint256) (runs: 256, μ: 86993, ~: 92841) -ERC4626Test:invariantMetadata() (runs: 256, calls: 3840, reverts: 2883) -ERC4626Test:testFailDepositWithNoApproval() (gas: 13369) -ERC4626Test:testFailDepositWithNotEnoughApproval() (gas: 87005) -ERC4626Test:testFailDepositZero() (gas: 7780) -ERC4626Test:testFailMintWithNoApproval() (gas: 13308) -ERC4626Test:testFailRedeemWithNoShareAmount() (gas: 32342) -ERC4626Test:testFailRedeemWithNotEnoughShareAmount() (gas: 203643) -ERC4626Test:testFailRedeemZero() (gas: 7967) -ERC4626Test:testFailWithdrawWithNoUnderlyingAmount() (gas: 32289) -ERC4626Test:testFailWithdrawWithNotEnoughUnderlyingAmount() (gas: 203607) -ERC4626Test:testMetadata(string,string) (runs: 256, μ: 1471865, ~: 1472396) -ERC4626Test:testMintZero() (gas: 54607) -ERC4626Test:testMultipleMintDepositRedeemWithdraw() (gas: 411804) -ERC4626Test:testSingleDepositWithdraw(uint128) (runs: 256, μ: 201539, ~: 201550) -ERC4626Test:testSingleMintRedeem(uint128) (runs: 256, μ: 201465, ~: 201476) -ERC4626Test:testVaultInteractionsForSomeoneElse() (gas: 286238) -ERC4626Test:testWithdrawZero() (gas: 52468) -ERC6909Test:testApprove() (gas: 31572) -ERC6909Test:testApprove(address,uint256,uint256) (runs: 256, μ: 30860, ~: 31793) -ERC6909Test:testBurn() (gas: 35411) -ERC6909Test:testBurn(address,uint256,uint256) (runs: 256, μ: 23773, ~: 24153) -ERC6909Test:testFailMintBalanceOverflow() (gas: 31727) -ERC6909Test:testFailTransferBalanceOverflow() (gas: 85598) -ERC6909Test:testFailTransferBalanceOverflow(address,address,uint256,uint256) (runs: 256, μ: 87909, ~: 87909) -ERC6909Test:testFailTransferBalanceUnderflow() (gas: 10847) -ERC6909Test:testFailTransferBalanceUnderflow(address,address,uint256,uint256) (runs: 256, μ: 13194, ~: 13194) -ERC6909Test:testFailTransferFromBalanceOverflow() (gas: 85554) -ERC6909Test:testFailTransferFromBalanceOverflow(address,address,uint256,uint256) (runs: 256, μ: 88327, ~: 88327) -ERC6909Test:testFailTransferFromBalanceUnderflow() (gas: 10868) -ERC6909Test:testFailTransferFromBalanceUnderflow(address,address,uint256,uint256) (runs: 256, μ: 13424, ~: 13424) -ERC6909Test:testFailTransferFromNotAuthorized() (gas: 36078) -ERC6909Test:testFailTransferFromNotAuthorized(address,address,uint256,uint256) (runs: 256, μ: 38677, ~: 38677) -ERC6909Test:testMint() (gas: 31579) -ERC6909Test:testMint(address,uint256,uint256) (runs: 256, μ: 30878, ~: 31811) -ERC6909Test:testSetOperator() (gas: 31083) -ERC6909Test:testSetOperator(address,bool) (runs: 256, μ: 22873, ~: 31411) -ERC6909Test:testTransfer() (gas: 61874) -ERC6909Test:testTransfer(address,address,uint256,uint256,uint256) (runs: 256, μ: 59908, ~: 64506) -ERC6909Test:testTransferFromAsOperator() (gas: 87418) -ERC6909Test:testTransferFromAsOperator(address,address,uint256,uint256,uint256) (runs: 256, μ: 85351, ~: 90112) -ERC6909Test:testTransferFromWithApproval() (gas: 91890) -ERC6909Test:testTransferFromWithApproval(address,address,uint256,uint256,uint256) (runs: 256, μ: 88775, ~: 94697) -ERC6909Test:testTransferFromWithInfiniteApproval() (gas: 91541) -ERC6909Test:testTransferFromWithInfiniteApproval(address,address,uint256,uint256,uint256) (runs: 256, μ: 89501, ~: 94262) -ERC721Test:invariantMetadata() (runs: 256, calls: 3840, reverts: 2192) -ERC721Test:testApprove() (gas: 78427) -ERC721Test:testApprove(address,uint256) (runs: 256, μ: 78637, ~: 78637) -ERC721Test:testApproveAll() (gas: 31063) -ERC721Test:testApproveAll(address,bool) (runs: 256, μ: 22869, ~: 31407) -ERC721Test:testApproveBurn() (gas: 65550) -ERC721Test:testApproveBurn(address,uint256) (runs: 256, μ: 65609, ~: 65621) -ERC721Test:testBurn() (gas: 46107) -ERC721Test:testBurn(address,uint256) (runs: 256, μ: 46148, ~: 46160) -ERC721Test:testFailApproveUnAuthorized() (gas: 55598) -ERC721Test:testFailApproveUnAuthorized(address,uint256,address) (runs: 256, μ: 55872, ~: 55873) -ERC721Test:testFailApproveUnMinted() (gas: 10236) -ERC721Test:testFailApproveUnMinted(uint256,address) (runs: 256, μ: 10363, ~: 10363) -ERC721Test:testFailBalanceOfZeroAddress() (gas: 5555) -ERC721Test:testFailBurnUnMinted() (gas: 7857) -ERC721Test:testFailBurnUnMinted(uint256) (runs: 256, μ: 7938, ~: 7938) -ERC721Test:testFailDoubleBurn() (gas: 58943) -ERC721Test:testFailDoubleBurn(uint256,address) (runs: 256, μ: 59174, ~: 59174) -ERC721Test:testFailDoubleMint() (gas: 53286) -ERC721Test:testFailDoubleMint(uint256,address) (runs: 256, μ: 53496, ~: 53496) -ERC721Test:testFailMintToZero() (gas: 5753) -ERC721Test:testFailMintToZero(uint256) (runs: 256, μ: 5835, ~: 5835) -ERC721Test:testFailOwnerOfUnminted() (gas: 7609) -ERC721Test:testFailOwnerOfUnminted(uint256) (runs: 256, μ: 7689, ~: 7689) -ERC721Test:testFailSafeMintToERC721RecipientWithWrongReturnData() (gas: 159076) -ERC721Test:testFailSafeMintToERC721RecipientWithWrongReturnData(uint256) (runs: 256, μ: 159125, ~: 159125) -ERC721Test:testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() (gas: 159831) -ERC721Test:testFailSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256,bytes) (runs: 256, μ: 160231, ~: 160182) -ERC721Test:testFailSafeMintToNonERC721Recipient() (gas: 89210) -ERC721Test:testFailSafeMintToNonERC721Recipient(uint256) (runs: 256, μ: 89279, ~: 89279) -ERC721Test:testFailSafeMintToNonERC721RecipientWithData() (gas: 89995) -ERC721Test:testFailSafeMintToNonERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 90420, ~: 90368) -ERC721Test:testFailSafeMintToRevertingERC721Recipient() (gas: 204743) -ERC721Test:testFailSafeMintToRevertingERC721Recipient(uint256) (runs: 256, μ: 204815, ~: 204815) -ERC721Test:testFailSafeMintToRevertingERC721RecipientWithData() (gas: 205517) -ERC721Test:testFailSafeMintToRevertingERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 205964, ~: 205915) -ERC721Test:testFailSafeTransferFromToERC721RecipientWithWrongReturnData() (gas: 187276) -ERC721Test:testFailSafeTransferFromToERC721RecipientWithWrongReturnData(uint256) (runs: 256, μ: 187360, ~: 187360) -ERC721Test:testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() (gas: 187728) -ERC721Test:testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData(uint256,bytes) (runs: 256, μ: 188067, ~: 188063) -ERC721Test:testFailSafeTransferFromToNonERC721Recipient() (gas: 117413) -ERC721Test:testFailSafeTransferFromToNonERC721Recipient(uint256) (runs: 256, μ: 117495, ~: 117495) -ERC721Test:testFailSafeTransferFromToNonERC721RecipientWithData() (gas: 117872) -ERC721Test:testFailSafeTransferFromToNonERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 118280, ~: 118276) -ERC721Test:testFailSafeTransferFromToRevertingERC721Recipient() (gas: 233009) -ERC721Test:testFailSafeTransferFromToRevertingERC721Recipient(uint256) (runs: 256, μ: 233050, ~: 233050) -ERC721Test:testFailSafeTransferFromToRevertingERC721RecipientWithData() (gas: 233396) -ERC721Test:testFailSafeTransferFromToRevertingERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 233823, ~: 233819) -ERC721Test:testFailTransferFromNotOwner() (gas: 57872) -ERC721Test:testFailTransferFromNotOwner(address,address,uint256) (runs: 256, μ: 58144, ~: 58162) -ERC721Test:testFailTransferFromToZero() (gas: 53381) -ERC721Test:testFailTransferFromToZero(uint256) (runs: 256, μ: 53463, ~: 53463) -ERC721Test:testFailTransferFromUnOwned() (gas: 8000) -ERC721Test:testFailTransferFromUnOwned(address,address,uint256) (runs: 256, μ: 8276, ~: 8241) -ERC721Test:testFailTransferFromWrongFrom() (gas: 53361) -ERC721Test:testFailTransferFromWrongFrom(address,address,address,uint256) (runs: 256, μ: 53194, ~: 53752) -ERC721Test:testMetadata(string,string) (runs: 256, μ: 1309634, ~: 1309984) -ERC721Test:testMint() (gas: 54336) -ERC721Test:testMint(address,uint256) (runs: 256, μ: 54521, ~: 54521) -ERC721Test:testSafeMintToEOA() (gas: 56993) -ERC721Test:testSafeMintToEOA(uint256,address) (runs: 256, μ: 56754, ~: 57421) -ERC721Test:testSafeMintToERC721Recipient() (gas: 427035) -ERC721Test:testSafeMintToERC721Recipient(uint256) (runs: 256, μ: 426053, ~: 427142) -ERC721Test:testSafeMintToERC721RecipientWithData() (gas: 448149) -ERC721Test:testSafeMintToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 480015, ~: 470905) -ERC721Test:testSafeTransferFromToEOA() (gas: 95666) -ERC721Test:testSafeTransferFromToEOA(uint256,address) (runs: 256, μ: 94979, ~: 96099) -ERC721Test:testSafeTransferFromToERC721Recipient() (gas: 485549) -ERC721Test:testSafeTransferFromToERC721Recipient(uint256) (runs: 256, μ: 484593, ~: 485682) -ERC721Test:testSafeTransferFromToERC721RecipientWithData() (gas: 506317) -ERC721Test:testSafeTransferFromToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 538126, ~: 529082) -ERC721Test:testTransferFrom() (gas: 86347) -ERC721Test:testTransferFrom(uint256,address) (runs: 256, μ: 86467, ~: 86475) -ERC721Test:testTransferFromApproveAll() (gas: 92898) -ERC721Test:testTransferFromApproveAll(uint256,address) (runs: 256, μ: 93181, ~: 93182) -ERC721Test:testTransferFromSelf() (gas: 64776) -ERC721Test:testTransferFromSelf(uint256,address) (runs: 256, μ: 65060, ~: 65061) -FixedPointMathLibTest:testDifferentiallyFuzzSqrt(uint256) (runs: 256, μ: 13868, ~: 6222) -FixedPointMathLibTest:testDivWadDown() (gas: 820) -FixedPointMathLibTest:testDivWadDown(uint256,uint256) (runs: 256, μ: 716, ~: 813) -FixedPointMathLibTest:testDivWadDownEdgeCases() (gas: 439) -FixedPointMathLibTest:testDivWadUp() (gas: 943) -FixedPointMathLibTest:testDivWadUp(uint256,uint256) (runs: 256, μ: 799, ~: 952) -FixedPointMathLibTest:testDivWadUpEdgeCases() (gas: 442) -FixedPointMathLibTest:testFailDivWadDownOverflow(uint256,uint256) (runs: 256, μ: 440, ~: 419) -FixedPointMathLibTest:testFailDivWadDownZeroDenominator() (gas: 332) -FixedPointMathLibTest:testFailDivWadDownZeroDenominator(uint256) (runs: 256, μ: 387, ~: 387) -FixedPointMathLibTest:testFailDivWadUpOverflow(uint256,uint256) (runs: 256, μ: 395, ~: 374) -FixedPointMathLibTest:testFailDivWadUpZeroDenominator() (gas: 332) -FixedPointMathLibTest:testFailDivWadUpZeroDenominator(uint256) (runs: 256, μ: 386, ~: 386) -FixedPointMathLibTest:testFailMulDivDownOverflow(uint256,uint256,uint256) (runs: 256, μ: 436, ~: 414) -FixedPointMathLibTest:testFailMulDivDownZeroDenominator() (gas: 328) -FixedPointMathLibTest:testFailMulDivDownZeroDenominator(uint256,uint256) (runs: 256, μ: 385, ~: 385) -FixedPointMathLibTest:testFailMulDivUpOverflow(uint256,uint256,uint256) (runs: 256, μ: 459, ~: 437) -FixedPointMathLibTest:testFailMulDivUpZeroDenominator() (gas: 329) -FixedPointMathLibTest:testFailMulDivUpZeroDenominator(uint256,uint256) (runs: 256, μ: 428, ~: 428) -FixedPointMathLibTest:testFailMulWadDownOverflow(uint256,uint256) (runs: 256, μ: 419, ~: 387) -FixedPointMathLibTest:testFailMulWadUpOverflow(uint256,uint256) (runs: 256, μ: 396, ~: 364) -FixedPointMathLibTest:testMulDivDown() (gas: 1813) -FixedPointMathLibTest:testMulDivDown(uint256,uint256,uint256) (runs: 256, μ: 680, ~: 786) -FixedPointMathLibTest:testMulDivDownEdgeCases() (gas: 686) -FixedPointMathLibTest:testMulDivUp() (gas: 2095) -FixedPointMathLibTest:testMulDivUp(uint256,uint256,uint256) (runs: 256, μ: 810, ~: 1034) -FixedPointMathLibTest:testMulDivUpEdgeCases() (gas: 785) -FixedPointMathLibTest:testMulWadDown() (gas: 823) -FixedPointMathLibTest:testMulWadDown(uint256,uint256) (runs: 256, μ: 688, ~: 803) -FixedPointMathLibTest:testMulWadDownEdgeCases() (gas: 822) -FixedPointMathLibTest:testMulWadUp() (gas: 921) -FixedPointMathLibTest:testMulWadUp(uint256,uint256) (runs: 256, μ: 834, ~: 1053) -FixedPointMathLibTest:testMulWadUpEdgeCases() (gas: 899) -FixedPointMathLibTest:testRPow() (gas: 2164) -FixedPointMathLibTest:testSqrt() (gas: 2580) -FixedPointMathLibTest:testSqrt(uint256) (runs: 256, μ: 997, ~: 1013) -FixedPointMathLibTest:testSqrtBack(uint256) (runs: 256, μ: 14998, ~: 340) -FixedPointMathLibTest:testSqrtBackHashed(uint256) (runs: 256, μ: 59001, ~: 59500) -FixedPointMathLibTest:testSqrtBackHashedSingle() (gas: 58937) -LibStringTest:testDifferentiallyFuzzToString(uint256,bytes) (runs: 256, μ: 20460, ~: 7749) -LibStringTest:testDifferentiallyFuzzToStringInt(int256,bytes) (runs: 256, μ: 20610, ~: 8980) -LibStringTest:testToString() (gas: 10069) -LibStringTest:testToStringDirty() (gas: 8145) -LibStringTest:testToStringIntNegative() (gas: 9634) -LibStringTest:testToStringIntPositive() (gas: 10481) -LibStringTest:testToStringOverwrite() (gas: 506) -MerkleProofLibTest:testValidProofSupplied() (gas: 2153) -MerkleProofLibTest:testVerifyEmptyMerkleProofSuppliedLeafAndRootDifferent() (gas: 1458) -MerkleProofLibTest:testVerifyEmptyMerkleProofSuppliedLeafAndRootSame() (gas: 1452) -MerkleProofLibTest:testVerifyInvalidProofSupplied() (gas: 2172) -MultiRolesAuthorityTest:testCanCallPublicCapability() (gas: 34204) -MultiRolesAuthorityTest:testCanCallPublicCapability(address,address,bytes4) (runs: 256, μ: 34392, ~: 34361) -MultiRolesAuthorityTest:testCanCallWithAuthorizedRole() (gas: 80416) -MultiRolesAuthorityTest:testCanCallWithAuthorizedRole(address,uint8,address,bytes4) (runs: 256, μ: 80700, ~: 80671) -MultiRolesAuthorityTest:testCanCallWithCustomAuthority() (gas: 422439) -MultiRolesAuthorityTest:testCanCallWithCustomAuthority(address,address,bytes4) (runs: 256, μ: 422858, ~: 422858) -MultiRolesAuthorityTest:testCanCallWithCustomAuthorityOverridesPublicCapability() (gas: 247388) -MultiRolesAuthorityTest:testCanCallWithCustomAuthorityOverridesPublicCapability(address,address,bytes4) (runs: 256, μ: 247841, ~: 247841) -MultiRolesAuthorityTest:testCanCallWithCustomAuthorityOverridesUserWithRole() (gas: 256546) -MultiRolesAuthorityTest:testCanCallWithCustomAuthorityOverridesUserWithRole(address,uint8,address,bytes4) (runs: 256, μ: 256878, ~: 256852) -MultiRolesAuthorityTest:testSetPublicCapabilities() (gas: 27727) -MultiRolesAuthorityTest:testSetPublicCapabilities(bytes4) (runs: 256, μ: 27837, ~: 27835) -MultiRolesAuthorityTest:testSetRoleCapabilities() (gas: 28932) -MultiRolesAuthorityTest:testSetRoleCapabilities(uint8,bytes4) (runs: 256, μ: 29075, ~: 29072) -MultiRolesAuthorityTest:testSetRoles() (gas: 28918) -MultiRolesAuthorityTest:testSetRoles(address,uint8) (runs: 256, μ: 29027, ~: 29014) -MultiRolesAuthorityTest:testSetTargetCustomAuthority() (gas: 28102) -MultiRolesAuthorityTest:testSetTargetCustomAuthority(address,address) (runs: 256, μ: 28115, ~: 28144) -OwnedTest:testCallFunctionAsNonOwner() (gas: 11344) -OwnedTest:testCallFunctionAsNonOwner(address) (runs: 256, μ: 16271, ~: 16290) -OwnedTest:testCallFunctionAsOwner() (gas: 10435) -OwnedTest:testTransferOwnership() (gas: 13123) -OwnedTest:testTransferOwnership(address) (runs: 256, μ: 13162, ~: 13192) -ReentrancyGuardTest:invariantReentrancyStatusAlways1() (runs: 256, calls: 3840, reverts: 254) -ReentrancyGuardTest:testFailUnprotectedCall() (gas: 46147) -ReentrancyGuardTest:testNoReentrancy() (gas: 7515) -ReentrancyGuardTest:testProtectedCall() (gas: 33467) -RolesAuthorityTest:testCanCallPublicCapability() (gas: 33409) -RolesAuthorityTest:testCanCallPublicCapability(address,address,bytes4) (runs: 256, μ: 33559, ~: 33532) -RolesAuthorityTest:testCanCallWithAuthorizedRole() (gas: 79995) -RolesAuthorityTest:testCanCallWithAuthorizedRole(address,uint8,address,bytes4) (runs: 256, μ: 80265, ~: 80238) -RolesAuthorityTest:testSetPublicCapabilities() (gas: 29095) -RolesAuthorityTest:testSetPublicCapabilities(address,bytes4) (runs: 256, μ: 29211, ~: 29192) -RolesAuthorityTest:testSetRoleCapabilities() (gas: 30276) -RolesAuthorityTest:testSetRoleCapabilities(uint8,address,bytes4) (runs: 256, μ: 30507, ~: 30489) -RolesAuthorityTest:testSetRoles() (gas: 29005) -RolesAuthorityTest:testSetRoles(address,uint8) (runs: 256, μ: 29118, ~: 29106) -SSTORE2Test:testFailReadInvalidPointer() (gas: 2927) -SSTORE2Test:testFailReadInvalidPointer(address,bytes) (runs: 256, μ: 3858, ~: 3892) -SSTORE2Test:testFailReadInvalidPointerCustomBounds() (gas: 3099) -SSTORE2Test:testFailReadInvalidPointerCustomBounds(address,uint256,uint256,bytes) (runs: 256, μ: 4072, ~: 4130) -SSTORE2Test:testFailReadInvalidPointerCustomStartBound() (gas: 3004) -SSTORE2Test:testFailReadInvalidPointerCustomStartBound(address,uint256,bytes) (runs: 256, μ: 3960, ~: 3988) -SSTORE2Test:testFailWriteReadCustomBoundsOutOfRange(bytes,uint256,uint256,bytes) (runs: 256, μ: 46236, ~: 43603) -SSTORE2Test:testFailWriteReadCustomStartBoundOutOfRange(bytes,uint256,bytes) (runs: 256, μ: 46017, ~: 43452) -SSTORE2Test:testFailWriteReadEmptyOutOfBounds() (gas: 34470) -SSTORE2Test:testFailWriteReadOutOfBounds() (gas: 34426) -SSTORE2Test:testFailWriteReadOutOfStartBound() (gas: 34362) -SSTORE2Test:testWriteRead() (gas: 53497) -SSTORE2Test:testWriteRead(bytes,bytes) (runs: 256, μ: 44019, ~: 41555) -SSTORE2Test:testWriteReadCustomBounds() (gas: 34869) -SSTORE2Test:testWriteReadCustomBounds(bytes,uint256,uint256,bytes) (runs: 256, μ: 28531, ~: 41976) -SSTORE2Test:testWriteReadCustomStartBound() (gas: 34740) -SSTORE2Test:testWriteReadCustomStartBound(bytes,uint256,bytes) (runs: 256, μ: 46485, ~: 44053) -SSTORE2Test:testWriteReadEmptyBound() (gas: 34677) -SSTORE2Test:testWriteReadFullBoundedRead() (gas: 53672) -SSTORE2Test:testWriteReadFullStartBound() (gas: 34764) -SafeCastLibTest:testFailSafeCastTo104() (gas: 387) -SafeCastLibTest:testFailSafeCastTo104(uint256) (runs: 256, μ: 468, ~: 468) -SafeCastLibTest:testFailSafeCastTo112() (gas: 388) -SafeCastLibTest:testFailSafeCastTo112(uint256) (runs: 256, μ: 445, ~: 445) -SafeCastLibTest:testFailSafeCastTo120() (gas: 409) -SafeCastLibTest:testFailSafeCastTo120(uint256) (runs: 256, μ: 490, ~: 490) -SafeCastLibTest:testFailSafeCastTo128() (gas: 365) -SafeCastLibTest:testFailSafeCastTo128(uint256) (runs: 256, μ: 487, ~: 487) -SafeCastLibTest:testFailSafeCastTo136() (gas: 409) -SafeCastLibTest:testFailSafeCastTo136(uint256) (runs: 256, μ: 489, ~: 489) -SafeCastLibTest:testFailSafeCastTo144() (gas: 365) -SafeCastLibTest:testFailSafeCastTo144(uint256) (runs: 256, μ: 423, ~: 423) -SafeCastLibTest:testFailSafeCastTo152() (gas: 368) -SafeCastLibTest:testFailSafeCastTo152(uint256) (runs: 256, μ: 468, ~: 468) -SafeCastLibTest:testFailSafeCastTo16() (gas: 388) -SafeCastLibTest:testFailSafeCastTo16(uint256) (runs: 256, μ: 468, ~: 468) -SafeCastLibTest:testFailSafeCastTo160() (gas: 409) -SafeCastLibTest:testFailSafeCastTo160(uint256) (runs: 256, μ: 444, ~: 444) -SafeCastLibTest:testFailSafeCastTo168() (gas: 341) -SafeCastLibTest:testFailSafeCastTo168(uint256) (runs: 256, μ: 488, ~: 488) -SafeCastLibTest:testFailSafeCastTo176() (gas: 363) -SafeCastLibTest:testFailSafeCastTo176(uint256) (runs: 256, μ: 489, ~: 489) -SafeCastLibTest:testFailSafeCastTo184() (gas: 343) -SafeCastLibTest:testFailSafeCastTo184(uint256) (runs: 256, μ: 490, ~: 490) -SafeCastLibTest:testFailSafeCastTo192() (gas: 367) -SafeCastLibTest:testFailSafeCastTo192(uint256) (runs: 256, μ: 446, ~: 446) -SafeCastLibTest:testFailSafeCastTo200() (gas: 343) -SafeCastLibTest:testFailSafeCastTo200(uint256) (runs: 256, μ: 490, ~: 490) -SafeCastLibTest:testFailSafeCastTo208() (gas: 386) -SafeCastLibTest:testFailSafeCastTo208(uint256) (runs: 256, μ: 446, ~: 446) -SafeCastLibTest:testFailSafeCastTo216() (gas: 365) -SafeCastLibTest:testFailSafeCastTo216(uint256) (runs: 256, μ: 424, ~: 424) -SafeCastLibTest:testFailSafeCastTo224() (gas: 409) -SafeCastLibTest:testFailSafeCastTo224(uint256) (runs: 256, μ: 423, ~: 423) -SafeCastLibTest:testFailSafeCastTo232() (gas: 410) -SafeCastLibTest:testFailSafeCastTo232(uint256) (runs: 256, μ: 467, ~: 467) -SafeCastLibTest:testFailSafeCastTo24() (gas: 387) -SafeCastLibTest:testFailSafeCastTo24(uint256) (runs: 256, μ: 424, ~: 424) -SafeCastLibTest:testFailSafeCastTo240() (gas: 364) -SafeCastLibTest:testFailSafeCastTo240(uint256) (runs: 256, μ: 467, ~: 467) -SafeCastLibTest:testFailSafeCastTo248() (gas: 365) -SafeCastLibTest:testFailSafeCastTo248(uint256) (runs: 256, μ: 466, ~: 466) -SafeCastLibTest:testFailSafeCastTo32() (gas: 364) -SafeCastLibTest:testFailSafeCastTo32(uint256) (runs: 256, μ: 468, ~: 468) -SafeCastLibTest:testFailSafeCastTo40() (gas: 366) -SafeCastLibTest:testFailSafeCastTo40(uint256) (runs: 256, μ: 422, ~: 422) -SafeCastLibTest:testFailSafeCastTo48() (gas: 366) -SafeCastLibTest:testFailSafeCastTo48(uint256) (runs: 256, μ: 488, ~: 488) -SafeCastLibTest:testFailSafeCastTo56() (gas: 388) -SafeCastLibTest:testFailSafeCastTo56(uint256) (runs: 256, μ: 445, ~: 445) -SafeCastLibTest:testFailSafeCastTo64() (gas: 410) -SafeCastLibTest:testFailSafeCastTo64(uint256) (runs: 256, μ: 446, ~: 446) -SafeCastLibTest:testFailSafeCastTo72() (gas: 410) -SafeCastLibTest:testFailSafeCastTo72(uint256) (runs: 256, μ: 467, ~: 467) -SafeCastLibTest:testFailSafeCastTo8() (gas: 341) -SafeCastLibTest:testFailSafeCastTo8(uint256) (runs: 256, μ: 421, ~: 421) -SafeCastLibTest:testFailSafeCastTo80() (gas: 343) -SafeCastLibTest:testFailSafeCastTo80(uint256) (runs: 256, μ: 424, ~: 424) -SafeCastLibTest:testFailSafeCastTo88() (gas: 344) -SafeCastLibTest:testFailSafeCastTo88(uint256) (runs: 256, μ: 489, ~: 489) -SafeCastLibTest:testFailSafeCastTo96() (gas: 366) -SafeCastLibTest:testFailSafeCastTo96(uint256) (runs: 256, μ: 469, ~: 469) -SafeCastLibTest:testSafeCastTo104() (gas: 515) -SafeCastLibTest:testSafeCastTo104(uint256) (runs: 256, μ: 2779, ~: 2779) -SafeCastLibTest:testSafeCastTo112() (gas: 469) -SafeCastLibTest:testSafeCastTo112(uint256) (runs: 256, μ: 2755, ~: 2755) -SafeCastLibTest:testSafeCastTo120() (gas: 491) -SafeCastLibTest:testSafeCastTo120(uint256) (runs: 256, μ: 2735, ~: 2735) -SafeCastLibTest:testSafeCastTo128() (gas: 516) -SafeCastLibTest:testSafeCastTo128(uint256) (runs: 256, μ: 2735, ~: 2735) -SafeCastLibTest:testSafeCastTo136() (gas: 470) -SafeCastLibTest:testSafeCastTo136(uint256) (runs: 256, μ: 2757, ~: 2757) -SafeCastLibTest:testSafeCastTo144() (gas: 514) -SafeCastLibTest:testSafeCastTo144(uint256) (runs: 256, μ: 2798, ~: 2798) -SafeCastLibTest:testSafeCastTo152() (gas: 494) -SafeCastLibTest:testSafeCastTo152(uint256) (runs: 256, μ: 2734, ~: 2734) -SafeCastLibTest:testSafeCastTo16() (gas: 469) -SafeCastLibTest:testSafeCastTo16(uint256) (runs: 256, μ: 2779, ~: 2779) -SafeCastLibTest:testSafeCastTo160() (gas: 491) -SafeCastLibTest:testSafeCastTo160(uint256) (runs: 256, μ: 2775, ~: 2775) -SafeCastLibTest:testSafeCastTo168() (gas: 494) -SafeCastLibTest:testSafeCastTo168(uint256) (runs: 256, μ: 2799, ~: 2799) -SafeCastLibTest:testSafeCastTo176() (gas: 493) -SafeCastLibTest:testSafeCastTo176(uint256) (runs: 256, μ: 2734, ~: 2734) -SafeCastLibTest:testSafeCastTo184() (gas: 513) -SafeCastLibTest:testSafeCastTo184(uint256) (runs: 256, μ: 2801, ~: 2801) -SafeCastLibTest:testSafeCastTo192() (gas: 494) -SafeCastLibTest:testSafeCastTo192(uint256) (runs: 256, μ: 2734, ~: 2734) -SafeCastLibTest:testSafeCastTo200() (gas: 470) -SafeCastLibTest:testSafeCastTo200(uint256) (runs: 256, μ: 2734, ~: 2734) -SafeCastLibTest:testSafeCastTo208() (gas: 472) -SafeCastLibTest:testSafeCastTo208(uint256) (runs: 256, μ: 2756, ~: 2756) -SafeCastLibTest:testSafeCastTo216() (gas: 493) -SafeCastLibTest:testSafeCastTo216(uint256) (runs: 256, μ: 2777, ~: 2777) -SafeCastLibTest:testSafeCastTo224() (gas: 469) -SafeCastLibTest:testSafeCastTo224(uint256) (runs: 256, μ: 2733, ~: 2733) -SafeCastLibTest:testSafeCastTo232() (gas: 492) -SafeCastLibTest:testSafeCastTo232(uint256) (runs: 256, μ: 2735, ~: 2735) -SafeCastLibTest:testSafeCastTo24() (gas: 515) -SafeCastLibTest:testSafeCastTo24(uint256) (runs: 256, μ: 2733, ~: 2733) -SafeCastLibTest:testSafeCastTo240() (gas: 513) -SafeCastLibTest:testSafeCastTo240(uint256) (runs: 256, μ: 2800, ~: 2800) -SafeCastLibTest:testSafeCastTo248() (gas: 472) -SafeCastLibTest:testSafeCastTo248(uint256) (runs: 256, μ: 2777, ~: 2777) -SafeCastLibTest:testSafeCastTo32() (gas: 516) -SafeCastLibTest:testSafeCastTo32(uint256) (runs: 256, μ: 2777, ~: 2777) -SafeCastLibTest:testSafeCastTo40() (gas: 517) -SafeCastLibTest:testSafeCastTo40(uint256) (runs: 256, μ: 2756, ~: 2756) -SafeCastLibTest:testSafeCastTo48() (gas: 469) -SafeCastLibTest:testSafeCastTo48(uint256) (runs: 256, μ: 2778, ~: 2778) -SafeCastLibTest:testSafeCastTo56() (gas: 470) -SafeCastLibTest:testSafeCastTo56(uint256) (runs: 256, μ: 2801, ~: 2801) -SafeCastLibTest:testSafeCastTo64() (gas: 537) -SafeCastLibTest:testSafeCastTo64(uint256) (runs: 256, μ: 2799, ~: 2799) -SafeCastLibTest:testSafeCastTo72(uint256) (runs: 256, μ: 2798, ~: 2798) -SafeCastLibTest:testSafeCastTo8() (gas: 513) -SafeCastLibTest:testSafeCastTo8(uint256) (runs: 256, μ: 2755, ~: 2755) -SafeCastLibTest:testSafeCastTo80(uint256) (runs: 256, μ: 2736, ~: 2736) -SafeCastLibTest:testSafeCastTo88(uint256) (runs: 256, μ: 2755, ~: 2755) -SafeCastLibTest:testSafeCastTo96() (gas: 536) -SafeCastLibTest:testSafeCastTo96(uint256) (runs: 256, μ: 2800, ~: 2800) -SafeTransferLibTest:testApproveWithGarbage(address,uint256,bytes,bytes) (runs: 256, μ: 3016, ~: 2231) -SafeTransferLibTest:testApproveWithMissingReturn() (gas: 30757) -SafeTransferLibTest:testApproveWithMissingReturn(address,uint256,bytes) (runs: 256, μ: 30800, ~: 31572) -SafeTransferLibTest:testApproveWithNonContract() (gas: 3041) -SafeTransferLibTest:testApproveWithNonContract(address,address,uint256,bytes) (runs: 256, μ: 4095, ~: 4123) -SafeTransferLibTest:testApproveWithReturnsTooMuch() (gas: 31140) -SafeTransferLibTest:testApproveWithReturnsTooMuch(address,uint256,bytes) (runs: 256, μ: 31268, ~: 32040) -SafeTransferLibTest:testApproveWithStandardERC20() (gas: 30888) -SafeTransferLibTest:testApproveWithStandardERC20(address,uint256,bytes) (runs: 256, μ: 30994, ~: 31766) -SafeTransferLibTest:testFailApproveWithGarbage(address,uint256,bytes,bytes) (runs: 256, μ: 85288, ~: 77915) -SafeTransferLibTest:testFailApproveWithReturnsFalse() (gas: 5633) -SafeTransferLibTest:testFailApproveWithReturnsFalse(address,uint256,bytes) (runs: 256, μ: 6486, ~: 6481) -SafeTransferLibTest:testFailApproveWithReturnsTooLittle() (gas: 5574) -SafeTransferLibTest:testFailApproveWithReturnsTooLittle(address,uint256,bytes) (runs: 256, μ: 6450, ~: 6445) -SafeTransferLibTest:testFailApproveWithReturnsTwo(address,uint256,bytes) (runs: 256, μ: 6458, ~: 6453) -SafeTransferLibTest:testFailApproveWithReverting() (gas: 5508) -SafeTransferLibTest:testFailApproveWithReverting(address,uint256,bytes) (runs: 256, μ: 6409, ~: 6404) -SafeTransferLibTest:testFailTransferETHToContractWithoutFallback() (gas: 7244) -SafeTransferLibTest:testFailTransferETHToContractWithoutFallback(uint256,bytes) (runs: 256, μ: 7758, ~: 8055) -SafeTransferLibTest:testFailTransferFromWithGarbage(address,address,uint256,bytes,bytes) (runs: 256, μ: 120098, ~: 117413) -SafeTransferLibTest:testFailTransferFromWithReturnsFalse() (gas: 13675) -SafeTransferLibTest:testFailTransferFromWithReturnsFalse(address,address,uint256,bytes) (runs: 256, μ: 14606, ~: 14600) -SafeTransferLibTest:testFailTransferFromWithReturnsTooLittle() (gas: 13556) -SafeTransferLibTest:testFailTransferFromWithReturnsTooLittle(address,address,uint256,bytes) (runs: 256, μ: 14465, ~: 14459) -SafeTransferLibTest:testFailTransferFromWithReturnsTwo(address,address,uint256,bytes) (runs: 256, μ: 14572, ~: 14566) -SafeTransferLibTest:testFailTransferFromWithReverting() (gas: 9757) -SafeTransferLibTest:testFailTransferFromWithReverting(address,address,uint256,bytes) (runs: 256, μ: 10686, ~: 10680) -SafeTransferLibTest:testFailTransferWithGarbage(address,uint256,bytes,bytes) (runs: 256, μ: 91079, ~: 83995) -SafeTransferLibTest:testFailTransferWithReturnsFalse() (gas: 8538) -SafeTransferLibTest:testFailTransferWithReturnsFalse(address,uint256,bytes) (runs: 256, μ: 9457, ~: 9452) -SafeTransferLibTest:testFailTransferWithReturnsTooLittle() (gas: 8544) -SafeTransferLibTest:testFailTransferWithReturnsTooLittle(address,uint256,bytes) (runs: 256, μ: 9397, ~: 9392) -SafeTransferLibTest:testFailTransferWithReturnsTwo(address,uint256,bytes) (runs: 256, μ: 9384, ~: 9379) -SafeTransferLibTest:testFailTransferWithReverting() (gas: 8500) -SafeTransferLibTest:testFailTransferWithReverting(address,uint256,bytes) (runs: 256, μ: 9356, ~: 9351) -SafeTransferLibTest:testTransferETH() (gas: 34592) -SafeTransferLibTest:testTransferETH(address,uint256,bytes) (runs: 256, μ: 35787, ~: 37975) -SafeTransferLibTest:testTransferFromWithGarbage(address,address,uint256,bytes,bytes) (runs: 256, μ: 3348, ~: 2253) -SafeTransferLibTest:testTransferFromWithMissingReturn() (gas: 49196) -SafeTransferLibTest:testTransferFromWithMissingReturn(address,address,uint256,bytes) (runs: 256, μ: 48997, ~: 49580) -SafeTransferLibTest:testTransferFromWithNonContract() (gas: 3047) -SafeTransferLibTest:testTransferFromWithNonContract(address,address,address,uint256,bytes) (runs: 256, μ: 4234, ~: 4240) -SafeTransferLibTest:testTransferFromWithReturnsTooMuch() (gas: 49820) -SafeTransferLibTest:testTransferFromWithReturnsTooMuch(address,address,uint256,bytes) (runs: 256, μ: 49640, ~: 50219) -SafeTransferLibTest:testTransferFromWithStandardERC20() (gas: 47612) -SafeTransferLibTest:testTransferFromWithStandardERC20(address,address,uint256,bytes) (runs: 256, μ: 47345, ~: 48031) -SafeTransferLibTest:testTransferWithGarbage(address,uint256,bytes,bytes) (runs: 256, μ: 3006, ~: 2187) -SafeTransferLibTest:testTransferWithMissingReturn() (gas: 36672) -SafeTransferLibTest:testTransferWithMissingReturn(address,uint256,bytes) (runs: 256, μ: 36539, ~: 37552) -SafeTransferLibTest:testTransferWithNonContract() (gas: 3018) -SafeTransferLibTest:testTransferWithNonContract(address,address,uint256,bytes) (runs: 256, μ: 4159, ~: 4187) -SafeTransferLibTest:testTransferWithReturnsTooMuch() (gas: 37118) -SafeTransferLibTest:testTransferWithReturnsTooMuch(address,uint256,bytes) (runs: 256, μ: 36942, ~: 37955) -SafeTransferLibTest:testTransferWithStandardERC20() (gas: 36702) -SafeTransferLibTest:testTransferWithStandardERC20(address,uint256,bytes) (runs: 256, μ: 36592, ~: 37605) -SignedWadMathTest:testFailWadDivOverflow(int256,int256) (runs: 256, μ: 347, ~: 329) -SignedWadMathTest:testFailWadDivZeroDenominator(int256) (runs: 256, μ: 296, ~: 296) -SignedWadMathTest:testFailWadMulEdgeCase() (gas: 286) -SignedWadMathTest:testFailWadMulEdgeCase2() (gas: 309) -SignedWadMathTest:testFailWadMulOverflow(int256,int256) (runs: 256, μ: 354, ~: 319) -SignedWadMathTest:testWadDiv(uint256,uint256,bool,bool) (runs: 256, μ: 5712, ~: 5714) -SignedWadMathTest:testWadMul(uint256,uint256,bool,bool) (runs: 256, μ: 5760, ~: 5762) -WETHInvariants:invariantTotalSupplyEqualsBalance() (runs: 256, calls: 3840, reverts: 1776) -WETHTest:testDeposit() (gas: 63535) -WETHTest:testDeposit(uint256) (runs: 256, μ: 63155, ~: 65880) -WETHTest:testFallbackDeposit() (gas: 63249) -WETHTest:testFallbackDeposit(uint256) (runs: 256, μ: 62879, ~: 65604) -WETHTest:testPartialWithdraw() (gas: 73281) -WETHTest:testWithdraw() (gas: 54360) -WETHTest:testWithdraw(uint256,uint256) (runs: 256, μ: 75313, ~: 78076) \ No newline at end of file diff --git a/lib/solmate/.gitattributes b/lib/solmate/.gitattributes deleted file mode 100644 index 7c5f3db..0000000 --- a/lib/solmate/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -.gas-snapshot linguist-language=Julia \ No newline at end of file diff --git a/lib/solmate/.github/pull_request_template.md b/lib/solmate/.github/pull_request_template.md deleted file mode 100644 index 5cca391..0000000 --- a/lib/solmate/.github/pull_request_template.md +++ /dev/null @@ -1,13 +0,0 @@ -## Description - -Describe the changes made in your pull request here. - -## Checklist - -Ensure you completed **all of the steps** below before submitting your pull request: - -- [ ] Ran `forge snapshot`? -- [ ] Ran `npm run lint`? -- [ ] Ran `forge test`? - -_Pull requests with an incomplete checklist will be thrown out._ diff --git a/lib/solmate/.github/workflows/tests.yml b/lib/solmate/.github/workflows/tests.yml deleted file mode 100644 index 2a29890..0000000 --- a/lib/solmate/.github/workflows/tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Tests - -on: [push, pull_request] - -jobs: - tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly - - - name: Install dependencies - run: forge install - - - name: Check contract sizes - run: forge build --sizes - - - name: Check gas snapshots - run: forge snapshot --check - - - name: Run tests - run: forge test - env: - # Only fuzz intensely if we're running this action on a push to main or for a PR going into main: - FOUNDRY_PROFILE: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'intense' }} diff --git a/lib/solmate/.gitignore b/lib/solmate/.gitignore deleted file mode 100644 index 5dfe93f..0000000 --- a/lib/solmate/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/cache -/node_modules -/out \ No newline at end of file diff --git a/lib/solmate/.gitmodules b/lib/solmate/.gitmodules deleted file mode 100644 index e124719..0000000 --- a/lib/solmate/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/ds-test"] - path = lib/ds-test - url = https://github.com/dapphub/ds-test diff --git a/lib/solmate/.prettierignore b/lib/solmate/.prettierignore deleted file mode 100644 index 7951405..0000000 --- a/lib/solmate/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -lib \ No newline at end of file diff --git a/lib/solmate/.prettierrc b/lib/solmate/.prettierrc deleted file mode 100644 index 15ae8a7..0000000 --- a/lib/solmate/.prettierrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tabWidth": 2, - "printWidth": 100, - - "overrides": [ - { - "files": "*.sol", - "options": { - "tabWidth": 4, - "printWidth": 120 - } - } - ] -} diff --git a/lib/solmate/.vscode/settings.json b/lib/solmate/.vscode/settings.json deleted file mode 100644 index f91a167..0000000 --- a/lib/solmate/.vscode/settings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "solidity.packageDefaultDependenciesContractsDirectory": "src", - "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.15", - "search.exclude": { "lib": true }, - "files.associations": { - ".gas-snapshot": "julia" - } -} diff --git a/lib/solmate/LICENSE b/lib/solmate/LICENSE deleted file mode 100644 index 29ebfa5..0000000 --- a/lib/solmate/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. \ No newline at end of file diff --git a/lib/solmate/README.md b/lib/solmate/README.md deleted file mode 100644 index 87fda96..0000000 --- a/lib/solmate/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# solmate - -**Modern**, **opinionated**, and **gas optimized** building blocks for **smart contract development**. - -## Contracts - -```ml -auth -├─ Owned — "Simple single owner authorization" -├─ Auth — "Flexible and updatable auth pattern" -├─ authorities -│ ├─ RolesAuthority — "Role based Authority that supports up to 256 roles" -│ ├─ MultiRolesAuthority — "Flexible and target agnostic role based Authority" -mixins -├─ ERC4626 — "Minimal ERC4626 tokenized Vault implementation" -tokens -├─ WETH — "Minimalist and modern Wrapped Ether implementation" -├─ ERC20 — "Modern and gas efficient ERC20 + EIP-2612 implementation" -├─ ERC721 — "Modern, minimalist, and gas efficient ERC721 implementation" -├─ ERC1155 — "Minimalist and gas efficient standard ERC1155 implementation" -├─ ERC6909 — "Minimalist and gas efficient standard ERC6909 implementation" -utils -├─ SSTORE2 — "Library for cheaper reads and writes to persistent storage" -├─ CREATE3 — "Deploy to deterministic addresses without an initcode factor" -├─ LibString — "Library for creating string representations of uint values" -├─ SafeCastLib — "Safe unsigned integer casting lib that reverts on overflow" -├─ SignedWadMath — "Signed integer 18 decimal fixed point arithmetic library" -├─ MerkleProofLib — "Efficient merkle tree inclusion proof verification library" -├─ ReentrancyGuard — "Gas optimized reentrancy protection for smart contracts" -├─ FixedPointMathLib — "Arithmetic library with operations for fixed-point numbers" -├─ Bytes32AddressLib — "Library for converting between addresses and bytes32 values" -├─ SafeTransferLib — "Safe ERC20/ETH transfer lib that handles missing return values" -``` - -## Safety - -This is **experimental software** and is provided on an "as is" and "as available" basis. - -While each [major release has been audited](audits), these contracts are **not designed with user safety** in mind: - -- There are implicit invariants these contracts expect to hold. -- **You can easily shoot yourself in the foot if you're not careful.** -- You should thoroughly read each contract you plan to use top to bottom. - -We **do not give any warranties** and **will not be liable for any loss** incurred through any use of this codebase. - -## Installation - -To install with [**Foundry**](https://github.com/gakonst/foundry): - -```sh -forge install transmissions11/solmate -``` - -To install with [**Hardhat**](https://github.com/nomiclabs/hardhat) or [**Truffle**](https://github.com/trufflesuite/truffle): - -```sh -npm install solmate -``` - -## Acknowledgements - -These contracts were inspired by or directly modified from many sources, primarily: - -- [Gnosis](https://github.com/gnosis/gp-v2-contracts) -- [Uniswap](https://github.com/Uniswap/uniswap-lib) -- [Dappsys](https://github.com/dapphub/dappsys) -- [Dappsys V2](https://github.com/dapp-org/dappsys-v2) -- [0xSequence](https://github.com/0xSequence) -- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts) diff --git a/lib/solmate/audits/v6-Fixed-Point-Solutions.pdf b/lib/solmate/audits/v6-Fixed-Point-Solutions.pdf deleted file mode 100644 index 5c4243425bf4fdaef1dcd87eceb2365ba97bd6f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170456 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 ?uKH_LBG3w}|gz7C!T-OPS2=2wL82qXf;9 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 diff --git a/lib/solmate/foundry.toml b/lib/solmate/foundry.toml deleted file mode 100644 index ebdfa11..0000000 --- a/lib/solmate/foundry.toml +++ /dev/null @@ -1,7 +0,0 @@ -[profile.default] -solc = "0.8.15" -bytecode_hash = "none" -optimizer_runs = 1000000 - -[profile.intense.fuzz] -runs = 10000 diff --git a/lib/solmate/lib/ds-test/.gitignore b/lib/solmate/lib/ds-test/.gitignore deleted file mode 100644 index 63f0b2c..0000000 --- a/lib/solmate/lib/ds-test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/.dapple -/build -/out diff --git a/lib/solmate/lib/ds-test/LICENSE b/lib/solmate/lib/ds-test/LICENSE deleted file mode 100644 index 94a9ed0..0000000 --- a/lib/solmate/lib/ds-test/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/lib/solmate/lib/ds-test/Makefile b/lib/solmate/lib/ds-test/Makefile deleted file mode 100644 index 661dac4..0000000 --- a/lib/solmate/lib/ds-test/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all:; dapp build - -test: - -dapp --use solc:0.4.23 build - -dapp --use solc:0.4.26 build - -dapp --use solc:0.5.17 build - -dapp --use solc:0.6.12 build - -dapp --use solc:0.7.5 build - -demo: - DAPP_SRC=demo dapp --use solc:0.7.5 build - -hevm dapp-test --verbose 3 - -.PHONY: test demo diff --git a/lib/solmate/lib/ds-test/default.nix b/lib/solmate/lib/ds-test/default.nix deleted file mode 100644 index cf65419..0000000 --- a/lib/solmate/lib/ds-test/default.nix +++ /dev/null @@ -1,4 +0,0 @@ -{ solidityPackage, dappsys }: solidityPackage { - name = "ds-test"; - src = ./src; -} diff --git a/lib/solmate/lib/ds-test/demo/demo.sol b/lib/solmate/lib/ds-test/demo/demo.sol deleted file mode 100644 index f3bb48e..0000000 --- a/lib/solmate/lib/ds-test/demo/demo.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.5.0; - -import "../src/test.sol"; - -contract DemoTest is DSTest { - function test_this() public pure { - require(true); - } - function test_logs() public { - emit log("-- log(string)"); - emit log("a string"); - - emit log("-- log_named_uint(string, uint)"); - emit log_named_uint("uint", 512); - - emit log("-- log_named_int(string, int)"); - emit log_named_int("int", -512); - - emit log("-- log_named_address(string, address)"); - emit log_named_address("address", address(this)); - - emit log("-- log_named_bytes32(string, bytes32)"); - emit log_named_bytes32("bytes32", "a string"); - - emit log("-- log_named_bytes(string, bytes)"); - emit log_named_bytes("bytes", hex"cafefe"); - - emit log("-- log_named_string(string, string)"); - emit log_named_string("string", "a string"); - - emit log("-- log_named_decimal_uint(string, uint, uint)"); - emit log_named_decimal_uint("decimal uint", 1.0e18, 18); - - emit log("-- log_named_decimal_int(string, int, uint)"); - emit log_named_decimal_int("decimal int", -1.0e18, 18); - } - event log_old_named_uint(bytes32,uint); - function test_old_logs() public { - emit log_old_named_uint("key", 500); - emit log_named_bytes32("bkey", "val"); - } - function test_trace() public view { - this.echo("string 1", "string 2"); - } - function test_multiline() public { - emit log("a multiline\\nstring"); - emit log("a multiline string"); - emit log_bytes("a string"); - emit log_bytes("a multiline\nstring"); - emit log_bytes("a multiline\\nstring"); - emit logs(hex"0000"); - emit log_named_bytes("0x0000", hex"0000"); - emit logs(hex"ff"); - } - function echo(string memory s1, string memory s2) public pure - returns (string memory, string memory) - { - return (s1, s2); - } - - function prove_this(uint x) public { - emit log_named_uint("sym x", x); - assertGt(x + 1, 0); - } - - function test_logn() public { - assembly { - log0(0x01, 0x02) - log1(0x01, 0x02, 0x03) - log2(0x01, 0x02, 0x03, 0x04) - log3(0x01, 0x02, 0x03, 0x04, 0x05) - } - } - - event MyEvent(uint, uint indexed, uint, uint indexed); - function test_events() public { - emit MyEvent(1, 2, 3, 4); - } - - function test_asserts() public { - string memory err = "this test has failed!"; - emit log("## assertTrue(bool)\n"); - assertTrue(false); - emit log("\n"); - assertTrue(false, err); - - emit log("\n## assertEq(address,address)\n"); - assertEq(address(this), msg.sender); - emit log("\n"); - assertEq(address(this), msg.sender, err); - - emit log("\n## assertEq32(bytes32,bytes32)\n"); - assertEq32("bytes 1", "bytes 2"); - emit log("\n"); - assertEq32("bytes 1", "bytes 2", err); - - emit log("\n## assertEq(bytes32,bytes32)\n"); - assertEq32("bytes 1", "bytes 2"); - emit log("\n"); - assertEq32("bytes 1", "bytes 2", err); - - emit log("\n## assertEq(uint,uint)\n"); - assertEq(uint(0), 1); - emit log("\n"); - assertEq(uint(0), 1, err); - - emit log("\n## assertEq(int,int)\n"); - assertEq(-1, -2); - emit log("\n"); - assertEq(-1, -2, err); - - emit log("\n## assertEqDecimal(int,int,uint)\n"); - assertEqDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertEqDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertEqDecimal(uint,uint,uint)\n"); - assertEqDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertEqDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertGt(uint,uint)\n"); - assertGt(uint(0), 0); - emit log("\n"); - assertGt(uint(0), 0, err); - - emit log("\n## assertGt(int,int)\n"); - assertGt(-1, -1); - emit log("\n"); - assertGt(-1, -1, err); - - emit log("\n## assertGtDecimal(int,int,uint)\n"); - assertGtDecimal(-2.0e18, -1.1e18, 18); - emit log("\n"); - assertGtDecimal(-2.0e18, -1.1e18, 18, err); - - emit log("\n## assertGtDecimal(uint,uint,uint)\n"); - assertGtDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertGtDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertGe(uint,uint)\n"); - assertGe(uint(0), 1); - emit log("\n"); - assertGe(uint(0), 1, err); - - emit log("\n## assertGe(int,int)\n"); - assertGe(-1, 0); - emit log("\n"); - assertGe(-1, 0, err); - - emit log("\n## assertGeDecimal(int,int,uint)\n"); - assertGeDecimal(-2.0e18, -1.1e18, 18); - emit log("\n"); - assertGeDecimal(-2.0e18, -1.1e18, 18, err); - - emit log("\n## assertGeDecimal(uint,uint,uint)\n"); - assertGeDecimal(uint(1.0e18), 1.1e18, 18); - emit log("\n"); - assertGeDecimal(uint(1.0e18), 1.1e18, 18, err); - - emit log("\n## assertLt(uint,uint)\n"); - assertLt(uint(0), 0); - emit log("\n"); - assertLt(uint(0), 0, err); - - emit log("\n## assertLt(int,int)\n"); - assertLt(-1, -1); - emit log("\n"); - assertLt(-1, -1, err); - - emit log("\n## assertLtDecimal(int,int,uint)\n"); - assertLtDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertLtDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertLtDecimal(uint,uint,uint)\n"); - assertLtDecimal(uint(2.0e18), 1.1e18, 18); - emit log("\n"); - assertLtDecimal(uint(2.0e18), 1.1e18, 18, err); - - emit log("\n## assertLe(uint,uint)\n"); - assertLe(uint(1), 0); - emit log("\n"); - assertLe(uint(1), 0, err); - - emit log("\n## assertLe(int,int)\n"); - assertLe(0, -1); - emit log("\n"); - assertLe(0, -1, err); - - emit log("\n## assertLeDecimal(int,int,uint)\n"); - assertLeDecimal(-1.0e18, -1.1e18, 18); - emit log("\n"); - assertLeDecimal(-1.0e18, -1.1e18, 18, err); - - emit log("\n## assertLeDecimal(uint,uint,uint)\n"); - assertLeDecimal(uint(2.0e18), 1.1e18, 18); - emit log("\n"); - assertLeDecimal(uint(2.0e18), 1.1e18, 18, err); - - emit log("\n## assertEq(string,string)\n"); - string memory s1 = "string 1"; - string memory s2 = "string 2"; - assertEq(s1, s2); - emit log("\n"); - assertEq(s1, s2, err); - - emit log("\n## assertEq0(bytes,bytes)\n"); - assertEq0(hex"abcdef01", hex"abcdef02"); - emit log("\n"); - assertEq0(hex"abcdef01", hex"abcdef02", err); - } -} - -contract DemoTestWithSetUp { - function setUp() public { - } - function test_pass() public pure { - } -} diff --git a/lib/solmate/lib/ds-test/package.json b/lib/solmate/lib/ds-test/package.json deleted file mode 100644 index 4802ada..0000000 --- a/lib/solmate/lib/ds-test/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "ds-test", - "version": "1.0.0", - "description": "Assertions, equality checks and other test helpers ", - "bugs": "https://github.com/dapphub/ds-test/issues", - "license": "GPL-3.0", - "author": "Contributors to ds-test", - "files": [ - "src/*" - ], - "repository": { - "type": "git", - "url": "https://github.com/dapphub/ds-test.git" - } -} diff --git a/lib/solmate/lib/ds-test/src/test.sol b/lib/solmate/lib/ds-test/src/test.sol deleted file mode 100644 index 515a3bd..0000000 --- a/lib/solmate/lib/ds-test/src/test.sol +++ /dev/null @@ -1,469 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -pragma solidity >=0.5.0; - -contract DSTest { - event log (string); - event logs (bytes); - - event log_address (address); - event log_bytes32 (bytes32); - event log_int (int); - event log_uint (uint); - event log_bytes (bytes); - event log_string (string); - - event log_named_address (string key, address val); - event log_named_bytes32 (string key, bytes32 val); - event log_named_decimal_int (string key, int val, uint decimals); - event log_named_decimal_uint (string key, uint val, uint decimals); - event log_named_int (string key, int val); - event log_named_uint (string key, uint val); - event log_named_bytes (string key, bytes val); - event log_named_string (string key, string val); - - bool public IS_TEST = true; - bool private _failed; - - address constant HEVM_ADDRESS = - address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); - - modifier mayRevert() { _; } - modifier testopts(string memory) { _; } - - function failed() public returns (bool) { - if (_failed) { - return _failed; - } else { - bool globalFailed = false; - if (hasHEVMContext()) { - (, bytes memory retdata) = HEVM_ADDRESS.call( - abi.encodePacked( - bytes4(keccak256("load(address,bytes32)")), - abi.encode(HEVM_ADDRESS, bytes32("failed")) - ) - ); - globalFailed = abi.decode(retdata, (bool)); - } - return globalFailed; - } - } - - function fail() internal { - if (hasHEVMContext()) { - (bool status, ) = HEVM_ADDRESS.call( - abi.encodePacked( - bytes4(keccak256("store(address,bytes32,bytes32)")), - abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) - ) - ); - status; // Silence compiler warnings - } - _failed = true; - } - - function hasHEVMContext() internal view returns (bool) { - uint256 hevmCodeSize = 0; - assembly { - hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) - } - return hevmCodeSize > 0; - } - - modifier logs_gas() { - uint startGas = gasleft(); - _; - uint endGas = gasleft(); - emit log_named_uint("gas", startGas - endGas); - } - - function assertTrue(bool condition) internal { - if (!condition) { - emit log("Error: Assertion Failed"); - fail(); - } - } - - function assertTrue(bool condition, string memory err) internal { - if (!condition) { - emit log_named_string("Error", err); - assertTrue(condition); - } - } - - function assertEq(address a, address b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [address]"); - emit log_named_address(" Expected", b); - emit log_named_address(" Actual", a); - fail(); - } - } - function assertEq(address a, address b, string memory err) internal { - if (a != b) { - emit log_named_string ("Error", err); - assertEq(a, b); - } - } - - function assertEq(bytes32 a, bytes32 b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [bytes32]"); - emit log_named_bytes32(" Expected", b); - emit log_named_bytes32(" Actual", a); - fail(); - } - } - function assertEq(bytes32 a, bytes32 b, string memory err) internal { - if (a != b) { - emit log_named_string ("Error", err); - assertEq(a, b); - } - } - function assertEq32(bytes32 a, bytes32 b) internal { - assertEq(a, b); - } - function assertEq32(bytes32 a, bytes32 b, string memory err) internal { - assertEq(a, b, err); - } - - function assertEq(int a, int b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [int]"); - emit log_named_int(" Expected", b); - emit log_named_int(" Actual", a); - fail(); - } - } - function assertEq(int a, int b, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - function assertEq(uint a, uint b) internal { - if (a != b) { - emit log("Error: a == b not satisfied [uint]"); - emit log_named_uint(" Expected", b); - emit log_named_uint(" Actual", a); - fail(); - } - } - function assertEq(uint a, uint b, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - function assertEqDecimal(int a, int b, uint decimals) internal { - if (a != b) { - emit log("Error: a == b not satisfied [decimal int]"); - emit log_named_decimal_int(" Expected", b, decimals); - emit log_named_decimal_int(" Actual", a, decimals); - fail(); - } - } - function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEqDecimal(a, b, decimals); - } - } - function assertEqDecimal(uint a, uint b, uint decimals) internal { - if (a != b) { - emit log("Error: a == b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Expected", b, decimals); - emit log_named_decimal_uint(" Actual", a, decimals); - fail(); - } - } - function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a != b) { - emit log_named_string("Error", err); - assertEqDecimal(a, b, decimals); - } - } - - function assertGt(uint a, uint b) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertGt(uint a, uint b, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGt(a, b); - } - } - function assertGt(int a, int b) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertGt(int a, int b, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGt(a, b); - } - } - function assertGtDecimal(int a, int b, uint decimals) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGtDecimal(a, b, decimals); - } - } - function assertGtDecimal(uint a, uint b, uint decimals) internal { - if (a <= b) { - emit log("Error: a > b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a <= b) { - emit log_named_string("Error", err); - assertGtDecimal(a, b, decimals); - } - } - - function assertGe(uint a, uint b) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertGe(uint a, uint b, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGe(a, b); - } - } - function assertGe(int a, int b) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertGe(int a, int b, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGe(a, b); - } - } - function assertGeDecimal(int a, int b, uint decimals) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - function assertGeDecimal(uint a, uint b, uint decimals) internal { - if (a < b) { - emit log("Error: a >= b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a < b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - - function assertLt(uint a, uint b) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertLt(uint a, uint b, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLt(a, b); - } - } - function assertLt(int a, int b) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertLt(int a, int b, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLt(a, b); - } - } - function assertLtDecimal(int a, int b, uint decimals) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLtDecimal(a, b, decimals); - } - } - function assertLtDecimal(uint a, uint b, uint decimals) internal { - if (a >= b) { - emit log("Error: a < b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a >= b) { - emit log_named_string("Error", err); - assertLtDecimal(a, b, decimals); - } - } - - function assertLe(uint a, uint b) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [uint]"); - emit log_named_uint(" Value a", a); - emit log_named_uint(" Value b", b); - fail(); - } - } - function assertLe(uint a, uint b, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLe(a, b); - } - } - function assertLe(int a, int b) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [int]"); - emit log_named_int(" Value a", a); - emit log_named_int(" Value b", b); - fail(); - } - } - function assertLe(int a, int b, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLe(a, b); - } - } - function assertLeDecimal(int a, int b, uint decimals) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [decimal int]"); - emit log_named_decimal_int(" Value a", a, decimals); - emit log_named_decimal_int(" Value b", b, decimals); - fail(); - } - } - function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertLeDecimal(a, b, decimals); - } - } - function assertLeDecimal(uint a, uint b, uint decimals) internal { - if (a > b) { - emit log("Error: a <= b not satisfied [decimal uint]"); - emit log_named_decimal_uint(" Value a", a, decimals); - emit log_named_decimal_uint(" Value b", b, decimals); - fail(); - } - } - function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { - if (a > b) { - emit log_named_string("Error", err); - assertGeDecimal(a, b, decimals); - } - } - - function assertEq(string memory a, string memory b) internal { - if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { - emit log("Error: a == b not satisfied [string]"); - emit log_named_string(" Expected", b); - emit log_named_string(" Actual", a); - fail(); - } - } - function assertEq(string memory a, string memory b, string memory err) internal { - if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { - emit log_named_string("Error", err); - assertEq(a, b); - } - } - - function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { - ok = true; - if (a.length == b.length) { - for (uint i = 0; i < a.length; i++) { - if (a[i] != b[i]) { - ok = false; - } - } - } else { - ok = false; - } - } - function assertEq0(bytes memory a, bytes memory b) internal { - if (!checkEq0(a, b)) { - emit log("Error: a == b not satisfied [bytes]"); - emit log_named_bytes(" Expected", b); - emit log_named_bytes(" Actual", a); - fail(); - } - } - function assertEq0(bytes memory a, bytes memory b, string memory err) internal { - if (!checkEq0(a, b)) { - emit log_named_string("Error", err); - assertEq0(a, b); - } - } -} diff --git a/lib/solmate/package-lock.json b/lib/solmate/package-lock.json deleted file mode 100644 index b99f283..0000000 --- a/lib/solmate/package-lock.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "name": "solmate", - "version": "6.2.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@solidity-parser/parser": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.13.2.tgz", - "integrity": "sha512-RwHnpRnfrnD2MSPveYoPh8nhofEvX7fgjHk1Oq+NNvCcLx4r1js91CO9o+F/F3fBzOCyvm8kKRTriFICX/odWw==", - "dev": true, - "requires": { - "antlr4ts": "^0.5.0-alpha.4" - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "antlr4ts": { - "version": "0.5.0-alpha.4", - "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", - "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "prettier": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", - "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", - "dev": true - }, - "prettier-plugin-solidity": { - "version": "1.0.0-beta.16", - "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.16.tgz", - "integrity": "sha512-xVBcnoWpe52dNnCCbqPHC9ZrTWXcNfldf852ZD0DBcHDqVMwjHTAPEdfBVy6FczbFpVa8bmxQil+G5XkEz5WHA==", - "dev": true, - "requires": { - "@solidity-parser/parser": "^0.13.2", - "emoji-regex": "^9.2.2", - "escape-string-regexp": "^4.0.0", - "semver": "^7.3.5", - "solidity-comments-extractor": "^0.0.7", - "string-width": "^4.2.2" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "solidity-comments-extractor": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", - "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - } - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } -} diff --git a/lib/solmate/package.json b/lib/solmate/package.json deleted file mode 100644 index e94cbdf..0000000 --- a/lib/solmate/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "solmate", - "license": "AGPL-3.0-only", - "version": "6.2.0", - "description": "Modern, opinionated and gas optimized building blocks for smart contract development.", - "files": [ - "src/**/*.sol" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/transmissions11/solmate.git" - }, - "devDependencies": { - "prettier": "^2.3.1", - "prettier-plugin-solidity": "^1.0.0-beta.13" - }, - "scripts": { - "lint": "prettier --write **.sol" - } -} diff --git a/lib/solmate/src/auth/Auth.sol b/lib/solmate/src/auth/Auth.sol deleted file mode 100644 index 3807ccd..0000000 --- a/lib/solmate/src/auth/Auth.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) -abstract contract Auth { - event OwnershipTransferred(address indexed user, address indexed newOwner); - - event AuthorityUpdated(address indexed user, Authority indexed newAuthority); - - address public owner; - - Authority public authority; - - constructor(address _owner, Authority _authority) { - owner = _owner; - authority = _authority; - - emit OwnershipTransferred(msg.sender, _owner); - emit AuthorityUpdated(msg.sender, _authority); - } - - modifier requiresAuth() virtual { - require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED"); - - _; - } - - function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { - Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. - - // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be - // aware that this makes protected functions uncallable even to the owner if the authority is out of order. - return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner; - } - - function setAuthority(Authority newAuthority) public virtual { - // We check if the caller is the owner first because we want to ensure they can - // always swap out the authority even if it's reverting or using up a lot of gas. - require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig)); - - authority = newAuthority; - - emit AuthorityUpdated(msg.sender, newAuthority); - } - - function transferOwnership(address newOwner) public virtual requiresAuth { - owner = newOwner; - - emit OwnershipTransferred(msg.sender, newOwner); - } -} - -/// @notice A generic interface for a contract which provides authorization data to an Auth instance. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) -interface Authority { - function canCall( - address user, - address target, - bytes4 functionSig - ) external view returns (bool); -} diff --git a/lib/solmate/src/auth/Owned.sol b/lib/solmate/src/auth/Owned.sol deleted file mode 100644 index e82b44d..0000000 --- a/lib/solmate/src/auth/Owned.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Simple single owner authorization mixin. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) -abstract contract Owned { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event OwnershipTransferred(address indexed user, address indexed newOwner); - - /*////////////////////////////////////////////////////////////// - OWNERSHIP STORAGE - //////////////////////////////////////////////////////////////*/ - - address public owner; - - modifier onlyOwner() virtual { - require(msg.sender == owner, "UNAUTHORIZED"); - - _; - } - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner) { - owner = _owner; - - emit OwnershipTransferred(address(0), _owner); - } - - /*////////////////////////////////////////////////////////////// - OWNERSHIP LOGIC - //////////////////////////////////////////////////////////////*/ - - function transferOwnership(address newOwner) public virtual onlyOwner { - owner = newOwner; - - emit OwnershipTransferred(msg.sender, newOwner); - } -} diff --git a/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol b/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol deleted file mode 100644 index 3ff80bb..0000000 --- a/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Auth, Authority} from "../Auth.sol"; - -/// @notice Flexible and target agnostic role based Authority that supports up to 256 roles. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/authorities/MultiRolesAuthority.sol) -contract MultiRolesAuthority is Auth, Authority { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); - - event PublicCapabilityUpdated(bytes4 indexed functionSig, bool enabled); - - event RoleCapabilityUpdated(uint8 indexed role, bytes4 indexed functionSig, bool enabled); - - event TargetCustomAuthorityUpdated(address indexed target, Authority indexed authority); - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} - - /*////////////////////////////////////////////////////////////// - CUSTOM TARGET AUTHORITY STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => Authority) public getTargetCustomAuthority; - - /*////////////////////////////////////////////////////////////// - ROLE/USER STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => bytes32) public getUserRoles; - - mapping(bytes4 => bool) public isCapabilityPublic; - - mapping(bytes4 => bytes32) public getRolesWithCapability; - - function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { - return (uint256(getUserRoles[user]) >> role) & 1 != 0; - } - - function doesRoleHaveCapability(uint8 role, bytes4 functionSig) public view virtual returns (bool) { - return (uint256(getRolesWithCapability[functionSig]) >> role) & 1 != 0; - } - - /*////////////////////////////////////////////////////////////// - AUTHORIZATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function canCall( - address user, - address target, - bytes4 functionSig - ) public view virtual override returns (bool) { - Authority customAuthority = getTargetCustomAuthority[target]; - - if (address(customAuthority) != address(0)) return customAuthority.canCall(user, target, functionSig); - - return - isCapabilityPublic[functionSig] || bytes32(0) != getUserRoles[user] & getRolesWithCapability[functionSig]; - } - - /*/////////////////////////////////////////////////////////////// - CUSTOM TARGET AUTHORITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setTargetCustomAuthority(address target, Authority customAuthority) public virtual requiresAuth { - getTargetCustomAuthority[target] = customAuthority; - - emit TargetCustomAuthorityUpdated(target, customAuthority); - } - - /*////////////////////////////////////////////////////////////// - PUBLIC CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setPublicCapability(bytes4 functionSig, bool enabled) public virtual requiresAuth { - isCapabilityPublic[functionSig] = enabled; - - emit PublicCapabilityUpdated(functionSig, enabled); - } - - /*////////////////////////////////////////////////////////////// - USER ROLE ASSIGNMENT LOGIC - //////////////////////////////////////////////////////////////*/ - - function setUserRole( - address user, - uint8 role, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getUserRoles[user] |= bytes32(1 << role); - } else { - getUserRoles[user] &= ~bytes32(1 << role); - } - - emit UserRoleUpdated(user, role, enabled); - } - - /*////////////////////////////////////////////////////////////// - ROLE CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setRoleCapability( - uint8 role, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getRolesWithCapability[functionSig] |= bytes32(1 << role); - } else { - getRolesWithCapability[functionSig] &= ~bytes32(1 << role); - } - - emit RoleCapabilityUpdated(role, functionSig, enabled); - } -} diff --git a/lib/solmate/src/auth/authorities/RolesAuthority.sol b/lib/solmate/src/auth/authorities/RolesAuthority.sol deleted file mode 100644 index aa5cc71..0000000 --- a/lib/solmate/src/auth/authorities/RolesAuthority.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Auth, Authority} from "../Auth.sol"; - -/// @notice Role based Authority that supports up to 256 roles. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/authorities/RolesAuthority.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol) -contract RolesAuthority is Auth, Authority { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); - - event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled); - - event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled); - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} - - /*////////////////////////////////////////////////////////////// - ROLE/USER STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => bytes32) public getUserRoles; - - mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic; - - mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability; - - function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { - return (uint256(getUserRoles[user]) >> role) & 1 != 0; - } - - function doesRoleHaveCapability( - uint8 role, - address target, - bytes4 functionSig - ) public view virtual returns (bool) { - return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0; - } - - /*////////////////////////////////////////////////////////////// - AUTHORIZATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function canCall( - address user, - address target, - bytes4 functionSig - ) public view virtual override returns (bool) { - return - isCapabilityPublic[target][functionSig] || - bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig]; - } - - /*////////////////////////////////////////////////////////////// - ROLE CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setPublicCapability( - address target, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - isCapabilityPublic[target][functionSig] = enabled; - - emit PublicCapabilityUpdated(target, functionSig, enabled); - } - - function setRoleCapability( - uint8 role, - address target, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getRolesWithCapability[target][functionSig] |= bytes32(1 << role); - } else { - getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role); - } - - emit RoleCapabilityUpdated(role, target, functionSig, enabled); - } - - /*////////////////////////////////////////////////////////////// - USER ROLE ASSIGNMENT LOGIC - //////////////////////////////////////////////////////////////*/ - - function setUserRole( - address user, - uint8 role, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getUserRoles[user] |= bytes32(1 << role); - } else { - getUserRoles[user] &= ~bytes32(1 << role); - } - - emit UserRoleUpdated(user, role, enabled); - } -} diff --git a/lib/solmate/src/mixins/ERC4626.sol b/lib/solmate/src/mixins/ERC4626.sol deleted file mode 100644 index af56c15..0000000 --- a/lib/solmate/src/mixins/ERC4626.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../tokens/ERC20.sol"; -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; -import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; - -/// @notice Minimal ERC4626 tokenized Vault implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol) -abstract contract ERC4626 is ERC20 { - using SafeTransferLib for ERC20; - using FixedPointMathLib for uint256; - - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); - - event Withdraw( - address indexed caller, - address indexed receiver, - address indexed owner, - uint256 assets, - uint256 shares - ); - - /*////////////////////////////////////////////////////////////// - IMMUTABLES - //////////////////////////////////////////////////////////////*/ - - ERC20 public immutable asset; - - constructor( - ERC20 _asset, - string memory _name, - string memory _symbol - ) ERC20(_name, _symbol, _asset.decimals()) { - asset = _asset; - } - - /*////////////////////////////////////////////////////////////// - DEPOSIT/WITHDRAWAL LOGIC - //////////////////////////////////////////////////////////////*/ - - function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { - // Check for rounding error since we round down in previewDeposit. - require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); - - // Need to transfer before minting or ERC777s could reenter. - asset.safeTransferFrom(msg.sender, address(this), assets); - - _mint(receiver, shares); - - emit Deposit(msg.sender, receiver, assets, shares); - - afterDeposit(assets, shares); - } - - function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { - assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. - - // Need to transfer before minting or ERC777s could reenter. - asset.safeTransferFrom(msg.sender, address(this), assets); - - _mint(receiver, shares); - - emit Deposit(msg.sender, receiver, assets, shares); - - afterDeposit(assets, shares); - } - - function withdraw( - uint256 assets, - address receiver, - address owner - ) public virtual returns (uint256 shares) { - shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. - - if (msg.sender != owner) { - uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; - } - - beforeWithdraw(assets, shares); - - _burn(owner, shares); - - emit Withdraw(msg.sender, receiver, owner, assets, shares); - - asset.safeTransfer(receiver, assets); - } - - function redeem( - uint256 shares, - address receiver, - address owner - ) public virtual returns (uint256 assets) { - if (msg.sender != owner) { - uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; - } - - // Check for rounding error since we round down in previewRedeem. - require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); - - beforeWithdraw(assets, shares); - - _burn(owner, shares); - - emit Withdraw(msg.sender, receiver, owner, assets, shares); - - asset.safeTransfer(receiver, assets); - } - - /*////////////////////////////////////////////////////////////// - ACCOUNTING LOGIC - //////////////////////////////////////////////////////////////*/ - - function totalAssets() public view virtual returns (uint256); - - function convertToShares(uint256 assets) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); - } - - function convertToAssets(uint256 shares) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); - } - - function previewDeposit(uint256 assets) public view virtual returns (uint256) { - return convertToShares(assets); - } - - function previewMint(uint256 shares) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); - } - - function previewWithdraw(uint256 assets) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); - } - - function previewRedeem(uint256 shares) public view virtual returns (uint256) { - return convertToAssets(shares); - } - - /*////////////////////////////////////////////////////////////// - DEPOSIT/WITHDRAWAL LIMIT LOGIC - //////////////////////////////////////////////////////////////*/ - - function maxDeposit(address) public view virtual returns (uint256) { - return type(uint256).max; - } - - function maxMint(address) public view virtual returns (uint256) { - return type(uint256).max; - } - - function maxWithdraw(address owner) public view virtual returns (uint256) { - return convertToAssets(balanceOf[owner]); - } - - function maxRedeem(address owner) public view virtual returns (uint256) { - return balanceOf[owner]; - } - - /*////////////////////////////////////////////////////////////// - INTERNAL HOOKS LOGIC - //////////////////////////////////////////////////////////////*/ - - function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} - - function afterDeposit(uint256 assets, uint256 shares) internal virtual {} -} diff --git a/lib/solmate/src/test/Auth.t.sol b/lib/solmate/src/test/Auth.t.sol deleted file mode 100644 index 9e31528..0000000 --- a/lib/solmate/src/test/Auth.t.sol +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {MockAuthChild} from "./utils/mocks/MockAuthChild.sol"; -import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; - -import {Authority} from "../auth/Auth.sol"; - -contract OutOfOrderAuthority is Authority { - function canCall( - address, - address, - bytes4 - ) public pure override returns (bool) { - revert("OUT_OF_ORDER"); - } -} - -contract AuthTest is DSTestPlus { - MockAuthChild mockAuthChild; - - function setUp() public { - mockAuthChild = new MockAuthChild(); - } - - function testTransferOwnershipAsOwner() public { - mockAuthChild.transferOwnership(address(0xBEEF)); - assertEq(mockAuthChild.owner(), address(0xBEEF)); - } - - function testSetAuthorityAsOwner() public { - mockAuthChild.setAuthority(Authority(address(0xBEEF))); - assertEq(address(mockAuthChild.authority()), address(0xBEEF)); - } - - function testCallFunctionAsOwner() public { - mockAuthChild.updateFlag(); - } - - function testTransferOwnershipWithPermissiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.transferOwnership(address(this)); - } - - function testSetAuthorityWithPermissiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.setAuthority(Authority(address(0xBEEF))); - } - - function testCallFunctionWithPermissiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.updateFlag(); - } - - function testSetAuthorityAsOwnerWithOutOfOrderAuthority() public { - mockAuthChild.setAuthority(new OutOfOrderAuthority()); - mockAuthChild.setAuthority(new MockAuthority(true)); - } - - function testFailTransferOwnershipAsNonOwner() public { - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.transferOwnership(address(0xBEEF)); - } - - function testFailSetAuthorityAsNonOwner() public { - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.setAuthority(Authority(address(0xBEEF))); - } - - function testFailCallFunctionAsNonOwner() public { - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.updateFlag(); - } - - function testFailTransferOwnershipWithRestrictiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.transferOwnership(address(this)); - } - - function testFailSetAuthorityWithRestrictiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.setAuthority(Authority(address(0xBEEF))); - } - - function testFailCallFunctionWithRestrictiveAuthority() public { - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(address(0)); - mockAuthChild.updateFlag(); - } - - function testFailTransferOwnershipAsOwnerWithOutOfOrderAuthority() public { - mockAuthChild.setAuthority(new OutOfOrderAuthority()); - mockAuthChild.transferOwnership(address(0)); - } - - function testFailCallFunctionAsOwnerWithOutOfOrderAuthority() public { - mockAuthChild.setAuthority(new OutOfOrderAuthority()); - mockAuthChild.updateFlag(); - } - - function testTransferOwnershipAsOwner(address newOwner) public { - mockAuthChild.transferOwnership(newOwner); - assertEq(mockAuthChild.owner(), newOwner); - } - - function testSetAuthorityAsOwner(Authority newAuthority) public { - mockAuthChild.setAuthority(newAuthority); - assertEq(address(mockAuthChild.authority()), address(newAuthority)); - } - - function testTransferOwnershipWithPermissiveAuthority(address deadOwner, address newOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.transferOwnership(newOwner); - } - - function testSetAuthorityWithPermissiveAuthority(address deadOwner, Authority newAuthority) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.setAuthority(newAuthority); - } - - function testCallFunctionWithPermissiveAuthority(address deadOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(true)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.updateFlag(); - } - - function testFailTransferOwnershipAsNonOwner(address deadOwner, address newOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.transferOwnership(newOwner); - } - - function testFailSetAuthorityAsNonOwner(address deadOwner, Authority newAuthority) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.setAuthority(newAuthority); - } - - function testFailCallFunctionAsNonOwner(address deadOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.updateFlag(); - } - - function testFailTransferOwnershipWithRestrictiveAuthority(address deadOwner, address newOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.transferOwnership(newOwner); - } - - function testFailSetAuthorityWithRestrictiveAuthority(address deadOwner, Authority newAuthority) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.setAuthority(newAuthority); - } - - function testFailCallFunctionWithRestrictiveAuthority(address deadOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new MockAuthority(false)); - mockAuthChild.transferOwnership(deadOwner); - mockAuthChild.updateFlag(); - } - - function testFailTransferOwnershipAsOwnerWithOutOfOrderAuthority(address deadOwner) public { - if (deadOwner == address(this)) deadOwner = address(0); - - mockAuthChild.setAuthority(new OutOfOrderAuthority()); - mockAuthChild.transferOwnership(deadOwner); - } -} diff --git a/lib/solmate/src/test/Bytes32AddressLib.t.sol b/lib/solmate/src/test/Bytes32AddressLib.t.sol deleted file mode 100644 index 6c0a3d8..0000000 --- a/lib/solmate/src/test/Bytes32AddressLib.t.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {Bytes32AddressLib} from "../utils/Bytes32AddressLib.sol"; - -contract Bytes32AddressLibTest is DSTestPlus { - function testFillLast12Bytes() public { - assertEq( - Bytes32AddressLib.fillLast12Bytes(0xfEEDFaCEcaFeBEEFfEEDFACecaFEBeeFfeEdfAce), - 0xfeedfacecafebeeffeedfacecafebeeffeedface000000000000000000000000 - ); - } - - function testFromLast20Bytes() public { - assertEq( - Bytes32AddressLib.fromLast20Bytes(0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef), - 0xCAfeBeefFeedfAceCAFeBEEffEEDfaCecafEBeeF - ); - } -} diff --git a/lib/solmate/src/test/CREATE3.t.sol b/lib/solmate/src/test/CREATE3.t.sol deleted file mode 100644 index 7ee8fc0..0000000 --- a/lib/solmate/src/test/CREATE3.t.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {WETH} from "../tokens/WETH.sol"; -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {MockERC20} from "./utils/mocks/MockERC20.sol"; -import {MockAuthChild} from "./utils/mocks/MockAuthChild.sol"; - -import {CREATE3} from "../utils/CREATE3.sol"; - -contract Factory { - function deploy(bytes32 salt) public returns (address deployed) { - deployed = CREATE3.deploy( - salt, - abi.encodePacked(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)), - 0 - ); - } -} - -contract CREATE3Test is DSTestPlus { - function testDeployERC20() public { - bytes32 salt = keccak256(bytes("A salt!")); - MockERC20 deployed = MockERC20( - CREATE3.deploy( - salt, - abi.encodePacked(type(MockERC20).creationCode, abi.encode("Mock Token", "MOCK", 18)), - 0 - ) - ); - - assertEq(address(deployed), CREATE3.getDeployed(salt)); - - assertEq(deployed.name(), "Mock Token"); - assertEq(deployed.symbol(), "MOCK"); - assertEq(deployed.decimals(), 18); - } - - function testPredictDeployERC20() public { - bytes32 salt = keccak256(bytes("A salt!")); - Factory factory = new Factory(); - - MockERC20 deployed = MockERC20( - factory.deploy(salt) - ); - - assertEq(address(deployed), CREATE3.getDeployed(salt, address(factory))); - assertTrue(address(deployed) != CREATE3.getDeployed(salt)); - - assertEq(deployed.name(), "Mock Token"); - assertEq(deployed.symbol(), "MOCK"); - assertEq(deployed.decimals(), 18); - } - - function testFailDoubleDeploySameBytecode() public { - bytes32 salt = keccak256(bytes("Salty...")); - - CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); - CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); - } - - function testFailDoubleDeployDifferentBytecode() public { - bytes32 salt = keccak256(bytes("and sweet!")); - - CREATE3.deploy(salt, type(WETH).creationCode, 0); - CREATE3.deploy(salt, type(MockAuthChild).creationCode, 0); - } - - function testDeployERC20( - bytes32 salt, - string calldata name, - string calldata symbol, - uint8 decimals - ) public { - MockERC20 deployed = MockERC20( - CREATE3.deploy(salt, abi.encodePacked(type(MockERC20).creationCode, abi.encode(name, symbol, decimals)), 0) - ); - - assertEq(address(deployed), CREATE3.getDeployed(salt)); - - assertEq(deployed.name(), name); - assertEq(deployed.symbol(), symbol); - assertEq(deployed.decimals(), decimals); - } - - function testFailDoubleDeploySameBytecode(bytes32 salt, bytes calldata bytecode) public { - CREATE3.deploy(salt, bytecode, 0); - CREATE3.deploy(salt, bytecode, 0); - } - - function testFailDoubleDeployDifferentBytecode( - bytes32 salt, - bytes calldata bytecode1, - bytes calldata bytecode2 - ) public { - CREATE3.deploy(salt, bytecode1, 0); - CREATE3.deploy(salt, bytecode2, 0); - } -} diff --git a/lib/solmate/src/test/DSTestPlus.t.sol b/lib/solmate/src/test/DSTestPlus.t.sol deleted file mode 100644 index db10860..0000000 --- a/lib/solmate/src/test/DSTestPlus.t.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -contract DSTestPlusTest is DSTestPlus { - function testBound() public { - assertEq(bound(0, 69, 69), 69); - assertEq(bound(0, 68, 69), 68); - assertEq(bound(5, 0, 4), 0); - assertEq(bound(9999, 1337, 6666), 6006); - assertEq(bound(0, type(uint256).max - 6, type(uint256).max), type(uint256).max - 6); - assertEq(bound(6, type(uint256).max - 6, type(uint256).max), type(uint256).max); - } - - function testFailBoundMinBiggerThanMax() public { - bound(5, 100, 10); - } - - function testRelApproxEqBothZeroesPasses() public { - assertRelApproxEq(0, 0, 1e18); - assertRelApproxEq(0, 0, 0); - } - - function testBound( - uint256 num, - uint256 min, - uint256 max - ) public { - if (min > max) (min, max) = (max, min); - - uint256 bounded = bound(num, min, max); - - assertGe(bounded, min); - assertLe(bounded, max); - } - - function testFailBoundMinBiggerThanMax( - uint256 num, - uint256 min, - uint256 max - ) public { - if (max == min) { - unchecked { - min++; // Overflow is handled below. - } - } - - if (max > min) (min, max) = (max, min); - - bound(num, min, max); - } - - function testBrutalizeMemory() public brutalizeMemory("FEEDFACECAFEBEEFFEEDFACECAFEBEEF") { - bytes32 scratchSpace1; - bytes32 scratchSpace2; - bytes32 freeMem1; - bytes32 freeMem2; - - assembly { - scratchSpace1 := mload(0) - scratchSpace2 := mload(32) - freeMem1 := mload(mload(0x40)) - freeMem2 := mload(add(mload(0x40), 32)) - } - - assertGt(uint256(freeMem1), 0); - assertGt(uint256(freeMem2), 0); - assertGt(uint256(scratchSpace1), 0); - assertGt(uint256(scratchSpace2), 0); - } -} diff --git a/lib/solmate/src/test/ERC1155.t.sol b/lib/solmate/src/test/ERC1155.t.sol deleted file mode 100644 index 9e32d88..0000000 --- a/lib/solmate/src/test/ERC1155.t.sol +++ /dev/null @@ -1,1777 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; - -import {MockERC1155} from "./utils/mocks/MockERC1155.sol"; - -import {ERC1155TokenReceiver} from "../tokens/ERC1155.sol"; - -contract ERC1155Recipient is ERC1155TokenReceiver { - address public operator; - address public from; - uint256 public id; - uint256 public amount; - bytes public mintData; - - function onERC1155Received( - address _operator, - address _from, - uint256 _id, - uint256 _amount, - bytes calldata _data - ) public override returns (bytes4) { - operator = _operator; - from = _from; - id = _id; - amount = _amount; - mintData = _data; - - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - address public batchOperator; - address public batchFrom; - uint256[] internal _batchIds; - uint256[] internal _batchAmounts; - bytes public batchData; - - function batchIds() external view returns (uint256[] memory) { - return _batchIds; - } - - function batchAmounts() external view returns (uint256[] memory) { - return _batchAmounts; - } - - function onERC1155BatchReceived( - address _operator, - address _from, - uint256[] calldata _ids, - uint256[] calldata _amounts, - bytes calldata _data - ) external override returns (bytes4) { - batchOperator = _operator; - batchFrom = _from; - _batchIds = _ids; - _batchAmounts = _amounts; - batchData = _data; - - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } -} - -contract RevertingERC1155Recipient is ERC1155TokenReceiver { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) public pure override returns (bytes4) { - revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155Received.selector))); - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - revert(string(abi.encodePacked(ERC1155TokenReceiver.onERC1155BatchReceived.selector))); - } -} - -contract WrongReturnDataERC1155Recipient is ERC1155TokenReceiver { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) public pure override returns (bytes4) { - return 0xCAFEBEEF; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - return 0xCAFEBEEF; - } -} - -contract NonERC1155Recipient {} - -contract ERC1155Test is DSTestPlus, ERC1155TokenReceiver { - MockERC1155 token; - - mapping(address => mapping(uint256 => uint256)) public userMintAmounts; - mapping(address => mapping(uint256 => uint256)) public userTransferOrBurnAmounts; - - function setUp() public { - token = new MockERC1155(); - } - - function testMintToEOA() public { - token.mint(address(0xBEEF), 1337, 1, ""); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 1); - } - - function testMintToERC1155Recipient() public { - ERC1155Recipient to = new ERC1155Recipient(); - - token.mint(address(to), 1337, 1, "testing 123"); - - assertEq(token.balanceOf(address(to), 1337), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), 1337); - assertBytesEq(to.mintData(), "testing 123"); - } - - function testBatchMintToEOA() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory amounts = new uint256[](5); - amounts[0] = 100; - amounts[1] = 200; - amounts[2] = 300; - amounts[3] = 400; - amounts[4] = 500; - - token.batchMint(address(0xBEEF), ids, amounts, ""); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 100); - assertEq(token.balanceOf(address(0xBEEF), 1338), 200); - assertEq(token.balanceOf(address(0xBEEF), 1339), 300); - assertEq(token.balanceOf(address(0xBEEF), 1340), 400); - assertEq(token.balanceOf(address(0xBEEF), 1341), 500); - } - - function testBatchMintToERC1155Recipient() public { - ERC1155Recipient to = new ERC1155Recipient(); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory amounts = new uint256[](5); - amounts[0] = 100; - amounts[1] = 200; - amounts[2] = 300; - amounts[3] = 400; - amounts[4] = 500; - - token.batchMint(address(to), ids, amounts, "testing 123"); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), address(0)); - assertUintArrayEq(to.batchIds(), ids); - assertUintArrayEq(to.batchAmounts(), amounts); - assertBytesEq(to.batchData(), "testing 123"); - - assertEq(token.balanceOf(address(to), 1337), 100); - assertEq(token.balanceOf(address(to), 1338), 200); - assertEq(token.balanceOf(address(to), 1339), 300); - assertEq(token.balanceOf(address(to), 1340), 400); - assertEq(token.balanceOf(address(to), 1341), 500); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1337, 100, ""); - - token.burn(address(0xBEEF), 1337, 70); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 30); - } - - function testBatchBurn() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory burnAmounts = new uint256[](5); - burnAmounts[0] = 50; - burnAmounts[1] = 100; - burnAmounts[2] = 150; - burnAmounts[3] = 200; - burnAmounts[4] = 250; - - token.batchMint(address(0xBEEF), ids, mintAmounts, ""); - - token.batchBurn(address(0xBEEF), ids, burnAmounts); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 50); - assertEq(token.balanceOf(address(0xBEEF), 1338), 100); - assertEq(token.balanceOf(address(0xBEEF), 1339), 150); - assertEq(token.balanceOf(address(0xBEEF), 1340), 200); - assertEq(token.balanceOf(address(0xBEEF), 1341), 250); - } - - function testApproveAll() public { - token.setApprovalForAll(address(0xBEEF), true); - - assertTrue(token.isApprovedForAll(address(this), address(0xBEEF))); - } - - function testSafeTransferFromToEOA() public { - address from = address(0xABCD); - - token.mint(from, 1337, 100, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(0xBEEF), 1337, 70, ""); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 70); - assertEq(token.balanceOf(from, 1337), 30); - } - - function testSafeTransferFromToERC1155Recipient() public { - ERC1155Recipient to = new ERC1155Recipient(); - - address from = address(0xABCD); - - token.mint(from, 1337, 100, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(to), 1337, 70, "testing 123"); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), from); - assertEq(to.id(), 1337); - assertBytesEq(to.mintData(), "testing 123"); - - assertEq(token.balanceOf(address(to), 1337), 70); - assertEq(token.balanceOf(from, 1337), 30); - } - - function testSafeTransferFromSelf() public { - token.mint(address(this), 1337, 100, ""); - - token.safeTransferFrom(address(this), address(0xBEEF), 1337, 70, ""); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 70); - assertEq(token.balanceOf(address(this), 1337), 30); - } - - function testSafeBatchTransferFromToEOA() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); - - assertEq(token.balanceOf(from, 1337), 50); - assertEq(token.balanceOf(address(0xBEEF), 1337), 50); - - assertEq(token.balanceOf(from, 1338), 100); - assertEq(token.balanceOf(address(0xBEEF), 1338), 100); - - assertEq(token.balanceOf(from, 1339), 150); - assertEq(token.balanceOf(address(0xBEEF), 1339), 150); - - assertEq(token.balanceOf(from, 1340), 200); - assertEq(token.balanceOf(address(0xBEEF), 1340), 200); - - assertEq(token.balanceOf(from, 1341), 250); - assertEq(token.balanceOf(address(0xBEEF), 1341), 250); - } - - function testSafeBatchTransferFromToERC1155Recipient() public { - address from = address(0xABCD); - - ERC1155Recipient to = new ERC1155Recipient(); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(to), ids, transferAmounts, "testing 123"); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), from); - assertUintArrayEq(to.batchIds(), ids); - assertUintArrayEq(to.batchAmounts(), transferAmounts); - assertBytesEq(to.batchData(), "testing 123"); - - assertEq(token.balanceOf(from, 1337), 50); - assertEq(token.balanceOf(address(to), 1337), 50); - - assertEq(token.balanceOf(from, 1338), 100); - assertEq(token.balanceOf(address(to), 1338), 100); - - assertEq(token.balanceOf(from, 1339), 150); - assertEq(token.balanceOf(address(to), 1339), 150); - - assertEq(token.balanceOf(from, 1340), 200); - assertEq(token.balanceOf(address(to), 1340), 200); - - assertEq(token.balanceOf(from, 1341), 250); - assertEq(token.balanceOf(address(to), 1341), 250); - } - - function testBatchBalanceOf() public { - address[] memory tos = new address[](5); - tos[0] = address(0xBEEF); - tos[1] = address(0xCAFE); - tos[2] = address(0xFACE); - tos[3] = address(0xDEAD); - tos[4] = address(0xFEED); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - token.mint(address(0xBEEF), 1337, 100, ""); - token.mint(address(0xCAFE), 1338, 200, ""); - token.mint(address(0xFACE), 1339, 300, ""); - token.mint(address(0xDEAD), 1340, 400, ""); - token.mint(address(0xFEED), 1341, 500, ""); - - uint256[] memory balances = token.balanceOfBatch(tos, ids); - - assertEq(balances[0], 100); - assertEq(balances[1], 200); - assertEq(balances[2], 300); - assertEq(balances[3], 400); - assertEq(balances[4], 500); - } - - function testFailMintToZero() public { - token.mint(address(0), 1337, 1, ""); - } - - function testFailMintToNonERC155Recipient() public { - token.mint(address(new NonERC1155Recipient()), 1337, 1, ""); - } - - function testFailMintToRevertingERC155Recipient() public { - token.mint(address(new RevertingERC1155Recipient()), 1337, 1, ""); - } - - function testFailMintToWrongReturnDataERC155Recipient() public { - token.mint(address(new RevertingERC1155Recipient()), 1337, 1, ""); - } - - function testFailBurnInsufficientBalance() public { - token.mint(address(0xBEEF), 1337, 70, ""); - token.burn(address(0xBEEF), 1337, 100); - } - - function testFailSafeTransferFromInsufficientBalance() public { - address from = address(0xABCD); - - token.mint(from, 1337, 70, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(0xBEEF), 1337, 100, ""); - } - - function testFailSafeTransferFromSelfInsufficientBalance() public { - token.mint(address(this), 1337, 70, ""); - token.safeTransferFrom(address(this), address(0xBEEF), 1337, 100, ""); - } - - function testFailSafeTransferFromToZero() public { - token.mint(address(this), 1337, 100, ""); - token.safeTransferFrom(address(this), address(0), 1337, 70, ""); - } - - function testFailSafeTransferFromToNonERC155Recipient() public { - token.mint(address(this), 1337, 100, ""); - token.safeTransferFrom(address(this), address(new NonERC1155Recipient()), 1337, 70, ""); - } - - function testFailSafeTransferFromToRevertingERC1155Recipient() public { - token.mint(address(this), 1337, 100, ""); - token.safeTransferFrom(address(this), address(new RevertingERC1155Recipient()), 1337, 70, ""); - } - - function testFailSafeTransferFromToWrongReturnDataERC1155Recipient() public { - token.mint(address(this), 1337, 100, ""); - token.safeTransferFrom(address(this), address(new WrongReturnDataERC1155Recipient()), 1337, 70, ""); - } - - function testFailSafeBatchTransferInsufficientBalance() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - - mintAmounts[0] = 50; - mintAmounts[1] = 100; - mintAmounts[2] = 150; - mintAmounts[3] = 200; - mintAmounts[4] = 250; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 100; - transferAmounts[1] = 200; - transferAmounts[2] = 300; - transferAmounts[3] = 400; - transferAmounts[4] = 500; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); - } - - function testFailSafeBatchTransferFromToZero() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(0), ids, transferAmounts, ""); - } - - function testFailSafeBatchTransferFromToNonERC1155Recipient() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(new NonERC1155Recipient()), ids, transferAmounts, ""); - } - - function testFailSafeBatchTransferFromToRevertingERC1155Recipient() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(new RevertingERC1155Recipient()), ids, transferAmounts, ""); - } - - function testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](5); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - transferAmounts[4] = 250; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(new WrongReturnDataERC1155Recipient()), ids, transferAmounts, ""); - } - - function testFailSafeBatchTransferFromWithArrayLengthMismatch() public { - address from = address(0xABCD); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory transferAmounts = new uint256[](4); - transferAmounts[0] = 50; - transferAmounts[1] = 100; - transferAmounts[2] = 150; - transferAmounts[3] = 200; - - token.batchMint(from, ids, mintAmounts, ""); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(0xBEEF), ids, transferAmounts, ""); - } - - function testFailBatchMintToZero() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - token.batchMint(address(0), ids, mintAmounts, ""); - } - - function testFailBatchMintToNonERC1155Recipient() public { - NonERC1155Recipient to = new NonERC1155Recipient(); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - token.batchMint(address(to), ids, mintAmounts, ""); - } - - function testFailBatchMintToRevertingERC1155Recipient() public { - RevertingERC1155Recipient to = new RevertingERC1155Recipient(); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - token.batchMint(address(to), ids, mintAmounts, ""); - } - - function testFailBatchMintToWrongReturnDataERC1155Recipient() public { - WrongReturnDataERC1155Recipient to = new WrongReturnDataERC1155Recipient(); - - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - token.batchMint(address(to), ids, mintAmounts, ""); - } - - function testFailBatchMintWithArrayMismatch() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory amounts = new uint256[](4); - amounts[0] = 100; - amounts[1] = 200; - amounts[2] = 300; - amounts[3] = 400; - - token.batchMint(address(0xBEEF), ids, amounts, ""); - } - - function testFailBatchBurnInsufficientBalance() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 50; - mintAmounts[1] = 100; - mintAmounts[2] = 150; - mintAmounts[3] = 200; - mintAmounts[4] = 250; - - uint256[] memory burnAmounts = new uint256[](5); - burnAmounts[0] = 100; - burnAmounts[1] = 200; - burnAmounts[2] = 300; - burnAmounts[3] = 400; - burnAmounts[4] = 500; - - token.batchMint(address(0xBEEF), ids, mintAmounts, ""); - - token.batchBurn(address(0xBEEF), ids, burnAmounts); - } - - function testFailBatchBurnWithArrayLengthMismatch() public { - uint256[] memory ids = new uint256[](5); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - ids[4] = 1341; - - uint256[] memory mintAmounts = new uint256[](5); - mintAmounts[0] = 100; - mintAmounts[1] = 200; - mintAmounts[2] = 300; - mintAmounts[3] = 400; - mintAmounts[4] = 500; - - uint256[] memory burnAmounts = new uint256[](4); - burnAmounts[0] = 50; - burnAmounts[1] = 100; - burnAmounts[2] = 150; - burnAmounts[3] = 200; - - token.batchMint(address(0xBEEF), ids, mintAmounts, ""); - - token.batchBurn(address(0xBEEF), ids, burnAmounts); - } - - function testFailBalanceOfBatchWithArrayMismatch() public view { - address[] memory tos = new address[](5); - tos[0] = address(0xBEEF); - tos[1] = address(0xCAFE); - tos[2] = address(0xFACE); - tos[3] = address(0xDEAD); - tos[4] = address(0xFEED); - - uint256[] memory ids = new uint256[](4); - ids[0] = 1337; - ids[1] = 1338; - ids[2] = 1339; - ids[3] = 1340; - - token.balanceOfBatch(tos, ids); - } - - function testMintToEOA( - address to, - uint256 id, - uint256 amount, - bytes memory mintData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - token.mint(to, id, amount, mintData); - - assertEq(token.balanceOf(to, id), amount); - } - - function testMintToERC1155Recipient( - uint256 id, - uint256 amount, - bytes memory mintData - ) public { - ERC1155Recipient to = new ERC1155Recipient(); - - token.mint(address(to), id, amount, mintData); - - assertEq(token.balanceOf(address(to), id), amount); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), id); - assertBytesEq(to.mintData(), mintData); - } - - function testBatchMintToEOA( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[to][id] += mintAmount; - } - - token.batchMint(to, normalizedIds, normalizedAmounts, mintData); - - for (uint256 i = 0; i < normalizedIds.length; i++) { - uint256 id = normalizedIds[i]; - - assertEq(token.balanceOf(to, id), userMintAmounts[to][id]); - } - } - - function testBatchMintToERC1155Recipient( - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - ERC1155Recipient to = new ERC1155Recipient(); - - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), address(0)); - assertUintArrayEq(to.batchIds(), normalizedIds); - assertUintArrayEq(to.batchAmounts(), normalizedAmounts); - assertBytesEq(to.batchData(), mintData); - - for (uint256 i = 0; i < normalizedIds.length; i++) { - uint256 id = normalizedIds[i]; - - assertEq(token.balanceOf(address(to), id), userMintAmounts[address(to)][id]); - } - } - - function testBurn( - address to, - uint256 id, - uint256 mintAmount, - bytes memory mintData, - uint256 burnAmount - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - burnAmount = bound(burnAmount, 0, mintAmount); - - token.mint(to, id, mintAmount, mintData); - - token.burn(to, id, burnAmount); - - assertEq(token.balanceOf(address(to), id), mintAmount - burnAmount); - } - - function testBatchBurn( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory burnAmounts, - bytes memory mintData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - uint256 minLength = min3(ids.length, mintAmounts.length, burnAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedBurnAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - normalizedIds[i] = id; - normalizedMintAmounts[i] = bound(mintAmounts[i], 0, remainingMintAmountForId); - normalizedBurnAmounts[i] = bound(burnAmounts[i], 0, normalizedMintAmounts[i]); - - userMintAmounts[address(to)][id] += normalizedMintAmounts[i]; - userTransferOrBurnAmounts[address(to)][id] += normalizedBurnAmounts[i]; - } - - token.batchMint(to, normalizedIds, normalizedMintAmounts, mintData); - - token.batchBurn(to, normalizedIds, normalizedBurnAmounts); - - for (uint256 i = 0; i < normalizedIds.length; i++) { - uint256 id = normalizedIds[i]; - - assertEq(token.balanceOf(to, id), userMintAmounts[to][id] - userTransferOrBurnAmounts[to][id]); - } - } - - function testApproveAll(address to, bool approved) public { - token.setApprovalForAll(to, approved); - - assertBoolEq(token.isApprovedForAll(address(this), to), approved); - } - - function testSafeTransferFromToEOA( - uint256 id, - uint256 mintAmount, - bytes memory mintData, - uint256 transferAmount, - address to, - bytes memory transferData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - transferAmount = bound(transferAmount, 0, mintAmount); - - address from = address(0xABCD); - - token.mint(from, id, mintAmount, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, to, id, transferAmount, transferData); - - if (to == from) { - assertEq(token.balanceOf(to, id), mintAmount); - } else { - assertEq(token.balanceOf(to, id), transferAmount); - assertEq(token.balanceOf(from, id), mintAmount - transferAmount); - } - } - - function testSafeTransferFromToERC1155Recipient( - uint256 id, - uint256 mintAmount, - bytes memory mintData, - uint256 transferAmount, - bytes memory transferData - ) public { - ERC1155Recipient to = new ERC1155Recipient(); - - address from = address(0xABCD); - - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(from, id, mintAmount, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(to), id, transferAmount, transferData); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), from); - assertEq(to.id(), id); - assertBytesEq(to.mintData(), transferData); - - assertEq(token.balanceOf(address(to), id), transferAmount); - assertEq(token.balanceOf(from, id), mintAmount - transferAmount); - } - - function testSafeTransferFromSelf( - uint256 id, - uint256 mintAmount, - bytes memory mintData, - uint256 transferAmount, - address to, - bytes memory transferData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(address(this), id, mintAmount, mintData); - - token.safeTransferFrom(address(this), to, id, transferAmount, transferData); - - assertEq(token.balanceOf(to, id), transferAmount); - assertEq(token.balanceOf(address(this), id), mintAmount - transferAmount); - } - - function testSafeBatchTransferFromToEOA( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - userTransferOrBurnAmounts[from][id] += transferAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, to, normalizedIds, normalizedTransferAmounts, transferData); - - for (uint256 i = 0; i < normalizedIds.length; i++) { - uint256 id = normalizedIds[i]; - - assertEq(token.balanceOf(address(to), id), userTransferOrBurnAmounts[from][id]); - assertEq(token.balanceOf(from, id), userMintAmounts[from][id] - userTransferOrBurnAmounts[from][id]); - } - } - - function testSafeBatchTransferFromToERC1155Recipient( - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - ERC1155Recipient to = new ERC1155Recipient(); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - userTransferOrBurnAmounts[from][id] += transferAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(to), normalizedIds, normalizedTransferAmounts, transferData); - - assertEq(to.batchOperator(), address(this)); - assertEq(to.batchFrom(), from); - assertUintArrayEq(to.batchIds(), normalizedIds); - assertUintArrayEq(to.batchAmounts(), normalizedTransferAmounts); - assertBytesEq(to.batchData(), transferData); - - for (uint256 i = 0; i < normalizedIds.length; i++) { - uint256 id = normalizedIds[i]; - uint256 transferAmount = userTransferOrBurnAmounts[from][id]; - - assertEq(token.balanceOf(address(to), id), transferAmount); - assertEq(token.balanceOf(from, id), userMintAmounts[from][id] - transferAmount); - } - } - - function testBatchBalanceOf( - address[] memory tos, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - uint256 minLength = min3(tos.length, ids.length, amounts.length); - - address[] memory normalizedTos = new address[](minLength); - uint256[] memory normalizedIds = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - address to = tos[i] == address(0) || tos[i].code.length > 0 ? address(0xBEEF) : tos[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; - - normalizedTos[i] = to; - normalizedIds[i] = id; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - token.mint(to, id, mintAmount, mintData); - - userMintAmounts[to][id] += mintAmount; - } - - uint256[] memory balances = token.balanceOfBatch(normalizedTos, normalizedIds); - - for (uint256 i = 0; i < normalizedTos.length; i++) { - assertEq(balances[i], token.balanceOf(normalizedTos[i], normalizedIds[i])); - } - } - - function testFailMintToZero( - uint256 id, - uint256 amount, - bytes memory data - ) public { - token.mint(address(0), id, amount, data); - } - - function testFailMintToNonERC155Recipient( - uint256 id, - uint256 mintAmount, - bytes memory mintData - ) public { - token.mint(address(new NonERC1155Recipient()), id, mintAmount, mintData); - } - - function testFailMintToRevertingERC155Recipient( - uint256 id, - uint256 mintAmount, - bytes memory mintData - ) public { - token.mint(address(new RevertingERC1155Recipient()), id, mintAmount, mintData); - } - - function testFailMintToWrongReturnDataERC155Recipient( - uint256 id, - uint256 mintAmount, - bytes memory mintData - ) public { - token.mint(address(new RevertingERC1155Recipient()), id, mintAmount, mintData); - } - - function testFailBurnInsufficientBalance( - address to, - uint256 id, - uint256 mintAmount, - uint256 burnAmount, - bytes memory mintData - ) public { - burnAmount = bound(burnAmount, mintAmount + 1, type(uint256).max); - - token.mint(to, id, mintAmount, mintData); - token.burn(to, id, burnAmount); - } - - function testFailSafeTransferFromInsufficientBalance( - address to, - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - transferAmount = bound(transferAmount, mintAmount + 1, type(uint256).max); - - token.mint(from, id, mintAmount, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, to, id, transferAmount, transferData); - } - - function testFailSafeTransferFromSelfInsufficientBalance( - address to, - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - transferAmount = bound(transferAmount, mintAmount + 1, type(uint256).max); - - token.mint(address(this), id, mintAmount, mintData); - token.safeTransferFrom(address(this), to, id, transferAmount, transferData); - } - - function testFailSafeTransferFromToZero( - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(address(this), id, mintAmount, mintData); - token.safeTransferFrom(address(this), address(0), id, transferAmount, transferData); - } - - function testFailSafeTransferFromToNonERC155Recipient( - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(address(this), id, mintAmount, mintData); - token.safeTransferFrom(address(this), address(new NonERC1155Recipient()), id, transferAmount, transferData); - } - - function testFailSafeTransferFromToRevertingERC1155Recipient( - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(address(this), id, mintAmount, mintData); - token.safeTransferFrom( - address(this), - address(new RevertingERC1155Recipient()), - id, - transferAmount, - transferData - ); - } - - function testFailSafeTransferFromToWrongReturnDataERC1155Recipient( - uint256 id, - uint256 mintAmount, - uint256 transferAmount, - bytes memory mintData, - bytes memory transferData - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(address(this), id, mintAmount, mintData); - token.safeTransferFrom( - address(this), - address(new WrongReturnDataERC1155Recipient()), - id, - transferAmount, - transferData - ); - } - - function testFailSafeBatchTransferInsufficientBalance( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - if (minLength == 0) revert(); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], mintAmount + 1, type(uint256).max); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, to, normalizedIds, normalizedTransferAmounts, transferData); - } - - function testFailSafeBatchTransferFromToZero( - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, address(0), normalizedIds, normalizedTransferAmounts, transferData); - } - - function testFailSafeBatchTransferFromToNonERC1155Recipient( - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom( - from, - address(new NonERC1155Recipient()), - normalizedIds, - normalizedTransferAmounts, - transferData - ); - } - - function testFailSafeBatchTransferFromToRevertingERC1155Recipient( - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom( - from, - address(new RevertingERC1155Recipient()), - normalizedIds, - normalizedTransferAmounts, - transferData - ); - } - - function testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient( - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - uint256 minLength = min3(ids.length, mintAmounts.length, transferAmounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedTransferAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[from][id]; - - uint256 mintAmount = bound(mintAmounts[i], 0, remainingMintAmountForId); - uint256 transferAmount = bound(transferAmounts[i], 0, mintAmount); - - normalizedIds[i] = id; - normalizedMintAmounts[i] = mintAmount; - normalizedTransferAmounts[i] = transferAmount; - - userMintAmounts[from][id] += mintAmount; - } - - token.batchMint(from, normalizedIds, normalizedMintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom( - from, - address(new WrongReturnDataERC1155Recipient()), - normalizedIds, - normalizedTransferAmounts, - transferData - ); - } - - function testFailSafeBatchTransferFromWithArrayLengthMismatch( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory transferAmounts, - bytes memory mintData, - bytes memory transferData - ) public { - address from = address(0xABCD); - - if (ids.length == transferAmounts.length) revert(); - - token.batchMint(from, ids, mintAmounts, mintData); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeBatchTransferFrom(from, to, ids, transferAmounts, transferData); - } - - function testFailBatchMintToZero( - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(0)][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[address(0)][id] += mintAmount; - } - - token.batchMint(address(0), normalizedIds, normalizedAmounts, mintData); - } - - function testFailBatchMintToNonERC1155Recipient( - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - NonERC1155Recipient to = new NonERC1155Recipient(); - - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); - } - - function testFailBatchMintToRevertingERC1155Recipient( - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - RevertingERC1155Recipient to = new RevertingERC1155Recipient(); - - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); - } - - function testFailBatchMintToWrongReturnDataERC1155Recipient( - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - WrongReturnDataERC1155Recipient to = new WrongReturnDataERC1155Recipient(); - - uint256 minLength = min2(ids.length, amounts.length); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[address(to)][id]; - - uint256 mintAmount = bound(amounts[i], 0, remainingMintAmountForId); - - normalizedIds[i] = id; - normalizedAmounts[i] = mintAmount; - - userMintAmounts[address(to)][id] += mintAmount; - } - - token.batchMint(address(to), normalizedIds, normalizedAmounts, mintData); - } - - function testFailBatchMintWithArrayMismatch( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory mintData - ) public { - if (ids.length == amounts.length) revert(); - - token.batchMint(address(to), ids, amounts, mintData); - } - - function testFailBatchBurnInsufficientBalance( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory burnAmounts, - bytes memory mintData - ) public { - uint256 minLength = min3(ids.length, mintAmounts.length, burnAmounts.length); - - if (minLength == 0) revert(); - - uint256[] memory normalizedIds = new uint256[](minLength); - uint256[] memory normalizedMintAmounts = new uint256[](minLength); - uint256[] memory normalizedBurnAmounts = new uint256[](minLength); - - for (uint256 i = 0; i < minLength; i++) { - uint256 id = ids[i]; - - uint256 remainingMintAmountForId = type(uint256).max - userMintAmounts[to][id]; - - normalizedIds[i] = id; - normalizedMintAmounts[i] = bound(mintAmounts[i], 0, remainingMintAmountForId); - normalizedBurnAmounts[i] = bound(burnAmounts[i], normalizedMintAmounts[i] + 1, type(uint256).max); - - userMintAmounts[to][id] += normalizedMintAmounts[i]; - } - - token.batchMint(to, normalizedIds, normalizedMintAmounts, mintData); - - token.batchBurn(to, normalizedIds, normalizedBurnAmounts); - } - - function testFailBatchBurnWithArrayLengthMismatch( - address to, - uint256[] memory ids, - uint256[] memory mintAmounts, - uint256[] memory burnAmounts, - bytes memory mintData - ) public { - if (ids.length == burnAmounts.length) revert(); - - token.batchMint(to, ids, mintAmounts, mintData); - - token.batchBurn(to, ids, burnAmounts); - } - - function testFailBalanceOfBatchWithArrayMismatch(address[] memory tos, uint256[] memory ids) public view { - if (tos.length == ids.length) revert(); - - token.balanceOfBatch(tos, ids); - } -} diff --git a/lib/solmate/src/test/ERC20.t.sol b/lib/solmate/src/test/ERC20.t.sol deleted file mode 100644 index 1506d8c..0000000 --- a/lib/solmate/src/test/ERC20.t.sol +++ /dev/null @@ -1,531 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; - -import {MockERC20} from "./utils/mocks/MockERC20.sol"; - -contract ERC20Test is DSTestPlus { - MockERC20 token; - - bytes32 constant PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - - function setUp() public { - token = new MockERC20("Token", "TKN", 18); - } - - function invariantMetadata() public { - assertEq(token.name(), "Token"); - assertEq(token.symbol(), "TKN"); - assertEq(token.decimals(), 18); - } - - function testMint() public { - token.mint(address(0xBEEF), 1e18); - - assertEq(token.totalSupply(), 1e18); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1e18); - token.burn(address(0xBEEF), 0.9e18); - - assertEq(token.totalSupply(), 1e18 - 0.9e18); - assertEq(token.balanceOf(address(0xBEEF)), 0.1e18); - } - - function testApprove() public { - assertTrue(token.approve(address(0xBEEF), 1e18)); - - assertEq(token.allowance(address(this), address(0xBEEF)), 1e18); - } - - function testTransfer() public { - token.mint(address(this), 1e18); - - assertTrue(token.transfer(address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - hevm.prank(from); - token.approve(address(this), 1e18); - - assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.allowance(from, address(this)), 0); - - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testInfiniteApproveTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - hevm.prank(from); - token.approve(address(this), type(uint256).max); - - assertTrue(token.transferFrom(from, address(0xBEEF), 1e18)); - assertEq(token.totalSupply(), 1e18); - - assertEq(token.allowance(from, address(this)), type(uint256).max); - - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(address(0xBEEF)), 1e18); - } - - function testPermit() public { - uint256 privateKey = 0xBEEF; - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) - ) - ) - ); - - token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); - - assertEq(token.allowance(owner, address(0xCAFE)), 1e18); - assertEq(token.nonces(owner), 1); - } - - function testFailTransferInsufficientBalance() public { - token.mint(address(this), 0.9e18); - token.transfer(address(0xBEEF), 1e18); - } - - function testFailTransferFromInsufficientAllowance() public { - address from = address(0xABCD); - - token.mint(from, 1e18); - - hevm.prank(from); - token.approve(address(this), 0.9e18); - - token.transferFrom(from, address(0xBEEF), 1e18); - } - - function testFailTransferFromInsufficientBalance() public { - address from = address(0xABCD); - - token.mint(from, 0.9e18); - - hevm.prank(from); - token.approve(address(this), 1e18); - - token.transferFrom(from, address(0xBEEF), 1e18); - } - - function testFailPermitBadNonce() public { - uint256 privateKey = 0xBEEF; - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 1, block.timestamp)) - ) - ) - ); - - token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); - } - - function testFailPermitBadDeadline() public { - uint256 privateKey = 0xBEEF; - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) - ) - ) - ); - - token.permit(owner, address(0xCAFE), 1e18, block.timestamp + 1, v, r, s); - } - - function testFailPermitPastDeadline() public { - uint256 oldTimestamp = block.timestamp; - uint256 privateKey = 0xBEEF; - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, oldTimestamp)) - ) - ) - ); - - hevm.warp(block.timestamp + 1); - token.permit(owner, address(0xCAFE), 1e18, oldTimestamp, v, r, s); - } - - function testFailPermitReplay() public { - uint256 privateKey = 0xBEEF; - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, address(0xCAFE), 1e18, 0, block.timestamp)) - ) - ) - ); - - token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); - token.permit(owner, address(0xCAFE), 1e18, block.timestamp, v, r, s); - } - - function testMetadata( - string calldata name, - string calldata symbol, - uint8 decimals - ) public { - MockERC20 tkn = new MockERC20(name, symbol, decimals); - assertEq(tkn.name(), name); - assertEq(tkn.symbol(), symbol); - assertEq(tkn.decimals(), decimals); - } - - function testMint(address from, uint256 amount) public { - token.mint(from, amount); - - assertEq(token.totalSupply(), amount); - assertEq(token.balanceOf(from), amount); - } - - function testBurn( - address from, - uint256 mintAmount, - uint256 burnAmount - ) public { - burnAmount = bound(burnAmount, 0, mintAmount); - - token.mint(from, mintAmount); - token.burn(from, burnAmount); - - assertEq(token.totalSupply(), mintAmount - burnAmount); - assertEq(token.balanceOf(from), mintAmount - burnAmount); - } - - function testApprove(address to, uint256 amount) public { - assertTrue(token.approve(to, amount)); - - assertEq(token.allowance(address(this), to), amount); - } - - function testTransfer(address from, uint256 amount) public { - token.mint(address(this), amount); - - assertTrue(token.transfer(from, amount)); - assertEq(token.totalSupply(), amount); - - if (address(this) == from) { - assertEq(token.balanceOf(address(this)), amount); - } else { - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.balanceOf(from), amount); - } - } - - function testTransferFrom( - address to, - uint256 approval, - uint256 amount - ) public { - amount = bound(amount, 0, approval); - - address from = address(0xABCD); - - token.mint(from, amount); - - hevm.prank(from); - token.approve(address(this), approval); - - assertTrue(token.transferFrom(from, to, amount)); - assertEq(token.totalSupply(), amount); - - uint256 app = from == address(this) || approval == type(uint256).max ? approval : approval - amount; - assertEq(token.allowance(from, address(this)), app); - - if (from == to) { - assertEq(token.balanceOf(from), amount); - } else { - assertEq(token.balanceOf(from), 0); - assertEq(token.balanceOf(to), amount); - } - } - - function testPermit( - uint248 privKey, - address to, - uint256 amount, - uint256 deadline - ) public { - uint256 privateKey = privKey; - if (deadline < block.timestamp) deadline = block.timestamp; - if (privateKey == 0) privateKey = 1; - - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) - ) - ) - ); - - token.permit(owner, to, amount, deadline, v, r, s); - - assertEq(token.allowance(owner, to), amount); - assertEq(token.nonces(owner), 1); - } - - function testFailBurnInsufficientBalance( - address to, - uint256 mintAmount, - uint256 burnAmount - ) public { - burnAmount = bound(burnAmount, mintAmount + 1, type(uint256).max); - - token.mint(to, mintAmount); - token.burn(to, burnAmount); - } - - function testFailTransferInsufficientBalance( - address to, - uint256 mintAmount, - uint256 sendAmount - ) public { - sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max); - - token.mint(address(this), mintAmount); - token.transfer(to, sendAmount); - } - - function testFailTransferFromInsufficientAllowance( - address to, - uint256 approval, - uint256 amount - ) public { - amount = bound(amount, approval + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, amount); - - hevm.prank(from); - token.approve(address(this), approval); - - token.transferFrom(from, to, amount); - } - - function testFailTransferFromInsufficientBalance( - address to, - uint256 mintAmount, - uint256 sendAmount - ) public { - sendAmount = bound(sendAmount, mintAmount + 1, type(uint256).max); - - address from = address(0xABCD); - - token.mint(from, mintAmount); - - hevm.prank(from); - token.approve(address(this), sendAmount); - - token.transferFrom(from, to, sendAmount); - } - - function testFailPermitBadNonce( - uint256 privateKey, - address to, - uint256 amount, - uint256 deadline, - uint256 nonce - ) public { - if (deadline < block.timestamp) deadline = block.timestamp; - if (privateKey == 0) privateKey = 1; - if (nonce == 0) nonce = 1; - - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, nonce, deadline)) - ) - ) - ); - - token.permit(owner, to, amount, deadline, v, r, s); - } - - function testFailPermitBadDeadline( - uint256 privateKey, - address to, - uint256 amount, - uint256 deadline - ) public { - if (deadline < block.timestamp) deadline = block.timestamp; - if (privateKey == 0) privateKey = 1; - - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) - ) - ) - ); - - token.permit(owner, to, amount, deadline + 1, v, r, s); - } - - function testFailPermitPastDeadline( - uint256 privateKey, - address to, - uint256 amount, - uint256 deadline - ) public { - deadline = bound(deadline, 0, block.timestamp - 1); - if (privateKey == 0) privateKey = 1; - - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) - ) - ) - ); - - token.permit(owner, to, amount, deadline, v, r, s); - } - - function testFailPermitReplay( - uint256 privateKey, - address to, - uint256 amount, - uint256 deadline - ) public { - if (deadline < block.timestamp) deadline = block.timestamp; - if (privateKey == 0) privateKey = 1; - - address owner = hevm.addr(privateKey); - - (uint8 v, bytes32 r, bytes32 s) = hevm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - token.DOMAIN_SEPARATOR(), - keccak256(abi.encode(PERMIT_TYPEHASH, owner, to, amount, 0, deadline)) - ) - ) - ); - - token.permit(owner, to, amount, deadline, v, r, s); - token.permit(owner, to, amount, deadline, v, r, s); - } -} - -contract ERC20Invariants is DSTestPlus, DSInvariantTest { - BalanceSum balanceSum; - MockERC20 token; - - function setUp() public { - token = new MockERC20("Token", "TKN", 18); - balanceSum = new BalanceSum(token); - - addTargetContract(address(balanceSum)); - } - - function invariantBalanceSum() public { - assertEq(token.totalSupply(), balanceSum.sum()); - } -} - -contract BalanceSum { - MockERC20 token; - uint256 public sum; - - constructor(MockERC20 _token) { - token = _token; - } - - function mint(address from, uint256 amount) public { - token.mint(from, amount); - sum += amount; - } - - function burn(address from, uint256 amount) public { - token.burn(from, amount); - sum -= amount; - } - - function approve(address to, uint256 amount) public { - token.approve(to, amount); - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public { - token.transferFrom(from, to, amount); - } - - function transfer(address to, uint256 amount) public { - token.transfer(to, amount); - } -} diff --git a/lib/solmate/src/test/ERC4626.t.sol b/lib/solmate/src/test/ERC4626.t.sol deleted file mode 100644 index 816c8e4..0000000 --- a/lib/solmate/src/test/ERC4626.t.sol +++ /dev/null @@ -1,446 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {MockERC20} from "./utils/mocks/MockERC20.sol"; -import {MockERC4626} from "./utils/mocks/MockERC4626.sol"; - -contract ERC4626Test is DSTestPlus { - MockERC20 underlying; - MockERC4626 vault; - - function setUp() public { - underlying = new MockERC20("Mock Token", "TKN", 18); - vault = new MockERC4626(underlying, "Mock Token Vault", "vwTKN"); - } - - function invariantMetadata() public { - assertEq(vault.name(), "Mock Token Vault"); - assertEq(vault.symbol(), "vwTKN"); - assertEq(vault.decimals(), 18); - } - - function testMetadata(string calldata name, string calldata symbol) public { - MockERC4626 vlt = new MockERC4626(underlying, name, symbol); - assertEq(vlt.name(), name); - assertEq(vlt.symbol(), symbol); - assertEq(address(vlt.asset()), address(underlying)); - } - - function testSingleDepositWithdraw(uint128 amount) public { - if (amount == 0) amount = 1; - - uint256 aliceUnderlyingAmount = amount; - - address alice = address(0xABCD); - - underlying.mint(alice, aliceUnderlyingAmount); - - hevm.prank(alice); - underlying.approve(address(vault), aliceUnderlyingAmount); - assertEq(underlying.allowance(alice, address(vault)), aliceUnderlyingAmount); - - uint256 alicePreDepositBal = underlying.balanceOf(alice); - - hevm.prank(alice); - uint256 aliceShareAmount = vault.deposit(aliceUnderlyingAmount, alice); - - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect exchange rate to be 1:1 on initial deposit. - assertEq(aliceUnderlyingAmount, aliceShareAmount); - assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); - assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); - assertEq(vault.totalSupply(), aliceShareAmount); - assertEq(vault.totalAssets(), aliceUnderlyingAmount); - assertEq(vault.balanceOf(alice), aliceShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); - assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); - - hevm.prank(alice); - vault.withdraw(aliceUnderlyingAmount, alice, alice); - - assertEq(vault.beforeWithdrawHookCalledCounter(), 1); - - assertEq(vault.totalAssets(), 0); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(underlying.balanceOf(alice), alicePreDepositBal); - } - - function testSingleMintRedeem(uint128 amount) public { - if (amount == 0) amount = 1; - - uint256 aliceShareAmount = amount; - - address alice = address(0xABCD); - - underlying.mint(alice, aliceShareAmount); - - hevm.prank(alice); - underlying.approve(address(vault), aliceShareAmount); - assertEq(underlying.allowance(alice, address(vault)), aliceShareAmount); - - uint256 alicePreDepositBal = underlying.balanceOf(alice); - - hevm.prank(alice); - uint256 aliceUnderlyingAmount = vault.mint(aliceShareAmount, alice); - - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect exchange rate to be 1:1 on initial mint. - assertEq(aliceShareAmount, aliceUnderlyingAmount); - assertEq(vault.previewWithdraw(aliceShareAmount), aliceUnderlyingAmount); - assertEq(vault.previewDeposit(aliceUnderlyingAmount), aliceShareAmount); - assertEq(vault.totalSupply(), aliceShareAmount); - assertEq(vault.totalAssets(), aliceUnderlyingAmount); - assertEq(vault.balanceOf(alice), aliceUnderlyingAmount); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); - assertEq(underlying.balanceOf(alice), alicePreDepositBal - aliceUnderlyingAmount); - - hevm.prank(alice); - vault.redeem(aliceShareAmount, alice, alice); - - assertEq(vault.beforeWithdrawHookCalledCounter(), 1); - - assertEq(vault.totalAssets(), 0); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(underlying.balanceOf(alice), alicePreDepositBal); - } - - function testMultipleMintDepositRedeemWithdraw() public { - // Scenario: - // A = Alice, B = Bob - // ________________________________________________________ - // | Vault shares | A share | A assets | B share | B assets | - // |========================================================| - // | 1. Alice mints 2000 shares (costs 2000 tokens) | - // |--------------|---------|----------|---------|----------| - // | 2000 | 2000 | 2000 | 0 | 0 | - // |--------------|---------|----------|---------|----------| - // | 2. Bob deposits 4000 tokens (mints 4000 shares) | - // |--------------|---------|----------|---------|----------| - // | 6000 | 2000 | 2000 | 4000 | 4000 | - // |--------------|---------|----------|---------|----------| - // | 3. Vault mutates by +3000 tokens... | - // | (simulated yield returned from strategy)... | - // |--------------|---------|----------|---------|----------| - // | 6000 | 2000 | 3000 | 4000 | 6000 | - // |--------------|---------|----------|---------|----------| - // | 4. Alice deposits 2000 tokens (mints 1333 shares) | - // |--------------|---------|----------|---------|----------| - // | 7333 | 3333 | 4999 | 4000 | 6000 | - // |--------------|---------|----------|---------|----------| - // | 5. Bob mints 2000 shares (costs 3001 assets) | - // | NOTE: Bob's assets spent got rounded up | - // | NOTE: Alice's vault assets got rounded up | - // |--------------|---------|----------|---------|----------| - // | 9333 | 3333 | 5000 | 6000 | 9000 | - // |--------------|---------|----------|---------|----------| - // | 6. Vault mutates by +3000 tokens... | - // | (simulated yield returned from strategy) | - // | NOTE: Vault holds 17001 tokens, but sum of | - // | assetsOf() is 17000. | - // |--------------|---------|----------|---------|----------| - // | 9333 | 3333 | 6071 | 6000 | 10929 | - // |--------------|---------|----------|---------|----------| - // | 7. Alice redeem 1333 shares (2428 assets) | - // |--------------|---------|----------|---------|----------| - // | 8000 | 2000 | 3643 | 6000 | 10929 | - // |--------------|---------|----------|---------|----------| - // | 8. Bob withdraws 2928 assets (1608 shares) | - // |--------------|---------|----------|---------|----------| - // | 6392 | 2000 | 3643 | 4392 | 8000 | - // |--------------|---------|----------|---------|----------| - // | 9. Alice withdraws 3643 assets (2000 shares) | - // | NOTE: Bob's assets have been rounded back up | - // |--------------|---------|----------|---------|----------| - // | 4392 | 0 | 0 | 4392 | 8001 | - // |--------------|---------|----------|---------|----------| - // | 10. Bob redeem 4392 shares (8001 tokens) | - // |--------------|---------|----------|---------|----------| - // | 0 | 0 | 0 | 0 | 0 | - // |______________|_________|__________|_________|__________| - - address alice = address(0xABCD); - address bob = address(0xDCBA); - - uint256 mutationUnderlyingAmount = 3000; - - underlying.mint(alice, 4000); - - hevm.prank(alice); - underlying.approve(address(vault), 4000); - - assertEq(underlying.allowance(alice, address(vault)), 4000); - - underlying.mint(bob, 7001); - - hevm.prank(bob); - underlying.approve(address(vault), 7001); - - assertEq(underlying.allowance(bob, address(vault)), 7001); - - // 1. Alice mints 2000 shares (costs 2000 tokens) - hevm.prank(alice); - uint256 aliceUnderlyingAmount = vault.mint(2000, alice); - - uint256 aliceShareAmount = vault.previewDeposit(aliceUnderlyingAmount); - assertEq(vault.afterDepositHookCalledCounter(), 1); - - // Expect to have received the requested mint amount. - assertEq(aliceShareAmount, 2000); - assertEq(vault.balanceOf(alice), aliceShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), aliceUnderlyingAmount); - assertEq(vault.convertToShares(aliceUnderlyingAmount), vault.balanceOf(alice)); - - // Expect a 1:1 ratio before mutation. - assertEq(aliceUnderlyingAmount, 2000); - - // Sanity check. - assertEq(vault.totalSupply(), aliceShareAmount); - assertEq(vault.totalAssets(), aliceUnderlyingAmount); - - // 2. Bob deposits 4000 tokens (mints 4000 shares) - hevm.prank(bob); - uint256 bobShareAmount = vault.deposit(4000, bob); - uint256 bobUnderlyingAmount = vault.previewWithdraw(bobShareAmount); - assertEq(vault.afterDepositHookCalledCounter(), 2); - - // Expect to have received the requested underlying amount. - assertEq(bobUnderlyingAmount, 4000); - assertEq(vault.balanceOf(bob), bobShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount); - assertEq(vault.convertToShares(bobUnderlyingAmount), vault.balanceOf(bob)); - - // Expect a 1:1 ratio before mutation. - assertEq(bobShareAmount, bobUnderlyingAmount); - - // Sanity check. - uint256 preMutationShareBal = aliceShareAmount + bobShareAmount; - uint256 preMutationBal = aliceUnderlyingAmount + bobUnderlyingAmount; - assertEq(vault.totalSupply(), preMutationShareBal); - assertEq(vault.totalAssets(), preMutationBal); - assertEq(vault.totalSupply(), 6000); - assertEq(vault.totalAssets(), 6000); - - // 3. Vault mutates by +3000 tokens... | - // (simulated yield returned from strategy)... - // The Vault now contains more tokens than deposited which causes the exchange rate to change. - // Alice share is 33.33% of the Vault, Bob 66.66% of the Vault. - // Alice's share count stays the same but the underlying amount changes from 2000 to 3000. - // Bob's share count stays the same but the underlying amount changes from 4000 to 6000. - underlying.mint(address(vault), mutationUnderlyingAmount); - assertEq(vault.totalSupply(), preMutationShareBal); - assertEq(vault.totalAssets(), preMutationBal + mutationUnderlyingAmount); - assertEq(vault.balanceOf(alice), aliceShareAmount); - assertEq( - vault.convertToAssets(vault.balanceOf(alice)), - aliceUnderlyingAmount + (mutationUnderlyingAmount / 3) * 1 - ); - assertEq(vault.balanceOf(bob), bobShareAmount); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), bobUnderlyingAmount + (mutationUnderlyingAmount / 3) * 2); - - // 4. Alice deposits 2000 tokens (mints 1333 shares) - hevm.prank(alice); - vault.deposit(2000, alice); - - assertEq(vault.totalSupply(), 7333); - assertEq(vault.balanceOf(alice), 3333); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 4999); - assertEq(vault.balanceOf(bob), 4000); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 6000); - - // 5. Bob mints 2000 shares (costs 3001 assets) - // NOTE: Bob's assets spent got rounded up - // NOTE: Alices's vault assets got rounded up - hevm.prank(bob); - vault.mint(2000, bob); - - assertEq(vault.totalSupply(), 9333); - assertEq(vault.balanceOf(alice), 3333); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 5000); - assertEq(vault.balanceOf(bob), 6000); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 9000); - - // Sanity checks: - // Alice and bob should have spent all their tokens now - assertEq(underlying.balanceOf(alice), 0); - assertEq(underlying.balanceOf(bob), 0); - // Assets in vault: 4k (alice) + 7k (bob) + 3k (yield) + 1 (round up) - assertEq(vault.totalAssets(), 14001); - - // 6. Vault mutates by +3000 tokens - // NOTE: Vault holds 17001 tokens, but sum of assetsOf() is 17000. - underlying.mint(address(vault), mutationUnderlyingAmount); - assertEq(vault.totalAssets(), 17001); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 6071); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); - - // 7. Alice redeem 1333 shares (2428 assets) - hevm.prank(alice); - vault.redeem(1333, alice, alice); - - assertEq(underlying.balanceOf(alice), 2428); - assertEq(vault.totalSupply(), 8000); - assertEq(vault.totalAssets(), 14573); - assertEq(vault.balanceOf(alice), 2000); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); - assertEq(vault.balanceOf(bob), 6000); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 10929); - - // 8. Bob withdraws 2929 assets (1608 shares) - hevm.prank(bob); - vault.withdraw(2929, bob, bob); - - assertEq(underlying.balanceOf(bob), 2929); - assertEq(vault.totalSupply(), 6392); - assertEq(vault.totalAssets(), 11644); - assertEq(vault.balanceOf(alice), 2000); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 3643); - assertEq(vault.balanceOf(bob), 4392); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8000); - - // 9. Alice withdraws 3643 assets (2000 shares) - // NOTE: Bob's assets have been rounded back up - hevm.prank(alice); - vault.withdraw(3643, alice, alice); - - assertEq(underlying.balanceOf(alice), 6071); - assertEq(vault.totalSupply(), 4392); - assertEq(vault.totalAssets(), 8001); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(vault.balanceOf(bob), 4392); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 8001); - - // 10. Bob redeem 4392 shares (8001 tokens) - hevm.prank(bob); - vault.redeem(4392, bob, bob); - assertEq(underlying.balanceOf(bob), 10930); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets(), 0); - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.convertToAssets(vault.balanceOf(alice)), 0); - assertEq(vault.balanceOf(bob), 0); - assertEq(vault.convertToAssets(vault.balanceOf(bob)), 0); - - // Sanity check - assertEq(underlying.balanceOf(address(vault)), 0); - } - - function testFailDepositWithNotEnoughApproval() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - assertEq(underlying.allowance(address(this), address(vault)), 0.5e18); - - vault.deposit(1e18, address(this)); - } - - function testFailWithdrawWithNotEnoughUnderlyingAmount() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - - vault.deposit(0.5e18, address(this)); - - vault.withdraw(1e18, address(this), address(this)); - } - - function testFailRedeemWithNotEnoughShareAmount() public { - underlying.mint(address(this), 0.5e18); - underlying.approve(address(vault), 0.5e18); - - vault.deposit(0.5e18, address(this)); - - vault.redeem(1e18, address(this), address(this)); - } - - function testFailWithdrawWithNoUnderlyingAmount() public { - vault.withdraw(1e18, address(this), address(this)); - } - - function testFailRedeemWithNoShareAmount() public { - vault.redeem(1e18, address(this), address(this)); - } - - function testFailDepositWithNoApproval() public { - vault.deposit(1e18, address(this)); - } - - function testFailMintWithNoApproval() public { - vault.mint(1e18, address(this)); - } - - function testFailDepositZero() public { - vault.deposit(0, address(this)); - } - - function testMintZero() public { - vault.mint(0, address(this)); - - assertEq(vault.balanceOf(address(this)), 0); - assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets(), 0); - } - - function testFailRedeemZero() public { - vault.redeem(0, address(this), address(this)); - } - - function testWithdrawZero() public { - vault.withdraw(0, address(this), address(this)); - - assertEq(vault.balanceOf(address(this)), 0); - assertEq(vault.convertToAssets(vault.balanceOf(address(this))), 0); - assertEq(vault.totalSupply(), 0); - assertEq(vault.totalAssets(), 0); - } - - function testVaultInteractionsForSomeoneElse() public { - // init 2 users with a 1e18 balance - address alice = address(0xABCD); - address bob = address(0xDCBA); - underlying.mint(alice, 1e18); - underlying.mint(bob, 1e18); - - hevm.prank(alice); - underlying.approve(address(vault), 1e18); - - hevm.prank(bob); - underlying.approve(address(vault), 1e18); - - // alice deposits 1e18 for bob - hevm.prank(alice); - vault.deposit(1e18, bob); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(alice), 0); - - // bob mint 1e18 for alice - hevm.prank(bob); - vault.mint(1e18, alice); - assertEq(vault.balanceOf(alice), 1e18); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(bob), 0); - - // alice redeem 1e18 for bob - hevm.prank(alice); - vault.redeem(1e18, bob, alice); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 1e18); - assertEq(underlying.balanceOf(bob), 1e18); - - // bob withdraw 1e18 for alice - hevm.prank(bob); - vault.withdraw(1e18, alice, bob); - - assertEq(vault.balanceOf(alice), 0); - assertEq(vault.balanceOf(bob), 0); - assertEq(underlying.balanceOf(alice), 1e18); - } -} diff --git a/lib/solmate/src/test/ERC6909.t.sol b/lib/solmate/src/test/ERC6909.t.sol deleted file mode 100644 index 57cdb73..0000000 --- a/lib/solmate/src/test/ERC6909.t.sol +++ /dev/null @@ -1,378 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; - -import {MockERC6909} from "./utils/mocks/MockERC6909.sol"; - -contract ERC6909Test is DSTestPlus { - MockERC6909 token; - - mapping(address => mapping(uint256 => uint256)) public userMintAmounts; - mapping(address => mapping(uint256 => uint256)) public userTransferOrBurnAmounts; - - function setUp() public { - token = new MockERC6909(); - } - - function testMint() public { - token.mint(address(0xBEEF), 1337, 100); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 100); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1337, 100); - token.burn(address(0xBEEF), 1337, 70); - - assertEq(token.balanceOf(address(0xBEEF), 1337), 30); - } - - function testSetOperator() public { - token.setOperator(address(0xBEEF), true); - - assertTrue(token.isOperator(address(this), address(0xBEEF))); - } - - function testApprove() public { - token.approve(address(0xBEEF), 1337, 100); - - assertEq(token.allowance(address(this), address(0xBEEF), 1337), 100); - } - - function testTransfer() public { - address sender = address(0xABCD); - - token.mint(sender, 1337, 100); - - hevm.prank(sender); - token.transfer(address(0xBEEF), 1337, 70); - - assertEq(token.balanceOf(sender, 1337), 30); - assertEq(token.balanceOf(address(0xBEEF), 1337), 70); - } - - function testTransferFromWithApproval() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, 100); - - hevm.prank(sender); - token.approve(address(this), 1337, 100); - - token.transferFrom(sender, receiver, 1337, 70); - - assertEq(token.allowance(sender, address(this), 1337), 30); - assertEq(token.balanceOf(sender, 1337), 30); - assertEq(token.balanceOf(receiver, 1337), 70); - } - - function testTransferFromWithInfiniteApproval() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, 100); - - hevm.prank(sender); - token.approve(address(this), 1337, type(uint256).max); - - token.transferFrom(sender, receiver, 1337, 70); - - assertEq(token.allowance(sender, address(this), 1337), type(uint256).max); - assertEq(token.balanceOf(sender, 1337), 30); - assertEq(token.balanceOf(receiver, 1337), 70); - } - - function testTransferFromAsOperator() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, 100); - - hevm.prank(sender); - token.setOperator(address(this), true); - - token.transferFrom(sender, receiver, 1337, 70); - - assertEq(token.balanceOf(sender, 1337), 30); - assertEq(token.balanceOf(receiver, 1337), 70); - } - - function testFailMintBalanceOverflow() public { - token.mint(address(0xDEAD), 1337, type(uint256).max); - token.mint(address(0xDEAD), 1337, 1); - } - - function testFailTransferBalanceUnderflow() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, 1); - } - - function testFailTransferBalanceOverflow() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, type(uint256).max); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, type(uint256).max); - - token.mint(sender, 1337, 1); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, 1); - } - - function testFailTransferFromBalanceUnderflow() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, 1); - } - - function testFailTransferFromBalanceOverflow() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, type(uint256).max); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, type(uint256).max); - - token.mint(sender, 1337, 1); - - hevm.prank(sender); - token.transferFrom(sender, receiver, 1337, 1); - } - - function testFailTransferFromNotAuthorized() public { - address sender = address(0xABCD); - address receiver = address(0xBEEF); - - token.mint(sender, 1337, 100); - - token.transferFrom(sender, receiver, 1337, 100); - } - - function testMint( - address receiver, - uint256 id, - uint256 amount - ) public { - token.mint(receiver, id, amount); - - assertEq(token.balanceOf(receiver, id), amount); - } - - function testBurn( - address sender, - uint256 id, - uint256 amount - ) public { - token.mint(sender, id, amount); - token.burn(sender, id, amount); - - assertEq(token.balanceOf(sender, id), 0); - } - - function testSetOperator(address operator, bool approved) public { - token.setOperator(operator, approved); - - assertBoolEq(token.isOperator(address(this), operator), approved); - } - - function testApprove( - address spender, - uint256 id, - uint256 amount - ) public { - token.approve(spender, id, amount); - - assertEq(token.allowance(address(this), spender, id), amount); - } - - function testTransfer( - address sender, - address receiver, - uint256 id, - uint256 mintAmount, - uint256 transferAmount - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(sender, id, mintAmount); - - hevm.prank(sender); - token.transfer(receiver, id, transferAmount); - - if (sender == receiver) { - assertEq(token.balanceOf(sender, id), mintAmount); - } else { - assertEq(token.balanceOf(sender, id), mintAmount - transferAmount); - assertEq(token.balanceOf(receiver, id), transferAmount); - } - } - - function testTransferFromWithApproval( - address sender, - address receiver, - uint256 id, - uint256 mintAmount, - uint256 transferAmount - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(sender, id, mintAmount); - - hevm.prank(sender); - token.approve(address(this), id, mintAmount); - - token.transferFrom(sender, receiver, id, transferAmount); - - if (mintAmount == type(uint256).max) { - assertEq(token.allowance(sender, address(this), id), type(uint256).max); - } else { - assertEq(token.allowance(sender, address(this), id), mintAmount - transferAmount); - } - - if (sender == receiver) { - assertEq(token.balanceOf(sender, id), mintAmount); - } else { - assertEq(token.balanceOf(sender, id), mintAmount - transferAmount); - assertEq(token.balanceOf(receiver, id), transferAmount); - } - } - - function testTransferFromWithInfiniteApproval( - address sender, - address receiver, - uint256 id, - uint256 mintAmount, - uint256 transferAmount - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(sender, id, mintAmount); - - hevm.prank(sender); - token.approve(address(this), id, type(uint256).max); - - token.transferFrom(sender, receiver, id, transferAmount); - - assertEq(token.allowance(sender, address(this), id), type(uint256).max); - - if (sender == receiver) { - assertEq(token.balanceOf(sender, id), mintAmount); - } else { - assertEq(token.balanceOf(sender, id), mintAmount - transferAmount); - assertEq(token.balanceOf(receiver, id), transferAmount); - } - } - - function testTransferFromAsOperator( - address sender, - address receiver, - uint256 id, - uint256 mintAmount, - uint256 transferAmount - ) public { - transferAmount = bound(transferAmount, 0, mintAmount); - - token.mint(sender, id, mintAmount); - - hevm.prank(sender); - token.setOperator(address(this), true); - - token.transferFrom(sender, receiver, id, transferAmount); - - if (sender == receiver) { - assertEq(token.balanceOf(sender, id), mintAmount); - } else { - assertEq(token.balanceOf(sender, id), mintAmount - transferAmount); - assertEq(token.balanceOf(receiver, id), transferAmount); - } - } - - function testFailTransferBalanceUnderflow( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public { - amount = bound(amount, 1, type(uint256).max); - - hevm.prank(sender); - token.transfer(receiver, id, amount); - } - - function testFailTransferBalanceOverflow( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public { - amount = bound(amount, 1, type(uint256).max); - uint256 overflowAmount = type(uint256).max - amount + 1; - - token.mint(sender, id, amount); - - hevm.prank(sender); - token.transfer(receiver, id, amount); - - token.mint(sender, id, overflowAmount); - - hevm.prank(sender); - token.transfer(receiver, id, overflowAmount); - } - - function testFailTransferFromBalanceUnderflow( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public { - amount = bound(amount, 1, type(uint256).max); - - hevm.prank(sender); - token.transferFrom(sender, receiver, id, amount); - } - - function testFailTransferFromBalanceOverflow( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public { - amount = bound(amount, 1, type(uint256).max); - uint256 overflowAmount = type(uint256).max - amount + 1; - - token.mint(sender, id, amount); - - hevm.prank(sender); - token.transferFrom(sender, receiver, id, amount); - - token.mint(sender, id, overflowAmount); - - hevm.prank(sender); - token.transferFrom(sender, receiver, id, overflowAmount); - } - - function testFailTransferFromNotAuthorized( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public { - amount = bound(amount, 1, type(uint256).max); - - token.mint(sender, id, amount); - - token.transferFrom(sender, receiver, id, amount); - } -} diff --git a/lib/solmate/src/test/ERC721.t.sol b/lib/solmate/src/test/ERC721.t.sol deleted file mode 100644 index 81de0ff..0000000 --- a/lib/solmate/src/test/ERC721.t.sol +++ /dev/null @@ -1,727 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; - -import {MockERC721} from "./utils/mocks/MockERC721.sol"; - -import {ERC721TokenReceiver} from "../tokens/ERC721.sol"; - -contract ERC721Recipient is ERC721TokenReceiver { - address public operator; - address public from; - uint256 public id; - bytes public data; - - function onERC721Received( - address _operator, - address _from, - uint256 _id, - bytes calldata _data - ) public virtual override returns (bytes4) { - operator = _operator; - from = _from; - id = _id; - data = _data; - - return ERC721TokenReceiver.onERC721Received.selector; - } -} - -contract RevertingERC721Recipient is ERC721TokenReceiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) public virtual override returns (bytes4) { - revert(string(abi.encodePacked(ERC721TokenReceiver.onERC721Received.selector))); - } -} - -contract WrongReturnDataERC721Recipient is ERC721TokenReceiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) public virtual override returns (bytes4) { - return 0xCAFEBEEF; - } -} - -contract NonERC721Recipient {} - -contract ERC721Test is DSTestPlus { - MockERC721 token; - - function setUp() public { - token = new MockERC721("Token", "TKN"); - } - - function invariantMetadata() public { - assertEq(token.name(), "Token"); - assertEq(token.symbol(), "TKN"); - } - - function testMint() public { - token.mint(address(0xBEEF), 1337); - - assertEq(token.balanceOf(address(0xBEEF)), 1); - assertEq(token.ownerOf(1337), address(0xBEEF)); - } - - function testBurn() public { - token.mint(address(0xBEEF), 1337); - token.burn(1337); - - assertEq(token.balanceOf(address(0xBEEF)), 0); - - hevm.expectRevert("NOT_MINTED"); - token.ownerOf(1337); - } - - function testApprove() public { - token.mint(address(this), 1337); - - token.approve(address(0xBEEF), 1337); - - assertEq(token.getApproved(1337), address(0xBEEF)); - } - - function testApproveBurn() public { - token.mint(address(this), 1337); - - token.approve(address(0xBEEF), 1337); - - token.burn(1337); - - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.getApproved(1337), address(0)); - - hevm.expectRevert("NOT_MINTED"); - token.ownerOf(1337); - } - - function testApproveAll() public { - token.setApprovalForAll(address(0xBEEF), true); - - assertTrue(token.isApprovedForAll(address(this), address(0xBEEF))); - } - - function testTransferFrom() public { - address from = address(0xABCD); - - token.mint(from, 1337); - - hevm.prank(from); - token.approve(address(this), 1337); - - token.transferFrom(from, address(0xBEEF), 1337); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(0xBEEF)); - assertEq(token.balanceOf(address(0xBEEF)), 1); - assertEq(token.balanceOf(from), 0); - } - - function testTransferFromSelf() public { - token.mint(address(this), 1337); - - token.transferFrom(address(this), address(0xBEEF), 1337); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(0xBEEF)); - assertEq(token.balanceOf(address(0xBEEF)), 1); - assertEq(token.balanceOf(address(this)), 0); - } - - function testTransferFromApproveAll() public { - address from = address(0xABCD); - - token.mint(from, 1337); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.transferFrom(from, address(0xBEEF), 1337); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(0xBEEF)); - assertEq(token.balanceOf(address(0xBEEF)), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToEOA() public { - address from = address(0xABCD); - - token.mint(from, 1337); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(0xBEEF), 1337); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(0xBEEF)); - assertEq(token.balanceOf(address(0xBEEF)), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToERC721Recipient() public { - address from = address(0xABCD); - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, 1337); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(recipient), 1337); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - assertEq(recipient.id(), 1337); - assertBytesEq(recipient.data(), ""); - } - - function testSafeTransferFromToERC721RecipientWithData() public { - address from = address(0xABCD); - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, 1337); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(recipient), 1337, "testing 123"); - - assertEq(token.getApproved(1337), address(0)); - assertEq(token.ownerOf(1337), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - assertEq(recipient.id(), 1337); - assertBytesEq(recipient.data(), "testing 123"); - } - - function testSafeMintToEOA() public { - token.safeMint(address(0xBEEF), 1337); - - assertEq(token.ownerOf(1337), address(address(0xBEEF))); - assertEq(token.balanceOf(address(address(0xBEEF))), 1); - } - - function testSafeMintToERC721Recipient() public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), 1337); - - assertEq(token.ownerOf(1337), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), 1337); - assertBytesEq(to.data(), ""); - } - - function testSafeMintToERC721RecipientWithData() public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), 1337, "testing 123"); - - assertEq(token.ownerOf(1337), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), 1337); - assertBytesEq(to.data(), "testing 123"); - } - - function testFailMintToZero() public { - token.mint(address(0), 1337); - } - - function testFailDoubleMint() public { - token.mint(address(0xBEEF), 1337); - token.mint(address(0xBEEF), 1337); - } - - function testFailBurnUnMinted() public { - token.burn(1337); - } - - function testFailDoubleBurn() public { - token.mint(address(0xBEEF), 1337); - - token.burn(1337); - token.burn(1337); - } - - function testFailApproveUnMinted() public { - token.approve(address(0xBEEF), 1337); - } - - function testFailApproveUnAuthorized() public { - token.mint(address(0xCAFE), 1337); - - token.approve(address(0xBEEF), 1337); - } - - function testFailTransferFromUnOwned() public { - token.transferFrom(address(0xFEED), address(0xBEEF), 1337); - } - - function testFailTransferFromWrongFrom() public { - token.mint(address(0xCAFE), 1337); - - token.transferFrom(address(0xFEED), address(0xBEEF), 1337); - } - - function testFailTransferFromToZero() public { - token.mint(address(this), 1337); - - token.transferFrom(address(this), address(0), 1337); - } - - function testFailTransferFromNotOwner() public { - token.mint(address(0xFEED), 1337); - - token.transferFrom(address(0xFEED), address(0xBEEF), 1337); - } - - function testFailSafeTransferFromToNonERC721Recipient() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337); - } - - function testFailSafeTransferFromToNonERC721RecipientWithData() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new NonERC721Recipient()), 1337, "testing 123"); - } - - function testFailSafeTransferFromToRevertingERC721Recipient() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337); - } - - function testFailSafeTransferFromToRevertingERC721RecipientWithData() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), 1337, "testing 123"); - } - - function testFailSafeTransferFromToERC721RecipientWithWrongReturnData() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337); - } - - function testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() public { - token.mint(address(this), 1337); - - token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), 1337, "testing 123"); - } - - function testFailSafeMintToNonERC721Recipient() public { - token.safeMint(address(new NonERC721Recipient()), 1337); - } - - function testFailSafeMintToNonERC721RecipientWithData() public { - token.safeMint(address(new NonERC721Recipient()), 1337, "testing 123"); - } - - function testFailSafeMintToRevertingERC721Recipient() public { - token.safeMint(address(new RevertingERC721Recipient()), 1337); - } - - function testFailSafeMintToRevertingERC721RecipientWithData() public { - token.safeMint(address(new RevertingERC721Recipient()), 1337, "testing 123"); - } - - function testFailSafeMintToERC721RecipientWithWrongReturnData() public { - token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337); - } - - function testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() public { - token.safeMint(address(new WrongReturnDataERC721Recipient()), 1337, "testing 123"); - } - - function testFailBalanceOfZeroAddress() public view { - token.balanceOf(address(0)); - } - - function testFailOwnerOfUnminted() public view { - token.ownerOf(1337); - } - - function testMetadata(string memory name, string memory symbol) public { - MockERC721 tkn = new MockERC721(name, symbol); - - assertEq(tkn.name(), name); - assertEq(tkn.symbol(), symbol); - } - - function testMint(address to, uint256 id) public { - if (to == address(0)) to = address(0xBEEF); - - token.mint(to, id); - - assertEq(token.balanceOf(to), 1); - assertEq(token.ownerOf(id), to); - } - - function testBurn(address to, uint256 id) public { - if (to == address(0)) to = address(0xBEEF); - - token.mint(to, id); - token.burn(id); - - assertEq(token.balanceOf(to), 0); - - hevm.expectRevert("NOT_MINTED"); - token.ownerOf(id); - } - - function testApprove(address to, uint256 id) public { - if (to == address(0)) to = address(0xBEEF); - - token.mint(address(this), id); - - token.approve(to, id); - - assertEq(token.getApproved(id), to); - } - - function testApproveBurn(address to, uint256 id) public { - token.mint(address(this), id); - - token.approve(address(to), id); - - token.burn(id); - - assertEq(token.balanceOf(address(this)), 0); - assertEq(token.getApproved(id), address(0)); - - hevm.expectRevert("NOT_MINTED"); - token.ownerOf(id); - } - - function testApproveAll(address to, bool approved) public { - token.setApprovalForAll(to, approved); - - assertBoolEq(token.isApprovedForAll(address(this), to), approved); - } - - function testTransferFrom(uint256 id, address to) public { - address from = address(0xABCD); - - if (to == address(0) || to == from) to = address(0xBEEF); - - token.mint(from, id); - - hevm.prank(from); - token.approve(address(this), id); - - token.transferFrom(from, to, id); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testTransferFromSelf(uint256 id, address to) public { - if (to == address(0) || to == address(this)) to = address(0xBEEF); - - token.mint(address(this), id); - - token.transferFrom(address(this), to, id); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(address(this)), 0); - } - - function testTransferFromApproveAll(uint256 id, address to) public { - address from = address(0xABCD); - - if (to == address(0) || to == from) to = address(0xBEEF); - - token.mint(from, id); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.transferFrom(from, to, id); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToEOA(uint256 id, address to) public { - address from = address(0xABCD); - - if (to == address(0) || to == from) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - token.mint(from, id); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, to, id); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), to); - assertEq(token.balanceOf(to), 1); - assertEq(token.balanceOf(from), 0); - } - - function testSafeTransferFromToERC721Recipient(uint256 id) public { - address from = address(0xABCD); - - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, id); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(recipient), id); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - assertEq(recipient.id(), id); - assertBytesEq(recipient.data(), ""); - } - - function testSafeTransferFromToERC721RecipientWithData(uint256 id, bytes calldata data) public { - address from = address(0xABCD); - ERC721Recipient recipient = new ERC721Recipient(); - - token.mint(from, id); - - hevm.prank(from); - token.setApprovalForAll(address(this), true); - - token.safeTransferFrom(from, address(recipient), id, data); - - assertEq(token.getApproved(id), address(0)); - assertEq(token.ownerOf(id), address(recipient)); - assertEq(token.balanceOf(address(recipient)), 1); - assertEq(token.balanceOf(from), 0); - - assertEq(recipient.operator(), address(this)); - assertEq(recipient.from(), from); - assertEq(recipient.id(), id); - assertBytesEq(recipient.data(), data); - } - - function testSafeMintToEOA(uint256 id, address to) public { - if (to == address(0)) to = address(0xBEEF); - - if (uint256(uint160(to)) <= 18 || to.code.length > 0) return; - - token.safeMint(to, id); - - assertEq(token.ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - } - - function testSafeMintToERC721Recipient(uint256 id) public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), id); - - assertEq(token.ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), id); - assertBytesEq(to.data(), ""); - } - - function testSafeMintToERC721RecipientWithData(uint256 id, bytes calldata data) public { - ERC721Recipient to = new ERC721Recipient(); - - token.safeMint(address(to), id, data); - - assertEq(token.ownerOf(id), address(to)); - assertEq(token.balanceOf(address(to)), 1); - - assertEq(to.operator(), address(this)); - assertEq(to.from(), address(0)); - assertEq(to.id(), id); - assertBytesEq(to.data(), data); - } - - function testFailMintToZero(uint256 id) public { - token.mint(address(0), id); - } - - function testFailDoubleMint(uint256 id, address to) public { - if (to == address(0)) to = address(0xBEEF); - - token.mint(to, id); - token.mint(to, id); - } - - function testFailBurnUnMinted(uint256 id) public { - token.burn(id); - } - - function testFailDoubleBurn(uint256 id, address to) public { - if (to == address(0)) to = address(0xBEEF); - - token.mint(to, id); - - token.burn(id); - token.burn(id); - } - - function testFailApproveUnMinted(uint256 id, address to) public { - token.approve(to, id); - } - - function testFailApproveUnAuthorized( - address owner, - uint256 id, - address to - ) public { - if (owner == address(0) || owner == address(this)) owner = address(0xBEEF); - - token.mint(owner, id); - - token.approve(to, id); - } - - function testFailTransferFromUnOwned( - address from, - address to, - uint256 id - ) public { - token.transferFrom(from, to, id); - } - - function testFailTransferFromWrongFrom( - address owner, - address from, - address to, - uint256 id - ) public { - if (owner == address(0)) to = address(0xBEEF); - if (from == owner) revert(); - - token.mint(owner, id); - - token.transferFrom(from, to, id); - } - - function testFailTransferFromToZero(uint256 id) public { - token.mint(address(this), id); - - token.transferFrom(address(this), address(0), id); - } - - function testFailTransferFromNotOwner( - address from, - address to, - uint256 id - ) public { - if (from == address(this)) from = address(0xBEEF); - - token.mint(from, id); - - token.transferFrom(from, to, id); - } - - function testFailSafeTransferFromToNonERC721Recipient(uint256 id) public { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id); - } - - function testFailSafeTransferFromToNonERC721RecipientWithData(uint256 id, bytes calldata data) public { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new NonERC721Recipient()), id, data); - } - - function testFailSafeTransferFromToRevertingERC721Recipient(uint256 id) public { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id); - } - - function testFailSafeTransferFromToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new RevertingERC721Recipient()), id, data); - } - - function testFailSafeTransferFromToERC721RecipientWithWrongReturnData(uint256 id) public { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id); - } - - function testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data) - public - { - token.mint(address(this), id); - - token.safeTransferFrom(address(this), address(new WrongReturnDataERC721Recipient()), id, data); - } - - function testFailSafeMintToNonERC721Recipient(uint256 id) public { - token.safeMint(address(new NonERC721Recipient()), id); - } - - function testFailSafeMintToNonERC721RecipientWithData(uint256 id, bytes calldata data) public { - token.safeMint(address(new NonERC721Recipient()), id, data); - } - - function testFailSafeMintToRevertingERC721Recipient(uint256 id) public { - token.safeMint(address(new RevertingERC721Recipient()), id); - } - - function testFailSafeMintToRevertingERC721RecipientWithData(uint256 id, bytes calldata data) public { - token.safeMint(address(new RevertingERC721Recipient()), id, data); - } - - function testFailSafeMintToERC721RecipientWithWrongReturnData(uint256 id) public { - token.safeMint(address(new WrongReturnDataERC721Recipient()), id); - } - - function testFailSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256 id, bytes calldata data) public { - token.safeMint(address(new WrongReturnDataERC721Recipient()), id, data); - } - - function testFailOwnerOfUnminted(uint256 id) public view { - token.ownerOf(id); - } -} diff --git a/lib/solmate/src/test/FixedPointMathLib.t.sol b/lib/solmate/src/test/FixedPointMathLib.t.sol deleted file mode 100644 index 789f957..0000000 --- a/lib/solmate/src/test/FixedPointMathLib.t.sol +++ /dev/null @@ -1,360 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; - -contract FixedPointMathLibTest is DSTestPlus { - function testMulWadDown() public { - assertEq(FixedPointMathLib.mulWadDown(2.5e18, 0.5e18), 1.25e18); - assertEq(FixedPointMathLib.mulWadDown(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.mulWadDown(369, 271), 0); - } - - function testMulWadDownEdgeCases() public { - assertEq(FixedPointMathLib.mulWadDown(0, 1e18), 0); - assertEq(FixedPointMathLib.mulWadDown(1e18, 0), 0); - assertEq(FixedPointMathLib.mulWadDown(0, 0), 0); - } - - function testMulWadUp() public { - assertEq(FixedPointMathLib.mulWadUp(2.5e18, 0.5e18), 1.25e18); - assertEq(FixedPointMathLib.mulWadUp(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.mulWadUp(369, 271), 1); - } - - function testMulWadUpEdgeCases() public { - assertEq(FixedPointMathLib.mulWadUp(0, 1e18), 0); - assertEq(FixedPointMathLib.mulWadUp(1e18, 0), 0); - assertEq(FixedPointMathLib.mulWadUp(0, 0), 0); - } - - function testDivWadDown() public { - assertEq(FixedPointMathLib.divWadDown(1.25e18, 0.5e18), 2.5e18); - assertEq(FixedPointMathLib.divWadDown(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.divWadDown(2, 100000000000000e18), 0); - } - - function testDivWadDownEdgeCases() public { - assertEq(FixedPointMathLib.divWadDown(0, 1e18), 0); - } - - function testFailDivWadDownZeroDenominator() public pure { - FixedPointMathLib.divWadDown(1e18, 0); - } - - function testDivWadUp() public { - assertEq(FixedPointMathLib.divWadUp(1.25e18, 0.5e18), 2.5e18); - assertEq(FixedPointMathLib.divWadUp(3e18, 1e18), 3e18); - assertEq(FixedPointMathLib.divWadUp(2, 100000000000000e18), 1); - } - - function testDivWadUpEdgeCases() public { - assertEq(FixedPointMathLib.divWadUp(0, 1e18), 0); - } - - function testFailDivWadUpZeroDenominator() public pure { - FixedPointMathLib.divWadUp(1e18, 0); - } - - function testMulDivDown() public { - assertEq(FixedPointMathLib.mulDivDown(2.5e27, 0.5e27, 1e27), 1.25e27); - assertEq(FixedPointMathLib.mulDivDown(2.5e18, 0.5e18, 1e18), 1.25e18); - assertEq(FixedPointMathLib.mulDivDown(2.5e8, 0.5e8, 1e8), 1.25e8); - assertEq(FixedPointMathLib.mulDivDown(369, 271, 1e2), 999); - - assertEq(FixedPointMathLib.mulDivDown(1e27, 1e27, 2e27), 0.5e27); - assertEq(FixedPointMathLib.mulDivDown(1e18, 1e18, 2e18), 0.5e18); - assertEq(FixedPointMathLib.mulDivDown(1e8, 1e8, 2e8), 0.5e8); - - assertEq(FixedPointMathLib.mulDivDown(2e27, 3e27, 2e27), 3e27); - assertEq(FixedPointMathLib.mulDivDown(3e18, 2e18, 3e18), 2e18); - assertEq(FixedPointMathLib.mulDivDown(2e8, 3e8, 2e8), 3e8); - } - - function testMulDivDownEdgeCases() public { - assertEq(FixedPointMathLib.mulDivDown(0, 1e18, 1e18), 0); - assertEq(FixedPointMathLib.mulDivDown(1e18, 0, 1e18), 0); - assertEq(FixedPointMathLib.mulDivDown(0, 0, 1e18), 0); - } - - function testFailMulDivDownZeroDenominator() public pure { - FixedPointMathLib.mulDivDown(1e18, 1e18, 0); - } - - function testMulDivUp() public { - assertEq(FixedPointMathLib.mulDivUp(2.5e27, 0.5e27, 1e27), 1.25e27); - assertEq(FixedPointMathLib.mulDivUp(2.5e18, 0.5e18, 1e18), 1.25e18); - assertEq(FixedPointMathLib.mulDivUp(2.5e8, 0.5e8, 1e8), 1.25e8); - assertEq(FixedPointMathLib.mulDivUp(369, 271, 1e2), 1000); - - assertEq(FixedPointMathLib.mulDivUp(1e27, 1e27, 2e27), 0.5e27); - assertEq(FixedPointMathLib.mulDivUp(1e18, 1e18, 2e18), 0.5e18); - assertEq(FixedPointMathLib.mulDivUp(1e8, 1e8, 2e8), 0.5e8); - - assertEq(FixedPointMathLib.mulDivUp(2e27, 3e27, 2e27), 3e27); - assertEq(FixedPointMathLib.mulDivUp(3e18, 2e18, 3e18), 2e18); - assertEq(FixedPointMathLib.mulDivUp(2e8, 3e8, 2e8), 3e8); - } - - function testMulDivUpEdgeCases() public { - assertEq(FixedPointMathLib.mulDivUp(0, 1e18, 1e18), 0); - assertEq(FixedPointMathLib.mulDivUp(1e18, 0, 1e18), 0); - assertEq(FixedPointMathLib.mulDivUp(0, 0, 1e18), 0); - } - - function testFailMulDivUpZeroDenominator() public pure { - FixedPointMathLib.mulDivUp(1e18, 1e18, 0); - } - - function testRPow() public { - assertEq(FixedPointMathLib.rpow(2e27, 2, 1e27), 4e27); - assertEq(FixedPointMathLib.rpow(2e18, 2, 1e18), 4e18); - assertEq(FixedPointMathLib.rpow(2e8, 2, 1e8), 4e8); - assertEq(FixedPointMathLib.rpow(8, 3, 1), 512); - } - - function testSqrt() public { - assertEq(FixedPointMathLib.sqrt(0), 0); - assertEq(FixedPointMathLib.sqrt(1), 1); - assertEq(FixedPointMathLib.sqrt(2704), 52); - assertEq(FixedPointMathLib.sqrt(110889), 333); - assertEq(FixedPointMathLib.sqrt(32239684), 5678); - assertEq(FixedPointMathLib.sqrt(type(uint256).max), 340282366920938463463374607431768211455); - } - - function testSqrtBackHashedSingle() public { - testSqrtBackHashed(123); - } - - function testMulWadDown(uint256 x, uint256 y) public { - // Ignore cases where x * y overflows. - unchecked { - if (x != 0 && (x * y) / x != y) return; - } - - assertEq(FixedPointMathLib.mulWadDown(x, y), (x * y) / 1e18); - } - - function testFailMulWadDownOverflow(uint256 x, uint256 y) public pure { - // Ignore cases where x * y does not overflow. - unchecked { - if ((x * y) / x == y) revert(); - } - - FixedPointMathLib.mulWadDown(x, y); - } - - function testMulWadUp(uint256 x, uint256 y) public { - // Ignore cases where x * y overflows. - unchecked { - if (x != 0 && (x * y) / x != y) return; - } - - assertEq(FixedPointMathLib.mulWadUp(x, y), x * y == 0 ? 0 : (x * y - 1) / 1e18 + 1); - } - - function testFailMulWadUpOverflow(uint256 x, uint256 y) public pure { - // Ignore cases where x * y does not overflow. - unchecked { - if ((x * y) / x == y) revert(); - } - - FixedPointMathLib.mulWadUp(x, y); - } - - function testDivWadDown(uint256 x, uint256 y) public { - // Ignore cases where x * WAD overflows or y is 0. - unchecked { - if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; - } - - assertEq(FixedPointMathLib.divWadDown(x, y), (x * 1e18) / y); - } - - function testFailDivWadDownOverflow(uint256 x, uint256 y) public pure { - // Ignore cases where x * WAD does not overflow or y is 0. - unchecked { - if (y == 0 || (x * 1e18) / 1e18 == x) revert(); - } - - FixedPointMathLib.divWadDown(x, y); - } - - function testFailDivWadDownZeroDenominator(uint256 x) public pure { - FixedPointMathLib.divWadDown(x, 0); - } - - function testDivWadUp(uint256 x, uint256 y) public { - // Ignore cases where x * WAD overflows or y is 0. - unchecked { - if (y == 0 || (x != 0 && (x * 1e18) / 1e18 != x)) return; - } - - assertEq(FixedPointMathLib.divWadUp(x, y), x == 0 ? 0 : (x * 1e18 - 1) / y + 1); - } - - function testFailDivWadUpOverflow(uint256 x, uint256 y) public pure { - // Ignore cases where x * WAD does not overflow or y is 0. - unchecked { - if (y == 0 || (x * 1e18) / 1e18 == x) revert(); - } - - FixedPointMathLib.divWadUp(x, y); - } - - function testFailDivWadUpZeroDenominator(uint256 x) public pure { - FixedPointMathLib.divWadUp(x, 0); - } - - function testMulDivDown( - uint256 x, - uint256 y, - uint256 denominator - ) public { - // Ignore cases where x * y overflows or denominator is 0. - unchecked { - if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; - } - - assertEq(FixedPointMathLib.mulDivDown(x, y, denominator), (x * y) / denominator); - } - - function testFailMulDivDownOverflow( - uint256 x, - uint256 y, - uint256 denominator - ) public pure { - // Ignore cases where x * y does not overflow or denominator is 0. - unchecked { - if (denominator == 0 || (x * y) / x == y) revert(); - } - - FixedPointMathLib.mulDivDown(x, y, denominator); - } - - function testFailMulDivDownZeroDenominator(uint256 x, uint256 y) public pure { - FixedPointMathLib.mulDivDown(x, y, 0); - } - - function testMulDivUp( - uint256 x, - uint256 y, - uint256 denominator - ) public { - // Ignore cases where x * y overflows or denominator is 0. - unchecked { - if (denominator == 0 || (x != 0 && (x * y) / x != y)) return; - } - - assertEq(FixedPointMathLib.mulDivUp(x, y, denominator), x * y == 0 ? 0 : (x * y - 1) / denominator + 1); - } - - function testFailMulDivUpOverflow( - uint256 x, - uint256 y, - uint256 denominator - ) public pure { - // Ignore cases where x * y does not overflow or denominator is 0. - unchecked { - if (denominator == 0 || (x * y) / x == y) revert(); - } - - FixedPointMathLib.mulDivUp(x, y, denominator); - } - - function testFailMulDivUpZeroDenominator(uint256 x, uint256 y) public pure { - FixedPointMathLib.mulDivUp(x, y, 0); - } - - function testDifferentiallyFuzzSqrt(uint256 x) public { - assertEq(FixedPointMathLib.sqrt(x), uniswapSqrt(x)); - assertEq(FixedPointMathLib.sqrt(x), abdkSqrt(x)); - } - - function testSqrt(uint256 x) public { - uint256 root = FixedPointMathLib.sqrt(x); - uint256 next = root + 1; - - // Ignore cases where next * next overflows. - unchecked { - if (next * next < next) return; - } - - assertTrue(root * root <= x && next * next > x); - } - - function testSqrtBack(uint256 x) public { - unchecked { - x >>= 128; - while (x != 0) { - assertEq(FixedPointMathLib.sqrt(x * x), x); - x >>= 1; - } - } - } - - function testSqrtBackHashed(uint256 x) public { - testSqrtBack(uint256(keccak256(abi.encode(x)))); - } - - function uniswapSqrt(uint256 y) internal pure returns (uint256 z) { - if (y > 3) { - z = y; - uint256 x = y / 2 + 1; - while (x < z) { - z = x; - x = (y / x + x) / 2; - } - } else if (y != 0) { - z = 1; - } - } - - function abdkSqrt(uint256 x) private pure returns (uint256) { - unchecked { - if (x == 0) return 0; - else { - uint256 xx = x; - uint256 r = 1; - if (xx >= 0x100000000000000000000000000000000) { - xx >>= 128; - r <<= 64; - } - if (xx >= 0x10000000000000000) { - xx >>= 64; - r <<= 32; - } - if (xx >= 0x100000000) { - xx >>= 32; - r <<= 16; - } - if (xx >= 0x10000) { - xx >>= 16; - r <<= 8; - } - if (xx >= 0x100) { - xx >>= 8; - r <<= 4; - } - if (xx >= 0x10) { - xx >>= 4; - r <<= 2; - } - if (xx >= 0x8) { - r <<= 1; - } - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; - r = (r + x / r) >> 1; // Seven iterations should be enough - uint256 r1 = x / r; - return r < r1 ? r : r1; - } - } - } -} diff --git a/lib/solmate/src/test/LibString.t.sol b/lib/solmate/src/test/LibString.t.sol deleted file mode 100644 index 36ed435..0000000 --- a/lib/solmate/src/test/LibString.t.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {LibString} from "../utils/LibString.sol"; - -contract LibStringTest is DSTestPlus { - function testToString() public { - assertEq(LibString.toString(uint256(0)), "0"); - assertEq(LibString.toString(uint256(1)), "1"); - assertEq(LibString.toString(uint256(17)), "17"); - assertEq(LibString.toString(uint256(99999999)), "99999999"); - assertEq(LibString.toString(uint256(99999999999)), "99999999999"); - assertEq(LibString.toString(uint256(2342343923423)), "2342343923423"); - assertEq(LibString.toString(uint256(98765685434567)), "98765685434567"); - } - - function testToStringIntPositive() public { - assertEq(LibString.toString(int256(0)), "0"); - assertEq(LibString.toString(int256(1)), "1"); - assertEq(LibString.toString(int256(17)), "17"); - assertEq(LibString.toString(int256(99999999)), "99999999"); - assertEq(LibString.toString(int256(99999999999)), "99999999999"); - assertEq(LibString.toString(int256(2342343923423)), "2342343923423"); - assertEq(LibString.toString(int256(98765685434567)), "98765685434567"); - } - - function testToStringIntNegative() public { - assertEq(LibString.toString(int256(-0)), "0"); - assertEq(LibString.toString(int256(-17)), "-17"); - assertEq(LibString.toString(int256(-99999999)), "-99999999"); - assertEq(LibString.toString(int256(-99999999999)), "-99999999999"); - assertEq(LibString.toString(int256(-2342343923423)), "-2342343923423"); - assertEq(LibString.toString(int256(-98765685434567)), "-98765685434567"); - } - - function testDifferentiallyFuzzToString(uint256 value, bytes calldata brutalizeWith) - public - brutalizeMemory(brutalizeWith) - { - string memory libString = LibString.toString(value); - string memory oz = toStringOZ(value); - - assertEq(bytes(libString).length, bytes(oz).length); - assertEq(libString, oz); - } - - function testDifferentiallyFuzzToStringInt(int256 value, bytes calldata brutalizeWith) - public - brutalizeMemory(brutalizeWith) - { - string memory libString = LibString.toString(value); - string memory oz = toStringOZ(value); - - assertEq(bytes(libString).length, bytes(oz).length); - assertEq(libString, oz); - } - - function testToStringOverwrite() public { - string memory str = LibString.toString(uint256(1)); - - bytes32 data; - bytes32 expected; - - assembly { - // Imagine a high level allocation writing something to the current free memory. - // Should have sufficient higher order bits for this to be visible - mstore(mload(0x40), not(0)) - // Correctly allocate 32 more bytes, to avoid more interference - mstore(0x40, add(mload(0x40), 32)) - data := mload(add(str, 32)) - - // the expected value should be the uft-8 encoding of 1 (49), - // followed by clean bits. We achieve this by taking the value and - // shifting left to the end of the 32 byte word - expected := shl(248, 49) - } - - assertEq(data, expected); - } - - function testToStringDirty() public { - uint256 freememptr; - // Make the next 4 bytes of the free memory dirty - assembly { - let dirty := not(0) - freememptr := mload(0x40) - mstore(freememptr, dirty) - mstore(add(freememptr, 32), dirty) - mstore(add(freememptr, 64), dirty) - mstore(add(freememptr, 96), dirty) - mstore(add(freememptr, 128), dirty) - } - string memory str = LibString.toString(uint256(1)); - uint256 len; - bytes32 data; - bytes32 expected; - assembly { - freememptr := str - len := mload(str) - data := mload(add(str, 32)) - // the expected value should be the uft-8 encoding of 1 (49), - // followed by clean bits. We achieve this by taking the value and - // shifting left to the end of the 32 byte word - expected := shl(248, 49) - } - emit log_named_uint("str: ", freememptr); - emit log_named_uint("len: ", len); - emit log_named_bytes32("data: ", data); - assembly { - freememptr := mload(0x40) - } - emit log_named_uint("memptr: ", freememptr); - - assertEq(data, expected); - } -} - -function toStringOZ(int256 value) pure returns (string memory) { - return string(abi.encodePacked(value < 0 ? "-" : "", toStringOZ(absOZ(value)))); -} - -function toStringOZ(uint256 value) pure returns (string memory) { - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); -} - -function absOZ(int256 n) pure returns (uint256) { - unchecked { - // must be unchecked in order to support `n = type(int256).min` - return uint256(n >= 0 ? n : -n); - } -} diff --git a/lib/solmate/src/test/MerkleProofLib.t.sol b/lib/solmate/src/test/MerkleProofLib.t.sol deleted file mode 100644 index 5279679..0000000 --- a/lib/solmate/src/test/MerkleProofLib.t.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {MerkleProofLib} from "../utils/MerkleProofLib.sol"; - -contract MerkleProofLibTest is DSTestPlus { - function testVerifyEmptyMerkleProofSuppliedLeafAndRootSame() public { - bytes32[] memory proof; - assertBoolEq(this.verify(proof, 0x00, 0x00), true); - } - - function testVerifyEmptyMerkleProofSuppliedLeafAndRootDifferent() public { - bytes32[] memory proof; - bytes32 leaf = "a"; - assertBoolEq(this.verify(proof, 0x00, leaf), false); - } - - function testValidProofSupplied() public { - // Merkle tree created from leaves ['a', 'b', 'c']. - // Leaf is 'a'. - bytes32[] memory proof = new bytes32[](2); - proof[0] = 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510; - proof[1] = 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2; - bytes32 root = 0x5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7; - bytes32 leaf = 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb; - assertBoolEq(this.verify(proof, root, leaf), true); - } - - function testVerifyInvalidProofSupplied() public { - // Merkle tree created from leaves ['a', 'b', 'c']. - // Leaf is 'a'. - // Proof is same as testValidProofSupplied but last byte of first element is modified. - bytes32[] memory proof = new bytes32[](2); - proof[0] = 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5511; - proof[1] = 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2; - bytes32 root = 0x5842148bc6ebeb52af882a317c765fccd3ae80589b21a9b8cbf21abb630e46a7; - bytes32 leaf = 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb; - assertBoolEq(this.verify(proof, root, leaf), false); - } - - function verify( - bytes32[] calldata proof, - bytes32 root, - bytes32 leaf - ) external pure returns (bool) { - return MerkleProofLib.verify(proof, root, leaf); - } -} diff --git a/lib/solmate/src/test/MultiRolesAuthority.t.sol b/lib/solmate/src/test/MultiRolesAuthority.t.sol deleted file mode 100644 index eedf6a0..0000000 --- a/lib/solmate/src/test/MultiRolesAuthority.t.sol +++ /dev/null @@ -1,321 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; - -import {Authority} from "../auth/Auth.sol"; - -import {MultiRolesAuthority} from "../auth/authorities/MultiRolesAuthority.sol"; - -contract MultiRolesAuthorityTest is DSTestPlus { - MultiRolesAuthority multiRolesAuthority; - - function setUp() public { - multiRolesAuthority = new MultiRolesAuthority(address(this), Authority(address(0))); - } - - function testSetRoles() public { - assertFalse(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertTrue(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertFalse(multiRolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - } - - function testSetRoleCapabilities() public { - assertFalse(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); - assertFalse(multiRolesAuthority.doesRoleHaveCapability(0, 0xBEEFCAFE)); - } - - function testSetPublicCapabilities() public { - assertFalse(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); - assertFalse(multiRolesAuthority.isCapabilityPublic(0xBEEFCAFE)); - } - - function testSetTargetCustomAuthority() public { - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xBEEF), Authority(address(0xCAFE))); - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0xCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xBEEF), Authority(address(0))); - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(address(0xBEEF))), address(0)); - } - - function testCanCallWithAuthorizedRole() public { - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallPublicCapability() public { - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallWithCustomAuthority() public { - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallWithCustomAuthorityOverridesPublicCapability() public { - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, false); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setPublicCapability(0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallWithCustomAuthorityOverridesUserWithRole() public { - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setTargetCustomAuthority(address(0xCAFE), Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertTrue(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setRoleCapability(0, 0xBEEFCAFE, false); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - multiRolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertFalse(multiRolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testSetRoles(address user, uint8 role) public { - assertFalse(multiRolesAuthority.doesUserHaveRole(user, role)); - - multiRolesAuthority.setUserRole(user, role, true); - assertTrue(multiRolesAuthority.doesUserHaveRole(user, role)); - - multiRolesAuthority.setUserRole(user, role, false); - assertFalse(multiRolesAuthority.doesUserHaveRole(user, role)); - } - - function testSetRoleCapabilities(uint8 role, bytes4 functionSig) public { - assertFalse(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, true); - assertTrue(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, false); - assertFalse(multiRolesAuthority.doesRoleHaveCapability(role, functionSig)); - } - - function testSetPublicCapabilities(bytes4 functionSig) public { - assertFalse(multiRolesAuthority.isCapabilityPublic(functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, true); - assertTrue(multiRolesAuthority.isCapabilityPublic(functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, false); - assertFalse(multiRolesAuthority.isCapabilityPublic(functionSig)); - } - - function testSetTargetCustomAuthority(address user, Authority customAuthority) public { - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(0)); - - multiRolesAuthority.setTargetCustomAuthority(user, customAuthority); - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(customAuthority)); - - multiRolesAuthority.setTargetCustomAuthority(user, Authority(address(0))); - assertEq(address(multiRolesAuthority.getTargetCustomAuthority(user)), address(0)); - } - - function testCanCallWithAuthorizedRole( - address user, - uint8 role, - address target, - bytes4 functionSig - ) public { - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, true); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, false); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, false); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - } - - function testCanCallPublicCapability( - address user, - address target, - bytes4 functionSig - ) public { - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, false); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - } - - function testCanCallWithCustomAuthority( - address user, - address target, - bytes4 functionSig - ) public { - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - } - - function testCanCallWithCustomAuthorityOverridesPublicCapability( - address user, - address target, - bytes4 functionSig - ) public { - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, false); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setPublicCapability(functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - } - - function testCanCallWithCustomAuthorityOverridesUserWithRole( - address user, - uint8 role, - address target, - bytes4 functionSig - ) public { - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, true); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(false)); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, new MockAuthority(true)); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, false); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setTargetCustomAuthority(target, Authority(address(0))); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, true); - assertTrue(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setRoleCapability(role, functionSig, false); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - - multiRolesAuthority.setUserRole(user, role, false); - assertFalse(multiRolesAuthority.canCall(user, target, functionSig)); - } -} diff --git a/lib/solmate/src/test/Owned.t.sol b/lib/solmate/src/test/Owned.t.sol deleted file mode 100644 index 08a0239..0000000 --- a/lib/solmate/src/test/Owned.t.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {MockOwned} from "./utils/mocks/MockOwned.sol"; - -contract OwnedTest is DSTestPlus { - MockOwned mockOwned; - - function setUp() public { - mockOwned = new MockOwned(); - } - - function testTransferOwnership() public { - testTransferOwnership(address(0xBEEF)); - } - - function testCallFunctionAsNonOwner() public { - testCallFunctionAsNonOwner(address(0)); - } - - function testCallFunctionAsOwner() public { - mockOwned.updateFlag(); - } - - function testTransferOwnership(address newOwner) public { - mockOwned.transferOwnership(newOwner); - - assertEq(mockOwned.owner(), newOwner); - } - - function testCallFunctionAsNonOwner(address owner) public { - hevm.assume(owner != address(this)); - - mockOwned.transferOwnership(owner); - - hevm.expectRevert("UNAUTHORIZED"); - mockOwned.updateFlag(); - } -} diff --git a/lib/solmate/src/test/ReentrancyGuard.t.sol b/lib/solmate/src/test/ReentrancyGuard.t.sol deleted file mode 100644 index 842e367..0000000 --- a/lib/solmate/src/test/ReentrancyGuard.t.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {ReentrancyGuard} from "../utils/ReentrancyGuard.sol"; - -contract RiskyContract is ReentrancyGuard { - uint256 public enterTimes; - - function unprotectedCall() public { - enterTimes++; - - if (enterTimes > 1) return; - - this.protectedCall(); - } - - function protectedCall() public nonReentrant { - enterTimes++; - - if (enterTimes > 1) return; - - this.protectedCall(); - } - - function overprotectedCall() public nonReentrant {} -} - -contract ReentrancyGuardTest is DSTestPlus { - RiskyContract riskyContract; - - function setUp() public { - riskyContract = new RiskyContract(); - } - - function invariantReentrancyStatusAlways1() public { - assertEq(uint256(hevm.load(address(riskyContract), 0)), 1); - } - - function testFailUnprotectedCall() public { - riskyContract.unprotectedCall(); - - assertEq(riskyContract.enterTimes(), 1); - } - - function testProtectedCall() public { - try riskyContract.protectedCall() { - fail("Reentrancy Guard Failed To Stop Attacker"); - } catch {} - } - - function testNoReentrancy() public { - riskyContract.overprotectedCall(); - } -} diff --git a/lib/solmate/src/test/RolesAuthority.t.sol b/lib/solmate/src/test/RolesAuthority.t.sol deleted file mode 100644 index e01db7e..0000000 --- a/lib/solmate/src/test/RolesAuthority.t.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {MockAuthority} from "./utils/mocks/MockAuthority.sol"; - -import {Authority} from "../auth/Auth.sol"; - -import {RolesAuthority} from "../auth/authorities/RolesAuthority.sol"; - -contract RolesAuthorityTest is DSTestPlus { - RolesAuthority rolesAuthority; - - function setUp() public { - rolesAuthority = new RolesAuthority(address(this), Authority(address(0))); - } - - function testSetRoles() public { - assertFalse(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - - rolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertTrue(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - - rolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertFalse(rolesAuthority.doesUserHaveRole(address(0xBEEF), 0)); - } - - function testSetRoleCapabilities() public { - assertFalse(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); - assertTrue(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, false); - assertFalse(rolesAuthority.doesRoleHaveCapability(0, address(0xCAFE), 0xBEEFCAFE)); - } - - function testSetPublicCapabilities() public { - assertFalse(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, true); - assertTrue(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, false); - assertFalse(rolesAuthority.isCapabilityPublic(address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallWithAuthorizedRole() public { - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setUserRole(address(0xBEEF), 0, true); - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); - assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, false); - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setRoleCapability(0, address(0xCAFE), 0xBEEFCAFE, true); - assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setUserRole(address(0xBEEF), 0, false); - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testCanCallPublicCapability() public { - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, true); - assertTrue(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - - rolesAuthority.setPublicCapability(address(0xCAFE), 0xBEEFCAFE, false); - assertFalse(rolesAuthority.canCall(address(0xBEEF), address(0xCAFE), 0xBEEFCAFE)); - } - - function testSetRoles(address user, uint8 role) public { - assertFalse(rolesAuthority.doesUserHaveRole(user, role)); - - rolesAuthority.setUserRole(user, role, true); - assertTrue(rolesAuthority.doesUserHaveRole(user, role)); - - rolesAuthority.setUserRole(user, role, false); - assertFalse(rolesAuthority.doesUserHaveRole(user, role)); - } - - function testSetRoleCapabilities( - uint8 role, - address target, - bytes4 functionSig - ) public { - assertFalse(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); - - rolesAuthority.setRoleCapability(role, target, functionSig, true); - assertTrue(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); - - rolesAuthority.setRoleCapability(role, target, functionSig, false); - assertFalse(rolesAuthority.doesRoleHaveCapability(role, target, functionSig)); - } - - function testSetPublicCapabilities(address target, bytes4 functionSig) public { - assertFalse(rolesAuthority.isCapabilityPublic(target, functionSig)); - - rolesAuthority.setPublicCapability(target, functionSig, true); - assertTrue(rolesAuthority.isCapabilityPublic(target, functionSig)); - - rolesAuthority.setPublicCapability(target, functionSig, false); - assertFalse(rolesAuthority.isCapabilityPublic(target, functionSig)); - } - - function testCanCallWithAuthorizedRole( - address user, - uint8 role, - address target, - bytes4 functionSig - ) public { - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setUserRole(user, role, true); - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setRoleCapability(role, target, functionSig, true); - assertTrue(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setRoleCapability(role, target, functionSig, false); - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setRoleCapability(role, target, functionSig, true); - assertTrue(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setUserRole(user, role, false); - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - } - - function testCanCallPublicCapability( - address user, - address target, - bytes4 functionSig - ) public { - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setPublicCapability(target, functionSig, true); - assertTrue(rolesAuthority.canCall(user, target, functionSig)); - - rolesAuthority.setPublicCapability(target, functionSig, false); - assertFalse(rolesAuthority.canCall(user, target, functionSig)); - } -} diff --git a/lib/solmate/src/test/SSTORE2.t.sol b/lib/solmate/src/test/SSTORE2.t.sol deleted file mode 100644 index 5d97ed7..0000000 --- a/lib/solmate/src/test/SSTORE2.t.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {SSTORE2} from "../utils/SSTORE2.sol"; - -contract SSTORE2Test is DSTestPlus { - function testWriteRead() public { - bytes memory testBytes = abi.encode("this is a test"); - - address pointer = SSTORE2.write(testBytes); - - assertBytesEq(SSTORE2.read(pointer), testBytes); - } - - function testWriteReadFullStartBound() public { - assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 0), hex"11223344"); - } - - function testWriteReadCustomStartBound() public { - assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1), hex"223344"); - } - - function testWriteReadFullBoundedRead() public { - bytes memory testBytes = abi.encode("this is a test"); - - assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes), 0, testBytes.length), testBytes); - } - - function testWriteReadCustomBounds() public { - assertBytesEq(SSTORE2.read(SSTORE2.write(hex"11223344"), 1, 3), hex"2233"); - } - - function testWriteReadEmptyBound() public { - SSTORE2.read(SSTORE2.write(hex"11223344"), 3, 3); - } - - function testFailReadInvalidPointer() public view { - SSTORE2.read(DEAD_ADDRESS); - } - - function testFailReadInvalidPointerCustomStartBound() public view { - SSTORE2.read(DEAD_ADDRESS, 1); - } - - function testFailReadInvalidPointerCustomBounds() public view { - SSTORE2.read(DEAD_ADDRESS, 2, 4); - } - - function testFailWriteReadOutOfStartBound() public { - SSTORE2.read(SSTORE2.write(hex"11223344"), 41000); - } - - function testFailWriteReadEmptyOutOfBounds() public { - SSTORE2.read(SSTORE2.write(hex"11223344"), 42000, 42000); - } - - function testFailWriteReadOutOfBounds() public { - SSTORE2.read(SSTORE2.write(hex"11223344"), 41000, 42000); - } - - function testWriteRead(bytes calldata testBytes, bytes calldata brutalizeWith) - public - brutalizeMemory(brutalizeWith) - { - assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes)), testBytes); - } - - function testWriteReadCustomStartBound( - bytes calldata testBytes, - uint256 startIndex, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if (testBytes.length == 0) return; - - startIndex = bound(startIndex, 0, testBytes.length); - - assertBytesEq(SSTORE2.read(SSTORE2.write(testBytes), startIndex), bytes(testBytes[startIndex:])); - } - - function testWriteReadCustomBounds( - bytes calldata testBytes, - uint256 startIndex, - uint256 endIndex, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if (testBytes.length == 0) return; - - endIndex = bound(endIndex, 0, testBytes.length); - startIndex = bound(startIndex, 0, testBytes.length); - - if (startIndex > endIndex) return; - - assertBytesEq( - SSTORE2.read(SSTORE2.write(testBytes), startIndex, endIndex), - bytes(testBytes[startIndex:endIndex]) - ); - } - - function testFailReadInvalidPointer(address pointer, bytes calldata brutalizeWith) - public - view - brutalizeMemory(brutalizeWith) - { - if (pointer.code.length > 0) revert(); - - SSTORE2.read(pointer); - } - - function testFailReadInvalidPointerCustomStartBound( - address pointer, - uint256 startIndex, - bytes calldata brutalizeWith - ) public view brutalizeMemory(brutalizeWith) { - if (pointer.code.length > 0) revert(); - - SSTORE2.read(pointer, startIndex); - } - - function testFailReadInvalidPointerCustomBounds( - address pointer, - uint256 startIndex, - uint256 endIndex, - bytes calldata brutalizeWith - ) public view brutalizeMemory(brutalizeWith) { - if (pointer.code.length > 0) revert(); - - SSTORE2.read(pointer, startIndex, endIndex); - } - - function testFailWriteReadCustomStartBoundOutOfRange( - bytes calldata testBytes, - uint256 startIndex, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - startIndex = bound(startIndex, testBytes.length + 1, type(uint256).max); - - SSTORE2.read(SSTORE2.write(testBytes), startIndex); - } - - function testFailWriteReadCustomBoundsOutOfRange( - bytes calldata testBytes, - uint256 startIndex, - uint256 endIndex, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - endIndex = bound(endIndex, testBytes.length + 1, type(uint256).max); - - SSTORE2.read(SSTORE2.write(testBytes), startIndex, endIndex); - } -} diff --git a/lib/solmate/src/test/SafeCastLib.t.sol b/lib/solmate/src/test/SafeCastLib.t.sol deleted file mode 100644 index 52a6c07..0000000 --- a/lib/solmate/src/test/SafeCastLib.t.sol +++ /dev/null @@ -1,644 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {SafeCastLib} from "../utils/SafeCastLib.sol"; - -contract SafeCastLibTest is DSTestPlus { - function testSafeCastTo248() public { - assertEq(SafeCastLib.safeCastTo248(2.5e45), 2.5e45); - assertEq(SafeCastLib.safeCastTo248(2.5e27), 2.5e27); - } - - function testSafeCastTo240() public { - assertEq(SafeCastLib.safeCastTo240(2.5e45), 2.5e45); - assertEq(SafeCastLib.safeCastTo240(2.5e27), 2.5e27); - } - - function testSafeCastTo232() public { - assertEq(SafeCastLib.safeCastTo232(2.5e45), 2.5e45); - assertEq(SafeCastLib.safeCastTo232(2.5e27), 2.5e27); - } - - function testSafeCastTo224() public { - assertEq(SafeCastLib.safeCastTo224(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo224(2.5e27), 2.5e27); - } - - function testSafeCastTo216() public { - assertEq(SafeCastLib.safeCastTo216(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo216(2.5e27), 2.5e27); - } - - function testSafeCastTo208() public { - assertEq(SafeCastLib.safeCastTo208(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo208(2.5e27), 2.5e27); - } - - function testSafeCastTo200() public { - assertEq(SafeCastLib.safeCastTo200(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo200(2.5e27), 2.5e27); - } - - function testSafeCastTo192() public { - assertEq(SafeCastLib.safeCastTo192(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo192(2.5e27), 2.5e27); - } - - function testSafeCastTo184() public { - assertEq(SafeCastLib.safeCastTo184(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo184(2.5e27), 2.5e27); - } - - function testSafeCastTo176() public { - assertEq(SafeCastLib.safeCastTo176(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo176(2.5e27), 2.5e27); - } - - function testSafeCastTo168() public { - assertEq(SafeCastLib.safeCastTo168(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo168(2.5e27), 2.5e27); - } - - function testSafeCastTo160() public { - assertEq(SafeCastLib.safeCastTo160(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo160(2.5e27), 2.5e27); - } - - function testSafeCastTo152() public { - assertEq(SafeCastLib.safeCastTo152(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo152(2.5e27), 2.5e27); - } - - function testSafeCastTo144() public { - assertEq(SafeCastLib.safeCastTo144(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo144(2.5e27), 2.5e27); - } - - function testSafeCastTo136() public { - assertEq(SafeCastLib.safeCastTo136(2.5e36), 2.5e36); - assertEq(SafeCastLib.safeCastTo136(2.5e27), 2.5e27); - } - - function testSafeCastTo128() public { - assertEq(SafeCastLib.safeCastTo128(2.5e27), 2.5e27); - assertEq(SafeCastLib.safeCastTo128(2.5e18), 2.5e18); - } - - function testSafeCastTo120() public { - assertEq(SafeCastLib.safeCastTo120(2.5e27), 2.5e27); - assertEq(SafeCastLib.safeCastTo120(2.5e18), 2.5e18); - } - - function testSafeCastTo112() public { - assertEq(SafeCastLib.safeCastTo112(2.5e27), 2.5e27); - assertEq(SafeCastLib.safeCastTo112(2.5e18), 2.5e18); - } - - function testSafeCastTo104() public { - assertEq(SafeCastLib.safeCastTo104(2.5e27), 2.5e27); - assertEq(SafeCastLib.safeCastTo104(2.5e18), 2.5e18); - } - - function testSafeCastTo96() public { - assertEq(SafeCastLib.safeCastTo96(2.5e18), 2.5e18); - assertEq(SafeCastLib.safeCastTo96(2.5e17), 2.5e17); - } - - function testSafeCastTo64() public { - assertEq(SafeCastLib.safeCastTo64(2.5e18), 2.5e18); - assertEq(SafeCastLib.safeCastTo64(2.5e17), 2.5e17); - } - - function testSafeCastTo56() public { - assertEq(SafeCastLib.safeCastTo56(2.5e16), 2.5e16); - assertEq(SafeCastLib.safeCastTo56(2.5e15), 2.5e15); - } - - function testSafeCastTo48() public { - assertEq(SafeCastLib.safeCastTo48(2.5e12), 2.5e12); - assertEq(SafeCastLib.safeCastTo48(2.5e11), 2.5e11); - } - - function testSafeCastTo40() public { - assertEq(SafeCastLib.safeCastTo40(2.5e10), 2.5e10); - assertEq(SafeCastLib.safeCastTo40(2.5e9), 2.5e9); - } - - function testSafeCastTo32() public { - assertEq(SafeCastLib.safeCastTo32(2.5e8), 2.5e8); - assertEq(SafeCastLib.safeCastTo32(2.5e7), 2.5e7); - } - - function testSafeCastTo24() public { - assertEq(SafeCastLib.safeCastTo24(2.5e4), 2.5e4); - assertEq(SafeCastLib.safeCastTo24(2.5e3), 2.5e3); - } - - function testSafeCastTo16() public { - assertEq(SafeCastLib.safeCastTo16(2.5e3), 2.5e3); - assertEq(SafeCastLib.safeCastTo16(2.5e2), 2.5e2); - } - - function testSafeCastTo8() public { - assertEq(SafeCastLib.safeCastTo8(100), 100); - assertEq(SafeCastLib.safeCastTo8(250), 250); - } - - function testFailSafeCastTo248() public pure { - SafeCastLib.safeCastTo248(type(uint248).max + 1); - } - - function testFailSafeCastTo240() public pure { - SafeCastLib.safeCastTo240(type(uint240).max + 1); - } - - function testFailSafeCastTo232() public pure { - SafeCastLib.safeCastTo232(type(uint232).max + 1); - } - - function testFailSafeCastTo224() public pure { - SafeCastLib.safeCastTo224(type(uint224).max + 1); - } - - function testFailSafeCastTo216() public pure { - SafeCastLib.safeCastTo216(type(uint216).max + 1); - } - - function testFailSafeCastTo208() public pure { - SafeCastLib.safeCastTo208(type(uint208).max + 1); - } - - function testFailSafeCastTo200() public pure { - SafeCastLib.safeCastTo200(type(uint200).max + 1); - } - - function testFailSafeCastTo192() public pure { - SafeCastLib.safeCastTo192(type(uint192).max + 1); - } - - function testFailSafeCastTo184() public pure { - SafeCastLib.safeCastTo184(type(uint184).max + 1); - } - - function testFailSafeCastTo176() public pure { - SafeCastLib.safeCastTo176(type(uint176).max + 1); - } - - function testFailSafeCastTo168() public pure { - SafeCastLib.safeCastTo168(type(uint168).max + 1); - } - - function testFailSafeCastTo160() public pure { - SafeCastLib.safeCastTo160(type(uint160).max + 1); - } - - function testFailSafeCastTo152() public pure { - SafeCastLib.safeCastTo152(type(uint152).max + 1); - } - - function testFailSafeCastTo144() public pure { - SafeCastLib.safeCastTo144(type(uint144).max + 1); - } - - function testFailSafeCastTo136() public pure { - SafeCastLib.safeCastTo136(type(uint136).max + 1); - } - - function testFailSafeCastTo128() public pure { - SafeCastLib.safeCastTo128(type(uint128).max + 1); - } - - function testFailSafeCastTo120() public pure { - SafeCastLib.safeCastTo120(type(uint120).max + 1); - } - - function testFailSafeCastTo112() public pure { - SafeCastLib.safeCastTo112(type(uint112).max + 1); - } - - function testFailSafeCastTo104() public pure { - SafeCastLib.safeCastTo104(type(uint104).max + 1); - } - - function testFailSafeCastTo96() public pure { - SafeCastLib.safeCastTo96(type(uint96).max + 1); - } - - function testFailSafeCastTo88() public pure { - SafeCastLib.safeCastTo88(type(uint88).max + 1); - } - - function testFailSafeCastTo80() public pure { - SafeCastLib.safeCastTo80(type(uint80).max + 1); - } - - function testFailSafeCastTo72() public pure { - SafeCastLib.safeCastTo72(type(uint72).max + 1); - } - - function testFailSafeCastTo64() public pure { - SafeCastLib.safeCastTo64(type(uint64).max + 1); - } - - function testFailSafeCastTo56() public pure { - SafeCastLib.safeCastTo56(type(uint56).max + 1); - } - - function testFailSafeCastTo48() public pure { - SafeCastLib.safeCastTo48(type(uint48).max + 1); - } - - function testFailSafeCastTo40() public pure { - SafeCastLib.safeCastTo40(type(uint40).max + 1); - } - - function testFailSafeCastTo32() public pure { - SafeCastLib.safeCastTo32(type(uint32).max + 1); - } - - function testFailSafeCastTo24() public pure { - SafeCastLib.safeCastTo24(type(uint24).max + 1); - } - - function testFailSafeCastTo16() public pure { - SafeCastLib.safeCastTo16(type(uint16).max + 1); - } - - function testFailSafeCastTo8() public pure { - SafeCastLib.safeCastTo8(type(uint8).max + 1); - } - - function testSafeCastTo248(uint256 x) public { - x = bound(x, 0, type(uint248).max); - - assertEq(SafeCastLib.safeCastTo248(x), x); - } - - function testSafeCastTo240(uint256 x) public { - x = bound(x, 0, type(uint240).max); - - assertEq(SafeCastLib.safeCastTo240(x), x); - } - - function testSafeCastTo232(uint256 x) public { - x = bound(x, 0, type(uint232).max); - - assertEq(SafeCastLib.safeCastTo232(x), x); - } - - function testSafeCastTo224(uint256 x) public { - x = bound(x, 0, type(uint224).max); - - assertEq(SafeCastLib.safeCastTo224(x), x); - } - - function testSafeCastTo216(uint256 x) public { - x = bound(x, 0, type(uint216).max); - - assertEq(SafeCastLib.safeCastTo216(x), x); - } - - function testSafeCastTo208(uint256 x) public { - x = bound(x, 0, type(uint208).max); - - assertEq(SafeCastLib.safeCastTo208(x), x); - } - - function testSafeCastTo200(uint256 x) public { - x = bound(x, 0, type(uint200).max); - - assertEq(SafeCastLib.safeCastTo200(x), x); - } - - function testSafeCastTo192(uint256 x) public { - x = bound(x, 0, type(uint192).max); - - assertEq(SafeCastLib.safeCastTo192(x), x); - } - - function testSafeCastTo184(uint256 x) public { - x = bound(x, 0, type(uint184).max); - - assertEq(SafeCastLib.safeCastTo184(x), x); - } - - function testSafeCastTo176(uint256 x) public { - x = bound(x, 0, type(uint176).max); - - assertEq(SafeCastLib.safeCastTo176(x), x); - } - - function testSafeCastTo168(uint256 x) public { - x = bound(x, 0, type(uint168).max); - - assertEq(SafeCastLib.safeCastTo168(x), x); - } - - function testSafeCastTo160(uint256 x) public { - x = bound(x, 0, type(uint160).max); - - assertEq(SafeCastLib.safeCastTo160(x), x); - } - - function testSafeCastTo152(uint256 x) public { - x = bound(x, 0, type(uint152).max); - - assertEq(SafeCastLib.safeCastTo152(x), x); - } - - function testSafeCastTo144(uint256 x) public { - x = bound(x, 0, type(uint144).max); - - assertEq(SafeCastLib.safeCastTo144(x), x); - } - - function testSafeCastTo136(uint256 x) public { - x = bound(x, 0, type(uint136).max); - - assertEq(SafeCastLib.safeCastTo136(x), x); - } - - function testSafeCastTo128(uint256 x) public { - x = bound(x, 0, type(uint128).max); - - assertEq(SafeCastLib.safeCastTo128(x), x); - } - - function testSafeCastTo120(uint256 x) public { - x = bound(x, 0, type(uint120).max); - - assertEq(SafeCastLib.safeCastTo120(x), x); - } - - function testSafeCastTo112(uint256 x) public { - x = bound(x, 0, type(uint112).max); - - assertEq(SafeCastLib.safeCastTo112(x), x); - } - - function testSafeCastTo104(uint256 x) public { - x = bound(x, 0, type(uint104).max); - - assertEq(SafeCastLib.safeCastTo104(x), x); - } - - function testSafeCastTo96(uint256 x) public { - x = bound(x, 0, type(uint96).max); - - assertEq(SafeCastLib.safeCastTo96(x), x); - } - - function testSafeCastTo88(uint256 x) public { - x = bound(x, 0, type(uint88).max); - - assertEq(SafeCastLib.safeCastTo88(x), x); - } - - function testSafeCastTo80(uint256 x) public { - x = bound(x, 0, type(uint80).max); - - assertEq(SafeCastLib.safeCastTo80(x), x); - } - - function testSafeCastTo72(uint256 x) public { - x = bound(x, 0, type(uint72).max); - - assertEq(SafeCastLib.safeCastTo72(x), x); - } - - function testSafeCastTo64(uint256 x) public { - x = bound(x, 0, type(uint64).max); - - assertEq(SafeCastLib.safeCastTo64(x), x); - } - - function testSafeCastTo56(uint256 x) public { - x = bound(x, 0, type(uint56).max); - - assertEq(SafeCastLib.safeCastTo56(x), x); - } - - function testSafeCastTo48(uint256 x) public { - x = bound(x, 0, type(uint48).max); - - assertEq(SafeCastLib.safeCastTo48(x), x); - } - - function testSafeCastTo40(uint256 x) public { - x = bound(x, 0, type(uint40).max); - - assertEq(SafeCastLib.safeCastTo40(x), x); - } - - function testSafeCastTo32(uint256 x) public { - x = bound(x, 0, type(uint32).max); - - assertEq(SafeCastLib.safeCastTo32(x), x); - } - - function testSafeCastTo24(uint256 x) public { - x = bound(x, 0, type(uint24).max); - - assertEq(SafeCastLib.safeCastTo24(x), x); - } - - function testSafeCastTo16(uint256 x) public { - x = bound(x, 0, type(uint16).max); - - assertEq(SafeCastLib.safeCastTo16(x), x); - } - - function testSafeCastTo8(uint256 x) public { - x = bound(x, 0, type(uint8).max); - - assertEq(SafeCastLib.safeCastTo8(x), x); - } - - function testFailSafeCastTo248(uint256 x) public { - x = bound(x, type(uint248).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo248(x); - } - - function testFailSafeCastTo240(uint256 x) public { - x = bound(x, type(uint240).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo240(x); - } - - function testFailSafeCastTo232(uint256 x) public { - x = bound(x, type(uint232).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo232(x); - } - - function testFailSafeCastTo224(uint256 x) public { - x = bound(x, type(uint224).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo224(x); - } - - function testFailSafeCastTo216(uint256 x) public { - x = bound(x, type(uint216).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo216(x); - } - - function testFailSafeCastTo208(uint256 x) public { - x = bound(x, type(uint208).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo208(x); - } - - function testFailSafeCastTo200(uint256 x) public { - x = bound(x, type(uint200).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo200(x); - } - - function testFailSafeCastTo192(uint256 x) public { - x = bound(x, type(uint192).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo192(x); - } - - function testFailSafeCastTo184(uint256 x) public { - x = bound(x, type(uint184).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo184(x); - } - - function testFailSafeCastTo176(uint256 x) public { - x = bound(x, type(uint176).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo176(x); - } - - function testFailSafeCastTo168(uint256 x) public { - x = bound(x, type(uint168).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo168(x); - } - - function testFailSafeCastTo160(uint256 x) public { - x = bound(x, type(uint160).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo160(x); - } - - function testFailSafeCastTo152(uint256 x) public { - x = bound(x, type(uint152).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo152(x); - } - - function testFailSafeCastTo144(uint256 x) public { - x = bound(x, type(uint144).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo144(x); - } - - function testFailSafeCastTo136(uint256 x) public { - x = bound(x, type(uint136).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo136(x); - } - - function testFailSafeCastTo128(uint256 x) public { - x = bound(x, type(uint128).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo128(x); - } - - function testFailSafeCastTo120(uint256 x) public { - x = bound(x, type(uint120).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo120(x); - } - - function testFailSafeCastTo112(uint256 x) public { - x = bound(x, type(uint112).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo112(x); - } - - function testFailSafeCastTo104(uint256 x) public { - x = bound(x, type(uint104).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo104(x); - } - - function testFailSafeCastTo96(uint256 x) public { - x = bound(x, type(uint96).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo96(x); - } - - function testFailSafeCastTo88(uint256 x) public { - x = bound(x, type(uint88).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo88(x); - } - - function testFailSafeCastTo80(uint256 x) public { - x = bound(x, type(uint80).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo80(x); - } - - function testFailSafeCastTo72(uint256 x) public { - x = bound(x, type(uint72).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo72(x); - } - - function testFailSafeCastTo64(uint256 x) public { - x = bound(x, type(uint64).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo64(x); - } - - function testFailSafeCastTo56(uint256 x) public { - x = bound(x, type(uint56).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo56(x); - } - - function testFailSafeCastTo48(uint256 x) public { - x = bound(x, type(uint48).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo48(x); - } - - function testFailSafeCastTo40(uint256 x) public { - x = bound(x, type(uint40).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo40(x); - } - - function testFailSafeCastTo32(uint256 x) public { - x = bound(x, type(uint32).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo32(x); - } - - function testFailSafeCastTo24(uint256 x) public { - x = bound(x, type(uint24).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo24(x); - } - - function testFailSafeCastTo16(uint256 x) public { - x = bound(x, type(uint16).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo16(x); - } - - function testFailSafeCastTo8(uint256 x) public { - x = bound(x, type(uint8).max + 1, type(uint256).max); - - SafeCastLib.safeCastTo8(x); - } -} diff --git a/lib/solmate/src/test/SafeTransferLib.t.sol b/lib/solmate/src/test/SafeTransferLib.t.sol deleted file mode 100644 index b976e9f..0000000 --- a/lib/solmate/src/test/SafeTransferLib.t.sol +++ /dev/null @@ -1,610 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {MockERC20} from "./utils/mocks/MockERC20.sol"; -import {RevertingToken} from "./utils/weird-tokens/RevertingToken.sol"; -import {ReturnsTwoToken} from "./utils/weird-tokens/ReturnsTwoToken.sol"; -import {ReturnsFalseToken} from "./utils/weird-tokens/ReturnsFalseToken.sol"; -import {MissingReturnToken} from "./utils/weird-tokens/MissingReturnToken.sol"; -import {ReturnsTooMuchToken} from "./utils/weird-tokens/ReturnsTooMuchToken.sol"; -import {ReturnsGarbageToken} from "./utils/weird-tokens/ReturnsGarbageToken.sol"; -import {ReturnsTooLittleToken} from "./utils/weird-tokens/ReturnsTooLittleToken.sol"; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {ERC20} from "../tokens/ERC20.sol"; -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; - -contract SafeTransferLibTest is DSTestPlus { - RevertingToken reverting; - ReturnsTwoToken returnsTwo; - ReturnsFalseToken returnsFalse; - MissingReturnToken missingReturn; - ReturnsTooMuchToken returnsTooMuch; - ReturnsGarbageToken returnsGarbage; - ReturnsTooLittleToken returnsTooLittle; - - MockERC20 erc20; - - function setUp() public { - reverting = new RevertingToken(); - returnsTwo = new ReturnsTwoToken(); - returnsFalse = new ReturnsFalseToken(); - missingReturn = new MissingReturnToken(); - returnsTooMuch = new ReturnsTooMuchToken(); - returnsGarbage = new ReturnsGarbageToken(); - returnsTooLittle = new ReturnsTooLittleToken(); - - erc20 = new MockERC20("StandardToken", "ST", 18); - erc20.mint(address(this), type(uint256).max); - } - - function testTransferWithMissingReturn() public { - verifySafeTransfer(address(missingReturn), address(0xBEEF), 1e18); - } - - function testTransferWithStandardERC20() public { - verifySafeTransfer(address(erc20), address(0xBEEF), 1e18); - } - - function testTransferWithReturnsTooMuch() public { - verifySafeTransfer(address(returnsTooMuch), address(0xBEEF), 1e18); - } - - function testTransferWithNonContract() public { - SafeTransferLib.safeTransfer(ERC20(address(0xBADBEEF)), address(0xBEEF), 1e18); - } - - function testTransferFromWithMissingReturn() public { - verifySafeTransferFrom(address(missingReturn), address(0xFEED), address(0xBEEF), 1e18); - } - - function testTransferFromWithStandardERC20() public { - verifySafeTransferFrom(address(erc20), address(0xFEED), address(0xBEEF), 1e18); - } - - function testTransferFromWithReturnsTooMuch() public { - verifySafeTransferFrom(address(returnsTooMuch), address(0xFEED), address(0xBEEF), 1e18); - } - - function testTransferFromWithNonContract() public { - SafeTransferLib.safeTransferFrom(ERC20(address(0xBADBEEF)), address(0xFEED), address(0xBEEF), 1e18); - } - - function testApproveWithMissingReturn() public { - verifySafeApprove(address(missingReturn), address(0xBEEF), 1e18); - } - - function testApproveWithStandardERC20() public { - verifySafeApprove(address(erc20), address(0xBEEF), 1e18); - } - - function testApproveWithReturnsTooMuch() public { - verifySafeApprove(address(returnsTooMuch), address(0xBEEF), 1e18); - } - - function testApproveWithNonContract() public { - SafeTransferLib.safeApprove(ERC20(address(0xBADBEEF)), address(0xBEEF), 1e18); - } - - function testTransferETH() public { - SafeTransferLib.safeTransferETH(address(0xBEEF), 1e18); - } - - function testFailTransferWithReturnsFalse() public { - verifySafeTransfer(address(returnsFalse), address(0xBEEF), 1e18); - } - - function testFailTransferWithReverting() public { - verifySafeTransfer(address(reverting), address(0xBEEF), 1e18); - } - - function testFailTransferWithReturnsTooLittle() public { - verifySafeTransfer(address(returnsTooLittle), address(0xBEEF), 1e18); - } - - function testFailTransferFromWithReturnsFalse() public { - verifySafeTransferFrom(address(returnsFalse), address(0xFEED), address(0xBEEF), 1e18); - } - - function testFailTransferFromWithReverting() public { - verifySafeTransferFrom(address(reverting), address(0xFEED), address(0xBEEF), 1e18); - } - - function testFailTransferFromWithReturnsTooLittle() public { - verifySafeTransferFrom(address(returnsTooLittle), address(0xFEED), address(0xBEEF), 1e18); - } - - function testFailApproveWithReturnsFalse() public { - verifySafeApprove(address(returnsFalse), address(0xBEEF), 1e18); - } - - function testFailApproveWithReverting() public { - verifySafeApprove(address(reverting), address(0xBEEF), 1e18); - } - - function testFailApproveWithReturnsTooLittle() public { - verifySafeApprove(address(returnsTooLittle), address(0xBEEF), 1e18); - } - - function testTransferWithMissingReturn( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(missingReturn), to, amount); - } - - function testTransferWithStandardERC20( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(erc20), to, amount); - } - - function testTransferWithReturnsTooMuch( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(returnsTooMuch), to, amount); - } - - function testTransferWithGarbage( - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if ( - (garbage.length < 32 || - (garbage[0] != 0 || - garbage[1] != 0 || - garbage[2] != 0 || - garbage[3] != 0 || - garbage[4] != 0 || - garbage[5] != 0 || - garbage[6] != 0 || - garbage[7] != 0 || - garbage[8] != 0 || - garbage[9] != 0 || - garbage[10] != 0 || - garbage[11] != 0 || - garbage[12] != 0 || - garbage[13] != 0 || - garbage[14] != 0 || - garbage[15] != 0 || - garbage[16] != 0 || - garbage[17] != 0 || - garbage[18] != 0 || - garbage[19] != 0 || - garbage[20] != 0 || - garbage[21] != 0 || - garbage[22] != 0 || - garbage[23] != 0 || - garbage[24] != 0 || - garbage[25] != 0 || - garbage[26] != 0 || - garbage[27] != 0 || - garbage[28] != 0 || - garbage[29] != 0 || - garbage[30] != 0 || - garbage[31] != bytes1(0x01))) && garbage.length != 0 - ) return; - - returnsGarbage.setGarbage(garbage); - - verifySafeTransfer(address(returnsGarbage), to, amount); - } - - function testTransferWithNonContract( - address nonContract, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; - - SafeTransferLib.safeTransfer(ERC20(nonContract), to, amount); - } - - function testFailTransferETHToContractWithoutFallback() public { - SafeTransferLib.safeTransferETH(address(this), 1e18); - } - - function testTransferFromWithMissingReturn( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(missingReturn), from, to, amount); - } - - function testTransferFromWithStandardERC20( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(erc20), from, to, amount); - } - - function testTransferFromWithReturnsTooMuch( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(returnsTooMuch), from, to, amount); - } - - function testTransferFromWithGarbage( - address from, - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if ( - (garbage.length < 32 || - (garbage[0] != 0 || - garbage[1] != 0 || - garbage[2] != 0 || - garbage[3] != 0 || - garbage[4] != 0 || - garbage[5] != 0 || - garbage[6] != 0 || - garbage[7] != 0 || - garbage[8] != 0 || - garbage[9] != 0 || - garbage[10] != 0 || - garbage[11] != 0 || - garbage[12] != 0 || - garbage[13] != 0 || - garbage[14] != 0 || - garbage[15] != 0 || - garbage[16] != 0 || - garbage[17] != 0 || - garbage[18] != 0 || - garbage[19] != 0 || - garbage[20] != 0 || - garbage[21] != 0 || - garbage[22] != 0 || - garbage[23] != 0 || - garbage[24] != 0 || - garbage[25] != 0 || - garbage[26] != 0 || - garbage[27] != 0 || - garbage[28] != 0 || - garbage[29] != 0 || - garbage[30] != 0 || - garbage[31] != bytes1(0x01))) && garbage.length != 0 - ) return; - - returnsGarbage.setGarbage(garbage); - - verifySafeTransferFrom(address(returnsGarbage), from, to, amount); - } - - function testTransferFromWithNonContract( - address nonContract, - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; - - SafeTransferLib.safeTransferFrom(ERC20(nonContract), from, to, amount); - } - - function testApproveWithMissingReturn( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(missingReturn), to, amount); - } - - function testApproveWithStandardERC20( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(erc20), to, amount); - } - - function testApproveWithReturnsTooMuch( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(returnsTooMuch), to, amount); - } - - function testApproveWithGarbage( - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if ( - (garbage.length < 32 || - (garbage[0] != 0 || - garbage[1] != 0 || - garbage[2] != 0 || - garbage[3] != 0 || - garbage[4] != 0 || - garbage[5] != 0 || - garbage[6] != 0 || - garbage[7] != 0 || - garbage[8] != 0 || - garbage[9] != 0 || - garbage[10] != 0 || - garbage[11] != 0 || - garbage[12] != 0 || - garbage[13] != 0 || - garbage[14] != 0 || - garbage[15] != 0 || - garbage[16] != 0 || - garbage[17] != 0 || - garbage[18] != 0 || - garbage[19] != 0 || - garbage[20] != 0 || - garbage[21] != 0 || - garbage[22] != 0 || - garbage[23] != 0 || - garbage[24] != 0 || - garbage[25] != 0 || - garbage[26] != 0 || - garbage[27] != 0 || - garbage[28] != 0 || - garbage[29] != 0 || - garbage[30] != 0 || - garbage[31] != bytes1(0x01))) && garbage.length != 0 - ) return; - - returnsGarbage.setGarbage(garbage); - - verifySafeApprove(address(returnsGarbage), to, amount); - } - - function testApproveWithNonContract( - address nonContract, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - if (uint256(uint160(nonContract)) <= 18 || nonContract.code.length > 0) return; - - SafeTransferLib.safeApprove(ERC20(nonContract), to, amount); - } - - function testTransferETH( - address recipient, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - // Transferring to msg.sender can fail because it's possible to overflow their ETH balance as it begins non-zero. - if (recipient.code.length > 0 || uint256(uint160(recipient)) <= 18 || recipient == msg.sender) return; - - amount = bound(amount, 0, address(this).balance); - - SafeTransferLib.safeTransferETH(recipient, amount); - } - - function testFailTransferWithReturnsFalse( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(returnsFalse), to, amount); - } - - function testFailTransferWithReverting( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(reverting), to, amount); - } - - function testFailTransferWithReturnsTooLittle( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(returnsTooLittle), to, amount); - } - - function testFailTransferWithReturnsTwo( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransfer(address(returnsTwo), to, amount); - } - - function testFailTransferWithGarbage( - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); - - returnsGarbage.setGarbage(garbage); - - verifySafeTransfer(address(returnsGarbage), to, amount); - } - - function testFailTransferFromWithReturnsFalse( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(returnsFalse), from, to, amount); - } - - function testFailTransferFromWithReverting( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(reverting), from, to, amount); - } - - function testFailTransferFromWithReturnsTooLittle( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(returnsTooLittle), from, to, amount); - } - - function testFailTransferFromWithReturnsTwo( - address from, - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeTransferFrom(address(returnsTwo), from, to, amount); - } - - function testFailTransferFromWithGarbage( - address from, - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); - - returnsGarbage.setGarbage(garbage); - - verifySafeTransferFrom(address(returnsGarbage), from, to, amount); - } - - function testFailApproveWithReturnsFalse( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(returnsFalse), to, amount); - } - - function testFailApproveWithReverting( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(reverting), to, amount); - } - - function testFailApproveWithReturnsTooLittle( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(returnsTooLittle), to, amount); - } - - function testFailApproveWithReturnsTwo( - address to, - uint256 amount, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - verifySafeApprove(address(returnsTwo), to, amount); - } - - function testFailApproveWithGarbage( - address to, - uint256 amount, - bytes memory garbage, - bytes calldata brutalizeWith - ) public brutalizeMemory(brutalizeWith) { - require(garbage.length != 0 && (garbage.length < 32 || garbage[31] != bytes1(0x01))); - - returnsGarbage.setGarbage(garbage); - - verifySafeApprove(address(returnsGarbage), to, amount); - } - - function testFailTransferETHToContractWithoutFallback(uint256 amount, bytes calldata brutalizeWith) - public - brutalizeMemory(brutalizeWith) - { - SafeTransferLib.safeTransferETH(address(this), amount); - } - - function verifySafeTransfer( - address token, - address to, - uint256 amount - ) internal { - uint256 preBal = ERC20(token).balanceOf(to); - SafeTransferLib.safeTransfer(ERC20(address(token)), to, amount); - uint256 postBal = ERC20(token).balanceOf(to); - - if (to == address(this)) { - assertEq(preBal, postBal); - } else { - assertEq(postBal - preBal, amount); - } - } - - function verifySafeTransferFrom( - address token, - address from, - address to, - uint256 amount - ) internal { - forceApprove(token, from, address(this), amount); - - // We cast to MissingReturnToken here because it won't check - // that there was return data, which accommodates all tokens. - MissingReturnToken(token).transfer(from, amount); - - uint256 preBal = ERC20(token).balanceOf(to); - SafeTransferLib.safeTransferFrom(ERC20(token), from, to, amount); - uint256 postBal = ERC20(token).balanceOf(to); - - if (from == to) { - assertEq(preBal, postBal); - } else { - assertEq(postBal - preBal, amount); - } - } - - function verifySafeApprove( - address token, - address to, - uint256 amount - ) internal { - SafeTransferLib.safeApprove(ERC20(address(token)), to, amount); - - assertEq(ERC20(token).allowance(address(this), to), amount); - } - - function forceApprove( - address token, - address from, - address to, - uint256 amount - ) internal { - uint256 slot = token == address(erc20) ? 4 : 2; // Standard ERC20 name and symbol aren't constant. - - hevm.store( - token, - keccak256(abi.encode(to, keccak256(abi.encode(from, uint256(slot))))), - bytes32(uint256(amount)) - ); - - assertEq(ERC20(token).allowance(from, to), amount, "wrong allowance"); - } -} diff --git a/lib/solmate/src/test/SignedWadMath.t.sol b/lib/solmate/src/test/SignedWadMath.t.sol deleted file mode 100644 index 4849448..0000000 --- a/lib/solmate/src/test/SignedWadMath.t.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; - -import {wadMul, wadDiv} from "../utils/SignedWadMath.sol"; - -contract SignedWadMathTest is DSTestPlus { - function testWadMul( - uint256 x, - uint256 y, - bool negX, - bool negY - ) public { - x = bound(x, 0, 99999999999999e18); - y = bound(x, 0, 99999999999999e18); - - int256 xPrime = negX ? -int256(x) : int256(x); - int256 yPrime = negY ? -int256(y) : int256(y); - - assertEq(wadMul(xPrime, yPrime), (xPrime * yPrime) / 1e18); - } - - function testFailWadMulEdgeCase() public pure { - int256 x = -1; - int256 y = type(int256).min; - - wadMul(x, y); - } - - function testFailWadMulEdgeCase2() public pure { - int256 x = type(int256).min; - int256 y = -1; - - wadMul(x, y); - } - - function testFailWadMulOverflow(int256 x, int256 y) public pure { - // Ignore cases where x * y does not overflow. - unchecked { - if ((x * y) / x == y) revert(); - } - - wadMul(x, y); - } - - function testWadDiv( - uint256 x, - uint256 y, - bool negX, - bool negY - ) public { - x = bound(x, 0, 99999999e18); - y = bound(x, 1, 99999999e18); - - int256 xPrime = negX ? -int256(x) : int256(x); - int256 yPrime = negY ? -int256(y) : int256(y); - - assertEq(wadDiv(xPrime, yPrime), (xPrime * 1e18) / yPrime); - } - - function testFailWadDivOverflow(int256 x, int256 y) public pure { - // Ignore cases where x * WAD does not overflow or y is 0. - unchecked { - if (y == 0 || (x * 1e18) / 1e18 == x) revert(); - } - - wadDiv(x, y); - } - - function testFailWadDivZeroDenominator(int256 x) public pure { - wadDiv(x, 0); - } -} diff --git a/lib/solmate/src/test/WETH.t.sol b/lib/solmate/src/test/WETH.t.sol deleted file mode 100644 index a13761e..0000000 --- a/lib/solmate/src/test/WETH.t.sol +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.15; - -import {DSTestPlus} from "./utils/DSTestPlus.sol"; -import {DSInvariantTest} from "./utils/DSInvariantTest.sol"; - -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; - -import {WETH} from "../tokens/WETH.sol"; - -contract WETHTest is DSTestPlus { - WETH weth; - - function setUp() public { - weth = new WETH(); - } - - function testFallbackDeposit() public { - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - SafeTransferLib.safeTransferETH(address(weth), 1 ether); - - assertEq(weth.balanceOf(address(this)), 1 ether); - assertEq(weth.totalSupply(), 1 ether); - } - - function testDeposit() public { - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - weth.deposit{value: 1 ether}(); - - assertEq(weth.balanceOf(address(this)), 1 ether); - assertEq(weth.totalSupply(), 1 ether); - } - - function testWithdraw() public { - uint256 startingBalance = address(this).balance; - - weth.deposit{value: 1 ether}(); - - weth.withdraw(1 ether); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, startingBalance); - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - } - - function testPartialWithdraw() public { - weth.deposit{value: 1 ether}(); - - uint256 balanceBeforeWithdraw = address(this).balance; - - weth.withdraw(0.5 ether); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + 0.5 ether); - assertEq(weth.balanceOf(address(this)), 0.5 ether); - assertEq(weth.totalSupply(), 0.5 ether); - } - - function testFallbackDeposit(uint256 amount) public { - amount = bound(amount, 0, address(this).balance); - - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - SafeTransferLib.safeTransferETH(address(weth), amount); - - assertEq(weth.balanceOf(address(this)), amount); - assertEq(weth.totalSupply(), amount); - } - - function testDeposit(uint256 amount) public { - amount = bound(amount, 0, address(this).balance); - - assertEq(weth.balanceOf(address(this)), 0); - assertEq(weth.totalSupply(), 0); - - weth.deposit{value: amount}(); - - assertEq(weth.balanceOf(address(this)), amount); - assertEq(weth.totalSupply(), amount); - } - - function testWithdraw(uint256 depositAmount, uint256 withdrawAmount) public { - depositAmount = bound(depositAmount, 0, address(this).balance); - withdrawAmount = bound(withdrawAmount, 0, depositAmount); - - weth.deposit{value: depositAmount}(); - - uint256 balanceBeforeWithdraw = address(this).balance; - - weth.withdraw(withdrawAmount); - - uint256 balanceAfterWithdraw = address(this).balance; - - assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + withdrawAmount); - assertEq(weth.balanceOf(address(this)), depositAmount - withdrawAmount); - assertEq(weth.totalSupply(), depositAmount - withdrawAmount); - } - - receive() external payable {} -} - -contract WETHInvariants is DSTestPlus, DSInvariantTest { - WETHTester wethTester; - WETH weth; - - function setUp() public { - weth = new WETH(); - wethTester = new WETHTester{value: address(this).balance}(weth); - - addTargetContract(address(wethTester)); - } - - function invariantTotalSupplyEqualsBalance() public { - assertEq(address(weth).balance, weth.totalSupply()); - } -} - -contract WETHTester { - WETH weth; - - constructor(WETH _weth) payable { - weth = _weth; - } - - function deposit(uint256 amount) public { - weth.deposit{value: amount}(); - } - - function fallbackDeposit(uint256 amount) public { - SafeTransferLib.safeTransferETH(address(weth), amount); - } - - function withdraw(uint256 amount) public { - weth.withdraw(amount); - } - - receive() external payable {} -} diff --git a/lib/solmate/src/test/utils/DSInvariantTest.sol b/lib/solmate/src/test/utils/DSInvariantTest.sol deleted file mode 100644 index 820775c..0000000 --- a/lib/solmate/src/test/utils/DSInvariantTest.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract DSInvariantTest { - address[] private targets; - - function targetContracts() public view virtual returns (address[] memory) { - require(targets.length > 0, "NO_TARGET_CONTRACTS"); - - return targets; - } - - function addTargetContract(address newTargetContract) internal virtual { - targets.push(newTargetContract); - } -} diff --git a/lib/solmate/src/test/utils/DSTestPlus.sol b/lib/solmate/src/test/utils/DSTestPlus.sol deleted file mode 100644 index b56d4c9..0000000 --- a/lib/solmate/src/test/utils/DSTestPlus.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {DSTest} from "ds-test/test.sol"; - -import {Hevm} from "./Hevm.sol"; - -/// @notice Extended testing framework for DappTools projects. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/test/utils/DSTestPlus.sol) -contract DSTestPlus is DSTest { - Hevm internal constant hevm = Hevm(HEVM_ADDRESS); - - address internal constant DEAD_ADDRESS = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; - - string private checkpointLabel; - uint256 private checkpointGasLeft = 1; // Start the slot warm. - - modifier brutalizeMemory(bytes memory brutalizeWith) { - /// @solidity memory-safe-assembly - assembly { - // Fill the 64 bytes of scratch space with the data. - pop( - staticcall( - gas(), // Pass along all the gas in the call. - 0x04, // Call the identity precompile address. - brutalizeWith, // Offset is the bytes' pointer. - 64, // Copy enough to only fill the scratch space. - 0, // Store the return value in the scratch space. - 64 // Scratch space is only 64 bytes in size, we don't want to write further. - ) - ) - - let size := add(mload(brutalizeWith), 32) // Add 32 to include the 32 byte length slot. - - // Fill the free memory pointer's destination with the data. - pop( - staticcall( - gas(), // Pass along all the gas in the call. - 0x04, // Call the identity precompile address. - brutalizeWith, // Offset is the bytes' pointer. - size, // We want to pass the length of the bytes. - mload(0x40), // Store the return value at the free memory pointer. - size // Since the precompile just returns its input, we reuse size. - ) - ) - } - - _; - } - - function startMeasuringGas(string memory label) internal virtual { - checkpointLabel = label; - - checkpointGasLeft = gasleft(); - } - - function stopMeasuringGas() internal virtual { - uint256 checkpointGasLeft2 = gasleft(); - - // Subtract 100 to account for the warm SLOAD in startMeasuringGas. - uint256 gasDelta = checkpointGasLeft - checkpointGasLeft2 - 100; - - emit log_named_uint(string(abi.encodePacked(checkpointLabel, " Gas")), gasDelta); - } - - function fail(string memory err) internal virtual { - emit log_named_string("Error", err); - fail(); - } - - function assertFalse(bool data) internal virtual { - assertTrue(!data); - } - - function assertUint128Eq(uint128 a, uint128 b) internal virtual { - assertEq(uint256(a), uint256(b)); - } - - function assertUint64Eq(uint64 a, uint64 b) internal virtual { - assertEq(uint256(a), uint256(b)); - } - - function assertUint96Eq(uint96 a, uint96 b) internal virtual { - assertEq(uint256(a), uint256(b)); - } - - function assertUint32Eq(uint32 a, uint32 b) internal virtual { - assertEq(uint256(a), uint256(b)); - } - - function assertBoolEq(bool a, bool b) internal virtual { - b ? assertTrue(a) : assertFalse(a); - } - - function assertApproxEq( - uint256 a, - uint256 b, - uint256 maxDelta - ) internal virtual { - uint256 delta = a > b ? a - b : b - a; - - if (delta > maxDelta) { - emit log("Error: a ~= b not satisfied [uint]"); - emit log_named_uint(" Expected", b); - emit log_named_uint(" Actual", a); - emit log_named_uint(" Max Delta", maxDelta); - emit log_named_uint(" Delta", delta); - fail(); - } - } - - function assertRelApproxEq( - uint256 a, - uint256 b, - uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% - ) internal virtual { - if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. - - uint256 percentDelta = ((a > b ? a - b : b - a) * 1e18) / b; - - if (percentDelta > maxPercentDelta) { - emit log("Error: a ~= b not satisfied [uint]"); - emit log_named_uint(" Expected", b); - emit log_named_uint(" Actual", a); - emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); - emit log_named_decimal_uint(" % Delta", percentDelta, 18); - fail(); - } - } - - function assertBytesEq(bytes memory a, bytes memory b) internal virtual { - if (keccak256(a) != keccak256(b)) { - emit log("Error: a == b not satisfied [bytes]"); - emit log_named_bytes(" Expected", b); - emit log_named_bytes(" Actual", a); - fail(); - } - } - - function assertUintArrayEq(uint256[] memory a, uint256[] memory b) internal virtual { - require(a.length == b.length, "LENGTH_MISMATCH"); - - for (uint256 i = 0; i < a.length; i++) { - assertEq(a[i], b[i]); - } - } - - function bound( - uint256 x, - uint256 min, - uint256 max - ) internal virtual returns (uint256 result) { - require(max >= min, "MAX_LESS_THAN_MIN"); - - uint256 size = max - min; - - if (size == 0) result = min; - else if (size == type(uint256).max) result = x; - else { - ++size; // Make max inclusive. - uint256 mod = x % size; - result = min + mod; - } - - emit log_named_uint("Bound Result", result); - } - - function min3( - uint256 a, - uint256 b, - uint256 c - ) internal pure returns (uint256) { - return a > b ? (b > c ? c : b) : (a > c ? c : a); - } - - function min2(uint256 a, uint256 b) internal pure returns (uint256) { - return a > b ? b : a; - } -} diff --git a/lib/solmate/src/test/utils/Hevm.sol b/lib/solmate/src/test/utils/Hevm.sol deleted file mode 100644 index 8ca0eff..0000000 --- a/lib/solmate/src/test/utils/Hevm.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -interface Hevm { - /// @notice Sets the block timestamp. - function warp(uint256) external; - - /// @notice Sets the block height. - function roll(uint256) external; - - /// @notice Sets the block base fee. - function fee(uint256) external; - - /// @notice Loads a storage slot from an address. - function load(address, bytes32) external returns (bytes32); - - /// @notice Stores a value to an address' storage slot. - function store( - address, - bytes32, - bytes32 - ) external; - - /// @notice Signs a digest with a private key, returns v r s. - function sign(uint256, bytes32) - external - returns ( - uint8, - bytes32, - bytes32 - ); - - /// @notice Gets address for a given private key. - function addr(uint256) external returns (address); - - /// @notice Performs a foreign function call via a terminal call. - function ffi(string[] calldata) external returns (bytes memory); - - /// @notice Sets the next call's msg.sender to be the input address. - function prank(address) external; - - /// @notice Sets all subsequent calls' msg.sender to be the input address until stopPrank is called. - function startPrank(address) external; - - /// @notice Sets the next call's msg.sender to be the input address and the tx.origin to be the second input. - function prank(address, address) external; - - /// @notice Sets all subsequent calls' msg.sender to be the input address and - /// sets tx.origin to be the second address inputted until stopPrank is called. - function startPrank(address, address) external; - - /// @notice Resets msg.sender to its original value before a prank. - function stopPrank() external; - - /// @notice Sets an address' balance. - function deal(address, uint256) external; - - /// @notice Sets an address' code. - function etch(address, bytes calldata) external; - - /// @notice Expects an error from the next call. - function expectRevert(bytes calldata) external; - - /// @notice Expects a revert from the next call. - function expectRevert(bytes4) external; - - /// @notice Record all storage reads and writes. - function record() external; - - /// @notice Gets all accessed reads and write slots from a recording session, for a given address. - function accesses(address) external returns (bytes32[] memory reads, bytes32[] memory writes); - - /// @notice Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData). - /// @notice Call this function, then emit an event, then call a function. Internally after the call, we check - /// if logs were emitted in the expected order with the expected topics and data as specified by the booleans. - function expectEmit( - bool, - bool, - bool, - bool - ) external; - - /// @notice Mocks the behavior of a contract call, setting the input and output for a function. - /// @notice Calldata can either be strict or a partial match, e.g. if only passed - /// a selector to the expected calldata, then the entire function will be mocked. - function mockCall( - address, - bytes calldata, - bytes calldata - ) external; - - /// @notice Clears all mocked calls. - function clearMockedCalls() external; - - /// @notice Expect a call to an address with the specified calldata. - /// @notice Calldata can either be strict or a partial match. - function expectCall(address, bytes calldata) external; - - /// @notice Fetches the contract bytecode from its artifact file. - function getCode(string calldata) external returns (bytes memory); - - /// @notice Label an address in test traces. - function label(address addr, string calldata label) external; - - /// @notice When fuzzing, generate new inputs if the input conditional is not met. - function assume(bool) external; -} diff --git a/lib/solmate/src/test/utils/mocks/MockAuthChild.sol b/lib/solmate/src/test/utils/mocks/MockAuthChild.sol deleted file mode 100644 index d2c3276..0000000 --- a/lib/solmate/src/test/utils/mocks/MockAuthChild.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Auth, Authority} from "../../../auth/Auth.sol"; - -contract MockAuthChild is Auth(msg.sender, Authority(address(0))) { - bool public flag; - - function updateFlag() public virtual requiresAuth { - flag = true; - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockAuthority.sol b/lib/solmate/src/test/utils/mocks/MockAuthority.sol deleted file mode 100644 index acb3689..0000000 --- a/lib/solmate/src/test/utils/mocks/MockAuthority.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Authority} from "../../../auth/Auth.sol"; - -contract MockAuthority is Authority { - bool immutable allowCalls; - - constructor(bool _allowCalls) { - allowCalls = _allowCalls; - } - - function canCall( - address, - address, - bytes4 - ) public view override returns (bool) { - return allowCalls; - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockERC1155.sol b/lib/solmate/src/test/utils/mocks/MockERC1155.sol deleted file mode 100644 index ede086d..0000000 --- a/lib/solmate/src/test/utils/mocks/MockERC1155.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC1155} from "../../../tokens/ERC1155.sol"; - -contract MockERC1155 is ERC1155 { - function uri(uint256) public pure virtual override returns (string memory) {} - - function mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { - _mint(to, id, amount, data); - } - - function batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - _batchMint(to, ids, amounts, data); - } - - function burn( - address from, - uint256 id, - uint256 amount - ) public virtual { - _burn(from, id, amount); - } - - function batchBurn( - address from, - uint256[] memory ids, - uint256[] memory amounts - ) public virtual { - _batchBurn(from, ids, amounts); - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockERC20.sol b/lib/solmate/src/test/utils/mocks/MockERC20.sol deleted file mode 100644 index fbbaef5..0000000 --- a/lib/solmate/src/test/utils/mocks/MockERC20.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../../../tokens/ERC20.sol"; - -contract MockERC20 is ERC20 { - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals - ) ERC20(_name, _symbol, _decimals) {} - - function mint(address to, uint256 value) public virtual { - _mint(to, value); - } - - function burn(address from, uint256 value) public virtual { - _burn(from, value); - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockERC4626.sol b/lib/solmate/src/test/utils/mocks/MockERC4626.sol deleted file mode 100644 index edc7d5f..0000000 --- a/lib/solmate/src/test/utils/mocks/MockERC4626.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../../../tokens/ERC20.sol"; -import {ERC4626} from "../../../mixins/ERC4626.sol"; - -contract MockERC4626 is ERC4626 { - uint256 public beforeWithdrawHookCalledCounter = 0; - uint256 public afterDepositHookCalledCounter = 0; - - constructor( - ERC20 _underlying, - string memory _name, - string memory _symbol - ) ERC4626(_underlying, _name, _symbol) {} - - function totalAssets() public view override returns (uint256) { - return asset.balanceOf(address(this)); - } - - function beforeWithdraw(uint256, uint256) internal override { - beforeWithdrawHookCalledCounter++; - } - - function afterDeposit(uint256, uint256) internal override { - afterDepositHookCalledCounter++; - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockERC6909.sol b/lib/solmate/src/test/utils/mocks/MockERC6909.sol deleted file mode 100644 index 5f7eabe..0000000 --- a/lib/solmate/src/test/utils/mocks/MockERC6909.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {ERC6909} from "../../../tokens/ERC6909.sol"; - -contract MockERC6909 is ERC6909 { - function mint( - address receiver, - uint256 id, - uint256 amount - ) public virtual { - _mint(receiver, id, amount); - } - - function burn( - address sender, - uint256 id, - uint256 amount - ) public virtual { - _burn(sender, id, amount); - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockERC721.sol b/lib/solmate/src/test/utils/mocks/MockERC721.sol deleted file mode 100644 index 51227c0..0000000 --- a/lib/solmate/src/test/utils/mocks/MockERC721.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC721} from "../../../tokens/ERC721.sol"; - -contract MockERC721 is ERC721 { - constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} - - function tokenURI(uint256) public pure virtual override returns (string memory) {} - - function mint(address to, uint256 tokenId) public virtual { - _mint(to, tokenId); - } - - function burn(uint256 tokenId) public virtual { - _burn(tokenId); - } - - function safeMint(address to, uint256 tokenId) public virtual { - _safeMint(to, tokenId); - } - - function safeMint( - address to, - uint256 tokenId, - bytes memory data - ) public virtual { - _safeMint(to, tokenId, data); - } -} diff --git a/lib/solmate/src/test/utils/mocks/MockOwned.sol b/lib/solmate/src/test/utils/mocks/MockOwned.sol deleted file mode 100644 index 52ef918..0000000 --- a/lib/solmate/src/test/utils/mocks/MockOwned.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Owned} from "../../../auth/Owned.sol"; - -contract MockOwned is Owned(msg.sender) { - bool public flag; - - function updateFlag() public virtual onlyOwner { - flag = true; - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol b/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol deleted file mode 100644 index 23f4633..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract MissingReturnToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "MissingReturnToken"; - - string public constant symbol = "MRT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol deleted file mode 100644 index 8139efe..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract ReturnsFalseToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsFalseToken"; - - string public constant symbol = "RFT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual returns (bool) { - return false; - } - - function transfer(address, uint256) public virtual returns (bool) { - return false; - } - - function transferFrom( - address, - address, - uint256 - ) public virtual returns (bool) { - return false; - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol deleted file mode 100644 index 77c9575..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract ReturnsGarbageToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsGarbageToken"; - - string public constant symbol = "RGT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - MOCK STORAGE - //////////////////////////////////////////////////////////////*/ - - bytes garbage; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - bytes memory _garbage = garbage; - - assembly { - return(add(_garbage, 32), mload(_garbage)) - } - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - bytes memory _garbage = garbage; - - assembly { - return(add(_garbage, 32), mload(_garbage)) - } - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - bytes memory _garbage = garbage; - - assembly { - return(add(_garbage, 32), mload(_garbage)) - } - } - - /*/////////////////////////////////////////////////////////////// - MOCK LOGIC - //////////////////////////////////////////////////////////////*/ - - function setGarbage(bytes memory _garbage) public virtual { - garbage = _garbage; - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol deleted file mode 100644 index 69947c3..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract ReturnsTooLittleToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsTooLittleToken"; - - string public constant symbol = "RTLT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual { - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } - - function transfer(address, uint256) public virtual { - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } - - function transferFrom( - address, - address, - uint256 - ) public virtual { - assembly { - mstore(0, 0x0100000000000000000000000000000000000000000000000000000000000000) - return(0, 8) - } - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol deleted file mode 100644 index 8774cbb..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract ReturnsTooMuchToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsTooMuchToken"; - - string public constant symbol = "RTMT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - assembly { - mstore(0, 1) - return(0, 4096) - } - } - - function transfer(address to, uint256 amount) public virtual { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - assembly { - mstore(0, 1) - return(0, 4096) - } - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - assembly { - mstore(0, 1) - return(0, 4096) - } - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol b/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol deleted file mode 100644 index ac980f8..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract ReturnsTwoToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "ReturnsFalseToken"; - - string public constant symbol = "RTT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual returns (uint256) { - return 2; - } - - function transfer(address, uint256) public virtual returns (uint256) { - return 2; - } - - function transferFrom( - address, - address, - uint256 - ) public virtual returns (uint256) { - return 2; - } -} diff --git a/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol b/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol deleted file mode 100644 index 48ac1fa..0000000 --- a/lib/solmate/src/test/utils/weird-tokens/RevertingToken.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -contract RevertingToken { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public constant name = "RevertingToken"; - - string public constant symbol = "RT"; - - uint8 public constant decimals = 18; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor() { - totalSupply = type(uint256).max; - balanceOf[msg.sender] = type(uint256).max; - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address, uint256) public virtual { - revert(); - } - - function transfer(address, uint256) public virtual { - revert(); - } - - function transferFrom( - address, - address, - uint256 - ) public virtual { - revert(); - } -} diff --git a/lib/solmate/src/tokens/ERC1155.sol b/lib/solmate/src/tokens/ERC1155.sol deleted file mode 100644 index cff0f02..0000000 --- a/lib/solmate/src/tokens/ERC1155.sol +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Minimalist and gas efficient standard ERC1155 implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) -abstract contract ERC1155 { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event TransferSingle( - address indexed operator, - address indexed from, - address indexed to, - uint256 id, - uint256 amount - ); - - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] amounts - ); - - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - event URI(string value, uint256 indexed id); - - /*////////////////////////////////////////////////////////////// - ERC1155 STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => mapping(uint256 => uint256)) public balanceOf; - - mapping(address => mapping(address => bool)) public isApprovedForAll; - - /*////////////////////////////////////////////////////////////// - METADATA LOGIC - //////////////////////////////////////////////////////////////*/ - - function uri(uint256 id) public view virtual returns (string memory); - - /*////////////////////////////////////////////////////////////// - ERC1155 LOGIC - //////////////////////////////////////////////////////////////*/ - - function setApprovalForAll(address operator, bool approved) public virtual { - isApprovedForAll[msg.sender][operator] = approved; - - emit ApprovalForAll(msg.sender, operator, approved); - } - - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes calldata data - ) public virtual { - require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); - - balanceOf[from][id] -= amount; - balanceOf[to][id] += amount; - - emit TransferSingle(msg.sender, from, to, id, amount); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == - ERC1155TokenReceiver.onERC1155Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function safeBatchTransferFrom( - address from, - address to, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) public virtual { - require(ids.length == amounts.length, "LENGTH_MISMATCH"); - - require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); - - // Storing these outside the loop saves ~15 gas per iteration. - uint256 id; - uint256 amount; - - for (uint256 i = 0; i < ids.length; ) { - id = ids[i]; - amount = amounts[i]; - - balanceOf[from][id] -= amount; - balanceOf[to][id] += amount; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, from, to, ids, amounts); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == - ERC1155TokenReceiver.onERC1155BatchReceived.selector, - "UNSAFE_RECIPIENT" - ); - } - - function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) - public - view - virtual - returns (uint256[] memory balances) - { - require(owners.length == ids.length, "LENGTH_MISMATCH"); - - balances = new uint256[](owners.length); - - // Unchecked because the only math done is incrementing - // the array index counter which cannot possibly overflow. - unchecked { - for (uint256 i = 0; i < owners.length; ++i) { - balances[i] = balanceOf[owners[i]][ids[i]]; - } - } - } - - /*////////////////////////////////////////////////////////////// - ERC165 LOGIC - //////////////////////////////////////////////////////////////*/ - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return - interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 - interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 - interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI - } - - /*////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal virtual { - balanceOf[to][id] += amount; - - emit TransferSingle(msg.sender, address(0), to, id, amount); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == - ERC1155TokenReceiver.onERC1155Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal virtual { - uint256 idsLength = ids.length; // Saves MLOADs. - - require(idsLength == amounts.length, "LENGTH_MISMATCH"); - - for (uint256 i = 0; i < idsLength; ) { - balanceOf[to][ids[i]] += amounts[i]; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, address(0), to, ids, amounts); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == - ERC1155TokenReceiver.onERC1155BatchReceived.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _batchBurn( - address from, - uint256[] memory ids, - uint256[] memory amounts - ) internal virtual { - uint256 idsLength = ids.length; // Saves MLOADs. - - require(idsLength == amounts.length, "LENGTH_MISMATCH"); - - for (uint256 i = 0; i < idsLength; ) { - balanceOf[from][ids[i]] -= amounts[i]; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, from, address(0), ids, amounts); - } - - function _burn( - address from, - uint256 id, - uint256 amount - ) internal virtual { - balanceOf[from][id] -= amount; - - emit TransferSingle(msg.sender, from, address(0), id, amount); - } -} - -/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol) -abstract contract ERC1155TokenReceiver { - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external virtual returns (bytes4) { - return ERC1155TokenReceiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external virtual returns (bytes4) { - return ERC1155TokenReceiver.onERC1155BatchReceived.selector; - } -} diff --git a/lib/solmate/src/tokens/ERC20.sol b/lib/solmate/src/tokens/ERC20.sol deleted file mode 100644 index 9657044..0000000 --- a/lib/solmate/src/tokens/ERC20.sol +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) -/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) -/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. -abstract contract ERC20 { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public name; - - string public symbol; - - uint8 public immutable decimals; - - /*////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*////////////////////////////////////////////////////////////// - EIP-2612 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 internal immutable INITIAL_CHAIN_ID; - - bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; - - mapping(address => uint256) public nonces; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals - ) { - name = _name; - symbol = _symbol; - decimals = _decimals; - - INITIAL_CHAIN_ID = block.chainid; - INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); - } - - /*////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - return true; - } - - function transfer(address to, uint256 amount) public virtual returns (bool) { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - return true; - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual returns (bool) { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - return true; - } - - /*////////////////////////////////////////////////////////////// - EIP-2612 LOGIC - //////////////////////////////////////////////////////////////*/ - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); - - // Unchecked because the only math done is incrementing - // the owner's nonce which cannot realistically overflow. - unchecked { - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ), - owner, - spender, - value, - nonces[owner]++, - deadline - ) - ) - ) - ), - v, - r, - s - ); - - require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); - - allowance[recoveredAddress][spender] = value; - } - - emit Approval(owner, spender, value); - } - - function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { - return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); - } - - function computeDomainSeparator() internal view virtual returns (bytes32) { - return - keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(name)), - keccak256("1"), - block.chainid, - address(this) - ) - ); - } - - /*////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint(address to, uint256 amount) internal virtual { - totalSupply += amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(address(0), to, amount); - } - - function _burn(address from, uint256 amount) internal virtual { - balanceOf[from] -= amount; - - // Cannot underflow because a user's balance - // will never be larger than the total supply. - unchecked { - totalSupply -= amount; - } - - emit Transfer(from, address(0), amount); - } -} diff --git a/lib/solmate/src/tokens/ERC6909.sol b/lib/solmate/src/tokens/ERC6909.sol deleted file mode 100644 index 15b4f30..0000000 --- a/lib/solmate/src/tokens/ERC6909.sol +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -/// @notice Minimalist and gas efficient standard ERC6909 implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC6909.sol) -abstract contract ERC6909 { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event OperatorSet(address indexed owner, address indexed operator, bool approved); - - event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount); - - event Transfer(address caller, address indexed from, address indexed to, uint256 indexed id, uint256 amount); - - /*////////////////////////////////////////////////////////////// - ERC6909 STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => mapping(address => bool)) public isOperator; - - mapping(address => mapping(uint256 => uint256)) public balanceOf; - - mapping(address => mapping(address => mapping(uint256 => uint256))) public allowance; - - /*////////////////////////////////////////////////////////////// - ERC6909 LOGIC - //////////////////////////////////////////////////////////////*/ - - function transfer( - address receiver, - uint256 id, - uint256 amount - ) public virtual returns (bool) { - balanceOf[msg.sender][id] -= amount; - - balanceOf[receiver][id] += amount; - - emit Transfer(msg.sender, msg.sender, receiver, id, amount); - - return true; - } - - function transferFrom( - address sender, - address receiver, - uint256 id, - uint256 amount - ) public virtual returns (bool) { - if (msg.sender != sender && !isOperator[sender][msg.sender]) { - uint256 allowed = allowance[sender][msg.sender][id]; - if (allowed != type(uint256).max) allowance[sender][msg.sender][id] = allowed - amount; - } - - balanceOf[sender][id] -= amount; - - balanceOf[receiver][id] += amount; - - emit Transfer(msg.sender, sender, receiver, id, amount); - - return true; - } - - function approve( - address spender, - uint256 id, - uint256 amount - ) public virtual returns (bool) { - allowance[msg.sender][spender][id] = amount; - - emit Approval(msg.sender, spender, id, amount); - - return true; - } - - function setOperator(address operator, bool approved) public virtual returns (bool) { - isOperator[msg.sender][operator] = approved; - - emit OperatorSet(msg.sender, operator, approved); - - return true; - } - - /*////////////////////////////////////////////////////////////// - ERC165 LOGIC - //////////////////////////////////////////////////////////////*/ - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return - interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 - interfaceId == 0x0f632fb3; // ERC165 Interface ID for ERC6909 - } - - /*////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint( - address receiver, - uint256 id, - uint256 amount - ) internal virtual { - balanceOf[receiver][id] += amount; - - emit Transfer(msg.sender, address(0), receiver, id, amount); - } - - function _burn( - address sender, - uint256 id, - uint256 amount - ) internal virtual { - balanceOf[sender][id] -= amount; - - emit Transfer(msg.sender, sender, address(0), id, amount); - } -} diff --git a/lib/solmate/src/tokens/ERC721.sol b/lib/solmate/src/tokens/ERC721.sol deleted file mode 100644 index b47f271..0000000 --- a/lib/solmate/src/tokens/ERC721.sol +++ /dev/null @@ -1,231 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Modern, minimalist, and gas efficient ERC-721 implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) -abstract contract ERC721 { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 indexed id); - - event Approval(address indexed owner, address indexed spender, uint256 indexed id); - - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - /*////////////////////////////////////////////////////////////// - METADATA STORAGE/LOGIC - //////////////////////////////////////////////////////////////*/ - - string public name; - - string public symbol; - - function tokenURI(uint256 id) public view virtual returns (string memory); - - /*////////////////////////////////////////////////////////////// - ERC721 BALANCE/OWNER STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(uint256 => address) internal _ownerOf; - - mapping(address => uint256) internal _balanceOf; - - function ownerOf(uint256 id) public view virtual returns (address owner) { - require((owner = _ownerOf[id]) != address(0), "NOT_MINTED"); - } - - function balanceOf(address owner) public view virtual returns (uint256) { - require(owner != address(0), "ZERO_ADDRESS"); - - return _balanceOf[owner]; - } - - /*////////////////////////////////////////////////////////////// - ERC721 APPROVAL STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(uint256 => address) public getApproved; - - mapping(address => mapping(address => bool)) public isApprovedForAll; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(string memory _name, string memory _symbol) { - name = _name; - symbol = _symbol; - } - - /*////////////////////////////////////////////////////////////// - ERC721 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 id) public virtual { - address owner = _ownerOf[id]; - - require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); - - getApproved[id] = spender; - - emit Approval(owner, spender, id); - } - - function setApprovalForAll(address operator, bool approved) public virtual { - isApprovedForAll[msg.sender][operator] = approved; - - emit ApprovalForAll(msg.sender, operator, approved); - } - - function transferFrom( - address from, - address to, - uint256 id - ) public virtual { - require(from == _ownerOf[id], "WRONG_FROM"); - - require(to != address(0), "INVALID_RECIPIENT"); - - require( - msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], - "NOT_AUTHORIZED" - ); - - // Underflow of the sender's balance is impossible because we check for - // ownership above and the recipient's balance can't realistically overflow. - unchecked { - _balanceOf[from]--; - - _balanceOf[to]++; - } - - _ownerOf[id] = to; - - delete getApproved[id]; - - emit Transfer(from, to, id); - } - - function safeTransferFrom( - address from, - address to, - uint256 id - ) public virtual { - transferFrom(from, to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function safeTransferFrom( - address from, - address to, - uint256 id, - bytes calldata data - ) public virtual { - transferFrom(from, to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - /*////////////////////////////////////////////////////////////// - ERC165 LOGIC - //////////////////////////////////////////////////////////////*/ - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return - interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 - interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 - interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata - } - - /*////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint(address to, uint256 id) internal virtual { - require(to != address(0), "INVALID_RECIPIENT"); - - require(_ownerOf[id] == address(0), "ALREADY_MINTED"); - - // Counter overflow is incredibly unrealistic. - unchecked { - _balanceOf[to]++; - } - - _ownerOf[id] = to; - - emit Transfer(address(0), to, id); - } - - function _burn(uint256 id) internal virtual { - address owner = _ownerOf[id]; - - require(owner != address(0), "NOT_MINTED"); - - // Ownership check above ensures no underflow. - unchecked { - _balanceOf[owner]--; - } - - delete _ownerOf[id]; - - delete getApproved[id]; - - emit Transfer(owner, address(0), id); - } - - /*////////////////////////////////////////////////////////////// - INTERNAL SAFE MINT LOGIC - //////////////////////////////////////////////////////////////*/ - - function _safeMint(address to, uint256 id) internal virtual { - _mint(to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _safeMint( - address to, - uint256 id, - bytes memory data - ) internal virtual { - _mint(to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } -} - -/// @notice A generic interface for a contract which properly accepts ERC721 tokens. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol) -abstract contract ERC721TokenReceiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external virtual returns (bytes4) { - return ERC721TokenReceiver.onERC721Received.selector; - } -} diff --git a/lib/solmate/src/tokens/WETH.sol b/lib/solmate/src/tokens/WETH.sol deleted file mode 100644 index ddf9647..0000000 --- a/lib/solmate/src/tokens/WETH.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "./ERC20.sol"; - -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; - -/// @notice Minimalist and modern Wrapped Ether implementation. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/WETH.sol) -/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) -contract WETH is ERC20("Wrapped Ether", "WETH", 18) { - using SafeTransferLib for address; - - event Deposit(address indexed from, uint256 amount); - - event Withdrawal(address indexed to, uint256 amount); - - function deposit() public payable virtual { - _mint(msg.sender, msg.value); - - emit Deposit(msg.sender, msg.value); - } - - function withdraw(uint256 amount) public virtual { - _burn(msg.sender, amount); - - emit Withdrawal(msg.sender, amount); - - msg.sender.safeTransferETH(amount); - } - - receive() external payable virtual { - deposit(); - } -} diff --git a/lib/solmate/src/utils/Bytes32AddressLib.sol b/lib/solmate/src/utils/Bytes32AddressLib.sol deleted file mode 100644 index 448fb75..0000000 --- a/lib/solmate/src/utils/Bytes32AddressLib.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Library for converting between addresses and bytes32 values. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Bytes32AddressLib.sol) -library Bytes32AddressLib { - function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { - return address(uint160(uint256(bytesValue))); - } - - function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { - return bytes32(bytes20(addressValue)); - } -} diff --git a/lib/solmate/src/utils/CREATE3.sol b/lib/solmate/src/utils/CREATE3.sol deleted file mode 100644 index 8fe0511..0000000 --- a/lib/solmate/src/utils/CREATE3.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Bytes32AddressLib} from "./Bytes32AddressLib.sol"; - -/// @notice Deploy to deterministic addresses without an initcode factor. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/CREATE3.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) -library CREATE3 { - using Bytes32AddressLib for bytes32; - - //--------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //--------------------------------------------------------------------------------// - // 0x36 | 0x36 | CALLDATASIZE | size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size // - // 0x37 | 0x37 | CALLDATACOPY | // - // 0x36 | 0x36 | CALLDATASIZE | size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 size // - // 0x34 | 0x34 | CALLVALUE | value 0 size // - // 0xf0 | 0xf0 | CREATE | newContract // - //--------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //--------------------------------------------------------------------------------// - // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode // - // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode // - // 0x52 | 0x52 | MSTORE | // - // 0x60 | 0x6008 | PUSH1 08 | 8 // - // 0x60 | 0x6018 | PUSH1 18 | 24 8 // - // 0xf3 | 0xf3 | RETURN | // - //--------------------------------------------------------------------------------// - bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3"; - - bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE); - - function deploy( - bytes32 salt, - bytes memory creationCode, - uint256 value - ) internal returns (address deployed) { - bytes memory proxyChildBytecode = PROXY_BYTECODE; - - address proxy; - /// @solidity memory-safe-assembly - assembly { - // Deploy a new contract with our pre-made bytecode via CREATE2. - // We start 32 bytes into the code to avoid copying the byte length. - proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt) - } - require(proxy != address(0), "DEPLOYMENT_FAILED"); - - deployed = getDeployed(salt); - (bool success, ) = proxy.call{value: value}(creationCode); - require(success && deployed.code.length != 0, "INITIALIZATION_FAILED"); - } - - function getDeployed(bytes32 salt) internal view returns (address) { - return getDeployed(salt, address(this)); - } - - function getDeployed(bytes32 salt, address creator) internal pure returns (address) { - address proxy = keccak256( - abi.encodePacked( - // Prefix: - bytes1(0xFF), - // Creator: - creator, - // Salt: - salt, - // Bytecode hash: - PROXY_BYTECODE_HASH - ) - ).fromLast20Bytes(); - - return - keccak256( - abi.encodePacked( - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) - // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) - hex"d6_94", - proxy, - hex"01" // Nonce of the proxy contract (1) - ) - ).fromLast20Bytes(); - } -} diff --git a/lib/solmate/src/utils/FixedPointMathLib.sol b/lib/solmate/src/utils/FixedPointMathLib.sol deleted file mode 100644 index 6887722..0000000 --- a/lib/solmate/src/utils/FixedPointMathLib.sol +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Arithmetic library with operations for fixed-point numbers. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) -/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) -library FixedPointMathLib { - /*////////////////////////////////////////////////////////////// - SIMPLIFIED FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - uint256 internal constant MAX_UINT256 = 2**256 - 1; - - uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. - - function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. - } - - function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. - } - - function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. - } - - function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. - } - - /*////////////////////////////////////////////////////////////// - LOW LEVEL FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function mulDivDown( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } - - // Divide x * y by the denominator. - z := div(mul(x, y), denominator) - } - } - - function mulDivUp( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) - if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { - revert(0, 0) - } - - // If x * y modulo the denominator is strictly greater than 0, - // 1 is added to round up the division of x * y by the denominator. - z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) - } - } - - function rpow( - uint256 x, - uint256 n, - uint256 scalar - ) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - switch x - case 0 { - switch n - case 0 { - // 0 ** 0 = 1 - z := scalar - } - default { - // 0 ** n = 0 - z := 0 - } - } - default { - switch mod(n, 2) - case 0 { - // If n is even, store scalar in z for now. - z := scalar - } - default { - // If n is odd, store x in z for now. - z := x - } - - // Shifting right by 1 is like dividing by 2. - let half := shr(1, scalar) - - for { - // Shift n right by 1 before looping to halve it. - n := shr(1, n) - } n { - // Shift n right by 1 each iteration to halve it. - n := shr(1, n) - } { - // Revert immediately if x ** 2 would overflow. - // Equivalent to iszero(eq(div(xx, x), x)) here. - if shr(128, x) { - revert(0, 0) - } - - // Store x squared. - let xx := mul(x, x) - - // Round to the nearest number. - let xxRound := add(xx, half) - - // Revert if xx + half overflowed. - if lt(xxRound, xx) { - revert(0, 0) - } - - // Set x to scaled xxRound. - x := div(xxRound, scalar) - - // If n is even: - if mod(n, 2) { - // Compute z * x. - let zx := mul(z, x) - - // If z * x overflowed: - if iszero(eq(div(zx, x), z)) { - // Revert if x is non-zero. - if iszero(iszero(x)) { - revert(0, 0) - } - } - - // Round to the nearest number. - let zxRound := add(zx, half) - - // Revert if zx + half overflowed. - if lt(zxRound, zx) { - revert(0, 0) - } - - // Return properly scaled zxRound. - z := div(zxRound, scalar) - } - } - } - } - } - - /*////////////////////////////////////////////////////////////// - GENERAL NUMBER UTILITIES - //////////////////////////////////////////////////////////////*/ - - function sqrt(uint256 x) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - let y := x // We start y at x, which will help us make our initial estimate. - - z := 181 // The "correct" value is 1, but this saves a multiplication later. - - // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad - // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. - - // We check y >= 2^(k + 8) but shift right by k bits - // each branch to ensure that if x >= 256, then y >= 256. - if iszero(lt(y, 0x10000000000000000000000000000000000)) { - y := shr(128, y) - z := shl(64, z) - } - if iszero(lt(y, 0x1000000000000000000)) { - y := shr(64, y) - z := shl(32, z) - } - if iszero(lt(y, 0x10000000000)) { - y := shr(32, y) - z := shl(16, z) - } - if iszero(lt(y, 0x1000000)) { - y := shr(16, y) - z := shl(8, z) - } - - // Goal was to get z*z*y within a small factor of x. More iterations could - // get y in a tighter range. Currently, we will have y in [256, 256*2^16). - // We ensured y >= 256 so that the relative difference between y and y+1 is small. - // That's not possible if x < 256 but we can just verify those cases exhaustively. - - // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. - // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. - // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. - - // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range - // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. - - // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate - // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. - - // There is no overflow risk here since y < 2^136 after the first branch above. - z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. - - // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - - // If x+1 is a perfect square, the Babylonian method cycles between - // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. - // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division - // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. - // If you don't care whether the floor or ceil square root is returned, you can remove this statement. - z := sub(z, lt(div(x, z), z)) - } - } - - function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Mod x by y. Note this will return - // 0 instead of reverting if y is zero. - z := mod(x, y) - } - } - - function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Divide x by y. Note this will return - // 0 instead of reverting if y is zero. - r := div(x, y) - } - } - - function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { - /// @solidity memory-safe-assembly - assembly { - // Add 1 to x * y if x % y > 0. Note this will - // return 0 instead of reverting if y is zero. - z := add(gt(mod(x, y), 0), div(x, y)) - } - } -} diff --git a/lib/solmate/src/utils/LibString.sol b/lib/solmate/src/utils/LibString.sol deleted file mode 100644 index 97c89e0..0000000 --- a/lib/solmate/src/utils/LibString.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -/// @notice Efficient library for creating string representations of integers. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) -/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol) -library LibString { - function toString(int256 value) internal pure returns (string memory str) { - if (value >= 0) return toString(uint256(value)); - - unchecked { - str = toString(uint256(-value)); - - /// @solidity memory-safe-assembly - assembly { - // Note: This is only safe because we over-allocate memory - // and write the string from right to left in toString(uint256), - // and thus can be sure that sub(str, 1) is an unused memory location. - - let length := mload(str) // Load the string length. - // Put the - character at the start of the string contents. - mstore(str, 45) // 45 is the ASCII code for the - character. - str := sub(str, 1) // Move back the string pointer by a byte. - mstore(str, add(length, 1)) // Update the string length. - } - } - } - - function toString(uint256 value) internal pure returns (string memory str) { - /// @solidity memory-safe-assembly - assembly { - // The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes - // to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the - // trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes. - let newFreeMemoryPointer := add(mload(0x40), 160) - - // Update the free memory pointer to avoid overriding our string. - mstore(0x40, newFreeMemoryPointer) - - // Assign str to the end of the zone of newly allocated memory. - str := sub(newFreeMemoryPointer, 32) - - // Clean the last word of memory it may not be overwritten. - mstore(str, 0) - - // Cache the end of the memory to calculate the length later. - let end := str - - // We write the string from rightmost digit to leftmost digit. - // The following is essentially a do-while loop that also handles the zero case. - // prettier-ignore - for { let temp := value } 1 {} { - // Move the pointer 1 byte to the left. - str := sub(str, 1) - - // Write the character to the pointer. - // The ASCII index of the '0' character is 48. - mstore8(str, add(48, mod(temp, 10))) - - // Keep dividing temp until zero. - temp := div(temp, 10) - - // prettier-ignore - if iszero(temp) { break } - } - - // Compute and cache the final total length of the string. - let length := sub(end, str) - - // Move the pointer 32 bytes leftwards to make room for the length. - str := sub(str, 32) - - // Store the string's length at the start of memory allocated for our string. - mstore(str, length) - } - } -} diff --git a/lib/solmate/src/utils/MerkleProofLib.sol b/lib/solmate/src/utils/MerkleProofLib.sol deleted file mode 100644 index 8fd7cbd..0000000 --- a/lib/solmate/src/utils/MerkleProofLib.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -/// @notice Gas optimized merkle proof verification library. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) -/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) -library MerkleProofLib { - function verify( - bytes32[] calldata proof, - bytes32 root, - bytes32 leaf - ) internal pure returns (bool isValid) { - /// @solidity memory-safe-assembly - assembly { - if proof.length { - // Left shifting by 5 is like multiplying by 32. - let end := add(proof.offset, shl(5, proof.length)) - - // Initialize offset to the offset of the proof in calldata. - let offset := proof.offset - - // Iterate over proof elements to compute root hash. - // prettier-ignore - for {} 1 {} { - // Slot where the leaf should be put in scratch space. If - // leaf > calldataload(offset): slot 32, otherwise: slot 0. - let leafSlot := shl(5, gt(leaf, calldataload(offset))) - - // Store elements to hash contiguously in scratch space. - // The xor puts calldataload(offset) in whichever slot leaf - // is not occupying, so 0 if leafSlot is 32, and 32 otherwise. - mstore(leafSlot, leaf) - mstore(xor(leafSlot, 32), calldataload(offset)) - - // Reuse leaf to store the hash to reduce stack operations. - leaf := keccak256(0, 64) // Hash both slots of scratch space. - - offset := add(offset, 32) // Shift 1 word per cycle. - - // prettier-ignore - if iszero(lt(offset, end)) { break } - } - } - - isValid := eq(leaf, root) // The proof is valid if the roots match. - } - } -} diff --git a/lib/solmate/src/utils/ReentrancyGuard.sol b/lib/solmate/src/utils/ReentrancyGuard.sol deleted file mode 100644 index 1453e24..0000000 --- a/lib/solmate/src/utils/ReentrancyGuard.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Gas optimized reentrancy protection for smart contracts. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) -abstract contract ReentrancyGuard { - uint256 private locked = 1; - - modifier nonReentrant() virtual { - require(locked == 1, "REENTRANCY"); - - locked = 2; - - _; - - locked = 1; - } -} diff --git a/lib/solmate/src/utils/SSTORE2.sol b/lib/solmate/src/utils/SSTORE2.sol deleted file mode 100644 index 23d6980..0000000 --- a/lib/solmate/src/utils/SSTORE2.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Read and write to persistent storage at a fraction of the cost. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) -library SSTORE2 { - uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. - - /*////////////////////////////////////////////////////////////// - WRITE LOGIC - //////////////////////////////////////////////////////////////*/ - - function write(bytes memory data) internal returns (address pointer) { - // Prefix the bytecode with a STOP opcode to ensure it cannot be called. - bytes memory runtimeCode = abi.encodePacked(hex"00", data); - - bytes memory creationCode = abi.encodePacked( - //---------------------------------------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //---------------------------------------------------------------------------------------------------------------// - // 0x60 | 0x600B | PUSH1 11 | codeOffset // - // 0x59 | 0x59 | MSIZE | 0 codeOffset // - // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // - // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // - // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // - // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // - // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // - // 0xf3 | 0xf3 | RETURN | // - //---------------------------------------------------------------------------------------------------------------// - hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. - runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. - ); - - /// @solidity memory-safe-assembly - assembly { - // Deploy a new contract with the generated creation code. - // We start 32 bytes into the code to avoid copying the byte length. - pointer := create(0, add(creationCode, 32), mload(creationCode)) - } - - require(pointer != address(0), "DEPLOYMENT_FAILED"); - } - - /*////////////////////////////////////////////////////////////// - READ LOGIC - //////////////////////////////////////////////////////////////*/ - - function read(address pointer) internal view returns (bytes memory) { - return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); - } - - function read(address pointer, uint256 start) internal view returns (bytes memory) { - start += DATA_OFFSET; - - return readBytecode(pointer, start, pointer.code.length - start); - } - - function read( - address pointer, - uint256 start, - uint256 end - ) internal view returns (bytes memory) { - start += DATA_OFFSET; - end += DATA_OFFSET; - - require(pointer.code.length >= end, "OUT_OF_BOUNDS"); - - return readBytecode(pointer, start, end - start); - } - - /*////////////////////////////////////////////////////////////// - INTERNAL HELPER LOGIC - //////////////////////////////////////////////////////////////*/ - - function readBytecode( - address pointer, - uint256 start, - uint256 size - ) private view returns (bytes memory data) { - /// @solidity memory-safe-assembly - assembly { - // Get a pointer to some free memory. - data := mload(0x40) - - // Update the free memory pointer to prevent overriding our data. - // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). - // Adding 31 to size and running the result through the logic above ensures - // the memory pointer remains word-aligned, following the Solidity convention. - mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) - - // Store the size of the data in the first 32 byte chunk of free memory. - mstore(data, size) - - // Copy the code into memory right after the 32 bytes we used to store the size. - extcodecopy(pointer, add(data, 32), start, size) - } - } -} diff --git a/lib/solmate/src/utils/SafeCastLib.sol b/lib/solmate/src/utils/SafeCastLib.sol deleted file mode 100644 index 9e8a2af..0000000 --- a/lib/solmate/src/utils/SafeCastLib.sol +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Safe unsigned integer casting library that reverts on overflow. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) -library SafeCastLib { - function safeCastTo248(uint256 x) internal pure returns (uint248 y) { - require(x < 1 << 248); - - y = uint248(x); - } - - function safeCastTo240(uint256 x) internal pure returns (uint240 y) { - require(x < 1 << 240); - - y = uint240(x); - } - - function safeCastTo232(uint256 x) internal pure returns (uint232 y) { - require(x < 1 << 232); - - y = uint232(x); - } - - function safeCastTo224(uint256 x) internal pure returns (uint224 y) { - require(x < 1 << 224); - - y = uint224(x); - } - - function safeCastTo216(uint256 x) internal pure returns (uint216 y) { - require(x < 1 << 216); - - y = uint216(x); - } - - function safeCastTo208(uint256 x) internal pure returns (uint208 y) { - require(x < 1 << 208); - - y = uint208(x); - } - - function safeCastTo200(uint256 x) internal pure returns (uint200 y) { - require(x < 1 << 200); - - y = uint200(x); - } - - function safeCastTo192(uint256 x) internal pure returns (uint192 y) { - require(x < 1 << 192); - - y = uint192(x); - } - - function safeCastTo184(uint256 x) internal pure returns (uint184 y) { - require(x < 1 << 184); - - y = uint184(x); - } - - function safeCastTo176(uint256 x) internal pure returns (uint176 y) { - require(x < 1 << 176); - - y = uint176(x); - } - - function safeCastTo168(uint256 x) internal pure returns (uint168 y) { - require(x < 1 << 168); - - y = uint168(x); - } - - function safeCastTo160(uint256 x) internal pure returns (uint160 y) { - require(x < 1 << 160); - - y = uint160(x); - } - - function safeCastTo152(uint256 x) internal pure returns (uint152 y) { - require(x < 1 << 152); - - y = uint152(x); - } - - function safeCastTo144(uint256 x) internal pure returns (uint144 y) { - require(x < 1 << 144); - - y = uint144(x); - } - - function safeCastTo136(uint256 x) internal pure returns (uint136 y) { - require(x < 1 << 136); - - y = uint136(x); - } - - function safeCastTo128(uint256 x) internal pure returns (uint128 y) { - require(x < 1 << 128); - - y = uint128(x); - } - - function safeCastTo120(uint256 x) internal pure returns (uint120 y) { - require(x < 1 << 120); - - y = uint120(x); - } - - function safeCastTo112(uint256 x) internal pure returns (uint112 y) { - require(x < 1 << 112); - - y = uint112(x); - } - - function safeCastTo104(uint256 x) internal pure returns (uint104 y) { - require(x < 1 << 104); - - y = uint104(x); - } - - function safeCastTo96(uint256 x) internal pure returns (uint96 y) { - require(x < 1 << 96); - - y = uint96(x); - } - - function safeCastTo88(uint256 x) internal pure returns (uint88 y) { - require(x < 1 << 88); - - y = uint88(x); - } - - function safeCastTo80(uint256 x) internal pure returns (uint80 y) { - require(x < 1 << 80); - - y = uint80(x); - } - - function safeCastTo72(uint256 x) internal pure returns (uint72 y) { - require(x < 1 << 72); - - y = uint72(x); - } - - function safeCastTo64(uint256 x) internal pure returns (uint64 y) { - require(x < 1 << 64); - - y = uint64(x); - } - - function safeCastTo56(uint256 x) internal pure returns (uint56 y) { - require(x < 1 << 56); - - y = uint56(x); - } - - function safeCastTo48(uint256 x) internal pure returns (uint48 y) { - require(x < 1 << 48); - - y = uint48(x); - } - - function safeCastTo40(uint256 x) internal pure returns (uint40 y) { - require(x < 1 << 40); - - y = uint40(x); - } - - function safeCastTo32(uint256 x) internal pure returns (uint32 y) { - require(x < 1 << 32); - - y = uint32(x); - } - - function safeCastTo24(uint256 x) internal pure returns (uint24 y) { - require(x < 1 << 24); - - y = uint24(x); - } - - function safeCastTo16(uint256 x) internal pure returns (uint16 y) { - require(x < 1 << 16); - - y = uint16(x); - } - - function safeCastTo8(uint256 x) internal pure returns (uint8 y) { - require(x < 1 << 8); - - y = uint8(x); - } -} diff --git a/lib/solmate/src/utils/SafeTransferLib.sol b/lib/solmate/src/utils/SafeTransferLib.sol deleted file mode 100644 index 93ef5a9..0000000 --- a/lib/solmate/src/utils/SafeTransferLib.sol +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../tokens/ERC20.sol"; - -/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) -/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. -/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. -library SafeTransferLib { - /*////////////////////////////////////////////////////////////// - ETH OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function safeTransferETH(address to, uint256 amount) internal { - bool success; - - /// @solidity memory-safe-assembly - assembly { - // Transfer the ETH and store if it succeeded or not. - success := call(gas(), to, amount, 0, 0, 0, 0) - } - - require(success, "ETH_TRANSFER_FAILED"); - } - - /*////////////////////////////////////////////////////////////// - ERC20 OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function safeTransferFrom( - ERC20 token, - address from, - address to, - uint256 amount - ) internal { - bool success; - - /// @solidity memory-safe-assembly - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. - mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. - mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) - ) - } - - require(success, "TRANSFER_FROM_FAILED"); - } - - function safeTransfer( - ERC20 token, - address to, - uint256 amount - ) internal { - bool success; - - /// @solidity memory-safe-assembly - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. - mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) - } - - require(success, "TRANSFER_FAILED"); - } - - function safeApprove( - ERC20 token, - address to, - uint256 amount - ) internal { - bool success; - - /// @solidity memory-safe-assembly - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. - mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) - } - - require(success, "APPROVE_FAILED"); - } -} diff --git a/lib/solmate/src/utils/SignedWadMath.sol b/lib/solmate/src/utils/SignedWadMath.sol deleted file mode 100644 index e7d30a4..0000000 --- a/lib/solmate/src/utils/SignedWadMath.sol +++ /dev/null @@ -1,245 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -/// @notice Signed 18 decimal fixed point (wad) arithmetic library. -/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SignedWadMath.sol) -/// @author Modified from Remco Bloemen (https://xn--2-umb.com/22/exp-ln/index.html) - -/// @dev Will not revert on overflow, only use where overflow is not possible. -function toWadUnsafe(uint256 x) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Multiply x by 1e18. - r := mul(x, 1000000000000000000) - } -} - -/// @dev Takes an integer amount of seconds and converts it to a wad amount of days. -/// @dev Will not revert on overflow, only use where overflow is not possible. -/// @dev Not meant for negative second amounts, it assumes x is positive. -function toDaysWadUnsafe(uint256 x) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Multiply x by 1e18 and then divide it by 86400. - r := div(mul(x, 1000000000000000000), 86400) - } -} - -/// @dev Takes a wad amount of days and converts it to an integer amount of seconds. -/// @dev Will not revert on overflow, only use where overflow is not possible. -/// @dev Not meant for negative day amounts, it assumes x is positive. -function fromDaysWadUnsafe(int256 x) pure returns (uint256 r) { - /// @solidity memory-safe-assembly - assembly { - // Multiply x by 86400 and then divide it by 1e18. - r := div(mul(x, 86400), 1000000000000000000) - } -} - -/// @dev Will not revert on overflow, only use where overflow is not possible. -function unsafeWadMul(int256 x, int256 y) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Multiply x by y and divide by 1e18. - r := sdiv(mul(x, y), 1000000000000000000) - } -} - -/// @dev Will return 0 instead of reverting if y is zero and will -/// not revert on overflow, only use where overflow is not possible. -function unsafeWadDiv(int256 x, int256 y) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Multiply x by 1e18 and divide it by y. - r := sdiv(mul(x, 1000000000000000000), y) - } -} - -function wadMul(int256 x, int256 y) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Store x * y in r for now. - r := mul(x, y) - - // Combined overflow check (`x == 0 || (x * y) / x == y`) and edge case check - // where x == -1 and y == type(int256).min, for y == -1 and x == min int256, - // the second overflow check will catch this. - // See: https://secure-contracts.com/learn_evm/arithmetic-checks.html#arithmetic-checks-for-int256-multiplication - // Combining into 1 expression saves gas as resulting bytecode will only have 1 `JUMPI` - // rather than 2. - if iszero( - and( - or(iszero(x), eq(sdiv(r, x), y)), - or(lt(x, not(0)), sgt(y, 0x8000000000000000000000000000000000000000000000000000000000000000)) - ) - ) { - revert(0, 0) - } - - // Scale the result down by 1e18. - r := sdiv(r, 1000000000000000000) - } -} - -function wadDiv(int256 x, int256 y) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Store x * 1e18 in r for now. - r := mul(x, 1000000000000000000) - - // Equivalent to require(y != 0 && ((x * 1e18) / 1e18 == x)) - if iszero(and(iszero(iszero(y)), eq(sdiv(r, 1000000000000000000), x))) { - revert(0, 0) - } - - // Divide r by y. - r := sdiv(r, y) - } -} - -/// @dev Will not work with negative bases, only use when x is positive. -function wadPow(int256 x, int256 y) pure returns (int256) { - // Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y) - return wadExp((wadLn(x) * y) / 1e18); // Using ln(x) means x must be greater than 0. -} - -function wadExp(int256 x) pure returns (int256 r) { - unchecked { - // When the result is < 0.5 we return zero. This happens when - // x <= floor(log(0.5e18) * 1e18) ~ -42e18 - if (x <= -42139678854452767551) return 0; - - // When the result is > (2**255 - 1) / 1e18 we can not represent it as an - // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135. - if (x >= 135305999368893231589) revert("EXP_OVERFLOW"); - - // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 - // for more intermediate precision and a binary basis. This base conversion - // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. - x = (x << 78) / 5**18; - - // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers - // of two such that exp(x) = exp(x') * 2**k, where k is an integer. - // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). - int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96; - x = x - k * 54916777467707473351141471128; - - // k is in the range [-61, 195]. - - // Evaluate using a (6, 7)-term rational approximation. - // p is made monic, we'll multiply by a scale factor later. - int256 y = x + 1346386616545796478920950773328; - y = ((y * x) >> 96) + 57155421227552351082224309758442; - int256 p = y + x - 94201549194550492254356042504812; - p = ((p * y) >> 96) + 28719021644029726153956944680412240; - p = p * x + (4385272521454847904659076985693276 << 96); - - // We leave p in 2**192 basis so we don't need to scale it back up for the division. - int256 q = x - 2855989394907223263936484059900; - q = ((q * x) >> 96) + 50020603652535783019961831881945; - q = ((q * x) >> 96) - 533845033583426703283633433725380; - q = ((q * x) >> 96) + 3604857256930695427073651918091429; - q = ((q * x) >> 96) - 14423608567350463180887372962807573; - q = ((q * x) >> 96) + 26449188498355588339934803723976023; - - /// @solidity memory-safe-assembly - assembly { - // Div in assembly because solidity adds a zero check despite the unchecked. - // The q polynomial won't have zeros in the domain as all its roots are complex. - // No scaling is necessary because p is already 2**96 too large. - r := sdiv(p, q) - } - - // r should be in the range (0.09, 0.25) * 2**96. - - // We now need to multiply r by: - // * the scale factor s = ~6.031367120. - // * the 2**k factor from the range reduction. - // * the 1e18 / 2**96 factor for base conversion. - // We do this all at once, with an intermediate result in 2**213 - // basis, so the final right shift is always by a positive amount. - r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)); - } -} - -function wadLn(int256 x) pure returns (int256 r) { - unchecked { - require(x > 0, "UNDEFINED"); - - // We want to convert x from 10**18 fixed point to 2**96 fixed point. - // We do this by multiplying by 2**96 / 10**18. But since - // ln(x * C) = ln(x) + ln(C), we can simply do nothing here - // and add ln(2**96 / 10**18) at the end. - - /// @solidity memory-safe-assembly - assembly { - r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) - r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) - r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) - r := or(r, shl(4, lt(0xffff, shr(r, x)))) - r := or(r, shl(3, lt(0xff, shr(r, x)))) - r := or(r, shl(2, lt(0xf, shr(r, x)))) - r := or(r, shl(1, lt(0x3, shr(r, x)))) - r := or(r, lt(0x1, shr(r, x))) - } - - // Reduce range of x to (1, 2) * 2**96 - // ln(2^k * x) = k * ln(2) + ln(x) - int256 k = r - 96; - x <<= uint256(159 - k); - x = int256(uint256(x) >> 159); - - // Evaluate using a (8, 8)-term rational approximation. - // p is made monic, we will multiply by a scale factor later. - int256 p = x + 3273285459638523848632254066296; - p = ((p * x) >> 96) + 24828157081833163892658089445524; - p = ((p * x) >> 96) + 43456485725739037958740375743393; - p = ((p * x) >> 96) - 11111509109440967052023855526967; - p = ((p * x) >> 96) - 45023709667254063763336534515857; - p = ((p * x) >> 96) - 14706773417378608786704636184526; - p = p * x - (795164235651350426258249787498 << 96); - - // We leave p in 2**192 basis so we don't need to scale it back up for the division. - // q is monic by convention. - int256 q = x + 5573035233440673466300451813936; - q = ((q * x) >> 96) + 71694874799317883764090561454958; - q = ((q * x) >> 96) + 283447036172924575727196451306956; - q = ((q * x) >> 96) + 401686690394027663651624208769553; - q = ((q * x) >> 96) + 204048457590392012362485061816622; - q = ((q * x) >> 96) + 31853899698501571402653359427138; - q = ((q * x) >> 96) + 909429971244387300277376558375; - /// @solidity memory-safe-assembly - assembly { - // Div in assembly because solidity adds a zero check despite the unchecked. - // The q polynomial is known not to have zeros in the domain. - // No scaling required because p is already 2**96 too large. - r := sdiv(p, q) - } - - // r is in the range (0, 0.125) * 2**96 - - // Finalization, we need to: - // * multiply by the scale factor s = 5.549… - // * add ln(2**96 / 10**18) - // * add k * ln(2) - // * multiply by 10**18 / 2**96 = 5**18 >> 78 - - // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 - r *= 1677202110996718588342820967067443963516166; - // add ln(2) * k * 5e18 * 2**192 - r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k; - // add ln(2**96 / 10**18) * 5e18 * 2**192 - r += 600920179829731861736702779321621459595472258049074101567377883020018308; - // base conversion: mul 2**18 / 2**192 - r >>= 174; - } -} - -/// @dev Will return 0 instead of reverting if y is zero. -function unsafeDiv(int256 x, int256 y) pure returns (int256 r) { - /// @solidity memory-safe-assembly - assembly { - // Divide x by y. - r := sdiv(x, y) - } -}