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(