diff --git a/.gas-snapshot b/.gas-snapshot
index ea09c74b..f43f6674 100644
--- a/.gas-snapshot
+++ b/.gas-snapshot
@@ -1,134 +1,41 @@
-KaliTest:testBurnProposal() (gas: 309077)
-KaliTest:testCallProposal() (gas: 301236)
-KaliTest:testDaoURI() (gas: 12365)
-KaliTest:testDeleteProposal() (gas: 386781)
-KaliTest:testDeploy() (gas: 162214)
-KaliTest:testDetermination() (gas: 10624)
-KaliTest:testERC1155BatchReceiver() (gas: 44630)
-KaliTest:testERC1155Receiver() (gas: 39762)
-KaliTest:testERC20Receiver() (gas: 19850)
-KaliTest:testERC721Receiver() (gas: 39660)
-KaliTest:testExtensionBurn() (gas: 125045)
-KaliTest:testExtensionDeleteProposal() (gas: 311172)
-KaliTest:testExtensionMint() (gas: 128533)
-KaliTest:testExtensionRelay() (gas: 102506)
-KaliTest:testExtensionSetExtension(address,bool) (runs: 256, μ: 51080, ~: 45491)
-KaliTest:testExtensionSetTransferability(bool) (runs: 256, μ: 89034, ~: 83360)
-KaliTest:testExtensionSetURI(string) (runs: 256, μ: 91130, ~: 97656)
-KaliTest:testExtensionUpdateGovSettings() (gas: 78959)
-KaliTest:testExtensionUpdateGovSettingsInvalid() (gas: 126344)
-KaliTest:testFailDeploy() (gas: 8937393460516749706)
-KaliTest:testFailNotAuthorizedExtension() (gas: 18102)
-KaliTest:testFailProposalCreation() (gas: 132488)
-KaliTest:testFailProposalRepeatVoting() (gas: 310819)
-KaliTest:testGracePeriod() (gas: 10365)
-KaliTest:testGracePeriodProposal() (gas: 266211)
-KaliTest:testMintProposal() (gas: 316290)
-KaliTest:testMultiBurnProposal() (gas: 356252)
-KaliTest:testMultiCallProposal() (gas: 340299)
-KaliTest:testMultiMintProposal() (gas: 512652)
-KaliTest:testName() (gas: 9773)
-KaliTest:testPauseProposal() (gas: 296167)
-KaliTest:testProposal() (gas: 303651)
-KaliTest:testProposalCancellation() (gas: 168217)
-KaliTest:testProposalCreation() (gas: 131162)
-KaliTest:testProposalRepeatProcessingFail() (gas: 302123)
-KaliTest:testProposalSponsorship() (gas: 409053)
-KaliTest:testProposalVoteStored() (gas: 268301)
-KaliTest:testQuorum() (gas: 10460)
-KaliTest:testQuorumProposal() (gas: 266362)
-KaliTest:testSupermajority() (gas: 10415)
-KaliTest:testSupermajorityProposal() (gas: 266387)
-KaliTest:testSupportsInterface() (gas: 10845)
-KaliTest:testToken() (gas: 10428)
-KaliTest:testTokenId() (gas: 8257)
-KaliTest:testTypeProposal() (gas: 291531)
-KaliTest:testURIProposal() (gas: 272592)
-KaliTest:testVotingPeriod() (gas: 10444)
-KaliTest:testVotingPeriodProposal() (gas: 266523)
-KeepFactoryTest:testDeploy() (gas: 178028)
-KeepFactoryTest:testDetermination() (gas: 206990)
-KeepTest:testBalanceOf() (gas: 116152)
-KeepTest:testBalanceOfBatch() (gas: 125417)
-KeepTest:testBalanceOfSigner() (gas: 49599)
-KeepTest:testBurn() (gas: 125149)
-KeepTest:testBurnSigner() (gas: 28745)
-KeepTest:testCannotBatchTransferKeepERC1155ToUnsafeContractAddress() (gas: 390794)
-KeepTest:testCannotBatchTransferKeepERC1155ToZeroAddress() (gas: 247550)
-KeepTest:testCannotBurnUnderflow() (gas: 116287)
-KeepTest:testCannotExecuteWithImproperSignatures() (gas: 44742)
-KeepTest:testCannotExecuteWithNullSignatures() (gas: 32330)
-KeepTest:testCannotExecuteWithSignaturesOutOfOrder() (gas: 42061)
-KeepTest:testCannotExecuteWithSignaturesRepeated() (gas: 37849)
-KeepTest:testCannotFetchMismatchedLengthBalanceOfBatch() (gas: 13287)
-KeepTest:testCannotMintOverflowExecuteID() (gas: 65940)
-KeepTest:testCannotMintOverflowSupply() (gas: 261379)
-KeepTest:testCannotMintToUnsafeAddress() (gas: 313707)
-KeepTest:testCannotMintToZeroAddress() (gas: 119138)
-KeepTest:testCannotRepeatKeepSetup() (gas: 4267374)
-KeepTest:testCannotSetTransferability(address,uint256) (runs: 256, μ: 24778, ~: 24778)
-KeepTest:testCannotSetupWithExcessiveQuorum() (gas: 83492)
-KeepTest:testCannotSetupWithOutOfOrderSigners() (gas: 102766)
-KeepTest:testCannotSetupWithZeroQuorum() (gas: 83486)
-KeepTest:testCannotSpendKeepTokenDelegateBySigAfterDeadline(address,uint256) (runs: 256, μ: 15192, ~: 15192)
-KeepTest:testCannotSpendKeepTokenPermitAfterDeadline(address,bool) (runs: 256, μ: 15278, ~: 15278)
-KeepTest:testCannotTransferKeepERC1155ToUnsafeContractAddress() (gas: 380892)
-KeepTest:testCannotTransferKeepERC1155ToZeroAddress() (gas: 241982)
-KeepTest:testCannotTransferKeepTokenWithUnderflow(uint256) (runs: 256, μ: 154659, ~: 154659)
-KeepTest:testCannotTransferKeepTokenWithoutPermission(address,address,uint256,uint256) (runs: 256, μ: 180066, ~: 183984)
-KeepTest:testExecuteCreateCall() (gas: 1500184)
-KeepTest:testExecuteDelegateCall() (gas: 46886)
-KeepTest:testExecuteEthCall() (gas: 72686)
-KeepTest:testExecuteTokenCallWithContractSignatures() (gas: 68306)
-KeepTest:testExecuteTokenCallWithRole() (gas: 144861)
-KeepTest:testExecuteTokenCallWithSignatures() (gas: 56968)
-KeepTest:testIdKeyRole() (gas: 230237)
-KeepTest:testKeepNonce() (gas: 10396)
-KeepTest:testKeepTokenApprove(address,address) (runs: 256, μ: 29557, ~: 29528)
-KeepTest:testKeepTokenBatchTransferByOperator() (gas: 444672)
-KeepTest:testKeepTokenBatchTransferByOwner() (gas: 419371)
-KeepTest:testKeepTokenDelegateBySig(address,uint256,uint256) (runs: 256, μ: 222646, ~: 229001)
-KeepTest:testKeepTokenDelegation(address,address,uint256,uint256) (runs: 256, μ: 255462, ~: 261812)
-KeepTest:testKeepTokenDelegationBalanceByTransfer(address,address,uint256,uint256) (runs: 256, μ: 269111, ~: 276114)
-KeepTest:testKeepTokenInitDelegationBalance(address,uint256,uint256) (runs: 256, μ: 125127, ~: 129100)
-KeepTest:testKeepTokenPermit(address,bool) (runs: 256, μ: 53358, ~: 47839)
-KeepTest:testMint(uint256,uint256,bytes) (runs: 256, μ: 122048, ~: 125302)
-KeepTest:testMintCoreIdKey(uint256) (runs: 256, μ: 120814, ~: 126113)
-KeepTest:testMintExecuteIdKey() (gas: 56215)
-KeepTest:testName() (gas: 9857)
-KeepTest:testNoKeepKeyCollision() (gas: 230)
-KeepTest:testNonceIncrementAfterExecute() (gas: 88647)
-KeepTest:testQuorum() (gas: 23966)
-KeepTest:testReceiveBatchERC1155() (gas: 44337)
-KeepTest:testReceiveERC1155() (gas: 40945)
-KeepTest:testReceiveERC721() (gas: 47587)
-KeepTest:testReceiveETH() (gas: 13046)
-KeepTest:testSetURI(address) (runs: 256, μ: 53169, ~: 53169)
-KeepTest:testSignerSetup() (gas: 29085)
-KeepTest:testSupportsInterface() (gas: 14024)
-KeepTest:testTotalSignerSupply() (gas: 49190)
-KeepTest:testTotalSupply() (gas: 115804)
-KeepTest:testUserNonce() (gas: 10517)
-MulticallableTest:testMulticallableBenchmark() (gas: 29217)
-MulticallableTest:testMulticallableOriginalBenchmark() (gas: 38474)
-MulticallableTest:testMulticallablePreservesMsgSender() (gas: 11022)
-MulticallableTest:testMulticallablePreservesMsgValue() (gas: 37542)
-MulticallableTest:testMulticallablePreservesMsgValueUsedTwice() (gas: 39315)
-MulticallableTest:testMulticallableReturnDataIsProperlyEncoded() (gas: 11617)
-MulticallableTest:testMulticallableReturnDataIsProperlyEncoded(string,string,uint256) (runs: 256, μ: 9732, ~: 7390)
-MulticallableTest:testMulticallableReturnDataIsProperlyEncoded(uint256,uint256,uint256,uint256) (runs: 256, μ: 11718, ~: 11718)
-MulticallableTest:testMulticallableRevertWithCustomError() (gas: 10247)
-MulticallableTest:testMulticallableRevertWithMessage() (gas: 11992)
-MulticallableTest:testMulticallableRevertWithMessage(string) (runs: 256, μ: 12638, ~: 12672)
-MulticallableTest:testMulticallableRevertWithNothing() (gas: 10168)
-MulticallableTest:testMulticallableWithNoData() (gas: 6266)
-OwnedTest:testCallFunctionAsNonOwner() (gas: 11280)
-OwnedTest:testCallFunctionAsNonOwner(address) (runs: 256, μ: 16217, ~: 16236)
-OwnedTest:testCallFunctionAsOwner() (gas: 10432)
-OwnedTest:testERC165Support() (gas: 5479)
-OwnedTest:testTransferOwnership() (gas: 13097)
-OwnedTest:testTransferOwnership(address) (runs: 256, μ: 13170, ~: 13189)
-ReentrancyGuardTest:invariantReentrancyStatusAlways1() (runs: 256, calls: 3840, reverts: 290)
-ReentrancyGuardTest:testFailUnprotectedCall() (gas: 43432)
-ReentrancyGuardTest:testNoReentrancy() (gas: 5354)
-ReentrancyGuardTest:testProtectedCall() (gas: 30985)
\ No newline at end of file
+KeepFactoryTest:testDeploy() (gas: 166225)
+KeepFactoryTest:testDetermination() (gas: 170370)
+KeepTest:testBalanceOf() (gas: 116179)
+KeepTest:testBalanceOfBatch() (gas: 125388)
+KeepTest:testBalanceOfSigner() (gas: 49553)
+KeepTest:testCannotBatchTransferKeepERC1155ToUnsafeContractAddress() (gas: 390770)
+KeepTest:testCannotBatchTransferKeepERC1155ToZeroAddress() (gas: 247497)
+KeepTest:testCannotFetchMismatchedLengthBalanceOfBatch() (gas: 14769)
+KeepTest:testCannotMintToZeroAddress() (gas: 119242)
+KeepTest:testCannotRepeatKeepSetup() (gas: 4731394)
+KeepTest:testCannotSetupWithExcessiveQuorum() (gas: 83526)
+KeepTest:testCannotSetupWithOutOfOrderSigners() (gas: 102812)
+KeepTest:testCannotSetupWithZeroQuorum() (gas: 83543)
+KeepTest:testCannotSpendKeepTokenDelegateBySigAfterDeadline(address,uint256) (runs: 256, μ: 15130, ~: 15130)
+KeepTest:testCannotSpendKeepTokenPermitAfterDeadline(address,bool) (runs: 256, μ: 15261, ~: 15261)
+KeepTest:testCannotTransferKeepERC1155ToUnsafeContractAddress() (gas: 380817)
+KeepTest:testCannotTransferKeepERC1155ToZeroAddress() (gas: 241953)
+KeepTest:testCannotTransferKeepTokenWithUnderflow(uint256) (runs: 256, μ: 154576, ~: 154576)
+KeepTest:testCannotTransferKeepTokenWithoutPermission(address,address,uint256,uint256) (runs: 256, μ: 180588, ~: 183865)
+KeepTest:testExecuteDelegateCall() (gas: 48482)
+KeepTest:testExecuteEthCall() (gas: 74317)
+KeepTest:testExecuteTokenCallWithContractSignatures() (gas: 69959)
+KeepTest:testName() (gas: 9812)
+KeepTest:testNoKeepKeyCollision() (gas: 208)
+KeepTest:testNonceIncrementAfterExecute() (gas: 89818)
+KeepTest:testQuorum() (gas: 24370)
+KeepTest:testReceiveBatchERC1155() (gas: 44228)
+MulticallableTest:testMulticallableBenchmark() (gas: 28510)
+MulticallableTest:testMulticallableOriginalBenchmark() (gas: 37767)
+MulticallableTest:testMulticallablePreservesMsgSender() (gas: 10980)
+MulticallableTest:testMulticallablePreservesMsgValue() (gas: 37455)
+MulticallableTest:testMulticallablePreservesMsgValueUsedTwice() (gas: 39182)
+MulticallableTest:testMulticallableRevertWithCustomError() (gas: 11730)
+MulticallableTest:testMulticallableRevertWithNothing() (gas: 11651)
+MulticallableTest:testMulticallableWithNoData() (gas: 6293)
+OwnableTest:testCallFunctionAsNonOwner() (gas: 9038)
+OwnableTest:testCallFunctionAsNonOwner(address) (runs: 256, μ: 13975, ~: 13994)
+OwnableTest:testCallFunctionAsOwner() (gas: 8296)
+OwnableTest:testERC165Support() (gas: 3373)
+OwnableTest:testTransferOwnership() (gas: 10855)
+OwnableTest:testTransferOwnership(address) (runs: 256, μ: 10928, ~: 10947)
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 37e21d48..a9eec891 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -38,7 +38,7 @@ jobs:
${{ runner.os }}-pnpm-store-
- name: Install dev dependencies
- run: pnpm i
+ run: pnpm install --no-frozen-lockfile
- name: Run formatter
run: pnpm run format
diff --git a/.gitpod.yml b/.gitpod.yml
deleted file mode 100644
index 98974eb2..00000000
--- a/.gitpod.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-tasks:
- - init: curl -L https://foundry.paradigm.xyz | bash && source /home/gitpod/.bashrc && foundryup && pnpm i
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 02001a00..34269f3b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,5 +5,5 @@
"[solidity]": {
"editor.defaultFormatter": "JuanBlanco.solidity"
},
- "solidity.compileUsingRemoteVersion": "v0.8.18"
+ "solidity.compileUsingRemoteVersion": "v0.8.19"
}
diff --git a/LICENSE b/LICENSE
index a2668bfe..29ebfa54 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,661 @@
-MIT License
-
-Copyright (c) 2022, 2023 KaliCo LLC.
-
-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
+ 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/foundry.toml b/foundry.toml
index 1a75b37f..c64ae5c8 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -1,5 +1,5 @@
[profile.default]
-solc = "0.8.18"
+solc = "0.8.19"
optimizer_runs = 9_999_999
# Configure remappings
diff --git a/package.json b/package.json
index 5300ac36..72388a66 100644
--- a/package.json
+++ b/package.json
@@ -8,12 +8,12 @@
"url": "git+https://github.com/kalidao/keep.git"
},
"author": "KaliCo LLC ",
- "license": "MIT",
+ "license": "AGPL-3.0-only",
"private": false,
"scripts": {
- "format": "prettier --write src/**.sol src/utils/**.sol src/utils/interfaces/**.sol src/extensions/dao/**.sol src/extensions/dao/utils/**.sol src/extensions/metadata/**.sol src/extensions/mint/**.sol src/extensions/storage/**.sol src/extensions/utils/**.sol test/**.sol test/utils/mocks/**.sol flat/**.sol --plugin-search-dir=.",
- "format:list": "prettier --list-different 'src/**/*.sol' --plugin-search-dir=.",
- "format:check": "prettier --check 'src/**/*.sol' --plugin-search-dir=.",
+ "format": "prettier --write --plugin=prettier-plugin-solidity 'src/**/*.sol' 'src/extensions/validate/*.sol' 'src/extensions/mint/*.sol' 'test/**/*.sol' 'flat/**/*.sol'",
+ "format:list": "prettier --list-different 'src/**/*.sol'",
+ "format:check": "prettier --check 'src/**/*.sol'",
"solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix --ignore-path .solhintignore",
"solhint:check": "solhint --config ./.solhint.json 'src/**/*.sol'",
"clean": "forge clean",
@@ -23,8 +23,8 @@
"snapshot": "forge clean && forge snapshot --optimize --optimizer-runs 9999999"
},
"devDependencies": {
- "prettier": "^2.8.8",
+ "prettier": "^3.0.3",
"prettier-plugin-solidity": "1.1.3",
- "solhint": "^3.4.1"
+ "solhint": "^3.6.2"
}
}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13dc2c41..e81632a1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,41 +1,46 @@
lockfileVersion: '6.0'
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
devDependencies:
prettier:
- specifier: ^2.8.8
- version: 2.8.8
+ specifier: ^3.0.3
+ version: 3.0.3
prettier-plugin-solidity:
specifier: 1.1.3
- version: 1.1.3(prettier@2.8.8)
+ version: 1.1.3(prettier@3.0.3)
solhint:
- specifier: ^3.4.1
- version: 3.4.1
+ specifier: ^3.6.2
+ version: 3.6.2
packages:
- /@babel/code-frame@7.22.5:
- resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
+ /@babel/code-frame@7.22.13:
+ resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/highlight': 7.22.5
+ '@babel/highlight': 7.22.13
+ chalk: 2.4.2
dev: true
- /@babel/helper-validator-identifier@7.22.5:
- resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
+ /@babel/helper-validator-identifier@7.22.15:
+ resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==}
engines: {node: '>=6.9.0'}
dev: true
- /@babel/highlight@7.22.5:
- resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==}
+ /@babel/highlight@7.22.13:
+ resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/helper-validator-identifier': 7.22.5
+ '@babel/helper-validator-identifier': 7.22.15
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
- /@solidity-parser/parser@0.16.0:
- resolution: {integrity: sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==}
+ /@solidity-parser/parser@0.16.1:
+ resolution: {integrity: sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==}
dependencies:
antlr4ts: 0.5.0-alpha.4
dev: true
@@ -157,9 +162,14 @@ packages:
engines: {node: '>=14'}
dev: true
- /cosmiconfig@8.2.0:
- resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==}
+ /cosmiconfig@8.3.3:
+ resolution: {integrity: sha512-/VY+0IvFoE47hwgKHu8feeBFIb1Z1mcJFiLrNwaJpLoLa9qwLVquMGMr2OUwQmhpJDtsSQSasg/TMv1imec9xA==}
engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
dependencies:
import-fresh: 3.3.0
js-yaml: 4.1.0
@@ -318,7 +328,7 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
dependencies:
- '@babel/code-frame': 7.22.5
+ '@babel/code-frame': 7.22.13
error-ex: 1.3.2
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
@@ -334,15 +344,15 @@ packages:
engines: {node: '>=4'}
dev: true
- /prettier-plugin-solidity@1.1.3(prettier@2.8.8):
+ /prettier-plugin-solidity@1.1.3(prettier@3.0.3):
resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==}
engines: {node: '>=12'}
peerDependencies:
prettier: '>=2.3.0 || >=3.0.0-alpha.0'
dependencies:
- '@solidity-parser/parser': 0.16.0
- prettier: 2.8.8
- semver: 7.5.2
+ '@solidity-parser/parser': 0.16.1
+ prettier: 3.0.3
+ semver: 7.5.4
solidity-comments-extractor: 0.0.7
dev: true
@@ -350,6 +360,14 @@ packages:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /prettier@3.0.3:
+ resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==}
+ engines: {node: '>=14'}
+ hasBin: true
dev: true
/punycode@2.3.0:
@@ -367,13 +385,8 @@ packages:
engines: {node: '>=4'}
dev: true
- /semver@6.3.0:
- resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
- hasBin: true
- dev: true
-
- /semver@7.5.2:
- resolution: {integrity: sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==}
+ /semver@7.5.4:
+ resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'}
hasBin: true
dependencies:
@@ -389,29 +402,31 @@ packages:
is-fullwidth-code-point: 3.0.0
dev: true
- /solhint@3.4.1:
- resolution: {integrity: sha512-pzZn2RlZhws1XwvLPVSsxfHrwsteFf5eySOhpAytzXwKQYbTCJV6z8EevYDiSVKMpWrvbKpEtJ055CuEmzp4Xg==}
+ /solhint@3.6.2:
+ resolution: {integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==}
hasBin: true
dependencies:
- '@solidity-parser/parser': 0.16.0
+ '@solidity-parser/parser': 0.16.1
ajv: 6.12.6
antlr4: 4.13.0
ast-parents: 0.0.1
chalk: 4.1.2
commander: 10.0.1
- cosmiconfig: 8.2.0
+ cosmiconfig: 8.3.3
fast-diff: 1.3.0
glob: 8.1.0
ignore: 5.2.4
js-yaml: 4.1.0
lodash: 4.17.21
pluralize: 8.0.0
- semver: 6.3.0
+ semver: 7.5.4
strip-ansi: 6.0.1
table: 6.8.1
text-table: 0.2.0
optionalDependencies:
prettier: 2.8.8
+ transitivePeerDependencies:
+ - typescript
dev: true
/solidity-comments-extractor@0.0.7:
@@ -475,4 +490,4 @@ packages:
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
- dev: true
\ No newline at end of file
+ dev: true
diff --git a/src/Keep.sol b/src/Keep.sol
index d1114677..303dc868 100644
--- a/src/Keep.sol
+++ b/src/Keep.sol
@@ -1,11 +1,12 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
import {ERC1155TokenReceiver, KeepToken} from "./KeepToken.sol";
import {Multicallable} from "./utils/Multicallable.sol";
/// @title Keep
-/// @notice Tokenized multisig wallet.
+/// @notice Multitoken signature auth system.
+/// @dev Optimized for multisig operations.
/// @author z0r0z.eth
/// @custom:coauthor @ControlCplusControlV
/// @custom:coauthor boredretard.eth
@@ -24,7 +25,8 @@ import {Multicallable} from "./utils/Multicallable.sol";
enum Operation {
call,
delegatecall,
- create
+ create,
+ create2
}
struct Call {
@@ -41,12 +43,26 @@ struct Signature {
bytes32 s;
}
+struct UserOperation {
+ address sender;
+ uint256 nonce;
+ bytes initCode;
+ bytes callData;
+ uint256 callGasLimit;
+ uint256 verificationGasLimit;
+ uint256 preVerificationGas;
+ uint256 maxFeePerGas;
+ uint256 maxPriorityFeePerGas;
+ bytes paymasterAndData;
+ bytes signature;
+}
+
contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
- /// @dev Emitted when Keep executes call.
+ /// @dev Emitted when Keep executes EXEC_KEY op.
event Executed(
uint256 indexed nonce,
Operation op,
@@ -55,14 +71,17 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
bytes data
);
- /// @dev Emitted when Keep relays call.
+ /// @dev Emitted when Keep relays op.
event Relayed(Call call);
- /// @dev Emitted when Keep relays calls.
+ /// @dev Emitted when Keep relays ops.
event Multirelayed(Call[] calls);
- /// @dev Emitted when quorum threshold is updated.
- event QuorumSet(uint256 threshold);
+ /// @dev Emitted when `quorum` threshold updates.
+ event QuorumSet(uint256 id, uint256 quorum);
+
+ /// @dev Emitted when signature revoked.
+ event SignatureRevoked(address indexed user, bytes32 hash);
/// -----------------------------------------------------------------------
/// Custom Errors
@@ -71,45 +90,47 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
/// @dev Throws if `initialize()` is called more than once.
error AlreadyInit();
- /// @dev Throws if quorum exceeds `totalSupply(SIGN_KEY)`.
- error QuorumOverSupply();
-
- /// @dev Throws if quorum with `threshold = 0` is set.
+ /// @dev Throws if `quorum` exceeds `totalSupply()` or is zero.
error InvalidThreshold();
- /// @dev Throws if `execute()` doesn't complete operation.
- error ExecuteFailed();
-
/// -----------------------------------------------------------------------
/// Keep Storage/Logic
/// -----------------------------------------------------------------------
/// @dev Core ID key permission.
- uint256 internal immutable CORE_KEY = uint32(type(KeepToken).interfaceId);
+ uint256 internal constant CORE_KEY = uint32(type(KeepToken).interfaceId);
- /// @dev Default metadata fetcher for `uri()`.
- Keep internal immutable uriFetcher;
+ /// @dev External validation for ERC1155 `uri()` & ERC4337 permissioning.
+ Keep internal immutable validator;
/// @dev Record of states verifying `execute()`.
- uint120 public nonce;
-
- /// @dev SIGN_KEY threshold to `execute()`.
- uint120 public quorum;
+ uint256 public nonce;
/// @dev Internal ID metadata mapping.
- mapping(uint256 => string) internal _uris;
+ mapping(uint256 id => string) internal _uri;
+
+ /// @dev Internal ID signature threshold mapping.
+ mapping(uint256 id => uint256) public quorum;
+
+ /// @dev Internal signature hash revocation status.
+ mapping(address user => mapping(bytes32 hash => bool)) internal revoked;
+
+ /// @dev ERC4337 entrypoint.
+ function entryPoint() public view virtual returns (address) {
+ return validator.entryPoint();
+ }
/// @dev ID metadata fetcher.
/// @param id ID to fetch from.
/// @return tokenURI Metadata.
function uri(uint256 id) public view virtual returns (string memory) {
- string memory tokenURI = _uris[id];
+ string memory tokenURI = _uri[id];
if (bytes(tokenURI).length > 0) return tokenURI;
- else return uriFetcher.uri(id);
+ else return validator.uri(id);
}
- /// @dev Access control check for ID key balance holders.
+ /// @dev Access control check for ID key holders & other auth.
/// Initializes with `address(this)` having implicit permission
/// without writing to storage by checking `totalSupply()` is zero.
/// Otherwise, this permission can be set to additional accounts,
@@ -117,6 +138,7 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
function _authorized() internal view virtual returns (bool) {
if (
(totalSupply[CORE_KEY] == 0 && msg.sender == address(this)) ||
+ msg.sender == validator.entryPoint() ||
balanceOf[msg.sender][CORE_KEY] != 0 ||
balanceOf[msg.sender][uint32(msg.sig)] != 0
) return true;
@@ -129,19 +151,23 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
/// @dev ERC165 interface detection.
/// @param interfaceId ID to check.
- /// @return Fetch detection success.
+ /// @return supported Status field.
function supportsInterface(
bytes4 interfaceId
- ) public view virtual override returns (bool) {
- return
- // ERC165 Interface ID for ERC721TokenReceiver.
- interfaceId == this.onERC721Received.selector ||
- // ERC165 Interface ID for ERC1155TokenReceiver.
- interfaceId == type(ERC1155TokenReceiver).interfaceId ||
- // ERC165 interface ID for ERC1155MetadataURI.
- interfaceId == this.uri.selector ||
- // ERC165 Interface IDs for ERC1155.
- super.supportsInterface(interfaceId);
+ ) public view virtual returns (bool supported) {
+ assembly ("memory-safe") {
+ let s := shr(224, interfaceId)
+ // ERC165: 0x01ffc9a7, ERC1155: 0xd9b67a26, ERC1155MetadataURI: 0x0e89341c,
+ // ERC721TokenReceiver: 0x150b7a02, ERC1155TokenReceiver: 0x4e2312e0,
+ // ERC1271: 0x1626ba7e, ERC6066: 0x12edb34f
+ supported := or(
+ or(or(eq(s, 0x01ffc9a7), eq(s, 0xd9b67a26)), eq(s, 0x0e89341c)),
+ or(
+ or(eq(s, 0x150b7a02), eq(s, 0x4e2312e0)),
+ or(eq(s, 0x1626ba7e), eq(s, 0x12edb34f))
+ )
+ )
+ }
}
/// -----------------------------------------------------------------------
@@ -162,12 +188,13 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
/// -----------------------------------------------------------------------
/// @notice Create Keep template.
- /// @param _uriFetcher Metadata default.
- constructor(Keep _uriFetcher) payable {
- uriFetcher = _uriFetcher;
+ /// @param _validator ERC1155/4337 helper.
+ constructor(Keep _validator) payable {
+ // Set as immutable.
+ validator = _validator;
// Deploy as singleton.
- quorum = 1;
+ quorum[EXEC_KEY] = 999;
}
/// @notice Initialize Keep configuration.
@@ -179,26 +206,26 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
address[] calldata signers,
uint256 threshold
) public payable virtual {
- if (quorum != 0) revert AlreadyInit();
+ if (quorum[EXEC_KEY] != 0) revert AlreadyInit();
if (threshold == 0) revert InvalidThreshold();
- if (threshold > signers.length) revert QuorumOverSupply();
+ if (threshold > signers.length) revert InvalidThreshold();
if (calls.length != 0) {
for (uint256 i; i < calls.length; ) {
+ // An array can't have a total length
+ // larger than the max uint256 value.
+ unchecked {
+ ++i;
+ }
+
_execute(
calls[i].op,
calls[i].to,
calls[i].value,
calls[i].data
);
-
- // An array can't have a total length
- // larger than the max uint256 value.
- unchecked {
- ++i;
- }
}
}
@@ -210,35 +237,40 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
signer = signers[i];
// Prevent zero and duplicate signers.
- if (previous >= signer) revert InvalidSig();
+ if (previous >= signer) revert InvalidArray();
previous = signer;
- emit TransferSingle(tx.origin, address(0), signer, SIGN_KEY, 1);
+ emit TransferSingle(
+ tx.origin,
+ address(0),
+ signer,
+ EXEC_KEY,
+ balanceOf[signer][EXEC_KEY] = 1
+ );
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
- ++balanceOf[signer][SIGN_KEY];
++supply;
++i;
}
}
- totalSupply[SIGN_KEY] = supply;
- quorum = uint120(threshold);
+ totalSupply[EXEC_KEY] = supply;
+ quorum[EXEC_KEY] = threshold;
}
/// -----------------------------------------------------------------------
/// Execution Logic
/// -----------------------------------------------------------------------
- /// @notice Execute operation from Keep with signatures.
+ /// @notice Execute Keep-structured operation.
/// @param op Enum operation to execute.
/// @param to Address to send operation to.
/// @param value Amount of ETH to send in operation.
/// @param data Payload to send in operation.
- /// @param sigs Array of Keep signatures in ascending order by addresses.
+ /// @param sigs Ascending array of Keep signatures.
function execute(
Operation op,
address to,
@@ -246,15 +278,14 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
bytes calldata data,
Signature[] calldata sigs
) public payable virtual {
- uint120 txNonce;
-
+ uint256 txNonce;
// Unchecked because the only math done is incrementing
- // Keep nonce which cannot realistically overflow.
+ // Keep `nonce` which cannot realistically overflow.
unchecked {
emit Executed(txNonce = nonce++, op, to, value, data);
}
- // Begin signature validation with hashed inputs.
+ // Derive operation hash.
bytes32 hash = keccak256(
abi.encodePacked(
"\x19\x01",
@@ -262,7 +293,7 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
keccak256(
abi.encode(
keccak256(
- "Execute(uint8 op,address to,uint256 value,bytes data,uint120 nonce)"
+ "Execute(uint8 op,address to,uint256 value,bytes data,uint256 nonce)"
),
op,
to,
@@ -274,31 +305,31 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
)
);
- // Start zero in loop to ensure ascending addresses.
+ // Memo zero `user` for ascending order.
address previous;
-
- // Validation is length of quorum threshold.
- uint256 threshold = quorum;
-
- // Store outside loop for gas optimization.
+ // Memo `quorum` threshold for loop length.
+ uint256 threshold = quorum[EXEC_KEY];
+ // Memo signature outside loop for optimization.
Signature calldata sig;
- for (uint256 i; i < threshold; ) {
- // Load signature items.
+ // Check enough valid `sigs` to pass `quorum`.
+ uint256 i;
+ for (i; i < threshold; ) {
+ // Load `user` details.
sig = sigs[i];
address user = sig.user;
- // Check SIGN_KEY balance.
+ // Check EXEC_KEY balance.
// This also confirms non-zero `user`.
- if (balanceOf[user][SIGN_KEY] == 0) revert InvalidSig();
+ if (balanceOf[user][EXEC_KEY] == 0) revert Unauthorized();
- // Check signature recovery.
- _recoverSig(hash, user, sig.v, sig.r, sig.s);
+ // Check `user` `sig` recovery for `hash`.
+ _checkSig(user, hash, sig.v, sig.r, sig.s);
- // Check against duplicates.
- if (previous >= user) revert InvalidSig();
+ // Check for `user` duplicates.
+ if (previous >= user) revert InvalidArray();
- // Memo signature for next iteration until quorum.
+ // Memo `user` for next loop until `quorum`.
previous = user;
// An array can't have a total length
@@ -311,29 +342,30 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
_execute(op, to, value, data);
}
- /// @notice Relay operation from Keep via `execute()` or as ID key holder.
- /// @param call Keep operation as struct of `op, to, value, data`.
+ /// @notice Relay op from Keep `execute()` or as `authorized()`.
+ /// @param call Keep op as struct of `op, to, value, data`.
function relay(Call calldata call) public payable virtual {
_authorized();
- _execute(call.op, call.to, call.value, call.data);
-
emit Relayed(call);
+
+ _execute(call.op, call.to, call.value, call.data);
}
- /// @notice Relay operations from Keep via `execute()` or as ID key holder.
- /// @param calls Keep operations as struct arrays of `op, to, value, data`.
+ /// @notice Relay ops from Keep `execute()` or as `authorized()`.
+ /// @param calls Keep ops as struct arrays of `op, to, value, data`.
function multirelay(Call[] calldata calls) public payable virtual {
_authorized();
- for (uint256 i; i < calls.length; ) {
- _execute(calls[i].op, calls[i].to, calls[i].value, calls[i].data);
-
+ uint256 i;
+ for (i; i < calls.length; ) {
// An array can't have a total length
// larger than the max uint256 value.
unchecked {
++i;
}
+
+ _execute(calls[i].op, calls[i].to, calls[i].value, calls[i].data);
}
emit Multirelayed(calls);
@@ -346,42 +378,330 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
bytes memory data
) internal virtual {
if (op == Operation.call) {
- bool success;
-
assembly {
- success := call(
+ let success := call(
gas(),
to,
value,
add(data, 0x20),
mload(data),
- 0,
- 0
+ gas(),
+ 0x00
)
+ returndatacopy(0x00, 0x00, returndatasize())
+ if iszero(success) {
+ revert(0x00, returndatasize())
+ }
+ return(0x00, returndatasize())
}
-
- if (!success) revert ExecuteFailed();
} else if (op == Operation.delegatecall) {
- bool success;
-
assembly {
- success := delegatecall(
+ let success := delegatecall(
gas(),
to,
add(data, 0x20),
mload(data),
- 0,
- 0
+ gas(),
+ 0x00
)
+ returndatacopy(0x00, 0x00, returndatasize())
+ if iszero(success) {
+ revert(0x00, returndatasize())
+ }
+ return(0x00, returndatasize())
+ }
+ } else if (op == Operation.create) {
+ assembly {
+ let created := create(value, add(data, 0x20), mload(data))
+ if iszero(created) {
+ revert(0x00, 0x00)
+ }
+ mstore(0x00, created)
+ return(0x00, 0x20)
}
-
- if (!success) revert ExecuteFailed();
} else {
assembly {
- to := create(value, add(data, 0x20), mload(data))
+ let created := create2(
+ value,
+ add(add(data, 0x20), 0x20),
+ sub(mload(data), 0x20),
+ mload(add(data, 0x20))
+ )
+ if iszero(created) {
+ revert(0x00, 0x00)
+ }
+ mstore(0x00, created)
+ return(0x00, 0x20)
}
+ }
+ }
+
+ /// -----------------------------------------------------------------------
+ /// Revocation Logic
+ /// -----------------------------------------------------------------------
+
+ /// @notice Signature revocation.
+ /// @param hash Signed data hash.
+ /// @param sig Keep-structured sig.
+ function revokeSignature(
+ bytes32 hash,
+ Signature calldata sig
+ ) public payable virtual {
+ _checkSig(sig.user, hash, sig.v, sig.r, sig.s);
+
+ revoked[sig.user][hash] = true;
- if (to == address(0)) revert ExecuteFailed();
+ emit SignatureRevoked(sig.user, hash);
+ }
+
+ /// -----------------------------------------------------------------------
+ /// ERC1271 Logic
+ /// -----------------------------------------------------------------------
+
+ function isValidSignature(
+ bytes32 hash,
+ bytes calldata sig
+ ) public view virtual returns (bytes4) {
+ // Check `EXEC_KEY` as this denotes Keep ownership.
+ if (_validate(hash, sig, EXEC_KEY) == 0) return 0x1626ba7e;
+ else return 0xffffffff;
+ }
+
+ /// -----------------------------------------------------------------------
+ /// ERC6066 Logic
+ /// -----------------------------------------------------------------------
+
+ /// @param id ID of signing NFT.
+ /// @param hash Signed data hash.
+ /// @param sig Signature payload.
+ function isValidSignature(
+ uint256 id,
+ bytes32 hash,
+ bytes calldata sig
+ ) public view virtual returns (bytes4) {
+ if (_validate(hash, sig, id) == 0) return 0x12edb34f;
+ else return 0xffffffff;
+ }
+
+ /// -----------------------------------------------------------------------
+ /// ERC4337 Logic
+ /// -----------------------------------------------------------------------
+
+ function validateUserOp(
+ UserOperation calldata userOp,
+ bytes32 userOpHash,
+ uint256 missingAccountFunds
+ ) public payable virtual returns (uint256 validationData) {
+ // Check request comes from `entrypoint()`.
+ if (msg.sender != validator.entryPoint()) revert Unauthorized();
+
+ // Build ERC191-structured `userOpHash`.
+ assembly ("memory-safe") {
+ mstore(0x20, userOpHash) // Store into scratch space for keccak256.
+ mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\n32") // 28 bytes.
+ userOpHash := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
+ }
+
+ // Shift `userOp.nonce` to get ID key.
+ uint256 key = (userOp.nonce >> 64);
+ uint256 id = key >> 191;
+
+ // Check signature `quorum` is met and validate auth.
+ validationData = _validate(userOpHash, userOp.signature, id);
+
+ // Extract 'validator flag' in 192rd bit. If set, `validator` check `userOp` with `id`.
+ if ((key & (1 << 191)) != 0)
+ validationData = validator.validateUserOp(userOp, userOpHash, id);
+
+ // Send any missing funds to `entrypoint()` (msg.sender).
+ if (missingAccountFunds != 0)
+ assembly {
+ pop(
+ call(
+ gas(),
+ caller(),
+ missingAccountFunds,
+ gas(),
+ 0x00,
+ gas(),
+ 0x00
+ )
+ )
+ }
+ }
+
+ function _validate(
+ bytes32 hash,
+ bytes calldata sig,
+ uint256 id
+ ) internal view virtual returns (uint256 validationData) {
+ address user;
+ uint256 threshold = quorum[id];
+
+ // Return invalid if inactive.
+ if (threshold == 0) return 1;
+
+ // Return early for single `sig`.
+ if (threshold == 1) {
+ (user, validationData) = _validateSig(hash, sig);
+
+ if (validationData == 1) return 1;
+
+ if (revoked[user][hash]) return 1;
+
+ return balanceOf[user][id] != 0 ? 0 : 1;
+ }
+
+ // Memo split `sig` into `sigs`.
+ bytes[] memory sigs = _splitSigs(sig);
+ // Memo zero `user` for ascending order.
+ address previous;
+
+ // Check enough valid `sigs` to pass `quorum`.
+ uint256 i;
+ for (i; i < threshold; ) {
+ (user, validationData) = _validateSig(hash, sigs[i]);
+
+ if (validationData == 1) return 1;
+
+ if (revoked[user][hash]) return 1;
+
+ // Check for `user` duplicates.
+ if (previous >= user) return 1;
+
+ // Memo `user` for next loop until `quorum`.
+ previous = user;
+
+ // If not keyholding `user`, `SIG_VALIDATION_FAILED`.
+ if (balanceOf[user][id] == 0) return 1;
+
+ // An array can't have a total length
+ // larger than the max uint256 value.
+ unchecked {
+ ++i;
+ }
+ }
+
+ return 0;
+ }
+
+ function _validateSig(
+ bytes32 hash,
+ bytes memory sig
+ ) internal view virtual returns (address user, uint256 validationData) {
+ bytes32 r;
+ bytes32 s;
+ uint8 v;
+
+ assembly ("memory-safe") {
+ r := mload(add(sig, 0x20))
+ s := mload(add(sig, 0x40))
+ v := byte(0x00, mload(add(sig, 0x60)))
+ }
+
+ // Check `v` to branch ecrecover or ERC1271.
+ if (v != 0) {
+ assembly ("memory-safe") {
+ let m := mload(0x40) // Cache the free memory pointer.
+ mstore(0x00, hash)
+ mstore(0x20, and(v, 0xff))
+ mstore(0x40, r)
+ mstore(0x60, s)
+ user := 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.
+ )
+ )
+ mstore(0x60, 0x00) // Restore the zero slot.
+ mstore(0x40, m) // Restore the free memory pointer.
+ }
+ } else {
+ // Derive `user` from `r`.
+ user = address(uint160(uint256(r)));
+
+ assembly ("memory-safe") {
+ 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`.
+
+ if iszero(
+ 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.
+ user, // The `user` address.
+ m, // Offset of calldata in memory.
+ 0xa5, // Length of calldata in memory.
+ d, // Offset of returndata.
+ 0x20 // Length of returndata to write.
+ )
+ )
+ ) {
+ validationData := 1
+ }
+ }
+ }
+ }
+
+ function _splitSigs(
+ bytes memory sig
+ ) internal pure virtual returns (bytes[] memory sigs) {
+ // todo: check memsafe
+ assembly {
+ // Check if `sig.length % 65 == 0`.
+ if iszero(eq(mod(mload(sig), 65), 0x00)) {
+ // If not, revert with InvalidSignature.
+ mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
+ revert(0x1c, 0x04)
+ }
+
+ // Calculate count in assembly.
+ let count := div(mload(sig), 65)
+
+ // Allocate memory for split array.
+ sigs := mload(0x40) // Current free memory pointer (using mload(0x40) instead of msize).
+ mstore(sigs, count) // Store length of the sigs array.
+
+ let sigPtr := add(sig, 0x20) // Pointer to start of `sig` data.
+ let splitDataPtr := add(sigs, 0x20) // Pointer to data section of `sigs` array.
+
+ for {
+ let i := 0
+ } lt(i, count) {
+ i := add(i, 1)
+ } {
+ let m := mload(0x40) // Cache free memory pointer.
+
+ // Store pointer to new memory in `sigs` array's data section.
+ mstore(splitDataPtr, m)
+
+ // Store length and data for the `sig`.
+ mstore(m, 65)
+ mstore(add(m, 0x20), mload(sigPtr))
+ mstore(add(m, 0x40), mload(add(sigPtr, 0x20)))
+ mstore8(add(m, 0x60), mload(add(sigPtr, 0x40)))
+
+ // Move the pointers for the next iteration.
+ mstore(0x40, add(m, 0x61)) // Update free memory pointer.
+ sigPtr := add(sigPtr, 65) // Move to next `sig`.
+ splitDataPtr := add(splitDataPtr, 0x20) // Move to next position.
+ }
}
}
@@ -420,26 +740,28 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
_burn(from, id, amount);
- if (id == SIGN_KEY)
- if (quorum > totalSupply[SIGN_KEY]) revert QuorumOverSupply();
+ if (id == EXEC_KEY)
+ if (quorum[EXEC_KEY] > totalSupply[EXEC_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 quorum threshold.
+ /// @param id ID key to set quorum threshold for.
+ /// @param threshold Signature threshold for quorum.
+ function setQuorum(uint256 id, uint256 threshold) public payable virtual {
_authorized();
if (threshold == 0) revert InvalidThreshold();
- if (threshold > totalSupply[SIGN_KEY]) revert QuorumOverSupply();
+ if (threshold > totalSupply[id]) revert InvalidThreshold();
- quorum = uint120(threshold);
+ quorum[id] = threshold;
- emit QuorumSet(threshold);
+ emit QuorumSet(id, threshold);
}
/// -----------------------------------------------------------------------
@@ -489,7 +811,7 @@ contract Keep is ERC1155TokenReceiver, KeepToken, Multicallable {
) public payable virtual {
_authorized();
- _uris[id] = tokenURI;
+ _uri[id] = tokenURI;
emit URI(tokenURI, id);
}
diff --git a/src/KeepFactory.sol b/src/KeepFactory.sol
index 46a51f9f..1b056826 100644
--- a/src/KeepFactory.sol
+++ b/src/KeepFactory.sol
@@ -1,63 +1,201 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
import {Multicallable, Call, Keep} from "./Keep.sol";
-import {LibClone} from "./utils/LibClone.sol";
+import {Ownable, Validator} from "./extensions/validate/Validator.sol";
/// @notice Keep Factory.
-contract KeepFactory is Multicallable {
+contract KeepFactory is Multicallable, Ownable(tx.origin) {
/// -----------------------------------------------------------------------
- /// Library Usage
+ /// Events
/// -----------------------------------------------------------------------
- using LibClone for address;
+ event Deployed(Keep indexed keep, uint256 quorum);
/// -----------------------------------------------------------------------
- /// Events
+ /// Custom Errors
/// -----------------------------------------------------------------------
- event Deployed(address indexed keep, address[] signers, uint256 threshold);
+ error DeploymentFailed();
/// -----------------------------------------------------------------------
/// Immutables
/// -----------------------------------------------------------------------
- address internal immutable keepTemplate;
+ Keep internal immutable keepTemplate;
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
- constructor(address _keepTemplate) payable {
- keepTemplate = _keepTemplate;
+ constructor() payable {
+ keepTemplate = new Keep(Keep(address(new Validator())));
}
/// -----------------------------------------------------------------------
/// Deployment Logic
/// -----------------------------------------------------------------------
- function determineKeep(bytes32 name) public view virtual returns (address) {
- return
- keepTemplate.predictDeterministicAddress(
- abi.encodePacked(name),
- name,
- address(this)
- );
- }
-
function deployKeep(
bytes32 name, // create2 salt.
Call[] calldata calls,
address[] calldata signers,
- uint256 threshold
- ) public payable virtual {
- address keep = keepTemplate.cloneDeterministic(
- abi.encodePacked(name),
- name
+ uint256 quorum
+ ) public payable virtual returns (Keep keep) {
+ bytes memory data = abi.encodePacked(name);
+ address implementation = address(keepTemplate);
+
+ 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)
+
+ // Write the bytecode before the data.
+ mstore(data, 0x5af43d3d93803e606057fd5bf3)
+ // Write the address of the implementation.
+ mstore(sub(data, 0x0d), 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))
+
+ // Create the instance.
+ keep := create2(
+ callvalue(),
+ sub(data, 0x4c),
+ add(extraLength, 0x6c),
+ name
+ )
+
+ // If `keep` is zero, revert.
+ if iszero(keep) {
+ // Store the function selector of `DeploymentFailed()`.
+ mstore(0x00, 0x30116425)
+ // Revert with (offset, size).
+ 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)
+ }
+
+ keep.initialize(calls, signers, quorum);
+
+ emit Deployed(keep, quorum);
+ }
+
+ function determineKeep(
+ bytes32 name
+ ) public view virtual returns (address keep, bool deployed) {
+ bytes memory data = abi.encodePacked(name);
+ address implementation = address(keepTemplate);
+
+ 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)
+
+ // Write the bytecode before the data.
+ mstore(data, 0x5af43d3d93803e606057fd5bf3)
+ // Write the address of the implementation.
+ mstore(sub(data, 0x0d), 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))
+
+ // Compute and store the bytecode hash.
+ mstore(0x35, keccak256(sub(data, 0x4c), add(extraLength, 0x6c)))
+ mstore8(0x00, 0xff) // Write the prefix.
+ mstore(0x01, shl(96, address()))
+ mstore(0x15, name)
+ keep := keccak256(0x00, 0x55)
+ deployed := extcodesize(keep)
+ // Restore the part of the free memory pointer that has been overwritten.
+ mstore(0x35, 0x00)
+
+ // 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)
+ }
+ }
+
+ /// -----------------------------------------------------------------------
+ /// ERC4337 Staking Logic
+ /// -----------------------------------------------------------------------
+
+ function addStake(uint32 unstakeDelaySec) public payable virtual onlyOwner {
+ KeepFactory(keepTemplate.entryPoint()).addStake{value: msg.value}(
+ unstakeDelaySec
);
+ }
- Keep(keep).initialize{value: msg.value}(calls, signers, threshold);
+ function unlockStake() public payable virtual onlyOwner {
+ KeepFactory(keepTemplate.entryPoint()).unlockStake();
+ }
- emit Deployed(keep, signers, threshold);
+ function withdrawStake(
+ address withdrawAddress
+ ) public payable virtual onlyOwner {
+ KeepFactory(keepTemplate.entryPoint()).withdrawStake(withdrawAddress);
}
}
diff --git a/src/KeepToken.sol b/src/KeepToken.sol
index 6be5e1d8..241f140a 100644
--- a/src/KeepToken.sol
+++ b/src/KeepToken.sol
@@ -1,33 +1,7 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
-/// @notice ERC1155 interface to receive tokens.
-/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/tokens/ERC1155/ERC1155.sol)
-abstract contract ERC1155TokenReceiver {
- function onERC1155Received(
- address,
- address,
- uint256,
- uint256,
- bytes calldata
- ) public payable virtual returns (bytes4) {
- return this.onERC1155Received.selector;
- }
-
- function onERC1155BatchReceived(
- address,
- address,
- uint256[] calldata,
- uint256[] calldata,
- bytes calldata
- ) public payable virtual returns (bytes4) {
- return this.onERC1155BatchReceived.selector;
- }
-}
-
-/// @notice Modern, minimalist, and gas-optimized ERC1155 implementation with Compound-style voting and flexible permissioning scheme.
-/// @author Modified from ERC1155V (https://github.com/kalidao/ERC1155V/blob/main/src/ERC1155V.sol)
-/// @author Modified from Compound (https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol)
+/// @notice ERC1155 token with checkpointing, delegation and transfer controls.
abstract contract KeepToken {
/// -----------------------------------------------------------------------
/// Events
@@ -90,24 +64,15 @@ abstract contract KeepToken {
/// Custom Errors
/// -----------------------------------------------------------------------
- error InvalidSig();
-
- error LengthMismatch();
-
+ error InvalidSignature();
+ error InvalidArray();
error Unauthorized();
-
error NonTransferable();
-
error NotPermitted();
-
error UnsafeRecipient();
-
error InvalidRecipient();
-
error ExpiredSig();
-
error Undetermined();
-
error Overflow();
/// -----------------------------------------------------------------------
@@ -122,117 +87,112 @@ abstract contract KeepToken {
/// EIP-712 Storage/Logic
/// -----------------------------------------------------------------------
- bytes32 internal constant MALLEABILITY_THRESHOLD =
- 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;
-
mapping(address => uint256) public nonces;
- function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
- return
- keccak256(
- abi.encode(
- // `keccak256(
- // "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
- // )`
- 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
- // `keccak256(bytes("Keep"))`
- 0x21d66785fec14e4da3d76f3866cf99a28f4da49ec8782c3cab7cf79c1b6fa66b,
- // `keccak256("1")`
- 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6,
- block.chainid,
- address(this)
- )
- );
+ function DOMAIN_SEPARATOR()
+ public
+ view
+ virtual
+ returns (bytes32 separator)
+ {
+ /// @solidity memory-safe-assembly
+ assembly {
+ let m := mload(0x40) // Load the free memory pointer.
+ mstore(
+ m,
+ 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f
+ )
+ mstore(
+ add(m, 0x20),
+ 0x21d66785fec14e4da3d76f3866cf99a28f4da49ec8782c3cab7cf79c1b6fa66b
+ )
+ mstore(
+ add(m, 0x40),
+ 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6
+ )
+ mstore(add(m, 0x60), chainid())
+ mstore(add(m, 0x80), address())
+ separator := keccak256(m, 0xa0)
+ }
}
- function _recoverSig(
+ function _checkSig(
+ address user,
bytes32 hash,
- address signer,
uint8 v,
bytes32 r,
bytes32 s
) internal view virtual {
- bool isValid;
-
/// @solidity memory-safe-assembly
assembly {
- // Clean the upper 96 bits of `signer` in case they are dirty.
+ // Clean the upper 96 bits of `user` in case they are dirty.
for {
- signer := shr(96, shl(96, signer))
- } signer {
+ user := shr(96, shl(96, user))
+ } user {
} {
- // Load the free memory pointer.
- // Simply using the free memory usually costs less if many slots are needed.
let m := mload(0x40)
-
- // Clean the excess bits of `v` in case they are dirty.
- v := and(v, 0xff)
- // If `s` in lower half order, such that the signature is not malleable.
- if iszero(gt(s, MALLEABILITY_THRESHOLD)) {
- mstore(m, hash)
- mstore(add(m, 0x20), v)
- mstore(add(m, 0x40), r)
- mstore(add(m, 0x60), s)
- pop(
- staticcall(
- gas(), // Amount of gas left for the transaction.
- 0x01, // Address of `ecrecover`.
- m, // Start of input.
- 0x80, // Size of input.
- m, // Start of output.
- 0x20 // Size of output.
- )
- )
- // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
- if mul(eq(mload(m), signer), returndatasize()) {
- isValid := 1
- break
- }
+ 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(user, mload(t)))) {
+ mstore(0x60, 0x00) // Restore the zero slot.
+ mstore(0x40, m) // Restore the free memory pointer.
+ break
}
- // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
let f := shl(224, 0x1626ba7e)
- // Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
mstore(add(m, 0x04), hash)
- mstore(add(m, 0x24), 0x40) // The offset of the `signature` in the calldata.
- mstore(add(m, 0x44), 65) // Store the length of the signature.
- mstore(add(m, 0x64), r) // Store `r` of the signature.
- mstore(add(m, 0x84), s) // Store `s` of the signature.
- mstore8(add(m, 0xa4), v) // Store `v` of the signature.
-
- isValid := and(
+ 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`.
+
+ if iszero(
and(
// Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
- eq(mload(0x00), f),
- // 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.
- signer, // The `signer` address.
- m, // Offset of calldata in memory.
- 0xa5, // Length of calldata in memory.
- 0x00, // Offset of returndata.
- 0x20 // Length of returndata to write.
+ 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.
+ user, // The `user` address.
+ m, // Offset of calldata in memory.
+ 0xa5, // Length of calldata in memory.
+ d, // Offset of returndata.
+ 0x20 // Length of returndata to write.
+ )
)
- )
+ ) {
+ mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
+ revert(0x1c, 0x04)
+ }
+ mstore(0x60, 0) // Restore the zero slot.
+ mstore(0x40, m) // Restore the free memory pointer.
break
}
}
-
- if (!isValid) revert InvalidSig();
}
/// -----------------------------------------------------------------------
/// ID Storage
/// -----------------------------------------------------------------------
- uint256 internal constant SIGN_KEY = uint32(0x6c4b5546); // `execute()`
+ uint256 internal constant EXEC_KEY = uint32(0x6c4b5546); // `execute()`.
mapping(uint256 => uint256) public totalSupply;
@@ -263,33 +223,23 @@ abstract contract KeepToken {
/// -----------------------------------------------------------------------
function name() public pure virtual returns (string memory) {
- uint256 placeholder;
-
- assembly {
- placeholder := sub(
- calldatasize(),
- add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
+ uint256 n;
+ assembly ("memory-safe") {
+ n := calldataload(
+ add(
+ sub(
+ calldatasize(),
+ add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
+ ),
+ 2
+ )
)
-
- placeholder := calldataload(add(placeholder, 2))
}
- return string(abi.encodePacked(placeholder));
+ return string(abi.encodePacked(n));
}
- /// -----------------------------------------------------------------------
- /// ERC165 Logic
- /// -----------------------------------------------------------------------
-
- function supportsInterface(
- bytes4 interfaceId
- ) public view virtual returns (bool) {
- return
- // ERC165 interface ID for ERC165.
- interfaceId == this.supportsInterface.selector ||
- // ERC165 interface ID for ERC1155.
- interfaceId == 0xd9b67a26;
- }
+ string public constant symbol = "KEEP";
/// -----------------------------------------------------------------------
/// ERC1155 Logic
@@ -299,11 +249,12 @@ abstract contract KeepToken {
address[] calldata owners,
uint256[] calldata ids
) public view virtual returns (uint256[] memory balances) {
- if (owners.length != ids.length) revert LengthMismatch();
+ if (owners.length != ids.length) revert InvalidArray();
balances = new uint256[](owners.length);
- for (uint256 i; i < owners.length; ) {
+ uint256 i;
+ for (i; i < owners.length; ) {
balances[i] = balanceOf[owners[i]][ids[i]];
// Unchecked because the only math done is incrementing
@@ -339,9 +290,9 @@ abstract contract KeepToken {
if (!userPermissioned[to][id] || !userPermissioned[from][id])
revert NotPermitted();
- // If not transferring SIGN_KEY, update delegation balance.
- // Otherwise, prevent transfer to SIGN_KEY holder.
- if (id != SIGN_KEY)
+ // If not transferring EXEC_KEY, update delegation balance.
+ // Otherwise, prevent transfer to EXEC_KEY holder.
+ if (id != EXEC_KEY)
_moveDelegates(delegates(from, id), delegates(to, id), id, amount);
else if (balanceOf[to][id] != 0) revert Overflow();
@@ -375,7 +326,7 @@ abstract contract KeepToken {
uint256[] calldata amounts,
bytes calldata data
) public payable virtual {
- if (ids.length != amounts.length) revert LengthMismatch();
+ if (ids.length != amounts.length) revert InvalidArray();
if (msg.sender != from)
if (!isApprovedForAll[from][msg.sender]) revert Unauthorized();
@@ -383,8 +334,9 @@ abstract contract KeepToken {
// Storing these outside the loop saves ~15 gas per iteration.
uint256 id;
uint256 amount;
+ uint256 i;
- for (uint256 i; i < ids.length; ) {
+ for (i; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
@@ -394,9 +346,9 @@ abstract contract KeepToken {
if (!userPermissioned[to][id] || !userPermissioned[from][id])
revert NotPermitted();
- // If not transferring SIGN_KEY, update delegation balance.
- // Otherwise, prevent transfer to SIGN_KEY holder.
- if (id != SIGN_KEY)
+ // If not transferring EXEC_KEY, update delegation balance.
+ // Otherwise, prevent transfer to EXEC_KEY holder.
+ if (id != EXEC_KEY)
_moveDelegates(
delegates(from, id),
delegates(to, id),
@@ -448,7 +400,7 @@ abstract contract KeepToken {
bytes32 r,
bytes32 s
) public payable virtual {
- if (owner == address(0)) revert InvalidSig();
+ if (owner == address(0)) revert InvalidSignature();
if (block.timestamp > deadline) revert ExpiredSig();
@@ -474,7 +426,7 @@ abstract contract KeepToken {
)
);
- _recoverSig(hash, owner, v, r, s);
+ _checkSig(owner, hash, v, r, s);
}
isApprovedForAll[owner][operator] = approved;
@@ -489,13 +441,6 @@ abstract contract KeepToken {
function getVotes(
address account,
uint256 id
- ) public view virtual returns (uint256) {
- return getCurrentVotes(account, id);
- }
-
- function getCurrentVotes(
- address account,
- uint256 id
) public view virtual returns (uint256) {
// Unchecked because subtraction only occurs if positive `nCheckpoints`.
unchecked {
@@ -514,14 +459,6 @@ abstract contract KeepToken {
address account,
uint256 id,
uint256 timestamp
- ) public view virtual returns (uint256) {
- return getPriorVotes(account, id, timestamp);
- }
-
- function getPriorVotes(
- address account,
- uint256 id,
- uint256 timestamp
) public view virtual returns (uint256) {
if (block.timestamp <= timestamp) revert Undetermined();
@@ -590,7 +527,7 @@ abstract contract KeepToken {
bytes32 r,
bytes32 s
) public payable virtual {
- if (delegator == address(0)) revert InvalidSig();
+ if (delegator == address(0)) revert InvalidSignature();
if (block.timestamp > deadline) revert ExpiredSig();
@@ -616,7 +553,7 @@ abstract contract KeepToken {
)
);
- _recoverSig(hash, delegator, v, r, s);
+ _checkSig(delegator, hash, v, r, s);
}
_delegate(delegator, delegatee, id);
@@ -755,9 +692,9 @@ abstract contract KeepToken {
) internal virtual {
_safeCastTo216(totalSupply[id] += amount);
- // If not minting SIGN_KEY, update delegation balance.
- // Otherwise, prevent minting to SIGN_KEY holder.
- if (id != SIGN_KEY)
+ // If not minting EXEC_KEY, update delegation balance.
+ // Otherwise, prevent minting to EXEC_KEY holder.
+ if (id != EXEC_KEY)
_moveDelegates(address(0), delegates(to, id), id, amount);
else if (balanceOf[to][id] != 0) revert Overflow();
@@ -793,8 +730,8 @@ abstract contract KeepToken {
emit TransferSingle(msg.sender, from, address(0), id, amount);
- // If not burning SIGN_KEY, update delegation balance.
- if (id != SIGN_KEY)
+ // If not burning EXEC_KEY, update delegation balance.
+ if (id != EXEC_KEY)
_moveDelegates(delegates(from, id), address(0), id, amount);
}
@@ -824,3 +761,26 @@ abstract contract KeepToken {
emit UserPermissionSet(msg.sender, to, id, on);
}
}
+
+/// @notice ERC1155 interface to receive tokens.
+abstract contract ERC1155TokenReceiver {
+ function onERC1155Received(
+ address,
+ address,
+ uint256,
+ uint256,
+ bytes calldata
+ ) public payable virtual returns (bytes4) {
+ return this.onERC1155Received.selector;
+ }
+
+ function onERC1155BatchReceived(
+ address,
+ address,
+ uint256[] calldata,
+ uint256[] calldata,
+ bytes calldata
+ ) public payable virtual returns (bytes4) {
+ return this.onERC1155BatchReceived.selector;
+ }
+}
diff --git a/src/extensions/dao/Kali.sol b/src/extensions/dao/Kali.sol
deleted file mode 100644
index 79a8f3fd..00000000
--- a/src/extensions/dao/Kali.sol
+++ /dev/null
@@ -1,906 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {KaliExtension} from "./utils/KaliExtension.sol";
-import {Multicallable} from "../../utils/Multicallable.sol";
-import {ReentrancyGuard} from "../utils/ReentrancyGuard.sol";
-import {KeepTokenManager} from "../utils/KeepTokenManager.sol";
-import {ERC1155TokenReceiver, Operation, Call, Signature} from "./../../Keep.sol";
-
-/// @title Kali
-/// @notice Kali DAO core for on-chain governance.
-/// @author z0r0z.eth
-
-enum ProposalType {
- MINT, // Add to membership.
- BURN, // Revoke membership.
- CALL, // Call to external code.
- VPERIOD, // Set `votingPeriod`.
- GPERIOD, // Set `gracePeriod`.
- QUORUM, // Set `quorum`.
- SUPERMAJORITY, // Set `supermajority`.
- TYPE, // Set `VoteType` `ProposalType`.
- PAUSE, // Flip membership transferability.
- EXTENSION, // Flip `extensions` permission.
- ESCAPE, // Delete pending proposal in queue.
- URI // Amend root documentation for the DAO.
-}
-
-enum VoteType {
- SIMPLE_MAJORITY_QUORUM_REQUIRED,
- SIMPLE_MAJORITY,
- SUPERMAJORITY_QUORUM_REQUIRED,
- SUPERMAJORITY
-}
-
-struct Proposal {
- uint256 prevProposal;
- bytes32 proposalHash;
- address proposer;
- uint40 creationTime;
- uint216 yesVotes;
- uint216 noVotes;
-}
-
-struct ProposalState {
- bool passed;
- bool processed;
-}
-
-contract Kali is ERC1155TokenReceiver, Multicallable, ReentrancyGuard {
- /// -----------------------------------------------------------------------
- /// Events
- /// -----------------------------------------------------------------------
-
- event NewProposal(
- address indexed proposer,
- uint256 indexed proposal,
- Call[] calls,
- ProposalType setting,
- string details,
- uint256 creationTime,
- bool sponsored
- );
-
- event ProposalCancelled(address indexed proposer, uint256 indexed proposal);
-
- event ProposalSponsored(address indexed sponsor, uint256 indexed proposal);
-
- event VoteCast(
- address indexed voter,
- uint256 indexed proposal,
- bool approve,
- uint256 weight,
- string details
- );
-
- event ProposalProcessed(uint256 indexed proposal, bool passed);
-
- event ExtensionSet(address indexed extension, bool on);
-
- event URISet(string daoURI);
-
- event GovSettingsUpdated(
- uint256 votingPeriod,
- uint256 gracePeriod,
- uint256 quorum,
- uint256 supermajority,
- uint256[2] typeSetting
- );
-
- event Executed(
- Operation op,
- address to,
- uint256 value,
- bytes data,
- bool success
- );
-
- /// -----------------------------------------------------------------------
- /// Custom Errors
- /// -----------------------------------------------------------------------
-
- error Initialized();
-
- error PeriodBounds();
-
- error QuorumMax();
-
- error SupermajorityBounds();
-
- error TypeBounds();
-
- error Unauthorized();
-
- error Sponsored();
-
- error InvalidProposal();
-
- error AlreadyVoted();
-
- error InvalidHash();
-
- error PrevNotProcessed();
-
- error VotingNotEnded();
-
- error InvalidSig();
-
- error Overflow();
-
- /// -----------------------------------------------------------------------
- /// DAO Storage/Logic
- /// -----------------------------------------------------------------------
-
- bytes32 internal constant MALLEABILITY_THRESHOLD =
- 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;
-
- uint256 internal currentSponsoredProposal;
-
- uint256 public proposalCount;
-
- string public daoURI;
-
- uint120 public votingPeriod;
-
- uint120 public gracePeriod;
-
- uint8 public quorum; // 1-100.
-
- uint8 public supermajority; // 1-100.
-
- mapping(address => bool) public extensions;
-
- mapping(uint256 => Proposal) public proposals;
-
- mapping(uint256 => ProposalState) public proposalStates;
-
- mapping(ProposalType => VoteType) public proposalVoteTypes;
-
- mapping(uint256 => mapping(address => bool)) public voted;
-
- mapping(address => uint256) public lastYesVote;
-
- function token() public pure virtual returns (KeepTokenManager tkn) {
- uint256 placeholder;
-
- assembly {
- placeholder := sub(
- calldatasize(),
- add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
- )
-
- tkn := shr(0x60, calldataload(add(placeholder, 2)))
- }
- }
-
- function tokenId() public pure virtual returns (uint256 id) {
- return _fetchImmutable(22);
- }
-
- function name() public pure virtual returns (string memory) {
- return string(abi.encodePacked(_fetchImmutable(54)));
- }
-
- function _fetchImmutable(
- uint256 place
- ) internal pure virtual returns (uint256 ref) {
- uint256 placeholder;
-
- assembly {
- placeholder := sub(
- calldatasize(),
- add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
- )
-
- ref := calldataload(add(placeholder, place))
- }
- }
-
- /// -----------------------------------------------------------------------
- /// ERC165 Logic
- /// -----------------------------------------------------------------------
-
- function supportsInterface(
- bytes4 interfaceId
- ) public view virtual returns (bool) {
- return
- // ERC165 interface ID for ERC165.
- interfaceId == this.supportsInterface.selector ||
- // ERC165 Interface ID for ERC721TokenReceiver.
- interfaceId == this.onERC721Received.selector ||
- // ERC165 Interface ID for ERC1155TokenReceiver.
- interfaceId == type(ERC1155TokenReceiver).interfaceId;
- }
-
- /// -----------------------------------------------------------------------
- /// ERC721 Receiver Logic
- /// -----------------------------------------------------------------------
-
- function onERC721Received(
- address,
- address,
- uint256,
- bytes calldata
- ) public payable virtual returns (bytes4) {
- return this.onERC721Received.selector;
- }
-
- /// -----------------------------------------------------------------------
- /// Initialization Logic
- /// -----------------------------------------------------------------------
-
- constructor() payable {
- // Deploy as singleton.
- votingPeriod = 1;
- }
-
- function initialize(
- Call[] calldata _calls,
- string calldata _daoURI,
- uint120[4] calldata _govSettings
- ) public payable virtual {
- if (votingPeriod != 0) revert Initialized();
-
- if (_govSettings[0] == 0) revert PeriodBounds();
-
- if (_govSettings[0] > 365 days) revert PeriodBounds();
-
- if (_govSettings[1] > 365 days) revert PeriodBounds();
-
- if (_govSettings[2] > 100) revert QuorumMax();
-
- if (_govSettings[3] <= 51) revert SupermajorityBounds();
-
- if (_govSettings[3] > 100) revert SupermajorityBounds();
-
- if (_calls.length != 0) {
- for (uint256 i; i < _calls.length; ) {
- extensions[_calls[i].to] = true;
-
- if (_calls[i].data.length > 3)
- _execute(
- _calls[i].op,
- _calls[i].to,
- _calls[i].value,
- _calls[i].data
- );
-
- // An array can't have a total length
- // larger than the max uint256 value.
- unchecked {
- ++i;
- }
- }
- }
-
- daoURI = _daoURI;
-
- votingPeriod = uint120(_govSettings[0]);
-
- gracePeriod = uint120(_govSettings[1]);
-
- quorum = uint8(_govSettings[2]);
-
- supermajority = uint8(_govSettings[3]);
- }
-
- /// -----------------------------------------------------------------------
- /// EIP-712 Logic
- /// -----------------------------------------------------------------------
-
- function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
- return
- keccak256(
- abi.encode(
- // `keccak256(
- // "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
- // )`
- 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
- // `keccak256(bytes("Kali"))`
- 0xd321353274be6f42cf7b550879ff1a1c924e1e8f469054b23c7354e7f1737c64,
- // `keccak256("1")`
- 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6,
- block.chainid,
- address(this)
- )
- );
- }
-
- /// -----------------------------------------------------------------------
- /// Proposal Logic
- /// -----------------------------------------------------------------------
-
- function propose(
- Call[] calldata calls,
- ProposalType setting,
- string calldata details
- ) public payable virtual returns (uint256 proposal, uint40 creationTime) {
- if (setting != ProposalType.MINT)
- if (setting != ProposalType.BURN)
- if (setting != ProposalType.CALL)
- if (setting == ProposalType.VPERIOD)
- if (calls[0].value == 0 || calls[0].value > 365 days)
- revert PeriodBounds();
- else if (setting == ProposalType.GPERIOD)
- if (calls[0].value > 365 days)
- revert PeriodBounds();
- else if (setting == ProposalType.QUORUM)
- if (calls[0].value > 100) revert QuorumMax();
- else if (setting == ProposalType.SUPERMAJORITY)
- if (
- calls[0].value <= 51 ||
- calls[0].value > 100
- ) revert SupermajorityBounds();
- else if (setting == ProposalType.TYPE)
- if (
- calls[0].value > 11 ||
- calls[1].value > 3
- ) revert TypeBounds();
- bool sponsored;
-
- // If member or extension is making proposal, include sponsorship.
- if (
- token().balanceOf(msg.sender, tokenId()) != 0 ||
- extensions[msg.sender]
- ) sponsored = true;
-
- // Proposal count cannot realistically overflow on human timescales.
- unchecked {
- proposals[proposal = ++proposalCount] = Proposal({
- prevProposal: sponsored ? currentSponsoredProposal : 0,
- proposalHash: keccak256(abi.encode(calls, setting, details)),
- proposer: msg.sender,
- creationTime: creationTime = sponsored
- ? _safeCastTo40(block.timestamp)
- : 0,
- yesVotes: 0,
- noVotes: 0
- });
- }
-
- if (sponsored) currentSponsoredProposal = proposal;
-
- emit NewProposal(
- msg.sender,
- proposal,
- calls,
- setting,
- details,
- creationTime,
- sponsored
- );
- }
-
- function cancelProposal(uint256 proposal) public payable virtual {
- Proposal storage prop = proposals[proposal];
-
- if (msg.sender != prop.proposer)
- if (!extensions[msg.sender]) revert Unauthorized();
-
- if (prop.creationTime != 0) revert Sponsored();
-
- delete proposals[proposal];
-
- emit ProposalCancelled(msg.sender, proposal);
- }
-
- function sponsorProposal(uint256 proposal) public payable virtual {
- Proposal storage prop = proposals[proposal];
-
- if (token().balanceOf(msg.sender, tokenId()) == 0)
- if (!extensions[msg.sender]) revert Unauthorized();
-
- if (prop.proposer == address(0)) revert InvalidProposal();
-
- if (prop.creationTime != 0) revert Sponsored();
-
- prop.prevProposal = currentSponsoredProposal;
-
- currentSponsoredProposal = proposal;
-
- prop.creationTime = _safeCastTo40(block.timestamp);
-
- emit ProposalSponsored(msg.sender, proposal);
- }
-
- /// -----------------------------------------------------------------------
- /// Voting Logic
- /// -----------------------------------------------------------------------
-
- function vote(
- uint256 proposal,
- bool approve,
- string calldata details
- ) public payable virtual {
- _vote(msg.sender, proposal, approve, details);
- }
-
- function voteBySig(
- uint256 proposal,
- bool approve,
- string calldata details,
- Signature calldata sig
- ) public payable virtual {
- bytes32 hash = keccak256(
- abi.encodePacked(
- "\x19\x01",
- DOMAIN_SEPARATOR(),
- keccak256(
- abi.encode(
- keccak256(
- "SignVote(uint256 proposal,bool approve,string details)"
- ),
- proposal,
- approve,
- details
- )
- )
- )
- );
-
- // Check signature recovery.
- _recoverSig(hash, sig.user, sig.v, sig.r, sig.s);
-
- _vote(sig.user, proposal, approve, details);
- }
-
- function _vote(
- address user,
- uint256 proposal,
- bool approve,
- string calldata details
- ) internal virtual {
- Proposal storage prop = proposals[proposal];
-
- if (voted[proposal][user]) revert AlreadyVoted();
-
- voted[proposal][user] = true;
-
- // This is safe from overflow because `votingPeriod`
- // is capped so it will not combine with unix time
- // to exceed the max uint256 value.
- unchecked {
- if (block.timestamp > prop.creationTime + votingPeriod)
- revert InvalidProposal();
- }
-
- uint216 weight = uint216(
- token().getPriorVotes(user, tokenId(), prop.creationTime)
- );
-
- // This is safe from overflow because `yesVotes`
- // and `noVotes` are capped by `totalSupply`
- // which is checked for overflow in `token` contract.
- unchecked {
- if (approve) {
- prop.yesVotes += weight;
-
- lastYesVote[user] = proposal;
- } else {
- prop.noVotes += weight;
- }
- }
-
- emit VoteCast(user, proposal, approve, weight, details);
- }
-
- /// -----------------------------------------------------------------------
- /// Processing Logic
- /// -----------------------------------------------------------------------
-
- function processProposal(
- uint256 proposal,
- Call[] calldata calls,
- ProposalType setting,
- string calldata details
- ) public payable virtual nonReentrant returns (bool passed) {
- Proposal storage prop = proposals[proposal];
-
- if (prop.creationTime == 0) revert InvalidProposal();
-
- if (keccak256(abi.encode(calls, setting, details)) != prop.proposalHash)
- revert InvalidHash();
-
- // Skip previous proposal processing requirement
- // in case of escape hatch.
- if (setting != ProposalType.ESCAPE)
- if (proposals[prop.prevProposal].creationTime != 0)
- revert PrevNotProcessed();
-
- VoteType voteType = proposalVoteTypes[setting];
-
- passed = _countVotes(voteType, prop.yesVotes, prop.noVotes);
-
- // If quorum and approval threshold are met,
- // skip voting period for fast processing.
- // If a grace period has been set,
- // or if quorum is set to nothing,
- // maintain voting period check.
- if (
- !passed ||
- gracePeriod != 0 ||
- quorum == 0 ||
- voteType == VoteType.SIMPLE_MAJORITY ||
- voteType == VoteType.SUPERMAJORITY
- ) {
- // This is safe from overflow because `votingPeriod`
- // and `gracePeriod` are capped so they will not combine
- // with unix time to exceed the max uint256 value.
- unchecked {
- if (
- block.timestamp <=
- prop.creationTime + votingPeriod + gracePeriod
- ) revert VotingNotEnded();
- }
- }
-
- if (passed) {
- // An array can't have a total length
- // larger than the max uint256 value.
- unchecked {
- if (setting == ProposalType.MINT) {
- for (uint256 i; i < calls.length; ++i) {
- token().mint(
- calls[i].to,
- tokenId(),
- calls[i].value,
- calls[i].data
- );
- }
- } else if (setting == ProposalType.BURN) {
- for (uint256 i; i < calls.length; ++i) {
- token().burn(calls[i].to, tokenId(), calls[i].value);
- }
- } else if (setting == ProposalType.CALL) {
- for (uint256 i; i < calls.length; ++i) {
- _execute(
- calls[i].op,
- calls[i].to,
- calls[i].value,
- calls[i].data
- );
- }
- } else if (setting == ProposalType.VPERIOD) {
- votingPeriod = uint120(calls[0].value);
- } else if (setting == ProposalType.GPERIOD) {
- gracePeriod = uint120(calls[0].value);
- } else if (setting == ProposalType.QUORUM) {
- quorum = uint8(calls[0].value);
- } else if (setting == ProposalType.SUPERMAJORITY) {
- supermajority = uint8(calls[0].value);
- } else if (setting == ProposalType.TYPE) {
- proposalVoteTypes[ProposalType(calls[0].value)] = VoteType(
- calls[1].value
- );
- } else if (setting == ProposalType.PAUSE) {
- token().setTransferability(
- tokenId(),
- !token().transferable(tokenId())
- );
- } else if (setting == ProposalType.EXTENSION) {
- for (uint256 i; i < calls.length; ++i) {
- if (calls[i].value != 0)
- extensions[calls[i].to] = !extensions[calls[i].to];
-
- if (calls[i].data.length > 3)
- KaliExtension(calls[i].to).setExtension(
- calls[i].data
- );
- }
- } else if (setting == ProposalType.ESCAPE) {
- delete proposals[calls[0].value];
- } else if (setting == ProposalType.URI) {
- daoURI = details;
- }
-
- proposalStates[proposal].passed = true;
- }
- }
-
- delete proposals[proposal];
-
- proposalStates[proposal].processed = true;
-
- emit ProposalProcessed(proposal, passed);
- }
-
- function _countVotes(
- VoteType voteType,
- uint256 yesVotes,
- uint256 noVotes
- ) internal view virtual returns (bool passed) {
- // Fail proposal if no participation.
- if (yesVotes == 0)
- if (noVotes == 0) return false;
-
- // Rule out any failed quorums.
- if (
- voteType == VoteType.SIMPLE_MAJORITY_QUORUM_REQUIRED ||
- voteType == VoteType.SUPERMAJORITY_QUORUM_REQUIRED
- ) {
- // This is safe from overflow because `yesVotes`
- // and `noVotes` supply are checked
- // in `token` contract.
- unchecked {
- if (
- (yesVotes + noVotes) <
- ((token().totalSupply(tokenId()) * quorum) / 100)
- ) return false;
- }
- }
-
- if (
- // Simple majority check.
- voteType == VoteType.SIMPLE_MAJORITY ||
- voteType == VoteType.SIMPLE_MAJORITY_QUORUM_REQUIRED
- ) {
- if (yesVotes > noVotes) return true;
- } else {
- // Supermajority check.
- // Example: 7 yes, 2 no, supermajority = 66
- // ((7+2) * 66) / 100 = 5.94; 7 yes will pass.
- // This is safe from overflow because `yesVotes`
- // and `noVotes` supply are checked
- // in `token` contract.
- unchecked {
- if (yesVotes >= ((yesVotes + noVotes) * supermajority) / 100)
- return true;
- }
- }
- }
-
- /// -----------------------------------------------------------------------
- /// Execution Logic
- /// -----------------------------------------------------------------------
-
- function _execute(
- Operation op,
- address to,
- uint256 value,
- bytes memory data
- ) internal virtual {
- bool success;
-
- if (op == Operation.call) {
- assembly {
- success := call(
- gas(),
- to,
- value,
- add(data, 0x20),
- mload(data),
- 0,
- 0
- )
- }
-
- emit Executed(op, to, value, data, success);
- } else if (op == Operation.delegatecall) {
- assembly {
- success := delegatecall(
- gas(),
- to,
- add(data, 0x20),
- mload(data),
- 0,
- 0
- )
- }
-
- emit Executed(op, to, value, data, success);
- } else {
- assembly {
- success := create(value, add(data, 0x20), mload(data))
- }
-
- emit Executed(op, to, value, data, success);
- }
- }
-
- /// -----------------------------------------------------------------------
- /// Signature Recovery Logic
- /// -----------------------------------------------------------------------
-
- function _recoverSig(
- bytes32 hash,
- address signer,
- uint8 v,
- bytes32 r,
- bytes32 s
- ) internal view virtual {
- if (signer == address(0)) revert InvalidSig();
-
- 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 {
-
- } {
- // Load the free memory pointer.
- // Simply using the free memory usually costs less if many slots are needed.
- let m := mload(0x40)
-
- // Clean the excess bits of `v` in case they are dirty.
- v := and(v, 0xff)
- // If `s` in lower half order, such that the signature is not malleable.
- if iszero(gt(s, MALLEABILITY_THRESHOLD)) {
- mstore(m, hash)
- mstore(add(m, 0x20), v)
- mstore(add(m, 0x40), r)
- mstore(add(m, 0x60), s)
- pop(
- staticcall(
- gas(), // Amount of gas left for the transaction.
- 0x01, // Address of `ecrecover`.
- m, // Start of input.
- 0x80, // Size of input.
- m, // Start of output.
- 0x20 // Size of output.
- )
- )
- // `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
- if mul(eq(mload(m), signer), returndatasize()) {
- isValid := 1
- break
- }
- }
-
- // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
- let f := shl(224, 0x1626ba7e)
- // Write the abi-encoded calldata into memory, beginning with the function selector.
- mstore(m, f) // `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`.
- mstore(add(m, 0x04), hash)
- mstore(add(m, 0x24), 0x40) // The offset of the `signature` in the calldata.
- mstore(add(m, 0x44), 65) // Store the length of the signature.
- mstore(add(m, 0x64), r) // Store `r` of the signature.
- mstore(add(m, 0x84), s) // Store `s` of the signature.
- mstore8(add(m, 0xa4), v) // Store `v` of the signature.
-
- isValid := and(
- and(
- // Whether the returndata is the magic value `0x1626ba7e` (left-aligned).
- eq(mload(0x00), f),
- // 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.
- signer, // The `signer` address.
- m, // Offset of calldata in memory.
- 0xa5, // Length of calldata in memory.
- 0x00, // Offset of returndata.
- 0x20 // Length of returndata to write.
- )
- )
- break
- }
- }
-
- if (!isValid) revert InvalidSig();
- }
-
- /// -----------------------------------------------------------------------
- /// Safecast Logic
- /// -----------------------------------------------------------------------
-
- function _safeCastTo40(uint256 x) internal pure virtual returns (uint40) {
- if (x >= (1 << 40)) revert Overflow();
-
- return uint40(x);
- }
-
- /// -----------------------------------------------------------------------
- /// Extension Logic
- /// -----------------------------------------------------------------------
-
- modifier onlyExtension() {
- if (!extensions[msg.sender]) revert Unauthorized();
-
- _;
- }
-
- function relay(
- Call calldata call
- ) public payable virtual onlyExtension nonReentrant {
- _execute(call.op, call.to, call.value, call.data);
- }
-
- function mint(
- KeepTokenManager source,
- address to,
- uint256 id,
- uint256 amount,
- bytes calldata data
- ) public payable virtual onlyExtension nonReentrant {
- source.mint(to, id, amount, data);
- }
-
- function burn(
- KeepTokenManager source,
- address from,
- uint256 id,
- uint256 amount
- ) public payable virtual onlyExtension nonReentrant {
- source.burn(from, id, amount);
- }
-
- function setTransferability(
- KeepTokenManager source,
- uint256 id,
- bool on
- ) public payable virtual onlyExtension nonReentrant {
- source.setTransferability(id, on);
- }
-
- function setExtension(address extension, bool on) public payable virtual {
- if (!extensions[msg.sender])
- if (msg.sender != address(this)) revert Unauthorized();
-
- extensions[extension] = on;
-
- emit ExtensionSet(extension, on);
- }
-
- function setURI(
- string calldata _daoURI
- ) public payable virtual onlyExtension {
- daoURI = _daoURI;
-
- emit URISet(_daoURI);
- }
-
- function deleteProposal(uint256 proposal) public payable virtual {
- if (!extensions[msg.sender])
- if (msg.sender != address(this)) revert Unauthorized();
-
- if (proposals[proposal].creationTime == 0) revert InvalidProposal();
-
- delete proposals[proposal];
-
- proposalStates[proposal].processed = true;
- }
-
- function updateGovSettings(
- uint256 _votingPeriod,
- uint256 _gracePeriod,
- uint256 _quorum,
- uint256 _supermajority,
- uint256[2] calldata _typeSetting
- ) public payable virtual {
- if (!extensions[msg.sender])
- if (msg.sender != address(this)) revert Unauthorized();
-
- if (_votingPeriod != 0)
- if (_votingPeriod <= 365 days)
- votingPeriod = uint120(_votingPeriod);
-
- if (_gracePeriod <= 365 days) gracePeriod = uint120(_gracePeriod);
-
- if (_quorum <= 100) quorum = uint8(_quorum);
-
- if (_supermajority > 51)
- if (_supermajority <= 100) supermajority = uint8(_supermajority);
-
- if (_typeSetting[0] <= 11)
- if (_typeSetting[1] <= 3)
- proposalVoteTypes[ProposalType(_typeSetting[0])] = VoteType(
- _typeSetting[1]
- );
-
- emit GovSettingsUpdated(
- _votingPeriod,
- _gracePeriod,
- _quorum,
- _supermajority,
- _typeSetting
- );
- }
-}
diff --git a/src/extensions/dao/KaliFactory.sol b/src/extensions/dao/KaliFactory.sol
deleted file mode 100644
index c276ef82..00000000
--- a/src/extensions/dao/KaliFactory.sol
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {Multicallable, Call, KeepTokenManager, Kali} from "./Kali.sol";
-import {LibClone} from "./../../utils/LibClone.sol";
-
-/// @notice Kali Factory.
-contract KaliFactory is Multicallable {
- /// -----------------------------------------------------------------------
- /// Library Usage
- /// -----------------------------------------------------------------------
-
- using LibClone for address;
-
- /// -----------------------------------------------------------------------
- /// Events
- /// -----------------------------------------------------------------------
-
- event Deployed(
- Kali indexed kali,
- KeepTokenManager token,
- uint256 tokenId,
- bytes32 name,
- Call[] calls,
- string daoURI,
- uint120[4] govSettings
- );
-
- /// -----------------------------------------------------------------------
- /// Immutables
- /// -----------------------------------------------------------------------
-
- address internal immutable kaliTemplate;
-
- /// -----------------------------------------------------------------------
- /// Constructor
- /// -----------------------------------------------------------------------
-
- constructor(address _kaliTemplate) payable {
- kaliTemplate = _kaliTemplate;
- }
-
- /// -----------------------------------------------------------------------
- /// Deployment Logic
- /// -----------------------------------------------------------------------
-
- function determineKali(
- KeepTokenManager token,
- uint256 tokenId,
- bytes32 name
- ) public view virtual returns (address) {
- return
- kaliTemplate.predictDeterministicAddress(
- abi.encodePacked(token, tokenId, name),
- name,
- address(this)
- );
- }
-
- function deployKali(
- KeepTokenManager _token,
- uint256 _tokenId,
- bytes32 _name, // create2 salt.
- Call[] calldata _calls,
- string calldata _daoURI,
- uint120[4] calldata _govSettings
- ) public payable virtual {
- Kali kali = Kali(
- kaliTemplate.cloneDeterministic(
- abi.encodePacked(_token, _tokenId, _name),
- _name
- )
- );
-
- kali.initialize{value: msg.value}(_calls, _daoURI, _govSettings);
-
- emit Deployed(
- kali,
- _token,
- _tokenId,
- _name,
- _calls,
- _daoURI,
- _govSettings
- );
- }
-}
diff --git a/src/extensions/dao/utils/KaliExtension.sol b/src/extensions/dao/utils/KaliExtension.sol
deleted file mode 100644
index d2a6c598..00000000
--- a/src/extensions/dao/utils/KaliExtension.sol
+++ /dev/null
@@ -1,7 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @notice Contract helper for Kali DAO extensions.
-abstract contract KaliExtension {
- function setExtension(bytes calldata extensionData) public payable virtual;
-}
diff --git a/src/extensions/metadata/URIFetcher.sol b/src/extensions/metadata/URIFetcher.sol
deleted file mode 100644
index 4517e3a2..00000000
--- a/src/extensions/metadata/URIFetcher.sol
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {Owned} from "../utils/Owned.sol";
-import {URIRemoteFetcher} from "./URIRemoteFetcher.sol";
-
-/// @notice Open-ended metadata fetcher for ERC1155.
-/// @author z0r0z.eth
-contract URIFetcher is Owned(tx.origin) {
- /// -----------------------------------------------------------------------
- /// Events
- /// -----------------------------------------------------------------------
-
- event URIRemoteFetcherSet(URIRemoteFetcher indexed uriRemoteFetcher);
-
- /// -----------------------------------------------------------------------
- /// URI Remote Storage
- /// -----------------------------------------------------------------------
-
- URIRemoteFetcher public uriRemoteFetcher;
-
- /// -----------------------------------------------------------------------
- /// Constructor
- /// -----------------------------------------------------------------------
-
- constructor() payable {
- emit URIRemoteFetcherSet(uriRemoteFetcher = new URIRemoteFetcher());
- }
-
- /// -----------------------------------------------------------------------
- /// URI Remote Logic
- /// -----------------------------------------------------------------------
-
- function uri(uint256 id) public view virtual returns (string memory) {
- return uriRemoteFetcher.fetchURI(msg.sender, id);
- }
-
- function setURIRemoteFetcher(
- URIRemoteFetcher _uriRemoteFetcher
- ) public payable virtual onlyOwner {
- uriRemoteFetcher = _uriRemoteFetcher;
-
- emit URIRemoteFetcherSet(_uriRemoteFetcher);
- }
-}
diff --git a/src/extensions/metadata/URIRemoteFetcher.sol b/src/extensions/metadata/URIRemoteFetcher.sol
deleted file mode 100644
index 58e0dad3..00000000
--- a/src/extensions/metadata/URIRemoteFetcher.sol
+++ /dev/null
@@ -1,116 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {Owned} from "../utils/Owned.sol";
-
-/// @notice Remote metadata fetcher for ERC1155.
-contract URIRemoteFetcher is Owned(tx.origin) {
- /// -----------------------------------------------------------------------
- /// Events
- /// -----------------------------------------------------------------------
-
- event AlphaURISet(string alphaURI);
-
- event BetaURISet(address indexed origin, string betaURI);
-
- event URISet(address indexed origin, uint256 indexed id, string uri);
-
- event UserURISet(address indexed origin, address indexed user, string uri);
-
- event UserIdURISet(
- address indexed origin,
- address indexed user,
- uint256 indexed id,
- string uri
- );
-
- /// -----------------------------------------------------------------------
- /// URI Storage
- /// -----------------------------------------------------------------------
-
- string public alphaURI;
-
- mapping(address => string) public betaURI;
-
- mapping(address => mapping(uint256 => string)) public uris;
-
- mapping(address => mapping(address => string)) public userUris;
-
- mapping(address => mapping(address => mapping(uint256 => string)))
- public userIdUris;
-
- /// -----------------------------------------------------------------------
- /// Constructor
- /// -----------------------------------------------------------------------
-
- constructor() payable {}
-
- /// -----------------------------------------------------------------------
- /// URI Logic
- /// -----------------------------------------------------------------------
-
- function fetchURI(
- address origin,
- uint256 id
- ) public view virtual returns (string memory) {
- string memory alpha = alphaURI;
- string memory beta = betaURI[origin];
- string memory uri = uris[origin][id];
-
- if (bytes(uri).length != 0) {
- return uri;
- } else if (bytes(beta).length != 0) {
- return beta;
- } else {
- return bytes(alpha).length != 0 ? alpha : "";
- }
- }
-
- function setAlphaURI(
- string calldata _alphaURI
- ) public payable virtual onlyOwner {
- alphaURI = _alphaURI;
-
- emit AlphaURISet(_alphaURI);
- }
-
- function setBetaURI(
- address origin,
- string calldata beta
- ) public payable virtual onlyOwner {
- betaURI[origin] = beta;
-
- emit BetaURISet(origin, beta);
- }
-
- function setURI(
- address origin,
- uint256 id,
- string calldata uri
- ) public payable virtual onlyOwner {
- uris[origin][id] = uri;
-
- emit URISet(origin, id, uri);
- }
-
- function setUserURI(
- address origin,
- address user,
- string calldata uri
- ) public payable virtual onlyOwner {
- userUris[origin][user] = uri;
-
- emit UserURISet(origin, user, uri);
- }
-
- function setUserIdURI(
- address origin,
- address user,
- uint256 id,
- string calldata uri
- ) public payable virtual onlyOwner {
- userIdUris[origin][user][id] = uri;
-
- emit UserIdURISet(origin, user, id, uri);
- }
-}
diff --git a/src/extensions/mint/MintManager.sol b/src/extensions/mint/MintManager.sol
index e77919b8..e68c25f0 100644
--- a/src/extensions/mint/MintManager.sol
+++ b/src/extensions/mint/MintManager.sol
@@ -1,18 +1,17 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
-import {KeepTokenManager} from "../utils/KeepTokenManager.sol";
import {Multicallable} from "../../utils/Multicallable.sol";
+import {IKeep} from "../../utils/interfaces/IKeep.sol";
/// @title Mint Manager
/// @notice ERC1155 token ID mint permission manager.
-/// @author z0r0z.eth
contract MintManager is Multicallable {
- event Approved(
- address indexed source,
- address indexed manager,
- uint256 id,
- bool approve
+ event Authorized(
+ address indexed src,
+ address indexed usr,
+ uint256 indexed id,
+ bool on
);
error Unauthorized();
@@ -20,25 +19,25 @@ contract MintManager is Multicallable {
mapping(address => mapping(address => mapping(uint256 => bool)))
public approved;
- function approve(
- address manager,
+ function authorize(
+ address usr,
uint256 id,
bool on
) public payable virtual {
- approved[msg.sender][manager][id] = on;
+ approved[msg.sender][usr][id] = on;
- emit Approved(msg.sender, manager, id, on);
+ emit Authorized(msg.sender, usr, id, on);
}
function mint(
- address source,
+ address src,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) public payable virtual {
- if (!approved[source][msg.sender][id]) revert Unauthorized();
+ if (!approved[src][msg.sender][id]) revert Unauthorized();
- KeepTokenManager(source).mint(to, id, amount, data);
+ IKeep(src).mint(to, id, amount, data);
}
}
diff --git a/src/extensions/storage/DataRoom.sol b/src/extensions/storage/DataRoom.sol
deleted file mode 100644
index 0f5f6896..00000000
--- a/src/extensions/storage/DataRoom.sol
+++ /dev/null
@@ -1,137 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @title DataRoom
-/// @notice Data room for on-chain orgs.
-/// @author audsssy.eth
-contract DataRoom {
- /// -----------------------------------------------------------------------
- /// Events
- /// -----------------------------------------------------------------------
-
- event PermissionSet(
- address indexed dao,
- address indexed account,
- bool permissioned
- );
-
- event RecordSet(address indexed dao, string data, address indexed caller);
-
- /// -----------------------------------------------------------------------
- /// Custom Errors
- /// -----------------------------------------------------------------------
-
- error Unauthorized();
-
- error LengthMismatch();
-
- error InvalidRoom();
-
- /// -----------------------------------------------------------------------
- /// DataRoom Storage
- /// -----------------------------------------------------------------------
-
- mapping(address => string[]) public room;
-
- mapping(address => mapping(address => bool)) public authorized;
-
- /// -----------------------------------------------------------------------
- /// Constructor
- /// -----------------------------------------------------------------------
-
- constructor() payable {}
-
- /// -----------------------------------------------------------------------
- /// DataRoom Logic
- /// -----------------------------------------------------------------------
-
- /// @notice Record data on-chain.
- /// @param account Identifier of a Room.
- /// @param data The data to record.
- /// @dev Calls are permissioned to those authorized to access a Room.
- function setRecord(
- address account,
- string[] calldata data
- ) public payable virtual {
- // Initialize Room.
- if (account == msg.sender && !authorized[account][msg.sender]) {
- authorized[account][msg.sender] = true;
- }
-
- _authorized(account, msg.sender);
-
- for (uint256 i; i < data.length; ) {
- room[account].push(data[i]);
-
- emit RecordSet(account, data[i], msg.sender);
-
- // Unchecked because the only math done is incrementing
- // the array index counter which cannot possibly overflow.
- unchecked {
- ++i;
- }
- }
- }
-
- /// @notice Retrieve data from a Room.
- /// @param account Identifier of a Room.
- /// @return The array of data associated with a Room.
- function getRoom(
- address account
- ) public view virtual returns (string[] memory) {
- return room[account];
- }
-
- /// @notice Initialize a Room or authorize users to a Room.
- /// @param account Identifier of a Room.
- /// @param users Users to be authorized or deauthorized to access a Room.
- /// @param authorize Authorization status.
- /// @dev Calls are permissioned to the authorized accounts of a Room.
- function setPermission(
- address account,
- address[] calldata users,
- bool[] calldata authorize
- ) public payable virtual {
- if (account == address(0)) revert InvalidRoom();
-
- // Initialize Room.
- if (account == msg.sender && !authorized[account][msg.sender]) {
- authorized[account][msg.sender] = true;
- }
-
- _authorized(account, msg.sender);
-
- uint256 numUsers = users.length;
-
- if (numUsers != authorize.length) revert LengthMismatch();
-
- if (numUsers != 0) {
- for (uint i; i < numUsers; ) {
- authorized[account][users[i]] = authorize[i];
-
- emit PermissionSet(account, users[i], authorize[i]);
-
- // Unchecked because the only math done is incrementing
- // the array index counter which cannot possibly overflow.
- unchecked {
- ++i;
- }
- }
- }
- }
-
- /// -----------------------------------------------------------------------
- /// Internal Functions
- /// -----------------------------------------------------------------------
-
- /// @notice Helper function to check access to a Room.
- /// @param account Identifier of a Room.
- /// @param user The user in question.
- function _authorized(
- address account,
- address user
- ) internal view virtual returns (bool) {
- if (authorized[account][user]) return true;
- else revert Unauthorized();
- }
-}
diff --git a/src/extensions/utils/KeepTokenManager.sol b/src/extensions/utils/KeepTokenManager.sol
deleted file mode 100644
index 91c86824..00000000
--- a/src/extensions/utils/KeepTokenManager.sol
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @notice Contract helper for Keep token management.
-abstract contract KeepTokenManager {
- function balanceOf(
- address account,
- uint256 id
- ) public view virtual returns (uint256);
-
- function totalSupply(uint256 id) public view virtual returns (uint256);
-
- function transferable(uint256 id) public view virtual returns (bool);
-
- function getPriorVotes(
- address account,
- uint256 id,
- uint256 timestamp
- ) public view virtual returns (uint256);
-
- function mint(
- address to,
- uint256 id,
- uint256 amount,
- bytes calldata data
- ) public payable virtual;
-
- function burn(
- address from,
- uint256 id,
- uint256 amount
- ) public payable virtual;
-
- function setTransferability(uint256 id, bool on) public payable virtual;
-}
diff --git a/src/extensions/utils/ReentrancyGuard.sol b/src/extensions/utils/ReentrancyGuard.sol
deleted file mode 100644
index 681f715c..00000000
--- a/src/extensions/utils/ReentrancyGuard.sol
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @notice Reentrancy protection for contracts.
-/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/ReentrancyGuard.sol)
-abstract contract ReentrancyGuard {
- error Reentrancy();
-
- uint256 internal locked = 1;
-
- modifier nonReentrant() virtual {
- if (locked == 2) revert Reentrancy();
-
- locked = 2;
-
- _;
-
- locked = 1;
- }
-}
diff --git a/src/extensions/validate/PermissionRemoteFetcher.sol b/src/extensions/validate/PermissionRemoteFetcher.sol
new file mode 100644
index 00000000..7910808d
--- /dev/null
+++ b/src/extensions/validate/PermissionRemoteFetcher.sol
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
+
+import {UserOperation} from "../../Keep.sol";
+
+struct Permission {
+ uint256 validAfter;
+ uint256 validUntil;
+ address receiver;
+ bytes4 func;
+ uint256 allowance;
+ uint256 maxUses;
+}
+
+/// @notice Remote permission fetcher for ERC4337.
+contract PermissionRemoteFetcher {
+ function validateUserOp(
+ UserOperation calldata userOp,
+ bytes32 userOpHash,
+ uint256 missingAccountFunds
+ ) public view virtual returns (uint256) {
+ /*Permission memory permission = abi.decode(userOp.data, (Permission));
+
+ if (permission.validAfter > block.timestamp) {
+ return 1;
+ }
+
+ if (permission.validUntil < block.timestamp) {
+ return 2;
+ }
+
+ if (permission.maxUses > 0) {
+ if (permission.allowance == 0) {
+ return 3;
+ }
+
+ if (permission.allowance < userOp.amount) {
+ return 4;
+ }
+ }
+
+ if (permission.receiver != address(0)) {
+ if (permission.receiver != userOp.sender) {
+ return 5;
+ }
+ }
+
+ return 0;*/
+ }
+}
diff --git a/src/extensions/metadata/URIRemoteFetcherV2.sol b/src/extensions/validate/URIRemoteFetcher.sol
similarity index 95%
rename from src/extensions/metadata/URIRemoteFetcherV2.sol
rename to src/extensions/validate/URIRemoteFetcher.sol
index 005751ae..8c13f334 100644
--- a/src/extensions/metadata/URIRemoteFetcherV2.sol
+++ b/src/extensions/validate/URIRemoteFetcher.sol
@@ -1,13 +1,13 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
/// @notice Remote metadata fetcher for ERC1155.
-contract URIRemoteFetcherV2 {
+contract URIRemoteFetcher {
uint256 internal immutable chainId = block.chainid;
constructor() payable {}
- function fetchURI(
+ function uri(
address origin,
uint256 id
) public view virtual returns (string memory) {
@@ -76,7 +76,7 @@ contract URIRemoteFetcherV2 {
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(
address value
- ) internal pure virtual returns (string memory str) {
+ ) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
@@ -111,7 +111,7 @@ contract URIRemoteFetcherV2 {
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(
address value
- ) internal pure virtual returns (string memory str) {
+ ) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
@@ -126,7 +126,7 @@ contract URIRemoteFetcherV2 {
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(
address value
- ) internal pure virtual returns (string memory str) {
+ ) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
diff --git a/src/extensions/validate/Validator.sol b/src/extensions/validate/Validator.sol
new file mode 100644
index 00000000..15131e13
--- /dev/null
+++ b/src/extensions/validate/Validator.sol
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
+
+import {Ownable} from "../../utils/Ownable.sol";
+import {UserOperation} from "../../Keep.sol";
+
+/// @notice Open-ended metadata for ERC1155 and ERC4337 permission fetching.
+contract Validator is Ownable(tx.origin) {
+ /// -----------------------------------------------------------------------
+ /// Events
+ /// -----------------------------------------------------------------------
+
+ event PermissionRemoteValidatorSet(
+ Validator indexed permissionRemoteValidator
+ );
+
+ event URIRemoteValidatorSet(Validator indexed uriRemoteValidator);
+
+ event EntryPointSet(address indexed entryPoint);
+
+ /// -----------------------------------------------------------------------
+ /// Remote Storage
+ /// -----------------------------------------------------------------------
+
+ Validator internal permissionRemoteValidator;
+
+ Validator internal uriRemoteValidator;
+
+ address public entryPoint;
+
+ /// -----------------------------------------------------------------------
+ /// Constructor
+ /// -----------------------------------------------------------------------
+
+ constructor() payable {
+ emit EntryPointSet(
+ entryPoint = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789
+ );
+ }
+
+ /// -----------------------------------------------------------------------
+ /// Permission Remote Logic
+ /// -----------------------------------------------------------------------
+
+ function validateUserOp(
+ UserOperation calldata userOp,
+ bytes32 userOpHash,
+ uint256 missingAccountFunds
+ ) public view virtual returns (uint256) {
+ return
+ permissionRemoteValidator.validateUserOp(
+ userOp,
+ userOpHash,
+ missingAccountFunds
+ );
+ }
+
+ function setPermissionRemoteValidator(
+ Validator _permissionRemoteValidator
+ ) public payable virtual onlyOwner {
+ permissionRemoteValidator = _permissionRemoteValidator;
+
+ emit PermissionRemoteValidatorSet(_permissionRemoteValidator);
+ }
+
+ /// -----------------------------------------------------------------------
+ /// URI Remote Logic
+ /// -----------------------------------------------------------------------
+
+ function uri(
+ address,
+ uint256
+ ) public view virtual returns (string memory) {}
+
+ function uri(uint256 id) public view virtual returns (string memory) {
+ return uriRemoteValidator.uri(msg.sender, id);
+ }
+
+ function setURIRemoteValidator(
+ Validator _uriRemoteValidator
+ ) public payable virtual onlyOwner {
+ uriRemoteValidator = _uriRemoteValidator;
+
+ emit URIRemoteValidatorSet(_uriRemoteValidator);
+ }
+
+ /// -----------------------------------------------------------------------
+ /// Entry Point Logic
+ /// -----------------------------------------------------------------------
+
+ function setEntryPoint(
+ address _entryPoint
+ ) public payable virtual onlyOwner {
+ entryPoint = _entryPoint;
+
+ emit EntryPointSet(_entryPoint);
+ }
+}
diff --git a/src/utils/LibClone.sol b/src/utils/LibClone.sol
deleted file mode 100644
index b6e466d9..00000000
--- a/src/utils/LibClone.sol
+++ /dev/null
@@ -1,151 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @notice Minimal proxy library with immutable args operations.
-/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibClone.sol)
-library LibClone {
- /// -----------------------------------------------------------------------
- /// Custom Errors
- /// -----------------------------------------------------------------------
-
- /// @dev Unable to deploy the clone.
- error DeploymentFailed();
-
- /// -----------------------------------------------------------------------
- /// Clone Operations
- /// -----------------------------------------------------------------------
-
- /// @dev Deploys a deterministic clone of `implementation`,
- /// using immutable arguments encoded in `data`, with `salt`.
- function cloneDeterministic(
- 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)
-
- // Write the bytecode before the data.
- mstore(data, 0x5af43d3d93803e606057fd5bf3)
- // Write the address of the implementation.
- mstore(sub(data, 0x0d), 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))
-
- // Create the instance.
- instance := create2(
- 0,
- sub(data, 0x4c),
- add(extraLength, 0x6c),
- salt
- )
-
- // If `instance` is zero, revert.
- if iszero(instance) {
- // Store the function selector of `DeploymentFailed()`.
- mstore(0x00, 0x30116425)
- // Revert with (offset, size).
- 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 address of the deterministic clone of
- /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`.
- function predictDeterministicAddress(
- address implementation,
- bytes memory data,
- bytes32 salt,
- address deployer
- ) internal pure returns (address predicted) {
- 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)
-
- // Write the bytecode before the data.
- mstore(data, 0x5af43d3d93803e606057fd5bf3)
- // Write the address of the implementation.
- mstore(sub(data, 0x0d), 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))
-
- // Compute and store the bytecode hash.
- mstore(0x35, keccak256(sub(data, 0x4c), add(extraLength, 0x6c)))
- mstore8(0x00, 0xff) // Write the prefix.
- 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)
-
- // 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)
- }
- }
-}
diff --git a/src/utils/Multicallable.sol b/src/utils/Multicallable.sol
index 7bbb7e7e..b93f185d 100644
--- a/src/utils/Multicallable.sol
+++ b/src/utils/Multicallable.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
/// @notice Contract that enables a single call to call multiple methods on itself.
/// @author Modified from Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol)
diff --git a/src/extensions/utils/Owned.sol b/src/utils/Ownable.sol
similarity index 85%
rename from src/extensions/utils/Owned.sol
rename to src/utils/Ownable.sol
index b4a7ce1e..4be7d0ef 100644
--- a/src/extensions/utils/Owned.sol
+++ b/src/utils/Ownable.sol
@@ -1,14 +1,13 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
/// @notice Simple single owner authorization mixin that implements ERC173.
-/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/auth/Owned.sol)
-abstract contract Owned {
+abstract contract Ownable {
/// -----------------------------------------------------------------------
/// Events
/// -----------------------------------------------------------------------
- event OwnershipTransferred(address indexed owner, address indexed newOwner);
+ event OwnershipTransferred(address indexed owner, address indexed _owner);
/// -----------------------------------------------------------------------
/// Custom Errors
@@ -43,11 +42,11 @@ abstract contract Owned {
/// -----------------------------------------------------------------------
function transferOwnership(
- address newOwner
+ address _owner
) public payable virtual onlyOwner {
- owner = newOwner;
+ owner = _owner;
- emit OwnershipTransferred(msg.sender, newOwner);
+ emit OwnershipTransferred(msg.sender, _owner);
}
/// -----------------------------------------------------------------------
diff --git a/src/utils/interfaces/IKeep.sol b/src/utils/interfaces/IKeep.sol
index 50201c17..29fbc6f1 100644
--- a/src/utils/interfaces/IKeep.sol
+++ b/src/utils/interfaces/IKeep.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
interface IKeep {
event ApprovalForAll(
diff --git a/src/utils/interfaces/IKeepFactory.sol b/src/utils/interfaces/IKeepFactory.sol
index 60224d82..0c79960b 100644
--- a/src/utils/interfaces/IKeepFactory.sol
+++ b/src/utils/interfaces/IKeepFactory.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
interface IKeepFactory {
event Deployed(address indexed keep, address[] signers, uint256 threshold);
diff --git a/test/Kali.t.sol b/test/Kali.t.sol
deleted file mode 100644
index 9cae6513..00000000
--- a/test/Kali.t.sol
+++ /dev/null
@@ -1,1808 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-/// @dev Keep core.
-import {KeepToken, Operation, Call, Signature, Keep} from "../src/Keep.sol";
-import {KeepFactory} from "../src/KeepFactory.sol";
-import {URIFetcher} from "../src/extensions/metadata/URIFetcher.sol";
-
-/// @dev Kali core.
-import {KeepTokenManager, Proposal, ProposalType, VoteType, Kali} from "../src/extensions/dao/Kali.sol";
-import {KaliFactory} from "../src/extensions/dao/KaliFactory.sol";
-
-/// @dev Mocks.
-import {MockERC20} from "@solady/test/utils/mocks/MockERC20.sol";
-import {MockERC721} from "@solady/test/utils/mocks/MockERC721.sol";
-import {MockERC1155} from "@solady/test/utils/mocks/MockERC1155.sol";
-import {MockERC1271Wallet} from "@solady/test/utils/mocks/MockERC1271Wallet.sol";
-import {MockUnsafeERC1155Receiver} from "./utils/mocks/MockUnsafeERC1155Receiver.sol";
-
-import "@std/Test.sol";
-
-error Initialized();
-
-error PeriodBounds();
-
-error QuorumMax();
-
-error SupermajorityBounds();
-
-error TypeBounds();
-
-error InvalidProposal();
-
-error Sponsored();
-
-error AlreadyVoted();
-
-contract KaliTest is Test, Keep(Keep(address(0))) {
- address keepAddr;
- address kaliAddr;
-
- address keep;
- KeepFactory keepFactory;
-
- address kali;
- KaliFactory kaliFactory;
-
- MockERC20 internal mockDai;
- MockERC721 internal mockNFT;
- MockERC1155 internal mock1155;
- MockERC1271Wallet internal mockERC1271Wallet;
- MockUnsafeERC1155Receiver internal mockUnsafeERC1155Receiver;
-
- address[] signers;
-
- /// @dev Users.
-
- address public immutable alice = makeAddr("alice");
- address public immutable bob = makeAddr("bob");
- address public immutable charlie = makeAddr("charlie");
- address public immutable dave = makeAddr("dave");
-
- /// @dev Helpers.
-
- Call[] calls;
-
- string internal constant description = "TEST";
-
- bytes32 internal constant name1 =
- 0x5445535400000000000000000000000000000000000000000000000000000000;
-
- bytes32 internal constant name2 =
- 0x5445535432000000000000000000000000000000000000000000000000000000;
-
- /// -----------------------------------------------------------------------
- /// Kali Setup Tests
- /// -----------------------------------------------------------------------
-
- /// @notice Set up the testing suite.
-
- function setUp() public payable {
- mockDai = new MockERC20("Dai", "DAI", 18);
- mockNFT = new MockERC721();
- mock1155 = new MockERC1155();
- mockERC1271Wallet = new MockERC1271Wallet(alice);
- mockUnsafeERC1155Receiver = new MockUnsafeERC1155Receiver();
-
- // Create the Keep templates.
- keep = address(new Keep(Keep(address(address(0)))));
- // Create the Keep factory.
- keepFactory = new KeepFactory(keep);
- // Create the Signer[] for setup.
- address[] memory setupSigners = new address[](2);
- setupSigners[0] = alice > bob ? bob : alice;
- setupSigners[1] = alice > bob ? alice : bob;
- // Store the Keep signers.
- signers.push(alice);
- signers.push(bob);
- // Initialize Keep from factory.
- keepAddr = keepFactory.determineKeep(name1);
- keep = keepAddr;
- keepFactory.deployKeep(name1, calls, setupSigners, 2);
-
- // Mint alice, bob and charlie Kali Keep DAO ID key (0).
- vm.prank(keep);
- Keep(keep).mint(alice, 0, 1, "");
- vm.prank(keep);
- Keep(keep).mint(bob, 0, 1, "");
- vm.prank(keep);
- Keep(keep).mint(charlie, 0, 1, "");
-
- // Check balances.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
- assertEq(Keep(keep).balanceOf(bob, 0), 1);
- assertEq(Keep(keep).balanceOf(charlie, 0), 1);
-
- // Create the Kali template.
- kali = address(new Kali());
- // Create the Kali factory.
- kaliFactory = new KaliFactory(kali);
-
- // Prime dummy inputs;
- Call[] memory dummyCall = new Call[](1);
-
- uint120[4] memory govSettings;
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 20;
- govSettings[3] = 52;
-
- kali = kaliFactory.determineKali(KeepTokenManager(keep), 0, name1);
-
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name1, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Mint Core Key ID uber permission to DAO.
- vm.prank(keep);
- Keep(keep).mint(kali, CORE_KEY, 1, "");
-
- assertEq(Keep(keep).balanceOf(kali, CORE_KEY), 1);
-
- bool sent;
-
- // Deposit 1 ETH to Keep.
- (sent, ) = keep.call{value: 1 ether}("");
- assert(sent);
-
- // Deposit 10 ETH to Kali.
- (sent, ) = kali.call{value: 10 ether}("");
- assert(sent);
-
- // Bump time.
- vm.warp(block.timestamp + 1000);
-
- // Mint mock ERC20.
- mockDai.mint(address(this), 1_000_000_000 ether);
- mockDai.mint(kali, 1_000_000_000 ether);
- // Mint mock 721.
- mockNFT.mint(address(this), 1);
- // Mint mock 1155.
- mock1155.mint(address(this), 1, 1, "");
- }
-
- /// @notice Check deployment.
-
- function testDetermination() public payable {
- // Check CREATE2 clones match expected outputs.
- address computedAddr = kaliFactory.determineKali(
- KeepTokenManager(keep),
- 0,
- name1
- );
-
- assertEq(kali, computedAddr);
- }
-
- function testDeploy() public payable {
- // Prime dummy inputs;
- Call[] memory dummyCall = new Call[](1);
-
- uint120[4] memory govSettings;
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 20;
- govSettings[3] = 52;
-
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
- }
-
- function testFailDeploy() public payable {
- // Create the Signer[].
- address[] memory setupSigners = new address[](2);
- setupSigners[0] = alice > bob ? bob : alice;
- setupSigners[1] = alice > bob ? alice : bob;
-
- // Prime dummy inputs;
- Call[] memory dummyCall = new Call[](1);
-
- // Check against zero voting period.
- uint120[4] memory govSettings;
- govSettings[0] = 0;
- govSettings[1] = 0;
- govSettings[2] = 20;
- govSettings[3] = 52;
-
- vm.expectRevert(PeriodBounds.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against excessive voting period.
- govSettings[0] = 366 days;
-
- vm.expectRevert(PeriodBounds.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against excessive grace period.
- govSettings[0] = 1 days;
- govSettings[1] = 366 days;
-
- vm.expectRevert(PeriodBounds.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against excessive quorum.
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 101;
-
- vm.expectRevert(QuorumMax.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against zero supermajority.
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 0;
- govSettings[3] = 0;
-
- vm.expectRevert(SupermajorityBounds.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against excessive supermajority.
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 0;
- govSettings[3] = 101;
-
- vm.expectRevert(SupermajorityBounds.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- // Check against repeat initialization.
- govSettings[0] = 1 days;
- govSettings[1] = 0;
- govSettings[2] = 20;
- govSettings[3] = 52;
-
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
-
- vm.expectRevert(Initialized.selector);
- kaliFactory.deployKali(
- KeepTokenManager(keep),
- 0,
- name2, // create2 salt.
- dummyCall,
- "DAO",
- govSettings
- );
- }
-
- /// -----------------------------------------------------------------------
- /// Kali State Tests
- /// -----------------------------------------------------------------------
-
- function testName() public payable {
- assertEq(Kali(kali).name(), string(abi.encodePacked(name1)));
- }
-
- function testToken() public payable {
- assert(Kali(kali).token() == KeepTokenManager(keep));
- }
-
- function testTokenId() public payable {
- assertEq(Kali(kali).tokenId(), 0);
- }
-
- function testDaoURI() public payable {
- assertEq(Kali(kali).daoURI(), "DAO");
- }
-
- function testVotingPeriod() public payable {
- assertEq(Kali(kali).votingPeriod(), 1 days);
- }
-
- function testGracePeriod() public payable {
- assertEq(Kali(kali).gracePeriod(), 0);
- }
-
- function testQuorum() public payable {
- assertEq(Kali(kali).quorum(), 20);
- }
-
- function testSupermajority() public payable {
- assertEq(Kali(kali).supermajority(), 52);
- }
-
- function testSupportsInterface() public payable {
- assert(Kali(kali).supportsInterface(0x01ffc9a7));
- assert(Kali(kali).supportsInterface(0x150b7a02));
- assert(Kali(kali).supportsInterface(0x4e2312e0));
- }
-
- /// -----------------------------------------------------------------------
- /// Kali Receiver Tests
- /// -----------------------------------------------------------------------
-
- function testERC20Receiver() public payable {
- mockDai.transfer(kali, 1 ether);
- }
-
- function testERC721Receiver() public payable {
- mockNFT.transferFrom(address(this), kali, 1);
- }
-
- function testERC1155Receiver() public payable {
- mock1155.safeTransferFrom(address(this), kali, 1, 1, "");
- }
-
- function testERC1155BatchReceiver() public payable {
- uint256[] memory ids = new uint256[](2);
- ids[0] = 1;
- ids[1] = 1;
-
- uint256[] memory amounts = new uint256[](2);
- amounts[0] = 1;
- amounts[1] = 0;
-
- mock1155.safeBatchTransferFrom(address(this), kali, ids, amounts, "");
- }
-
- /// -----------------------------------------------------------------------
- /// Kali Proposal Tests
- /// -----------------------------------------------------------------------
-
- function testProposalCreation() public payable {
- vm.warp(block.timestamp + 1 days);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Check proposal creation.
- (, , , uint40 creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
- assert(block.timestamp != 0);
-
- // Check proposal hash.
- (, bytes32 digest, , , , ) = Kali(kali).proposals(proposalId);
- bytes32 proposalHash = keccak256(
- abi.encode(call, ProposalType.CALL, "test")
- );
- assertEq(digest, proposalHash);
- }
-
- function testFailProposalCreation() public payable {
- vm.warp(block.timestamp + 1 days);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 0;
- call[0].data = "";
-
- vm.prank(alice);
- vm.expectRevert(PeriodBounds.selector);
- Kali(kali).propose(call, ProposalType.VPERIOD, "test");
-
- call[0].value = 366;
-
- vm.prank(alice);
- vm.expectRevert(PeriodBounds.selector);
- Kali(kali).propose(call, ProposalType.VPERIOD, "test");
-
- vm.prank(alice);
- vm.expectRevert(PeriodBounds.selector);
- Kali(kali).propose(call, ProposalType.GPERIOD, "test");
-
- vm.prank(alice);
- vm.expectRevert(QuorumMax.selector);
- Kali(kali).propose(call, ProposalType.QUORUM, "test");
-
- vm.prank(alice);
- vm.expectRevert(SupermajorityBounds.selector);
- Kali(kali).propose(call, ProposalType.SUPERMAJORITY, "test");
-
- call[0].value = 50;
-
- vm.prank(alice);
- vm.expectRevert(SupermajorityBounds.selector);
- Kali(kali).propose(call, ProposalType.SUPERMAJORITY, "test");
-
- vm.prank(alice);
- vm.expectRevert(TypeBounds.selector);
- Kali(kali).propose(call, ProposalType.TYPE, "test");
-
- call[0].value = 1;
- call[0].value = 50;
-
- vm.prank(alice);
- vm.expectRevert(TypeBounds.selector);
- Kali(kali).propose(call, ProposalType.TYPE, "test");
- }
-
- function testProposal() public payable {
- // Check initial ETH.
- assertEq(kali.balance, 10 ether);
- assertEq(alice.balance, 0 ether);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, uint40 creationTime) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- assertEq(creationTime, block.timestamp);
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 2);
- assertEq(noVotes, 0);
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.CALL,
- "test"
- );
- assert(passed);
-
- // Check proposal state.
- (bool didPass, bool processed) = Kali(kali).proposalStates(proposalId);
- assert(didPass);
- assert(processed);
-
- // Check ETH was sent.
- assertEq(kali.balance, 9 ether);
- assertEq(alice.balance, 1 ether);
- }
-
- function testProposalRepeatProcessingFail() public payable {
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 2);
- assertEq(noVotes, 0);
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.CALL,
- "test"
- );
- assert(passed);
-
- // Process proposal.
- vm.expectRevert(InvalidProposal.selector);
- Kali(kali).processProposal(proposalId, call, ProposalType.CALL, "test");
- }
-
- function testFailProposalRepeatVoting() public payable {
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, false, "");
- vm.expectRevert(AlreadyVoted.selector);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 1);
- assertEq(noVotes, 1);
- }
-
- function testProposalSponsorship() public payable {
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as dave.
- vm.prank(dave);
- (uint256 proposalId, uint40 creationTime) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Check proposal creation time is zero.
- assertEq(creationTime, 0);
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, 0);
-
- // Sponsor as alice.
- vm.prank(alice);
- Kali(kali).sponsorProposal(proposalId);
-
- // Check proposal creation time is current.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
-
- // Check can't sponsor after.
- vm.prank(alice);
- vm.expectRevert(Sponsored.selector);
- Kali(kali).sponsorProposal(proposalId);
-
- // Propose as dave.
- vm.prank(dave);
- (proposalId, creationTime) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Check proposal creation time is zero.
- assertEq(creationTime, 0);
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, 0);
-
- // Sponsor as alice.
- vm.prank(alice);
- Kali(kali).sponsorProposal(proposalId);
-
- // Check prev prop storage.
- (uint256 prevProposal, , , , , ) = Kali(kali).proposals(proposalId);
- assertEq(prevProposal, proposalId - 1);
-
- // Propose as alice.
- vm.prank(alice);
- (proposalId, creationTime) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Check proposal creation time.
- assertEq(creationTime, block.timestamp);
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
-
- // Propose as alice.
- vm.prank(alice);
- (proposalId, ) = Kali(kali).propose(call, ProposalType.CALL, "test");
-
- // Check can't sponsor after.
- vm.prank(alice);
- vm.expectRevert(Sponsored.selector);
- Kali(kali).sponsorProposal(proposalId);
- }
-
- function testProposalCancellation() public payable {
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as dave and cancel.
- vm.prank(dave);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
- vm.prank(dave);
- Kali(kali).cancelProposal(proposalId);
- vm.expectRevert(Unauthorized.selector);
- Kali(kali).cancelProposal(proposalId);
-
- // Propose as dave and fail after sponsored.
- vm.prank(dave);
- (proposalId, ) = Kali(kali).propose(call, ProposalType.CALL, "test");
-
- vm.prank(alice);
- Kali(kali).sponsorProposal(proposalId);
-
- vm.prank(dave);
- vm.expectRevert(Sponsored.selector);
- Kali(kali).cancelProposal(proposalId);
- }
-
- function testProposalVoteStored() public payable {
- // Check initial alice DAO (0) balance.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.BURN,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 2);
- assertEq(noVotes, 0);
-
- assert(Kali(kali).voted(1, alice));
- assert(Kali(kali).voted(1, bob));
- }
-
- function testMintProposal() public payable {
- // Check initial alice DAO (0) balance.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.MINT,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 2);
- assertEq(noVotes, 0);
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.MINT,
- "test"
- );
- assert(passed);
-
- // Check proposal state.
- (bool didPass, bool processed) = Kali(kali).proposalStates(proposalId);
- assert(didPass);
- assert(processed);
-
- // Check processed alice DAO (0) balance.
- assertEq(Keep(keep).balanceOf(alice, 0), 2);
- }
-
- function testMultiMintProposal() public payable {
- // Check initial DAO (0) balances.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
- assertEq(Keep(keep).balanceOf(bob, 0), 1);
- assertEq(Keep(keep).balanceOf(charlie, 0), 1);
- assertEq(Keep(keep).balanceOf(dave, 0), 0);
-
- // Setup proposal.
- Call[] memory call = new Call[](4);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1;
- call[0].data = "";
-
- call[1].op = Operation.call;
- call[1].to = bob;
- call[1].value = 1;
- call[1].data = "";
-
- call[2].op = Operation.call;
- call[2].to = charlie;
- call[2].value = 1;
- call[2].data = "";
-
- call[3].op = Operation.call;
- call[3].to = dave;
- call[3].value = 1;
- call[3].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.MINT,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal votes.
- (, , , , uint216 yesVotes, uint216 noVotes) = Kali(kali).proposals(
- proposalId
- );
- assertEq(yesVotes, 2);
- assertEq(noVotes, 0);
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.MINT,
- "test"
- );
- assert(passed);
-
- // Check proposal state.
- (bool didPass, bool processed) = Kali(kali).proposalStates(proposalId);
- assert(didPass);
- assert(processed);
-
- // Check processed DAO (0) balances.
- assertEq(Keep(keep).balanceOf(alice, 0), 2);
- assertEq(Keep(keep).balanceOf(bob, 0), 2);
- assertEq(Keep(keep).balanceOf(charlie, 0), 2);
- assertEq(Keep(keep).balanceOf(dave, 0), 1);
- }
-
- function testBurnProposal() public payable {
- // Check initial alice DAO (0) balance.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.BURN,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.BURN,
- "test"
- );
- assert(passed);
-
- // Check processed alice DAO (0) balance.
- assertEq(Keep(keep).balanceOf(alice, 0), 0);
- }
-
- function testMultiBurnProposal() public payable {
- // Check initial DAO (0) balances.
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
- assertEq(Keep(keep).balanceOf(bob, 0), 1);
-
- // Setup proposal.
- Call[] memory call = new Call[](2);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1;
- call[0].data = "";
-
- call[1].op = Operation.call;
- call[1].to = bob;
- call[1].value = 1;
- call[1].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.BURN,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.BURN,
- "test"
- );
- assert(passed);
-
- // Check processed DAO (0) balances.
- assertEq(Keep(keep).balanceOf(alice, 0), 0);
- assertEq(Keep(keep).balanceOf(bob, 0), 0);
- }
-
- function testCallProposal() public payable {
- // Check initial alice Dai balance.
- assertEq(mockDai.balanceOf(alice), 0);
- assertEq(mockDai.balanceOf(kali), 1_000_000_000 ether);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- bytes memory data = abi.encodeCall(
- mockDai.transfer,
- (alice, 1_000_000_000 ether)
- );
-
- call[0].op = Operation.call;
- call[0].to = address(mockDai);
- call[0].value = 0;
- call[0].data = data;
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.CALL,
- "test"
- );
- assert(passed);
-
- // Check processed alice Dai balance.
- assertEq(mockDai.balanceOf(alice), 1_000_000_000 ether);
- assertEq(mockDai.balanceOf(kali), 0);
- }
-
- function testMultiCallProposal() public payable {
- // Check initial Dai balances.
- assertEq(mockDai.balanceOf(alice), 0);
- assertEq(mockDai.balanceOf(bob), 0);
- assertEq(mockDai.balanceOf(kali), 1_000_000_000 ether);
-
- // Setup proposal.
- Call[] memory call = new Call[](2);
-
- bytes memory data = abi.encodeCall(
- mockDai.transfer,
- (alice, 500_000_000 ether)
- );
- bytes memory data1 = abi.encodeCall(
- mockDai.transfer,
- (bob, 500_000_000 ether)
- );
-
- call[0].op = Operation.call;
- call[0].to = address(mockDai);
- call[0].value = 0;
- call[0].data = data;
-
- call[1].op = Operation.call;
- call[1].to = address(mockDai);
- call[1].value = 0;
- call[1].data = data1;
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.CALL,
- "test"
- );
- assert(passed);
-
- // Check processed Dai balances.
- assertEq(mockDai.balanceOf(alice), 500_000_000 ether);
- assertEq(mockDai.balanceOf(bob), 500_000_000 ether);
- assertEq(mockDai.balanceOf(kali), 0);
- }
-
- function testVotingPeriodProposal() public payable {
- // Check initial voting period.
- assertEq(Kali(kali).votingPeriod(), 1 days);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 2 days;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.VPERIOD,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.VPERIOD,
- "test"
- );
- assert(passed);
-
- // Check processed voting period.
- assertEq(Kali(kali).votingPeriod(), 2 days);
- }
-
- function testGracePeriodProposal() public payable {
- // Check initial grace period.
- assertEq(Kali(kali).gracePeriod(), 0);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 1 days;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.GPERIOD,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.GPERIOD,
- "test"
- );
- assert(passed);
-
- // Check processed grace period.
- assertEq(Kali(kali).gracePeriod(), 1 days);
- }
-
- function testQuorumProposal() public payable {
- // Check initial quorum.
- assertEq(Kali(kali).quorum(), 20);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 69;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.QUORUM,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.QUORUM,
- "test"
- );
- assert(passed);
-
- // Check processed quorum.
- assertEq(Kali(kali).quorum(), 69);
- }
-
- function testSupermajorityProposal() public payable {
- // Check initial supermajority.
- assertEq(Kali(kali).supermajority(), 52);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 88;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.SUPERMAJORITY,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.SUPERMAJORITY,
- "test"
- );
- assert(passed);
-
- // Check processed supermajority.
- assertEq(Kali(kali).supermajority(), 88);
- }
-
- function testTypeProposal() public payable {
- // Check initial type settings.
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.CALL) ==
- VoteType.SIMPLE_MAJORITY_QUORUM_REQUIRED
- );
-
- // Setup proposal.
- Call[] memory call = new Call[](2);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 2;
- call[0].data = "";
-
- call[1].op = Operation.call;
- call[1].to = address(0);
- call[1].value = 2;
- call[1].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.TYPE,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.TYPE,
- "test"
- );
- assert(passed);
-
- // Check processed type settings.
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.CALL) ==
- VoteType.SUPERMAJORITY_QUORUM_REQUIRED
- );
- }
-
- function testPauseProposal() public payable {
- // Check initial pause settings.
- assert(!Keep(keep).transferable(0));
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 2;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.PAUSE,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.PAUSE,
- "test"
- );
- assert(passed);
-
- // Check initial pause settings.
- assert(Keep(keep).transferable(0));
- }
-
- function testDeleteProposal() public payable {
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 0;
- call[0].data = "";
-
- // -- 1 -- //
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.URI,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // -- 2 -- //
-
- call[0].value = proposalId;
-
- // Propose as alice.
- vm.prank(alice);
- (proposalId, ) = Kali(kali).propose(call, ProposalType.ESCAPE, "test");
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Check proposal status before deletion.
- (, , , uint40 creationTime, , ) = Kali(kali).proposals(proposalId - 1);
- assertEq(creationTime, block.timestamp - 1 days);
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.ESCAPE,
- "test"
- );
- assert(passed);
-
- // Check proposal deletion.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId - 1);
- assertEq(creationTime, 0);
- }
-
- function testURIProposal() public payable {
- // Check initial uri settings.
- assertEq(Kali(kali).daoURI(), "DAO");
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = address(0);
- call[0].value = 0;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.URI,
- "test"
- );
-
- // Skip ahead in voting period.
- vm.warp(block.timestamp + 12 hours);
-
- // Vote as alice.
- vm.prank(alice);
- Kali(kali).vote(proposalId, true, "");
-
- // Vote as bob.
- vm.prank(bob);
- Kali(kali).vote(proposalId, true, "");
-
- // Process proposal.
- bool passed = Kali(kali).processProposal(
- proposalId,
- call,
- ProposalType.URI,
- "test"
- );
- assert(passed);
-
- // Check processed uri settings.
- assertEq(Kali(kali).daoURI(), "test");
- }
-
- /// -----------------------------------------------------------------------
- /// Kali Extension Tests
- /// -----------------------------------------------------------------------
-
- function testFailNotAuthorizedExtension() public payable {
- assert(!Kali(kali).extensions(alice));
- assertEq(alice.balance, 0);
-
- Call memory call;
-
- call.op = Operation.call;
- call.to = alice;
- call.value = 1 ether;
- call.data = "";
-
- vm.prank(alice);
- Kali(kali).relay(call);
-
- assertEq(alice.balance, 0);
- }
-
- function testExtensionRelay() public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- assertEq(alice.balance, 0);
-
- Call memory call;
-
- call.op = Operation.call;
- call.to = alice;
- call.value = 1 ether;
- call.data = "";
-
- vm.prank(alice);
- Kali(kali).relay(call);
-
- assertEq(alice.balance, 1 ether);
- }
-
- function testExtensionMint() public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
-
- vm.prank(alice);
- Kali(kali).mint(KeepTokenManager(keep), alice, 0, 1, "");
-
- assertEq(Keep(keep).balanceOf(alice, 0), 2);
- }
-
- function testExtensionBurn() public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- assertEq(Keep(keep).balanceOf(alice, 0), 1);
-
- vm.prank(alice);
- Kali(kali).burn(KeepTokenManager(keep), alice, 0, 1);
-
- assertEq(Keep(keep).balanceOf(alice, 0), 0);
- }
-
- function testExtensionSetTransferability(bool on) public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- assert(!Keep(keep).transferable(0));
-
- vm.prank(alice);
- Kali(kali).setTransferability(KeepTokenManager(keep), 0, on);
-
- assertEq(Keep(keep).transferable(0), on);
- }
-
- function testExtensionSetExtension(
- address extension,
- bool on
- ) public payable {
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- vm.prank(alice);
- Kali(kali).setExtension(extension, on);
-
- assertEq(Kali(kali).extensions(extension), on);
- }
-
- function testExtensionSetURI(string calldata uri) public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- vm.prank(alice);
- Kali(kali).setURI(uri);
-
- assertEq(Kali(kali).daoURI(), uri);
- }
-
- function testExtensionDeleteProposal() public payable {
- vm.warp(block.timestamp + 1 days);
-
- // Setup proposal.
- Call[] memory call = new Call[](1);
-
- call[0].op = Operation.call;
- call[0].to = alice;
- call[0].value = 1 ether;
- call[0].data = "";
-
- // Propose as alice.
- vm.prank(alice);
- (uint256 proposalId, ) = Kali(kali).propose(
- call,
- ProposalType.CALL,
- "test"
- );
-
- // Check proposal creation.
- (, , , uint40 creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
-
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- // Delete.
- vm.prank(alice);
- Kali(kali).deleteProposal(1);
-
- // Check proposal deletion.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, 0);
-
- // Check proposal processed state.
- (bool didPass, bool processed) = Kali(kali).proposalStates(proposalId);
- assert(!didPass);
- assert(processed);
-
- // Propose as alice.
- vm.prank(alice);
- (proposalId, ) = Kali(kali).propose(call, ProposalType.CALL, "test");
-
- // Check proposal creation.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
-
- // Delete.
- vm.prank(kali);
- Kali(kali).deleteProposal(2);
-
- // Check proposal deletion.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, 0);
-
- // Attempt repeat delete.
- vm.prank(kali);
- vm.expectRevert(InvalidProposal.selector);
- Kali(kali).deleteProposal(2);
-
- // Propose as alice.
- vm.prank(alice);
- (proposalId, ) = Kali(kali).propose(call, ProposalType.CALL, "test");
-
- // Check proposal creation.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
-
- // Delete.
- vm.prank(bob);
- vm.expectRevert(Unauthorized.selector);
- Kali(kali).deleteProposal(3);
-
- // Check proposal maintenance.
- (, , , creationTime, , ) = Kali(kali).proposals(proposalId);
- assertEq(creationTime, block.timestamp);
- }
-
- function testExtensionUpdateGovSettings() public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- uint256[2] memory setting;
- setting[0] = 1;
- setting[1] = 1;
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(2 days, 1 days, 42, 69, setting);
-
- assertEq(Kali(kali).votingPeriod(), 2 days);
- assertEq(Kali(kali).gracePeriod(), 1 days);
- assertEq(Kali(kali).quorum(), 42);
- assertEq(Kali(kali).supermajority(), 69);
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.BURN) ==
- VoteType.SIMPLE_MAJORITY
- );
- }
-
- function testExtensionUpdateGovSettingsInvalid() public payable {
- assert(!Kali(kali).extensions(alice));
- vm.prank(kali);
- Kali(kali).setExtension(alice, true);
-
- assert(Kali(kali).extensions(alice));
-
- uint256[2] memory setting;
- setting[0] = 1;
- setting[1] = 1;
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(0, 1 days, 42, 69, setting);
-
- assertEq(Kali(kali).votingPeriod(), 1 days);
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 1 days, 42, 69, setting);
-
- assertEq(Kali(kali).votingPeriod(), 1 days);
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(1 days, 366 days, 42, 69, setting);
-
- assertEq(Kali(kali).gracePeriod(), 1 days);
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 69, setting);
-
- assertEq(Kali(kali).quorum(), 42);
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 50, setting);
-
- assertEq(Kali(kali).supermajority(), 69);
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 101, setting);
-
- assertEq(Kali(kali).supermajority(), 69);
-
- setting[1] = 3;
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 101, setting);
-
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.BURN) ==
- VoteType.SUPERMAJORITY
- );
-
- setting[0] = 100;
- setting[1] = 1;
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 101, setting);
-
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.BURN) ==
- VoteType.SUPERMAJORITY
- );
-
- setting[0] = 1;
- setting[1] = 100;
-
- vm.prank(alice);
- Kali(kali).updateGovSettings(366 days, 366 days, 101, 101, setting);
-
- assert(
- Kali(kali).proposalVoteTypes(ProposalType.BURN) ==
- VoteType.SUPERMAJORITY
- );
- }
-}
diff --git a/test/Keep.t.sol b/test/Keep.t.sol
index 781dfd26..e5da2163 100644
--- a/test/Keep.t.sol
+++ b/test/Keep.t.sol
@@ -1,12 +1,12 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
/// @dev Core.
import {KeepToken, Operation, Call, Signature, Keep} from "../src/Keep.sol";
import {KeepFactory} from "../src/KeepFactory.sol";
/// @dev Extensions.
-import {URIFetcher} from "../src/extensions/metadata/URIFetcher.sol";
+import {Validator} from "../src/extensions/validate/Validator.sol";
/// @dev Mocks.
import {MockERC20} from "@solady/test/utils/mocks/MockERC20.sol";
@@ -18,13 +18,16 @@ import {MockUnsafeERC1155Receiver} from "./utils/mocks/MockUnsafeERC1155Receiver
/// @dev Test framework.
import "@std/Test.sol";
-contract KeepTest is Keep(this), Test {
+contract KeepTest is Keep(Keep(address(0))), Test {
/// -----------------------------------------------------------------------
/// Keep Storage/Logic
/// -----------------------------------------------------------------------
using stdStorage for StdStorage;
+ bytes32 internal constant name1 = keccak256("TEST1");
+ bytes32 internal constant name2 = keccak256("TEST2");
+
address internal keepAddr;
address internal keepAddrRepeat;
@@ -33,9 +36,9 @@ contract KeepTest is Keep(this), Test {
KeepFactory internal factory;
- URIFetcher internal mockUriFetcher;
- URIFetcher internal uriRemote;
- URIFetcher internal uriRemoteNew;
+ address internal mockUriValidator;
+ Validator internal uriRemote;
+ Validator internal uriRemoteNew;
MockERC20 internal mockDai;
MockERC721 internal mockNFT;
@@ -45,14 +48,6 @@ contract KeepTest is Keep(this), Test {
uint256 internal chainId;
- uint256 internal immutable SIGNER_KEY = uint32(keep.execute.selector);
-
- bytes32 internal constant name1 =
- 0x5445535400000000000000000000000000000000000000000000000000000000;
-
- bytes32 internal constant name2 =
- 0x5445535432000000000000000000000000000000000000000000000000000000;
-
bytes32 internal constant PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address operator,bool approved,uint256 nonce,uint256 deadline)"
@@ -111,7 +106,7 @@ contract KeepTest is Keep(this), Test {
address to,
uint256 value,
bytes memory data,
- uint120 nonce,
+ uint256 nonce,
bytes32 domainSeparator
) internal pure returns (bytes32) {
return
@@ -122,7 +117,7 @@ contract KeepTest is Keep(this), Test {
keccak256(
abi.encode(
keccak256(
- "Execute(uint8 op,address to,uint256 value,bytes data,uint120 nonce)"
+ "Execute(uint8 op,address to,uint256 value,bytes data,uint256 nonce)"
),
op,
to,
@@ -207,9 +202,7 @@ contract KeepTest is Keep(this), Test {
function setUp() public payable {
// Initialize templates.
- mockUriFetcher = new URIFetcher();
-
- keep = new Keep(Keep(address(mockUriFetcher)));
+ mockUriValidator = address(new Validator());
mockDai = new MockERC20("Dai", "DAI", 18);
mockNFT = new MockERC721();
@@ -225,7 +218,7 @@ contract KeepTest is Keep(this), Test {
mock1155.mint(address(this), 1, 1, "");
// Create the factory.
- factory = new KeepFactory(address(keep));
+ factory = new KeepFactory();
// Create the Signer[] for setup.
address[] memory setupSigners = new address[](2);
@@ -238,14 +231,14 @@ contract KeepTest is Keep(this), Test {
// Initialize Keep from factory.
// The factory is fully tested in KeepFactory.t.sol.
- keepAddr = factory.determineKeep(name1);
+ (keepAddr, ) = factory.determineKeep(name1);
keep = Keep(keepAddr);
factory.deployKeep(name1, calls, setupSigners, 2);
// Mint mock smart wallet a signer ID key.
vm.prank(address(keep));
- keep.mint(address(mockERC1271Wallet), SIGNER_KEY, 1, "");
+ keep.mint(address(mockERC1271Wallet), EXEC_KEY, 1, "");
// Store chainId.
chainId = block.chainid;
@@ -289,6 +282,7 @@ contract KeepTest is Keep(this), Test {
keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
}
+ /*
/// @dev Check setup conditions.
/*function testURISetup() public payable {
@@ -337,7 +331,7 @@ contract KeepTest is Keep(this), Test {
assertEq(keep.uri(1), "");
vm.prank(address(alice));
- mockUriFetcher.setURIRemoteFetcher(uriRemoteNew);
+ mockUriValidator.setURIRemoteValidator(uriRemoteNew);
vm.prank(address(alice));
@@ -353,26 +347,26 @@ contract KeepTest is Keep(this), Test {
assertEq(keep.uri(1), "");
}*/
- function testSignerSetup() public payable {
- // Check users.
- assert(keep.balanceOf(alice, SIGNER_KEY) == 1);
- assert(keep.balanceOf(bob, SIGNER_KEY) == 1);
- assert(keep.balanceOf(charlie, SIGNER_KEY) == 0);
+ // function testSignerSetup() public payable {
+ // // Check users.
+ // assert(keep.balanceOf(alice, EXEC_KEY) == 1);
+ // assert(keep.balanceOf(bob, EXEC_KEY) == 1);
+ // assert(keep.balanceOf(charlie, EXEC_KEY) == 0);
- // Also check smart wallet.
- assert(keep.balanceOf(address(mockERC1271Wallet), SIGNER_KEY) == 1);
+ // // Also check smart wallet.
+ // assert(keep.balanceOf(address(mockERC1271Wallet), EXEC_KEY) == 1);
- // Check supply.
- assert(keep.totalSupply(SIGNER_KEY) == 3);
- assert(keep.totalSupply(42069) == 0);
- }
+ // // Check supply.
+ // assert(keep.totalSupply(EXEC_KEY) == 3);
+ // assert(keep.totalSupply(42069) == 0);
+ // }
/// @notice Check setup errors.
function testCannotRepeatKeepSetup() public payable {
- keepRepeat = new Keep(Keep(address(mockUriFetcher)));
+ keepRepeat = new Keep(Keep(mockUriValidator));
- keepAddrRepeat = factory.determineKeep(name2);
+ (keepAddrRepeat, ) = factory.determineKeep(name2);
keepRepeat = Keep(keepAddrRepeat);
factory.deployKeep(name2, calls, signers, 2);
@@ -386,7 +380,7 @@ contract KeepTest is Keep(this), Test {
}
function testCannotSetupWithExcessiveQuorum() public payable {
- vm.expectRevert(QuorumOverSupply.selector);
+ vm.expectRevert(InvalidThreshold.selector);
factory.deployKeep(name2, calls, signers, 3);
}
@@ -395,7 +389,7 @@ contract KeepTest is Keep(this), Test {
outOfOrderSigners[0] = alice > bob ? alice : bob;
outOfOrderSigners[1] = alice > bob ? bob : alice;
- vm.expectRevert(InvalidSig.selector);
+ vm.expectRevert(InvalidArray.selector);
factory.deployKeep(name2, calls, outOfOrderSigners, 2);
}
@@ -407,21 +401,21 @@ contract KeepTest is Keep(this), Test {
assertEq(keep.name(), string(abi.encodePacked(name1)));
}
- function testKeepNonce() public view {
- assert(keep.nonce() == 1);
- }
+ // function testKeepNonce() public view {
+ // assert(keep.nonce() == 1);
+ // }
- function testUserNonce() public view {
- assert(keep.nonces(alice) == 0);
- }
+ // function testUserNonce() public view {
+ // assert(keep.nonces(alice) == 0);
+ // }
function testQuorum() public payable {
- assert(keep.quorum() == 2);
+ assert(keep.quorum(EXEC_KEY) == 2);
vm.prank(address(keep));
- keep.setQuorum(3);
+ keep.setQuorum(EXEC_KEY, 3);
- assert(keep.quorum() == 3);
+ assert(keep.quorum(EXEC_KEY) == 3);
}
function testBalanceOf() public {
@@ -434,12 +428,12 @@ contract KeepTest is Keep(this), Test {
}
function testBalanceOfSigner() public {
- assert(keep.balanceOf(charlie, SIGNER_KEY) == 0);
+ assert(keep.balanceOf(charlie, EXEC_KEY) == 0);
vm.prank(address(keep));
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ keep.mint(charlie, EXEC_KEY, 1, "");
- assert(keep.balanceOf(charlie, SIGNER_KEY) == 1);
+ assert(keep.balanceOf(charlie, EXEC_KEY) == 1);
}
function testBalanceOfBatch() public {
@@ -449,7 +443,7 @@ contract KeepTest is Keep(this), Test {
uint256[] memory ids = new uint256[](2);
ids[0] = 0;
- ids[1] = SIGNER_KEY;
+ ids[1] = EXEC_KEY;
uint256[] memory balances = new uint256[](2);
balances = keep.balanceOfBatch(owners, ids);
@@ -475,35 +469,35 @@ contract KeepTest is Keep(this), Test {
ids[0] = 0;
uint256[] memory balances = new uint256[](2);
- vm.expectRevert(LengthMismatch.selector);
+ vm.expectRevert(InvalidArray.selector);
balances = keep.balanceOfBatch(owners, ids);
}
- function testTotalSupply() public {
- assert(keep.totalSupply(0) == 0);
+ // function testTotalSupply() public {
+ // assert(keep.totalSupply(0) == 0);
- vm.prank(address(keep));
- keep.mint(alice, 0, 1, "");
+ // vm.prank(address(keep));
+ // keep.mint(alice, 0, 1, "");
- assert(keep.totalSupply(0) == 1);
- }
+ // assert(keep.totalSupply(0) == 1);
+ // }
- function testTotalSignerSupply() public {
- assert(keep.totalSupply(SIGNER_KEY) == 3);
+ // function testTotalSignerSupply() public {f
+ // assert(keep.totalSupply(EXEC_KEY) == 3);
- vm.prank(address(keep));
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ // vm.prank(address(keep));
+ // keep.mint(charlie, EXEC_KEY, 1, "");
- assert(keep.totalSupply(SIGNER_KEY) == 4);
- }
+ // assert(keep.totalSupply(EXEC_KEY) == 4);
+ // }
- function testSupportsInterface() public {
- assertTrue(keep.supportsInterface(0x150b7a02));
- assertTrue(keep.supportsInterface(0x4e2312e0));
- assertTrue(keep.supportsInterface(0x01ffc9a7));
- assertTrue(keep.supportsInterface(0xd9b67a26));
- assertTrue(keep.supportsInterface(0x0e89341c));
- }
+ // function testSupportsInterface() public {
+ // assertTrue(keep.supportsInterface(0x150b7a02));
+ // assertTrue(keep.supportsInterface(0x4e2312e0));
+ // assertTrue(keep.supportsInterface(0x01ffc9a7));
+ // assertTrue(keep.supportsInterface(0xd9b67a26));
+ // assertTrue(keep.supportsInterface(0x0e89341c));
+ // }
function testNoKeepKeyCollision() public view {
assert(
@@ -525,22 +519,22 @@ contract KeepTest is Keep(this), Test {
/// @dev Check receivers.
- function testReceiveETH() public payable {
- (bool sent, ) = address(keep).call{value: 5 ether}("");
- assert(sent);
- // We check addition to setup balance.
- assert(address(keep).balance == 10 ether);
- }
+ // function testReceiveETH() public payable {
+ // (bool sent, ) = address(keep).call{value: 5 ether}("");
+ // assert(sent);
+ // // We check addition to setup balance.
+ // assert(address(keep).balance == 10 ether);
+ // }
- function testReceiveERC721() public payable {
- mockNFT.safeTransferFrom(address(this), address(keep), 1);
- assert(mockNFT.ownerOf(1) == address(keep));
- }
+ // function testReceiveERC721() public payable {
+ // mockNFT.safeTransferFrom(address(this), address(keep), 1);
+ // assert(mockNFT.ownerOf(1) == address(keep));
+ // }
- function testReceiveERC1155() public payable {
- mock1155.safeTransferFrom(address(this), address(keep), 1, 1, "");
- assert(mock1155.balanceOf(address(keep), 1) == 1);
- }
+ // function testReceiveERC1155() public payable {
+ // mock1155.safeTransferFrom(address(this), address(keep), 1, 1, "");
+ // assert(mock1155.balanceOf(address(keep), 1) == 1);
+ // }
function testReceiveBatchERC1155() public payable {
uint256[] memory ids = new uint256[](1);
@@ -593,58 +587,58 @@ contract KeepTest is Keep(this), Test {
/// @dev Check call execution.
- function testExecuteTokenCallWithRole() public payable {
- // Mint executor role.
- vm.prank(address(keep));
- keep.mint(alice, uint32(keep.multirelay.selector), 1, "");
+ // function testExecuteTokenCallWithRole() public payable {
+ // // Mint executor role.
+ // vm.prank(address(keep));
+ // keep.mint(alice, uint32(keep.multirelay.selector), 1, "");
- // Mock execution.
- bytes memory data = abi.encodeCall(mockDai.transfer, (alice, 100));
+ // // Mock execution.
+ // bytes memory data = abi.encodeCall(mockDai.transfer, (alice, 100));
- Call[] memory call = new Call[](1);
+ // Call[] memory call = new Call[](1);
- call[0].op = Operation.call;
- call[0].to = address(mockDai);
- call[0].value = 0;
- call[0].data = data;
+ // call[0].op = Operation.call;
+ // call[0].to = address(mockDai);
+ // call[0].value = 0;
+ // call[0].data = data;
- uint256 balanceBefore = mockDai.balanceOf(alice);
+ // uint256 balanceBefore = mockDai.balanceOf(alice);
- vm.prank(alice);
- keep.multirelay(call);
+ // vm.prank(alice);
+ // keep.multirelay(call);
- assert(mockDai.balanceOf(alice) == balanceBefore + 100);
- }
+ // assert(mockDai.balanceOf(alice) == balanceBefore + 100);
+ // }
- function testExecuteTokenCallWithSignatures() public payable {
- bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
+ // function testExecuteTokenCallWithSignatures() public payable {
+ // bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
- Signature[] memory sigs = new Signature[](2);
+ // Signature[] memory sigs = new Signature[](2);
- Signature memory aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
+ // Signature memory aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
- Signature memory bobSig = signExecution(
- bob,
- bobsPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
+ // Signature memory bobSig = signExecution(
+ // bob,
+ // bobsPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
- sigs[0] = bobSig;
- sigs[1] = aliceSig;
+ // sigs[0] = bobSig;
+ // sigs[1] = aliceSig;
- // Execute tx.
- keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
- }
+ // // Execute tx.
+ // keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
+ // }
function testExecuteTokenCallWithContractSignatures() public payable {
bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
@@ -737,7 +731,7 @@ contract KeepTest is Keep(this), Test {
assert(keep.nonce() == 2);
// Confirm revert for stale nonce.
- vm.expectRevert(InvalidSig.selector);
+ vm.expectRevert(InvalidSignature.selector);
keep.execute(Operation.call, alice, 1 ether, "", sigs);
}
@@ -790,399 +784,399 @@ contract KeepTest is Keep(this), Test {
);
}
- function testExecuteCreateCall() public payable {
- bytes memory tx_data = type(MockERC1155).creationCode;
+ // function testExecuteCreateCall() public payable {
+ // bytes memory tx_data = type(MockERC1155).creationCode;
- Signature[] memory sigs = new Signature[](2);
+ // Signature[] memory sigs = new Signature[](2);
- Signature memory aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.create,
- address(0),
- 0,
- tx_data
- );
+ // Signature memory aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.create,
+ // address(0),
+ // 0,
+ // tx_data
+ // );
- Signature memory bobSig = signExecution(
- bob,
- bobsPk,
- Operation.create,
- address(0),
- 0,
- tx_data
- );
+ // Signature memory bobSig = signExecution(
+ // bob,
+ // bobsPk,
+ // Operation.create,
+ // address(0),
+ // 0,
+ // tx_data
+ // );
- sigs[0] = bobSig;
- sigs[1] = aliceSig;
+ // sigs[0] = bobSig;
+ // sigs[1] = aliceSig;
- // Execute tx.
- keep.execute(Operation.create, address(0), 0, tx_data, sigs);
- }
+ // // Execute tx.
+ // keep.execute(Operation.create, address(0), 0, tx_data, sigs);
+ // }
/// @dev Check execution errors.
- function testCannotExecuteWithImproperSignatures() public payable {
- bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
-
- Signature[] memory sigs = new Signature[](2);
-
- Signature memory aliceSig;
- Signature memory charlieSig;
-
- aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- charlieSig = signExecution(
- bob,
- charliesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- sigs[0] = alice > charlie ? charlieSig : aliceSig;
- sigs[1] = alice > charlie ? aliceSig : charlieSig;
-
- // Execute tx.
- vm.expectRevert(InvalidSig.selector);
- keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
- }
-
- function testCannotExecuteWithSignaturesOutOfOrder() public payable {
- bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
-
- Signature[] memory sigs = new Signature[](2);
-
- Signature memory aliceSig;
- Signature memory bobSig;
-
- aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- bobSig = signExecution(
- bob,
- bobsPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- sigs[0] = alice > bob ? aliceSig : bobSig;
- sigs[1] = alice > bob ? bobSig : aliceSig;
-
- // Execute tx.
- vm.expectRevert(InvalidSig.selector);
- keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
- }
-
- function testCannotExecuteWithSignaturesRepeated() public payable {
- bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
-
- Signature[] memory sigs = new Signature[](2);
-
- Signature memory aliceSig;
-
- aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- sigs[0] = aliceSig;
- sigs[1] = aliceSig;
-
- // Execute tx.
- vm.expectRevert(InvalidSig.selector);
- keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
- }
-
- function testCannotExecuteWithNullSignatures() public payable {
- bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
-
- Signature[] memory sigs = new Signature[](2);
-
- Signature memory aliceSig;
- Signature memory nullSig;
-
- aliceSig = signExecution(
- alice,
- alicesPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- nullSig = signExecution(
- nully,
- nullPk,
- Operation.call,
- address(mockDai),
- 0,
- tx_data
- );
-
- sigs[0] = alice > nully ? nullSig : aliceSig;
- sigs[1] = alice > nully ? aliceSig : nullSig;
-
- // Execute tx.
- vm.expectRevert(InvalidSig.selector);
- keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
- }
+ // function testCannotExecuteWithImproperSignatures() public payable {
+ // bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
+
+ // Signature[] memory sigs = new Signature[](2);
+
+ // Signature memory aliceSig;
+ // Signature memory charlieSig;
+
+ // aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // charlieSig = signExecution(
+ // bob,
+ // charliesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // sigs[0] = alice > charlie ? charlieSig : aliceSig;
+ // sigs[1] = alice > charlie ? aliceSig : charlieSig;
+
+ // // Execute tx.
+ // vm.expectRevert(InvalidSignature.selector);
+ // keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
+ // }
+
+ // function testCannotExecuteWithSignaturesOutOfOrder() public payable {
+ // bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
+
+ // Signature[] memory sigs = new Signature[](2);
+
+ // Signature memory aliceSig;
+ // Signature memory bobSig;
+
+ // aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // bobSig = signExecution(
+ // bob,
+ // bobsPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // sigs[0] = alice > bob ? aliceSig : bobSig;
+ // sigs[1] = alice > bob ? bobSig : aliceSig;
+
+ // // Execute tx.
+ // vm.expectRevert(InvalidSignature.selector);
+ // keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
+ // }
+
+ // function testCannotExecuteWithSignaturesRepeated() public payable {
+ // bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
+
+ // Signature[] memory sigs = new Signature[](2);
+
+ // Signature memory aliceSig;
+
+ // aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // sigs[0] = aliceSig;
+ // sigs[1] = aliceSig;
+
+ // // Execute tx.
+ // vm.expectRevert(InvalidSignature.selector);
+ // keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
+ // }
+
+ // function testCannotExecuteWithNullSignatures() public payable {
+ // bytes memory tx_data = abi.encodeCall(mockDai.transfer, (alice, 100));
+
+ // Signature[] memory sigs = new Signature[](2);
+
+ // Signature memory aliceSig;
+ // Signature memory nullSig;
+
+ // aliceSig = signExecution(
+ // alice,
+ // alicesPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // nullSig = signExecution(
+ // nully,
+ // nullPk,
+ // Operation.call,
+ // address(mockDai),
+ // 0,
+ // tx_data
+ // );
+
+ // sigs[0] = alice > nully ? nullSig : aliceSig;
+ // sigs[1] = alice > nully ? aliceSig : nullSig;
+
+ // // Execute tx.
+ // vm.expectRevert(InvalidSignature.selector);
+ // keep.execute(Operation.call, address(mockDai), 0, tx_data, sigs);
+ // }
/// -----------------------------------------------------------------------
/// Keep Governance Tests
/// -----------------------------------------------------------------------
- function testMint(
- uint256 id,
- uint256 amount,
- bytes calldata data
- ) public payable {
- vm.assume(id != SIGNER_KEY);
- vm.assume(id != uint32(type(KeepToken).interfaceId)); // CORE_KEY
- vm.assume(keep.totalSupply(uint32(type(KeepToken).interfaceId)) == 0);
- amount = bound(amount, 0, type(uint216).max);
+ // function testMint(
+ // uint256 id,
+ // uint256 amount,
+ // bytes calldata data
+ // ) public payable {
+ // vm.assume(id != EXEC_KEY);
+ // vm.assume(id != uint32(type(KeepToken).interfaceId)); // CORE_KEY
+ // vm.assume(keep.totalSupply(uint32(type(KeepToken).interfaceId)) == 0);
+ // amount = bound(amount, 0, type(uint216).max);
- uint256 preTotalSupply = keep.totalSupply(id);
- uint256 preBalance = keep.balanceOf(charlie, id);
+ // uint256 preTotalSupply = keep.totalSupply(id);
+ // uint256 preBalance = keep.balanceOf(charlie, id);
- vm.prank(address(keep));
- keep.mint(charlie, id, amount, data);
+ // vm.prank(address(keep));
+ // keep.mint(charlie, id, amount, data);
- assert(keep.balanceOf(charlie, id) == preBalance + amount);
- assert(keep.totalSupply(id) == preTotalSupply + amount);
- }
+ // assert(keep.balanceOf(charlie, id) == preBalance + amount);
+ // assert(keep.totalSupply(id) == preTotalSupply + amount);
+ // }
- function testMintExecuteIdKey() public payable {
- uint256 executeTotalSupply = keep.totalSupply(SIGNER_KEY);
- uint256 executeBalance = keep.balanceOf(charlie, SIGNER_KEY);
- uint256 preQuorum = keep.quorum();
+ // function testMintExecuteIdKey() public payable {
+ // uint256 executeTotalSupply = keep.totalSupply(EXEC_KEY);
+ // uint256 executeBalance = keep.balanceOf(charlie, EXEC_KEY);
+ // uint256 preQuorum = keep.quorum();
- vm.prank(address(keep));
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ // vm.prank(address(keep));
+ // keep.mint(charlie, EXEC_KEY, 1, "");
- assert(keep.balanceOf(charlie, SIGNER_KEY) == executeBalance + 1);
- assert(keep.totalSupply(SIGNER_KEY) == executeTotalSupply + 1);
- assert(keep.quorum() == preQuorum);
- }
+ // assert(keep.balanceOf(charlie, EXEC_KEY) == executeBalance + 1);
+ // assert(keep.totalSupply(EXEC_KEY) == executeTotalSupply + 1);
+ // assert(keep.quorum() == preQuorum);
+ // }
- function testMintCoreIdKey(uint256 amount) public payable {
- amount = bound(amount, 0, type(uint216).max);
+ // function testMintCoreIdKey(uint256 amount) public payable {
+ // amount = bound(amount, 0, type(uint216).max);
- uint256 CORE_KEY = uint32(type(KeepToken).interfaceId);
- uint256 totalSupply = keep.totalSupply(CORE_KEY);
- uint256 balance = keep.balanceOf(charlie, CORE_KEY);
- uint256 preQuorum = keep.quorum();
+ // uint256 CORE_KEY = uint32(type(KeepToken).interfaceId);
+ // uint256 totalSupply = keep.totalSupply(CORE_KEY);
+ // uint256 balance = keep.balanceOf(charlie, CORE_KEY);
+ // uint256 preQuorum = keep.quorum();
- vm.prank(address(keep));
- keep.mint(charlie, CORE_KEY, amount, "");
+ // vm.prank(address(keep));
+ // keep.mint(charlie, CORE_KEY, amount, "");
- assert(keep.balanceOf(charlie, CORE_KEY) == balance + amount);
- assert(keep.totalSupply(CORE_KEY) == totalSupply + amount);
- assert(keep.quorum() == preQuorum);
- assert(keep.totalSupply(CORE_KEY) == totalSupply + amount);
- }
+ // assert(keep.balanceOf(charlie, CORE_KEY) == balance + amount);
+ // assert(keep.totalSupply(CORE_KEY) == totalSupply + amount);
+ // assert(keep.quorum() == preQuorum);
+ // assert(keep.totalSupply(CORE_KEY) == totalSupply + amount);
+ // }
function testCannotMintToZeroAddress() public payable {
- assert(keep.totalSupply(SIGNER_KEY) == 3);
+ assert(keep.totalSupply(EXEC_KEY) == 3);
startHoax(address(keep), address(keep), type(uint256).max);
vm.expectRevert(InvalidRecipient.selector);
- keep.mint(address(0), SIGNER_KEY, 1, "");
+ keep.mint(address(0), EXEC_KEY, 1, "");
vm.expectRevert(InvalidRecipient.selector);
keep.mint(address(0), 1, 1, "");
- assert(keep.balanceOf(address(0), SIGNER_KEY) == 0);
+ assert(keep.balanceOf(address(0), EXEC_KEY) == 0);
assert(keep.balanceOf(address(0), 1) == 0);
- assert(keep.totalSupply(SIGNER_KEY) == 3);
+ assert(keep.totalSupply(EXEC_KEY) == 3);
assert(keep.totalSupply(1) == 0);
- assert(keep.quorum() == 2);
+ assert(keep.quorum(EXEC_KEY) == 2);
}
- function testCannotMintToUnsafeAddress() public payable {
- assert(keep.totalSupply(SIGNER_KEY) == 3);
+ // function testCannotMintToUnsafeAddress() public payable {
+ // assert(keep.totalSupply(EXEC_KEY) == 3);
- startHoax(address(keep), address(keep), type(uint256).max);
+ // startHoax(address(keep), address(keep), type(uint256).max);
- vm.expectRevert();
- keep.mint(address(mockDai), SIGNER_KEY, 1, "");
+ // vm.expectRevert();
+ // keep.mint(address(mockDai), EXEC_KEY, 1, "");
- vm.expectRevert(UnsafeRecipient.selector);
- keep.mint(address(mockUnsafeERC1155Receiver), SIGNER_KEY, 1, "");
+ // vm.expectRevert(UnsafeRecipient.selector);
+ // keep.mint(address(mockUnsafeERC1155Receiver), EXEC_KEY, 1, "");
- vm.expectRevert();
- keep.mint(address(mockDai), 1, 1, "");
+ // vm.expectRevert();
+ // keep.mint(address(mockDai), 1, 1, "");
- vm.expectRevert(UnsafeRecipient.selector);
- keep.mint(address(mockUnsafeERC1155Receiver), 1, 1, "");
+ // vm.expectRevert(UnsafeRecipient.selector);
+ // keep.mint(address(mockUnsafeERC1155Receiver), 1, 1, "");
- assert(keep.balanceOf(address(0), SIGNER_KEY) == 0);
- assert(keep.balanceOf(address(0), 1) == 0);
+ // assert(keep.balanceOf(address(0), EXEC_KEY) == 0);
+ // assert(keep.balanceOf(address(0), 1) == 0);
- assert(keep.totalSupply(SIGNER_KEY) == 3);
- assert(keep.totalSupply(1) == 0);
+ // assert(keep.totalSupply(EXEC_KEY) == 3);
+ // assert(keep.totalSupply(1) == 0);
- assert(keep.quorum() == 2);
- }
+ // assert(keep.quorum() == 2);
+ // }
- function testCannotMintOverflowSupply() public payable {
- uint256 amount = 1 << 216;
+ // function testCannotMintOverflowSupply() public payable {
+ // uint256 amount = 1 << 216;
- startHoax(address(keep), address(keep), type(uint256).max);
+ // startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(charlie, 0, type(uint96).max, "");
+ // keep.mint(charlie, 0, type(uint96).max, "");
- vm.expectRevert(Overflow.selector);
- keep.mint(charlie, 1, type(uint256).max, "");
+ // vm.expectRevert(Overflow.selector);
+ // keep.mint(charlie, 1, type(uint256).max, "");
- keep.mint(charlie, 2, type(uint216).max, "");
+ // keep.mint(charlie, 2, type(uint216).max, "");
- vm.expectRevert(Overflow.selector);
- keep.mint(charlie, 2, 1, "");
+ // vm.expectRevert(Overflow.selector);
+ // keep.mint(charlie, 2, 1, "");
- vm.expectRevert(Overflow.selector);
- keep.mint(charlie, 3, amount, "");
- }
+ // vm.expectRevert(Overflow.selector);
+ // keep.mint(charlie, 3, amount, "");
+ // }
- function testCannotMintOverflowExecuteID() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
+ // function testCannotMintOverflowExecuteID() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ // keep.mint(charlie, EXEC_KEY, 1, "");
- vm.expectRevert(Overflow.selector);
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ // vm.expectRevert(Overflow.selector);
+ // keep.mint(charlie, EXEC_KEY, 1, "");
- keep.burn(charlie, SIGNER_KEY, 1);
+ // keep.burn(charlie, EXEC_KEY, 1);
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ // keep.mint(charlie, EXEC_KEY, 1, "");
- vm.expectRevert(Overflow.selector);
- keep.mint(charlie, SIGNER_KEY, 1, "");
- }
+ // vm.expectRevert(Overflow.selector);
+ // keep.mint(charlie, EXEC_KEY, 1, "");
+ // }
- function testBurn() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(alice, 1, 2, "");
- keep.burn(alice, 1, 1);
+ // function testBurn() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.mint(alice, 1, 2, "");
+ // keep.burn(alice, 1, 1);
- assert(keep.balanceOf(alice, 1) == 1);
- assert(keep.totalSupply(1) == 1);
- }
+ // assert(keep.balanceOf(alice, 1) == 1);
+ // assert(keep.totalSupply(1) == 1);
+ // }
- function testBurnSigner() public payable {
- vm.prank(address(keep));
- keep.burn(alice, SIGNER_KEY, 1);
+ // function testBurnSigner() public payable {
+ // vm.prank(address(keep));
+ // keep.burn(alice, EXEC_KEY, 1);
- assert(keep.balanceOf(alice, SIGNER_KEY) == 0);
- assert(keep.totalSupply(SIGNER_KEY) == 2);
- }
+ // assert(keep.balanceOf(alice, EXEC_KEY) == 0);
+ // assert(keep.totalSupply(EXEC_KEY) == 2);
+ // }
- function testCannotBurnUnderflow() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(alice, 1, 1, "");
- vm.expectRevert(stdError.arithmeticError);
- keep.burn(alice, 1, 2);
- }
+ // function testCannotBurnUnderflow() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.mint(alice, 1, 1, "");
+ // vm.expectRevert(stdError.arithmeticError);
+ // keep.burn(alice, 1, 2);
+ // }
- function testIdKeyRole() public payable {
- vm.prank(charlie);
- vm.expectRevert(Unauthorized.selector);
- keep.mint(alice, 1, 100, "");
+ // // function testIdKeyRole() public payable {
+ // // vm.prank(charlie);
+ // // vm.expectRevert(Unauthorized.selector);
+ // // keep.mint(alice, 1, 100, "");
- vm.prank(address(keep));
- keep.mint(charlie, uint32(keep.mint.selector), 1, "");
+ // // vm.prank(address(keep));
+ // // keep.mint(charlie, uint32(keep.mint.selector), 1, "");
- vm.prank(charlie);
- keep.mint(alice, 1, 100, "");
+ // // vm.prank(charlie);
+ // // keep.mint(alice, 1, 100, "");
- assert(keep.balanceOf(alice, 1) == 100);
- }
+ // // assert(keep.balanceOf(alice, 1) == 100);
+ // // }
- /*
- function testSetTransferability() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.setTransferability(1, true);
- keep.mint(charlie, 1, 1, "");
+ // /*
+ // function testSetTransferability() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.setTransferability(1, true);
+ // keep.mint(charlie, 1, 1, "");
- assertTrue(keep.transferable(1));
+ // assertTrue(keep.transferable(1));
- vm.prank(charlie);
- keep.safeTransferFrom(charlie, alice, 1, 1, "");
+ // vm.prank(charlie);
+ // keep.safeTransferFrom(charlie, alice, 1, 1, "");
- vm.prank(address(keep));
- keep.setTransferability(1, false);
+ // vm.prank(address(keep));
+ // keep.setTransferability(1, false);
- assertFalse(keep.transferable(1));
- assert(keep.balanceOf(alice, 1) == 1);
+ // assertFalse(keep.transferable(1));
+ // assert(keep.balanceOf(alice, 1) == 1);
- vm.prank(alice);
- vm.expectRevert(NonTransferable.selector);
- keep.safeTransferFrom(alice, charlie, 1, 1, "");
- }
- */
- function testCannotSetTransferability(
- address user,
- uint256 id
- ) public payable {
- vm.assume(user != address(keep));
+ // vm.prank(alice);
+ // vm.expectRevert(NonTransferable.selector);
+ // keep.safeTransferFrom(alice, charlie, 1, 1, "");
+ // }
+ // */
+ // function testCannotSetTransferability(
+ // address user,
+ // uint256 id
+ // ) public payable {
+ // vm.assume(user != address(keep));
- vm.prank(user);
- vm.expectRevert(Unauthorized.selector);
- keep.setTransferability(id, true);
+ // vm.prank(user);
+ // vm.expectRevert(Unauthorized.selector);
+ // keep.setTransferability(id, true);
- assertFalse(keep.transferable(id));
- }
+ // assertFalse(keep.transferable(id));
+ // }
- function testSetURI(address user) public payable {
- vm.assume(user != address(keep));
+ // function testSetURI(address user) public payable {
+ // vm.assume(user != address(keep));
- vm.prank(user);
- vm.expectRevert(Unauthorized.selector);
- keep.setURI(0, "TEST");
+ // vm.prank(user);
+ // vm.expectRevert(Unauthorized.selector);
+ // keep.setURI(0, "TEST");
- // The Keep itself should be able to update uri.
- vm.prank(address(keep));
- keep.setURI(0, "TEST");
+ // // The Keep itself should be able to update uri.
+ // vm.prank(address(keep));
+ // keep.setURI(0, "TEST");
- assertEq(keccak256(bytes("TEST")), keccak256(bytes(keep.uri(0))));
- }
+ // assertEq(keccak256(bytes("TEST")), keccak256(bytes(keep.uri(0))));
+ // }
/// -----------------------------------------------------------------------
/// Keep Token Tests
/// -----------------------------------------------------------------------
- function testKeepTokenApprove(address userA, address userB) public payable {
- vm.prank(userA);
- keep.setApprovalForAll(userB, true);
+ // function testKeepTokenApprove(address userA, address userB) public payable {
+ // vm.prank(userA);
+ // keep.setApprovalForAll(userB, true);
- assertTrue(keep.isApprovedForAll(userA, userB));
+ // assertTrue(keep.isApprovedForAll(userA, userB));
- vm.prank(userA);
- keep.setApprovalForAll(userB, false);
+ // vm.prank(userA);
+ // keep.setApprovalForAll(userB, false);
- assertFalse(keep.isApprovedForAll(userA, userB));
- }
+ // assertFalse(keep.isApprovedForAll(userA, userB));
+ // }
/*
function testKeepTokenTransferByOwner(
@@ -1199,7 +1193,7 @@ contract KeepTest is Keep(this), Test {
amount = bound(amount, 0, type(uint216).max);
if (
- id == SIGNER_KEY &&
+ id == EXEC_KEY &&
(keep.balanceOf(userA, id) != 0 || keep.balanceOf(userB, id) != 0)
) {
amount = amount - 1;
@@ -1235,7 +1229,7 @@ contract KeepTest is Keep(this), Test {
amount = bound(amount, 0, type(uint216).max);
if (
- id == SIGNER_KEY &&
+ id == EXEC_KEY &&
(keep.balanceOf(userA, id) != 0 || keep.balanceOf(userB, id) != 0)
) {
amount = amount - 1;
@@ -1274,7 +1268,7 @@ contract KeepTest is Keep(this), Test {
amount = bound(amount, 0, type(uint216).max);
if (
- id == SIGNER_KEY &&
+ id == EXEC_KEY &&
(keep.balanceOf(userA, id) != 0 || keep.balanceOf(userB, id) != 0)
) {
amount = amount - 1;
@@ -1289,68 +1283,68 @@ contract KeepTest is Keep(this), Test {
keep.safeTransferFrom(userA, userB, id, amount, "");
}
*/
- function testKeepTokenBatchTransferByOwner() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.setTransferability(0, true);
- keep.setTransferability(1, true);
+ // function testKeepTokenBatchTransferByOwner() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.setTransferability(0, true);
+ // keep.setTransferability(1, true);
- assertTrue(keep.transferable(0));
- assertTrue(keep.transferable(1));
+ // assertTrue(keep.transferable(0));
+ // assertTrue(keep.transferable(1));
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(charlie, 0, 1, "");
- keep.mint(charlie, 1, 2, "");
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.mint(charlie, 0, 1, "");
+ // keep.mint(charlie, 1, 2, "");
- uint256[] memory ids = new uint256[](2);
- ids[0] = 0;
- ids[1] = 1;
+ // uint256[] memory ids = new uint256[](2);
+ // ids[0] = 0;
+ // ids[1] = 1;
- uint256[] memory amounts = new uint256[](2);
- amounts[0] = 1;
- amounts[1] = 1;
+ // uint256[] memory amounts = new uint256[](2);
+ // amounts[0] = 1;
+ // amounts[1] = 1;
- startHoax(charlie, charlie, type(uint256).max);
- keep.safeBatchTransferFrom(charlie, bob, ids, amounts, "");
+ // startHoax(charlie, charlie, type(uint256).max);
+ // keep.safeBatchTransferFrom(charlie, bob, ids, amounts, "");
- assert(keep.balanceOf(charlie, 0) == 0);
- assert(keep.balanceOf(charlie, 1) == 1);
- assert(keep.balanceOf(bob, 0) == 1);
- assert(keep.balanceOf(bob, 1) == 1);
- }
+ // assert(keep.balanceOf(charlie, 0) == 0);
+ // assert(keep.balanceOf(charlie, 1) == 1);
+ // assert(keep.balanceOf(bob, 0) == 1);
+ // assert(keep.balanceOf(bob, 1) == 1);
+ // }
- function testKeepTokenBatchTransferByOperator() public payable {
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.setTransferability(0, true);
- keep.setTransferability(1, true);
+ // function testKeepTokenBatchTransferByOperator() public payable {
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.setTransferability(0, true);
+ // keep.setTransferability(1, true);
- assertTrue(keep.transferable(0));
- assertTrue(keep.transferable(1));
+ // assertTrue(keep.transferable(0));
+ // assertTrue(keep.transferable(1));
- startHoax(charlie, charlie, type(uint256).max);
- keep.setApprovalForAll(alice, true);
+ // startHoax(charlie, charlie, type(uint256).max);
+ // keep.setApprovalForAll(alice, true);
- assertTrue(keep.isApprovedForAll(charlie, alice));
+ // assertTrue(keep.isApprovedForAll(charlie, alice));
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(charlie, 0, 1, "");
- keep.mint(charlie, 1, 2, "");
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.mint(charlie, 0, 1, "");
+ // keep.mint(charlie, 1, 2, "");
- uint256[] memory ids = new uint256[](2);
- ids[0] = 0;
- ids[1] = 1;
+ // uint256[] memory ids = new uint256[](2);
+ // ids[0] = 0;
+ // ids[1] = 1;
- uint256[] memory amounts = new uint256[](2);
- amounts[0] = 1;
- amounts[1] = 1;
+ // uint256[] memory amounts = new uint256[](2);
+ // amounts[0] = 1;
+ // amounts[1] = 1;
- startHoax(alice, alice, type(uint256).max);
- keep.safeBatchTransferFrom(charlie, bob, ids, amounts, "");
+ // startHoax(alice, alice, type(uint256).max);
+ // keep.safeBatchTransferFrom(charlie, bob, ids, amounts, "");
- assert(keep.balanceOf(charlie, 0) == 0);
- assert(keep.balanceOf(charlie, 1) == 1);
- assert(keep.balanceOf(bob, 0) == 1);
- assert(keep.balanceOf(bob, 1) == 1);
- }
+ // assert(keep.balanceOf(charlie, 0) == 0);
+ // assert(keep.balanceOf(charlie, 1) == 1);
+ // assert(keep.balanceOf(bob, 0) == 1);
+ // assert(keep.balanceOf(bob, 1) == 1);
+ // }
/*
function testKeepTokenTransferPermission(
@@ -1369,7 +1363,7 @@ contract KeepTest is Keep(this), Test {
vm.assume(userA.code.length == 0);
vm.assume(userB.code.length == 0);
vm.assume(userC.code.length == 0);
- vm.assume(id != SIGNER_KEY);
+ vm.assume(id != EXEC_KEY);
vm.assume(id != CORE_KEY);
amount = bound(amount, 0, type(uint216).max);
@@ -1445,27 +1439,27 @@ contract KeepTest is Keep(this), Test {
/*
function testCannotTransferExecuteOverflow() public payable {
startHoax(address(keep), address(keep), type(uint256).max);
- keep.setTransferability(SIGNER_KEY, true);
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ keep.setTransferability(EXEC_KEY, true);
+ keep.mint(charlie, EXEC_KEY, 1, "");
vm.prank(charlie);
- keep.safeTransferFrom(charlie, address(0xBeef), SIGNER_KEY, 1, "");
+ keep.safeTransferFrom(charlie, address(0xBeef), EXEC_KEY, 1, "");
startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(charlie, SIGNER_KEY, 1, "");
+ keep.mint(charlie, EXEC_KEY, 1, "");
vm.prank(charlie);
vm.expectRevert(Overflow.selector);
- keep.safeTransferFrom(charlie, address(0xBeef), SIGNER_KEY, 1, "");
+ keep.safeTransferFrom(charlie, address(0xBeef), EXEC_KEY, 1, "");
startHoax(address(keep), address(keep), type(uint256).max);
- keep.burn(address(0xBeef), SIGNER_KEY, 1);
+ keep.burn(address(0xBeef), EXEC_KEY, 1);
}*/
/*
function testCannotTransferKeepTokenNonTransferable(
uint256 id
) public payable {
- vm.assume(id != SIGNER_KEY);
+ vm.assume(id != EXEC_KEY);
vm.prank(address(keep));
keep.mint(charlie, id, 1, "");
@@ -1523,7 +1517,7 @@ contract KeepTest is Keep(this), Test {
uint256 id
) public payable {
vm.assume(id != 1816876358);
- vm.assume(id != SIGNER_KEY);
+ vm.assume(id != EXEC_KEY);
vm.prank(address(keep));
keep.setTransferability(id, true);
@@ -1556,7 +1550,7 @@ contract KeepTest is Keep(this), Test {
amount = bound(amount, 0, type(uint216).max);
if (
- id == SIGNER_KEY &&
+ id == EXEC_KEY &&
(keep.balanceOf(userA, id) != 0 || keep.balanceOf(userB, id) != 0)
) {
amount = amount - 1;
@@ -1678,187 +1672,187 @@ contract KeepTest is Keep(this), Test {
/// Keep Vote Delegation Tests
/// -----------------------------------------------------------------------
- function testKeepTokenInitDelegationBalance(
- address user,
- uint256 id,
- uint256 amount
- ) public payable {
- vm.assume(user != address(0));
- vm.assume(user.code.length == 0);
- vm.assume(id != SIGNER_KEY);
+ // function testKeepTokenInitDelegationBalance(
+ // address user,
+ // uint256 id,
+ // uint256 amount
+ // ) public payable {
+ // vm.assume(user != address(0));
+ // vm.assume(user.code.length == 0);
+ // vm.assume(id != EXEC_KEY);
- amount = bound(amount, 0, type(uint216).max);
+ // amount = bound(amount, 0, type(uint216).max);
- vm.warp(1665378008);
+ // vm.warp(1665378008);
- vm.startPrank(address(keep));
- keep.mint(user, id, amount, "");
+ // vm.startPrank(address(keep));
+ // keep.mint(user, id, amount, "");
- assert(keep.delegates(user, id) == user);
- assert(keep.getCurrentVotes(user, id) == amount);
- assert(keep.getVotes(user, id) == amount);
+ // assert(keep.delegates(user, id) == user);
+ // assert(keep.getCurrentVotes(user, id) == amount);
+ // assert(keep.getVotes(user, id) == amount);
- vm.warp(1665378010);
+ // vm.warp(1665378010);
- assert(keep.getPriorVotes(user, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(user, id, block.timestamp - 1) == amount);
- }
+ // assert(keep.getPriorVotes(user, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(user, id, block.timestamp - 1) == amount);
+ // }
- function testKeepTokenDelegation(
- address userA,
- address userB,
- uint256 id,
- uint256 amount
- ) public payable {
- amount = bound(amount, 0, type(uint216).max);
- vm.assume(userA != address(0));
- vm.assume(userB != address(0));
- vm.assume(userA != userB);
- vm.assume(userA.code.length == 0);
- vm.assume(userB.code.length == 0);
- vm.assume(id != SIGNER_KEY);
+ // function testKeepTokenDelegation(
+ // address userA,
+ // address userB,
+ // uint256 id,
+ // uint256 amount
+ // ) public payable {
+ // amount = bound(amount, 0, type(uint216).max);
+ // vm.assume(userA != address(0));
+ // vm.assume(userB != address(0));
+ // vm.assume(userA != userB);
+ // vm.assume(userA.code.length == 0);
+ // vm.assume(userB.code.length == 0);
+ // vm.assume(id != EXEC_KEY);
- vm.warp(1665378008);
+ // vm.warp(1665378008);
- vm.startPrank(address(keep));
- keep.mint(userA, id, amount, "");
+ // vm.startPrank(address(keep));
+ // keep.mint(userA, id, amount, "");
- assert(keep.delegates(userA, id) == userA);
- assert(keep.getCurrentVotes(userA, id) == amount);
- assert(keep.getVotes(userA, id) == amount);
+ // assert(keep.delegates(userA, id) == userA);
+ // assert(keep.getCurrentVotes(userA, id) == amount);
+ // assert(keep.getVotes(userA, id) == amount);
- assert(keep.getCurrentVotes(userB, id) == 0);
- assert(keep.getVotes(userB, id) == 0);
+ // assert(keep.getCurrentVotes(userB, id) == 0);
+ // assert(keep.getVotes(userB, id) == 0);
- vm.warp(1665378010);
+ // vm.warp(1665378010);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == 0);
- assert(keep.getPastVotes(userB, id, block.timestamp - 1) == 0);
+ // assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == 0);
+ // assert(keep.getPastVotes(userB, id, block.timestamp - 1) == 0);
- vm.startPrank(userA);
- keep.delegate(userB, id);
+ // vm.startPrank(userA);
+ // keep.delegate(userB, id);
- assert(keep.delegates(userA, id) == userB);
+ // assert(keep.delegates(userA, id) == userB);
- assert(keep.getCurrentVotes(userA, id) == 0);
- assert(keep.getVotes(userA, id) == 0);
+ // assert(keep.getCurrentVotes(userA, id) == 0);
+ // assert(keep.getVotes(userA, id) == 0);
- assert(keep.getCurrentVotes(userB, id) == amount);
- assert(keep.getVotes(userB, id) == amount);
+ // assert(keep.getCurrentVotes(userB, id) == amount);
+ // assert(keep.getVotes(userB, id) == amount);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
- vm.warp(1665378015);
+ // vm.warp(1665378015);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == 0);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == 0);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == 0);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == 0);
- assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userB, id, block.timestamp - 1) == amount);
- }
+ // assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userB, id, block.timestamp - 1) == amount);
+ // }
- function testKeepTokenDelegationBalanceByTransfer(
- address userA,
- address userB,
- uint256 id,
- uint256 amount
- ) public payable {
- amount = bound(amount, 0, type(uint216).max);
- vm.assume(userA != address(0));
- vm.assume(userB != address(0));
- vm.assume(userA != userB);
- vm.assume(userA.code.length == 0);
- vm.assume(userB.code.length == 0);
- vm.assume(id != SIGNER_KEY);
- vm.assume(id != CORE_KEY);
+ // function testKeepTokenDelegationBalanceByTransfer(
+ // address userA,
+ // address userB,
+ // uint256 id,
+ // uint256 amount
+ // ) public payable {
+ // amount = bound(amount, 0, type(uint216).max);
+ // vm.assume(userA != address(0));
+ // vm.assume(userB != address(0));
+ // vm.assume(userA != userB);
+ // vm.assume(userA.code.length == 0);
+ // vm.assume(userB.code.length == 0);
+ // vm.assume(id != EXEC_KEY);
+ // vm.assume(id != CORE_KEY);
- vm.warp(1665378008);
+ // vm.warp(1665378008);
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.mint(userA, id, amount, "");
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.mint(userA, id, amount, "");
- assert(keep.delegates(userA, id) == userA);
+ // assert(keep.delegates(userA, id) == userA);
- assert(keep.getCurrentVotes(userA, id) == amount);
- assert(keep.getVotes(userA, id) == amount);
+ // assert(keep.getCurrentVotes(userA, id) == amount);
+ // assert(keep.getVotes(userA, id) == amount);
- assert(keep.getCurrentVotes(userB, id) == 0);
- assert(keep.getVotes(userB, id) == 0);
+ // assert(keep.getCurrentVotes(userB, id) == 0);
+ // assert(keep.getVotes(userB, id) == 0);
- vm.warp(1665378010);
+ // vm.warp(1665378010);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == 0);
- assert(keep.getPastVotes(userB, id, block.timestamp - 1) == 0);
+ // assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == 0);
+ // assert(keep.getPastVotes(userB, id, block.timestamp - 1) == 0);
- startHoax(address(keep), address(keep), type(uint256).max);
- keep.setTransferability(id, true);
+ // startHoax(address(keep), address(keep), type(uint256).max);
+ // keep.setTransferability(id, true);
- assertTrue(keep.transferable(id));
+ // assertTrue(keep.transferable(id));
- startHoax(userA, userA, type(uint256).max);
- keep.safeTransferFrom(userA, userB, id, amount, "");
+ // startHoax(userA, userA, type(uint256).max);
+ // keep.safeTransferFrom(userA, userB, id, amount, "");
- assert(keep.delegates(userA, id) == userA);
+ // assert(keep.delegates(userA, id) == userA);
- assert(keep.getCurrentVotes(userA, id) == 0);
- assert(keep.getVotes(userA, id) == 0);
+ // assert(keep.getCurrentVotes(userA, id) == 0);
+ // assert(keep.getVotes(userA, id) == 0);
- assert(keep.getCurrentVotes(userB, id) == amount);
- assert(keep.getVotes(userB, id) == amount);
+ // assert(keep.getCurrentVotes(userB, id) == amount);
+ // assert(keep.getVotes(userB, id) == amount);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == amount);
- vm.warp(1665378015);
+ // vm.warp(1665378015);
- assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == 0);
- assert(keep.getPastVotes(userA, id, block.timestamp - 1) == 0);
+ // assert(keep.getPriorVotes(userA, id, block.timestamp - 1) == 0);
+ // assert(keep.getPastVotes(userA, id, block.timestamp - 1) == 0);
- assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == amount);
- assert(keep.getPastVotes(userB, id, block.timestamp - 1) == amount);
- }
+ // assert(keep.getPriorVotes(userB, id, block.timestamp - 1) == amount);
+ // assert(keep.getPastVotes(userB, id, block.timestamp - 1) == amount);
+ // }
/// -----------------------------------------------------------------------
/// Keep MetaTx Tests
/// -----------------------------------------------------------------------
- function testKeepTokenPermit(address userB, bool approved) public payable {
- uint256 privateKey = 0xBEEF;
- address userA = vm.addr(0xBEEF);
-
- (uint8 v, bytes32 r, bytes32 s) = vm.sign(
- privateKey,
- keccak256(
- abi.encodePacked(
- "\x19\x01",
- keep.DOMAIN_SEPARATOR(),
- keccak256(
- abi.encode(
- PERMIT_TYPEHASH,
- userA,
- userB,
- approved,
- 0,
- block.timestamp
- )
- )
- )
- )
- );
-
- vm.startPrank(userA);
- keep.permit(userA, userB, approved, block.timestamp, v, r, s);
-
- assert(keep.isApprovedForAll(userA, userB) == approved);
- assert(keep.nonces(userA) == 1);
- }
+ // function testKeepTokenPermit(address userB, bool approved) public payable {
+ // uint256 privateKey = 0xBEEF;
+ // address userA = vm.addr(0xBEEF);
+
+ // (uint8 v, bytes32 r, bytes32 s) = vm.sign(
+ // privateKey,
+ // keccak256(
+ // abi.encodePacked(
+ // "\x19\x01",
+ // keep.DOMAIN_SEPARATOR(),
+ // keccak256(
+ // abi.encode(
+ // PERMIT_TYPEHASH,
+ // userA,
+ // userB,
+ // approved,
+ // 0,
+ // block.timestamp
+ // )
+ // )
+ // )
+ // )
+ // );
+
+ // vm.startPrank(userA);
+ // keep.permit(userA, userB, approved, block.timestamp, v, r, s);
+
+ // assert(keep.isApprovedForAll(userA, userB) == approved);
+ // assert(keep.nonces(userA) == 1);
+ // }
function testCannotSpendKeepTokenPermitAfterDeadline(
address userB,
@@ -1897,48 +1891,48 @@ contract KeepTest is Keep(this), Test {
keep.permit(userA, userB, approved, deadline, v, r, s);
}
- function testKeepTokenDelegateBySig(
- address userB,
- uint256 id,
- uint256 amount
- ) public payable {
- amount = bound(amount, 0, type(uint216).max);
- vm.assume(userB != address(0));
- vm.assume(userB.code.length == 0);
- vm.assume(id != SIGNER_KEY);
-
- uint256 privateKey = 0xBEEF;
- address userA = vm.addr(0xBEEF);
-
- vm.prank(address(keep));
- keep.mint(userA, id, amount, "");
-
- (uint8 v, bytes32 r, bytes32 s) = vm.sign(
- privateKey,
- keccak256(
- abi.encodePacked(
- "\x19\x01",
- keep.DOMAIN_SEPARATOR(),
- keccak256(
- abi.encode(
- DELEGATION_TYPEHASH,
- userA,
- userB,
- id,
- 0,
- block.timestamp
- )
- )
- )
- )
- );
-
- vm.startPrank(userA);
- keep.delegateBySig(userA, userB, id, block.timestamp, v, r, s);
-
- assert(keep.delegates(userA, id) == userB);
- assert(keep.nonces(userA) == 1);
- }
+ // function testKeepTokenDelegateBySig(
+ // address userB,
+ // uint256 id,
+ // uint256 amount
+ // ) public payable {
+ // amount = bound(amount, 0, type(uint216).max);
+ // vm.assume(userB != address(0));
+ // vm.assume(userB.code.length == 0);
+ // vm.assume(id != EXEC_KEY);
+
+ // uint256 privateKey = 0xBEEF;
+ // address userA = vm.addr(0xBEEF);
+
+ // vm.prank(address(keep));
+ // keep.mint(userA, id, amount, "");
+
+ // (uint8 v, bytes32 r, bytes32 s) = vm.sign(
+ // privateKey,
+ // keccak256(
+ // abi.encodePacked(
+ // "\x19\x01",
+ // keep.DOMAIN_SEPARATOR(),
+ // keccak256(
+ // abi.encode(
+ // DELEGATION_TYPEHASH,
+ // userA,
+ // userB,
+ // id,
+ // 0,
+ // block.timestamp
+ // )
+ // )
+ // )
+ // )
+ // );
+
+ // vm.startPrank(userA);
+ // keep.delegateBySig(userA, userB, id, block.timestamp, v, r, s);
+
+ // assert(keep.delegates(userA, id) == userB);
+ // assert(keep.nonces(userA) == 1);
+ // }
function testCannotSpendKeepTokenDelegateBySigAfterDeadline(
address userB,
@@ -1976,4 +1970,14 @@ contract KeepTest is Keep(this), Test {
vm.expectRevert(ExpiredSig.selector);
keep.delegateBySig(userA, userB, id, deadline, v, r, s);
}
+
+ // function testValidateSignatures(bytes32 hash) public payable {
+ // (uint8 v, bytes32 r, bytes32 s) = vm.sign(alicesPk, hash);
+
+ // bytes memory sig = abi.encode(v, r, s);
+
+ // uint256 validationData = keep.validateSignatures(hash, sig);
+
+ // assert(validationData == 0);
+ // }
}
diff --git a/test/KeepFactory.t.sol b/test/KeepFactory.t.sol
index 0cbb85ad..49173da6 100644
--- a/test/KeepFactory.t.sol
+++ b/test/KeepFactory.t.sol
@@ -1,57 +1,47 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
import {Call, Keep} from "../src/Keep.sol";
import {KeepFactory} from "../src/KeepFactory.sol";
-import {URIFetcher} from "../src/extensions/metadata/URIFetcher.sol";
-
import "@std/Test.sol";
contract KeepFactoryTest is Test {
- address keepAddr;
- Keep keep;
- KeepFactory factory;
- URIFetcher uriFetcher;
+ KeepFactory immutable factory = new KeepFactory();
- address[] signers;
+ bytes32 internal constant mockName = keccak256("TEST1");
/// @dev Users.
- address public immutable alice = address(0xa);
- address public immutable bob = address(0xb);
+ address constant alice = address(0xa);
+ address constant bob = address(0xb);
/// @dev Helpers.
Call[] calls;
- bytes32 name =
- 0x5445535400000000000000000000000000000000000000000000000000000000;
-
- bytes32 name2 =
- 0x5445535432000000000000000000000000000000000000000000000000000000;
-
/// @notice Set up the testing suite.
- function setUp() public payable {
- // Create the templates.
- uriFetcher = new URIFetcher();
- keep = new Keep(Keep(address(uriFetcher)));
- // Create the factory.
- factory = new KeepFactory(address(keep));
- // Create the signers.
- signers.push(alice);
- signers.push(bob);
- }
+ function setUp() public payable {}
function testDeploy() public payable {
- factory.deployKeep(name, calls, signers, 2);
+ address[] memory signers = new address[](2);
+ signers[0] = alice;
+ signers[1] = bob;
+
+ factory.deployKeep(mockName, calls, signers, 2);
}
function testDetermination() public payable {
// Check CREATE2 clones match expected outputs.
- keepAddr = factory.determineKeep(name);
- keep = Keep(keepAddr);
- factory.deployKeep(name, calls, signers, 2);
- assertEq(address(keep), keepAddr);
+ (address predicted, ) = factory.determineKeep(mockName);
+
+ address[] memory signers = new address[](2);
+ signers[0] = alice;
+ signers[1] = bob;
+
+ address deployed = address(
+ factory.deployKeep(mockName, calls, signers, 2)
+ );
+ assertEq(predicted, deployed);
}
}
diff --git a/test/Multicallable.t.sol b/test/Multicallable.t.sol
index 1e7c689f..a50b097a 100644
--- a/test/Multicallable.t.sol
+++ b/test/Multicallable.t.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import {MockMulticallable} from "./utils/mocks/MockMulticallable.sol";
@@ -11,21 +11,21 @@ contract MulticallableTest is Test {
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 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);
@@ -45,69 +45,69 @@ contract MulticallableTest is Test {
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 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 {
diff --git a/test/Owned.t.sol b/test/Ownable.t.sol
similarity index 54%
rename from test/Owned.t.sol
rename to test/Ownable.t.sol
index 6b889f42..3b7b7c10 100644
--- a/test/Owned.t.sol
+++ b/test/Ownable.t.sol
@@ -1,16 +1,14 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
-import {MockOwned} from "./utils/mocks/MockOwned.sol";
+import {MockOwnable} from "./utils/mocks/MockOwnable.sol";
import "@std/Test.sol";
-contract OwnedTest is Test, MockOwned {
- MockOwned mockOwned;
+contract OwnableTest is Test, MockOwnable {
+ MockOwnable immutable mockOwnable = new MockOwnable();
- function setUp() public payable {
- mockOwned = new MockOwned();
- }
+ function setUp() public payable {}
function testTransferOwnership() public payable {
testTransferOwnership(address(0xBEEF));
@@ -21,26 +19,26 @@ contract OwnedTest is Test, MockOwned {
}
function testCallFunctionAsOwner() public payable {
- mockOwned.updateFlag();
+ mockOwnable.updateFlag();
}
function testTransferOwnership(address newOwner) public payable {
- mockOwned.transferOwnership(newOwner);
+ mockOwnable.transferOwnership(newOwner);
- assertEq(mockOwned.owner(), newOwner);
+ assertEq(mockOwnable.owner(), newOwner);
}
function testCallFunctionAsNonOwner(address owner) public payable {
vm.assume(owner != address(this));
- mockOwned.transferOwnership(owner);
+ mockOwnable.transferOwnership(owner);
vm.expectRevert(Unauthorized.selector);
- mockOwned.updateFlag();
+ mockOwnable.updateFlag();
}
function testERC165Support() public payable {
// ERC173 selector.
- assert(mockOwned.supportsInterface(0x7f5828d0));
+ assert(mockOwnable.supportsInterface(0x7f5828d0));
}
}
diff --git a/test/ReentrancyGuard.t.sol b/test/ReentrancyGuard.t.sol
deleted file mode 100644
index 687c001d..00000000
--- a/test/ReentrancyGuard.t.sol
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {ReentrancyGuard} from "../src/extensions/utils/ReentrancyGuard.sol";
-
-import "@std/Test.sol";
-
-contract RiskyContract is ReentrancyGuard {
- uint256 public enterTimes;
-
- function unprotectedCall() public payable {
- unchecked {
- ++enterTimes;
- }
-
- if (enterTimes > 1) {
- return;
- }
-
- this.protectedCall();
- }
-
- function protectedCall() public payable nonReentrant {
- unchecked {
- ++enterTimes;
- }
-
- if (enterTimes > 1) {
- return;
- }
-
- this.protectedCall();
- }
-
- function overprotectedCall() public payable nonReentrant {}
-}
-
-contract ReentrancyGuardTest is Test {
- RiskyContract internal immutable riskyContract = new RiskyContract();
-
- function setUp() public payable {}
-
- function invariantReentrancyStatusAlways1() public payable {
- assertEq(uint256(vm.load(address(riskyContract), 0)), 1);
- }
-
- function testFailUnprotectedCall() public payable {
- riskyContract.unprotectedCall();
-
- assertEq(riskyContract.enterTimes(), 1);
- }
-
- function testProtectedCall() public payable {
- try riskyContract.protectedCall() {
- fail("Reentrancy Guard Failed To Stop Attacker");
- } catch {}
- }
-
- function testNoReentrancy() public payable {
- riskyContract.overprotectedCall();
- }
-}
diff --git a/test/utils/mocks/MockMulticallable.sol b/test/utils/mocks/MockMulticallable.sol
index 1b862ec9..5b576f64 100644
--- a/test/utils/mocks/MockMulticallable.sol
+++ b/test/utils/mocks/MockMulticallable.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
import "../../../src/utils/Multicallable.sol";
diff --git a/test/utils/mocks/MockOwnable.sol b/test/utils/mocks/MockOwnable.sol
new file mode 100644
index 00000000..9ca8ac6f
--- /dev/null
+++ b/test/utils/mocks/MockOwnable.sol
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
+
+import {Ownable} from "../../../src/utils/Ownable.sol";
+
+contract MockOwnable is Ownable(msg.sender) {
+ bool public flag;
+
+ function updateFlag() public payable virtual onlyOwner {
+ flag = true;
+ }
+}
diff --git a/test/utils/mocks/MockOwned.sol b/test/utils/mocks/MockOwned.sol
deleted file mode 100644
index 850b3fb2..00000000
--- a/test/utils/mocks/MockOwned.sol
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {Owned} from "../../../src/extensions/utils/Owned.sol";
-
-contract MockOwned is Owned(msg.sender) {
- bool public flag;
-
- function updateFlag() public payable virtual onlyOwner {
- flag = true;
- }
-}
diff --git a/test/utils/mocks/MockUnsafeERC1155Receiver.sol b/test/utils/mocks/MockUnsafeERC1155Receiver.sol
index dd7348de..c471ee58 100644
--- a/test/utils/mocks/MockUnsafeERC1155Receiver.sol
+++ b/test/utils/mocks/MockUnsafeERC1155Receiver.sol
@@ -1,5 +1,5 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
+// SPDX-License-Identifier: AGPL-3.0-only
+pragma solidity ^0.8.19;
contract MockUnsafeERC1155Receiver {
function onERC1155Received(