From d863fdcdeebc0ba798a97c5e03dfbcad2701e6f1 Mon Sep 17 00:00:00 2001 From: shiv Date: Tue, 5 Sep 2023 08:18:02 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=B9=20~~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gas-snapshot | 36 +++++++++++----------- src/Keep.sol | 73 +++++++++++++++++++++++---------------------- src/KeepFactory.sol | 8 ++--- test/Keep.t.sol | 13 ++++---- 4 files changed, 67 insertions(+), 63 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 4124d76e..5323a433 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,30 +1,30 @@ -KeepFactoryTest:testDeploy() (gas: 166573) -KeepFactoryTest:testDetermination() (gas: 170987) +KeepFactoryTest:testDeploy() (gas: 166421) +KeepFactoryTest:testDetermination() (gas: 170835) KeepTest:testBalanceOf() (gas: 116093) -KeepTest:testBalanceOfBatch() (gas: 125406) +KeepTest:testBalanceOfBatch() (gas: 125384) KeepTest:testBalanceOfSigner() (gas: 49511) -KeepTest:testCannotBatchTransferKeepERC1155ToUnsafeContractAddress() (gas: 390859) -KeepTest:testCannotBatchTransferKeepERC1155ToZeroAddress() (gas: 247565) +KeepTest:testCannotBatchTransferKeepERC1155ToUnsafeContractAddress() (gas: 390750) +KeepTest:testCannotBatchTransferKeepERC1155ToZeroAddress() (gas: 247499) KeepTest:testCannotFetchMismatchedLengthBalanceOfBatch() (gas: 14813) -KeepTest:testCannotMintToZeroAddress() (gas: 119050) -KeepTest:testCannotRepeatKeepSetup() (gas: 4797791) -KeepTest:testCannotSetupWithExcessiveQuorum() (gas: 83736) -KeepTest:testCannotSetupWithOutOfOrderSigners() (gas: 103072) -KeepTest:testCannotSetupWithZeroQuorum() (gas: 83753) +KeepTest:testCannotMintToZeroAddress() (gas: 119161) +KeepTest:testCannotRepeatKeepSetup() (gas: 4781571) +KeepTest:testCannotSetupWithExcessiveQuorum() (gas: 83737) +KeepTest:testCannotSetupWithOutOfOrderSigners() (gas: 103051) +KeepTest:testCannotSetupWithZeroQuorum() (gas: 83754) KeepTest:testCannotSpendKeepTokenDelegateBySigAfterDeadline(address,uint256) (runs: 256, μ: 15152, ~: 15152) -KeepTest:testCannotSpendKeepTokenPermitAfterDeadline(address,bool) (runs: 256, μ: 15215, ~: 15215) +KeepTest:testCannotSpendKeepTokenPermitAfterDeadline(address,bool) (runs: 256, μ: 15260, ~: 15260) KeepTest:testCannotTransferKeepERC1155ToUnsafeContractAddress() (gas: 380841) KeepTest:testCannotTransferKeepERC1155ToZeroAddress() (gas: 241977) KeepTest:testCannotTransferKeepTokenWithUnderflow(uint256) (runs: 256, μ: 154600, ~: 154600) -KeepTest:testCannotTransferKeepTokenWithoutPermission(address,address,uint256,uint256) (runs: 256, μ: 180648, ~: 183911) -KeepTest:testExecuteDelegateCall() (gas: 46577) -KeepTest:testExecuteEthCall() (gas: 72390) -KeepTest:testExecuteTokenCallWithContractSignatures() (gas: 68032) +KeepTest:testCannotTransferKeepTokenWithoutPermission(address,address,uint256,uint256) (runs: 256, μ: 180316, ~: 183911) +KeepTest:testExecuteDelegateCall() (gas: 48581) +KeepTest:testExecuteEthCall() (gas: 74394) +KeepTest:testExecuteTokenCallWithContractSignatures() (gas: 70036) KeepTest:testName() (gas: 10048) KeepTest:testNoKeepKeyCollision() (gas: 208) -KeepTest:testNonceIncrementAfterExecute() (gas: 88075) -KeepTest:testQuorum() (gas: 23933) -KeepTest:testReceiveBatchERC1155() (gas: 44271) +KeepTest:testNonceIncrementAfterExecute() (gas: 90083) +KeepTest:testQuorum() (gas: 24319) +KeepTest:testReceiveBatchERC1155() (gas: 44228) MulticallableTest:testMulticallableBenchmark() (gas: 28510) MulticallableTest:testMulticallableOriginalBenchmark() (gas: 37767) MulticallableTest:testMulticallablePreservesMsgSender() (gas: 10980) diff --git a/src/Keep.sol b/src/Keep.sol index 725a0c5b..0b21fff9 100644 --- a/src/Keep.sol +++ b/src/Keep.sol @@ -76,8 +76,8 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { /// @dev Emitted when Keep relays calls. event Multirelayed(Call[] calls); - /// @dev Emitted when `quorum` updates. - event QuorumSet(uint256 threshold); + /// @dev Emitted when `quorum` `threshold` updates. + event ThresholdSet(uint256 id, uint256 quorum); /// @dev Emitted when signature revoked. event SignatureRevoked(address indexed user, bytes32 hash); @@ -105,11 +105,11 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { /// @dev Record of states verifying `execute()`. uint120 public nonce; - /// @dev SIGN_KEY threshold to `execute()`. - uint120 public quorum; - /// @dev Internal ID metadata mapping. - mapping(uint256 id => string meta) internal _uri; + mapping(uint256 id => string) internal _uri; + + /// @dev Internal ID `threshold` mapping. + mapping(uint256 id => uint256) public threshold; /// @dev Contract signature hash revocation status. mapping(address user => mapping(bytes32 hash => bool)) public revoked; @@ -192,27 +192,26 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { constructor(Keep _validator) payable { validator = _validator; // Deploy as singleton. - quorum = 1; + threshold[SIGN_KEY] = 1; } /// @notice Initialize Keep configuration. /// @param calls Initial Keep operations. /// @param signers Initial signer set. - /// @param threshold Initial quorum. + /// @param quorum Initial quorum. function initialize( Call[] calldata calls, address[] calldata signers, - uint256 threshold + uint256 quorum ) public payable virtual { - if (quorum != 0) revert AlreadyInit(); + if (threshold[SIGN_KEY] != 0) revert AlreadyInit(); - if (threshold == 0) revert InvalidThreshold(); + if (quorum == 0) revert InvalidThreshold(); - if (threshold > signers.length) revert InvalidThreshold(); + if (quorum > signers.length) revert InvalidThreshold(); - uint256 i; if (calls.length != 0) { - for (i; i < calls.length; ) { + for (uint256 i; i < calls.length; ) { // An array can't have a total length // larger than the max uint256 value. unchecked { @@ -232,7 +231,7 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { address signer; uint256 supply; - for (i; i < signers.length; ) { + for (uint256 i; i < signers.length; ) { signer = signers[i]; // Prevent zero and duplicate signers. @@ -252,7 +251,7 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { } totalSupply[SIGN_KEY] = supply; - quorum = uint120(threshold); + threshold[SIGN_KEY] = quorum; } /// ----------------------------------------------------------------------- @@ -302,13 +301,13 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { // Memo zero `user` in loop for ascending order. address previous; // Memo `quorum` `threshold` for loop length. - uint256 threshold = quorum; + uint256 quorum = threshold[SIGN_KEY]; // Memo `sig` outside loop for gas optimization. Signature calldata sig; - // Check enough valid `sigs` to pass `threshold`. + // Check enough valid `sigs` to pass `quorum`. uint256 i; - for (i; i < threshold; ) { + for (i; i < quorum; ) { // Load `user` details. sig = sigs[i]; address user = sig.user; @@ -504,11 +503,11 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { userOpHash := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`. } + uint32 key = uint32(userOp.nonce >> 64); + // Shift `userOp.nonce` to branch between `validator` & signature check. - if ( - userOp.nonce >> 64 != uint256(uint32(this.validateUserOp.selector)) - ) { - validationData = _validate(userOpHash, userOp.signature, SIGN_KEY); + if (key != uint32(this.validateUserOp.selector)) { + validationData = _validate(userOpHash, userOp.signature, key); } else { validationData = validator.validateUserOp( userOp, @@ -541,10 +540,12 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { uint256 id ) internal view virtual returns (uint256 validationData) { address user; - uint256 threshold = quorum; + uint256 quorum = threshold[id]; + + if (quorum == 0) return 1; // Early check for single `sig`. - if (threshold == 1) { + if (quorum == 1) { (user, validationData) = _validateSig(hash, sig); if (validationData == 1) return 1; @@ -559,9 +560,9 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { // Memo zero `user` in loop for ascending order. address previous; - // Check enough valid `sigs` to pass `threshold`. + // Check enough valid `sigs` to pass `quorum`. uint256 i; - for (i; i < threshold; ) { + for (i; i < quorum; ) { (user, validationData) = _validateSig(hash, sigs[i]); if (validationData == 1) return 1; @@ -743,24 +744,26 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable { _burn(from, id, amount); if (id == SIGN_KEY) - if (quorum > totalSupply[SIGN_KEY]) revert InvalidThreshold(); + if (threshold[SIGN_KEY] > totalSupply[SIGN_KEY]) + revert InvalidThreshold(); } /// ----------------------------------------------------------------------- /// Threshold Setting Logic /// ----------------------------------------------------------------------- - /// @notice Update Keep quorum threshold. - /// @param threshold Signature threshold for `execute()`. - function setQuorum(uint256 threshold) public payable virtual { + /// @notice Update Keep ID threshold. + /// @param id ID key to set threshold for. + /// @param quorum Signature threshold for operations. + function setThreshold(uint256 id, uint256 quorum) public payable virtual { _authorized(); - if (threshold == 0) revert InvalidThreshold(); - if (threshold > totalSupply[SIGN_KEY]) revert InvalidThreshold(); + if (quorum == 0) revert InvalidThreshold(); + if (quorum > totalSupply[id]) revert InvalidThreshold(); - quorum = uint120(threshold); + threshold[SIGN_KEY] = uint120(quorum); - emit QuorumSet(threshold); + emit ThresholdSet(id, quorum); } /// ----------------------------------------------------------------------- diff --git a/src/KeepFactory.sol b/src/KeepFactory.sol index 4fae353e..ae5a187b 100644 --- a/src/KeepFactory.sol +++ b/src/KeepFactory.sol @@ -11,7 +11,7 @@ contract KeepFactory is Multicallable, Ownable(tx.origin) { /// Events /// ----------------------------------------------------------------------- - event Deployed(Keep indexed keep, uint256 threshold); + event Deployed(Keep indexed keep, uint256 quorum); /// ----------------------------------------------------------------------- /// Custom Errors @@ -41,7 +41,7 @@ contract KeepFactory is Multicallable, Ownable(tx.origin) { bytes32 name, // create2 salt. Call[] calldata calls, address[] calldata signers, - uint256 threshold + uint256 quorum ) public payable virtual returns (Keep keep) { bytes memory data = abi.encodePacked(name); address implementation = address(keepTemplate); @@ -110,9 +110,9 @@ contract KeepFactory is Multicallable, Ownable(tx.origin) { mstore(sub(data, 0x60), mBefore3) } - keep.initialize(calls, signers, threshold); + keep.initialize(calls, signers, quorum); - emit Deployed(keep, threshold); + emit Deployed(keep, quorum); } function determineKeep( diff --git a/test/Keep.t.sol b/test/Keep.t.sol index a83223b6..548a1987 100644 --- a/test/Keep.t.sol +++ b/test/Keep.t.sol @@ -410,12 +410,12 @@ contract KeepTest is Keep(Keep(address(0))), Test, TestHelpers { // } function testQuorum() public payable { - assert(keep.quorum() == 2); + assert(keep.threshold(SIGN_KEY) == 2); vm.prank(address(keep)); - keep.setQuorum(3); + keep.setThreshold(SIGN_KEY, 3); - assert(keep.quorum() == 3); + assert(keep.threshold(SIGN_KEY) == 3); } function testBalanceOf() public { @@ -503,8 +503,9 @@ contract KeepTest is Keep(Keep(address(0))), Test, TestHelpers { assert( keep.multirelay.selector != keep.mint.selector && keep.mint.selector != keep.burn.selector && - keep.burn.selector != keep.setQuorum.selector && - keep.setQuorum.selector != keep.setTransferability.selector && + keep.burn.selector != keep.setThreshold.selector && + keep.setThreshold.selector != + keep.setTransferability.selector && keep.setTransferability.selector != keep.setPermission.selector && keep.setPermission.selector != @@ -1011,7 +1012,7 @@ contract KeepTest is Keep(Keep(address(0))), Test, TestHelpers { assert(keep.totalSupply(SIGNER_KEY) == 3); assert(keep.totalSupply(1) == 0); - assert(keep.quorum() == 2); + assert(keep.threshold(SIGN_KEY) == 2); } // function testCannotMintToUnsafeAddress() public payable {