From f6573570b71f62218893af76ad73cfedd0dbf6af Mon Sep 17 00:00:00 2001 From: zeroknots Date: Tue, 3 Sep 2024 20:14:38 +0700 Subject: [PATCH] init --- cache_forge/solidity-files-cache.json | 1 + foundry.toml | 30 +- script/Counter.s.sol | 19 - src/AssociatedArrayLib.sol | 190 +++++++++ src/Counter.sol | 14 - src/EnumerableMap.sol | 558 ++++++++++++++++++++++++++ src/EnumerableSet.sol | 368 +++++++++++++++++ test/Counter.t.sol | 24 -- test/EnumerableMap.t.sol | 277 +++++++++++++ test/EnumerableSet.t.sol | 186 +++++++++ 10 files changed, 1607 insertions(+), 60 deletions(-) create mode 100644 cache_forge/solidity-files-cache.json delete mode 100644 script/Counter.s.sol create mode 100644 src/AssociatedArrayLib.sol delete mode 100644 src/Counter.sol create mode 100644 src/EnumerableMap.sol create mode 100644 src/EnumerableSet.sol delete mode 100644 test/Counter.t.sol create mode 100644 test/EnumerableMap.t.sol create mode 100644 test/EnumerableSet.t.sol diff --git a/cache_forge/solidity-files-cache.json b/cache_forge/solidity-files-cache.json new file mode 100644 index 0000000..342974e --- /dev/null +++ b/cache_forge/solidity-files-cache.json @@ -0,0 +1 @@ +{"_format":"","paths":{"artifacts":"out","build_infos":"out/build-info","sources":"contracts","tests":"test","scripts":"script","libraries":["lib","node_modules"]},"files":{"lib/forge-std/src/Base.sol":{"lastModificationDate":1725369084214,"contentHash":"ee13c050b1914464f1d3f90cde90204b","sourceName":"lib/forge-std/src/Base.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/StdStorage.sol","lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"CommonBase":{"0.8.26":{"path":"Base.sol/CommonBase.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"ScriptBase":{"0.8.26":{"path":"Base.sol/ScriptBase.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"TestBase":{"0.8.26":{"path":"Base.sol/TestBase.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdAssertions.sol":{"lastModificationDate":1725369084214,"contentHash":"25b77f9806b64d497b8a46aeb8e5f6f0","sourceName":"lib/forge-std/src/StdAssertions.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"StdAssertions":{"0.8.26":{"path":"StdAssertions.sol/StdAssertions.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdChains.sol":{"lastModificationDate":1725369084214,"contentHash":"134a39a51ebf6702e9537d9b72fc4812","sourceName":"lib/forge-std/src/StdChains.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"StdChains":{"0.8.26":{"path":"StdChains.sol/StdChains.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdCheats.sol":{"lastModificationDate":1725369084255,"contentHash":"7922ae0087a21ee3cdb97137be18c06c","sourceName":"lib/forge-std/src/StdCheats.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/StdStorage.sol","lib/forge-std/src/Vm.sol","lib/forge-std/src/console.sol","lib/forge-std/src/console2.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"StdCheats":{"0.8.26":{"path":"StdCheats.sol/StdCheats.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"StdCheatsSafe":{"0.8.26":{"path":"StdCheats.sol/StdCheatsSafe.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdError.sol":{"lastModificationDate":1725369084215,"contentHash":"64c896e1276a291776e5ea5aecb3870a","sourceName":"lib/forge-std/src/StdError.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"stdError":{"0.8.26":{"path":"StdError.sol/stdError.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdInvariant.sol":{"lastModificationDate":1725369084215,"contentHash":"f16837d0e7cb829544ae1f1319ea7643","sourceName":"lib/forge-std/src/StdInvariant.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"StdInvariant":{"0.8.26":{"path":"StdInvariant.sol/StdInvariant.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdJson.sol":{"lastModificationDate":1725369084215,"contentHash":"3339192c616789604138e2d8206c0702","sourceName":"lib/forge-std/src/StdJson.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.0, <0.9.0","artifacts":{"stdJson":{"0.8.26":{"path":"StdJson.sol/stdJson.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdMath.sol":{"lastModificationDate":1725369084215,"contentHash":"9da8f453eba6bb98f3d75bc6822bfb29","sourceName":"lib/forge-std/src/StdMath.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"stdMath":{"0.8.26":{"path":"StdMath.sol/stdMath.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdStorage.sol":{"lastModificationDate":1725369084215,"contentHash":"ce68f6e336944f16d31351a47d0b19b8","sourceName":"lib/forge-std/src/StdStorage.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"stdStorage":{"0.8.26":{"path":"StdStorage.sol/stdStorage.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"stdStorageSafe":{"0.8.26":{"path":"StdStorage.sol/stdStorageSafe.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdStyle.sol":{"lastModificationDate":1725369084215,"contentHash":"6281165a12aa639705c691fccefd855e","sourceName":"lib/forge-std/src/StdStyle.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.4.22, <0.9.0","artifacts":{"StdStyle":{"0.8.26":{"path":"StdStyle.sol/StdStyle.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdToml.sol":{"lastModificationDate":1725369084215,"contentHash":"2bb543c13f276e5db311aa3b81ed1651","sourceName":"lib/forge-std/src/StdToml.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol"],"versionRequirement":">=0.6.0, <0.9.0","artifacts":{"stdToml":{"0.8.26":{"path":"StdToml.sol/stdToml.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/StdUtils.sol":{"lastModificationDate":1725369084216,"contentHash":"b50717f17f251a2a96a5884c90add7bf","sourceName":"lib/forge-std/src/StdUtils.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Vm.sol","lib/forge-std/src/interfaces/IERC165.sol","lib/forge-std/src/interfaces/IERC20.sol","lib/forge-std/src/interfaces/IERC721.sol","lib/forge-std/src/interfaces/IMulticall3.sol","lib/forge-std/src/mocks/MockERC20.sol","lib/forge-std/src/mocks/MockERC721.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"StdUtils":{"0.8.26":{"path":"StdUtils.sol/StdUtils.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/Test.sol":{"lastModificationDate":1725369084216,"contentHash":"b6f15605355fc8c421fe42a90f94bf32","sourceName":"lib/forge-std/src/Test.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Base.sol","lib/forge-std/src/StdAssertions.sol","lib/forge-std/src/StdChains.sol","lib/forge-std/src/StdCheats.sol","lib/forge-std/src/StdError.sol","lib/forge-std/src/StdInvariant.sol","lib/forge-std/src/StdJson.sol","lib/forge-std/src/StdMath.sol","lib/forge-std/src/StdStorage.sol","lib/forge-std/src/StdStyle.sol","lib/forge-std/src/StdToml.sol","lib/forge-std/src/StdUtils.sol","lib/forge-std/src/Vm.sol","lib/forge-std/src/console.sol","lib/forge-std/src/console2.sol","lib/forge-std/src/interfaces/IERC165.sol","lib/forge-std/src/interfaces/IERC20.sol","lib/forge-std/src/interfaces/IERC721.sol","lib/forge-std/src/interfaces/IMulticall3.sol","lib/forge-std/src/mocks/MockERC20.sol","lib/forge-std/src/mocks/MockERC721.sol","lib/forge-std/src/safeconsole.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"Test":{"0.8.26":{"path":"Test.sol/Test.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/Vm.sol":{"lastModificationDate":1725369084255,"contentHash":"bd1ebd6d961246e45ffb60d5c3f68f82","sourceName":"lib/forge-std/src/Vm.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"Vm":{"0.8.26":{"path":"Vm.sol/Vm.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"VmSafe":{"0.8.26":{"path":"Vm.sol/VmSafe.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/console.sol":{"lastModificationDate":1725369084216,"contentHash":"19bc22856c14b56e4e55ab19c7a27c87","sourceName":"lib/forge-std/src/console.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.4.22, <0.9.0","artifacts":{"console":{"0.8.26":{"path":"console.sol/console.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/console2.sol":{"lastModificationDate":1725369084216,"contentHash":"f65ad21034b111e70fb5342d5771efcd","sourceName":"lib/forge-std/src/console2.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/console.sol"],"versionRequirement":">=0.4.22, <0.9.0","artifacts":{},"seenByCompiler":true},"lib/forge-std/src/interfaces/IERC165.sol":{"lastModificationDate":1725369084217,"contentHash":"90fe5e2e3ed432d6f3b408e7c9e8a739","sourceName":"lib/forge-std/src/interfaces/IERC165.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2","artifacts":{"IERC165":{"0.8.26":{"path":"IERC165.sol/IERC165.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/interfaces/IERC20.sol":{"lastModificationDate":1725369084217,"contentHash":"8099161d518e5862a76750349d58e801","sourceName":"lib/forge-std/src/interfaces/IERC20.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2","artifacts":{"IERC20":{"0.8.26":{"path":"IERC20.sol/IERC20.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/interfaces/IERC721.sol":{"lastModificationDate":1725369084217,"contentHash":"efc26e7f9a2f76b68088c8760ceae2dc","sourceName":"lib/forge-std/src/interfaces/IERC721.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/interfaces/IERC165.sol"],"versionRequirement":">=0.6.2","artifacts":{"IERC721":{"0.8.26":{"path":"IERC721.sol/IERC721.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"IERC721Enumerable":{"0.8.26":{"path":"IERC721.sol/IERC721Enumerable.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"IERC721Metadata":{"0.8.26":{"path":"IERC721.sol/IERC721Metadata.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}},"IERC721TokenReceiver":{"0.8.26":{"path":"IERC721.sol/IERC721TokenReceiver.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/interfaces/IMulticall3.sol":{"lastModificationDate":1725369084217,"contentHash":"7b131ca1ca32ef6378b7b9ad5488b901","sourceName":"lib/forge-std/src/interfaces/IMulticall3.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"IMulticall3":{"0.8.26":{"path":"IMulticall3.sol/IMulticall3.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/mocks/MockERC20.sol":{"lastModificationDate":1725369084217,"contentHash":"8e14d63e81e1d54dbc2d44df38ae9dec","sourceName":"lib/forge-std/src/mocks/MockERC20.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/interfaces/IERC20.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"MockERC20":{"0.8.26":{"path":"MockERC20.sol/MockERC20.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/mocks/MockERC721.sol":{"lastModificationDate":1725369084217,"contentHash":"e91cd9dba7f88f03710c56a347d89d1e","sourceName":"lib/forge-std/src/mocks/MockERC721.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/interfaces/IERC165.sol","lib/forge-std/src/interfaces/IERC721.sol"],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"MockERC721":{"0.8.26":{"path":"MockERC721.sol/MockERC721.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"lib/forge-std/src/safeconsole.sol":{"lastModificationDate":1725369084218,"contentHash":"1445aa2f47000e212173e0cefd6c7a77","sourceName":"lib/forge-std/src/safeconsole.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":">=0.6.2, <0.9.0","artifacts":{"safeconsole":{"0.8.26":{"path":"safeconsole.sol/safeconsole.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"src/AssociatedArrayLib.sol":{"lastModificationDate":1725369163930,"contentHash":"9e82f3d7f0b275b708a793d2d23c4a85","sourceName":"src/AssociatedArrayLib.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":[],"versionRequirement":"^0.8.20","artifacts":{"AssociatedArrayLib":{"0.8.26":{"path":"AssociatedArrayLib.sol/AssociatedArrayLib.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"src/EnumerableMap.sol":{"lastModificationDate":1725369248066,"contentHash":"7bcdddb4a14e6163792431e5b42abc85","sourceName":"src/EnumerableMap.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["src/AssociatedArrayLib.sol","src/EnumerableSet.sol"],"versionRequirement":"^0.8.20","artifacts":{"EnumerableMap":{"0.8.26":{"path":"EnumerableMap.sol/EnumerableMap.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"src/EnumerableSet.sol":{"lastModificationDate":1725369163930,"contentHash":"3763a1724aedd1b1c66c63e43806e58b","sourceName":"src/EnumerableSet.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["src/AssociatedArrayLib.sol"],"versionRequirement":"^0.8.20","artifacts":{"EnumerableSet":{"0.8.26":{"path":"EnumerableSet.sol/EnumerableSet.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"test/EnumerableMap.t.sol":{"lastModificationDate":1725369249161,"contentHash":"82eef0de83d92b3ae9b81064d43aaa1a","sourceName":"test/EnumerableMap.t.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Base.sol","lib/forge-std/src/StdAssertions.sol","lib/forge-std/src/StdChains.sol","lib/forge-std/src/StdCheats.sol","lib/forge-std/src/StdError.sol","lib/forge-std/src/StdInvariant.sol","lib/forge-std/src/StdJson.sol","lib/forge-std/src/StdMath.sol","lib/forge-std/src/StdStorage.sol","lib/forge-std/src/StdStyle.sol","lib/forge-std/src/StdToml.sol","lib/forge-std/src/StdUtils.sol","lib/forge-std/src/Test.sol","lib/forge-std/src/Vm.sol","lib/forge-std/src/console.sol","lib/forge-std/src/console2.sol","lib/forge-std/src/interfaces/IERC165.sol","lib/forge-std/src/interfaces/IERC20.sol","lib/forge-std/src/interfaces/IERC721.sol","lib/forge-std/src/interfaces/IMulticall3.sol","lib/forge-std/src/mocks/MockERC20.sol","lib/forge-std/src/mocks/MockERC721.sol","lib/forge-std/src/safeconsole.sol","src/AssociatedArrayLib.sol","src/EnumerableMap.sol","src/EnumerableSet.sol"],"versionRequirement":"^0.8.20","artifacts":{"EnumerableMapTest":{"0.8.26":{"path":"EnumerableMap.t.sol/EnumerableMapTest.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true},"test/EnumerableSet.t.sol":{"lastModificationDate":1725369249161,"contentHash":"4ca6f2358e7318070c31960cc9076fdc","sourceName":"test/EnumerableSet.t.sol","compilerSettings":{"solc":{"optimizer":{"enabled":true,"runs":1000000},"metadata":{"useLiteralContent":false,"bytecodeHash":"none","appendCBOR":true},"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"evmVersion":"paris","viaIR":false,"libraries":{}},"vyper":{"evmVersion":"paris","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode"]}}}},"imports":["lib/forge-std/src/Base.sol","lib/forge-std/src/StdAssertions.sol","lib/forge-std/src/StdChains.sol","lib/forge-std/src/StdCheats.sol","lib/forge-std/src/StdError.sol","lib/forge-std/src/StdInvariant.sol","lib/forge-std/src/StdJson.sol","lib/forge-std/src/StdMath.sol","lib/forge-std/src/StdStorage.sol","lib/forge-std/src/StdStyle.sol","lib/forge-std/src/StdToml.sol","lib/forge-std/src/StdUtils.sol","lib/forge-std/src/Test.sol","lib/forge-std/src/Vm.sol","lib/forge-std/src/console.sol","lib/forge-std/src/console2.sol","lib/forge-std/src/interfaces/IERC165.sol","lib/forge-std/src/interfaces/IERC20.sol","lib/forge-std/src/interfaces/IERC721.sol","lib/forge-std/src/interfaces/IMulticall3.sol","lib/forge-std/src/mocks/MockERC20.sol","lib/forge-std/src/mocks/MockERC721.sol","lib/forge-std/src/safeconsole.sol","src/AssociatedArrayLib.sol","src/EnumerableSet.sol"],"versionRequirement":"^0.8.20","artifacts":{"EnumerableSetTest":{"0.8.26":{"path":"EnumerableSet.t.sol/EnumerableSetTest.json","build_id":"f2ca0f1d97db9810b466965ac563ed0b"}}},"seenByCompiler":true}},"builds":["f2ca0f1d97db9810b466965ac563ed0b"]} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 25b918f..f5477b4 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,6 +1,30 @@ [profile.default] -src = "src" -out = "out" -libs = ["lib"] + auto_detect_solc = false + block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT + bytecode_hash = "none" + evm_version = "paris" # See https://www.evmdiff.com/features?name=PUSH0&kind=opcode + fuzz = { runs = 1_000 } + gas_reports = ["*"] + optimizer = true + optimizer_runs = 1_000_000 + out = "out" + script = "script" + solc = "0.8.26" + src = "contracts" + test = "test" + cache_path = "cache_forge" + libs = ["node_modules", "lib"] + gas_reports_ignore = ["LockTest"] + + +[fmt] + bracket_spacing = true + int_types = "long" + line_length = 120 + multiline_func_header = "all" + number_underscore = "thousands" + quote_style = "double" + tab_width = 4 + wrap_comments = true # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/src/AssociatedArrayLib.sol b/src/AssociatedArrayLib.sol new file mode 100644 index 0000000..851cc42 --- /dev/null +++ b/src/AssociatedArrayLib.sol @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +library AssociatedArrayLib { + using AssociatedArrayLib for *; + + error AssociatedArray_OutOfBounds(uint256 index); + + struct Array { + uint256 _spacer; + } + + function _length(Array storage s, address account) private view returns (uint256 __length) { + assembly { + mstore(0x00, account) + mstore(0x20, s.slot) + __length := sload(keccak256(0x00, 0x40)) + } + } + + function _get(Array storage s, address account, uint256 index) private view returns (bytes32 value) { + assembly { + mstore(0x00, account) + mstore(0x20, s.slot) + value := sload(add(keccak256(0x00, 0x40), mul(0x20, add(index, 1)))) + } + } + + function _getAll(Array storage s, address account) private view returns (bytes32[] memory values) { + uint256 __length = _length(s, account); + values = new bytes32[](__length); + for (uint256 i; i < __length; i++) { + values[i] = _get(s, account, i); + } + } + + function _set(Array storage s, address account, uint256 index, bytes32 value) private { + if (index >= _length(s, account)) revert AssociatedArray_OutOfBounds(index); + assembly { + mstore(0x00, account) + mstore(0x20, s.slot) + sstore(add(keccak256(0x00, 0x40), mul(0x20, add(index, 1))), value) + } + } + + function _push(Array storage s, address account, bytes32 value) private { + assembly { + mstore(0x00, account) // store a + mstore(0x20, s.slot) //store x + let slot := keccak256(0x00, 0x40) + // load length (stored @ slot), add 1 to it => index. + // mul index by 0x20 and add it to orig slot to get the next free slot + let index := add(sload(slot), 1) + sstore(add(slot, mul(0x20, index)), value) + sstore(slot, index) //increment length by 1 + } + } + + function _pop(Array storage s, address account) private { + uint256 __length = _length(s, account); + if (__length == 0) return; + _set(s, account, __length - 1, 0); + assembly { + mstore(0x00, account) + mstore(0x20, s.slot) + sstore(keccak256(0x00, 0x40), sub(__length, 1)) + } + } + + function _remove(Array storage s, address account, uint256 index) private { + uint256 __length = _length(s, account); + if (index >= __length) revert AssociatedArray_OutOfBounds(index); + _set(s, account, index, _get(s, account, __length - 1)); + assembly { + mstore(0x00, account) + mstore(0x20, s.slot) + sstore(keccak256(0x00, 0x40), sub(__length, 1)) + } + } + + struct Bytes32Array { + Array _inner; + } + + function length(Bytes32Array storage s, address account) internal view returns (uint256) { + return _length(s._inner, account); + } + + function get(Bytes32Array storage s, address account, uint256 index) internal view returns (bytes32) { + return _get(s._inner, account, index); + } + + function getAll(Bytes32Array storage s, address account) internal view returns (bytes32[] memory) { + return _getAll(s._inner, account); + } + + function set(Bytes32Array storage s, address account, uint256 index, bytes32 value) internal { + _set(s._inner, account, index, value); + } + + function push(Bytes32Array storage s, address account, bytes32 value) internal { + _push(s._inner, account, value); + } + + function pop(Bytes32Array storage s, address account) internal { + _pop(s._inner, account); + } + + function remove(Bytes32Array storage s, address account, uint256 index) internal { + _remove(s._inner, account, index); + } + + struct AddressArray { + Array _inner; + } + + function length(AddressArray storage s, address account) internal view returns (uint256) { + return _length(s._inner, account); + } + + function get(AddressArray storage s, address account, uint256 index) internal view returns (address) { + return address(uint160(uint256(_get(s._inner, account, index)))); + } + + function getAll(AddressArray storage s, address account) internal view returns (address[] memory) { + bytes32[] memory bytes32Array = _getAll(s._inner, account); + address[] memory addressArray; + + /// @solidity memory-safe-assembly + assembly { + addressArray := bytes32Array + } + return addressArray; + } + + function set(AddressArray storage s, address account, uint256 index, address value) internal { + _set(s._inner, account, index, bytes32(uint256(uint160(value)))); + } + + function push(AddressArray storage s, address account, address value) internal { + _push(s._inner, account, bytes32(uint256(uint160(value)))); + } + + function pop(AddressArray storage s, address account) internal { + _pop(s._inner, account); + } + + function remove(AddressArray storage s, address account, uint256 index) internal { + _remove(s._inner, account, index); + } + + struct UintArray { + Array _inner; + } + + function length(UintArray storage s, address account) internal view returns (uint256) { + return _length(s._inner, account); + } + + function get(UintArray storage s, address account, uint256 index) internal view returns (uint256) { + return uint256(_get(s._inner, account, index)); + } + + function getAll(UintArray storage s, address account) internal view returns (uint256[] memory) { + bytes32[] memory bytes32Array = _getAll(s._inner, account); + uint256[] memory uintArray; + + /// @solidity memory-safe-assembly + assembly { + uintArray := bytes32Array + } + return uintArray; + } + + function set(UintArray storage s, address account, uint256 index, uint256 value) internal { + _set(s._inner, account, index, bytes32(value)); + } + + function push(UintArray storage s, address account, uint256 value) internal { + _push(s._inner, account, bytes32(value)); + } + + function pop(UintArray storage s, address account) internal { + _pop(s._inner, account); + } + + function remove(UintArray storage s, address account, uint256 index) internal { + _remove(s._inner, account, index); + } +} diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/EnumerableMap.sol b/src/EnumerableMap.sol new file mode 100644 index 0000000..8e53cc1 --- /dev/null +++ b/src/EnumerableMap.sol @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol) +// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js. + +pragma solidity ^0.8.20; + +import {EnumerableSet} from "./EnumerableSet.sol"; + +/** + * Fork of OZ's EnumerableSet that makes all storage access ERC-4337 compliant via associated storage + * @author zeroknots.eth (rhinestone) + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ```solidity + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * ``` + * + * The following map types are supported: + * + * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 + * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 + * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0 + * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0 + * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0 + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableMap. + * ==== + */ +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + // To implement this library for multiple types with as little code repetition as possible, we write it in + // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions, + // and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map. + // This means that we can only create new EnumerableMaps for types that fit in bytes32. + + /** + * @dev Query for a nonexistent map key. + */ + error EnumerableMapNonexistentKey(bytes32 key); + + struct Bytes32ToBytes32Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 key => mapping(address account => bytes32)) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(Bytes32ToBytes32Map storage map, address account, bytes32 key, bytes32 value) + internal + returns (bool) + { + map._values[key][account] = value; + return map._keys.add(account, key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToBytes32Map storage map, address account, bytes32 key) internal returns (bool) { + delete map._values[key][account]; + return map._keys.remove(account, key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToBytes32Map storage map, address account, bytes32 key) internal view returns (bool) { + return map._keys.contains(account, key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + function length(Bytes32ToBytes32Map storage map, address account) internal view returns (uint256) { + return map._keys.length(account); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToBytes32Map storage map, address account, uint256 index) + internal + view + returns (bytes32, bytes32) + { + bytes32 key = map._keys.at(account, index); + return (key, map._values[key][account]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToBytes32Map storage map, address account, bytes32 key) + internal + view + returns (bool, bytes32) + { + bytes32 value = map._values[key][account]; + if (value == bytes32(0)) { + return (contains(map, account, key), bytes32(0)); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToBytes32Map storage map, address account, bytes32 key) internal view returns (bytes32) { + bytes32 value = map._values[key][account]; + if (value == 0 && !contains(map, account, key)) { + revert EnumerableMapNonexistentKey(key); + } + return value; + } + + /** + * @dev Return the an array containing all the keys + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function keys(Bytes32ToBytes32Map storage map, address account) internal view returns (bytes32[] memory) { + return map._keys.values(account); + } + + // UintToUintMap + + struct UintToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(UintToUintMap storage map, address account, uint256 key, uint256 value) internal returns (bool) { + return set(map._inner, account, bytes32(key), bytes32(value)); + } + + /** + * @dev Removes a value from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToUintMap storage map, address account, uint256 key) internal returns (bool) { + return remove(map._inner, account, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToUintMap storage map, address account, uint256 key) internal view returns (bool) { + return contains(map._inner, account, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToUintMap storage map, address account) internal view returns (uint256) { + return length(map._inner, account); + } + + /** + * @dev Returns the element stored at position `index` in the map. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToUintMap storage map, address account, uint256 index) internal view returns (uint256, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, account, index); + return (uint256(key), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToUintMap storage map, address account, uint256 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, account, bytes32(key)); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToUintMap storage map, address account, uint256 key) internal view returns (uint256) { + return uint256(get(map._inner, account, bytes32(key))); + } + + /** + * @dev Return the an array containing all the keys + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function keys(UintToUintMap storage map, address account) internal view returns (uint256[] memory) { + bytes32[] memory store = keys(map._inner, account); + uint256[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // UintToAddressMap + + struct UintToAddressMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(UintToAddressMap storage map, address account, uint256 key, address value) internal returns (bool) { + return set(map._inner, account, bytes32(key), bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToAddressMap storage map, address account, uint256 key) internal returns (bool) { + return remove(map._inner, account, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToAddressMap storage map, address account, uint256 key) internal view returns (bool) { + return contains(map._inner, account, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToAddressMap storage map, address account) internal view returns (uint256) { + return length(map._inner, account); + } + + /** + * @dev Returns the element stored at position `index` in the map. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToAddressMap storage map, address account, uint256 index) + internal + view + returns (uint256, address) + { + (bytes32 key, bytes32 value) = at(map._inner, account, index); + return (uint256(key), address(uint160(uint256(value)))); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToAddressMap storage map, address account, uint256 key) internal view returns (bool, address) { + (bool success, bytes32 value) = tryGet(map._inner, account, bytes32(key)); + return (success, address(uint160(uint256(value)))); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToAddressMap storage map, address account, uint256 key) internal view returns (address) { + return address(uint160(uint256(get(map._inner, account, bytes32(key))))); + } + + /** + * @dev Return the an array containing all the keys + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function keys(UintToAddressMap storage map, address account) internal view returns (uint256[] memory) { + bytes32[] memory store = keys(map._inner, account); + uint256[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // AddressToUintMap + + struct AddressToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(AddressToUintMap storage map, address account, address key, uint256 value) internal returns (bool) { + return set(map._inner, account, bytes32(uint256(uint160(key))), bytes32(value)); + } + + /** + * @dev Removes a value from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(AddressToUintMap storage map, address account, address key) internal returns (bool) { + return remove(map._inner, account, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(AddressToUintMap storage map, address account, address key) internal view returns (bool) { + return contains(map._inner, account, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(AddressToUintMap storage map, address account) internal view returns (uint256) { + return length(map._inner, account); + } + + /** + * @dev Returns the element stored at position `index` in the map. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressToUintMap storage map, address account, uint256 index) + internal + view + returns (address, uint256) + { + (bytes32 key, bytes32 value) = at(map._inner, account, index); + return (address(uint160(uint256(key))), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(AddressToUintMap storage map, address account, address key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, account, bytes32(uint256(uint160(key)))); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(AddressToUintMap storage map, address account, address key) internal view returns (uint256) { + return uint256(get(map._inner, account, bytes32(uint256(uint160(key))))); + } + + /** + * @dev Return the an array containing all the keys + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function keys(AddressToUintMap storage map, address account) internal view returns (address[] memory) { + bytes32[] memory store = keys(map._inner, account); + address[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // Bytes32ToUintMap + + struct Bytes32ToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set(Bytes32ToUintMap storage map, address account, bytes32 key, uint256 value) internal returns (bool) { + return set(map._inner, account, key, bytes32(value)); + } + + /** + * @dev Removes a value from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToUintMap storage map, address account, bytes32 key) internal returns (bool) { + return remove(map._inner, account, key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToUintMap storage map, address account, bytes32 key) internal view returns (bool) { + return contains(map._inner, account, key); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(Bytes32ToUintMap storage map, address account) internal view returns (uint256) { + return length(map._inner, account); + } + + /** + * @dev Returns the element stored at position `index` in the map. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToUintMap storage map, address account, uint256 index) + internal + view + returns (bytes32, uint256) + { + (bytes32 key, bytes32 value) = at(map._inner, account, index); + return (key, uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToUintMap storage map, address account, bytes32 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, account, key); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToUintMap storage map, address account, bytes32 key) internal view returns (uint256) { + return uint256(get(map._inner, account, key)); + } + + /** + * @dev Return the an array containing all the keys + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function keys(Bytes32ToUintMap storage map, address account) internal view returns (bytes32[] memory) { + bytes32[] memory store = keys(map._inner, account); + bytes32[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } +} diff --git a/src/EnumerableSet.sol b/src/EnumerableSet.sol new file mode 100644 index 0000000..6b6c76f --- /dev/null +++ b/src/EnumerableSet.sol @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import "./AssociatedArrayLib.sol"; + +/** + * Fork of OZ's EnumerableSet that makes all storage access ERC-4337 compliant via associated storage + * @author zeroknots.eth (rhinestone) + */ +library EnumerableSet { + using AssociatedArrayLib for AssociatedArrayLib.Bytes32Array; + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Set type with + // bytes32 values. + // The Set implementation uses private functions, and user-facing + // implementations (such as AddressSet) are just wrappers around the + // underlying Set. + // This means that we can only create new EnumerableSets for types that fit + // in bytes32. + + struct Set { + // Storage of set values + AssociatedArrayLib.Bytes32Array _values; + // Position is the index of the value in the `values` array plus 1. + // Position 0 is used to mean a value is not in the set. + mapping(bytes32 value => mapping(address account => uint256)) _positions; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function _add(Set storage set, address account, bytes32 value) private returns (bool) { + if (!_contains(set, account, value)) { + set._values.push(account, value); + // The value is stored at length-1, but we add 1 to all indexes + // and use 0 as a sentinel value + set._positions[value][account] = set._values.length(account); + return true; + } else { + return false; + } + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function _remove(Set storage set, address account, bytes32 value) private returns (bool) { + // We cache the value's position to prevent multiple reads from the same storage slot + uint256 position = set._positions[value][account]; + + if (position != 0) { + // Equivalent to contains(set, value) + // To delete an element from the _values array in O(1), we swap the element to delete with the last one in + // the array, and then remove the last element (sometimes called as 'swap and pop'). + // This modifies the order of the array, as noted in {at}. + + uint256 valueIndex = position - 1; + uint256 lastIndex = set._values.length(account) - 1; + + if (valueIndex != lastIndex) { + bytes32 lastValue = set._values.get(account, lastIndex); + + // Move the lastValue to the index where the value to delete is + set._values.set(account, valueIndex, lastValue); + // Update the tracked position of the lastValue (that was just moved) + set._positions[lastValue][account] = position; + } + + // Delete the slot where the moved value was stored + set._values.pop(account); + + // Delete the tracked position for the deleted slot + delete set._positions[value][account]; + + return true; + } else { + return false; + } + } + + function _removeAll(Set storage set, address account) internal { + uint256 len = _length(set, account); + for (uint256 i; i < len; i++) { + _remove(set, account, _at(set, account, i)); + } + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function _contains(Set storage set, address account, bytes32 value) private view returns (bool) { + return set._positions[value][account] != 0; + } + + /** + * @dev Returns the number of values on the set. O(1). + */ + function _length(Set storage set, address account) private view returns (uint256) { + return set._values.length(account); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function _at(Set storage set, address account, uint256 index) private view returns (bytes32) { + return set._values.get(account, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function _values(Set storage set, address account) private view returns (bytes32[] memory) { + return set._values.getAll(account); + } + + // Bytes32Set + + struct Bytes32Set { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) { + return _add(set._inner, account, value); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(Bytes32Set storage set, address account, bytes32 value) internal returns (bool) { + return _remove(set._inner, account, value); + } + + function removeAll(Bytes32Set storage set, address account) internal { + return _removeAll(set._inner, account); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(Bytes32Set storage set, address account, bytes32 value) internal view returns (bool) { + return _contains(set._inner, account, value); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(Bytes32Set storage set, address account) internal view returns (uint256) { + return _length(set._inner, account); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32Set storage set, address account, uint256 index) internal view returns (bytes32) { + return _at(set._inner, account, index); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(Bytes32Set storage set, address account) internal view returns (bytes32[] memory) { + bytes32[] memory store = _values(set._inner, account); + bytes32[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // AddressSet + + struct AddressSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(AddressSet storage set, address account, address value) internal returns (bool) { + return _add(set._inner, account, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(AddressSet storage set, address account, address value) internal returns (bool) { + return _remove(set._inner, account, bytes32(uint256(uint160(value)))); + } + + function removeAll(AddressSet storage set, address account) internal { + return _removeAll(set._inner, account); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(AddressSet storage set, address account, address value) internal view returns (bool) { + return _contains(set._inner, account, bytes32(uint256(uint160(value)))); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(AddressSet storage set, address account) internal view returns (uint256) { + return _length(set._inner, account); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressSet storage set, address account, uint256 index) internal view returns (address) { + return address(uint160(uint256(_at(set._inner, account, index)))); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(AddressSet storage set, address account) internal view returns (address[] memory) { + bytes32[] memory store = _values(set._inner, account); + address[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } + + // UintSet + + struct UintSet { + Set _inner; + } + + /** + * @dev Add a value to a set. O(1). + * + * Returns true if the value was added to the set, that is if it was not + * already present. + */ + function add(UintSet storage set, address account, uint256 value) internal returns (bool) { + return _add(set._inner, account, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the value was removed from the set, that is if it was + * present. + */ + function remove(UintSet storage set, address account, uint256 value) internal returns (bool) { + return _remove(set._inner, account, bytes32(value)); + } + + function removeAll(UintSet storage set, address account) internal { + return _removeAll(set._inner, account); + } + + /** + * @dev Returns true if the value is in the set. O(1). + */ + function contains(UintSet storage set, address account, uint256 value) internal view returns (bool) { + return _contains(set._inner, account, bytes32(value)); + } + + /** + * @dev Returns the number of values in the set. O(1). + */ + function length(UintSet storage set, address account) internal view returns (uint256) { + return _length(set._inner, account); + } + + /** + * @dev Returns the value stored at position `index` in the set. O(1). + * + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintSet storage set, address account, uint256 index) internal view returns (uint256) { + return uint256(_at(set._inner, account, index)); + } + + /** + * @dev Return the entire set in an array + * + * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed + * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that + * this function has an unbounded cost, and using it as part of a state-changing function may render the function + * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. + */ + function values(UintSet storage set, address account) internal view returns (uint256[] memory) { + bytes32[] memory store = _values(set._inner, account); + uint256[] memory result; + + /// @solidity memory-safe-assembly + assembly { + result := store + } + + return result; + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/test/EnumerableMap.t.sol b/test/EnumerableMap.t.sol new file mode 100644 index 0000000..ea1508d --- /dev/null +++ b/test/EnumerableMap.t.sol @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "src/EnumerableMap.sol"; + +contract EnumerableMapTest is Test { + using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map; + using EnumerableMap for EnumerableMap.UintToUintMap; + using EnumerableMap for EnumerableMap.UintToAddressMap; + using EnumerableMap for EnumerableMap.AddressToUintMap; + using EnumerableMap for EnumerableMap.Bytes32ToUintMap; + + EnumerableMap.Bytes32ToBytes32Map private bytes32ToBytes32Map; + EnumerableMap.UintToUintMap private uintToUintMap; + EnumerableMap.UintToAddressMap private uintToAddressMap; + EnumerableMap.AddressToUintMap private addressToUintMap; + EnumerableMap.Bytes32ToUintMap private bytes32ToUintMap; + + address private constant ACCOUNT = address(0x1234); + + function setUp() public { + // Setup is empty as we're using fresh state for each test + } + + function testBytes32ToBytes32Map() public { + bytes32 key1 = bytes32(uint256(1)); + bytes32 value1 = bytes32(uint256(100)); + bytes32 key2 = bytes32(uint256(2)); + bytes32 value2 = bytes32(uint256(200)); + + // Test set + assertTrue(bytes32ToBytes32Map.set(ACCOUNT, key1, value1)); + assertFalse(bytes32ToBytes32Map.set(ACCOUNT, key1, value1)); // Duplicate set should return false + + // Test contains + assertTrue(bytes32ToBytes32Map.contains(ACCOUNT, key1)); + assertFalse(bytes32ToBytes32Map.contains(ACCOUNT, key2)); + + // Test length + assertEq(bytes32ToBytes32Map.length(ACCOUNT), 1); + + // Test get + assertEq(bytes32ToBytes32Map.get(ACCOUNT, key1), value1); + + // Test tryGet + (bool success, bytes32 result) = bytes32ToBytes32Map.tryGet(ACCOUNT, key1); + assertTrue(success); + assertEq(result, value1); + + (success, result) = bytes32ToBytes32Map.tryGet(ACCOUNT, key2); + assertFalse(success); + assertEq(result, bytes32(0)); + + // Test remove + assertTrue(bytes32ToBytes32Map.remove(ACCOUNT, key1)); + assertFalse(bytes32ToBytes32Map.remove(ACCOUNT, key1)); // Removing non-existent key should return false + + // Test at and keys + bytes32ToBytes32Map.set(ACCOUNT, key1, value1); + bytes32ToBytes32Map.set(ACCOUNT, key2, value2); + (bytes32 atKey, bytes32 atValue) = bytes32ToBytes32Map.at(ACCOUNT, 0); + assertTrue(atKey == key1 || atKey == key2); + assertTrue(atValue == value1 || atValue == value2); + + bytes32[] memory keys = bytes32ToBytes32Map.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue(keys[0] == key1 || keys[0] == key2); + assertTrue(keys[1] == key1 || keys[1] == key2); + } + + function testUintToUintMap() public { + uint256 key1 = 1; + uint256 value1 = 100; + uint256 key2 = 2; + uint256 value2 = 200; + + // Test set + assertTrue(uintToUintMap.set(ACCOUNT, key1, value1)); + assertFalse(uintToUintMap.set(ACCOUNT, key1, value1)); // Duplicate set should return false + + // Test contains + assertTrue(uintToUintMap.contains(ACCOUNT, key1)); + assertFalse(uintToUintMap.contains(ACCOUNT, key2)); + + // Test length + assertEq(uintToUintMap.length(ACCOUNT), 1); + + // Test get + assertEq(uintToUintMap.get(ACCOUNT, key1), value1); + + // Test tryGet + (bool success, uint256 result) = uintToUintMap.tryGet(ACCOUNT, key1); + assertTrue(success); + assertEq(result, value1); + + (success, result) = uintToUintMap.tryGet(ACCOUNT, key2); + assertFalse(success); + assertEq(result, 0); + + // Test remove + assertTrue(uintToUintMap.remove(ACCOUNT, key1)); + assertFalse(uintToUintMap.remove(ACCOUNT, key1)); // Removing non-existent key should return false + + // Test at and keys + uintToUintMap.set(ACCOUNT, key1, value1); + uintToUintMap.set(ACCOUNT, key2, value2); + (uint256 atKey, uint256 atValue) = uintToUintMap.at(ACCOUNT, 0); + assertTrue(atKey == key1 || atKey == key2); + assertTrue(atValue == value1 || atValue == value2); + + uint256[] memory keys = uintToUintMap.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue(keys[0] == key1 || keys[0] == key2); + assertTrue(keys[1] == key1 || keys[1] == key2); + } + + function testUintToAddressMap() public { + uint256 key1 = 1; + address value1 = address(0x5678); + uint256 key2 = 2; + address value2 = address(0x9ABC); + + // Test set + assertTrue(uintToAddressMap.set(ACCOUNT, key1, value1)); + assertFalse(uintToAddressMap.set(ACCOUNT, key1, value1)); // Duplicate set should return false + + // Test contains + assertTrue(uintToAddressMap.contains(ACCOUNT, key1)); + assertFalse(uintToAddressMap.contains(ACCOUNT, key2)); + + // Test length + assertEq(uintToAddressMap.length(ACCOUNT), 1); + + // Test get + assertEq(uintToAddressMap.get(ACCOUNT, key1), value1); + + // Test tryGet + (bool success, address result) = uintToAddressMap.tryGet(ACCOUNT, key1); + assertTrue(success); + assertEq(result, value1); + + (success, result) = uintToAddressMap.tryGet(ACCOUNT, key2); + assertFalse(success); + assertEq(result, address(0)); + + // Test remove + assertTrue(uintToAddressMap.remove(ACCOUNT, key1)); + assertFalse(uintToAddressMap.remove(ACCOUNT, key1)); // Removing non-existent key should return false + + // Test at and keys + uintToAddressMap.set(ACCOUNT, key1, value1); + uintToAddressMap.set(ACCOUNT, key2, value2); + (uint256 atKey, address atValue) = uintToAddressMap.at(ACCOUNT, 0); + assertTrue(atKey == key1 || atKey == key2); + assertTrue(atValue == value1 || atValue == value2); + + uint256[] memory keys = uintToAddressMap.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue(keys[0] == key1 || keys[0] == key2); + assertTrue(keys[1] == key1 || keys[1] == key2); + } + + function testAddressToUintMap() public { + address key1 = address(0x5678); + uint256 value1 = 100; + address key2 = address(0x9ABC); + uint256 value2 = 200; + + // Test set + assertTrue(addressToUintMap.set(ACCOUNT, key1, value1)); + assertFalse(addressToUintMap.set(ACCOUNT, key1, value1)); // Duplicate set should return false + + // Test contains + assertTrue(addressToUintMap.contains(ACCOUNT, key1)); + assertFalse(addressToUintMap.contains(ACCOUNT, key2)); + + // Test length + assertEq(addressToUintMap.length(ACCOUNT), 1); + + // Test get + assertEq(addressToUintMap.get(ACCOUNT, key1), value1); + + // Test tryGet + (bool success, uint256 result) = addressToUintMap.tryGet(ACCOUNT, key1); + assertTrue(success); + assertEq(result, value1); + + (success, result) = addressToUintMap.tryGet(ACCOUNT, key2); + assertFalse(success); + assertEq(result, 0); + + // Test remove + assertTrue(addressToUintMap.remove(ACCOUNT, key1)); + assertFalse(addressToUintMap.remove(ACCOUNT, key1)); // Removing non-existent key should return false + + // Test at and keys + addressToUintMap.set(ACCOUNT, key1, value1); + addressToUintMap.set(ACCOUNT, key2, value2); + (address atKey, uint256 atValue) = addressToUintMap.at(ACCOUNT, 0); + assertTrue(atKey == key1 || atKey == key2); + assertTrue(atValue == value1 || atValue == value2); + + address[] memory keys = addressToUintMap.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue(keys[0] == key1 || keys[0] == key2); + assertTrue(keys[1] == key1 || keys[1] == key2); + } + + function testBytes32ToUintMap() public { + bytes32 key1 = bytes32(uint256(1)); + uint256 value1 = 100; + bytes32 key2 = bytes32(uint256(2)); + uint256 value2 = 200; + + // Test set + assertTrue(bytes32ToUintMap.set(ACCOUNT, key1, value1)); + assertFalse(bytes32ToUintMap.set(ACCOUNT, key1, value1)); // Duplicate set should return false + + // Test contains + assertTrue(bytes32ToUintMap.contains(ACCOUNT, key1)); + assertFalse(bytes32ToUintMap.contains(ACCOUNT, key2)); + + // Test length + assertEq(bytes32ToUintMap.length(ACCOUNT), 1); + + // Test get + assertEq(bytes32ToUintMap.get(ACCOUNT, key1), value1); + + // Test tryGet + (bool success, uint256 result) = bytes32ToUintMap.tryGet(ACCOUNT, key1); + assertTrue(success); + assertEq(result, value1); + + (success, result) = bytes32ToUintMap.tryGet(ACCOUNT, key2); + assertFalse(success); + assertEq(result, 0); + + // Test remove + assertTrue(bytes32ToUintMap.remove(ACCOUNT, key1)); + assertFalse(bytes32ToUintMap.remove(ACCOUNT, key1)); // Removing non-existent key should return false + + // Test at and keys + bytes32ToUintMap.set(ACCOUNT, key1, value1); + bytes32ToUintMap.set(ACCOUNT, key2, value2); + (bytes32 atKey, uint256 atValue) = bytes32ToUintMap.at(ACCOUNT, 0); + assertTrue(atKey == key1 || atKey == key2); + assertTrue(atValue == value1 || atValue == value2); + + bytes32[] memory keys = bytes32ToUintMap.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue(keys[0] == key1 || keys[0] == key2); + assertTrue(keys[1] == key1 || keys[1] == key2); + } + + function testFuzzBytes32ToBytes32Map(bytes32 key1, bytes32 value1, bytes32 key2, bytes32 value2) public { + vm.assume(key1 != key2); + + assertTrue(bytes32ToBytes32Map.set(ACCOUNT, key1, value1)); + assertTrue(bytes32ToBytes32Map.set(ACCOUNT, key2, value2)); + + assertEq(bytes32ToBytes32Map.length(ACCOUNT), 2); + assertTrue(bytes32ToBytes32Map.contains(ACCOUNT, key1)); + assertTrue(bytes32ToBytes32Map.contains(ACCOUNT, key2)); + + assertEq(bytes32ToBytes32Map.get(ACCOUNT, key1), value1); + assertEq(bytes32ToBytes32Map.get(ACCOUNT, key2), value2); + + bytes32[] memory keys = bytes32ToBytes32Map.keys(ACCOUNT); + assertEq(keys.length, 2); + assertTrue((keys[0] == key1 && keys[1] == key2) || (keys[0] == key2 && keys[1] == key1)); + + assertTrue(bytes32ToBytes32Map.remove(ACCOUNT, key1)); + assertFalse(bytes32ToBytes32Map.contains(ACCOUNT, key1)); + assertEq(bytes32ToBytes32Map.length(ACCOUNT), 1); + } +} diff --git a/test/EnumerableSet.t.sol b/test/EnumerableSet.t.sol new file mode 100644 index 0000000..f1dc362 --- /dev/null +++ b/test/EnumerableSet.t.sol @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "src/EnumerableSet.sol"; + +contract EnumerableSetTest is Test { + using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableSet for EnumerableSet.UintSet; + + EnumerableSet.Bytes32Set private bytes32Set; + EnumerableSet.AddressSet private addressSet; + EnumerableSet.UintSet private uintSet; + + address private constant ACCOUNT = address(0x1234); + + function setUp() public { + // Setup is empty as we're using fresh state for each test + } + + function testBytes32Set() public { + bytes32 value1 = bytes32(uint256(1)); + bytes32 value2 = bytes32(uint256(2)); + + // Test add + assertTrue(bytes32Set.add(ACCOUNT, value1)); + assertFalse(bytes32Set.add(ACCOUNT, value1)); // Duplicate add should return false + + // Test contains + assertTrue(bytes32Set.contains(ACCOUNT, value1)); + assertFalse(bytes32Set.contains(ACCOUNT, value2)); + + // Test length + assertEq(bytes32Set.length(ACCOUNT), 1); + + // Test at + assertEq(bytes32Set.at(ACCOUNT, 0), value1); + + // Test remove + assertTrue(bytes32Set.remove(ACCOUNT, value1)); + assertFalse(bytes32Set.remove(ACCOUNT, value1)); // Removing non-existent element should return false + + // Test values + bytes32Set.add(ACCOUNT, value1); + bytes32Set.add(ACCOUNT, value2); + bytes32[] memory values = bytes32Set.values(ACCOUNT); + assertEq(values.length, 2); + assertTrue(values[0] == value1 || values[1] == value1); + assertTrue(values[0] == value2 || values[1] == value2); + } + + function testAddressSet() public { + address value1 = address(0x5678); + address value2 = address(0x9ABC); + + // Test add + assertTrue(addressSet.add(ACCOUNT, value1)); + assertFalse(addressSet.add(ACCOUNT, value1)); // Duplicate add should return false + + // Test contains + assertTrue(addressSet.contains(ACCOUNT, value1)); + assertFalse(addressSet.contains(ACCOUNT, value2)); + + // Test length + assertEq(addressSet.length(ACCOUNT), 1); + + // Test at + assertEq(addressSet.at(ACCOUNT, 0), value1); + + // Test remove + assertTrue(addressSet.remove(ACCOUNT, value1)); + assertFalse(addressSet.remove(ACCOUNT, value1)); // Removing non-existent element should return false + + // Test values + addressSet.add(ACCOUNT, value1); + addressSet.add(ACCOUNT, value2); + address[] memory values = addressSet.values(ACCOUNT); + assertEq(values.length, 2); + assertTrue(values[0] == value1 || values[1] == value1); + assertTrue(values[0] == value2 || values[1] == value2); + } + + function testUintSet() public { + uint256 value1 = 1; + uint256 value2 = 2; + + // Test add + assertTrue(uintSet.add(ACCOUNT, value1)); + assertFalse(uintSet.add(ACCOUNT, value1)); // Duplicate add should return false + + // Test contains + assertTrue(uintSet.contains(ACCOUNT, value1)); + assertFalse(uintSet.contains(ACCOUNT, value2)); + + // Test length + assertEq(uintSet.length(ACCOUNT), 1); + + // Test at + assertEq(uintSet.at(ACCOUNT, 0), value1); + + // Test remove + assertTrue(uintSet.remove(ACCOUNT, value1)); + assertFalse(uintSet.remove(ACCOUNT, value1)); // Removing non-existent element should return false + + // Test values + uintSet.add(ACCOUNT, value1); + uintSet.add(ACCOUNT, value2); + uint256[] memory values = uintSet.values(ACCOUNT); + assertEq(values.length, 2); + assertTrue(values[0] == value1 || values[1] == value1); + assertTrue(values[0] == value2 || values[1] == value2); + } + + function testFuzzBytes32Set(bytes32[] memory testValues) public { + uint256 uniqueCount = 0; + for (uint256 i = 0; i < testValues.length; i++) { + if (bytes32Set.add(ACCOUNT, testValues[i])) { + uniqueCount++; + } + } + + assertEq(bytes32Set.length(ACCOUNT), uniqueCount); + + for (uint256 i = 0; i < testValues.length; i++) { + assertTrue(bytes32Set.contains(ACCOUNT, testValues[i])); + } + + bytes32[] memory values = bytes32Set.values(ACCOUNT); + assertEq(values.length, uniqueCount); + + for (uint256 i = 0; i < uniqueCount; i++) { + assertTrue(bytes32Set.remove(ACCOUNT, values[i])); + } + + assertEq(bytes32Set.length(ACCOUNT), 0); + } + + function testFuzzAddressSet(address[] memory testValues) public { + uint256 uniqueCount = 0; + for (uint256 i = 0; i < testValues.length; i++) { + if (addressSet.add(ACCOUNT, testValues[i])) { + uniqueCount++; + } + } + + assertEq(addressSet.length(ACCOUNT), uniqueCount); + + for (uint256 i = 0; i < testValues.length; i++) { + assertTrue(addressSet.contains(ACCOUNT, testValues[i])); + } + + address[] memory values = addressSet.values(ACCOUNT); + assertEq(values.length, uniqueCount); + + for (uint256 i = 0; i < uniqueCount; i++) { + assertTrue(addressSet.remove(ACCOUNT, values[i])); + } + + assertEq(addressSet.length(ACCOUNT), 0); + } + + function testFuzzUintSet(uint256[] memory testValues) public { + uint256 uniqueCount = 0; + for (uint256 i = 0; i < testValues.length; i++) { + if (uintSet.add(ACCOUNT, testValues[i])) { + uniqueCount++; + } + } + + assertEq(uintSet.length(ACCOUNT), uniqueCount); + + for (uint256 i = 0; i < testValues.length; i++) { + assertTrue(uintSet.contains(ACCOUNT, testValues[i])); + } + + uint256[] memory values = uintSet.values(ACCOUNT); + assertEq(values.length, uniqueCount); + + for (uint256 i = 0; i < uniqueCount; i++) { + assertTrue(uintSet.remove(ACCOUNT, values[i])); + } + + assertEq(uintSet.length(ACCOUNT), 0); + } +}