diff --git a/README.md b/README.md index 36db78ab..dc64158c 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ See more contract diagrams [here](./examples/README.md). Storage layout diagram of USDC's [verified source code](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code) on Etherscan. ![USDC](./examples/storage/usdcData.svg) -See more contract storage diagram examples [here](./examples/storage/README.md). +See an explanation of how storage diagrams work with lots of examples [here](./examples/storage/README.md). # Install @@ -54,14 +54,19 @@ The three subcommands: The Solidity code can be pulled from verified source code on Blockchain explorers like Etherscan or from local Solidity files. Options: - -V, --version output the version number -sf, --subfolders number of subfolders that will be recursively searched for Solidity files. (default: all) -f, --outputFormat output file format. (choices: "svg", "png", "dot", "all", default: "svg") -o, --outputFileName output file name -i, --ignoreFilesOrFolders comma separated list of files or folders to ignore - -n, --network Ethereum network (choices: "mainnet", "ropsten", "kovan", "rinkeby", "goerli", "sepolia", "polygon", "testnet.polygon", "arbitrum", "testnet.arbitrum", "avalanche", "testnet.avalanche", "bsc", "testnet.bsc", "crono", "fantom", "testnet.fantom", "moonbeam", "optimistic", "kovan-optimistic", "gnosisscan", default: "mainnet", env: ETH_NETWORK) - -k, --apiKey Blockchain explorer API key. eg Etherscan, Arbiscan, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key (env: SCAN_API_KEY) + -n, --network Ethereum network (choices: "mainnet", "ropsten", "kovan", "rinkeby", "goerli", "sepolia", "polygon", "testnet.polygon", "arbitrum", "testnet.arbitrum", "avalanche", "testnet.avalanche", "bsc", "testnet.bsc", "crono", "fantom", + "testnet.fantom", "moonbeam", "optimistic", "kovan-optimistic", "gnosisscan", default: "mainnet", env: ETH_NETWORK) + -k, --apiKey Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key (env: SCAN_API_KEY) + -bc, --backColor Canvas background color. "none" will use a transparent canvas. (default: "white") + -sc, --shapeColor Basic drawing color for graphics, not text (default: "black") + -fc, --fillColor Color used to fill the background of a node (default: "gray95") + -tc, --textColor Color used for text (default: "black") -v, --verbose run with debugging statements (default: false) + -V, --version output the version number -h, --help display help for command Commands: @@ -131,6 +136,7 @@ Options: -s, --storage
The address of the contract with the storage values. This will be different from the contract with the code if a proxy contract is used. This is not needed if `fileFolderAddress` is an address and the contract is not proxied. -u, --url URL of the Ethereum node to get storage values if the `data` option is used. (default: "http://localhost:8545", env: NODE_URL) -bn, --block Block number to get the contract storage values from. (default: "latest") + -a, --array Number of slots to display at the start and end of arrays. (default: "2") -h, --help display help for command ``` @@ -272,6 +278,20 @@ Heads/Tails: See more storage slot diagrams [here](./examples/storage/README.md). +# Styling Colors + +The colors use by the diagrams can be configured using the `backColor`, `shapeColor`, `fillColor` and `textColor` options. +sol2uml uses the [X11 color scheme](https://graphviz.org/doc/info/colors.html#x11) for named colors. +Other color formats like Red-Green-Blue (RGB) can also be used. For example, #ffffff for white and #000000 for black. +See [Graphviz color](https://graphviz.org/docs/attr-types/color/) documentation for more details. + +Here's an example using the color options +``` +sol2uml storage -sc deeppink -tc #ffffff -fc dimgrey -bc black 0xfCc00A1e250644d89AF0df661bC6f04891E21585 +``` + +![Aave V3 Pool](./examples/storage/AaveV3PoolStorageColor.svg ) + # Version 2.x changes The biggest change with 2.x is the introduction of subcommands as sol2uml can now draw contract storage diagrams. diff --git a/examples/inheritanceDiamond.png b/examples/inheritanceDiamond.png index ff06bf67..f3774429 100644 Binary files a/examples/inheritanceDiamond.png and b/examples/inheritanceDiamond.png differ diff --git a/examples/storage/AaveV3PoolStorageColor.svg b/examples/storage/AaveV3PoolStorageColor.svg new file mode 100644 index 00000000..1d63fcf5 --- /dev/null +++ b/examples/storage/AaveV3PoolStorageColor.svg @@ -0,0 +1,270 @@ + + + + + + +StorageDiagram + + + +6 + +Pool <<Contract>> +0xfCc00A1e250644d89AF0df661bC6f04891E21585 + +slot + +0 + +1 + +2-51 + +52 + +53 + +54 + +55 + +56 + +57 + +58 + +59 + +type: <inherited contract>.variable (bytes) + +uint256: VersionedInitializable.lastInitializedRevision (32) + +unallocated (31) + +bool: VersionedInitializable.initializing (1) + +uint256[50]: VersionedInitializable.______gap (1600) + +mapping(address=>DataTypes.ReserveData): PoolStorage._reserves (32) + +mapping(address=>DataTypes.UserConfigurationMap): PoolStorage._usersConfig (32) + +mapping(uint256=>address): PoolStorage._reservesList (32) + +mapping(uint8=>DataTypes.EModeCategory): PoolStorage._eModeCategories (32) + +mapping(address=>uint8): PoolStorage._usersEModeCategory (32) + +uint256: PoolStorage._bridgeProtocolFee (32) + +uint128: PoolStorage._flashLoanPremiumToProtocol (16) + +uint128: PoolStorage._flashLoanPremiumTotal (16) + +unallocated (22) + +uint16: PoolStorage._reservesCount (2) + +uint64: PoolStorage._maxStableRateBorrowSizePercent (8) + + + +1 + +uint256[50]: ______gap <<Array>> + +slot + +2 + +3 + +4 + +5-48 + +49 + +50 + +51 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + +uint256 (32) + +---- (1376) + +uint256 (32) + +uint256 (32) + +uint256 (32) + + + +6:10->1 + + + + + +3 + +ReserveData <<Struct>> + +offset + +0 + +1 + +2 + +3 + +4 + +5 + +6 + +7 + +8 + +9 + +type: variable (bytes) + +ReserveConfigurationMap: configuration (32) + +uint128: currentLiquidityRate (16) + +uint128: liquidityIndex (16) + +uint128: currentVariableBorrowRate (16) + +uint128: variableBorrowIndex (16) + +unallocated (9) + +uint16: id (2) + +uint40: lastUpdateTimestamp (5) + +uint128: currentStableBorrowRate (16) + +unallocated (12) + +address: aTokenAddress (20) + +unallocated (12) + +address: stableDebtTokenAddress (20) + +unallocated (12) + +address: variableDebtTokenAddress (20) + +unallocated (12) + +address: interestRateStrategyAddress (20) + +uint128: unbacked (16) + +uint128: accruedToTreasury (16) + +unallocated (16) + +uint128: isolationModeTotalDebt (16) + + + +6:27->3 + + + + + +4 + +UserConfigurationMap <<Struct>> + +offset + +0 + +type: variable (bytes) + +uint256: data (32) + + + +6:29->4 + + + + + +5 + +EModeCategory <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (6) + +address: priceSource (20) + +uint16: liquidationBonus (2) + +uint16: liquidationThreshold (2) + +uint16: ltv (2) + +string: label (32) + + + +6:36->5 + + + + + +2 + +ReserveConfigurationMap <<Struct>> + +offset + +0 + +type: variable (bytes) + +uint256: data (32) + + + +3:12->2 + + + + + diff --git a/examples/storage/BasicStorage.svg b/examples/storage/BasicStorage.svg new file mode 100644 index 00000000..a7ba74b2 --- /dev/null +++ b/examples/storage/BasicStorage.svg @@ -0,0 +1,73 @@ + + + + + + +StorageDiagram + + + +1 + +BasicStorage <<Contract>> + +slot + +0 + +1 + +2 + +3 + +4 + +5 + +6 + +7 + +type: <inherited contract>.variable (bytes) + +unallocated (29) + +uint8: tinyNumber (1) + +Severity: severity (1) + +bool: someBool (1) + +uint256: fullSlotNumber (32) + +unallocated (14) + +uint128: halfSlotNumber (16) + +int16: smallNegativeNumber (2) + +unallocated (12) + +address: owner (20) + +unallocated (3) + +bytes8: eightBytes (8) + +bytes1: oneByte (1) + +ITrade: exchange (20) + +bytes32: fullSlotBytes (32) + +string: name (32) + +bytes: data (32) + + + diff --git a/examples/storage/BasicStorageData.svg b/examples/storage/BasicStorageData.svg new file mode 100644 index 00000000..44ad0327 --- /dev/null +++ b/examples/storage/BasicStorageData.svg @@ -0,0 +1,124 @@ + + + + + + +StorageDiagram + + + +1 + +BasicStorage <<Contract>> +0x8E2587265C68CD9EE3EcBf22DC229980b47CB960 + +slot +   + +0 +   + +1 +   + +2 +   + +3 +   + +4 +   + +5 +   + +6 +   + +7 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000FA0101 +   + +0x00000000000000000000000000000000000000000000043C33C1937564800000 +   + +0x0000000000000000000000000000000000000000021E19E0C9BAB2400000D8F0 +   + +0x0000000000000000000000008E2587265C68CD9EE3ECBF22DC229980B47CB960 +   + +0x0000008100FF81C300FF01FF1C727A55EA3C11B0AB7D3A361FE0F3C47CE6DE5D +   + +0xE9B69CD5563A8BFBFFB0FA4F422862013492D43FE7FB62D771A0147B6E891D13 +   + +0x426173696353746F7261676520636F6E7472616374000000000000000000002A +   + +0xFFEEDDCCBBAA9988770011000000000000000000000000000000000000000016 + + +type: <inherited contract>.variable (bytes) +decoded data + +unallocated (29) + +uint8: tinyNumber (1) + 250   + +Severity: severity (1) + Medium   + +bool: someBool (1) + true + +uint256: fullSlotNumber (32) + 20,000,000,000,000,000,000,000 + +unallocated (14) + +uint128: halfSlotNumber (16) + 10,000,000,000,000,000,000,000   + +int16: smallNegativeNumber (2) + -10,000 + +unallocated (12) + +address: owner (20) + 0x8E2587265C68CD9EE3EcBf22DC229980b47CB960 + +unallocated (3) + +bytes8: eightBytes (8) + 0x8100FF81C300FF01   + +bytes1: oneByte (1) + 0xFF   + +ITrade: exchange (20) + 0x1C727a55eA3c11B0ab7D3a361Fe0F3C47cE6de5d + +bytes32: fullSlotBytes (32) + 0xE9B69CD5563A8BFBFFB0FA4F422862013492D43FE7FB62D771A0147B6E891D13 + +string: name (32) + "BasicStorage contract" + +bytes: data (32) + 0xFFEEDDCCBBAA9988770011 + + + diff --git a/examples/storage/DynamicArrayStorage.svg b/examples/storage/DynamicArrayStorage.svg new file mode 100644 index 00000000..d97c848a --- /dev/null +++ b/examples/storage/DynamicArrayStorage.svg @@ -0,0 +1,127 @@ + + + + + + +StorageDiagram + + + +5 + +DynamicArrayStorage <<Contract>> + +slot + +0 + +1 + +2 + +3 + +type: <inherited contract>.variable (bytes) + +uint256[]: numbers (32) + +uint256[]: empty (32) + +uint56[]: sevenByteNumbers (32) + +address[]: tokens (32) + + + +1 + +uint256[]: numbers <<Array>> +0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +5:2->1 + + + + + +2 + +uint256[]: empty <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +5:4->2 + + + + + +3 + +uint56[]: sevenByteNumbers <<Array>> +0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace + +offset + +0 + +type: variable (bytes) + +unallocated (25) + +uint56 (7) + + + +5:6->3 + + + + + +4 + +address[]: tokens <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +5:8->4 + + + + + diff --git a/examples/storage/DynamicArrayStorageData.svg b/examples/storage/DynamicArrayStorageData.svg new file mode 100644 index 00000000..7d601e8e --- /dev/null +++ b/examples/storage/DynamicArrayStorageData.svg @@ -0,0 +1,323 @@ + + + + + + +StorageDiagram + + + +5 + +DynamicArrayStorage <<Contract>> +0x66535378de7FB9219b637DBE3e3FFad33387f80B + +slot +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x000000000000000000000000000000000000000000000000000000000000000A +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000014 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: <inherited contract>.variable (bytes) +decoded data + +uint256[]: numbers (32) + 10 + +uint256[]: empty (32) + 0 + +uint56[]: sevenByteNumbers (32) + 20 + +address[]: tokens (32) + 3 + + + +1 + +uint256[]: numbers <<Array>> +0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + +offset +   + +0 +   + +1 +   + +2-7 + +8 +   + +9 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000009 +   + +0x000000000000000000000000000000000000000000000000000000000000000A + + +type: variable (bytes) +decoded data + +uint256 (32) + 1 + +uint256 (32) + 2 + +---- (192) + +uint256 (32) + 9 + +uint256 (32) + 10 + + + +5:2->1 + + + + + +2 + +uint256[]: empty <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + + + +5:4->2 + + + + + +3 + +uint56[]: sevenByteNumbers <<Array>> +0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace + +offset +   + +0 +   + +1 +   + +2 + +3 +   + +4 + + +value +   + +0x0000000000000000000004000000000000030000000000000200000000000001 +   + +0x0000000000000000000008000000000000070000000000000600000000000005 +   + + + +0x00000000000000000000100000000000000F0000000000000E0000000000000D +   + +0x0000000000000000000014000000000000130000000000001200000000000011 + + +type: variable (bytes) +decoded data + +unallocated (4) + +uint56 (7) + 4   + +uint56 (7) + 3   + +uint56 (7) + 2   + +uint56 (7) + 1 + +unallocated (4) + +uint56 (7) + 8   + +uint56 (7) + 7   + +uint56 (7) + 6   + +uint56 (7) + 5 + +---- (32) + +unallocated (4) + +uint56 (7) + 16   + +uint56 (7) + 15   + +uint56 (7) + 14   + +uint56 (7) + 13 + +unallocated (4) + +uint56 (7) + 20   + +uint56 (7) + 19   + +uint56 (7) + 18   + +uint56 (7) + 17 + + + +5:6->3 + + + + + +4 + +address[]: tokens <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48 +   + +0x000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7 +   + +0x0000000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + +unallocated (12) + +address (20) + 0xdAC17F958D2ee523a2206206994597C13D831ec7 + +unallocated (12) + +address (20) + 0x6B175474E89094C44Da98b954EedeAC495271d0F + + + +5:8->4 + + + + + diff --git a/examples/storage/EmissionsController.svg b/examples/storage/EmissionsController.svg index f62ca342..4d7f307e 100644 --- a/examples/storage/EmissionsController.svg +++ b/examples/storage/EmissionsController.svg @@ -4,228 +4,370 @@ - - + + StorageDiagram - + 8 - -EmissionsController <<Contract>> -0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 - -slot - -0 - -1 - -2 - -3 - -4 - -5 - -type: <inherited contract>.variable (bytes) - -unallocated (30) - -bool: Initializable._initializing (1) - -bool: Initializable._initialized (1) - -EpochHistory: epochs (32) - -mapping(address=>uint32): stakingContractAddTime (32) - -IVotes[]: stakingContracts (32) - -DialData[]: dials (32) - -mapping(address=>VoterPreferences): voterPreferences (32) + +EmissionsController <<Contract>> +0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 + +slot +   + +0 +   + +1 + +2 + +3 +   + +4 +   + +5 + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000013 +   + + + +type: <inherited contract>.variable (bytes) +decoded data + +unallocated (30) + +bool: Initializable._initializing (1) + false   + +bool: Initializable._initialized (1) + true + +EpochHistory: epochs (32) + +mapping(address=>uint32): stakingContractAddTime (32) + +IVotes[]: stakingContracts (32) + 2 + +DialData[]: dials (32) + 19 + +mapping(address=>VoterPreferences): voterPreferences (32) 1 - -EpochHistory <<Struct>> - -slot - -1 - -type: variable (bytes) - -unallocated (24) - -uint32: lastEpoch (4) - -uint32: startEpoch (4) + +EpochHistory <<Struct>> + +slot +   + +1 + + +value +   + +0x00000000000000000000000000000000000000000000000000000AD400000A96 + + +type: variable (bytes) +decoded data + +unallocated (24) + +uint32: lastEpoch (4) + 2,772   + +uint32: startEpoch (4) + 2,710 8:5->1 - - + + 2 - -IVotes[]: stakingContracts <<Array>> -0x11987c15ef5ed64ec2e3cd9cfc79d7bd155aea3982ea59f35a5e6b5c1593a54b - -slot - -0 - -type: variable (bytes) - -unallocated (12) - -IVotes (20) + +IVotes[]: stakingContracts <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000008F2326316EC696F6D023E37A9931C2B2C177A3D7 +   + +0x000000000000000000000000EFBE22085D9F29863CFB77EED16D3CC0D927B011 + + +type: variable (bytes) +decoded data + +unallocated (12) + +IVotes (20) + 0x8f2326316eC696F6d023E37A9931c2b2C177a3D7 + +unallocated (12) + +IVotes (20) + 0xeFbe22085D9f29863Cfb77EEd16d3cC0D927b011 8:8->2 - - + + 6 - -DialData[]: dials <<Array>> -0x68f9c7fa29c0442459fc0d2760448ff932de4dd67b90b4a6ac1899621cfd70a7 - -slot - -0 - -type: variable (bytes) - -DialData (96) + +DialData[]: dials <<Array>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0-2 + +3-6 + +6-51 + +51-54 + +54-57 + +value +   + + + + + + + + + + + +type: variable (bytes) +decoded data + +DialData (96) + +DialData (96) + +---- (1472) + +DialData (96) + +DialData (96) 8:19->6 - - + + 7 - -VoterPreferences <<Struct>> - -slot - -0 - -1 - -type: variable (bytes) - -uint256: dialWeights (32) - -unallocated (12) - -uint32: lastSourcePoke (4) - -uint128: votesCast (16) + +VoterPreferences <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: dialWeights (32) + +unallocated (12) + +uint32: lastSourcePoke (4) + +uint128: votesCast (16) 8:23->7 - - + + 3 - -HistoricVotes <<Struct>> - -slot - -0 - -type: variable (bytes) - -unallocated (12) - -uint32: epoch (4) - -uint128: votes (16) + +HistoricVotes <<Struct>> +0x60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79 + +offset +   + +0 + + +value +   + +0x00000000000000000000000000000A96000000000000614DDBBD96DBD25D5A21 + + +type: variable (bytes) +decoded data + +unallocated (12) + +uint32: epoch (4) + 2,710   + +uint128: votes (16) + 459,505,782,109,766,997,072,417 4 - -HistoricVotes[]: voteHistory <<Array>> -0xd3974deccfd8aa6b77f0fcc2c0014e6e0574d32e56c1d75717d2667b529cd073 - -slot - -0 - -type: variable (bytes) - -HistoricVotes (32) + +HistoricVotes[]: voteHistory <<Array>> +0x60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79 + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +HistoricVotes (32) -4:15->3 - - +4:16->3 + + 5 - -DialData <<Struct>> - -slot - -0 - -1 - -2 - -type: variable (bytes) - -unallocated (17) - -uint96: balance (12) - -uint8: cap (1) - -bool: notify (1) - -bool: disabled (1) - -unallocated (12) - -address: recipient (20) - -HistoricVotes[]: voteHistory (32) + +DialData <<Struct>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x0000000000000000000000000000000000000001AE012AE200C9E3D7800A0100 +   + +0x0000000000000000000000008F2326316EC696F6D023E37A9931C2B2C177A3D7 +   + +0x000000000000000000000000000000000000000000000000000000000000003F + + +type: variable (bytes) +decoded data + +unallocated (17) + +uint96: balance (12) + 7,932,184,079,731,182,000,000   + +uint8: cap (1) + 10   + +bool: notify (1) + true   + +bool: disabled (1) + false + +unallocated (12) + +address: recipient (20) + 0x8f2326316eC696F6d023E37A9931c2b2C177a3D7 + +HistoricVotes[]: voteHistory (32) + 63 -5:18->4 - - +5:17->4 + + -6:9->5 - - +6:18->5 + + diff --git a/examples/storage/EmissionsControllerData.svg b/examples/storage/EmissionsControllerData.svg index 09cc42df..7eef97ce 100644 --- a/examples/storage/EmissionsControllerData.svg +++ b/examples/storage/EmissionsControllerData.svg @@ -4,228 +4,373 @@ - - + + StorageDiagram - + 8 - -EmissionsController <<Contract>> -0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 - -slot - -0 - -1 - -2 - -3 - -4 - -5 - -type: <inherited contract>.variable (bytes) - -unallocated (30) - -bool: Initializable._initializing (1) - -bool: Initializable._initialized (1) - -EpochHistory: epochs (32) - -mapping(address=>uint32): stakingContractAddTime (32) - -IVotes[]: stakingContracts (32) - -DialData[]: dials (32) - -mapping(address=>VoterPreferences): voterPreferences (32) + +EmissionsController <<Contract>> +0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 + +slot +   + +0 +   + +1 + +2 + +3 + +4 + +5 + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000002 + +0x0000000000000000000000000000000000000000000000000000000000000013 + + + +type: <inherited contract>.variable (bytes) +decoded data + +unallocated (30) + +bool: Initializable._initializing (1) + false   + +bool: Initializable._initialized (1) + true + +EpochHistory: epochs (32) + +mapping(address=>uint32): stakingContractAddTime (32) + +IVotes[]: stakingContracts (32) + +DialData[]: dials (32) + +mapping(address=>VoterPreferences): voterPreferences (32) 1 - -EpochHistory <<Struct>> - -slot - -1 - -type: variable (bytes) - -unallocated (24) - -uint32: lastEpoch (4) - -uint32: startEpoch (4) + +EpochHistory <<Struct>> + +slot +   + +1 + + +value +   + +0x00000000000000000000000000000000000000000000000000000AD300000A96 + + +type: variable (bytes) +decoded data + +unallocated (24) + +uint32: lastEpoch (4) + 2,771   + +uint32: startEpoch (4) + 2,710 8:5->1 - - + + 2 - -IVotes[]: stakingContracts <<Array>> -0x11987c15ef5ed64ec2e3cd9cfc79d7bd155aea3982ea59f35a5e6b5c1593a54b - -slot - -0 - -type: variable (bytes) - -unallocated (12) - -IVotes (20) + +IVotes[]: stakingContracts <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000008F2326316EC696F6D023E37A9931C2B2C177A3D7 +   + +0x000000000000000000000000EFBE22085D9F29863CFB77EED16D3CC0D927B011 + + +type: variable (bytes) +decoded data + +unallocated (12) + +IVotes (20) + 0x8F2326316EC696F6D023E37A9931C2B2C177A3D7 + +unallocated (12) + +IVotes (20) + 0xEFBE22085D9F29863CFB77EED16D3CC0D927B011 8:8->2 - - + + 6 - -DialData[]: dials <<Array>> -0x68f9c7fa29c0442459fc0d2760448ff932de4dd67b90b4a6ac1899621cfd70a7 - -slot - -0-2 - -type: variable (bytes) - -DialData (96) + +DialData[]: dials <<Array>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0-2 + +3-5 + +6-8 + +9-47 + +48-50 + +51-53 + +54-56 + +value +   + + + + + + + + + + + + + + + +type: variable (bytes) +decoded data + +DialData (96) + +DialData (96) + +DialData (96) + +---- (1216) + +DialData (96) + +DialData (96) + +DialData (96) 8:19->6 - - + + 7 - -VoterPreferences <<Struct>> - -slot - -0 - -1 - -type: variable (bytes) - -uint256: dialWeights (32) - -unallocated (12) - -uint32: lastSourcePoke (4) - -uint128: votesCast (16) + +VoterPreferences <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: dialWeights (32) + +unallocated (12) + +uint32: lastSourcePoke (4) + +uint128: votesCast (16) 8:23->7 - - + + 3 - -HistoricVotes <<Struct>> - -slot - -0 - -type: variable (bytes) - -unallocated (12) - -uint32: epoch (4) - -uint128: votes (16) + +HistoricVotes <<Struct>> +0x60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79 + +offset +   + +0 + + +value +   + +0x00000000000000000000000000000A96000000000000614DDBBD96DBD25D5A21 + + +type: variable (bytes) +decoded data + +unallocated (12) + +uint32: epoch (4) + 2,710   + +uint128: votes (16) + 459,505,782,109,766,997,072,417 4 - -HistoricVotes[]: voteHistory <<Array>> -0xd3974deccfd8aa6b77f0fcc2c0014e6e0574d32e56c1d75717d2667b529cd073 - -slot - -0 - -type: variable (bytes) - -HistoricVotes (32) + +HistoricVotes[]: voteHistory <<Array>> +0x60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79 + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +HistoricVotes (32) -4:15->3 - - +4:16->3 + + 5 - -DialData <<Struct>> - -slot - -0 - -1 - -2 - -type: variable (bytes) - -unallocated (17) - -uint96: balance (12) - -uint8: cap (1) - -bool: notify (1) - -bool: disabled (1) - -unallocated (12) - -address: recipient (20) - -HistoricVotes[]: voteHistory (32) + +DialData <<Struct>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0 +   + +1 +   + +2 + +value +   + +0x00000000000000000000000000000000000000000000000000000000000A0100 +   + +0x0000000000000000000000008F2326316EC696F6D023E37A9931C2B2C177A3D7 +   + +0x000000000000000000000000000000000000000000000000000000000000003E + +type: variable (bytes) +decoded data + +unallocated (17) + +uint96: balance (12) + 0   + +uint8: cap (1) + 10   + +bool: notify (1) + true   + +bool: disabled (1) + false + +unallocated (12) + +address: recipient (20) + 0x8F2326316EC696F6D023E37A9931C2B2C177A3D7 + +HistoricVotes[]: voteHistory (32) -5:18->4 - - +5:17->4 + + -6:9->5 - - +6:18->5 + + diff --git a/examples/storage/FixedArrayStorageData.svg b/examples/storage/FixedArrayStorageData.svg new file mode 100644 index 00000000..4c98fb97 --- /dev/null +++ b/examples/storage/FixedArrayStorageData.svg @@ -0,0 +1,320 @@ + + + + + + +StorageDiagram + + + +5 + +FixedArrayStorage <<Contract>> +0x796c008d8ADDCc33Da3e946Ca457432a35913c85 + +slot +   + +0-1 + +2-7 + +8-10 + +11-60 + +value +   + + + + + + + + + +type: <inherited contract>.variable (bytes) +decoded data + +uint72[5]: five9ByteNumbers (64) + +uint56[21]: twentyOne7ByteNumbers (192) + +address[3]: tokens (96) + +bytes32[50]: gap (1600) + + + +1 + +uint72[5]: five9ByteNumbers <<Array>> + +slot +   + +0 +   + +1 + + +value +   + +0x000000000000000000000000FFFF0000000000000000FF000000000000000001 +   + +0x0000000000000000000000000000FFFFFFFFFFFFFFFFFF0000000000FFFFFFFF + + +type: variable (bytes) +decoded data + +unallocated (5) + +uint72 (9) + 65,535   + +uint72 (9) + 255   + +uint72 (9) + 1 + +unallocated (14) + +uint72 (9) + 4,722,366,482,869,645,213,695   + +uint72 (9) + 4,294,967,295 + + + +5:6->1 + + + + + +2 + +uint56[21]: twentyOne7ByteNumbers <<Array>> + +slot +   + +2 +   + +3 +   + +4-5 + +6 +   + +7 + + +value +   + +0x0000000000000000000003000000000000020000000000000100000000000000 +   + +0x0000000000000000000007000000000000060000000000000500000000000004 +   + + + +0x0000000000000000000013000000000000120000000000001100000000000010 +   + +0x0000000000000000000000000000000000000000000000000000000000000014 + + +type: variable (bytes) +decoded data + +unallocated (4) + +uint56 (7) + 3   + +uint56 (7) + 2   + +uint56 (7) + 1   + +uint56 (7) + 0 + +unallocated (4) + +uint56 (7) + 7   + +uint56 (7) + 6   + +uint56 (7) + 5   + +uint56 (7) + 4 + +---- (64) + +unallocated (4) + +uint56 (7) + 19   + +uint56 (7) + 18   + +uint56 (7) + 17   + +uint56 (7) + 16 + +unallocated (25) + +uint56 (7) + 20 + + + +5:21->2 + + + + + +3 + +address[3]: tokens <<Array>> + +slot +   + +8 +   + +9 +   + +10 + + +value +   + +0x000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48 +   + +0x000000000000000000000000DAC17F958D2EE523A2206206994597C13D831EC7 +   + +0x0000000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + +unallocated (12) + +address (20) + 0xdAC17F958D2ee523a2206206994597C13D831ec7 + +unallocated (12) + +address (20) + 0x6B175474E89094C44Da98b954EedeAC495271d0F + + + +5:25->3 + + + + + +4 + +bytes32[50]: gap <<Array>> + +slot +   + +11 +   + +12 +   + +13-58 + +59 +   + +60 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +---- (1472) + +bytes32 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +5:31->4 + + + + + diff --git a/examples/storage/MappingStorage.svg b/examples/storage/MappingStorage.svg new file mode 100644 index 00000000..81b1216b --- /dev/null +++ b/examples/storage/MappingStorage.svg @@ -0,0 +1,127 @@ + + + + + + +StorageDiagram + + + +4 + +MappingStorage <<Contract>> + +slot + +0 + +1 + +2 + +3 + +type: <inherited contract>.variable (bytes) + +mapping(address=>uint256): balances (32) + +mapping(address=>mapping(address=>uint256)): allowances (32) + +mapping(uint256=>address[]): grantRecipients (32) + +mapping(address=>ExampleStruct): mapToStruct (32) + + + +3 + +ExampleStruct <<Struct>> + +offset + +0 + +1 + +2-3 + +4 + +5 + +type: variable (bytes) + +unallocated (10) + +Severity: severity (1) + +address: token (20) + +bool: flag (1) + +address[]: dynamicAddressArray (32) + +uint256[2]: staticIntArray (64) + +string: someString (32) + +mapping(address=>uint256): claimed (32) + + + +4:14->3 + + + + + +1 + +address[]: dynamicAddressArray <<Array>> + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +2 + +uint256[2]: staticIntArray <<Array>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + + + +3:8->1 + + + + + +3:11->2 + + + + + diff --git a/examples/storage/MultiDynamicArrayStorage.svg b/examples/storage/MultiDynamicArrayStorage.svg new file mode 100644 index 00000000..92795409 --- /dev/null +++ b/examples/storage/MultiDynamicArrayStorage.svg @@ -0,0 +1,136 @@ + + + + + + +StorageDiagram + + + +6 + +MultiDynamicArrayStorage <<Contract>> + +slot + +0 + +1 + +type: <inherited contract>.variable (bytes) + +uint256[][]: twoDimDynIntArray (32) + +uint256[][][]: threeDimDynIntArray (32) + + + +2 + +uint256[][]: twoDimDynIntArray <<Array>> +0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + +offset + +0 + +type: variable (bytes) + +uint256[] (32) + + + +6:3->2 + + + + + +5 + +uint256[][][]: threeDimDynIntArray <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset + +0 + +type: variable (bytes) + +uint256[][] (32) + + + +6:7->5 + + + + + +1 + +uint256[]: twoDimDynIntArray <<Array>> +0x510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +2:2->1 + + + + + +3 + +uint256[]: threeDimDynIntArray <<Array>> +0x73139abbd176cf7f94893453632c00afefb2776f9805a21d03d176c885cd0d63 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +4 + +uint256[][]: threeDimDynIntArray <<Array>> +0xb5d9d894133a730aa651ef62d26b0ffa846233c74177a591a4a896adfda97d22 + +offset + +0 + +type: variable (bytes) + +uint256[] (32) + + + +4:5->3 + + + + + +5:6->4 + + + + + diff --git a/examples/storage/MultiDynamicArrayStorageData.svg b/examples/storage/MultiDynamicArrayStorageData.svg new file mode 100644 index 00000000..1b2c4ae1 --- /dev/null +++ b/examples/storage/MultiDynamicArrayStorageData.svg @@ -0,0 +1,304 @@ + + + + + + +StorageDiagram + + + +6 + +MultiDynamicArrayStorage <<Contract>> +0x6f44d1108bB79710C1BBE378661d90876682E027 + +slot +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000008 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: <inherited contract>.variable (bytes) +decoded data + +uint256[][]: twoDimDynIntArray (32) + 8 + +uint256[][][]: threeDimDynIntArray (32) + 4 + + + +2 + +uint256[][]: twoDimDynIntArray <<Array>> +0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + +offset +   + +0 +   + +1 +   + +2-5 + +6 +   + +7 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x000000000000000000000000000000000000000000000000000000000000000B +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: variable (bytes) +decoded data + +uint256[] (32) + 2 + +uint256[] (32) + 3 + +---- (128) + +uint256[] (32) + 11 + +uint256[] (32) + 4 + + + +6:3->2 + + + + + +5 + +uint256[][][]: threeDimDynIntArray <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000006 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256[][] (32) + 2 + +uint256[][] (32) + 6 + +uint256[][] (32) + 3 + +uint256[][] (32) + 0 + + + +6:7->5 + + + + + +1 + +uint256[]: twoDimDynIntArray <<Array>> +0x510e4e770828ddbf7f7b00ab00a9f6adaf81c0dc9cc85f1f8249c256942d61d9 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000002B67 +   + +0x0000000000000000000000000000000000000000000000000000000000002B72 + + +type: variable (bytes) +decoded data + +uint256 (32) + 11,111 + +uint256 (32) + 11,122 + + + +2:2->1 + + + + + +3 + +uint256[]: threeDimDynIntArray <<Array>> +0x73139abbd176cf7f94893453632c00afefb2776f9805a21d03d176c885cd0d63 + +offset +   + +0 +   + +1 +   + +1 + + +value +   + +0x000000000000000000000000000000000000000000000000000000000001B207 +   + +0x000000000000000000000000000000000000000000000000000000000001B212 +   + +0x000000000000000000000000000000000000000000000000000000000001B212 + + +type: variable (bytes) +decoded data + +uint256 (32) + 111,111 + +uint256 (32) + 111,122   + +uint256 (32) + 111,122 + +uint256 (32) + 111,122   + +uint256 (32) + 111,122 + + + +4 + +uint256[][]: threeDimDynIntArray <<Array>> +0xb5d9d894133a730aa651ef62d26b0ffa846233c74177a591a4a896adfda97d22 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +uint256[] (32) + 2 + +uint256[] (32) + 3 + + + +4:5->3 + + + + + +5:6->4 + + + + + diff --git a/examples/storage/MultiFixedArrayStorageData.svg b/examples/storage/MultiFixedArrayStorageData.svg new file mode 100644 index 00000000..6efe84db --- /dev/null +++ b/examples/storage/MultiFixedArrayStorageData.svg @@ -0,0 +1,429 @@ + + + + + + +StorageDiagram + + + +11 + +MultiFixedArrayStorage <<Contract>> +0xe147cB7D90B9253844130E2C4A7Ef0ffB641C3ea + +slot +   + +0-5 + +6-8 + +9-10 + +value +   + + + + + + + +type: <inherited contract>.variable (bytes) +decoded data + +uint256[3][2]: twoByThreeNumbers (192) + +bool[2][3]: threeByTwoBool (96) + +bool[3][2]: twoByThreeBool (64) + + + +3 + +uint256[3][2]: twoByThreeNumbers <<Array>> + +slot +   + +0-2 + +3-6 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256[3] (96) + +uint256[3] (96) + + + +11:9->3 + + + + + +7 + +bool[2][3]: threeByTwoBool <<Array>> + +slot +   + +6 + +7 + +8 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +bool[2] (32) + +bool[2] (32) + +bool[2] (32) + + + +11:19->7 + + + + + +10 + +bool[3][2]: twoByThreeBool <<Array>> + +slot +   + +9 + +10 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[3] (32) + +bool[3] (32) + + + +11:28->10 + + + + + +1 + +uint256[3]: twoByThreeNumbers <<Array>> + +slot +   + +0 +   + +1 +   + +2 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +uint256 (32) + 1 + +uint256 (32) + 2 + +uint256 (32) + 3 + + + +2 + +uint256[3]: twoByThreeNumbers <<Array>> + +slot +   + +3 +   + +4 +   + +5 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000005 +   + +0x0000000000000000000000000000000000000000000000000000000000000006 + + +type: variable (bytes) +decoded data + +uint256 (32) + 4 + +uint256 (32) + 5 + +uint256 (32) + 6 + + + +3:4->1 + + + + + +3:5->2 + + + + + +4 + +bool[2]: threeByTwoBool <<Array>> + +slot +   + +6 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + true + + + +5 + +bool[2]: threeByTwoBool <<Array>> + +slot +   + +7 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + false + + + +6 + +bool[2]: threeByTwoBool <<Array>> + +slot +   + +8 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + true + + + +7:12->4 + + + + + +7:13->5 + + + + + +7:14->6 + + + + + +8 + +bool[3]: twoByThreeBool <<Array>> + +slot +   + +9 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +9 + +bool[3]: twoByThreeBool <<Array>> + +slot +   + +10 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + false   + +bool (1) + true   + +bool (1) + false + + + +10:23->8 + + + + + +10:24->9 + + + + + diff --git a/examples/storage/README.md b/examples/storage/README.md index f5028d22..d873f5ab 100644 --- a/examples/storage/README.md +++ b/examples/storage/README.md @@ -1,75 +1,233 @@ # Example Storage Diagrams -## mStable Emissions Controller +## Local Elementary Storage -The [mStable](https://mstable.org/) Emissions Controller is deployed on mainnet to [0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780](https://etherscan.io/address/0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780). -This is a proxy contract so to visualise the storage layout sol2uml needs to be run against the proxy implementation [0xebfd9cD78510c591eDa8735D0F8a87414eF27A83](https://etherscan.io/address/0xebfd9cd78510c591eda8735d0f8a87414ef27a83). +The below storage diagram is a simple example from this repository [/src/contracts/storage/BasicStorage.sol](../../src/contracts/storage/BasicStorage.sol). -By default, the output file will be saved in the current working directory using the contract name as the filename in svg format. In this case, it'll be `EmissionsController.svg`. +Assuming this is run from the repository's root folder, the folder of the contract source file is specified with `./src/contracts`. +The root folder `./` could also be specified as sol2uml will recursively look in all the subfolders for Solidity files. +The folder and filename could also be specified with `./src/contracts/storage/BasicStorage.sol`. + +The `-c --contract` option specifies the contract name the storage should be visualised for. In this case, it's the `BasicStorage` contract. + +In this example, the `-o, --output` option just specifies the folder the diagram is saved to be saved to. +The filename will be the contract name so for the `BasicStorage` contract the filename will be `BasicStorage.svg`. +The output option can also specify the folder and file name. ``` -sol2uml storage 0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 +sol2uml storage ./src/contracts -c BasicStorage ``` -![Emissions Controller](./EmissionsController.svg) +![BasicStorage](./BasicStorage.svg) + +sol2uml storage diagrams will show where each storage variable is stored in the contract's slots. For each variable, the type, name and byte size is displayed. + +Solidity will pack storage variables in a single slot if the values are 16 bytes (128 bits) or less. You can see this in slots 0, 2 and 4 in the above diagram. +The variables are packed from right to left. sol2uml will mark any unused slot space as `unallocated` along with the number of bytes. +# Elementary Storage on Arbitrum -To visualise the data in the storage slots, the `-d --data` option is used. -As this is a proxied contract, the `-s --storage` option is used with the proxy contract address. -The `-u ---url` option can be used to tell which Ethereum node to get the storage values. Alternatively, the `NODE_URL` environment variable can be set. +In this example, we'll generate a storage diagram for the same [BasicStorage](../../src/contracts/storage/BasicStorage.sol) contract deployed on Arbitrum to [0x8E2587265C68CD9EE3EcBf22DC229980b47CB960](https://arbiscan.io/address/0x8E2587265C68CD9EE3EcBf22DC229980b47CB960#code). + +The `-n, --network ` option is used to specify the Solidity code is to be sourced from verified files on the Arbitrum blockchain explorer [Arbiscan](https://arbiscan.io). + +The `-d, --data` option is used to get the contract storage slot values from a JSON-RPC node provider like [Alchemy](https://www.alchemy.com/) or [Infura](https://www.infura.io/). +The `-u, url ` option can be used to specify the url of the node provider. +Alternatively, the `NODE_URL` environment variables can be used like in the following. ``` -export NODE_URL=https://your-node-url -sol2uml storage 0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 -d -s 0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780 +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0x8E2587265C68CD9EE3EcBf22DC229980b47CB960 -o BasicStorageData.svg ``` -![Emissions Controller Data](./EmissionsControllerData.svg) +![BasicStorageData](./BasicStorageData.svg) -## mStable Staking contract for MTA/ETH 80/20 Balancer Pool Token (BPT) +When the data option is used, the value of each storage slot is displayed in hexadecimal format. sol2uml will also parse each variable value from the slot values and display it in the appropriate format for the type. + +Boolean typed variables will be displayed as a `true` or `false`. See `someBool` in the above as an example. + +Numbers are displayed in comma-separated decimals. +This includes signed negative numbers like `smallNegativeNumber` in the above that converted the two bytes `0xD8F0` to -10,000. + +Addresses are formatted with mixed-case checksum encoding as per [EIP-55](https://eips.ethereum.org/EIPS/eip-55). This includes contract and interface types like `ITrade` for the `exchange` variable in the above. + +The 1 byte enum values are converted from a number index to the string value of the enum. For example, the `severity` variable slot value of `0x01` is mapped to `Medium` as per the `Severity` enum with values Low, Medium and High. + +Strings are converted to [UTF-8](https://en.wikipedia.org/wiki/UTF-8) and any special characters escaped so they can be processed by Graphviz. See below for examples of strings 32 bytes or larger. -The `-f --format` option is used to output in svg, png or dot formats. +## Fixed-Sized Arrays + +The below example was generated for the [FixedArrayStorage](../../src/contracts/storage/FixedArrayStorage.sol) contract deployed on Arbitrum to [0x796c008d8ADDCc33Da3e946Ca457432a35913c85](https://arbiscan.io/address/0x796c008d8ADDCc33Da3e946Ca457432a35913c85#code). ``` -sol2uml storage 0xc63a48d85CCE7C3bD4d18db9c0972a4D223e4193 -d -s 0xeFbe22085D9f29863Cfb77EEd16d3cC0D927b011 -f png -o examples/storage/StakedTokenBPT.png +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0x796c008d8ADDCc33Da3e946Ca457432a35913c85 -o examples/storage/FixedArrayStorageData.svg ``` -![Staking Tokens BPT](./StakedTokenBPTData.png) +![FixedArrayStorageData](./FixedArrayStorageData.svg) + +The first `five9ByteNumbers` variable is an array of five 9 byte (72 bit) numbers. As the array item is less than or equal to 16 bytes (128 bits), the array items are packed into each slot. That is, up to three array items are stored in each slot. + +The second `twentyOne7ByteNumbers` variable is an array of twenty one 7 byte (56 bit) numbers. +Like the previous example, multiple array items are packed into a slot. +By default, sol2uml will just display the first two and last two slots in an array. In this case, the values for slots 4 and 5 are not fetched. +The `-a, array ` option can be used to override this default of two slots being displayed at the start and end of arrays. Just be careful not to set it too high as the node provider may have restrictions on the number of slot values you can fetch. -## Local Test Contract +The `tokens` array of addresses uses one slot per array item as the address type (20 bytes) is greater than 16 bytes. -The below storage diagram is an example from this repository [TestStorage.sol](../../src/contracts/TestStorage.sol). -Instead of specifying a contract address, this example uses the folder `./src/contracts`. All solidity contracts under this folder are parsed and can be visualised. -The `-c --contract` option specifies the contract name the storage should be visualised for. +Like the `twentyOne7ByteNumbers` variable, the `gap` variable will not display all 50 items in the array. sol2uml will by default just display the first two and last two slots. + +## Multidimensional Fixed Arrays + +This example was generated for the [MultiFixedArrayStorage](../../src/contracts/storage/MultiFixedArrayStorage.sol) contract deployed on Arbitrum to [0xe147cB7D90B9253844130E2C4A7Ef0ffB641C3ea](https://arbiscan.io/address/0xe147cB7D90B9253844130E2C4A7Ef0ffB641C3ea#code). ``` -sol2uml storage ./src/contracts -c TestStorage -o examples/storage +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0xe147cB7D90B9253844130E2C4A7Ef0ffB641C3ea -o MultiFixedArrayStorageData.svg ``` -![Test Storage](./TestStorage.svg) +![MultiFixedArrayStorageData](./MultiFixedArrayStorageData.svg) -## USDC +The first storage variable `twoByThreeNumbers` is a two-by-three, fixed-size, multidimensional array. Note the order of the dimensions goes from right to left when being declared. -USDC storage slots from the [verified source code](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code) on Etherscan. +Even though the `threeByTwoBool` and `twoByThreeBool` variables only store six boolean values, they use three and two storage slots respectively. -The `-d --data` option is used to get the values of the storage slots from mainnet. -As this is a proxied contract, the `-s --storage` option is used with the proxy contract address [0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48). -The `-u ---url` option is used to tell which Ethereum node to get the storage values. +## Dynamic Sized Arrays + +Below is an example of dynamic array storage using the [DynamicArrayStorage.sol](../../src/contracts/storage/DynamicArrayStorage.sol) contract that does not fetch the slot values. ``` -sol2uml storage 0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf -d -s 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -u https://your-url +sol2uml storage ./src/contracts -c DynamicArrayStorage ``` -![USDC](./usdcData.svg) +![DynamicArrayStorage](./DynamicArrayStorage.svg) + +As sol2uml don't know from just looking at the code how many items are in each array, it will just show the location of the first storage slot and its structure. + +The 32 byte string in hexidecimal format at the top of each dynamic array is the slot key. For example, the location of the values of `numbers` array starts from slot `0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563` in storage. +This is the keccak256 hash of slot 0 which has been assigned to the `numbers` array. + +The following is generated from the `DynamicArrayStorage` contract deployed on Arbitrum to [0x66535378de7FB9219b637DBE3e3FFad33387f80B](https://arbiscan.io/address/0x66535378de7FB9219b637DBE3e3FFad33387f80B#code). + +``` +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0x66535378de7FB9219b637DBE3e3FFad33387f80B -o DynamicArrayStorageData.svg +``` + +![DynamicArrayStorageData](./DynamicArrayStorageData.svg) + +The values in the first Contract storage section is the lengths of the dynamic arrays. For example, the `numbers` array has 10 items in it. + +The array storage sections how the slot offsets from the first item in the array. For example, offset 1 for the `sevenByteNumbers` variable is the next slot after the slot with key `0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace`. + +Even though the `empty` variable does not have any array items yet, the location of the first item is displayed so the storage structure can be seen. + +## Multidimensional Dynamic Sized Arrays + +Below is an example of multidimensional dynamic array storage using the [MultiDynamicArrayStorage.sol](../../src/contracts/storage/MultiDynamicArrayStorage.sol) contract. + +``` +sol2uml storage ./src/contracts -c MultiDynamicArrayStorage +``` + +![MultioDynamicArrayStorage](./MultiDynamicArrayStorage.svg) + +The following is generated from the `MultiDynamicArrayStorage` contract deployed on Arbitrum to [0x6f44d1108bB79710C1BBE378661d90876682E027](https://arbiscan.io/address/0x6f44d1108bB79710C1BBE378661d90876682E027#code). + +``` +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0x6f44d1108bB79710C1BBE378661d90876682E027 -o MultiDynamicArrayStorageData.svg +``` + +![MultiDynamicArrayStorageData](./MultiDynamicArrayStorageData.svg) + +## Structs + +The below example is of storage variables that use structs from the [StructStorage.sol](../../src/contracts/storage/StructStorage.sol) contract. + +``` +sol2uml storage ./src/contracts -c StructStorage +``` + +![StructStorage](./StructStorage.svg) + +The first `exampleStruct` variables is of `ExampleStruct` type. sol2uml will display how many slots the struct uses and then reference an expanded view of how the struct variables are stored in the slots. +If any of the struct variables are arrays, strings, bytes or other structs, they will recursively be referenced until the elementary types are reached. + +The second `dynamicStructs` variable is a dynamic array of type `ExampleStruct`. When sol2uml is run without the `-d, --data` option, it does not know how long the array is so will just display what the first array item would look like. + + +The following is generated from the `StructStorage` contract deployed on Arbitrum to [0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2](https://arbiscan.io/address/0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2#code). + +``` +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2 -o StructStorageData.svg +``` + +![StructStorageData](./StructStorageData.svg) + +## Strings + +Below is an example of string storage using the [StringStorage.sol](../../src/contracts/storage/StringStorage.sol) contract that does not fetch the slot values. + +``` +sol2uml storage ./src/contracts -c StringStorage +``` + +![StringStorage](./StringStorage.svg) + +Like the dynamic arrays, sol2uml does not know how long the strings are just by looking at the code. To get the string values, the `-d, --data` option is used. +The below diagram get the data for the `StringStorage` contract deployed on Arbitrum to [0xeF2A93be2beD1b577D460c347f82De1Ba8bD9861](https://arbiscan.io/address/0xeF2A93be2beD1b577D460c347f82De1Ba8bD9861#code). + +``` +export NODE_URL=https://arb-mainnet.g.alchemy.com/v2/your-api-key +sol2uml storage -d -n arbitrum 0xeF2A93be2beD1b577D460c347f82De1Ba8bD9861 -o StringStorageData.svg +``` + +![StringStorageData](./StringStorageData.svg) + +Variables `uninitString` and `emptyString` have the same slot values of zero bytes. + +The `name` variable with a 22 character string fits in a single slot. +The [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded string is stored from right to left. The last byte on the right is the length of the string that is left-bit shifted. Mathematically, the length is multiplied by 2. +So the 22 character string becomes 22 * 2 = 44 which is 2C in hexadecimal. + +The `long2` variable has a string that is 59 characters long. +As it is greater than 31 bytes, it can't fit in slot 5 which the variable is assigned. +Slot 5 contains the length of the string that is left-bit shifted and the last bit set to 1. +Mathematically, the length is multiplied by 2 and 1 is added. +So the encoded length of the `long2` variable becomes 59 * 2 + 1 = 119 which is 0x77 in hexadecimal format. +sol2uml will display the decoded string lengths when strings are greater than 31 bytes and the string when they are less than 32 bytes. + +If the rightmost bit of a string variable's slot is set to 1 then the string is dynamically stored in another location and then the slot just contains the encoded string length. + +The `long2` string is stored from slot `0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0` which is the keccak256 hash of the variable's slot 5. +The second slot with offset 1 is the slot with key `0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db1`. + +## Mappings + +Below is an example of mapping storage variables using the [MappingStorage.sol](../../src/contracts/storage/MappingStorage.sol) contract. + +``` +sol2uml storage ./src/contracts -c MappingStorage +``` + +![MappingStorage](./MappingStorage.svg) + +sol2uml does not fetch slot values for mapping types as no data is stored in the variable's slot. The variable's slot along with the mapped key is used to find the location of the mapped values. +Without looking at past transactions or events, sol2uml does not know what these mapped values are. + +sol2uml will show the structure of any mapped struct data types like the `mapToStruct` variable in the above. ## Contract Inheritance -The following example shows the storage slots with contract inheritance. This includes diamond inheritance, imports from other files and import aliases. +The following example shows the storage slots with contract inheritance. This includes [diamond inheritance](https://forum.openzeppelin.com/t/solidity-diamond-inheritance/2694), imports from other files, import aliases and duplicate contract names. ![Inheritance Class Diagram](../inheritanceDiamond.png) ``` -sol2uml class -c -f png -o examples/inheritanceDiamond.png ./src/contracts/inheritance +sol2uml class ./src/contracts/inheritance -c -f png -o examples/inheritanceDiamond.png ``` The storage slots for contract `D` in [inheritance/common.sol](../../src/contracts/inheritance/common.sol). @@ -77,5 +235,31 @@ The storage slots for contract `D` in [inheritance/common.sol](../../src/contrac ![Inheritance](./inheritanceStorage.svg) ``` -sol2uml storage -c D -o examples/storage/inheritanceStorage.svg ./src/contracts/inheritance +sol2uml storage ./src/contracts/inheritance -c D -o examples/storage/inheritanceStorage.svg +``` + +## USDC + +The USD Coin (USDC) token deployed to [0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48#code) on mainnet is a proxied contract. +For sol2uml to get the slot values, the implementation contract [0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code) +is specified for the Solidity source and the proxy address `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` is passed to the `-s --storage` option. +The storage option is the contract that the storage state is read from. + +``` +export NODE_URL=https://your-node-url +sol2uml storage 0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf -d -s 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -o usdcData.svg +``` + +![USDC](./usdcData.svg) + +## mStable Staking contract for MTA/ETH 80/20 Balancer Pool Token (BPT) + +By default, sol2uml will get the latest block number from the node provider and then all storage calls will use the same block for consistency. +The `-bn, --block ` option can be used to specify which block to get the storage values from. + +``` +sol2uml storage 0xc63a48d85CCE7C3bD4d18db9c0972a4D223e4193 -bn 16000000 -d -s 0xeFbe22085D9f29863Cfb77EEd16d3cC0D927b011 -o StakedTokenBPTData.svg ``` + +![Staking Tokens BPT](./StakedTokenBPTData.svg) + diff --git a/examples/storage/StakedTokenBPTData.png b/examples/storage/StakedTokenBPTData.png deleted file mode 100644 index 6c0ad57f..00000000 Binary files a/examples/storage/StakedTokenBPTData.png and /dev/null differ diff --git a/examples/storage/StakedTokenBPTData.svg b/examples/storage/StakedTokenBPTData.svg new file mode 100644 index 00000000..e1727b88 --- /dev/null +++ b/examples/storage/StakedTokenBPTData.svg @@ -0,0 +1,735 @@ + + + + + + +StorageDiagram + + + +12 + +StakedTokenBPT <<Contract>> +0xc63a48d85CCE7C3bD4d18db9c0972a4D223e4193 + +slot +   + +0-50 + +51 +   + +52 +   + +53 + +54 + +55 +   + +56 +   + +57 +   + +58 + +59 + +60-105 + +106 + +107 + +108 +   + +109 +   + +110-155 + +156 +   + +157 + +158 + +159-206 + +207 +   + +208 +   + +209 +   + +210 +   + +211 + + +value +   + + + +0x000000000000000000000000BA69E6FC7DF49A3B75B565068FB91FF2D9D91780 +   + +0x000000000000000000000000EB7D7F4CC139D09D4759D21EB92E6976D00AF1F7 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x5374616B656420546F6B656E2042505400000000000000000000000000000000 +   + +0x73746B4250540000000000000000000000000000000000000000000000000000 +   + + + + + + + + + + + +0x00000000000000000000000000000000000000000000000000000000000000CA +   + +0x000000000000000000000000BA69E6FC7DF49A3B75B565068FB91FF2D9D91780 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + + + + + +0x000000000000000000000000F6FF1F7FCEB2CE6D26687EAAB5988B445D0B94A2 +   + +0x000000000000000000000000B81473F20818225302B8FFFB905B53D58A793D84 +   + +0x0000000000000000000000000000000000000000000000051560CDD44A2B780E +   + +0x0000000000000000000000000000000000000000000000000000000000010D2A +   + +0x0000000000000000000000000000000000000000000000000000000063762A43 + + +type: <inherited contract>.variable (bytes) +decoded data + +uint256[51]: SlotFiller51.__gap (1632) + +unallocated (12) + +address: InitializableRewardsDistributionRecipient.rewardsDistributor (20) + 0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780 + +unallocated (12) + +address: HeadlessStakingRewards.rewardTokenVendor (20) + 0xEB7d7F4CC139D09d4759D21Eb92E6976D00Af1f7 + +Data: HeadlessStakingRewards.globalData (32) + +mapping(address=>UserData): HeadlessStakingRewards.userData (32) + +uint256: HeadlessStakingRewards.pendingAdditionalReward (32) + 1 + +bytes32: GamifiedToken._name (32) + 0x5374616B656420546F6B656E2042505400000000000000000000000000000000 + +bytes32: GamifiedToken._symbol (32) + 0x73746B4250540000000000000000000000000000000000000000000000000000 + +mapping(address=>Balance): GamifiedToken._balances (32) + +mapping(address=>uint256): GamifiedToken._userPriceCoeff (32) + +uint256[46]: GamifiedToken.__gap (1472) + +mapping(address=>address): GamifiedVotingToken._delegates (32) + +mapping(address=>Checkpoint[]): GamifiedVotingToken._checkpoints (32) + +Checkpoint[]: GamifiedVotingToken._totalSupplyCheckpoints (32) + 202 + +unallocated (12) + +IGovernanceHook: GamifiedVotingToken._governanceHook (20) + 0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780 + +uint256[46]: GamifiedVotingToken.__gap (1472) + +unallocated (31) + +bool: InitializableReentrancyGuard._notEntered (1) + true + +SafetyData: StakedToken.safetyData (32) + +mapping(address=>bool): StakedToken.whitelistedWrappers (32) + +uint256[48]: StakedToken.__gap (1536) + +unallocated (12) + +address: balRecipient (20) + 0xF6FF1F7FCEB2cE6d26687EaaB5988b445d0b94a2 + +unallocated (12) + +address: keeper (20) + 0xB81473F20818225302b8FfFB905B53D58a793D84 + +uint256: pendingBPTFees (32) + 93,774,177,752,769,591,310 + +uint256: priceCoefficient (32) + 68,906 + +uint256: lastPriceUpdateTime (32) + 1,668,688,451 + + + +1 + +uint256[51]: __gap <<Array>> + +slot +   + +0 +   + +1 +   + +2-48 + +49 +   + +50 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 1 + +uint256 (32) + 0 + +---- (1504) + +uint256 (32) + 0 + +uint256 (32) + 0 + + + +12:6->1 + + + + + +2 + +Data <<Struct>> + +slot +   + +53 + + +value +   + +0x000000000D4DEFC5C2AAA64600000000004B62FA5C28527663757FA3637EBA23 + + +type: variable (bytes) +decoded data + +uint96: rewardPerTokenStored (12) + 958,685,928,353,343,046   + +uint96: rewardRate (12) + 21,219,450,680,791,670   + +uint32: lastUpdateTime (4) + 1,668,644,771   + +uint32: periodFinish (4) + 1,669,249,571 + + + +12:13->2 + + + + + +3 + +UserData <<Struct>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +uint128: rewards (16) + +uint128: rewardPerTokenPaid (16) + + + +12:16->3 + + + + + +4 + +Balance <<Struct>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +uint88: cooldownUnits (11) + +uint32: cooldownTimestamp (4) + +uint8: questMultiplier (1) + +uint8: timeMultiplier (1) + +uint32: weightedTimestamp (4) + +uint88: raw (11) + + + +12:26->4 + + + + + +5 + +uint256[46]: __gap <<Array>> + +slot +   + +60 +   + +61 +   + +62-103 + +104 +   + +105 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + +uint256 (32) + 0 + +---- (1344) + +uint256 (32) + 0 + +uint256 (32) + 0 + + + +12:33->5 + + + + + +6 + +Checkpoint <<Struct>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +uint224: votes (28) + +uint32: fromBlock (4) + + + +12:37->6 + + + + + +8 + +Checkpoint[]: _totalSupplyCheckpoints <<Array>> +0x2b4a51ab505fc96a0952efda2ba61bcd3078d4c02c39a186ec16f21883fbe016 + +offset +   + +0 + +1 + +2-199 + +200 + +201 + +value +   + + + + + + + + + + + +type: variable (bytes) +decoded data + +Checkpoint (32) + +Checkpoint (32) + +---- (6336) + +Checkpoint (32) + +Checkpoint (32) + + + +12:41->8 + + + + + +9 + +uint256[46]: __gap <<Array>> + +slot +   + +110 +   + +111 +   + +112-153 + +154 +   + +155 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + +uint256 (32) + 0 + +---- (1344) + +uint256 (32) + 0 + +uint256 (32) + 0 + + + +12:48->9 + + + + + +10 + +SafetyData <<Struct>> + +slot +   + +157 + + +value +   + +0x0000000000000000000000000000000000000000000000000DE0B6B3A7640000 + + +type: variable (bytes) +decoded data + +uint128: slashingPercentage (16) + 0   + +uint128: collateralisationRatio (16) + 1,000,000,000,000,000,000 + + + +12:52->10 + + + + + +11 + +uint256[48]: __gap <<Array>> + +slot +   + +159 +   + +160 +   + +161-204 + +205 +   + +206 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + +uint256 (32) + 0 + +---- (1408) + +uint256 (32) + 0 + +uint256 (32) + 0 + + + +12:59->11 + + + + + +7 + +Checkpoint <<Struct>> +0x2b4a51ab505fc96a0952efda2ba61bcd3078d4c02c39a186ec16f21883fbe016 + +offset +   + +0 + + +value +   + +0x00000000000000000000000000000000000000E6A9FCB40B371C000000C9F77A + + +type: variable (bytes) +decoded data + +uint224: votes (28) + 4,255,000,000,000,000,000,000   + +uint32: fromBlock (4) + 13,236,090 + + + +8:40->7 + + + + + diff --git a/examples/storage/StringStorage.svg b/examples/storage/StringStorage.svg new file mode 100644 index 00000000..26ece23f --- /dev/null +++ b/examples/storage/StringStorage.svg @@ -0,0 +1,51 @@ + + + + + + +StorageDiagram + + + +1 + +StringStorage <<Contract>> + +slot + +0 + +1 + +2 + +3 + +4 + +5 + +6 + +type: <inherited contract>.variable (bytes) + +string: uninitString (32) + +string: emptyString (32) + +string: name (32) + +string: exactly31 (32) + +string: exactly32 (32) + +string: long2 (32) + +string: long3 (32) + + + diff --git a/examples/storage/StringStorageData.svg b/examples/storage/StringStorageData.svg new file mode 100644 index 00000000..4a1a2c5f --- /dev/null +++ b/examples/storage/StringStorageData.svg @@ -0,0 +1,216 @@ + + + + + + +StorageDiagram + + + +1 + +StringStorage <<Contract>> +0xeF2A93be2beD1b577D460c347f82De1Ba8bD9861 + +slot +   + +0 +   + +1 +   + +2 +   + +3 +   + +4 +   + +5 +   + +6 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x537472696E6753746F7261676520636F6E74726163740000000000000000002C +   + +0x65786163746C7920333120636861727320736F2075736573203120736C6F743E +   + +0x0000000000000000000000000000000000000000000000000000000000000041 +   + +0x0000000000000000000000000000000000000000000000000000000000000077 +   + +0x0000000000000000000000000000000000000000000000000000000000000099 + + +type: <inherited contract>.variable (bytes) +decoded data + +string: uninitString (32) + "" + +string: emptyString (32) + "" + +string: name (32) + "StringStorage contract" + +string: exactly31 (32) + "exactly 31 chars so uses 1 slot" + +string: exactly32 (32) + 32 + +string: long2 (32) + 59 + +string: long3 (32) + 76 + + + +2 + +string: exactly32 <<String>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0 + + +value +   + +0x3332206368617220736F2075736573206F6E652064796E616D696320736C6F74 + + +type: variable (bytes) +decoded data + +string (32) + "32 char so uses one dynamic slot" + + + +1:5->2 + + + + + +3 + +string: long2 <<String>> +0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0 + +offset +   + +0 +   + +1 + + +value +   + +0x6D6F7265207468616E20333220627974657320736F2064617461206973207374 +   + +0x6F7265642064796E616D6963616C6C7920696E203220736C6F74730000000000 + + +type: variable (bytes) +decoded data + +string (32) + "more than 32 bytes so data is st" + +string (27) + "ored dynamically in 2 slots"   + +unallocated (5) + + + +1:6->3 + + + + + +4 + +string: long3 <<String>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x6D6F7265207468616E20736978747920666F7572202836342920627974657320 +   + +0x736F20646174612069732073746F7265642064796E616D6963616C6C7920696E +   + +0x20746872656520736C6F74730000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "more than sixty four (64) bytes " + +string (32) + "so data is stored dynamically in" + +string (12) + " three slots"   + +unallocated (20) + + + +1:7->4 + + + + + diff --git a/examples/storage/StructStorage.svg b/examples/storage/StructStorage.svg new file mode 100644 index 00000000..31a81d7d --- /dev/null +++ b/examples/storage/StructStorage.svg @@ -0,0 +1,232 @@ + + + + + + +StorageDiagram + + + +8 + +StructStorage <<Contract>> + +slot + +0-5 + +6 + +type: <inherited contract>.variable (bytes) + +ExampleStruct: exampleStruct (192) + +ExampleStruct[]: dynamicStructs (32) + + + +3 + +ExampleStruct <<Struct>> + +slot + +0 + +1 + +2-3 + +4 + +5 + +type: variable (bytes) + +unallocated (10) + +Severity: severity (1) + +address: token (20) + +bool: flag (1) + +address[]: dynamicAddressArray (32) + +uint256[2]: staticIntArray (64) + +string: someString (32) + +mapping(address=>uint256): claimed (32) + + + +8:11->3 + + + + + +7 + +ExampleStruct[]: dynamicStructs <<Array>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset + +0-5 + +type: variable (bytes) + +ExampleStruct (192) + + + +8:23->7 + + + + + +1 + +address[]: dynamicAddressArray <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +2 + +uint256[2]: staticIntArray <<Array>> + +slot + +2 + +3 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + + + +3:5->1 + + + + + +3:8->2 + + + + + +4 + +address[]: dynamicAddressArray <<Array>> +0x768c3a22b1e4688c94525eb9bc2cf1ce7601fc9e871dc6e10fc44f0f06340ce1 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +5 + +uint256[2]: staticIntArray <<Array>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset + +2 + +3 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + + + +6 + +ExampleStruct <<Struct>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset + +0 + +1 + +2-3 + +4 + +5 + +type: variable (bytes) + +unallocated (10) + +Severity: severity (1) + +address: token (20) + +bool: flag (1) + +address[]: dynamicAddressArray (32) + +uint256[2]: staticIntArray (64) + +string: someString (32) + +mapping(address=>uint256): claimed (32) + + + +6:16->4 + + + + + +6:19->5 + + + + + +7:22->6 + + + + + diff --git a/examples/storage/StructStorageData.svg b/examples/storage/StructStorageData.svg new file mode 100644 index 00000000..0d57adeb --- /dev/null +++ b/examples/storage/StructStorageData.svg @@ -0,0 +1,508 @@ + + + + + + +StorageDiagram + + + +10 + +StructStorage <<Contract>> +0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2 + +slot +   + +0-5 + +6 + + +value +   + + + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: <inherited contract>.variable (bytes) +decoded data + +ExampleStruct: exampleStruct (192) + +ExampleStruct[]: dynamicStructs (32) + 1 + + + +4 + +ExampleStruct <<Struct>> + +slot +   + +0 +   + +1 +   + +2 +   + +3-4 + +5 + + +value +   + +0x00000000000000000000029FF58F4FFB29FA2266AB25E75E2A8B350331165601 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000099 + + +type: variable (bytes) +decoded data + +unallocated (10) + +Severity: severity (1) + High   + +address: token (20) + 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656   + +bool: flag (1) + true + +address[]: dynamicAddressArray (32) + 3 + +int64[]: dynamicIntArray (32) + 3 + +uint256[2]: staticIntArray (64) + +string: someString (32) + 76 + + + +10:12->4 + + + + + +9 + +ExampleStruct[]: dynamicStructs <<Array>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset +   + +0-5 + +value +   + + + +type: variable (bytes) +decoded data + +ExampleStruct (192) + + + +10:25->9 + + + + + +1 + +address[]: dynamicAddressArray <<Array>> +0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x000000000000000000000000EB4C2781E4EBA804CE9A9803C67D0893436BB27D +   + +0x000000000000000000000000FE18BE6B3BD88A2D2A7F928D00292E7A9963CFC6 +   + +0x000000000000000000000000DBDB4D16EDA451D0503B854CF79D55697F90C8DF + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D + +unallocated (12) + +address (20) + 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6 + +unallocated (12) + +address (20) + 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF + + + +2 + +int64[]: dynamicIntArray <<Array>> +0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace + +offset +   + +0 + + +value +   + +0x000000000000000000000000000003FFFFFFFFFFFFFFFFFF0000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (8) + +int64 (8) + 1,023   + +int64 (8) + -1   + +int64 (8) + 1 + + + +3 + +uint256[2]: staticIntArray <<Array>> + +slot +   + +3 +   + +4 + + +value +   + +0x000000000000000000000000000000000000000000000000000000000000000B +   + +0x0000000000000000000000000000000000000000000000000000000000000016 + + +type: variable (bytes) +decoded data + +uint256 (32) + 11 + +uint256 (32) + 22 + + + +4:5->1 + + + + + +4:7->2 + + + + + +4:10->3 + + + + + +11 + +string: someString <<String>> +0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0 + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x6D6F7265207468616E20736978747920666F7572202836342920627974657320 +   + +0x736F20646174612069732073746F7265642064796E616D6963616C6C7920696E +   + +0x20746872656520736C6F74730000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "more than sixty four (64) bytes " + +string (32) + "so data is stored dynamically in" + +string (12) + " three slots"   + +unallocated (20) + + + +4:11->11 + + + + + +5 + +address[]: dynamicAddressArray <<Array>> +0x768c3a22b1e4688c94525eb9bc2cf1ce7601fc9e871dc6e10fc44f0f06340ce1 + +offset +   + +0 + + +value +   + +0x0000000000000000000000002A0B4BDB2492EC4D8EA0015A6784ACC98216C5D2 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x2A0b4Bdb2492eC4D8eA0015A6784ACC98216c5D2 + + + +6 + +int64[]: dynamicIntArray <<Array>> +0xaf786ca8f985b8adb0de73df0052ad2ed91db8313035df9caa6938d80f1945c8 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000008AE0000000000000457 + + +type: variable (bytes) +decoded data + +unallocated (16) + +int64 (8) + 2,222   + +int64 (8) + 1,111 + + + +7 + +uint256[2]: staticIntArray <<Array>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset +   + +3 +   + +4 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000021 +   + +0x000000000000000000000000000000000000000000000000000000000000002C + + +type: variable (bytes) +decoded data + +uint256 (32) + 33 + +uint256 (32) + 44 + + + +8 + +ExampleStruct <<Struct>> +0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f + +offset +   + +0 +   + +1 +   + +2 +   + +3-4 + +5 + + +value +   + +0x000000000000000000000122E2219F098AB128F11BE752DA4FC8E1C6BA2F3F01 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + +0x612073686F727420737472696E6700000000000000000000000000000000001C + + +type: variable (bytes) +decoded data + +unallocated (10) + +Severity: severity (1) + Medium   + +address: token (20) + 0x22E2219F098Ab128F11BE752Da4fC8e1c6BA2F3f   + +bool: flag (1) + true + +address[]: dynamicAddressArray (32) + 1 + +int64[]: dynamicIntArray (32) + 2 + +uint256[2]: staticIntArray (64) + +string: someString (32) + "a short string" + + + +8:17->5 + + + + + +8:19->6 + + + + + +8:22->7 + + + + + +9:24->8 + + + + + diff --git a/examples/storage/TestStorage.svg b/examples/storage/TestStorage.svg index e278e151..a774bebe 100644 --- a/examples/storage/TestStorage.svg +++ b/examples/storage/TestStorage.svg @@ -4,556 +4,9317 @@ - - + + StorageDiagram - - + + -10 - -TestStorage <<Contract>> - -slot - -0 - -1 - -2 - -3 - -4 - -5 - -6-7 - -8-19 - -20-21 - -22-33 - -34-36 - -37-38 - -39 - -40 - -41 - -42 - -43 - -44 - -45-46 - -47 - -48-50 - -51 - -52-53 - -54-57 - -58-90 - -91 - -92 - -93 - -94-95 - -96-111 - -112-143 - -144 - -145-156 - -157-164 - -165 - -166-177 - -178-189 - -190 - -191 - -192 - -193 - -194 - -195-196 - -197-198 - -199-202 - -203-205 - -206 - -207 - -208-210 - -211 - -212 - -213 - -214 - -215 - -216 - -217 - -218 - -219 - -type: <inherited contract>.variable (bytes) - -unallocated (10) - -bool: Parent.initP (1) - -address: GrandParent.grandParent (20) - -bool: GrandParent.initGP (1) - -unallocated (11) - -bool: Parent2.initP2 (1) - -address: Parent.parent (20) - -unallocated (12) - -address: Parent2.parent2 (20) - -unallocated (12) - -address: owner (20) - -unallocated (12) - -IERC20: token (20) - -address[]: tokensDyn (32) - -IERC20[2]: tokenPair (64) - -address[12]: dozenTokens (384) - -address[N_COINS]: coins (64) - -address[N_COINS][3][N_COINS]: multiDimension (384) - -uint256[MAX_COINS]: maxCoins (96) - -IERC20[N_COINS]: tokens (64) - -uint256: totalSupply (32) - -uint128: rate2 (16) - -uint128: rate1 (16) - -bytes32: hash (32) - -unallocated (28) - -bool: flag4 (1) - -bool: flag3 (1) - -bool: flag2 (1) - -bool: flag1 (1) - -bool[2]: flags (32) - -unallocated (31) - -bool: flag5 (1) - -bool[2][2]: flags2x2 (64) - -unallocated (31) - -bool: flag6 (1) - -bool[2][3]: flags2x3 (96) - -unallocated (31) - -bool: flag7 (1) - -bool[3][2]: flags3x2 (64) - -bool[33][2]: flags33x2 (128) - -bool[2][33]: flags2x33 (1056) - -unallocated (31) - -bool: flag8 (1) - -bool[2][]: flags2xDyn (32) - -unallocated (31) - -bool: flag9 (1) - -bool[][2]: flagsDynx2 (64) - -bool[][16]: flagsDynx16 (512) - -bool[][32]: flagsDynx32 (1024) - -bool[32][]: flags32xDyn (32) - -bool[][4][3]: flagsDynx4x3 (384) - -bool[33][2][2]: bool_33x2x2 (256) - -unallocated (31) - -bool: flag10 (1) - -bytes30[2][6]: bytes30_2x6 (384) - -bytes30[6][2]: bytes30_6x2 (384) - -bytes32[]: bytes32Dyn (32) - -unallocated (29) - -Severity: severity (1) - -Status: status (1) - -bool: flag11 (1) - -SubOneSlot: subSlot (32) - -unallocated (31) - -uint8: oneByteNumber (1) - -OneSlot: oneSlot (32) - -SubTwoSlots: subTwoSlot (64) - -TwoSlots: twoSlots (64) - -FixedArray: fixedArray (128) - -FlagsStruct: flagStruct (96) - -unallocated (30) - -int16: arrayCount (2) - -uint64[]: dynamicIntArray (32) - -uint256[3]: fixedIntArray (96) - -mapping(address=>bool): blacklist (32) - -mapping(address=>uint256): balance (32) - -mapping(address=>ContractLevelStruct2): mapStruct (32) - -mapping(address=>mapping(address=>ContractLevelStruct2)): mapOfMapStruct (32) - -string: name (32) - -string: short (32) - -string: exactly32 (32) - -string: long2 (32) - -string: long3 (32) +306 + +TestStorage <<Contract>> + +slot + +0 + +1-2 + +3 + +4 + +5 + +6 + +7 + +8 + +9 + +10-15 + +16 + +17 + +18 + +19 + +20-21 + +22-33 + +34-35 + +36-47 + +48-50 + +51-52 + +53 + +54 + +55 + +56 + +57 + +58-60 + +61-62 + +63-66 + +67-99 + +100 + +101 + +102 + +103 + +104-105 + +106-121 + +122-153 + +154 + +155-166 + +167-174 + +175-186 + +187-198 + +199 + +200 + +201-202 + +203-210 + +211-214 + +215-219 + +220-225 + +226-231 + +232-238 + +239-245 + +246-253 + +254-303 + +304-308 + +309-312 + +313-336 + +337-360 + +361-363 + +364 + +365 + +366-377 + +378 + +379 + +380 + +381 + +382 + +383 + +384 + +385 + +386 + +387-388 + +389-390 + +391-394 + +395-397 + +398 + +399 + +400 + +401 + +402 + +403 + +404 + +405 + +406 + +407 + +408 + +409 + +410 + +411 + +412 + +413 + +414 + +415-435 + +436 + +437-478 + +479 + +480-481 + +482 + +483 + +484 + +485 + +486 + +487 + +488 + +489 + +490 + +491 + +492 + +493 + +494 + +495 + +496 + +497 + +498 + +499 + +500 + +501 + +502 + +type: <inherited contract>.variable (bytes) + +unallocated (11) + +address: GrandParent.grandParent (20) + +bool: GrandParent.initGP (1) + +address[2]: GrandParent.assets (64) + +uint256[]: GrandParent.grandNumbers (32) + +string: GrandParent.grandParentName (32) + +bytes: GrandParent.grandParentData (32) + +unallocated (11) + +address: Parent.parent (20) + +bool: Parent.initP (1) + +string: Parent.parentName (32) + +bytes: Parent.parentData (32) + +unallocated (11) + +address: Parent2.parent2 (20) + +bool: Parent2.initP2 (1) + +uint56[21]: Parent2.smallNumbers (192) + +bytes: Parent2.data (32) + +unallocated (12) + +address: owner (20) + +unallocated (12) + +IERC20: token (20) + +address[]: tokensDyn (32) + +IERC20[2]: tokenPair (64) + +address[12]: dozenTokens (384) + +address[N_COINS]: coins (64) + +address[N_COINS][3][N_COINS]: multiDimension (384) + +uint256[MAX_COINS]: maxCoins (96) + +IERC20[N_COINS]: tokens (64) + +uint256: totalSupply (32) + +uint128: rate2 (16) + +uint128: rate1 (16) + +bytes32: hash (32) + +unallocated (28) + +bool: flag4 (1) + +bool: flag3 (1) + +bool: flag2 (1) + +bool: flag1 (1) + +bool[2]: flags (32) + +bool[2][3]: flags2x3 (96) + +bool[3][2]: flags3x2 (64) + +bool[33][2]: flags33x2 (128) + +bool[2][33]: flags2x33 (1056) + +bool[]: flagsDyn (32) + +bool[][]: flagsDynDyn (32) + +bool[][][]: flagsDynDynDyn (32) + +bool[2][]: flags2xDyn (32) + +bool[][2]: flagsDynx2 (64) + +bool[][16]: flagsDynx16 (512) + +bool[][32]: flagsDynx32 (1024) + +bool[32][]: flags32xDyn (32) + +bool[][4][3]: flagsDynx4x3 (384) + +bool[33][2][2]: bool_33x2x2 (256) + +bytes30[2][6]: bytes30_2x6 (384) + +bytes30[6][2]: bytes30_6x2 (384) + +bytes32[]: bytes32Dyn (32) + +uint32[FileConstant]: timestamps (32) + +uint72[5]: five9ByteNumbers (64) + +uint72[22]: twentyTwo9ByteNumbers (256) + +uint128[7]: sevenHalfNumbers (128) + +uint128[9]: nineHalfNumbers (160) + +uint128[11]: elevenHalfNumbers (192) + +uint128[12]: twelveHalfNumbers (192) + +uint128[13]: thirteenHalfNumbers (224) + +uint128[14]: fourteenHalfNumbers (224) + +uint128[15]: fifteenHalfNumbers (256) + +uint256[50]: gap (1600) + +uint256[5]: fixedIntArray (160) + +TwoSlots[2]: twoSlots2x (128) + +TwoSlots[3][4]: twoSlots3x4 (768) + +TwoSlots[4][3]: twoSlots4x3 (768) + +TwoSlots[][3]: twoSlotsDynx3 (96) + +TwoSlots[3][]: twoSlots3xDyn (32) + +TwoSlots[][]: twoSlotsDynxDyn (32) + +TwoSlots[][4][3]: twoSlotsDynx4x3 (384) + +TwoSlots[3][4][]: twoSlotsDynx3x4xDyn (32) + +Structs.SubTwoSlots[]: twoContractStruct (32) + +unallocated (31) + +Status: status (1) + +Status[]: dynamicStatuses (32) + +unallocated (31) + +Severity: severity (1) + +Severity[4]: staticSeverities (32) + +Structs.SubOneSlot: subSlot (32) + +unallocated (31) + +uint8: oneByteNumber (1) + +Structs.OneSlot: oneSlot (32) + +Structs.SubTwoSlots: subTwoSlot (64) + +TwoSlots: twoSlots (64) + +FixedArray: fixedArray (128) + +FlagsStruct: flagStruct (96) + +unallocated (30) + +int16: arrayCount (2) + +uint256[]: numbers (32) + +uint256[]: empty (32) + +uint56[]: sevenByteNumbers (32) + +uint72[]: nineByteNumbers (32) + +uint64[]: dynamicInt64Array (32) + +uint128[]: dynamicInt128Array (32) + +uint136[]: dynamicInt136Array (32) + +uint256[]: dynamicInt256Array (32) + +uint256[][]: dynamicDynIntArray (32) + +uint256[][][]: dynamicDynDynIntArray (32) + +mapping(address=>bool): blacklist (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct2): mapStruct (32) + +mapping(address=>mapping(address=>ContractLevelStruct2)): mapOfMapStruct (32) + +mapping(address=>Structs.SubTwoSlots): mapContractStruct (32) + +mapping(address=>IERC20): mapInterface (32) + +DynamicStruct: dynamicStruct (672) + +DynamicStruct[]: dynDynamicStruct (32) + +DynamicStruct[2]: staticDynamicStruct (1344) + +mapping(address=>DynamicStruct): mapppedDynamicStruct (32) + +IERC20[2]: interfaceFixedArray (64) + +IERC20[]: interfaceDynArray (32) + +string: uninitialisedString (32) + +string: emptyString (32) + +string: name (32) + +string: short (32) + +string: exactly31 (32) + +string: exactly32 (32) + +string: long2 (32) + +string: long3 (32) + +string: testString (32) + +string: uninitialisedBytes (32) + +string: emptyBytes (32) + +bytes: testBytes (32) + +bytes: exactly31Bytes (32) + +bytes: exactly32Bytes (32) + +bytes: long3Bytes (32) + +uint256: testUint256 (32) + +int256: testInt256 (32) + +unallocated (12) + +address: testAddress (20) + +uint: $some_number (32) + +uint[]: $some_numbers (32) 1 - -SubOneSlot <<Struct>> - -slot - -192 - -type: variable (bytes) - -unallocated (10) - -int8: count (1) - -bool: flag (1) - -address: account (20) - - + +address[2]: assets <<Array>> + +slot + +1 + +2 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + -10:52->1 - - +306:5->1 + + 2 - -OneSlot <<Struct>> - -slot - -194 - -type: variable (bytes) - -uint8: count (1) - -uint88: sum (11) - -address: account (20) - - + +uint256[]: grandNumbers <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + -10:57->2 - - +306:7->2 + + 3 - -SubTwoSlots <<Struct>> - -slot - -195 - -196 - -type: variable (bytes) - -unallocated (12) - -address: account1 (20) - -unallocated (10) - -bool: flag2 (1) - -bool: flag1 (1) - -address: account2 (20) - - + +uint56[21]: smallNumbers <<Array>> + +slot + +10 + +11 + +12-13 + +14 + +15 + +type: variable (bytes) + +unallocated (4) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +unallocated (4) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +---- (64) + +unallocated (4) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +uint56 (7) + +unallocated (25) + +uint56 (7) + + -10:62->3 - - +306:30->3 + + 4 - -TwoSlots <<Struct>> - -slot - -197 - -198 - -type: variable (bytes) - -bytes32: hash1 (32) - -bytes32: hash2 (32) - - + +address[]: tokensDyn <<Array>> +0x66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + -10:65->4 - - +306:35->4 + + 5 - -FixedArray <<Struct>> - -slot - -199 - -200-201 - -202 - -type: variable (bytes) - -unallocated (30) - -uint16: num1 (2) - -bytes30[2]: data (64) - -unallocated (30) - -uint16: num2 (2) - - + +IERC20[2]: tokenPair <<Array>> + +slot + +20 + +21 + +type: variable (bytes) + +unallocated (12) + +IERC20 (20) + +unallocated (12) + +IERC20 (20) + + -10:69->5 - - +306:38->5 + + 6 - -FlagsStruct <<Struct>> - -slot - -203 - -204 - -205 - -type: variable (bytes) - -unallocated (31) - -bool: flag1 (1) - -bool[2]: flags (32) - -unallocated (31) - -bool: flag2 (1) - - + +address[12]: dozenTokens <<Array>> + +slot + +22 + +23 + +24-31 + +32 + +33 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + +---- (256) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + -10:73->6 - - +306:44->6 + + - - -9 - -ContractLevelStruct2 <<Struct>> - -slot - -0-1 - -2-3 - -type: variable (bytes) - -ContractLevelStruct0: param1 (64) - -ContractLevelStruct1: param2 (64) - - + + +7 + +address[N_COINS]: coins <<Array>> + +slot + +34 + +35 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + -10:87->9 - - +306:47->7 + + + + + +16 + +address[N_COINS][3][N_COINS]: multiDimension <<Array>> + +slot + +36-41 + +42-48 + +type: variable (bytes) + +address[N_COINS][3] (192) + +address[N_COINS][3] (192) - + -10:88->9 - - +306:68->16 + + - - -7 - -ContractLevelStruct0 <<Struct>> - -slot - -0 - -1 - -type: variable (bytes) - -uint256: param1 (32) - -unallocated (31) - -bool: param2 (1) + + +17 + +uint256[MAX_COINS]: maxCoins <<Array>> + +slot + +48 + +49 + +50 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + +uint256 (32) + + + +306:72->17 + + + + + +18 + +IERC20[N_COINS]: tokens <<Array>> + +slot + +51 + +52 + +type: variable (bytes) + +unallocated (12) + +IERC20 (20) + +unallocated (12) + +IERC20 (20) + + + +306:75->18 + + + + + +19 + +bool[2]: flags <<Array>> + +slot + +57 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +306:86->19 + + + + + +23 + +bool[2][3]: flags2x3 <<Array>> + +slot + +58 + +59 + +60 + +type: variable (bytes) + +bool[2] (32) + +bool[2] (32) + +bool[2] (32) + + + +306:96->23 + + + + + +26 + +bool[3][2]: flags3x2 <<Array>> + +slot + +61 + +62 + +type: variable (bytes) + +bool[3] (32) + +bool[3] (32) + + + +306:105->26 + + + + + +29 + +bool[33][2]: flags33x2 <<Array>> + +slot + +63-64 + +65-67 + +type: variable (bytes) + +bool[33] (64) + +bool[33] (64) + + + +306:174->29 + + + + + +34 + +bool[2][33]: flags2x33 <<Array>> + +slot + +67 + +68 + +69-97 + +98 + +99 + +type: variable (bytes) + +bool[2] (32) + +bool[2] (32) + +---- (928) + +bool[2] (32) + +bool[2] (32) + + + +306:188->34 + + + + + +35 + +bool[]: flagsDyn <<Array>> +0x26700e13983fefbd9cf16da2ed70fa5c6798ac55062a4803121a869731e308d2 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +306:190->35 + + + + + +37 + +bool[][]: flagsDynDyn <<Array>> +0x8ff97419363ffd7000167f130ef7168fbea05faf9251824ca5043f113cc6a7c7 + +offset + +0 + +type: variable (bytes) + +bool[] (32) + + + +306:193->37 + + + + + +40 + +bool[][][]: flagsDynDynDyn <<Array>> +0x46501879b8ca8525e8c2fd519e2fbfcfa2ebea26501294aa02cbfcfb12e94354 + +offset + +0 + +type: variable (bytes) + +bool[][] (32) + + + +306:197->40 + + + + + +42 + +bool[2][]: flags2xDyn <<Array>> +0x9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae + +offset + +0 + +type: variable (bytes) + +bool[2] (32) + + + +306:201->42 + + + + + +45 + +bool[][2]: flagsDynx2 <<Array>> + +slot + +104 + +105 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + + + +306:206->45 + + + + + +50 + +bool[][16]: flagsDynx16 <<Array>> + +slot + +106 + +107 + +108-119 + +120 + +121 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + +---- (384) + +bool[] (32) + +bool[] (32) + + + +306:216->50 + + + + + +55 + +bool[][32]: flagsDynx32 <<Array>> + +slot + +122 + +123 + +124-151 + +152 + +153 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + +---- (896) + +bool[] (32) + +bool[] (32) + + + +306:226->55 + + + + + +57 + +bool[32][]: flags32xDyn <<Array>> +0x44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4 + +offset + +0 + +type: variable (bytes) + +bool[32] (32) + + + +306:260->57 + + + + + +73 + +bool[][4][3]: flagsDynx4x3 <<Array>> + +slot + +155-158 + +159-163 + +163-167 + +type: variable (bytes) + +bool[][4] (128) + +bool[][4] (128) + +bool[][4] (128) + + + +306:288->73 + + + + + +80 + +bool[33][2][2]: bool_33x2x2 <<Array>> + +slot + +167-170 + +171-175 + +type: variable (bytes) + +bool[33][2] (128) + +bool[33][2] (128) + + + +306:427->80 + + + + + +85 + +bytes30[2][6]: bytes30_2x6 <<Array>> + +slot + +175-176 + +177-179 + +179-183 + +183-185 + +185-187 + +type: variable (bytes) + +bytes30[2] (64) + +bytes30[2] (64) + +---- (160) + +bytes30[2] (64) + +bytes30[2] (64) + + + +306:441->85 + + + + + +88 + +bytes30[6][2]: bytes30_6x2 <<Array>> + +slot + +187-192 + +193-199 + +type: variable (bytes) + +bytes30[6] (192) + +bytes30[6] (192) + + + +306:454->88 + + + + + +89 + +bytes32[]: bytes32Dyn <<Array>> +0xc1d0558604082af4380f8af6e6df686f24c7438ca4f2a67c86a71ee7852601f9 + +offset + +0 + +type: variable (bytes) + +bytes32 (32) + + + +306:456->89 + + + + + +90 + +uint32[FileConstant]: timestamps <<Array>> + +slot + +200 + +type: variable (bytes) + +unallocated (12) + +uint32 (4) + +uint32 (4) + +uint32 (4) + +uint32 (4) + +uint32 (4) + + + +306:462->90 + + + + + +91 + +uint72[5]: five9ByteNumbers <<Array>> + +slot + +201 + +202 + +type: variable (bytes) + +unallocated (5) + +uint72 (9) + +uint72 (9) + +uint72 (9) + +unallocated (14) + +uint72 (9) + +uint72 (9) + + + +306:468->91 + + + + + +92 + +uint72[22]: twentyTwo9ByteNumbers <<Array>> + +slot + +203 + +204 + +205-208 + +209 + +210 + +type: variable (bytes) + +unallocated (5) + +uint72 (9) + +uint72 (9) + +uint72 (9) + +unallocated (5) + +uint72 (9) + +uint72 (9) + +uint72 (9) + +---- (128) + +unallocated (5) + +uint72 (9) + +uint72 (9) + +uint72 (9) + +unallocated (23) + +uint72 (9) + + + +306:480->92 + + + + + +93 + +uint128[7]: sevenHalfNumbers <<Array>> + +slot + +211 + +212 + +213 + +214 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +unallocated (16) + +uint128 (16) + + + +306:488->93 + + + + + +94 + +uint128[9]: nineHalfNumbers <<Array>> + +slot + +215 + +216 + +217 + +218 + +219 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (32) + +uint128 (16) + +uint128 (16) + +unallocated (16) + +uint128 (16) + + + +306:497->94 + + + + + +95 + +uint128[11]: elevenHalfNumbers <<Array>> + +slot + +220 + +221 + +222-223 + +224 + +225 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (64) + +uint128 (16) + +uint128 (16) + +unallocated (16) + +uint128 (16) + + + +306:506->95 + + + + + +96 + +uint128[12]: twelveHalfNumbers <<Array>> + +slot + +226 + +227 + +228-229 + +230 + +231 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (64) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + + + +306:516->96 + + + + + +97 + +uint128[13]: thirteenHalfNumbers <<Array>> + +slot + +232 + +233 + +234-236 + +237 + +238 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (96) + +uint128 (16) + +uint128 (16) + +unallocated (16) + +uint128 (16) + + + +306:525->97 + + + + + +98 + +uint128[14]: fourteenHalfNumbers <<Array>> + +slot + +239 + +240 + +241-243 + +244 + +245 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (96) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + + + +306:535->98 + + + + + +99 + +uint128[15]: fifteenHalfNumbers <<Array>> + +slot + +246 + +247 + +248-251 + +252 + +253 + +type: variable (bytes) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +uint128 (16) + +---- (128) + +uint128 (16) + +uint128 (16) + +unallocated (16) + +uint128 (16) + + + +306:544->99 + + + + + +100 + +uint256[50]: gap <<Array>> + +slot + +254 + +255 + +256-301 + +302 + +303 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + +---- (1472) + +uint256 (32) + +uint256 (32) + + + +306:550->100 + + + + + +101 + +uint256[5]: fixedIntArray <<Array>> + +slot + +304 + +305 + +307 + +308 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + +uint256 (32) + +uint256 (32) + + + +306:555->101 + + + + + +104 + +TwoSlots[2]: twoSlots2x <<Array>> + +slot + +309-310 + +311-313 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + + + +306:562->104 + + + + + +121 + +TwoSlots[3][4]: twoSlots3x4 <<Array>> + +slot + +313-318 + +319-325 + +325-331 + +331-337 + +type: variable (bytes) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + + + +306:603->121 + + + + + +137 + +TwoSlots[4][3]: twoSlots4x3 <<Array>> + +slot + +337-344 + +345-353 + +353-361 + +type: variable (bytes) + +TwoSlots[4] (256) + +TwoSlots[4] (256) + +TwoSlots[4] (256) + + + +306:643->137 + + + + + +144 + +TwoSlots[][3]: twoSlotsDynx3 <<Array>> + +slot + +361 + +362 + +363 + +type: variable (bytes) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + + + +306:656->144 + + + + + +149 + +TwoSlots[3][]: twoSlots3xDyn <<Array>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset + +0-5 + +type: variable (bytes) + +TwoSlots[3] (192) + + + +306:667->149 + + + + + +152 + +TwoSlots[][]: twoSlotsDynxDyn <<Array>> +0x535f6990c27693b80d059530c371f53ea19bfc595ef3bd58348694aac9095300 + +offset + +0 + +type: variable (bytes) + +TwoSlots[] (32) + + + +306:672->152 + + + + + +180 + +TwoSlots[][4][3]: twoSlotsDynx4x3 <<Array>> + +slot + +366-369 + +370-374 + +374-378 + +type: variable (bytes) + +TwoSlots[][4] (128) + +TwoSlots[][4] (128) + +TwoSlots[][4] (128) + + + +306:724->180 + + + + + +198 + +TwoSlots[3][4][]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +0-23 + +type: variable (bytes) + +TwoSlots[3][4] (768) + + + +306:766->198 + + + + + +200 + +Structs.SubTwoSlots[]: twoContractStruct <<Array>> +0x46985247fac81b794ada23f07cfef771057cf55c53a3d12054bcb524935683d7 + +offset + +0-1 + +type: variable (bytes) + +Structs.SubTwoSlots (64) + + + +306:772->200 + + + + + +201 + +Status[]: dynamicStatuses <<Array>> +0x0f2aec2a8c6072690b3c86d5c08e34587f891268d22d8bdca6e66838c0f2f22d + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Status (1) + + + +306:775->201 + + + + + +202 + +Severity[4]: staticSeverities <<Array>> + +slot + +383 + +type: variable (bytes) + +unallocated (28) + +Severity (1) + +Severity (1) + +Severity (1) + +Severity (1) + + + +306:781->202 + + + + + +203 + +Structs.SubOneSlot <<Struct>> + +slot + +384 + +type: variable (bytes) + +unallocated (10) + +int8: SubOneSlot.count (1) + +bool: SubOneSlot.flag (1) + +address: SubOneSlot.account (20) + + + +306:785->203 + + + + + +204 + +Structs.OneSlot <<Struct>> + +slot + +386 + +type: variable (bytes) + +uint8: OneSlot.count (1) + +uint88: OneSlot.sum (11) + +address: OneSlot.account (20) + + + +306:790->204 + + + + + +205 + +Structs.SubTwoSlots <<Struct>> + +slot + +387 + +388 + +type: variable (bytes) + +unallocated (12) + +address: SubTwoSlots.account1 (20) + +unallocated (10) + +bool: SubTwoSlots.flag2 (1) + +bool: SubTwoSlots.flag1 (1) + +address: SubTwoSlots.account2 (20) + + + +306:795->205 + + + + + +206 + +TwoSlots <<Struct>> + +slot + +389 + +390 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +306:798->206 + + + + + +208 + +FixedArray <<Struct>> + +slot + +391 + +392-393 + +394 + +type: variable (bytes) + +unallocated (30) + +uint16: num1 (2) + +bytes30[2]: data (64) + +unallocated (30) + +uint16: num2 (2) + + + +306:804->208 + + + + + +210 + +FlagsStruct <<Struct>> + +slot + +395 + +396 + +397 + +type: variable (bytes) + +unallocated (31) + +bool: flag1 (1) + +bool[2]: flags (32) + +unallocated (31) + +bool: flag2 (1) + + + +306:810->210 + + + + + +211 + +uint256[]: numbers <<Array>> +0x77f3f3fc840536973a5d96a2bc039249e1d449fdef2adeb2ff7a19910de0ff24 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +306:813->211 + + + + + +212 + +uint256[]: empty <<Array>> +0xbd5c86c9fbaa47a598cf55ad1cee7a1d8f8450857cb10e0f122503b8c0e65243 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +306:815->212 + + + + + +213 + +uint56[]: sevenByteNumbers <<Array>> +0xfb4a0ff48df6de8777afc824e4cbf267beb8602db7c5a7456c954593226c1fd5 + +offset + +0 + +type: variable (bytes) + +unallocated (25) + +uint56 (7) + + + +306:817->213 + + + + + +214 + +uint72[]: nineByteNumbers <<Array>> +0xfcfced99f9d921eebdc59aa6f7a664084bd564a3d2d54ebc1a5c057c99c67aba + +offset + +0 + +type: variable (bytes) + +unallocated (23) + +uint72 (9) + + + +306:819->214 + + + + + +215 + +uint64[]: dynamicInt64Array <<Array>> +0xfc8af01f449989052b52093a58fc9f42d0b11f0c6dd5dca0463dab62346ccc68 + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +uint64 (8) + + + +306:821->215 + + + + + +216 + +uint128[]: dynamicInt128Array <<Array>> +0xa6f1ac7ad7b125ba5a5e1c96b00ad6914f90a503b1ac3d85a9dadbb4c639df92 + +offset + +0 + +type: variable (bytes) + +unallocated (16) + +uint128 (16) + + + +306:823->216 + + + + + +217 + +uint136[]: dynamicInt136Array <<Array>> +0x54034dca961b61bc2a3147cc0c1986762915b42723ed64155364f17a2e296770 + +offset + +0 + +type: variable (bytes) + +unallocated (15) + +uint136 (17) + + + +306:825->217 + + + + + +218 + +uint256[]: dynamicInt256Array <<Array>> +0x828feda00a4b64eb35101b6df8f6c29717b1ea6bae5dd03d3ddada8de0a9e7cb + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +306:827->218 + + + + + +220 + +uint256[][]: dynamicDynIntArray <<Array>> +0x3ea4d693734e62a1b4642df418cf4aae0e5ba336a2d6024b2d33585611a4e2eb + +offset + +0 + +type: variable (bytes) + +uint256[] (32) + + + +306:830->220 + + + + + +223 + +uint256[][][]: dynamicDynDynIntArray <<Array>> +0x3f539f465397ab387efa93a617e37005205c037386b5b474fcfac82f47f77f0d + +offset + +0 + +type: variable (bytes) + +uint256[][] (32) + + + +306:834->223 + + + + + +226 + +ContractLevelStruct2 <<Struct>> + +offset + +0-1 + +2-3 + +type: variable (bytes) + +ContractLevelStruct0: param1 (64) + +ContractLevelStruct$_1: param2 (64) + + + +306:845->226 + + + + + +229 + +ContractLevelStruct2 <<Struct>> + +offset + +0-1 + +2-3 + +type: variable (bytes) + +ContractLevelStruct0: param1 (64) + +ContractLevelStruct$_1: param2 (64) + + + +306:854->229 + + + + + +230 + +SubTwoSlots <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (12) + +address: account1 (20) + +unallocated (10) + +bool: flag2 (1) + +bool: flag1 (1) + +address: account2 (20) + + + +306:859->230 + + + + + +244 + +DynamicStruct <<Struct>> + +slot + +415 + +416 + +417 + +418 + +419 + +420-421 + +422 + +423-424 + +425 + +426 + +427-428 + +429-432 + +433 + +434 + +435 + +type: variable (bytes) + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +306:914->244 + + + + + +259 + +DynamicStruct[]: dynDynamicStruct <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +0-20 + +type: variable (bytes) + +DynamicStruct (672) + + + +306:969->259 + + + + + +288 + +DynamicStruct[2]: staticDynamicStruct <<Array>> + +slot + +437-457 + +458-479 + +type: variable (bytes) + +DynamicStruct (672) + +DynamicStruct (672) + + + +306:1078->288 + + + + + +302 + +DynamicStruct <<Struct>> + +offset + +0 + +1 + +2 + +3 + +4 + +5-6 + +7 + +8-9 + +10 + +11 + +12-13 + +14-17 + +18 + +19 + +20 + +type: variable (bytes) + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +306:1132->302 + + + + + +303 + +IERC20[2]: interfaceFixedArray <<Array>> + +slot + +480 + +481 + +type: variable (bytes) + +unallocated (12) + +IERC20 (20) + +unallocated (12) + +IERC20 (20) + + + +306:1135->303 + + + + + +304 + +IERC20[]: interfaceDynArray <<Array>> +0x4075a898d5491b608dbd073c96127d394f36fb783f61de61a7a8e6053f9f2d51 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +IERC20 (20) + + + +306:1137->304 + + + + + +305 + +uint[]: $some_numbers <<Array>> +0x6ea6ba7afdda9dd98317feb84bb80d157ea581b23ca788496ba1c38c64dd8212 + +offset + +0 + +type: variable (bytes) + +uint (32) + + + +306:1158->305 + + 8 - -ContractLevelStruct1 <<Struct>> - -slot - -2 - -3 - -type: variable (bytes) - -uint256: param1 (32) - -unallocated (10) - -bytes1: param4 (1) - -uint8: param3 (1) - -address: param2 (20) - - - -9:81->7 - - + +address[N_COINS]: multiDimension <<Array>> + +slot + +36 + +37 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) - - -9:86->8 - - + + +9 + +address[N_COINS]: multiDimension <<Array>> + +slot + +38 + +39 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +10 + +address[N_COINS]: multiDimension <<Array>> + +slot + +40 + +41 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +11 + +address[N_COINS][3]: multiDimension <<Array>> + +slot + +36-37 + +38-40 + +40-42 + +type: variable (bytes) + +address[N_COINS] (64) + +address[N_COINS] (64) + +address[N_COINS] (64) + + + +11:50->8 + + + + + +11:51->9 + + + + + +11:52->10 + + + + + +12 + +address[N_COINS]: multiDimension <<Array>> + +slot + +42 + +43 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +13 + +address[N_COINS]: multiDimension <<Array>> + +slot + +44 + +45 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +14 + +address[N_COINS]: multiDimension <<Array>> + +slot + +46 + +47 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +15 + +address[N_COINS][3]: multiDimension <<Array>> + +slot + +42-43 + +44-46 + +46-48 + +type: variable (bytes) + +address[N_COINS] (64) + +address[N_COINS] (64) + +address[N_COINS] (64) + + + +15:61->12 + + + + + +15:62->13 + + + + + +15:63->14 + + + + + +16:57->11 + + + + + +16:58->15 + + + + + +20 + +bool[2]: flags2x3 <<Array>> + +slot + +58 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +21 + +bool[2]: flags2x3 <<Array>> + +slot + +59 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +22 + +bool[2]: flags2x3 <<Array>> + +slot + +60 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +23:89->20 + + + + + +23:90->21 + + + + + +23:91->22 + + + + + +24 + +bool[3]: flags3x2 <<Array>> + +slot + +61 + +type: variable (bytes) + +unallocated (29) + +bool (1) + +bool (1) + +bool (1) + + + +25 + +bool[3]: flags3x2 <<Array>> + +slot + +62 + +type: variable (bytes) + +unallocated (29) + +bool (1) + +bool (1) + +bool (1) + + + +26:100->24 + + + + + +26:101->25 + + + + + +27 + +bool[33]: flags33x2 <<Array>> + +slot + +63 + +64 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +28 + +bool[33]: flags33x2 <<Array>> + +slot + +65 + +66 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +29:139->27 + + + + + +29:140->28 + + + + + +30 + +bool[2]: flags2x33 <<Array>> + +slot + +67 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +31 + +bool[2]: flags2x33 <<Array>> + +slot + +68 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +32 + +bool[2]: flags2x33 <<Array>> + +slot + +98 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +33 + +bool[2]: flags2x33 <<Array>> + +slot + +99 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +34:177->30 + + + + + +34:178->31 + + + + + +34:180->32 + + + + + +34:181->33 + + + + + +36 + +bool[]: flagsDynDyn <<Array>> +0x0585cf9928072cd8fc6111506770bc15ecb39777d1a0b80d75c571238837966a + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +37:192->36 + + + + + +38 + +bool[]: flagsDynDynDyn <<Array>> +0x7b6f38283dc0068171ba6f755e0903397b355ef72c81aab8ec95dc7f1ae3c8f8 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +39 + +bool[][]: flagsDynDynDyn <<Array>> +0x0023f9f5bc869f8a01704930a5f32546cd410a1b423c29d8c67c83157f056d0d + +offset + +0 + +type: variable (bytes) + +bool[] (32) + + + +39:195->38 + + + + + +40:196->39 + + + + + +41 + +bool[2]: flags2xDyn <<Array>> +0x9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae + +offset + +0 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +42:200->41 + + + + + +43 + +bool[]: flagsDynx2 <<Array>> +0xa2153420d844928b4421650203c77babc8b33d7f2e7b450e2966db0c22097753 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +44 + +bool[]: flagsDynx2 <<Array>> +0x7fb4302e8e91f9110a6554c2c0a24601252c2a42c2220ca988efcfe399914308 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +45:203->43 + + + + + +45:204->44 + + + + + +46 + +bool[]: flagsDynx16 <<Array>> +0x116fea137db6e131133e7f2bab296045d8f41cc5607279db17b218cab0929a51 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +47 + +bool[]: flagsDynx16 <<Array>> +0xbd43cb8ece8cd1863bcd6082d65c5b0d25665b1ce17980f0da43c0ed545f98b4 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +48 + +bool[]: flagsDynx16 <<Array>> +0x8dc6fb69531d98d70dc0420e638d2dfd04e09e1ec783ede9aac77da9c5a0dac4 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +49 + +bool[]: flagsDynx16 <<Array>> +0x957bbdc7fad0dec56e7c96af4a3ab63aa9daf934a52ffce891945b7fb622d791 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +50:208->46 + + + + + +50:209->47 + + + + + +50:211->48 + + + + + +50:212->49 + + + + + +51 + +bool[]: flagsDynx32 <<Array>> +0xf0440771a29e57e18c66727944770b82cc77924aef333c927ce6bdd2cdb3ae03 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +52 + +bool[]: flagsDynx32 <<Array>> +0x5569044719a1ec3b04d0afa9e7a5310c7c0473331d13dc9fafe143b2c4e8148a + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +53 + +bool[]: flagsDynx32 <<Array>> +0x2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d814 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +54 + +bool[]: flagsDynx32 <<Array>> +0x72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d00 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +55:218->51 + + + + + +55:219->52 + + + + + +55:221->53 + + + + + +55:222->54 + + + + + +56 + +bool[32]: flags32xDyn <<Array>> +0x44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4 + +offset + +0 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + + + +57:259->56 + + + + + +58 + +bool[]: flagsDynx4x3 <<Array>> +0xbba9db4cdbea0a37c207bbb83e20f828cd4441c49891101dc94fd20dc8efc349 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +59 + +bool[]: flagsDynx4x3 <<Array>> +0xaf85b9071dfafeac1409d3f1d19bafc9bc7c37974cde8df0ee6168f0086e539c + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +60 + +bool[]: flagsDynx4x3 <<Array>> +0xd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +61 + +bool[]: flagsDynx4x3 <<Array>> +0xcfe2a20ff701a1f3e14f63bd70d6c6bc6fba8172ec6d5a505cdab3927c0a9de6 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +62 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot + +155 + +156 + +157 + +158 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + +bool[] (32) + +bool[] (32) + + + +62:262->58 + + + + + +62:263->59 + + + + + +62:264->60 + + + + + +62:265->61 + + + + + +63 + +bool[]: flagsDynx4x3 <<Array>> +0x0bc14066c33013fe88f66e314e4cf150b0b2d4d6451a1a51dbbd1c27cd11de28 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +64 + +bool[]: flagsDynx4x3 <<Array>> +0x78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e88 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +65 + +bool[]: flagsDynx4x3 <<Array>> +0xaadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f649878 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +66 + +bool[]: flagsDynx4x3 <<Array>> +0xaaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +67 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot + +159 + +160 + +161 + +162 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + +bool[] (32) + +bool[] (32) + + + +67:273->63 + + + + + +67:274->64 + + + + + +67:275->65 + + + + + +67:276->66 + + + + + +68 + +bool[]: flagsDynx4x3 <<Array>> +0x60859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b17 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +69 + +bool[]: flagsDynx4x3 <<Array>> +0xe434dc35da084cf8d7e8186688ea2dacb53db7003d427af3abf351bd9d0a4e8d + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +70 + +bool[]: flagsDynx4x3 <<Array>> +0xb29a2b3b6f2ff1b765777a231725941da5072cc4fcc30ac4a2ce09706e8ddeff + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +71 + +bool[]: flagsDynx4x3 <<Array>> +0x2da56674729343acc9933752c8c469a244252915242eb6d4c02d11ddd69164a1 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +bool (1) + + + +72 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot + +163 + +164 + +165 + +166 + +type: variable (bytes) + +bool[] (32) + +bool[] (32) + +bool[] (32) + +bool[] (32) + + + +72:281->68 + + + + + +72:282->69 + + + + + +72:283->70 + + + + + +72:284->71 + + + + + +73:269->62 + + + + + +73:270->67 + + + + + +73:271->72 + + + + + +74 + +bool[33]: bool_33x2x2 <<Array>> + +slot + +167 + +168 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +75 + +bool[33]: bool_33x2x2 <<Array>> + +slot + +169 + +170 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +76 + +bool[33][2]: bool_33x2x2 <<Array>> + +slot + +167-168 + +169-171 + +type: variable (bytes) + +bool[33] (64) + +bool[33] (64) + + + +76:322->74 + + + + + +76:323->75 + + + + + +77 + +bool[33]: bool_33x2x2 <<Array>> + +slot + +171 + +172 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +78 + +bool[33]: bool_33x2x2 <<Array>> + +slot + +173 + +174 + +type: variable (bytes) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +bool (1) + +unallocated (31) + +bool (1) + + + +79 + +bool[33][2]: bool_33x2x2 <<Array>> + +slot + +171-172 + +173-175 + +type: variable (bytes) + +bool[33] (64) + +bool[33] (64) + + + +79:392->77 + + + + + +79:393->78 + + + + + +80:357->76 + + + + + +80:358->79 + + + + + +81 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot + +175 + +176 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +82 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot + +177 + +178 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +83 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot + +183 + +184 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +84 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot + +185 + +186 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +85:430->81 + + + + + +85:431->82 + + + + + +85:433->83 + + + + + +85:434->84 + + + + + +86 + +bytes30[6]: bytes30_6x2 <<Array>> + +slot + +187 + +188 + +189-190 + +191 + +192 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + +---- (64) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +87 + +bytes30[6]: bytes30_6x2 <<Array>> + +slot + +193 + +194 + +195-196 + +197 + +198 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + +---- (64) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +88:447->86 + + + + + +88:448->87 + + + + + +102 + +TwoSlots <<Struct>> + +slot + +309 + +310 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +103 + +TwoSlots <<Struct>> + +slot + +311 + +312 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +104:558->102 + + + + + +104:559->103 + + + + + +105 + +TwoSlots <<Struct>> + +slot + +313 + +314 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +106 + +TwoSlots <<Struct>> + +slot + +315 + +316 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +107 + +TwoSlots <<Struct>> + +slot + +317 + +318 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +108 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot + +313-314 + +315-317 + +317-319 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +108:565->105 + + + + + +108:566->106 + + + + + +108:567->107 + + + + + +109 + +TwoSlots <<Struct>> + +slot + +319 + +320 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +110 + +TwoSlots <<Struct>> + +slot + +321 + +322 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +111 + +TwoSlots <<Struct>> + +slot + +323 + +324 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +112 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot + +319-320 + +321-323 + +323-325 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +112:578->109 + + + + + +112:579->110 + + + + + +112:580->111 + + + + + +113 + +TwoSlots <<Struct>> + +slot + +325 + +326 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +114 + +TwoSlots <<Struct>> + +slot + +327 + +328 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +115 + +TwoSlots <<Struct>> + +slot + +329 + +330 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +116 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot + +325-326 + +327-329 + +329-331 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +116:587->113 + + + + + +116:588->114 + + + + + +116:589->115 + + + + + +117 + +TwoSlots <<Struct>> + +slot + +331 + +332 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +118 + +TwoSlots <<Struct>> + +slot + +333 + +334 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +119 + +TwoSlots <<Struct>> + +slot + +335 + +336 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +120 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot + +331-332 + +333-335 + +335-337 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +120:596->117 + + + + + +120:597->118 + + + + + +120:598->119 + + + + + +121:572->108 + + + + + +121:573->112 + + + + + +121:574->116 + + + + + +121:575->120 + + + + + +122 + +TwoSlots <<Struct>> + +slot + +337 + +338 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +123 + +TwoSlots <<Struct>> + +slot + +339 + +340 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +124 + +TwoSlots <<Struct>> + +slot + +341 + +342 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +125 + +TwoSlots <<Struct>> + +slot + +343 + +344 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +126 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot + +337-338 + +339-341 + +341-343 + +343-345 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +126:606->122 + + + + + +126:607->123 + + + + + +126:608->124 + + + + + +126:609->125 + + + + + +127 + +TwoSlots <<Struct>> + +slot + +345 + +346 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +128 + +TwoSlots <<Struct>> + +slot + +347 + +348 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +129 + +TwoSlots <<Struct>> + +slot + +349 + +350 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +130 + +TwoSlots <<Struct>> + +slot + +351 + +352 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +131 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot + +345-346 + +347-349 + +349-351 + +351-353 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +131:621->127 + + + + + +131:622->128 + + + + + +131:623->129 + + + + + +131:624->130 + + + + + +132 + +TwoSlots <<Struct>> + +slot + +353 + +354 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +133 + +TwoSlots <<Struct>> + +slot + +355 + +356 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +134 + +TwoSlots <<Struct>> + +slot + +357 + +358 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +135 + +TwoSlots <<Struct>> + +slot + +359 + +360 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +136 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot + +353-354 + +355-357 + +357-359 + +359-361 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +136:633->132 + + + + + +136:634->133 + + + + + +136:635->134 + + + + + +136:636->135 + + + + + +137:616->126 + + + + + +137:617->131 + + + + + +137:618->136 + + + + + +138 + +TwoSlots <<Struct>> +0xbd88e1fd0aa5f3038c0cd2a68644e2d11f4e4e7e3693adba414318f2127ca426 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +139 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0xbd88e1fd0aa5f3038c0cd2a68644e2d11f4e4e7e3693adba414318f2127ca426 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +139:646->138 + + + + + +140 + +TwoSlots <<Struct>> +0x17da1ae71935bc1a620f4cc216c63f7b5576c3970bae66f4178d3166b9912544 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +141 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0x17da1ae71935bc1a620f4cc216c63f7b5576c3970bae66f4178d3166b9912544 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +141:652->140 + + + + + +142 + +TwoSlots <<Struct>> +0x45c4c4b2842a4a2a717cf0ddf6c6d1dee52b7fd8d9da97eb519765c7a27020f4 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +143 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0x45c4c4b2842a4a2a717cf0ddf6c6d1dee52b7fd8d9da97eb519765c7a27020f4 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +143:655->142 + + + + + +144:647->139 + + + + + +144:648->141 + + + + + +144:649->143 + + + + + +145 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +146 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset + +2 + +3 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +147 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset + +4 + +5 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +148 + +TwoSlots[3]: twoSlots3xDyn <<Array>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset + +0-1 + +2-4 + +4-6 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +148:659->145 + + + + + +148:660->146 + + + + + +148:661->147 + + + + + +149:666->148 + + + + + +150 + +TwoSlots <<Struct>> +0xce4693280460682fc8eed876a7db2826b7eb557ee2a99bbdf3a56792e741fb3a + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +151 + +TwoSlots[]: twoSlotsDynxDyn <<Array>> +0xce4693280460682fc8eed876a7db2826b7eb557ee2a99bbdf3a56792e741fb3a + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +151:670->150 + + + + + +152:671->151 + + + + + +153 + +TwoSlots <<Struct>> +0xf82fbbf6ecba5c6bca8ffdb165c351ca78f1011f756f9b1202212482fd7d8313 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +154 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xf82fbbf6ecba5c6bca8ffdb165c351ca78f1011f756f9b1202212482fd7d8313 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +154:675->153 + + + + + +155 + +TwoSlots <<Struct>> +0x47d2f800c57de5c44dff408e225b320f1ea140ca87907f0726fd6049ee5595d7 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +156 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x47d2f800c57de5c44dff408e225b320f1ea140ca87907f0726fd6049ee5595d7 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +156:682->155 + + + + + +157 + +TwoSlots <<Struct>> +0x88e1afb22f13be7fec203799fd189c3b9471c5b75e2b22a6d5efec11c7f39255 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +158 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x88e1afb22f13be7fec203799fd189c3b9471c5b75e2b22a6d5efec11c7f39255 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +158:685->157 + + + + + +159 + +TwoSlots <<Struct>> +0x42b680591beb20d1de0d4ca368d3f10acc6db07bc74b68a9681655fb351b9039 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +160 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x42b680591beb20d1de0d4ca368d3f10acc6db07bc74b68a9681655fb351b9039 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +160:688->159 + + + + + +161 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot + +366 + +367 + +368 + +369 + +type: variable (bytes) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + + + +161:676->154 + + + + + +161:677->156 + + + + + +161:678->158 + + + + + +161:679->160 + + + + + +162 + +TwoSlots <<Struct>> +0xed95d52ae313e6be6d8c8f9945923a6497c37269ba35d5e3535b758e47227cfc + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +163 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xed95d52ae313e6be6d8c8f9945923a6497c37269ba35d5e3535b758e47227cfc + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +163:694->162 + + + + + +164 + +TwoSlots <<Struct>> +0x56c806aace937fe95f08e7525d5e3e3892fb531816a27802f66a5e4a06502bfc + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +165 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x56c806aace937fe95f08e7525d5e3e3892fb531816a27802f66a5e4a06502bfc + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +165:701->164 + + + + + +166 + +TwoSlots <<Struct>> +0x19628ef4f1e52435a91694e0b17e2591e7982b96f1883cadfe58900fe74230c4 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +167 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x19628ef4f1e52435a91694e0b17e2591e7982b96f1883cadfe58900fe74230c4 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +167:704->166 + + + + + +168 + +TwoSlots <<Struct>> +0x94b016e6b5eafb28c9d47c3523378ec087cc0ead8b7eff2840605504c1f1ac6f + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +169 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x94b016e6b5eafb28c9d47c3523378ec087cc0ead8b7eff2840605504c1f1ac6f + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +169:707->168 + + + + + +170 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot + +370 + +371 + +372 + +373 + +type: variable (bytes) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + + + +170:695->163 + + + + + +170:696->165 + + + + + +170:697->167 + + + + + +170:698->169 + + + + + +171 + +TwoSlots <<Struct>> +0xd8e59d83028feae5da415867a4f0e421f18a256acfabb8749b35464f6997ee83 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +172 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xd8e59d83028feae5da415867a4f0e421f18a256acfabb8749b35464f6997ee83 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +172:710->171 + + + + + +173 + +TwoSlots <<Struct>> +0xf582c8372b7074a2b8ed06ba0ff712db79e8bd5ed68aed830cac17f54eeda510 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +174 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xf582c8372b7074a2b8ed06ba0ff712db79e8bd5ed68aed830cac17f54eeda510 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +174:717->173 + + + + + +175 + +TwoSlots <<Struct>> +0x34be40e60503fe71e236a0cea8e2a63f076cacc21dad7950c8c1bd09a373b91b + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +176 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x34be40e60503fe71e236a0cea8e2a63f076cacc21dad7950c8c1bd09a373b91b + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +176:720->175 + + + + + +177 + +TwoSlots <<Struct>> +0x417650d1fcdb071a1ee8e15f023e1bf12032a7a2f58523e1cc7c932f73103e43 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +178 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x417650d1fcdb071a1ee8e15f023e1bf12032a7a2f58523e1cc7c932f73103e43 + +offset + +0-1 + +type: variable (bytes) + +TwoSlots (64) + + + +178:723->177 + + + + + +179 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot + +374 + +375 + +376 + +377 + +type: variable (bytes) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + +TwoSlots[] (32) + + + +179:711->172 + + + + + +179:712->174 + + + + + +179:713->176 + + + + + +179:714->178 + + + + + +180:689->161 + + + + + +180:690->170 + + + + + +180:691->179 + + + + + +181 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +0 + +1 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +182 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +2 + +3 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +183 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +4 + +5 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +184 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +0-1 + +2-4 + +4-6 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +184:727->181 + + + + + +184:728->182 + + + + + +184:729->183 + + + + + +185 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +6 + +7 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +186 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +8 + +9 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +187 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +10 + +11 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +188 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +6-7 + +8-10 + +10-12 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +188:740->185 + + + + + +188:741->186 + + + + + +188:742->187 + + + + + +189 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +12 + +13 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +190 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +14 + +15 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +191 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +16 + +17 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +192 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +12-13 + +14-16 + +16-18 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +192:749->189 + + + + + +192:750->190 + + + + + +192:751->191 + + + + + +193 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +18 + +19 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +194 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +20 + +21 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +195 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +22 + +23 + +type: variable (bytes) + +bytes32: hash1 (32) + +bytes32: hash2 (32) + + + +196 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +18-19 + +20-22 + +22-24 + +type: variable (bytes) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +196:758->193 + + + + + +196:759->194 + + + + + +196:760->195 + + + + + +197 + +TwoSlots[3][4]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset + +0-5 + +6-12 + +12-18 + +18-24 + +type: variable (bytes) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + + + +197:734->184 + + + + + +197:735->188 + + + + + +197:736->192 + + + + + +197:737->196 + + + + + +198:765->197 + + + + + +199 + +Structs.SubTwoSlots <<Struct>> +0x46985247fac81b794ada23f07cfef771057cf55c53a3d12054bcb524935683d7 + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (12) + +address: SubTwoSlots.account1 (20) + +unallocated (10) + +bool: SubTwoSlots.flag2 (1) + +bool: SubTwoSlots.flag1 (1) + +address: SubTwoSlots.account2 (20) + + + +200:771->199 + + + + + +207 + +bytes30[2]: data <<Array>> + +slot + +392 + +393 + +type: variable (bytes) + +unallocated (2) + +bytes30 (30) + +unallocated (2) + +bytes30 (30) + + + +208:802->207 + + + + + +209 + +bool[2]: flags <<Array>> + +slot + +396 + +type: variable (bytes) + +unallocated (30) + +bool (1) + +bool (1) + + + +210:808->209 + + + + + +219 + +uint256[]: dynamicDynIntArray <<Array>> +0xc1491625e5de6cf60f57d34b39f9a8be7328b8e366bf82d25c540486afdf7737 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +220:829->219 + + + + + +221 + +uint256[]: dynamicDynDynIntArray <<Array>> +0x530a3038a6150618b098d94aeb6f04ebdc7e5215f3ec24b33f6934ea0f1a3d83 + +offset + +0 + +type: variable (bytes) + +uint256 (32) + + + +222 + +uint256[][]: dynamicDynDynIntArray <<Array>> +0xb54252cb0985c00ba4e5a95901144db6f2dd840128a8a8dcb2c6f67f98081bb5 + +offset + +0 + +type: variable (bytes) + +uint256[] (32) + + + +222:832->221 + + + + + +223:833->222 + + + + + +224 + +ContractLevelStruct0 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (31) + +bool: param2 (1) + + + +225 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +226:839->224 + + + + + +226:844->225 + + + + + +227 + +ContractLevelStruct0 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (31) + +bool: param2 (1) + + + +228 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +229:848->227 + + + + + +229:853->228 + + + + + +231 + +Severity[3]: staticSeverities <<Array>> + +slot + +417 + +type: variable (bytes) + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +232 + +Severity[]: dynamicSeverities <<Array>> +0x16f51b7b770dc888307b215a468396cad0bf985bd7e546f15769c8fd0d2b9404 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Severity (1) + + + +233 + +address[]: dynamicAddressArray <<Array>> +0x9956c0f14fc2ed1836f96a0d9c70b58f1406f12ef284cbc764439ac2a31089c0 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +234 + +address[2]: staticAddressArray <<Array>> + +slot + +420 + +421 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +235 + +int64[]: dynamicIntArray <<Array>> +0xc215118b47753a1fd554d2ebe7dfcd1e01570cadae61797613b4a12ccec41217 + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +int64 (8) + + + +236 + +int64[5]: staticIntArray <<Array>> + +slot + +423 + +424 + +type: variable (bytes) + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +237 + +ContractLevelStruct$_1 <<Struct>> + +slot + +427 + +428 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +238 + +ContractLevelStruct$_1 <<Struct>> + +slot + +429 + +430 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +239 + +ContractLevelStruct$_1 <<Struct>> + +slot + +431 + +432 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +240 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot + +429-430 + +431-433 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +240:895->238 + + + + + +240:896->239 + + + + + +241 + +ContractLevelStruct$_1 <<Struct>> +0x1e831cd9a413edbf49535827428c0a05d2a0cc826f6b135dcd7395b76a16bb92 + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +242 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0x1e831cd9a413edbf49535827428c0a05d2a0cc826f6b135dcd7395b76a16bb92 + +offset + +0-1 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + + + +242:906->241 + + + + + +243 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +244:868->231 + + + + + +244:870->232 + + + + + +244:872->233 + + + + + +244:875->234 + + + + + +244:877->235 + + + + + +244:883->236 + + + + + +244:890->237 + + + + + +244:901->240 + + + + + +244:907->242 + + + + + +244:913->243 + + + + + +245 + +Severity[3]: staticSeverities <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +2 + +type: variable (bytes) + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +246 + +Severity[]: dynamicSeverities <<Array>> +0xb72780891006f3ea70943904343e9b9eafe6a9e933828589f35da6268d805370 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Severity (1) + + + +247 + +address[]: dynamicAddressArray <<Array>> +0x41722158d060294771616bee402f71f04c1bf37462266adcf737c2d764b9b93b + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +248 + +address[2]: staticAddressArray <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +5 + +6 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +249 + +int64[]: dynamicIntArray <<Array>> +0xbe4d12cecead8944bce521ca447523dfb797a24c040d9bc0b4fae6c66e9c0161 + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +int64 (8) + + + +250 + +int64[5]: staticIntArray <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +8 + +9 + +type: variable (bytes) + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +251 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +12 + +13 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +252 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +14 + +15 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +253 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +16 + +17 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +254 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +14-15 + +16-18 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +254:949->252 + + + + + +254:950->253 + + + + + +255 + +ContractLevelStruct$_1 <<Struct>> +0xa9ac31f29d0f1c928c4c1eee202b9f610d3017cdb2854fc8b91f8b41330b3cf4 + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +256 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xa9ac31f29d0f1c928c4c1eee202b9f610d3017cdb2854fc8b91f8b41330b3cf4 + +offset + +0-1 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + + + +256:960->255 + + + + + +257 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +258 + +DynamicStruct <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset + +0 + +1 + +2 + +3 + +4 + +5-6 + +7 + +8-9 + +10 + +11 + +12-13 + +14-17 + +18 + +19 + +20 + +type: variable (bytes) + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +258:922->245 + + + + + +258:924->246 + + + + + +258:926->247 + + + + + +258:929->248 + + + + + +258:931->249 + + + + + +258:937->250 + + + + + +258:944->251 + + + + + +258:955->254 + + + + + +258:961->256 + + + + + +258:967->257 + + + + + +259:968->258 + + + + + +260 + +Severity[3]: staticSeverities <<Array>> + +slot + +439 + +type: variable (bytes) + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +261 + +Severity[]: dynamicSeverities <<Array>> +0x70fd51becab23852b2dd090cdedf7d9d704171d1584775462189b90bc6e7db26 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Severity (1) + + + +262 + +address[]: dynamicAddressArray <<Array>> +0x86f721ae575c04fef389f7a94b7c0e72145101a4063a4b13c61b108052d81c93 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +263 + +address[2]: staticAddressArray <<Array>> + +slot + +442 + +443 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +264 + +int64[]: dynamicIntArray <<Array>> +0xa67c9a55b8253f6b29a35b777ec3f4948a657d7db9ddd16fdf650ef37821b12b + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +int64 (8) + + + +265 + +int64[5]: staticIntArray <<Array>> + +slot + +445 + +446 + +type: variable (bytes) + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +266 + +ContractLevelStruct$_1 <<Struct>> + +slot + +449 + +450 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +267 + +ContractLevelStruct$_1 <<Struct>> + +slot + +451 + +452 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +268 + +ContractLevelStruct$_1 <<Struct>> + +slot + +453 + +454 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +269 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot + +451-452 + +453-455 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +269:1004->267 + + + + + +269:1005->268 + + + + + +270 + +ContractLevelStruct$_1 <<Struct>> +0xff6df30967a6a678f565c59a19e91e5c0dbb20cfe9f9bf26d7da6dea0fffa24c + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +271 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xff6df30967a6a678f565c59a19e91e5c0dbb20cfe9f9bf26d7da6dea0fffa24c + +offset + +0-1 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + + + +271:1015->270 + + + + + +272 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +273 + +DynamicStruct <<Struct>> + +slot + +437 + +438 + +439 + +440 + +441 + +442-443 + +444 + +445-446 + +447 + +448 + +449-450 + +451-454 + +455 + +456 + +457 + +type: variable (bytes) + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +273:977->260 + + + + + +273:979->261 + + + + + +273:981->262 + + + + + +273:984->263 + + + + + +273:986->264 + + + + + +273:992->265 + + + + + +273:999->266 + + + + + +273:1010->269 + + + + + +273:1016->271 + + + + + +273:1022->272 + + + + + +274 + +Severity[3]: staticSeverities <<Array>> + +slot + +460 + +type: variable (bytes) + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +275 + +Severity[]: dynamicSeverities <<Array>> +0x41bf21270d8c221a457e2f64e0b5e3c274a814409eea17edf41bb9eb4ee64eb0 + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Severity (1) + + + +276 + +address[]: dynamicAddressArray <<Array>> +0x5d6f015ab75b09997e10f2fa9ea352d95dce65c2d7ce5ac920ae531c9a381e4a + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +277 + +address[2]: staticAddressArray <<Array>> + +slot + +463 + +464 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +278 + +int64[]: dynamicIntArray <<Array>> +0xcb7eb705d118d1261a01ba529793210c1e805fc376af74293a5f75da3c12b2ed + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +int64 (8) + + + +279 + +int64[5]: staticIntArray <<Array>> + +slot + +466 + +467 + +type: variable (bytes) + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +280 + +ContractLevelStruct$_1 <<Struct>> + +slot + +470 + +471 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +281 + +ContractLevelStruct$_1 <<Struct>> + +slot + +472 + +473 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +282 + +ContractLevelStruct$_1 <<Struct>> + +slot + +474 + +475 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +283 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot + +472-473 + +474-476 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +283:1059->281 + + + + + +283:1060->282 + + + + + +284 + +ContractLevelStruct$_1 <<Struct>> +0xcfe30d177f24701724aea052a3a6fc2a8609362a38e75e470a4fedd55b9b7e36 + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +285 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xcfe30d177f24701724aea052a3a6fc2a8609362a38e75e470a4fedd55b9b7e36 + +offset + +0-1 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + + + +285:1070->284 + + + + + +286 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +287 + +DynamicStruct <<Struct>> + +slot + +458 + +459 + +460 + +461 + +462 + +463-464 + +465 + +466-467 + +468 + +469 + +470-471 + +472-475 + +476 + +477 + +478 + +type: variable (bytes) + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +287:1032->274 + + + + + +287:1034->275 + + + + + +287:1036->276 + + + + + +287:1039->277 + + + + + +287:1041->278 + + + + + +287:1047->279 + + + + + +287:1054->280 + + + + + +287:1065->283 + + + + + +287:1071->285 + + + + + +287:1077->286 + + + + + +288:1023->273 + + + + + +288:1024->287 + + + + + +289 + +Severity[3]: staticSeverities <<Array>> + +offset + +0 + +type: variable (bytes) + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +290 + +Severity[]: dynamicSeverities <<Array>> + +offset + +0 + +type: variable (bytes) + +unallocated (31) + +Severity (1) + + + +291 + +address[]: dynamicAddressArray <<Array>> + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +292 + +address[2]: staticAddressArray <<Array>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +293 + +int64[]: dynamicIntArray <<Array>> + +offset + +0 + +type: variable (bytes) + +unallocated (24) + +int64 (8) + + + +294 + +int64[5]: staticIntArray <<Array>> + +offset + +0 + +1 + +type: variable (bytes) + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +295 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +296 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +297 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +298 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +offset + +0-1 + +2-4 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +298:1113->296 + + + + + +298:1114->297 + + + + + +299 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +300 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> + +offset + +0-1 + +type: variable (bytes) + +ContractLevelStruct$_1 (64) + + + +300:1124->299 + + + + + +301 + +ContractLevelStruct$_1 <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +302:1086->289 + + + + + +302:1088->290 + + + + + +302:1090->291 + + + + + +302:1093->292 + + + + + +302:1095->293 + + + + + +302:1101->294 + + + + + +302:1108->295 + + + + + +302:1119->298 + + + + + +302:1125->300 + + + + + +302:1131->301 + + diff --git a/examples/storage/TestStoragePolygonData.svg b/examples/storage/TestStoragePolygonData.svg new file mode 100644 index 00000000..deca3a3e --- /dev/null +++ b/examples/storage/TestStoragePolygonData.svg @@ -0,0 +1,15487 @@ + + + + + + +StorageDiagram + + + +306 + +TestStorage <<Contract>> +0xB78797ceE9Bf127D8eE2Fc4c967c82B5d27Af864 + +slot +   + +0 +   + +1-2 + +3 +   + +4 +   + +5 +   + +6 +   + +7 +   + +8 +   + +9 +   + +10-15 + +16 +   + +17 +   + +18 +   + +19 +   + +20-21 + +22-33 + +34-35 + +36-47 + +48-50 + +51-52 + +53 +   + +54 +   + +55 +   + +56 +   + +57 + +58-60 + +61-62 + +63-66 + +67-99 + +100 +   + +101 +   + +102 +   + +103 +   + +104-105 + +106-121 + +122-153 + +154 +   + +155-166 + +167-174 + +175-186 + +187-198 + +199 +   + +200 + +201-202 + +203-210 + +211-214 + +215-219 + +220-225 + +226-231 + +232-238 + +239-245 + +246-253 + +254-303 + +304-308 + +309-312 + +313-336 + +337-360 + +361-363 + +364 +   + +365 +   + +366-377 + +378 +   + +379 +   + +380 +   + +381 +   + +382 +   + +383 + +384 + +385 +   + +386 + +387-388 + +389-390 + +391-394 + +395-397 + +398 +   + +399 +   + +400 +   + +401 +   + +402 +   + +403 +   + +404 +   + +405 +   + +406 +   + +407 +   + +408 +   + +409 + +410 + +411 + +412 + +413 + +414 + +415-435 + +436 +   + +437-478 + +479 + +480-481 + +482 +   + +483 +   + +484 +   + +485 +   + +486 +   + +487 +   + +488 +   + +489 +   + +490 +   + +491 +   + +492 +   + +493 +   + +494 +   + +495 +   + +496 +   + +497 +   + +498 +   + +499 +   + +500 +   + +501 +   + +502 + + +value +   + +0x0000000000000000000000FF1B44F1FCCEBC4890B5E00A1EA9259D00A40FEB01 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x000000000000000000000000000000000000000000000000000000000000005F +   + +0xFFEEDDCCBBAA9988776655443322110011000000000000000000000000000022 +   + +0x0000000000000000000000EC20607AA654D823DD01BEB8780A44863C57ED0701 +   + +0x506172656E74000000000000000000000000000000000000000000000000000C +   + +0xF0123456789ABCDEFF0000000000000000000000000000000000000000000012 +   + +0x0000000000000000000000B985439AFA9314DCB002E191E230A5936493479B01 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000002F2DB75C5276481E2B018AC03E968AF7763ED118 +   + +0x00000000000000000000000034F08F2A3F4A86531E9C4139FDE571A62689AFEC +   + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + + + + + + + + + + + + + +0x00000000000000000000000000000000000000000065D855E0F157DB651CF34E +   + +0x0000000000000018B84570022A2000000000000000000006AAF7C8516D0C0000 +   + +0xE9B69CD5563A8BFBFFB0FA4F422862013492D43FE7FB62D771A0147B6E891D13 +   + +0x0000000000000000000000000000000000000000000000000000000000010101 +   + + + + + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000007 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000006 +   + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + + + +0x00000000000000000000000000000000000000000000000000000000000000FD +   + + + + + + + + + + + +0x000000000000000000000000000000000000000000000000000000000000F830 +   + +0x000000000000000000000000000000000000000000000000000000000000000A +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000014 +   + +0x0000000000000000000000000000000000000000000000000000000000000010 +   + +0x0000000000000000000000000000000000000000000000000000000000000007 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000005 +   + +0x000000000000000000000000000000000000000000000000000000000000000C +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + + + + + + + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + + + + + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x5465737453746F7261676520636F6E7472616374000000000000000000000028 +   + +0x4C657373207468616E2033312062797465730000000000000000000000000024 +   + +0x65786163746C7920333120636861727320736F2075736573203120736C6F743E +   + +0x0000000000000000000000000000000000000000000000000000000000000041 +   + +0x0000000000000000000000000000000000000000000000000000000000000077 +   + +0x0000000000000000000000000000000000000000000000000000000000000099 +   + +0x000000000000000000000000000000000000000000000000000000000000004D +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0xFFEEDDCCBBAA9988770011000000000000000000000000000000000000000016 +   + +0xEC0B854938343F85EB39A6648B9E449C2E4AEE4DC9B4E96AB592F9F497D0513E +   + +0x0000000000000000000000000000000000000000000000000000000000000041 +   + +0x0000000000000000000000000000000000000000000000000000000000000085 +   + +0x000000000000000000000000000000000000000000000000FEDCBA9876543210 +   + +0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC01 +   + +0x000000000000000000000000B78797CEE9BF127D8EE2FC4C967C82B5D27AF864 +   + +0x00000000000000000000000000000000000000000000000000000000000000FE +   + +0x0000000000000000000000000000000000000000000000000000000000000005 + + +type: <inherited contract>.variable (bytes) +decoded data + +unallocated (11) + +address: GrandParent.grandParent (20) + 0xfF1b44f1FCCebc4890B5E00a1EA9259d00a40fEb   + +bool: GrandParent.initGP (1) + true + +address[2]: GrandParent.assets (64) + +uint256[]: GrandParent.grandNumbers (32) + 4 + +string: GrandParent.grandParentName (32) + 47 + +bytes: GrandParent.grandParentData (32) + 0xFFEEDDCCBBAA9988776655443322110011 + +unallocated (11) + +address: Parent.parent (20) + 0xeC20607aa654D823DD01BEB8780a44863c57Ed07   + +bool: Parent.initP (1) + true + +string: Parent.parentName (32) + "Parent" + +bytes: Parent.parentData (32) + 0xF0123456789ABCDEFF + +unallocated (11) + +address: Parent2.parent2 (20) + 0xb985439AFa9314dCB002E191e230A5936493479B   + +bool: Parent2.initP2 (1) + true + +uint56[21]: Parent2.smallNumbers (192) + +bytes: Parent2.data (32) + 0x + +unallocated (12) + +address: owner (20) + 0x2f2Db75C5276481E2B018Ac03e968af7763Ed118 + +unallocated (12) + +IERC20: token (20) + 0x34f08F2A3f4a86531e9C4139Fde571a62689AFEC + +address[]: tokensDyn (32) + 4 + +IERC20[2]: tokenPair (64) + +address[12]: dozenTokens (384) + +address[N_COINS]: coins (64) + +address[N_COINS][3][N_COINS]: multiDimension (384) + +uint256[MAX_COINS]: maxCoins (96) + +IERC20[N_COINS]: tokens (64) + +uint256: totalSupply (32) + 123,123,123,123,456,789,012,345,678 + +uint128: rate2 (16) + 456,000,000,000,000,000,000   + +uint128: rate1 (16) + 123,000,000,000,000,000,000 + +bytes32: hash (32) + 0xE9B69CD5563A8BFBFFB0FA4F422862013492D43FE7FB62D771A0147B6E891D13 + +unallocated (28) + +bool: flag4 (1) + false   + +bool: flag3 (1) + true   + +bool: flag2 (1) + true   + +bool: flag1 (1) + true + +bool[2]: flags (32) + +bool[2][3]: flags2x3 (96) + +bool[3][2]: flags3x2 (64) + +bool[33][2]: flags33x2 (128) + +bool[2][33]: flags2x33 (1056) + +bool[]: flagsDyn (32) + 7 + +bool[][]: flagsDynDyn (32) + 4 + +bool[][][]: flagsDynDynDyn (32) + 3 + +bool[2][]: flags2xDyn (32) + 6 + +bool[][2]: flagsDynx2 (64) + +bool[][16]: flagsDynx16 (512) + +bool[][32]: flagsDynx32 (1024) + +bool[32][]: flags32xDyn (32) + 2 + +bool[][4][3]: flagsDynx4x3 (384) + +bool[33][2][2]: bool_33x2x2 (256) + +bytes30[2][6]: bytes30_2x6 (384) + +bytes30[6][2]: bytes30_6x2 (384) + +bytes32[]: bytes32Dyn (32) + 2 + +uint32[FileConstant]: timestamps (32) + +uint72[5]: five9ByteNumbers (64) + +uint72[22]: twentyTwo9ByteNumbers (256) + +uint128[7]: sevenHalfNumbers (128) + +uint128[9]: nineHalfNumbers (160) + +uint128[11]: elevenHalfNumbers (192) + +uint128[12]: twelveHalfNumbers (192) + +uint128[13]: thirteenHalfNumbers (224) + +uint128[14]: fourteenHalfNumbers (224) + +uint128[15]: fifteenHalfNumbers (256) + +uint256[50]: gap (1600) + +uint256[5]: fixedIntArray (160) + +TwoSlots[2]: twoSlots2x (128) + +TwoSlots[3][4]: twoSlots3x4 (768) + +TwoSlots[4][3]: twoSlots4x3 (768) + +TwoSlots[][3]: twoSlotsDynx3 (96) + +TwoSlots[3][]: twoSlots3xDyn (32) + 1 + +TwoSlots[][]: twoSlotsDynxDyn (32) + 0 + +TwoSlots[][4][3]: twoSlotsDynx4x3 (384) + +TwoSlots[3][4][]: twoSlotsDynx3x4xDyn (32) + 0 + +Structs.SubTwoSlots[]: twoContractStruct (32) + 0 + +unallocated (31) + +Status: status (1) + Open + +Status[]: dynamicStatuses (32) + 3 + +unallocated (31) + +Severity: severity (1) + High + +Severity[4]: staticSeverities (32) + +Structs.SubOneSlot: subSlot (32) + +unallocated (31) + +uint8: oneByteNumber (1) + 253 + +Structs.OneSlot: oneSlot (32) + +Structs.SubTwoSlots: subTwoSlot (64) + +TwoSlots: twoSlots (64) + +FixedArray: fixedArray (128) + +FlagsStruct: flagStruct (96) + +unallocated (30) + +int16: arrayCount (2) + -2,000 + +uint256[]: numbers (32) + 10 + +uint256[]: empty (32) + 0 + +uint56[]: sevenByteNumbers (32) + 20 + +uint72[]: nineByteNumbers (32) + 16 + +uint64[]: dynamicInt64Array (32) + 7 + +uint128[]: dynamicInt128Array (32) + 3 + +uint136[]: dynamicInt136Array (32) + 4 + +uint256[]: dynamicInt256Array (32) + 5 + +uint256[][]: dynamicDynIntArray (32) + 12 + +uint256[][][]: dynamicDynDynIntArray (32) + 3 + +mapping(address=>bool): blacklist (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct2): mapStruct (32) + +mapping(address=>mapping(address=>ContractLevelStruct2)): mapOfMapStruct (32) + +mapping(address=>Structs.SubTwoSlots): mapContractStruct (32) + +mapping(address=>IERC20): mapInterface (32) + +DynamicStruct: dynamicStruct (672) + +DynamicStruct[]: dynDynamicStruct (32) + 1 + +DynamicStruct[2]: staticDynamicStruct (1344) + +mapping(address=>DynamicStruct): mapppedDynamicStruct (32) + +IERC20[2]: interfaceFixedArray (64) + +IERC20[]: interfaceDynArray (32) + 3 + +string: uninitialisedString (32) + "" + +string: emptyString (32) + "" + +string: name (32) + "TestStorage contract" + +string: short (32) + "Less than 31 bytes" + +string: exactly31 (32) + "exactly 31 chars so uses 1 slot" + +string: exactly32 (32) + 32 + +string: long2 (32) + 59 + +string: long3 (32) + 76 + +string: testString (32) + 38 + +string: uninitialisedBytes (32) + "" + +string: emptyBytes (32) + "" + +bytes: testBytes (32) + 0xFFEEDDCCBBAA9988770011 + +bytes: exactly31Bytes (32) + 0xEC0B854938343F85EB39A6648B9E449C2E4AEE4DC9B4E96AB592F9F497D051 + +bytes: exactly32Bytes (32) + 32 + +bytes: long3Bytes (32) + 66 + +uint256: testUint256 (32) + 18,364,758,544,493,064,720 + +int256: testInt256 (32) + -1,023 + +unallocated (12) + +address: testAddress (20) + 0xB78797ceE9Bf127D8eE2Fc4c967c82B5d27Af864 + +uint: $some_number (32) + 254 + +uint[]: $some_numbers (32) + 5 + + + +1 + +address[2]: assets <<Array>> + +slot +   + +1 +   + +2 + + +value +   + +0x000000000000000000000000690B9A9E9AA1C9DB991C7721A92D351DB4FAC990 +   + +0x000000000000000000000000E688B84B23F322A994A53DBF8E15FA82CDB71127 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990 + +unallocated (12) + +address (20) + 0xe688b84b23f322a994A53dbF8E15FA82CDB71127 + + + +306:5->1 + + + + + +2 + +uint256[]: grandNumbers <<Array>> +0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b + +offset +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: variable (bytes) +decoded data + +uint256 (32) + 1 + +uint256 (32) + 2 + +uint256 (32) + 3 + +uint256 (32) + 4 + + + +306:7->2 + + + + + +3 + +uint56[21]: smallNumbers <<Array>> + +slot +   + +10 +   + +11 +   + +12-13 + +14 +   + +15 + + +value +   + +0x0000000000000000000004000000000000030000000000000200000000000001 +   + +0x0000000000000000000008000000000000070000000000000600000000000005 +   + + + +0x0000000000000000000014000000000000130000000000001200000000000011 +   + +0x0000000000000000000000000000000000000000000000000000000000000015 + + +type: variable (bytes) +decoded data + +unallocated (4) + +uint56 (7) + 4   + +uint56 (7) + 3   + +uint56 (7) + 2   + +uint56 (7) + 1 + +unallocated (4) + +uint56 (7) + 8   + +uint56 (7) + 7   + +uint56 (7) + 6   + +uint56 (7) + 5 + +---- (64) + +unallocated (4) + +uint56 (7) + 20   + +uint56 (7) + 19   + +uint56 (7) + 18   + +uint56 (7) + 17 + +unallocated (25) + +uint56 (7) + 21 + + + +306:30->3 + + + + + +4 + +address[]: tokensDyn <<Array>> +0x66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090 + +offset +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x000000000000000000000000FF000000000000000000000000000000000000A1 +   + +0x000000000000000000000000FF000000000000000000000000000000000000B2 +   + +0x000000000000000000000000FF000000000000000000000000000000000000C3 +   + +0x000000000000000000000000FF000000000000000000000000000000000000D4 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xfF000000000000000000000000000000000000A1 + +unallocated (12) + +address (20) + 0xff000000000000000000000000000000000000b2 + +unallocated (12) + +address (20) + 0xFf000000000000000000000000000000000000c3 + +unallocated (12) + +address (20) + 0xFF000000000000000000000000000000000000d4 + + + +306:35->4 + + + + + +5 + +IERC20[2]: tokenPair <<Array>> + +slot +   + +20 +   + +21 + + +value +   + +0x000000000000000000000000E2F2A5C287993345A840DB3B0845FBC70F5935A5 +   + +0x000000000000000000000000945FACB997494CC2570096C74B5F66A3507330A1 + + +type: variable (bytes) +decoded data + +unallocated (12) + +IERC20 (20) + 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5 + +unallocated (12) + +IERC20 (20) + 0x945Facb997494CC2570096c74b5F66A3507330a1 + + + +306:38->5 + + + + + +6 + +address[12]: dozenTokens <<Array>> + +slot +   + +22 +   + +23 +   + +24-31 + +32 +   + +33 + + +value +   + +0x000000000000000000000000A57BD00134B2850B2A1C55860C9E9EA100FDD6CF +   + +0x0000000000000000000000002000000000000000000000000000000000000001 +   + + + +0x000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xa57Bd00134B2850B2a1c55860c9e9ea100fDd6CF + +unallocated (12) + +address (20) + 0x2000000000000000000000000000000000000001 + +---- (256) + +unallocated (12) + +address (20) + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +306:44->6 + + + + + +7 + +address[N_COINS]: coins <<Array>> + +slot +   + +34 +   + +35 + + +value +   + +0x0000000000000000000000003000000000000000000000000000000000000001 +   + +0x0000000000000000000000003000000000000000000000000000000000000002 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x3000000000000000000000000000000000000001 + +unallocated (12) + +address (20) + 0x3000000000000000000000000000000000000002 + + + +306:47->7 + + + + + +16 + +address[N_COINS][3][N_COINS]: multiDimension <<Array>> + +slot +   + +36-41 + +42-48 + +value +   + + + + + +type: variable (bytes) +decoded data + +address[N_COINS][3] (192) + +address[N_COINS][3] (192) + + + +306:68->16 + + + + + +17 + +uint256[MAX_COINS]: maxCoins <<Array>> + +slot +   + +48 +   + +49 +   + +50 + + +value +   + +0x00000000000000000000000000000000000000000000000000000000000004D2 +   + +0x0000000000000000000000000000000000000000000000000000000000000237 +   + +0x00000000000000000000000000000000000000000000000000000000000022CE + + +type: variable (bytes) +decoded data + +uint256 (32) + 1,234 + +uint256 (32) + 567 + +uint256 (32) + 8,910 + + + +306:72->17 + + + + + +18 + +IERC20[N_COINS]: tokens <<Array>> + +slot +   + +51 +   + +52 + + +value +   + +0x0000000000000000000000004000000000000000000000000000000000000001 +   + +0x0000000000000000000000004000000000000000000000000000000000000002 + + +type: variable (bytes) +decoded data + +unallocated (12) + +IERC20 (20) + 0x4000000000000000000000000000000000000001 + +unallocated (12) + +IERC20 (20) + 0x4000000000000000000000000000000000000002 + + + +306:75->18 + + + + + +19 + +bool[2]: flags <<Array>> + +slot +   + +57 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +306:86->19 + + + + + +23 + +bool[2][3]: flags2x3 <<Array>> + +slot +   + +58 + +59 + +60 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +bool[2] (32) + +bool[2] (32) + +bool[2] (32) + + + +306:96->23 + + + + + +26 + +bool[3][2]: flags3x2 <<Array>> + +slot +   + +61 + +62 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[3] (32) + +bool[3] (32) + + + +306:105->26 + + + + + +29 + +bool[33][2]: flags33x2 <<Array>> + +slot +   + +63-64 + +65-67 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[33] (64) + +bool[33] (64) + + + +306:174->29 + + + + + +34 + +bool[2][33]: flags2x33 <<Array>> + +slot +   + +67 + +68 + +69-97 + +98 + +99 + +value +   + + + + + + + + + + + +type: variable (bytes) +decoded data + +bool[2] (32) + +bool[2] (32) + +---- (928) + +bool[2] (32) + +bool[2] (32) + + + +306:188->34 + + + + + +35 + +bool[]: flagsDyn <<Array>> +0x26700e13983fefbd9cf16da2ed70fa5c6798ac55062a4803121a869731e308d2 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000001000100010101 + + +type: variable (bytes) +decoded data + +unallocated (25) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + true + + + +306:190->35 + + + + + +37 + +bool[][]: flagsDynDyn <<Array>> +0x8ff97419363ffd7000167f130ef7168fbea05faf9251824ca5043f113cc6a7c7 + +offset +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000005 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 + + +type: variable (bytes) +decoded data + +bool[] (32) + 5 + +bool[] (32) + 3 + +bool[] (32) + 0 + +bool[] (32) + 2 + + + +306:193->37 + + + + + +40 + +bool[][][]: flagsDynDynDyn <<Array>> +0x46501879b8ca8525e8c2fd519e2fbfcfa2ebea26501294aa02cbfcfb12e94354 + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +bool[][] (32) + 1 + +bool[][] (32) + 1 + +bool[][] (32) + 1 + + + +306:197->40 + + + + + +42 + +bool[2][]: flags2xDyn <<Array>> +0x9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae + +offset +   + +0 + +1 + +2-3 + +4 + +5 + +value +   + + + + + + + + + + + +type: variable (bytes) +decoded data + +bool[2] (32) + +bool[2] (32) + +---- (64) + +bool[2] (32) + +bool[2] (32) + + + +306:201->42 + + + + + +45 + +bool[][2]: flagsDynx2 <<Array>> + +slot +   + +104 +   + +105 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000023 +   + +0x000000000000000000000000000000000000000000000000000000000000000A + + +type: variable (bytes) +decoded data + +bool[] (32) + 35 + +bool[] (32) + 10 + + + +306:206->45 + + + + + +50 + +bool[][16]: flagsDynx16 <<Array>> + +slot +   + +106 +   + +107 +   + +108-119 + +120 +   + +121 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +bool[] (32) + 4 + +bool[] (32) + 3 + +---- (384) + +bool[] (32) + 0 + +bool[] (32) + 1 + + + +306:216->50 + + + + + +55 + +bool[][32]: flagsDynx32 <<Array>> + +slot +   + +122 +   + +123 +   + +124-151 + +152 +   + +153 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool[] (32) + 2 + +bool[] (32) + 1 + +---- (896) + +bool[] (32) + 0 + +bool[] (32) + 0 + + + +306:226->55 + + + + + +57 + +bool[32][]: flags32xDyn <<Array>> +0x44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4 + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[32] (32) + +bool[32] (32) + + + +306:260->57 + + + + + +73 + +bool[][4][3]: flagsDynx4x3 <<Array>> + +slot +   + +155-158 + +159-163 + +163-167 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +bool[][4] (128) + +bool[][4] (128) + +bool[][4] (128) + + + +306:288->73 + + + + + +80 + +bool[33][2][2]: bool_33x2x2 <<Array>> + +slot +   + +167-170 + +171-175 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[33][2] (128) + +bool[33][2] (128) + + + +306:427->80 + + + + + +85 + +bytes30[2][6]: bytes30_2x6 <<Array>> + +slot +   + +175-176 + +177-179 + +179-183 + +183-185 + +185-187 + +value +   + + + + + + + + + + + +type: variable (bytes) +decoded data + +bytes30[2] (64) + +bytes30[2] (64) + +---- (160) + +bytes30[2] (64) + +bytes30[2] (64) + + + +306:441->85 + + + + + +88 + +bytes30[6][2]: bytes30_6x2 <<Array>> + +slot +   + +187-192 + +193-199 + +value +   + + + + + +type: variable (bytes) +decoded data + +bytes30[6] (192) + +bytes30[6] (192) + + + +306:454->88 + + + + + +89 + +bytes32[]: bytes32Dyn <<Array>> +0xc1d0558604082af4380f8af6e6df686f24c7438ca4f2a67c86a71ee7852601f9 + +offset +   + +0 +   + +1 + + +value +   + +0xFF00128251EC233D387A0AF31DB13F8318B61E40975C27476E1C1A02B79700FF +   + +0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + + +type: variable (bytes) +decoded data + +bytes32 (32) + 0xFF00128251EC233D387A0AF31DB13F8318B61E40975C27476E1C1A02B79700FF + +bytes32 (32) + 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + + + +306:456->89 + + + + + +90 + +uint32[FileConstant]: timestamps <<Array>> + +slot +   + +200 + + +value +   + +0x0000000000000000000000000019561D0014C1CD00133ABF0011B3AF00102CA1 + + +type: variable (bytes) +decoded data + +unallocated (12) + +uint32 (4) + 1,660,445   + +uint32 (4) + 1,360,333   + +uint32 (4) + 1,260,223   + +uint32 (4) + 1,160,111   + +uint32 (4) + 1,060,001 + + + +306:462->90 + + + + + +91 + +uint72[5]: five9ByteNumbers <<Array>> + +slot +   + +201 +   + +202 + + +value +   + +0x000000000000000000000000FFFF0000000000000000FF000000000000000001 +   + +0x0000000000000000000000000000FFFFFFFFFFFFFFFFFF0000000000FFFFFFFF + + +type: variable (bytes) +decoded data + +unallocated (5) + +uint72 (9) + 65,535   + +uint72 (9) + 255   + +uint72 (9) + 1 + +unallocated (14) + +uint72 (9) + 4,722,366,482,869,645,213,695   + +uint72 (9) + 4,294,967,295 + + + +306:468->91 + + + + + +92 + +uint72[22]: twentyTwo9ByteNumbers <<Array>> + +slot +   + +203 +   + +204 +   + +205-208 + +209 +   + +210 + + +value +   + +0x0000000000000000000000000002000000000000000001000000000000000000 +   + +0x0000000000000000000000000005000000000000000004000000000000000003 +   + + + +0x0000000000000000000000000014000000000000000013000000000000000012 +   + +0x0000000000000000000000000000000000000000000000000000000000000015 + + +type: variable (bytes) +decoded data + +unallocated (5) + +uint72 (9) + 2   + +uint72 (9) + 1   + +uint72 (9) + 0 + +unallocated (5) + +uint72 (9) + 5   + +uint72 (9) + 4   + +uint72 (9) + 3 + +---- (128) + +unallocated (5) + +uint72 (9) + 20   + +uint72 (9) + 19   + +uint72 (9) + 18 + +unallocated (23) + +uint72 (9) + 21 + + + +306:480->92 + + + + + +93 + +uint128[7]: sevenHalfNumbers <<Array>> + +slot +   + +211 +   + +212 +   + +213 +   + +214 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + +0x0000000000000000000000000000000500000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000006 + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +uint128 (16) + 5   + +uint128 (16) + 4 + +unallocated (16) + +uint128 (16) + 6 + + + +306:488->93 + + + + + +94 + +uint128[9]: nineHalfNumbers <<Array>> + +slot +   + +215 +   + +216 +   + +217 + +218 +   + +219 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000700000000000000000000000000000006 +   + +0x0000000000000000000000000000000000000000000000000000000000000008 + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (32) + +uint128 (16) + 7   + +uint128 (16) + 6 + +unallocated (16) + +uint128 (16) + 8 + + + +306:497->94 + + + + + +95 + +uint128[11]: elevenHalfNumbers <<Array>> + +slot +   + +220 +   + +221 +   + +222-223 + +224 +   + +225 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000900000000000000000000000000000008 +   + +0x000000000000000000000000000000000000000000000000000000000000000A + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (64) + +uint128 (16) + 9   + +uint128 (16) + 8 + +unallocated (16) + +uint128 (16) + 10 + + + +306:506->95 + + + + + +96 + +uint128[12]: twelveHalfNumbers <<Array>> + +slot +   + +226 +   + +227 +   + +228-229 + +230 +   + +231 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000900000000000000000000000000000008 +   + +0x0000000000000000000000000000000B0000000000000000000000000000000A + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (64) + +uint128 (16) + 9   + +uint128 (16) + 8 + +uint128 (16) + 11   + +uint128 (16) + 10 + + + +306:516->96 + + + + + +97 + +uint128[13]: thirteenHalfNumbers <<Array>> + +slot +   + +232 +   + +233 +   + +234-236 + +237 +   + +238 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000B0000000000000000000000000000000A +   + +0x000000000000000000000000000000000000000000000000000000000000000C + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (96) + +uint128 (16) + 11   + +uint128 (16) + 10 + +unallocated (16) + +uint128 (16) + 12 + + + +306:525->97 + + + + + +98 + +uint128[14]: fourteenHalfNumbers <<Array>> + +slot +   + +239 +   + +240 +   + +241-243 + +244 +   + +245 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000B0000000000000000000000000000000A +   + +0x0000000000000000000000000000000D0000000000000000000000000000000C + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (96) + +uint128 (16) + 11   + +uint128 (16) + 10 + +uint128 (16) + 13   + +uint128 (16) + 12 + + + +306:535->98 + + + + + +99 + +uint128[15]: fifteenHalfNumbers <<Array>> + +slot +   + +246 +   + +247 +   + +248-251 + +252 +   + +253 + + +value +   + +0x0000000000000000000000000000000100000000000000000000000000000000 +   + +0x0000000000000000000000000000000300000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000D0000000000000000000000000000000C +   + +0x000000000000000000000000000000000000000000000000000000000000000E + + +type: variable (bytes) +decoded data + +uint128 (16) + 1   + +uint128 (16) + 0 + +uint128 (16) + 3   + +uint128 (16) + 2 + +---- (128) + +uint128 (16) + 13   + +uint128 (16) + 12 + +unallocated (16) + +uint128 (16) + 14 + + + +306:544->99 + + + + + +100 + +uint256[50]: gap <<Array>> + +slot +   + +254 +   + +255 +   + +256-301 + +302 +   + +303 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + +uint256 (32) + 0 + +---- (1472) + +uint256 (32) + 0 + +uint256 (32) + 0 + + + +306:550->100 + + + + + +101 + +uint256[5]: fixedIntArray <<Array>> + +slot +   + +304 +   + +305 +   + +307 +   + +308 + + +value +   + +0x00000000000000000000000000000000000000000000000000000000000003E8 +   + +0x00000000000000000000000000000000000000000000000000000000000007D0 +   + +0x0000000000000000000000000000000000000000000000000000000000000FA0 +   + +0x0000000000000000000000000000000000000000000000000000000000001388 + + +type: variable (bytes) +decoded data + +uint256 (32) + 1,000 + +uint256 (32) + 2,000 + +uint256 (32) + 4,000 + +uint256 (32) + 5,000 + + + +306:555->101 + + + + + +104 + +TwoSlots[2]: twoSlots2x <<Array>> + +slot +   + +309-310 + +311-313 + +value +   + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + + + +306:562->104 + + + + + +121 + +TwoSlots[3][4]: twoSlots3x4 <<Array>> + +slot +   + +313-318 + +319-325 + +325-331 + +331-337 + +value +   + + + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + + + +306:603->121 + + + + + +137 + +TwoSlots[4][3]: twoSlots4x3 <<Array>> + +slot +   + +337-344 + +345-353 + +353-361 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots[4] (256) + +TwoSlots[4] (256) + +TwoSlots[4] (256) + + + +306:643->137 + + + + + +144 + +TwoSlots[][3]: twoSlotsDynx3 <<Array>> + +slot +   + +361 +   + +362 +   + +363 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + + + +306:656->144 + + + + + +149 + +TwoSlots[3][]: twoSlots3xDyn <<Array>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset +   + +0-5 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots[3] (192) + + + +306:667->149 + + + + + +152 + +TwoSlots[][]: twoSlotsDynxDyn <<Array>> +0x535f6990c27693b80d059530c371f53ea19bfc595ef3bd58348694aac9095300 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +TwoSlots[] (32) + 0 + + + +306:672->152 + + + + + +180 + +TwoSlots[][4][3]: twoSlotsDynx4x3 <<Array>> + +slot +   + +366-369 + +370-374 + +374-378 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots[][4] (128) + +TwoSlots[][4] (128) + +TwoSlots[][4] (128) + + + +306:724->180 + + + + + +198 + +TwoSlots[3][4][]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +0-23 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots[3][4] (768) + + + +306:766->198 + + + + + +200 + +Structs.SubTwoSlots[]: twoContractStruct <<Array>> +0x46985247fac81b794ada23f07cfef771057cf55c53a3d12054bcb524935683d7 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +Structs.SubTwoSlots (64) + + + +306:772->200 + + + + + +201 + +Status[]: dynamicStatuses <<Array>> +0x0f2aec2a8c6072690b3c86d5c08e34587f891268d22d8bdca6e66838c0f2f22d + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +Status (1) + +Status (1) + +Status (1) + Open + + + +306:775->201 + + + + + +202 + +Severity[4]: staticSeverities <<Array>> + +slot +   + +383 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000002010002 + + +type: variable (bytes) +decoded data + +unallocated (28) + +Severity (1) + High   + +Severity (1) + Medium   + +Severity (1) + Low   + +Severity (1) + High + + + +306:781->202 + + + + + +203 + +Structs.SubOneSlot <<Struct>> + +slot +   + +384 + + +value +   + +0x000000000000000000008701E2F2A5C287993345A840DB3B0845FBC70F5935A5 + + +type: variable (bytes) +decoded data + +unallocated (10) + +int8: SubOneSlot.count (1) + -121   + +bool: SubOneSlot.flag (1) + true   + +address: SubOneSlot.account (20) + 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5 + + + +306:785->203 + + + + + +204 + +Structs.OneSlot <<Struct>> + +slot +   + +386 + + +value +   + +0xFD00000000000000499602D2E2F2A5C287993345A840DB3B0845FBC70F5935A5 + + +type: variable (bytes) +decoded data + +uint8: OneSlot.count (1) + 253   + +uint88: OneSlot.sum (11) + 1,234,567,890   + +address: OneSlot.account (20) + 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5 + + + +306:790->204 + + + + + +205 + +Structs.SubTwoSlots <<Struct>> + +slot +   + +387 +   + +388 + + +value +   + +0x000000000000000000000000F4DDC5FF5ABA6E8739E5E056340827C573D191EC +   + +0x000000000000000000000101E63DFF84AA562DE11B28894F0391702B814F812D + + +type: variable (bytes) +decoded data + +unallocated (12) + +address: SubTwoSlots.account1 (20) + 0xF4dDc5FF5AbA6E8739E5E056340827c573d191Ec + +unallocated (10) + +bool: SubTwoSlots.flag2 (1) + true   + +bool: SubTwoSlots.flag1 (1) + true   + +address: SubTwoSlots.account2 (20) + 0xe63dfF84aa562dE11B28894f0391702b814f812D + + + +306:795->205 + + + + + +206 + +TwoSlots <<Struct>> + +slot +   + +389 +   + +390 + + +value +   + +0xAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 +   + +0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9 + +bytes32: hash2 (32) + 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + + + +306:798->206 + + + + + +208 + +FixedArray <<Struct>> + +slot +   + +391 +   + +392-393 + +394 + + +value +   + +0x000000000000000000000000000000000000000000000000000000000000FFFF +   + + + +0x000000000000000000000000000000000000000000000000000000000000FDE9 + + +type: variable (bytes) +decoded data + +unallocated (30) + +uint16: num1 (2) + 65,535 + +bytes30[2]: data (64) + +unallocated (30) + +uint16: num2 (2) + 65,001 + + + +306:804->208 + + + + + +210 + +FlagsStruct <<Struct>> + +slot +   + +395 +   + +396 + +397 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool: flag1 (1) + true + +bool[2]: flags (32) + +unallocated (31) + +bool: flag2 (1) + true + + + +306:810->210 + + + + + +211 + +uint256[]: numbers <<Array>> +0x77f3f3fc840536973a5d96a2bc039249e1d449fdef2adeb2ff7a19910de0ff24 + +offset +   + +0 +   + +1 +   + +2-7 + +8 +   + +9 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000009 +   + +0x000000000000000000000000000000000000000000000000000000000000000A + + +type: variable (bytes) +decoded data + +uint256 (32) + 1 + +uint256 (32) + 2 + +---- (192) + +uint256 (32) + 9 + +uint256 (32) + 10 + + + +306:813->211 + + + + + +212 + +uint256[]: empty <<Array>> +0xbd5c86c9fbaa47a598cf55ad1cee7a1d8f8450857cb10e0f122503b8c0e65243 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 0 + + + +306:815->212 + + + + + +213 + +uint56[]: sevenByteNumbers <<Array>> +0xfb4a0ff48df6de8777afc824e4cbf267beb8602db7c5a7456c954593226c1fd5 + +offset +   + +0 +   + +1 +   + +2 + +3 +   + +4 + + +value +   + +0x0000000000000000000004000000000000030000000000000200000000000001 +   + +0x0000000000000000000008000000000000070000000000000600000000000005 +   + + + +0x00000000000000000000100000000000000F0000000000000E0000000000000D +   + +0x0000000000000000000014000000000000130000000000001200000000000011 + + +type: variable (bytes) +decoded data + +unallocated (4) + +uint56 (7) + 4   + +uint56 (7) + 3   + +uint56 (7) + 2   + +uint56 (7) + 1 + +unallocated (4) + +uint56 (7) + 8   + +uint56 (7) + 7   + +uint56 (7) + 6   + +uint56 (7) + 5 + +---- (32) + +unallocated (4) + +uint56 (7) + 16   + +uint56 (7) + 15   + +uint56 (7) + 14   + +uint56 (7) + 13 + +unallocated (4) + +uint56 (7) + 20   + +uint56 (7) + 19   + +uint56 (7) + 18   + +uint56 (7) + 17 + + + +306:817->213 + + + + + +214 + +uint72[]: nineByteNumbers <<Array>> +0xfcfced99f9d921eebdc59aa6f7a664084bd564a3d2d54ebc1a5c057c99c67aba + +offset +   + +0 +   + +1 +   + +2-3 + +4 +   + +5 + + +value +   + +0x0000000000000000000000000003000000000000000002000000000000000001 +   + +0x0000000000000000000000000006000000000000000005000000000000000004 +   + + + +0x000000000000000000000000000F00000000000000000E00000000000000000D +   + +0x0000000000000000000000000000000000000000000000000000000000000010 + + +type: variable (bytes) +decoded data + +unallocated (5) + +uint72 (9) + 3   + +uint72 (9) + 2   + +uint72 (9) + 1 + +unallocated (5) + +uint72 (9) + 6   + +uint72 (9) + 5   + +uint72 (9) + 4 + +---- (64) + +unallocated (5) + +uint72 (9) + 15   + +uint72 (9) + 14   + +uint72 (9) + 13 + +unallocated (23) + +uint72 (9) + 16 + + + +306:819->214 + + + + + +215 + +uint64[]: dynamicInt64Array <<Array>> +0xfc8af01f449989052b52093a58fc9f42d0b11f0c6dd5dca0463dab62346ccc68 + +offset +   + +0 +   + +1 + + +value +   + +0x8AC7230489E8000000000000000000FE000000000000000100000000000007D0 +   + +0x00000000000000000000000000000100000000000000000200000000000000FE + + +type: variable (bytes) +decoded data + +uint64 (8) + 10,000,000,000,000,000,000   + +uint64 (8) + 254   + +uint64 (8) + 1   + +uint64 (8) + 2,000 + +unallocated (8) + +uint64 (8) + 256   + +uint64 (8) + 2   + +uint64 (8) + 254 + + + +306:821->215 + + + + + +216 + +uint128[]: dynamicInt128Array <<Array>> +0xa6f1ac7ad7b125ba5a5e1c96b00ad6914f90a503b1ac3d85a9dadbb4c639df92 + +offset +   + +0 +   + +1 + + +value +   + +0x96769950B50D88F413144480000000004B3B4CA85A86C47A098A224000000000 +   + +0x00000000000000000000000000000000E1B1E5F90F944D6E1C9E66C000000000 + + +type: variable (bytes) +decoded data + +uint128 (16) + 200,000,000,000,000,000,000,000,000,000,000,000,000   + +uint128 (16) + 100,000,000,000,000,000,000,000,000,000,000,000,000 + +unallocated (16) + +uint128 (16) + 300,000,000,000,000,000,000,000,000,000,000,000,000 + + + +306:823->216 + + + + + +217 + +uint136[]: dynamicInt136Array <<Array>> +0x54034dca961b61bc2a3147cc0c1986762915b42723ed64155364f17a2e296770 + +offset +   + +0 +   + +1 +   + +2 +   + +3 + + +value +   + +0x0000000000000000000000000000001D6329F1C35CA4BFABB9F5610000000000 +   + +0x00000000000000000000000000000005E0A1FD2712875988BECAAD0000000000 +   + +0x00000000000000000000000000000008D0F2FBBA9BCB064D1E30038000000000 +   + +0x0000000000000000000000000000000BC143FA4E250EB3117D955A0000000000 + + +type: variable (bytes) +decoded data + +unallocated (15) + +uint136 (17) + 10,000,000,000,000,000,000,000,000,000,000,000,000,000 + +unallocated (15) + +uint136 (17) + 2,000,000,000,000,000,000,000,000,000,000,000,000,000 + +unallocated (15) + +uint136 (17) + 3,000,000,000,000,000,000,000,000,000,000,000,000,000 + +unallocated (15) + +uint136 (17) + 4,000,000,000,000,000,000,000,000,000,000,000,000,000 + + + +306:825->217 + + + + + +218 + +uint256[]: dynamicInt256Array <<Array>> +0x828feda00a4b64eb35101b6df8f6c29717b1ea6bae5dd03d3ddada8de0a9e7cb + +offset +   + +0 +   + +1 +   + +3 +   + +4 + + +value +   + +0xDD15FE86AFFAD91249EF0EB713F39EBEAA987B6E6FD2A0000000000000000000 +   + +0x2C37994E23322B6A0EC96957D0CA52F2EEEB4BE2E32A20000000000000000000 +   + +0x586F329C466456D41D92D2AFA194A5E5DDD697C5C65440000000000000000000 +   + +0x6E8AFF4357FD6C8924F7875B89F9CF5F554C3DB737E950000000000000000000 + + +type: variable (bytes) +decoded data + +uint256 (32) + 100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 + +uint256 (32) + 20,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 + +uint256 (32) + 40,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 + +uint256 (32) + 50,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 + + + +306:827->218 + + + + + +220 + +uint256[][]: dynamicDynIntArray <<Array>> +0x3ea4d693734e62a1b4642df418cf4aae0e5ba336a2d6024b2d33585611a4e2eb + +offset +   + +0 +   + +1 +   + +2-9 + +10 +   + +11 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: variable (bytes) +decoded data + +uint256[] (32) + 2 + +uint256[] (32) + 3 + +---- (256) + +uint256[] (32) + 0 + +uint256[] (32) + 4 + + + +306:830->220 + + + + + +223 + +uint256[][][]: dynamicDynDynIntArray <<Array>> +0x3f539f465397ab387efa93a617e37005205c037386b5b474fcfac82f47f77f0d + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +uint256[][] (32) + 2 + +uint256[][] (32) + 2 + +uint256[][] (32) + 1 + + + +306:834->223 + + + + + +226 + +ContractLevelStruct2 <<Struct>> + +offset +   + +0-1 + +2-3 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct0: param1 (64) + +ContractLevelStruct$_1: param2 (64) + + + +306:845->226 + + + + + +229 + +ContractLevelStruct2 <<Struct>> + +offset +   + +0-1 + +2-3 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct0: param1 (64) + +ContractLevelStruct$_1: param2 (64) + + + +306:854->229 + + + + + +230 + +SubTwoSlots <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +unallocated (12) + +address: account1 (20) + +unallocated (10) + +bool: flag2 (1) + +bool: flag1 (1) + +address: account2 (20) + + + +306:859->230 + + + + + +244 + +DynamicStruct <<Struct>> + +slot +   + +415 +   + +416 +   + +417 + +418 +   + +419 +   + +420-421 + +422 +   + +423-424 + +425 +   + +426 +   + +427-428 + +429-432 + +433 +   + +434 + +435 + +value +   + +0x00000000000000000000009FF58F4FFB29FA2266AB25E75E2A8B350331165601 +   + +0x0000000000000000000000022260FAC5E5542A773AA44FBCFEDF7C193BC2C599 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x65786163746C7920333120636861727320736F2075736573203120736C6F743E +   + +0x000000000000000000000000000000000000000000000000000000000000006D +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + + + +type: variable (bytes) +decoded data + +unallocated (11) + +address: token (20) + 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656   + +bool: flag (1) + true + +unallocated (11) + +Severity: severity (1) + High   + +IERC20: asset (20) + 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + 2 + +address[]: dynamicAddressArray (32) + 3 + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + 3 + +int64[5]: staticIntArray (64) + +string: shortString (32) + "exactly 31 chars so uses 1 slot" + +string: longString (32) + 54 + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + 0 + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +306:914->244 + + + + + +259 + +DynamicStruct[]: dynDynamicStruct <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +0-20 + +value +   + + + +type: variable (bytes) +decoded data + +DynamicStruct (672) + + + +306:969->259 + + + + + +288 + +DynamicStruct[2]: staticDynamicStruct <<Array>> + +slot +   + +437-457 + +458-479 + +value +   + + + + + +type: variable (bytes) +decoded data + +DynamicStruct (672) + +DynamicStruct (672) + + + +306:1078->288 + + + + + +302 + +DynamicStruct <<Struct>> + +offset +   + +0 + +1 + +2 + +3 + +4 + +5-6 + +7 + +8-9 + +10 + +11 + +12-13 + +14-17 + +18 + +19 + +20 + +value +   + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +type: variable (bytes) +decoded data + +unallocated (11) + +address: token (20) + +bool: flag (1) + +unallocated (11) + +Severity: severity (1) + +IERC20: asset (20) + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + +address[]: dynamicAddressArray (32) + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + +int64[5]: staticIntArray (64) + +string: shortString (32) + +string: longString (32) + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +306:1132->302 + + + + + +303 + +IERC20[2]: interfaceFixedArray <<Array>> + +slot +   + +480 +   + +481 + + +value +   + +0x000000000000000000000000E2F2A5C287993345A840DB3B0845FBC70F5935A5 +   + +0x00000000000000000000000030647A72DC82D7FBB1123EA74716AB8A317EAC19 + + +type: variable (bytes) +decoded data + +unallocated (12) + +IERC20 (20) + 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5 + +unallocated (12) + +IERC20 (20) + 0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19 + + + +306:1135->303 + + + + + +304 + +IERC20[]: interfaceDynArray <<Array>> +0x4075a898d5491b608dbd073c96127d394f36fb783f61de61a7a8e6053f9f2d51 + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x000000000000000000000000E2F2A5C287993345A840DB3B0845FBC70F5935A5 +   + +0x00000000000000000000000030647A72DC82D7FBB1123EA74716AB8A317EAC19 +   + +0x00000000000000000000000078BEFCA7DE27D07DC6E71DA295CC2946681A6C7B + + +type: variable (bytes) +decoded data + +unallocated (12) + +IERC20 (20) + 0xe2f2a5C287993345a840Db3B0845fbC70f5935a5 + +unallocated (12) + +IERC20 (20) + 0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19 + +unallocated (12) + +IERC20 (20) + 0x78BefCa7de27d07DC6e71da295Cc2946681A6c7B + + + +306:1137->304 + + + + + +305 + +uint[]: $some_numbers <<Array>> +0x6ea6ba7afdda9dd98317feb84bb80d157ea581b23ca788496ba1c38c64dd8212 + +offset +   + +0 +   + +1 +   + +3 +   + +4 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: variable (bytes) +decoded data + +uint (32) + 0 + +uint (32) + 1 + +uint (32) + 3 + +uint (32) + 4 + + + +306:1158->305 + + + + + +307 + +string: grandParentName <<String>> +0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b + +offset +   + +0 +   + +1 + + +value +   + +0x4772616E6420706172656E74206E616D6520746861742074616B6573206D6F72 +   + +0x65207468616E206F6E6520736C6F740000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "Grand parent name that takes mor" + +string (15) + "e than one slot"   + +unallocated (17) + + + +306:8->307 + + + + + +308 + +string: exactly32 <<String>> +0xeff3ea65242f61d1ad52af7ccf5112db6c952c84cbec26b799baa6dd77508b36 + +offset +   + +0 + + +value +   + +0x3332206368617220736F2075736573206F6E652064796E616D696320736C6F74 + + +type: variable (bytes) +decoded data + +string (32) + "32 char so uses one dynamic slot" + + + +306:1143->308 + + + + + +309 + +string: long2 <<String>> +0x59296b16d186ff7184f7301d88db004c7d25ea4e59d9d375bb6c9e1abe56625a + +offset +   + +0 +   + +1 + + +value +   + +0x6D6F7265207468616E20333220627974657320736F2064617461206973207374 +   + +0x6F7265642064796E616D6963616C6C7920696E203220736C6F74730000000000 + + +type: variable (bytes) +decoded data + +string (32) + "more than 32 bytes so data is st" + +string (27) + "ored dynamically in 2 slots"   + +unallocated (5) + + + +306:1144->309 + + + + + +310 + +string: long3 <<String>> +0x1a335c11377aff09af404f74539b72b39d83951887cd31f7859edc934cbdc8bd + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x6D6F7265207468616E20736978747920666F7572202836342920627974657320 +   + +0x736F20646174612069732073746F7265642064796E616D6963616C6C7920696E +   + +0x20746872656520736C6F74730000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "more than sixty four (64) bytes " + +string (32) + "so data is stored dynamically in" + +string (12) + " three slots"   + +unallocated (20) + + + +306:1145->310 + + + + + +311 + +string: testString <<String>> +0xcd9c034cd8aa7c13ad4c5e447504094f99d8d01273b4f7728f61c400db4c5e58 + +offset +   + +0 +   + +1 + + +value +   + +0x546869732063616E206265207075626C69636C79206368616E67656420627920 +   + +0x616E796F6E650000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "This can be publicly changed by " + +string (6) + "anyone"   + +unallocated (26) + + + +306:1146->311 + + + + + +312 + +bytes: exactly32Bytes <<Bytes>> +0x28c338778cede5a6cb848edcdf4a94ca54d10795db56eecc9f3ddd2e035454b4 + +offset +   + +0 + + +value +   + +0x27F12ABFE35860A9A927B465BB3D4A9C23C8428174B83F278FE45ED7B4DA2662 + + +type: variable (bytes) +decoded data + +bytes (32) + 0x27F12ABFE35860A9A927B465BB3D4A9C23C8428174B83F278FE45ED7B4DA2662 + + + +306:1151->312 + + + + + +313 + +bytes: long3Bytes <<Bytes>> +0xc254b84a62dfff8c2ed231277b206a9d961be6dc8ce2acb422ed044e11d8d14f + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0xF34D3C319F536DEB74ED8F1F3205D9AEFEF7487C819E77D3351630820DBFF111 +   + +0x8CC7EE599E5D59FEE88C83157BD897847C5911DC7D317B3175E0B08519834997 +   + +0x3FFF000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes (32) + 0xF34D3C319F536DEB74ED8F1F3205D9AEFEF7487C819E77D3351630820DBFF111 + +bytes (32) + 0x8CC7EE599E5D59FEE88C83157BD897847C5911DC7D317B3175E0B08519834997 + +bytes (2) + 0x3FFF   + +unallocated (30) + + + +306:1152->313 + + + + + +8 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +36 +   + +37 + + +value +   + +0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1 +   + +0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xFfffFfFFFfFFFFfFFfFFFfFFFfFFfFFFfFfFfFf1 + +unallocated (12) + +address (20) + 0xfffFFFFFFFfFFFFfFFfFfFFffFfFfFffFFFFfFf2 + + + +9 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +38 +   + +39 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +10 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +40 +   + +41 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +11 + +address[N_COINS][3]: multiDimension <<Array>> + +slot +   + +36-37 + +38-40 + +40-42 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +address[N_COINS] (64) + +address[N_COINS] (64) + +address[N_COINS] (64) + + + +11:50->8 + + + + + +11:51->9 + + + + + +11:52->10 + + + + + +12 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +42 +   + +43 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +13 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +44 +   + +45 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +14 + +address[N_COINS]: multiDimension <<Array>> + +slot +   + +46 +   + +47 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +15 + +address[N_COINS][3]: multiDimension <<Array>> + +slot +   + +42-43 + +44-46 + +46-48 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +address[N_COINS] (64) + +address[N_COINS] (64) + +address[N_COINS] (64) + + + +15:61->12 + + + + + +15:62->13 + + + + + +15:63->14 + + + + + +16:57->11 + + + + + +16:58->15 + + + + + +20 + +bool[2]: flags2x3 <<Array>> + +slot +   + +58 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + true + + + +21 + +bool[2]: flags2x3 <<Array>> + +slot +   + +59 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + false + + + +22 + +bool[2]: flags2x3 <<Array>> + +slot +   + +60 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +23:89->20 + + + + + +23:90->21 + + + + + +23:91->22 + + + + + +24 + +bool[3]: flags3x2 <<Array>> + +slot +   + +61 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +25 + +bool[3]: flags3x2 <<Array>> + +slot +   + +62 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + false   + +bool (1) + true   + +bool (1) + false + + + +26:100->24 + + + + + +26:101->25 + + + + + +27 + +bool[33]: flags33x2 <<Array>> + +slot +   + +63 +   + +64 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000001000101 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true + +unallocated (31) + +bool (1) + true + + + +28 + +bool[33]: flags33x2 <<Array>> + +slot +   + +65 +   + +66 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000001010001 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + +unallocated (31) + +bool (1) + false + + + +29:139->27 + + + + + +29:140->28 + + + + + +30 + +bool[2]: flags2x33 <<Array>> + +slot +   + +67 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +31 + +bool[2]: flags2x33 <<Array>> + +slot +   + +68 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + true + + + +32 + +bool[2]: flags2x33 <<Array>> + +slot +   + +98 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + false + + + +33 + +bool[2]: flags2x33 <<Array>> + +slot +   + +99 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + false   + +bool (1) + false + + + +34:177->30 + + + + + +34:178->31 + + + + + +34:180->32 + + + + + +34:181->33 + + + + + +36 + +bool[]: flagsDynDyn <<Array>> +0x0585cf9928072cd8fc6111506770bc15ecb39777d1a0b80d75c571238837966a + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000101000101 + + +type: variable (bytes) +decoded data + +unallocated (27) + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true + + + +37:192->36 + + + + + +38 + +bool[]: flagsDynDynDyn <<Array>> +0x7b6f38283dc0068171ba6f755e0903397b355ef72c81aab8ec95dc7f1ae3c8f8 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000001000101 + + +type: variable (bytes) +decoded data + +unallocated (25) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true + + + +39 + +bool[][]: flagsDynDynDyn <<Array>> +0x0023f9f5bc869f8a01704930a5f32546cd410a1b423c29d8c67c83157f056d0d + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000004 + + +type: variable (bytes) +decoded data + +bool[] (32) + 4 + + + +39:195->38 + + + + + +40:196->39 + + + + + +41 + +bool[2]: flags2xDyn <<Array>> +0x9787eeb91fe3101235e4a76063c7023ecb40f923f97916639c598592fa30d6ae + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +42:200->41 + + + + + +43 + +bool[]: flagsDynx2 <<Array>> +0xa2153420d844928b4421650203c77babc8b33d7f2e7b450e2966db0c22097753 + +offset +   + +0 +   + +1 + + +value +   + +0x0101000001010001010000010001000100000100010100000101000101000001 +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +44 + +bool[]: flagsDynx2 <<Array>> +0x7fb4302e8e91f9110a6554c2c0a24601252c2a42c2220ca988efcfe399914308 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000001000101010100010001 + + +type: variable (bytes) +decoded data + +unallocated (22) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true   + +bool (1) + true   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +45:203->43 + + + + + +45:204->44 + + + + + +46 + +bool[]: flagsDynx16 <<Array>> +0x116fea137db6e131133e7f2bab296045d8f41cc5607279db17b218cab0929a51 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000001000101 + + +type: variable (bytes) +decoded data + +unallocated (28) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true   + +bool (1) + true + + + +47 + +bool[]: flagsDynx16 <<Array>> +0xbd43cb8ece8cd1863bcd6082d65c5b0d25665b1ce17980f0da43c0ed545f98b4 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +48 + +bool[]: flagsDynx16 <<Array>> +0x8dc6fb69531d98d70dc0420e638d2dfd04e09e1ec783ede9aac77da9c5a0dac4 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +49 + +bool[]: flagsDynx16 <<Array>> +0x957bbdc7fad0dec56e7c96af4a3ab63aa9daf934a52ffce891945b7fb622d791 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + true + + + +50:208->46 + + + + + +50:209->47 + + + + + +50:211->48 + + + + + +50:212->49 + + + + + +51 + +bool[]: flagsDynx32 <<Array>> +0xf0440771a29e57e18c66727944770b82cc77924aef333c927ce6bdd2cdb3ae03 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +52 + +bool[]: flagsDynx32 <<Array>> +0x5569044719a1ec3b04d0afa9e7a5310c7c0473331d13dc9fafe143b2c4e8148a + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + true + + + +53 + +bool[]: flagsDynx32 <<Array>> +0x2237a976fa961f5921fd19f2b03c925c725d77b20ce8f790c19709c03de4d814 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +54 + +bool[]: flagsDynx32 <<Array>> +0x72a152ddfb8e864297c917af52ea6c1c68aead0fee1a62673fcc7e0c94979d00 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +55:218->51 + + + + + +55:219->52 + + + + + +55:221->53 + + + + + +55:222->54 + + + + + +56 + +bool[32]: flags32xDyn <<Array>> +0x44da158ba27f9252712a74ff6a55c5d531f69609f1f6e7f17c4443a8e2089be4 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +57:259->56 + + + + + +58 + +bool[]: flagsDynx4x3 <<Array>> +0xbba9db4cdbea0a37c207bbb83e20f828cd4441c49891101dc94fd20dc8efc349 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +59 + +bool[]: flagsDynx4x3 <<Array>> +0xaf85b9071dfafeac1409d3f1d19bafc9bc7c37974cde8df0ee6168f0086e539c + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + false   + +bool (1) + true   + +bool (1) + false + + + +60 + +bool[]: flagsDynx4x3 <<Array>> +0xd26e832454299e9fabb89e0e5fffdc046d4e14431bc1bf607ffb2e8a1ddecf7b + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010000 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + false + + + +61 + +bool[]: flagsDynx4x3 <<Array>> +0xcfe2a20ff701a1f3e14f63bd70d6c6bc6fba8172ec6d5a505cdab3927c0a9de6 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010101 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + true   + +bool (1) + true + + + +62 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot +   + +155 +   + +156 +   + +157 +   + +158 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +bool[] (32) + 3 + +bool[] (32) + 3 + +bool[] (32) + 3 + +bool[] (32) + 3 + + + +62:262->58 + + + + + +62:263->59 + + + + + +62:264->60 + + + + + +62:265->61 + + + + + +63 + +bool[]: flagsDynx4x3 <<Array>> +0x0bc14066c33013fe88f66e314e4cf150b0b2d4d6451a1a51dbbd1c27cd11de28 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + false   + +bool (1) + false   + +bool (1) + false + + + +64 + +bool[]: flagsDynx4x3 <<Array>> +0x78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e88 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010101 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + true   + +bool (1) + true + + + +65 + +bool[]: flagsDynx4x3 <<Array>> +0xaadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f649878 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + false   + +bool (1) + true   + +bool (1) + false + + + +66 + +bool[]: flagsDynx4x3 <<Array>> +0xaaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +bool (1) + true   + +bool (1) + false   + +bool (1) + true + + + +67 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot +   + +159 +   + +160 +   + +161 +   + +162 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +bool[] (32) + 3 + +bool[] (32) + 3 + +bool[] (32) + 3 + +bool[] (32) + 3 + + + +67:273->63 + + + + + +67:274->64 + + + + + +67:275->65 + + + + + +67:276->66 + + + + + +68 + +bool[]: flagsDynx4x3 <<Array>> +0x60859188cffe297f44dde29f2d2865634621f26215049caeb304ccba566a8b17 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +69 + +bool[]: flagsDynx4x3 <<Array>> +0xe434dc35da084cf8d7e8186688ea2dacb53db7003d427af3abf351bd9d0a4e8d + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +70 + +bool[]: flagsDynx4x3 <<Array>> +0xb29a2b3b6f2ff1b765777a231725941da5072cc4fcc30ac4a2ce09706e8ddeff + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +71 + +bool[]: flagsDynx4x3 <<Array>> +0x2da56674729343acc9933752c8c469a244252915242eb6d4c02d11ddd69164a1 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +bool (1) + false + + + +72 + +bool[][4]: flagsDynx4x3 <<Array>> + +slot +   + +163 +   + +164 +   + +165 +   + +166 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool[] (32) + 0 + +bool[] (32) + 0 + +bool[] (32) + 0 + +bool[] (32) + 0 + + + +72:281->68 + + + + + +72:282->69 + + + + + +72:283->70 + + + + + +72:284->71 + + + + + +73:269->62 + + + + + +73:270->67 + + + + + +73:271->72 + + + + + +74 + +bool[33]: bool_33x2x2 <<Array>> + +slot +   + +167 +   + +168 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true + +unallocated (31) + +bool (1) + false + + + +75 + +bool[33]: bool_33x2x2 <<Array>> + +slot +   + +169 +   + +170 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false + +unallocated (31) + +bool (1) + false + + + +76 + +bool[33][2]: bool_33x2x2 <<Array>> + +slot +   + +167-168 + +169-171 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[33] (64) + +bool[33] (64) + + + +76:322->74 + + + + + +76:323->75 + + + + + +77 + +bool[33]: bool_33x2x2 <<Array>> + +slot +   + +171 +   + +172 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false + +unallocated (31) + +bool (1) + false + + + +78 + +bool[33]: bool_33x2x2 <<Array>> + +slot +   + +173 +   + +174 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000100 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + false   + +bool (1) + true   + +bool (1) + false + +unallocated (31) + +bool (1) + false + + + +79 + +bool[33][2]: bool_33x2x2 <<Array>> + +slot +   + +171-172 + +173-175 + +value +   + + + + + +type: variable (bytes) +decoded data + +bool[33] (64) + +bool[33] (64) + + + +79:392->77 + + + + + +79:393->78 + + + + + +80:357->76 + + + + + +80:358->79 + + + + + +81 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot +   + +175 +   + +176 + + +value +   + +0x0000BBB000000000000000000000000000000000000000000000000000000BBB +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0xBBB000000000000000000000000000000000000000000000000000000BBB + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +82 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot +   + +177 +   + +178 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +83 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot +   + +183 +   + +184 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +84 + +bytes30[2]: bytes30_2x6 <<Array>> + +slot +   + +185 +   + +186 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +85:430->81 + + + + + +85:431->82 + + + + + +85:433->83 + + + + + +85:434->84 + + + + + +86 + +bytes30[6]: bytes30_6x2 <<Array>> + +slot +   + +187 +   + +188 +   + +189-190 + +191 +   + +192 + + +value +   + +0x0000CCC000000000000000000000000000000000000000000000000000000CCC +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0xCCC000000000000000000000000000000000000000000000000000000CCC + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +---- (64) + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +87 + +bytes30[6]: bytes30_6x2 <<Array>> + +slot +   + +193 +   + +194 +   + +195-196 + +197 +   + +198 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +---- (64) + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + +unallocated (2) + +bytes30 (30) + 0x000000000000000000000000000000000000000000000000000000000000 + + + +88:447->86 + + + + + +88:448->87 + + + + + +102 + +TwoSlots <<Struct>> + +slot +   + +309 +   + +310 + + +value +   + +0xFFFF00000F000000000000000000000000000000000000000000000000FFFFFF +   + +0xFFFFF0000FF0000000000000000000000000000000000000000000000FFFFFFF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xFFFF00000F000000000000000000000000000000000000000000000000FFFFFF + +bytes32: hash2 (32) + 0xFFFFF0000FF0000000000000000000000000000000000000000000000FFFFFFF + + + +103 + +TwoSlots <<Struct>> + +slot +   + +311 +   + +312 + + +value +   + +0xFF0000000F0000000000000000000000000000000000000000000000000000FF +   + +0xFFF000000FF00000000000000000000000000000000000000000000000000FFF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xFF0000000F0000000000000000000000000000000000000000000000000000FF + +bytes32: hash2 (32) + 0xFFF000000FF00000000000000000000000000000000000000000000000000FFF + + + +104:558->102 + + + + + +104:559->103 + + + + + +105 + +TwoSlots <<Struct>> + +slot +   + +313 +   + +314 + + +value +   + +0xFF000000000000000000000000000000000000000000000000000000000000FF +   + +0xFFF000000F000000000000000000000000000000000000000000000000000FFF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xFF000000000000000000000000000000000000000000000000000000000000FF + +bytes32: hash2 (32) + 0xFFF000000F000000000000000000000000000000000000000000000000000FFF + + + +106 + +TwoSlots <<Struct>> + +slot +   + +315 +   + +316 + + +value +   + +0xAF0000000000000000000000000000000000000000000000000000000000000F +   + +0xAF0000000F0000000000000000000000000000000000000000000000000000FF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xAF0000000000000000000000000000000000000000000000000000000000000F + +bytes32: hash2 (32) + 0xAF0000000F0000000000000000000000000000000000000000000000000000FF + + + +107 + +TwoSlots <<Struct>> + +slot +   + +317 +   + +318 + + +value +   + +0xABC0000000000000000000000000000000000000000000000000000000000321 +   + +0xABC000000F000000000000000000000000000000000000000000000000000456 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xABC0000000000000000000000000000000000000000000000000000000000321 + +bytes32: hash2 (32) + 0xABC000000F000000000000000000000000000000000000000000000000000456 + + + +108 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot +   + +313-314 + +315-317 + +317-319 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +108:565->105 + + + + + +108:566->106 + + + + + +108:567->107 + + + + + +109 + +TwoSlots <<Struct>> + +slot +   + +319 +   + +320 + + +value +   + +0xDEF0000000000000000000000000F000000000000000000000000000000000F1 +   + +0xDEF000000F000000000000000000F00000000000000000000000000000000FF1 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xDEF0000000000000000000000000F000000000000000000000000000000000F1 + +bytes32: hash2 (32) + 0xDEF000000F000000000000000000F00000000000000000000000000000000FF1 + + + +110 + +TwoSlots <<Struct>> + +slot +   + +321 +   + +322 + + +value +   + +0x1000000000000000000000000000F00000000000000000000000000000000001 +   + +0x300000000F000000000000000000F00000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x1000000000000000000000000000F00000000000000000000000000000000001 + +bytes32: hash2 (32) + 0x300000000F000000000000000000F00000000000000000000000000000000003 + + + +111 + +TwoSlots <<Struct>> + +slot +   + +323 +   + +324 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +112 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot +   + +319-320 + +321-323 + +323-325 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +112:578->109 + + + + + +112:579->110 + + + + + +112:580->111 + + + + + +113 + +TwoSlots <<Struct>> + +slot +   + +325 +   + +326 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +114 + +TwoSlots <<Struct>> + +slot +   + +327 +   + +328 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +115 + +TwoSlots <<Struct>> + +slot +   + +329 +   + +330 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +116 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot +   + +325-326 + +327-329 + +329-331 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +116:587->113 + + + + + +116:588->114 + + + + + +116:589->115 + + + + + +117 + +TwoSlots <<Struct>> + +slot +   + +331 +   + +332 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +118 + +TwoSlots <<Struct>> + +slot +   + +333 +   + +334 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +119 + +TwoSlots <<Struct>> + +slot +   + +335 +   + +336 + + +value +   + +0xB00000000000000000000000000000000000000000000000000000F00000000D +   + +0xF00000000F00000000000000000000000000000000000000000000F00000000F + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xB00000000000000000000000000000000000000000000000000000F00000000D + +bytes32: hash2 (32) + 0xF00000000F00000000000000000000000000000000000000000000F00000000F + + + +120 + +TwoSlots[3]: twoSlots3x4 <<Array>> + +slot +   + +331-332 + +333-335 + +335-337 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +120:596->117 + + + + + +120:597->118 + + + + + +120:598->119 + + + + + +121:572->108 + + + + + +121:573->112 + + + + + +121:574->116 + + + + + +121:575->120 + + + + + +122 + +TwoSlots <<Struct>> + +slot +   + +337 +   + +338 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +123 + +TwoSlots <<Struct>> + +slot +   + +339 +   + +340 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +124 + +TwoSlots <<Struct>> + +slot +   + +341 +   + +342 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +125 + +TwoSlots <<Struct>> + +slot +   + +343 +   + +344 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +126 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot +   + +337-338 + +339-341 + +341-343 + +343-345 + +value +   + + + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +126:606->122 + + + + + +126:607->123 + + + + + +126:608->124 + + + + + +126:609->125 + + + + + +127 + +TwoSlots <<Struct>> + +slot +   + +345 +   + +346 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +128 + +TwoSlots <<Struct>> + +slot +   + +347 +   + +348 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +129 + +TwoSlots <<Struct>> + +slot +   + +349 +   + +350 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +130 + +TwoSlots <<Struct>> + +slot +   + +351 +   + +352 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +131 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot +   + +345-346 + +347-349 + +349-351 + +351-353 + +value +   + + + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +131:621->127 + + + + + +131:622->128 + + + + + +131:623->129 + + + + + +131:624->130 + + + + + +132 + +TwoSlots <<Struct>> + +slot +   + +353 +   + +354 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +133 + +TwoSlots <<Struct>> + +slot +   + +355 +   + +356 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +134 + +TwoSlots <<Struct>> + +slot +   + +357 +   + +358 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +135 + +TwoSlots <<Struct>> + +slot +   + +359 +   + +360 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +136 + +TwoSlots[4]: twoSlots4x3 <<Array>> + +slot +   + +353-354 + +355-357 + +357-359 + +359-361 + +value +   + + + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +136:633->132 + + + + + +136:634->133 + + + + + +136:635->134 + + + + + +136:636->135 + + + + + +137:616->126 + + + + + +137:617->131 + + + + + +137:618->136 + + + + + +138 + +TwoSlots <<Struct>> +0xbd88e1fd0aa5f3038c0cd2a68644e2d11f4e4e7e3693adba414318f2127ca426 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +139 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0xbd88e1fd0aa5f3038c0cd2a68644e2d11f4e4e7e3693adba414318f2127ca426 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +139:646->138 + + + + + +140 + +TwoSlots <<Struct>> +0x17da1ae71935bc1a620f4cc216c63f7b5576c3970bae66f4178d3166b9912544 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +141 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0x17da1ae71935bc1a620f4cc216c63f7b5576c3970bae66f4178d3166b9912544 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +141:652->140 + + + + + +142 + +TwoSlots <<Struct>> +0x45c4c4b2842a4a2a717cf0ddf6c6d1dee52b7fd8d9da97eb519765c7a27020f4 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +143 + +TwoSlots[]: twoSlotsDynx3 <<Array>> +0x45c4c4b2842a4a2a717cf0ddf6c6d1dee52b7fd8d9da97eb519765c7a27020f4 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +143:655->142 + + + + + +144:647->139 + + + + + +144:648->141 + + + + + +144:649->143 + + + + + +145 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset +   + +0 +   + +1 + + +value +   + +0xF00000000000000000000000000000000000000000000000000000000000000F +   + +0xF0000000F00000000000000000000000000000000000000000000000000000FF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xF00000000000000000000000000000000000000000000000000000000000000F + +bytes32: hash2 (32) + 0xF0000000F00000000000000000000000000000000000000000000000000000FF + + + +146 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset +   + +2 +   + +3 + + +value +   + +0xFF00000000000000000F0000000000000000000000000000000000000000000F +   + +0xFF000000F0000000000F000000000000000000000000000000000000000000FF + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0xFF00000000000000000F0000000000000000000000000000000000000000000F + +bytes32: hash2 (32) + 0xFF000000F0000000000F000000000000000000000000000000000000000000FF + + + +147 + +TwoSlots <<Struct>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset +   + +4 +   + +5 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +148 + +TwoSlots[3]: twoSlots3xDyn <<Array>> +0xe5af487827b121aa2456a019311df153887da6c98285803eea72252e4f09489b + +offset +   + +0-1 + +2-4 + +4-6 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +148:659->145 + + + + + +148:660->146 + + + + + +148:661->147 + + + + + +149:666->148 + + + + + +150 + +TwoSlots <<Struct>> +0xce4693280460682fc8eed876a7db2826b7eb557ee2a99bbdf3a56792e741fb3a + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +151 + +TwoSlots[]: twoSlotsDynxDyn <<Array>> +0xce4693280460682fc8eed876a7db2826b7eb557ee2a99bbdf3a56792e741fb3a + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +151:670->150 + + + + + +152:671->151 + + + + + +153 + +TwoSlots <<Struct>> +0xf82fbbf6ecba5c6bca8ffdb165c351ca78f1011f756f9b1202212482fd7d8313 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +154 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xf82fbbf6ecba5c6bca8ffdb165c351ca78f1011f756f9b1202212482fd7d8313 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +154:675->153 + + + + + +155 + +TwoSlots <<Struct>> +0x47d2f800c57de5c44dff408e225b320f1ea140ca87907f0726fd6049ee5595d7 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +156 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x47d2f800c57de5c44dff408e225b320f1ea140ca87907f0726fd6049ee5595d7 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +156:682->155 + + + + + +157 + +TwoSlots <<Struct>> +0x88e1afb22f13be7fec203799fd189c3b9471c5b75e2b22a6d5efec11c7f39255 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +158 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x88e1afb22f13be7fec203799fd189c3b9471c5b75e2b22a6d5efec11c7f39255 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +158:685->157 + + + + + +159 + +TwoSlots <<Struct>> +0x42b680591beb20d1de0d4ca368d3f10acc6db07bc74b68a9681655fb351b9039 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +160 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x42b680591beb20d1de0d4ca368d3f10acc6db07bc74b68a9681655fb351b9039 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +160:688->159 + + + + + +161 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot +   + +366 +   + +367 +   + +368 +   + +369 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + + + +161:676->154 + + + + + +161:677->156 + + + + + +161:678->158 + + + + + +161:679->160 + + + + + +162 + +TwoSlots <<Struct>> +0xed95d52ae313e6be6d8c8f9945923a6497c37269ba35d5e3535b758e47227cfc + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +163 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xed95d52ae313e6be6d8c8f9945923a6497c37269ba35d5e3535b758e47227cfc + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +163:694->162 + + + + + +164 + +TwoSlots <<Struct>> +0x56c806aace937fe95f08e7525d5e3e3892fb531816a27802f66a5e4a06502bfc + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +165 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x56c806aace937fe95f08e7525d5e3e3892fb531816a27802f66a5e4a06502bfc + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +165:701->164 + + + + + +166 + +TwoSlots <<Struct>> +0x19628ef4f1e52435a91694e0b17e2591e7982b96f1883cadfe58900fe74230c4 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +167 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x19628ef4f1e52435a91694e0b17e2591e7982b96f1883cadfe58900fe74230c4 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +167:704->166 + + + + + +168 + +TwoSlots <<Struct>> +0x94b016e6b5eafb28c9d47c3523378ec087cc0ead8b7eff2840605504c1f1ac6f + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +169 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x94b016e6b5eafb28c9d47c3523378ec087cc0ead8b7eff2840605504c1f1ac6f + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +169:707->168 + + + + + +170 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot +   + +370 +   + +371 +   + +372 +   + +373 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + + + +170:695->163 + + + + + +170:696->165 + + + + + +170:697->167 + + + + + +170:698->169 + + + + + +171 + +TwoSlots <<Struct>> +0xd8e59d83028feae5da415867a4f0e421f18a256acfabb8749b35464f6997ee83 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +172 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xd8e59d83028feae5da415867a4f0e421f18a256acfabb8749b35464f6997ee83 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +172:710->171 + + + + + +173 + +TwoSlots <<Struct>> +0xf582c8372b7074a2b8ed06ba0ff712db79e8bd5ed68aed830cac17f54eeda510 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +174 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0xf582c8372b7074a2b8ed06ba0ff712db79e8bd5ed68aed830cac17f54eeda510 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +174:717->173 + + + + + +175 + +TwoSlots <<Struct>> +0x34be40e60503fe71e236a0cea8e2a63f076cacc21dad7950c8c1bd09a373b91b + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +176 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x34be40e60503fe71e236a0cea8e2a63f076cacc21dad7950c8c1bd09a373b91b + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +176:720->175 + + + + + +177 + +TwoSlots <<Struct>> +0x417650d1fcdb071a1ee8e15f023e1bf12032a7a2f58523e1cc7c932f73103e43 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +178 + +TwoSlots[]: twoSlotsDynx4x3 <<Array>> +0x417650d1fcdb071a1ee8e15f023e1bf12032a7a2f58523e1cc7c932f73103e43 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + + + +178:723->177 + + + + + +179 + +TwoSlots[][4]: twoSlotsDynx4x3 <<Array>> + +slot +   + +374 +   + +375 +   + +376 +   + +377 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + +TwoSlots[] (32) + 0 + + + +179:711->172 + + + + + +179:712->174 + + + + + +179:713->176 + + + + + +179:714->178 + + + + + +180:689->161 + + + + + +180:690->170 + + + + + +180:691->179 + + + + + +181 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +182 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +2 +   + +3 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +183 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +4 +   + +5 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +184 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +0-1 + +2-4 + +4-6 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +184:727->181 + + + + + +184:728->182 + + + + + +184:729->183 + + + + + +185 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +6 +   + +7 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +186 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +8 +   + +9 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +187 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +10 +   + +11 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +188 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +6-7 + +8-10 + +10-12 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +188:740->185 + + + + + +188:741->186 + + + + + +188:742->187 + + + + + +189 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +12 +   + +13 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +190 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +14 +   + +15 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +191 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +16 +   + +17 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +192 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +12-13 + +14-16 + +16-18 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +192:749->189 + + + + + +192:750->190 + + + + + +192:751->191 + + + + + +193 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +18 +   + +19 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +194 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +20 +   + +21 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +195 + +TwoSlots <<Struct>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +22 +   + +23 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +bytes32: hash1 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + +bytes32: hash2 (32) + 0x0000000000000000000000000000000000000000000000000000000000000000 + + + +196 + +TwoSlots[3]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +18-19 + +20-22 + +22-24 + +value +   + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots (64) + +TwoSlots (64) + +TwoSlots (64) + + + +196:758->193 + + + + + +196:759->194 + + + + + +196:760->195 + + + + + +197 + +TwoSlots[3][4]: twoSlotsDynx3x4xDyn <<Array>> +0x0fbbe056a1e0b26d5079e00c6ba6930cb2b3fc66bf0ee8b956d6e28e76bd6086 + +offset +   + +0-5 + +6-12 + +12-18 + +18-24 + +value +   + + + + + + + + + +type: variable (bytes) +decoded data + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + +TwoSlots[3] (192) + + + +197:734->184 + + + + + +197:735->188 + + + + + +197:736->192 + + + + + +197:737->196 + + + + + +198:765->197 + + + + + +199 + +Structs.SubTwoSlots <<Struct>> +0x46985247fac81b794ada23f07cfef771057cf55c53a3d12054bcb524935683d7 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address: SubTwoSlots.account1 (20) + 0x0000000000000000000000000000000000000000 + +unallocated (10) + +bool: SubTwoSlots.flag2 (1) + false   + +bool: SubTwoSlots.flag1 (1) + false   + +address: SubTwoSlots.account2 (20) + 0x0000000000000000000000000000000000000000 + + + +200:771->199 + + + + + +207 + +bytes30[2]: data <<Array>> + +slot +   + +392 +   + +393 + + +value +   + +0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +   + +0x0000FFF000000F00000000000000000000000000000000000000000000000FFF + + +type: variable (bytes) +decoded data + +unallocated (2) + +bytes30 (30) + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + +unallocated (2) + +bytes30 (30) + 0xFFF000000F00000000000000000000000000000000000000000000000FFF + + + +208:802->207 + + + + + +209 + +bool[2]: flags <<Array>> + +slot +   + +396 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +bool (1) + true   + +bool (1) + true + + + +210:808->209 + + + + + +219 + +uint256[]: dynamicDynIntArray <<Array>> +0xc1491625e5de6cf60f57d34b39f9a8be7328b8e366bf82d25c540486afdf7737 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000002B67 +   + +0x0000000000000000000000000000000000000000000000000000000000002B72 + + +type: variable (bytes) +decoded data + +uint256 (32) + 11,111 + +uint256 (32) + 11,122 + + + +220:829->219 + + + + + +221 + +uint256[]: dynamicDynDynIntArray <<Array>> +0x530a3038a6150618b098d94aeb6f04ebdc7e5215f3ec24b33f6934ea0f1a3d83 + +offset +   + +0 +   + +1 +   + +1 + + +value +   + +0x000000000000000000000000000000000000000000000000000000000001B207 +   + +0x000000000000000000000000000000000000000000000000000000000001B212 +   + +0x000000000000000000000000000000000000000000000000000000000001B212 + + +type: variable (bytes) +decoded data + +uint256 (32) + 111,111 + +uint256 (32) + 111,122   + +uint256 (32) + 111,122 + +uint256 (32) + 111,122   + +uint256 (32) + 111,122 + + + +222 + +uint256[][]: dynamicDynDynIntArray <<Array>> +0xb54252cb0985c00ba4e5a95901144db6f2dd840128a8a8dcb2c6f67f98081bb5 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000003 + + +type: variable (bytes) +decoded data + +uint256[] (32) + 2 + +uint256[] (32) + 3 + + + +222:832->221 + + + + + +223:833->222 + + + + + +224 + +ContractLevelStruct0 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (31) + +bool: param2 (1) + + + +225 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +226:839->224 + + + + + +226:844->225 + + + + + +227 + +ContractLevelStruct0 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (31) + +bool: param2 (1) + + + +228 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +229:848->227 + + + + + +229:853->228 + + + + + +231 + +Severity[3]: staticSeverities <<Array>> + +slot +   + +417 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000010002 + + +type: variable (bytes) +decoded data + +unallocated (29) + +Severity (1) + Medium   + +Severity (1) + Low   + +Severity (1) + High + + + +232 + +Severity[]: dynamicSeverities <<Array>> +0x16f51b7b770dc888307b215a468396cad0bf985bd7e546f15769c8fd0d2b9404 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000102 + + +type: variable (bytes) +decoded data + +unallocated (30) + +Severity (1) + +Severity (1) + High + + + +233 + +address[]: dynamicAddressArray <<Array>> +0x9956c0f14fc2ed1836f96a0d9c70b58f1406f12ef284cbc764439ac2a31089c0 + +offset +   + +0 +   + +1 +   + +2 + + +value +   + +0x000000000000000000000000EB4C2781E4EBA804CE9A9803C67D0893436BB27D +   + +0x000000000000000000000000FE18BE6B3BD88A2D2A7F928D00292E7A9963CFC6 +   + +0x000000000000000000000000DBDB4D16EDA451D0503B854CF79D55697F90C8DF + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D + +unallocated (12) + +address (20) + 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6 + +unallocated (12) + +address (20) + 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF + + + +234 + +address[2]: staticAddressArray <<Array>> + +slot +   + +420 +   + +421 + + +value +   + +0x000000000000000000000000853D955ACEF822DB058EB8505911ED77F175B99E +   + +0x000000000000000000000000104592A158490A9228070E0A8E5343B499E125D0 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x853d955aCEf822Db058eb8505911ED77F175b99e + +unallocated (12) + +address (20) + 0x104592a158490a9228070E0A8e5343B499e125D0 + + + +235 + +int64[]: dynamicIntArray <<Array>> +0xc215118b47753a1fd554d2ebe7dfcd1e01570cadae61797613b4a12ccec41217 + +offset +   + +0 + + +value +   + +0x000000000000000000000000000003FFFFFFFFFFFFFFFFFF0000000000000001 + + +type: variable (bytes) +decoded data + +unallocated (8) + +int64 (8) + 1,023   + +int64 (8) + -1   + +int64 (8) + 1 + + + +236 + +int64[5]: staticIntArray <<Array>> + +slot +   + +423 +   + +424 + + +value +   + +0x0000000000000004000000000000000300000000000000020000000000000001 +   + +0x0000000000000000000000000000000000000000000000000000000000000005 + + +type: variable (bytes) +decoded data + +int64 (8) + 4   + +int64 (8) + 3   + +int64 (8) + 2   + +int64 (8) + 1 + +unallocated (24) + +int64 (8) + 5 + + + +237 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +427 +   + +428 + + +value +   + +0x0000000000000000000000000000000000000000000000000DE0B6B3A7640000 +   + +0x00000000000000000000FF7F6243D8CEA23066D098A15582D81A598B4E8391F4 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 1,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0xFF   + +uint8: param3 (1) + 127   + +address: param2 (20) + 0x6243d8CEA23066d098a15582d81a598b4e8391F4 + + + +238 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +429 +   + +430 + + +value +   + +0x0000000000000000000000000000000000000000000000001BC16D674EC80000 +   + +0x00000000000000000000FF0603AB458634910AAD20EF5F1C8EE96F1D6AC54919 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 2,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0xFF   + +uint8: param3 (1) + 6   + +address: param2 (20) + 0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919 + + + +239 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +431 +   + +432 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +240 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot +   + +429-430 + +431-433 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +240:895->238 + + + + + +240:896->239 + + + + + +241 + +ContractLevelStruct$_1 <<Struct>> +0x1e831cd9a413edbf49535827428c0a05d2a0cc826f6b135dcd7395b76a16bb92 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +242 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0x1e831cd9a413edbf49535827428c0a05d2a0cc826f6b135dcd7395b76a16bb92 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + + + +242:906->241 + + + + + +243 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +244:868->231 + + + + + +244:870->232 + + + + + +244:872->233 + + + + + +244:875->234 + + + + + +244:877->235 + + + + + +244:883->236 + + + + + +244:890->237 + + + + + +244:901->240 + + + + + +244:907->242 + + + + + +244:913->243 + + + + + +314 + +string: longString <<String>> +0xd95aa57af8f015d7181e1002579357c51a435510c85e5f9d8365d9ee702ec67a + +offset +   + +0 +   + +1 + + +value +   + +0x6F7665722033312063686172617465727320736F2069732064796E616D696320 +   + +0x6C656E677468207573696E672074776F20736C6F747300000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "over 31 charaters so is dynamic " + +string (22) + "length using two slots"   + +unallocated (10) + + + +244:885->314 + + + + + +245 + +Severity[3]: staticSeverities <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +2 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000020100 + + +type: variable (bytes) +decoded data + +unallocated (29) + +Severity (1) + High   + +Severity (1) + Medium   + +Severity (1) + Low + + + +246 + +Severity[]: dynamicSeverities <<Array>> +0xb72780891006f3ea70943904343e9b9eafe6a9e933828589f35da6268d805370 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000101 + + +type: variable (bytes) +decoded data + +unallocated (30) + +Severity (1) + +Severity (1) + Medium + + + +247 + +address[]: dynamicAddressArray <<Array>> +0x41722158d060294771616bee402f71f04c1bf37462266adcf737c2d764b9b93b + +offset +   + +0 + + +value +   + +0x000000000000000000000000DBDB4D16EDA451D0503B854CF79D55697F90C8DF + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF + + + +248 + +address[2]: staticAddressArray <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +5 +   + +6 + + +value +   + +0x000000000000000000000000853D955ACEF822DB058EB8505911ED77F175B99E +   + +0x000000000000000000000000104592A158490A9228070E0A8E5343B499E125D0 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x853d955aCEf822Db058eb8505911ED77F175b99e + +unallocated (12) + +address (20) + 0x104592a158490a9228070E0A8e5343B499e125D0 + + + +249 + +int64[]: dynamicIntArray <<Array>> +0xbe4d12cecead8944bce521ca447523dfb797a24c040d9bc0b4fae6c66e9c0161 + +offset +   + +0 + + +value +   + +0x00000000000000000000000000007D00FFFFFFFFFFFFFF3800000000000000C8 + + +type: variable (bytes) +decoded data + +unallocated (8) + +int64 (8) + 32,000   + +int64 (8) + -200   + +int64 (8) + 200 + + + +250 + +int64[5]: staticIntArray <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +8 +   + +9 + + +value +   + +0x0000000000000FA00000000000000BB800000000000007D000000000000003E8 +   + +0x0000000000000000000000000000000000000000000000000000000000001388 + + +type: variable (bytes) +decoded data + +int64 (8) + 4,000   + +int64 (8) + 3,000   + +int64 (8) + 2,000   + +int64 (8) + 1,000 + +unallocated (24) + +int64 (8) + 5,000 + + + +251 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +12 +   + +13 + + +value +   + +0x00000000000000000000000000000000000000000000000029A2241AF62C0000 +   + +0x00000000000000000000890236F944B7312EAC89381BD78326DF9C84691D8A5B + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 3,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0x89   + +uint8: param3 (1) + 2   + +address: param2 (20) + 0x36F944B7312EAc89381BD78326Df9C84691D8A5B + + + +252 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +14 +   + +15 + + +value +   + +0x0000000000000000000000000000000000000000000000003782DACE9D900000 +   + +0x0000000000000000000091FF4FB30C5A3AC8E85BC32785518633303C4590752D + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 4,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0x91   + +uint8: param3 (1) + 255   + +address: param2 (20) + 0x4fB30C5A3aC8e85bC32785518633303C4590752d + + + +253 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +16 +   + +17 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +254 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +14-15 + +16-18 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +254:949->252 + + + + + +254:950->253 + + + + + +255 + +ContractLevelStruct$_1 <<Struct>> +0xa9ac31f29d0f1c928c4c1eee202b9f610d3017cdb2854fc8b91f8b41330b3cf4 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +256 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xa9ac31f29d0f1c928c4c1eee202b9f610d3017cdb2854fc8b91f8b41330b3cf4 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + + + +256:960->255 + + + + + +257 + +ContractLevelStruct$_1 <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +258 + +DynamicStruct <<Struct>> +0xec895bd7db774023f2d3571bd772c125392c92fda0a6d7a7521d19ebaa465efc + +offset +   + +0 +   + +1 +   + +2 + +3 +   + +4 +   + +5-6 + +7 +   + +8-9 + +10 +   + +11 +   + +12-13 + +14-17 + +18 +   + +19 + +20 + +value +   + +0x00000000000000000000009FF58F4FFB29FA2266AB25E75E2A8B350331165601 +   + +0x0000000000000000000000022260FAC5E5542A773AA44FBCFEDF7C193BC2C599 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000002 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x737472696E67203C203331206368617273000000000000000000000000000022 +   + +0x000000000000000000000000000000000000000000000000000000000000005B +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + + + +type: variable (bytes) +decoded data + +unallocated (11) + +address: token (20) + 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656   + +bool: flag (1) + true + +unallocated (11) + +Severity: severity (1) + High   + +IERC20: asset (20) + 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + 2 + +address[]: dynamicAddressArray (32) + 1 + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + 3 + +int64[5]: staticIntArray (64) + +string: shortString (32) + "string < 31 chars" + +string: longString (32) + 45 + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + 0 + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +258:922->245 + + + + + +258:924->246 + + + + + +258:926->247 + + + + + +258:929->248 + + + + + +258:931->249 + + + + + +258:937->250 + + + + + +258:944->251 + + + + + +258:955->254 + + + + + +258:961->256 + + + + + +258:967->257 + + + + + +315 + +string: longString <<String>> +0xeae627f86fdeb51ceda0e42aa2a0890eabe346b9792d3d89fa1b39933ade73d0 + +offset +   + +0 +   + +1 + + +value +   + +0x6120737472696E672074686174206973206F7665722033312063686172616374 +   + +0x65727320696E206C656E67746800000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "a string that is over 31 charact" + +string (13) + "ers in length"   + +unallocated (19) + + + +258:939->315 + + + + + +259:968->258 + + + + + +260 + +Severity[3]: staticSeverities <<Array>> + +slot +   + +439 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (29) + +Severity (1) + Low   + +Severity (1) + Low   + +Severity (1) + Low + + + +261 + +Severity[]: dynamicSeverities <<Array>> +0x70fd51becab23852b2dd090cdedf7d9d704171d1584775462189b90bc6e7db26 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (31) + +Severity (1) + Low + + + +262 + +address[]: dynamicAddressArray <<Array>> +0x86f721ae575c04fef389f7a94b7c0e72145101a4063a4b13c61b108052d81c93 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +263 + +address[2]: staticAddressArray <<Array>> + +slot +   + +442 +   + +443 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + +unallocated (12) + +address (20) + 0x0000000000000000000000000000000000000000 + + + +264 + +int64[]: dynamicIntArray <<Array>> +0xa67c9a55b8253f6b29a35b777ec3f4948a657d7db9ddd16fdf650ef37821b12b + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +unallocated (24) + +int64 (8) + 0 + + + +265 + +int64[5]: staticIntArray <<Array>> + +slot +   + +445 +   + +446 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +int64 (8) + 0   + +int64 (8) + 0   + +int64 (8) + 0   + +int64 (8) + 0 + +unallocated (24) + +int64 (8) + 0 + + + +266 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +449 +   + +450 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +267 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +451 +   + +452 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +268 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +453 +   + +454 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +269 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot +   + +451-452 + +453-455 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +269:1004->267 + + + + + +269:1005->268 + + + + + +270 + +ContractLevelStruct$_1 <<Struct>> +0xff6df30967a6a678f565c59a19e91e5c0dbb20cfe9f9bf26d7da6dea0fffa24c + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +271 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xff6df30967a6a678f565c59a19e91e5c0dbb20cfe9f9bf26d7da6dea0fffa24c + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + + + +271:1015->270 + + + + + +272 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +273 + +DynamicStruct <<Struct>> + +slot +   + +437 +   + +438 +   + +439 + +440 +   + +441 +   + +442-443 + +444 +   + +445-446 + +447 +   + +448 +   + +449-450 + +451-454 + +455 +   + +456 + +457 + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + + + +type: variable (bytes) +decoded data + +unallocated (11) + +address: token (20) + 0x0000000000000000000000000000000000000000   + +bool: flag (1) + false + +unallocated (11) + +Severity: severity (1) + Low   + +IERC20: asset (20) + 0x0000000000000000000000000000000000000000 + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + 0 + +address[]: dynamicAddressArray (32) + 0 + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + 0 + +int64[5]: staticIntArray (64) + +string: shortString (32) + "" + +string: longString (32) + "" + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + 0 + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +273:977->260 + + + + + +273:979->261 + + + + + +273:981->262 + + + + + +273:984->263 + + + + + +273:986->264 + + + + + +273:992->265 + + + + + +273:999->266 + + + + + +273:1010->269 + + + + + +273:1016->271 + + + + + +273:1022->272 + + + + + +274 + +Severity[3]: staticSeverities <<Array>> + +slot +   + +460 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000020001 + + +type: variable (bytes) +decoded data + +unallocated (29) + +Severity (1) + High   + +Severity (1) + Low   + +Severity (1) + Medium + + + +275 + +Severity[]: dynamicSeverities <<Array>> +0x41bf21270d8c221a457e2f64e0b5e3c274a814409eea17edf41bb9eb4ee64eb0 + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000002000201 + + +type: variable (bytes) +decoded data + +unallocated (28) + +Severity (1) + +Severity (1) + +Severity (1) + +Severity (1) + Medium + + + +276 + +address[]: dynamicAddressArray <<Array>> +0x5d6f015ab75b09997e10f2fa9ea352d95dce65c2d7ce5ac920ae531c9a381e4a + +offset +   + +0 + + +value +   + +0x000000000000000000000000DBDB4D16EDA451D0503B854CF79D55697F90C8DF + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF + + + +277 + +address[2]: staticAddressArray <<Array>> + +slot +   + +463 +   + +464 + + +value +   + +0x000000000000000000000000853D955ACEF822DB058EB8505911ED77F175B99E +   + +0x000000000000000000000000104592A158490A9228070E0A8E5343B499E125D0 + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + 0x853d955aCEf822Db058eb8505911ED77F175b99e + +unallocated (12) + +address (20) + 0x104592a158490a9228070E0A8e5343B499e125D0 + + + +278 + +int64[]: dynamicIntArray <<Array>> +0xcb7eb705d118d1261a01ba529793210c1e805fc376af74293a5f75da3c12b2ed + +offset +   + +0 + + +value +   + +0x0000000000000000000000000000FA00FFFFFFFFFFFFD8F00000000000002710 + + +type: variable (bytes) +decoded data + +unallocated (8) + +int64 (8) + 64,000   + +int64 (8) + -10,000   + +int64 (8) + 10,000 + + + +279 + +int64[5]: staticIntArray <<Array>> + +slot +   + +466 +   + +467 + + +value +   + +0x0000000000000190000000000000012C00000000000000C80000000000000064 +   + +0x00000000000000000000000000000000000000000000000000000000000001F4 + + +type: variable (bytes) +decoded data + +int64 (8) + 400   + +int64 (8) + 300   + +int64 (8) + 200   + +int64 (8) + 100 + +unallocated (24) + +int64 (8) + 500 + + + +280 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +470 +   + +471 + + +value +   + +0x0000000000000000000000000000000000000000000000004563918244F40000 +   + +0x0000000000000000000089026B175474E89094C44DA98B954EEDEAC495271D0F + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 5,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0x89   + +uint8: param3 (1) + 2   + +address: param2 (20) + 0x6B175474E89094C44Da98b954EedeAC495271d0F + + + +281 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +472 +   + +473 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +282 + +ContractLevelStruct$_1 <<Struct>> + +slot +   + +474 +   + +475 + + +value +   + +0x00000000000000000000000000000000000000000000000053444835EC580000 +   + +0x000000000000000000009981DAC17F958D2EE523A2206206994597C13D831EC7 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 6,000,000,000,000,000,000 + +unallocated (10) + +bytes1: param4 (1) + 0x99   + +uint8: param3 (1) + 129   + +address: param2 (20) + 0xdAC17F958D2ee523a2206206994597C13D831ec7 + + + +283 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +slot +   + +472-473 + +474-476 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +283:1059->281 + + + + + +283:1060->282 + + + + + +284 + +ContractLevelStruct$_1 <<Struct>> +0xcfe30d177f24701724aea052a3a6fc2a8609362a38e75e470a4fedd55b9b7e36 + +offset +   + +0 +   + +1 + + +value +   + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x0000000000000000000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + 0 + +unallocated (10) + +bytes1: param4 (1) + 0x00   + +uint8: param3 (1) + 0   + +address: param2 (20) + 0x0000000000000000000000000000000000000000 + + + +285 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> +0xcfe30d177f24701724aea052a3a6fc2a8609362a38e75e470a4fedd55b9b7e36 + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + + + +285:1070->284 + + + + + +286 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +287 + +DynamicStruct <<Struct>> + +slot +   + +458 +   + +459 +   + +460 + +461 +   + +462 +   + +463-464 + +465 +   + +466-467 + +468 +   + +469 +   + +470-471 + +472-475 + +476 +   + +477 + +478 + +value +   + +0x0000000000000000000000D37EE7E4F452C6638C96536E68090DE8CBCDB58301 +   + +0x0000000000000000000000014FB30C5A3AC8E85BC32785518633303C4590752D +   + + + +0x0000000000000000000000000000000000000000000000000000000000000004 +   + +0x0000000000000000000000000000000000000000000000000000000000000001 +   + + + +0x0000000000000000000000000000000000000000000000000000000000000003 +   + + + +0x737472696E67203C20333120636861726163746572730000000000000000002C +   + +0x0000000000000000000000000000000000000000000000000000000000000051 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + + + + + +type: variable (bytes) +decoded data + +unallocated (11) + +address: token (20) + 0xD37EE7e4f452C6638c96536e68090De8cBcdb583   + +bool: flag (1) + true + +unallocated (11) + +Severity: severity (1) + Medium   + +IERC20: asset (20) + 0x4fB30C5A3aC8e85bC32785518633303C4590752d + +Severity[3]: staticSeverities (32) + +Severity[]: dynamicSeverities (32) + 4 + +address[]: dynamicAddressArray (32) + 1 + +address[2]: staticAddressArray (64) + +int64[]: dynamicIntArray (32) + 3 + +int64[5]: staticIntArray (64) + +string: shortString (32) + "string < 31 characters" + +string: longString (32) + 40 + +ContractLevelStruct$_1: struct1 (64) + +ContractLevelStruct$_1[2]: staticStruct1 (128) + +ContractLevelStruct$_1[]: dynamicStruct1 (32) + 0 + +mapping(address=>uint256): balance (32) + +mapping(address=>ContractLevelStruct$_1): mappedStruct1 (32) + + + +287:1032->274 + + + + + +287:1034->275 + + + + + +287:1036->276 + + + + + +287:1039->277 + + + + + +287:1041->278 + + + + + +287:1047->279 + + + + + +287:1054->280 + + + + + +287:1065->283 + + + + + +287:1071->285 + + + + + +287:1077->286 + + + + + +316 + +string: longString <<String>> +0x9f31c6e1fd7dfe4437c2313157aba3c74244a3ed366f8f45280769d409a8eb0a + +offset +   + +0 +   + +1 + + +value +   + +0x6120737472696E672074686174206973206F7665722033312063686172616374 +   + +0x657273206C6F6E67000000000000000000000000000000000000000000000000 + + +type: variable (bytes) +decoded data + +string (32) + "a string that is over 31 charact" + +string (8) + "ers long"   + +unallocated (24) + + + +287:1049->316 + + + + + +288:1023->273 + + + + + +288:1024->287 + + + + + +289 + +Severity[3]: staticSeverities <<Array>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +unallocated (29) + +Severity (1) + +Severity (1) + +Severity (1) + + + +290 + +Severity[]: dynamicSeverities <<Array>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +unallocated (31) + +Severity (1) + + + +291 + +address[]: dynamicAddressArray <<Array>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + + + +292 + +address[2]: staticAddressArray <<Array>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +unallocated (12) + +address (20) + +unallocated (12) + +address (20) + + + +293 + +int64[]: dynamicIntArray <<Array>> + +offset +   + +0 + +value +   + + + +type: variable (bytes) +decoded data + +unallocated (24) + +int64 (8) + + + +294 + +int64[5]: staticIntArray <<Array>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +int64 (8) + +int64 (8) + +int64 (8) + +int64 (8) + +unallocated (24) + +int64 (8) + + + +295 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +296 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +297 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +298 + +ContractLevelStruct$_1[2]: staticStruct1 <<Array>> + +offset +   + +0-1 + +2-4 + +value +   + + + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + +ContractLevelStruct$_1 (64) + + + +298:1113->296 + + + + + +298:1114->297 + + + + + +299 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +300 + +ContractLevelStruct$_1[]: dynamicStruct1 <<Array>> + +offset +   + +0-1 + +value +   + + + +type: variable (bytes) +decoded data + +ContractLevelStruct$_1 (64) + + + +300:1124->299 + + + + + +301 + +ContractLevelStruct$_1 <<Struct>> + +offset +   + +0 + +1 + +value +   + + + + + +type: variable (bytes) +decoded data + +uint256: param1 (32) + +unallocated (10) + +bytes1: param4 (1) + +uint8: param3 (1) + +address: param2 (20) + + + +302:1086->289 + + + + + +302:1088->290 + + + + + +302:1090->291 + + + + + +302:1093->292 + + + + + +302:1095->293 + + + + + +302:1101->294 + + + + + +302:1108->295 + + + + + +302:1119->298 + + + + + +302:1125->300 + + + + + +302:1131->301 + + + + + diff --git a/examples/storage/examples.sh b/examples/storage/examples.sh new file mode 100644 index 00000000..cdfb807b --- /dev/null +++ b/examples/storage/examples.sh @@ -0,0 +1,26 @@ + +cd examples/storage +sol2uml storage ../../src/contracts -c BasicStorage +sol2uml storage ../../src/contracts -c DynamicArrayStorage +sol2uml storage ../../src/contracts -c MultiDynamicArrayStorage +sol2uml storage ../../src/contracts -c StructStorage +sol2uml storage ../../src/contracts -c StringStorage +sol2uml storage ../../src/contracts -c MappingStorage +sol2uml class ../../src/contracts/inheritance -c -f png -o ../inheritanceDiamond.png +sol2uml storage ../../src/contracts/inheritance -c D -o inheritanceStorage.svg + +sol2uml storage -d -n arbitrum 0x8E2587265C68CD9EE3EcBf22DC229980b47CB960 -o BasicStorageData.svg +sol2uml storage -d -n arbitrum 0x796c008d8ADDCc33Da3e946Ca457432a35913c85 -o FixedArrayStorageData.svg +sol2uml storage -d -n arbitrum 0xe147cB7D90B9253844130E2C4A7Ef0ffB641C3ea -o MultiFixedArrayStorageData.svg +sol2uml storage -d -n arbitrum 0x66535378de7FB9219b637DBE3e3FFad33387f80B -o DynamicArrayStorageData.svg +sol2uml storage -d -n arbitrum 0x6f44d1108bB79710C1BBE378661d90876682E027 -o MultiDynamicArrayStorageData.svg +sol2uml storage -d -n arbitrum 0xB8F98C34e40E0D201CE2F3440cE92d0B5c5CfFe2 -o StructStorageData.svg +sol2uml storage -d -n arbitrum 0xeF2A93be2beD1b577D460c347f82De1Ba8bD9861 -o StringStorageData.svg + +# USDC +sol2uml storage 0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf -d -s 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -o usdcData.svg +# mStable Emissions Controller +sol2uml storage 0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 +sol2uml storage 0xebfd9cD78510c591eDa8735D0F8a87414eF27A83 -d -s 0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780 +# mStable Staking contract for MTA/ETH 80/20 Balancer Pool Token (BPT) +sol2uml storage 0xc63a48d85CCE7C3bD4d18db9c0972a4D223e4193 -bn 16000000 -d -s 0xeFbe22085D9f29863Cfb77EEd16d3cC0D927b011 -o StakedTokenBPTData.svg diff --git a/examples/storage/usdcData.svg b/examples/storage/usdcData.svg index e0634c84..ef0a7509 100644 --- a/examples/storage/usdcData.svg +++ b/examples/storage/usdcData.svg @@ -4,155 +4,196 @@ - - + + StorageDiagram - + 1 - -FiatTokenV2_1 <<Contract>> -0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf - -slot - -0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - -11 - -12 - -13 - -14 - -15 - -16 - -17 - -18 - -value - -0x000000000000000000000000FCB19E6A322B27C06842A71E8C725399F049AE3A - -0x000000000000000000000000F0D160DEC1749AFAF5A831668093B1431F7C8527 - -0x0000000000000000000000005DB0115F3B72D19CEA34DD697CF412FF86DC7E1B - - - -0x55534420436F696E000000000000000000000000000000000000000000000010 - -0x5553444300000000000000000000000000000000000000000000000000000008 - -0x0000000000000000000000000000000000000000000000000000000000000006 - -0x5553440000000000000000000000000000000000000000000000000000000006 - -0x000000000000000000000001E982615D461DD5CD06575BBEA87624FDA4E3DE17 - - - - - -0x00000000000000000000000000000000000000000000000000A1A3BFC373E70E - - - - - -0x0000000000000000000000000000000000000000000000000000000000000000 - -0x06C37168A7DB5138DEFC7866392BB87A741F9B3D104DEB5094588CE041CAE335 - - - - - -0x0000000000000000000000000000000000000000000000000000000000000002 - -type: <inherited contract>.variable (bytes) - -unallocated (12) - -address: Ownable._owner (20) - -unallocated (11) - -bool: Pausable.paused (1) - -address: Pausable.pauser (20) - -unallocated (12) - -address: Blacklistable.blacklister (20) - -mapping(address=>bool): Blacklistable.blacklisted (32) - -string: FiatTokenV1.name (32) - -string: FiatTokenV1.symbol (32) - -unallocated (31) - -uint8: FiatTokenV1.decimals (1) - -string: FiatTokenV1.currency (32) - -unallocated (11) - -bool: FiatTokenV1.initialized (1) - -address: FiatTokenV1.masterMinter (20) - -mapping(address=>uint256): FiatTokenV1.balances (32) - -mapping(address=>mapping(address=>uint256)): FiatTokenV1.allowed (32) - -uint256: FiatTokenV1.totalSupply_ (32) - -mapping(address=>bool): FiatTokenV1.minters (32) - -mapping(address=>uint256): FiatTokenV1.minterAllowed (32) - -unallocated (12) - -address: Rescuable._rescuer (20) - -bytes32: EIP712Domain.DOMAIN_SEPARATOR (32) - -mapping(address=>mapping(bytes32=>bool)): EIP3009._authorizationStates (32) - -mapping(address=>uint256): EIP2612._permitNonces (32) - -unallocated (31) - -uint8: FiatTokenV2._initializedVersion (1) + +FiatTokenV2_1 <<Contract>> +0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf + +slot +   + +0 +   + +1 +   + +2 +   + +3 + +4 +   + +5 +   + +6 +   + +7 +   + +8 +   + +9 + +10 + +11 +   + +12 + +13 + +14 +   + +15 +   + +16 + +17 + +18 + + +value +   + +0x000000000000000000000000FCB19E6A322B27C06842A71E8C725399F049AE3A +   + +0x000000000000000000000000F0D160DEC1749AFAF5A831668093B1431F7C8527 +   + +0x0000000000000000000000005DB0115F3B72D19CEA34DD697CF412FF86DC7E1B +   + + + +0x55534420436F696E000000000000000000000000000000000000000000000010 +   + +0x5553444300000000000000000000000000000000000000000000000000000008 +   + +0x0000000000000000000000000000000000000000000000000000000000000006 +   + +0x5553440000000000000000000000000000000000000000000000000000000006 +   + +0x000000000000000000000001E982615D461DD5CD06575BBEA87624FDA4E3DE17 +   + + + + + +0x00000000000000000000000000000000000000000000000000897EDBA2ECB5FE +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000000 +   + +0x06C37168A7DB5138DEFC7866392BB87A741F9B3D104DEB5094588CE041CAE335 +   + + + + + +0x0000000000000000000000000000000000000000000000000000000000000002 + + +type: <inherited contract>.variable (bytes) +decoded data + +unallocated (12) + +address: Ownable._owner (20) + 0xFcb19e6a322b27c06842A71e8c725399f049AE3a + +unallocated (11) + +bool: Pausable.paused (1) + false   + +address: Pausable.pauser (20) + 0xf0d160DEC1749aFaF5A831668093B1431f7C8527 + +unallocated (12) + +address: Blacklistable.blacklister (20) + 0x5dB0115f3B72d19cEa34dD697cf412Ff86dc7E1b + +mapping(address=>bool): Blacklistable.blacklisted (32) + +string: FiatTokenV1.name (32) + "USD Coin" + +string: FiatTokenV1.symbol (32) + "USDC" + +unallocated (31) + +uint8: FiatTokenV1.decimals (1) + 6 + +string: FiatTokenV1.currency (32) + "USD" + +unallocated (11) + +bool: FiatTokenV1.initialized (1) + true   + +address: FiatTokenV1.masterMinter (20) + 0xE982615d461DD5cD06575BbeA87624fda4e3de17 + +mapping(address=>uint256): FiatTokenV1.balances (32) + +mapping(address=>mapping(address=>uint256)): FiatTokenV1.allowed (32) + +uint256: FiatTokenV1.totalSupply_ (32) + 38,701,553,605,719,550 + +mapping(address=>bool): FiatTokenV1.minters (32) + +mapping(address=>uint256): FiatTokenV1.minterAllowed (32) + +unallocated (12) + +address: Rescuable._rescuer (20) + 0x0000000000000000000000000000000000000000 + +bytes32: EIP712Domain.DOMAIN_SEPARATOR (32) + 0x06C37168A7DB5138DEFC7866392BB87A741F9B3D104DEB5094588CE041CAE335 + +mapping(address=>mapping(bytes32=>bool)): EIP3009._authorizationStates (32) + +mapping(address=>uint256): EIP2612._permitNonces (32) + +unallocated (31) + +uint8: FiatTokenV2._initializedVersion (1) + 2 diff --git a/lib/SlotValueCache.d.ts b/lib/SlotValueCache.d.ts new file mode 100644 index 00000000..ccd0d935 --- /dev/null +++ b/lib/SlotValueCache.d.ts @@ -0,0 +1,31 @@ +import { BigNumberish } from '@ethersproject/bignumber'; +/** + * Singleton that caches a mapping of slot keys to values. + * Assumes all data is read from the same block and contract + */ +export declare class SlotValueCache { + private static slotCache; + /** + * @param slotKeys array of slot numbers or slot keys in hexadecimal format + * @return cachedValues array of the slot values that are in the cache. + * @return missingKeys array of the slot keys that are not cached in hexadecimal format. + */ + static readSlotValues(slotKeys: readonly BigNumberish[]): { + cachedValues: string[]; + missingKeys: string[]; + }; + /** + * Adds the missing slot values to the cache and then returns all slot values from + * the cache for each of the `slotKeys`. + * @param slotKeys array of slot numbers or keys in hexadecimal format. + * @param missingKeys array of the slot keys that are not cached in hexadecimal format. + * @param missingValues array of slot values in hexadecimal format. + * @return values array of slot values for each of the `slotKeys`. + */ + static addSlotValues(slotKeys: readonly BigNumberish[], missingKeys: readonly string[], missingValues: readonly string[]): string[]; + /** + * Used for testing purposes to clear the cache. + * This allows tests to run against different contracts and blockTags + */ + static clear(): void; +} diff --git a/lib/SlotValueCache.js b/lib/SlotValueCache.js new file mode 100644 index 00000000..b7e46b1e --- /dev/null +++ b/lib/SlotValueCache.js @@ -0,0 +1,65 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SlotValueCache = void 0; +const bignumber_1 = require("@ethersproject/bignumber"); +const debug = require('debug')('sol2uml'); +/** + * Singleton that caches a mapping of slot keys to values. + * Assumes all data is read from the same block and contract + */ +class SlotValueCache { + /** + * @param slotKeys array of slot numbers or slot keys in hexadecimal format + * @return cachedValues array of the slot values that are in the cache. + * @return missingKeys array of the slot keys that are not cached in hexadecimal format. + */ + static readSlotValues(slotKeys) { + const cachedValues = []; + const missingKeys = []; + slotKeys.forEach((slotKey, i) => { + const key = bignumber_1.BigNumber.from(slotKey).toHexString(); + if (this.slotCache[key]) { + cachedValues.push(this.slotCache[key]); + } + else { + missingKeys.push(key); + } + }); + return { cachedValues, missingKeys }; + } + /** + * Adds the missing slot values to the cache and then returns all slot values from + * the cache for each of the `slotKeys`. + * @param slotKeys array of slot numbers or keys in hexadecimal format. + * @param missingKeys array of the slot keys that are not cached in hexadecimal format. + * @param missingValues array of slot values in hexadecimal format. + * @return values array of slot values for each of the `slotKeys`. + */ + static addSlotValues(slotKeys, missingKeys, missingValues) { + if (missingKeys?.length !== missingValues?.length) { + throw Error(`${missingKeys?.length} keys does not match ${missingValues?.length} values`); + } + missingKeys.forEach((key, i) => { + if (!this.slotCache[key]) { + debug(`cached slot ${key} with ${missingValues[i]}`); + this.slotCache[key] = missingValues[i]; + } + }); + return slotKeys.map((slotKey) => { + const key = bignumber_1.BigNumber.from(slotKey).toHexString(); + // it should find the slot value in the cache. if not it'll return undefined + return this.slotCache[key]; + }); + } + /** + * Used for testing purposes to clear the cache. + * This allows tests to run against different contracts and blockTags + */ + static clear() { + this.slotCache = {}; + } +} +exports.SlotValueCache = SlotValueCache; +// Singleton of cached slot keys mapped to values +SlotValueCache.slotCache = {}; +//# sourceMappingURL=SlotValueCache.js.map \ No newline at end of file diff --git a/lib/associations.d.ts b/lib/associations.d.ts index ad4306ed..5501a215 100644 --- a/lib/associations.d.ts +++ b/lib/associations.d.ts @@ -1,2 +1,2 @@ import { Association, UmlClass } from './umlClass'; -export declare const findAssociatedClass: (association: Association, sourceUmlClass: UmlClass, umlClasses: UmlClass[], searchedAbsolutePaths?: string[]) => UmlClass | undefined; +export declare const findAssociatedClass: (association: Association, sourceUmlClass: UmlClass, umlClasses: readonly UmlClass[], searchedAbsolutePaths?: string[]) => UmlClass | undefined; diff --git a/lib/associations.js b/lib/associations.js index cd8142cf..72e062e9 100644 --- a/lib/associations.js +++ b/lib/associations.js @@ -1,29 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findAssociatedClass = void 0; -const umlClass_1 = require("./umlClass"); // Find the UML class linked to the association const findAssociatedClass = (association, sourceUmlClass, umlClasses, searchedAbsolutePaths = []) => { - let umlClass = umlClasses.find((targetUmlClass) => { - // is the source class link via the association to the target class? - if (isAssociated(association, sourceUmlClass, targetUmlClass)) - return true; - // Not linked so now try linking to target under the node_modules folder. - // eg remove node_modules from node_modules/@openzeppelin/contracts-upgradeable/proxy/Initializable.sol - // is the target class under node_modules? - if (targetUmlClass.relativePath.match(/^node_modules\//)) { - // clone the target and updated absolutePath and relativePath so it's no longer under node_modules - const clonedTargetClass = new umlClass_1.UmlClass(targetUmlClass); - clonedTargetClass.absolutePath = - targetUmlClass.absolutePath.replace(/^node_modules\//, ''); - clonedTargetClass.relativePath = - targetUmlClass.relativePath.replace(/^node_modules\//, ''); - // is the source class link via the association to the target class? - return isAssociated(association, sourceUmlClass, clonedTargetClass); - } - // could not find a link from the source to target via the association - return false; - }); + const umlClass = umlClasses.find((targetUmlClass) => isAssociated(association, sourceUmlClass, targetUmlClass)); // If a link was found if (umlClass) return umlClass; diff --git a/lib/converterAST2Classes.d.ts b/lib/converterAST2Classes.d.ts index d7fe79c8..85586708 100644 --- a/lib/converterAST2Classes.d.ts +++ b/lib/converterAST2Classes.d.ts @@ -1,10 +1,21 @@ import { ASTNode } from '@solidity-parser/parser/dist/src/ast-types'; import { UmlClass } from './umlClass'; +import { Remapping } from './parserEtherscan'; /** * Convert solidity parser output of type `ASTNode` to UML classes of type `UMLClass` * @param node output of Solidity parser of type `ASTNode` * @param relativePath relative path from the working directory to the Solidity source file + * @param remappings used to rename relative paths * @param filesystem flag if Solidity source code was parsed from the filesystem or Etherscan * @return umlClasses array of UML class definitions of type `UmlClass` */ -export declare function convertAST2UmlClasses(node: ASTNode, relativePath: string, filesystem?: boolean): UmlClass[]; +export declare function convertAST2UmlClasses(node: ASTNode, relativePath: string, remappings: Remapping[], filesystem?: boolean): UmlClass[]; +/** + * Used to rename import file names. For example + * @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * to + * lib/openzeppelin-contracts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * @param fileName file name in the Solidity code + * @param mappings an array of remappings from Etherscan's settings + */ +export declare const renameFile: (fileName: string, mappings: Remapping[]) => string; diff --git a/lib/converterAST2Classes.js b/lib/converterAST2Classes.js index 40a09a1a..e23e9dc6 100644 --- a/lib/converterAST2Classes.js +++ b/lib/converterAST2Classes.js @@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertAST2UmlClasses = void 0; +exports.renameFile = exports.convertAST2UmlClasses = void 0; const path = __importStar(require("path")); const path_1 = require("path"); const umlClass_1 = require("./umlClass"); @@ -34,10 +34,11 @@ let umlClasses; * Convert solidity parser output of type `ASTNode` to UML classes of type `UMLClass` * @param node output of Solidity parser of type `ASTNode` * @param relativePath relative path from the working directory to the Solidity source file + * @param remappings used to rename relative paths * @param filesystem flag if Solidity source code was parsed from the filesystem or Etherscan * @return umlClasses array of UML class definitions of type `UmlClass` */ -function convertAST2UmlClasses(node, relativePath, filesystem = false) { +function convertAST2UmlClasses(node, relativePath, remappings, filesystem = false) { const imports = []; umlClasses = []; if (node.type === 'SourceUnit') { @@ -110,11 +111,12 @@ function convertAST2UmlClasses(node, relativePath, filesystem = false) { } else { // this has come from Etherscan - const importPath = childNode.path[0] === '.' + const remappedFile = (0, exports.renameFile)(childNode.path, remappings); + const importPath = remappedFile[0] === '.' ? // Use Linux paths, not Windows paths, to resolve Etherscan files - path_1.posix.join(codeFolder.toString(), childNode.path) - : childNode.path; - debug(`codeFolder ${codeFolder} childNode.path ${childNode.path}`); + path_1.posix.join(codeFolder.toString(), remappedFile) + : remappedFile; + debug(`codeFolder ${codeFolder} childNode.path ${childNode.path} remapped to ${remappedFile}`); const newImport = { absolutePath: importPath, classNames: childNode.symbolAliases @@ -126,7 +128,10 @@ function convertAST2UmlClasses(node, relativePath, filesystem = false) { }) : [], }; - debug(`Added Etherscan import ${newImport.absolutePath} with class names: ${newImport.classNames}`); + debug(`Added Etherscan import ${newImport.absolutePath} with:`); + newImport.classNames.forEach((className) => { + debug(`\t alias ${className.className}, name ${className.className}`); + }); imports.push(newImport); } } @@ -671,4 +676,25 @@ function parseContractKind(kind) { throw Error(`Invalid kind ${kind}`); } } +/** + * Used to rename import file names. For example + * @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * to + * lib/openzeppelin-contracts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * @param fileName file name in the Solidity code + * @param mappings an array of remappings from Etherscan's settings + */ +const renameFile = (fileName, mappings) => { + let renamedFile = fileName; + for (const mapping of mappings) { + if (renamedFile.match(mapping.from)) { + const beforeFileName = renamedFile; + renamedFile = renamedFile.replace(mapping.from, mapping.to); + debug(`remapping ${beforeFileName} to ${renamedFile}`); + break; + } + } + return renamedFile; +}; +exports.renameFile = renameFile; //# sourceMappingURL=converterAST2Classes.js.map \ No newline at end of file diff --git a/lib/converterClass2Dot.d.ts b/lib/converterClass2Dot.d.ts index 6574c136..aca98745 100644 --- a/lib/converterClass2Dot.d.ts +++ b/lib/converterClass2Dot.d.ts @@ -13,5 +13,9 @@ export interface ClassOptions { hideAbstracts?: boolean; hideFilename?: boolean; hideSourceContract?: boolean; + backColor?: string; + shapeColor?: string; + fillColor?: string; + textColor?: string; } export declare const convertClass2Dot: (umlClass: UmlClass, options?: ClassOptions) => string; diff --git a/lib/converterClasses2Dot.js b/lib/converterClasses2Dot.js index 3aa4b273..e977ecd0 100644 --- a/lib/converterClasses2Dot.js +++ b/lib/converterClasses2Dot.js @@ -18,9 +18,10 @@ function convertUmlClasses2Dot(umlClasses, clusterFolders = false, classOptions let dotString = ` digraph UmlClassDiagram { rankdir=BT -color=black arrowhead=open -node [shape=record, style=filled, fillcolor=gray95]`; +bgcolor="${classOptions.backColor}" +edge [color="${classOptions.shapeColor}"] +node [shape=record, style=filled, color="${classOptions.shapeColor}", fillcolor="${classOptions.fillColor}", fontcolor="${classOptions.textColor}"]`; // Sort UML Classes by folder of source file const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses); let currentCodeFolder = ''; diff --git a/lib/converterClasses2Storage.d.ts b/lib/converterClasses2Storage.d.ts index fb1e3cd0..9e5323f1 100644 --- a/lib/converterClasses2Storage.d.ts +++ b/lib/converterClasses2Storage.d.ts @@ -1,9 +1,11 @@ -import { Attribute, UmlClass } from './umlClass'; +import { Attribute, AttributeType, UmlClass } from './umlClass'; import { BigNumberish } from '@ethersproject/bignumber'; -export declare enum StorageType { +export declare enum StorageSectionType { Contract = "Contract", Struct = "Struct", - Array = "Array" + Array = "Array", + Bytes = "Bytes", + String = "String" } export interface Variable { id: number; @@ -12,47 +14,67 @@ export interface Variable { byteSize: number; byteOffset: number; type: string; + attributeType: AttributeType; dynamic: boolean; - variable?: string; + name?: string; contractName?: string; - noValue: boolean; - value?: string; - referenceStorageId?: number; - enumId?: number; + displayValue: boolean; + getValue?: boolean; + slotValue?: string; + parsedValue?: string; + referenceSectionId?: number; + enumValues?: string[]; } -export interface Storage { +export interface StorageSection { id: number; name: string; address?: string; - slotKey?: string; - type: StorageType; + offset?: string; + type: StorageSectionType; arrayLength?: number; arrayDynamic?: boolean; + mapping: boolean; variables: Variable[]; } -/** - * - * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy - * @param contractAddress Contract address to get the storage slot values from. - * If proxied, use proxy and not the implementation contract. - * @param storage is mutated with the storage values - * @param blockTag block number or `latest` - */ -export declare const addStorageValues: (url: string, contractAddress: string, storage: Storage, blockTag?: BigNumberish | 'latest') => Promise; /** * * @param contractName name of the contract to get storage layout. * @param umlClasses array of UML classes of type `UMLClass` + * @param arrayItems the number of items to display at the start and end of an array * @param contractFilename relative path of the contract in the file system - * @return array of storage objects with consecutive slots + * @return storageSections array of storageSection objects + */ +export declare const convertClasses2StorageSections: (contractName: string, umlClasses: UmlClass[], arrayItems: number, contractFilename?: string) => StorageSection[]; +/** + * Recursively adds new storage sections under a class attribute. + * also returns the allowed enum values + * @param attribute the attribute that is referencing a storage section + * @param umlClass contract or file level struct + * @param otherClasses array of all the UML Classes + * @param storageSections mutable array of storageSection objects + * @param mapping flags that the storage section is under a mapping + * @param arrayItems the number of items to display at the start and end of an array + * @return storageSection new storage section that was added or undefined if none was added. + * @return enumValues array of allowed enum values. undefined if attribute is not an enum */ -export declare const convertClasses2Storages: (contractName: string, umlClasses: UmlClass[], contractFilename?: string) => Storage[]; -export declare const parseReferenceStorage: (attribute: Attribute, umlClass: UmlClass, otherClasses: UmlClass[], storages: Storage[]) => Storage | undefined; -export declare const calcStorageByteSize: (attribute: Attribute, umlClass: UmlClass, otherClasses: UmlClass[]) => { +export declare const parseStorageSectionFromAttribute: (attribute: Attribute, umlClass: UmlClass, otherClasses: readonly UmlClass[], storageSections: StorageSection[], mapping: boolean, arrayItems: number) => { + storageSection: StorageSection; + enumValues?: string[]; +}; +export declare const calcStorageByteSize: (attribute: Attribute, umlClass: UmlClass, otherClasses: readonly UmlClass[]) => { size: number; dynamic: boolean; }; export declare const isElementary: (type: string) => boolean; -export declare const calcSlotKey: (variable: Variable) => string | undefined; -export declare const offsetStorageSlots: (storage: Storage, slots: number, storages: Storage[]) => void; -export declare const findDimensionLength: (umlClass: UmlClass, dimension: string) => number; +export declare const calcSectionOffset: (variable: Variable, sectionOffset?: string) => string; +export declare const findDimensionLength: (umlClass: UmlClass, dimension: string, otherClasses: readonly UmlClass[]) => number; +/** + * Recursively adds variables for dynamic string, bytes or arrays + * @param storageSection + * @param storageSections + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +export declare const addDynamicVariables: (storageSection: StorageSection, storageSections: StorageSection[], url: string, contractAddress: string, arrayItems: number, blockTag: BigNumberish) => Promise; diff --git a/lib/converterClasses2Storage.js b/lib/converterClasses2Storage.js index 34b7802e..8df48422 100644 --- a/lib/converterClasses2Storage.js +++ b/lib/converterClasses2Storage.js @@ -3,47 +3,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.findDimensionLength = exports.offsetStorageSlots = exports.calcSlotKey = exports.isElementary = exports.calcStorageByteSize = exports.parseReferenceStorage = exports.convertClasses2Storages = exports.addStorageValues = exports.StorageType = void 0; +exports.addDynamicVariables = exports.findDimensionLength = exports.calcSectionOffset = exports.isElementary = exports.calcStorageByteSize = exports.parseStorageSectionFromAttribute = exports.convertClasses2StorageSections = exports.StorageSectionType = void 0; const umlClass_1 = require("./umlClass"); const associations_1 = require("./associations"); -const slotValues_1 = require("./slotValues"); const utils_1 = require("ethers/lib/utils"); const ethers_1 = require("ethers"); const path_1 = __importDefault(require("path")); +const slotValues_1 = require("./slotValues"); const debug = require('debug')('sol2uml'); -var StorageType; -(function (StorageType) { - StorageType["Contract"] = "Contract"; - StorageType["Struct"] = "Struct"; - StorageType["Array"] = "Array"; -})(StorageType = exports.StorageType || (exports.StorageType = {})); +var StorageSectionType; +(function (StorageSectionType) { + StorageSectionType["Contract"] = "Contract"; + StorageSectionType["Struct"] = "Struct"; + StorageSectionType["Array"] = "Array"; + StorageSectionType["Bytes"] = "Bytes"; + StorageSectionType["String"] = "String"; +})(StorageSectionType = exports.StorageSectionType || (exports.StorageSectionType = {})); let storageId = 1; let variableId = 1; -/** - * - * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy - * @param contractAddress Contract address to get the storage slot values from. - * If proxied, use proxy and not the implementation contract. - * @param storage is mutated with the storage values - * @param blockTag block number or `latest` - */ -const addStorageValues = async (url, contractAddress, storage, blockTag) => { - const valueVariables = storage.variables.filter((s) => !s.noValue); - const slots = valueVariables.map((s) => s.fromSlot); - const values = await (0, slotValues_1.getStorageValues)(url, contractAddress, slots, blockTag); - valueVariables.forEach((valueVariable, i) => { - valueVariable.value = values[i]; - }); -}; -exports.addStorageValues = addStorageValues; /** * * @param contractName name of the contract to get storage layout. * @param umlClasses array of UML classes of type `UMLClass` + * @param arrayItems the number of items to display at the start and end of an array * @param contractFilename relative path of the contract in the file system - * @return array of storage objects with consecutive slots + * @return storageSections array of storageSection objects */ -const convertClasses2Storages = (contractName, umlClasses, contractFilename) => { +const convertClasses2StorageSections = (contractName, umlClasses, arrayItems, contractFilename) => { // Find the base UML Class from the base contract name const umlClass = umlClasses.find(({ name, relativePath }) => { if (!contractFilename) { @@ -61,25 +47,32 @@ const convertClasses2Storages = (contractName, umlClasses, contractFilename) => throw Error(`Failed to find contract with name "${contractName}"${contractFilenameError}.\nIs the \`-c --contract \` option correct?`); } debug(`Found contract "${contractName}" in ${umlClass.absolutePath}`); - const storages = []; - const variables = parseVariables(umlClass, umlClasses, [], storages, []); - storages.unshift({ + const storageSections = []; + const variables = parseVariables(umlClass, umlClasses, [], storageSections, [], false, arrayItems); + // Add new storage section to the beginning of the array + storageSections.unshift({ id: storageId++, name: contractName, - type: StorageType.Contract, + type: StorageSectionType.Contract, variables: variables, + mapping: false, }); - return storages; + adjustSlots(storageSections[0], 0, storageSections); + return storageSections; }; -exports.convertClasses2Storages = convertClasses2Storages; +exports.convertClasses2StorageSections = convertClasses2StorageSections; /** - * Recursively parses the storage variables for a given contract. + * Recursively parse the storage variables for a given contract or struct. * @param umlClass contract or file level struct * @param umlClasses other contracts, structs and enums that may be a type of a storage variable. - * @param variables mutable array of storage slots that is appended to - * @param storages mutable array of storages that is appended with structs + * @param variables mutable array of storage variables that are appended to + * @param storageSections mutable array of storageSection objects + * @param inheritedContracts mutable array of contracts that have been inherited already + * @param mapping flags that the storage section is under a mapping + * @param arrayItems the number of items to display at the start and end of an array + * @return variables array of storage variables in the `umlClass` */ -const parseVariables = (umlClass, umlClasses, variables, storages, inheritedContracts) => { +const parseVariables = (umlClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems) => { // Add storage slots from inherited contracts first. // Get immediate parent contracts that the class inherits from const parentContracts = umlClass.getParentContracts(); @@ -94,7 +87,7 @@ const parseVariables = (umlClass, umlClasses, variables, storages, inheritedCont throw Error(`Failed to find inherited contract "${parent.targetUmlClassName}" of "${umlClass.absolutePath}"`); } // recursively parse inherited contract - parseVariables(parentClass, umlClasses, variables, storages, inheritedContracts); + parseVariables(parentClass, umlClasses, variables, storageSections, inheritedContracts, mapping, arrayItems); }); // Parse storage for each attribute umlClass.attributes.forEach((attribute) => { @@ -102,69 +95,97 @@ const parseVariables = (umlClass, umlClasses, variables, storages, inheritedCont if (attribute.compiled) return; const { size: byteSize, dynamic } = (0, exports.calcStorageByteSize)(attribute, umlClass, umlClasses); - const noValue = attribute.attributeType === umlClass_1.AttributeType.Mapping || - (attribute.attributeType === umlClass_1.AttributeType.Array && !dynamic); - // find any dependent storage locations - const referenceStorage = (0, exports.parseReferenceStorage)(attribute, umlClass, umlClasses, storages); + // parse any dependent storage sections or enums + const references = (0, exports.parseStorageSectionFromAttribute)(attribute, umlClass, umlClasses, storageSections, mapping || attribute.attributeType === umlClass_1.AttributeType.Mapping, arrayItems); + // should this new variable get the slot value + const displayValue = calcDisplayValue(attribute.attributeType, dynamic, mapping, references?.storageSection?.type); + const getValue = calcGetValue(attribute.attributeType, mapping); // Get the toSlot of the last storage item - let lastToSlot = 0; - let nextOffset = 0; - if (variables.length > 0) { - const lastStorage = variables[variables.length - 1]; - lastToSlot = lastStorage.toSlot; - nextOffset = lastStorage.byteOffset + lastStorage.byteSize; - } - let newVariable; + const lastVariable = variables[variables.length - 1]; + let lastToSlot = lastVariable ? lastVariable.toSlot : 0; + let nextOffset = lastVariable + ? lastVariable.byteOffset + lastVariable.byteSize + : 0; + let fromSlot; + let toSlot; + let byteOffset; if (nextOffset + byteSize > 32) { const nextFromSlot = variables.length > 0 ? lastToSlot + 1 : 0; - newVariable = { - id: variableId++, - fromSlot: nextFromSlot, - toSlot: nextFromSlot + Math.floor((byteSize - 1) / 32), - byteSize, - byteOffset: 0, - type: attribute.type, - dynamic, - noValue, - variable: attribute.name, - contractName: umlClass.name, - referenceStorageId: referenceStorage?.id, - }; + fromSlot = nextFromSlot; + toSlot = nextFromSlot + Math.floor((byteSize - 1) / 32); + byteOffset = 0; } else { - newVariable = { - id: variableId++, - fromSlot: lastToSlot, - toSlot: lastToSlot, - byteSize, - byteOffset: nextOffset, - type: attribute.type, - dynamic, - noValue, - variable: attribute.name, - contractName: umlClass.name, - referenceStorageId: referenceStorage?.id, - }; + fromSlot = lastToSlot; + toSlot = lastToSlot; + byteOffset = nextOffset; } - if (referenceStorage) { - if (!newVariable.dynamic) { - (0, exports.offsetStorageSlots)(referenceStorage, newVariable.fromSlot, storages); + variables.push({ + id: variableId++, + fromSlot, + toSlot, + byteSize, + byteOffset, + type: attribute.type, + attributeType: attribute.attributeType, + dynamic, + getValue, + displayValue, + name: attribute.name, + contractName: umlClass.name, + referenceSectionId: references?.storageSection?.id, + enumValues: references?.enumValues, + }); + }); + return variables; +}; +/** + * Recursively adjusts the fromSlot and toSlot properties of any storage variables + * that are referenced by a static array or struct. + * Also sets the storage slot offset for dynamic arrays, strings and bytes. + * @param storageSection + * @param slotOffset + * @param storageSections + */ +const adjustSlots = (storageSection, slotOffset, storageSections) => { + storageSection.variables.forEach((variable) => { + // offset storage slots + variable.fromSlot += slotOffset; + variable.toSlot += slotOffset; + // find storage section that the variable is referencing + const referenceStorageSection = storageSections.find((ss) => ss.id === variable.referenceSectionId); + if (referenceStorageSection) { + referenceStorageSection.offset = storageSection.offset; + if (!variable.dynamic) { + adjustSlots(referenceStorageSection, variable.fromSlot, storageSections); } - else if (attribute.attributeType === umlClass_1.AttributeType.Array) { - referenceStorage.slotKey = (0, exports.calcSlotKey)(newVariable); + else if (variable.attributeType === umlClass_1.AttributeType.Array) { + // attribute is a dynamic array + referenceStorageSection.offset = (0, exports.calcSectionOffset)(variable, storageSection.offset); + adjustSlots(referenceStorageSection, 0, storageSections); } } - variables.push(newVariable); }); - return variables; }; -const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => { +/** + * Recursively adds new storage sections under a class attribute. + * also returns the allowed enum values + * @param attribute the attribute that is referencing a storage section + * @param umlClass contract or file level struct + * @param otherClasses array of all the UML Classes + * @param storageSections mutable array of storageSection objects + * @param mapping flags that the storage section is under a mapping + * @param arrayItems the number of items to display at the start and end of an array + * @return storageSection new storage section that was added or undefined if none was added. + * @return enumValues array of allowed enum values. undefined if attribute is not an enum + */ +const parseStorageSectionFromAttribute = (attribute, umlClass, otherClasses, storageSections, mapping, arrayItems) => { if (attribute.attributeType === umlClass_1.AttributeType.Array) { // storage is dynamic if the attribute type ends in [] - const result = attribute.type.match(/\[(\w*)]$/); + const result = attribute.type.match(/\[([\w$.]*)]$/); const dynamic = result[1] === ''; const arrayLength = !dynamic - ? (0, exports.findDimensionLength)(umlClass, result[1]) + ? (0, exports.findDimensionLength)(umlClass, result[1], otherClasses) : undefined; // get the type of the array items. eg // address[][4][2] will have base type address[][4] @@ -181,14 +202,24 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => { } const baseAttribute = { visibility: attribute.visibility, - name: baseType, + name: attribute.name, type: baseType, attributeType: baseAttributeType, }; - const { size: arrayItemSize } = (0, exports.calcStorageByteSize)(baseAttribute, umlClass, otherClasses); + const { size: arrayItemSize, dynamic: dynamicBase } = (0, exports.calcStorageByteSize)(baseAttribute, umlClass, otherClasses); + // If more than 16 bytes, then round up in 32 bytes increments const arraySlotSize = arrayItemSize > 16 ? 32 * Math.ceil(arrayItemSize / 32) : arrayItemSize; + // If base type is not an Elementary type + // This can only be Array and UserDefined for base types of arrays. + let references; + if (baseAttributeType !== umlClass_1.AttributeType.Elementary) { + // recursively add storage section for Array and UserDefined types + references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems); + } + const displayValue = calcDisplayValue(baseAttribute.attributeType, dynamicBase, mapping, references?.storageSection?.type); + const getValue = calcGetValue(attribute.attributeType, mapping); const variables = []; variables[0] = { id: variableId++, @@ -197,58 +228,63 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => { byteSize: arrayItemSize, byteOffset: 0, type: baseType, - dynamic, - noValue: false, + attributeType: baseAttributeType, + dynamic: dynamicBase, + getValue, + displayValue, + referenceSectionId: references?.storageSection?.id, + enumValues: references?.enumValues, }; + // If a fixed size array. + // Note dynamic arrays will have undefined arrayLength if (arrayLength > 1) { - // For fixed length arrays. Dynamic arrays will have undefined arrayLength - for (let i = 1; i < arrayLength; i++) { - variables.push({ - id: variableId++, - fromSlot: Math.floor((i * arraySlotSize) / 32), - toSlot: Math.floor(((i + 1) * arraySlotSize - 1) / 32), - byteSize: arrayItemSize, - byteOffset: (i * arraySlotSize) % 32, - type: baseType, - dynamic, - noValue: false, - }); - } - } - // recursively add storage - if (baseAttributeType !== umlClass_1.AttributeType.Elementary) { - const referenceStorage = (0, exports.parseReferenceStorage)(baseAttribute, umlClass, otherClasses, storages); - variables[0].referenceStorageId = referenceStorage?.id; + // Add missing fixed array variables from index 1 + addArrayVariables(arrayLength, arrayItems, variables); + // For the newly added variables + variables.forEach((variable, i) => { + if (i > 0 && + baseAttributeType !== umlClass_1.AttributeType.Elementary && + variable.type !== '----' // ignore any filler variables + ) { + // recursively add storage section for Array and UserDefined types + references = (0, exports.parseStorageSectionFromAttribute)(baseAttribute, umlClass, otherClasses, storageSections, mapping, arrayItems); + variable.referenceSectionId = references?.storageSection?.id; + variable.enumValues = references?.enumValues; + } + }); } - const newStorage = { + const storageSection = { id: storageId++, name: `${attribute.type}: ${attribute.name}`, - type: StorageType.Array, + type: StorageSectionType.Array, arrayDynamic: dynamic, arrayLength, variables, + mapping, }; - storages.push(newStorage); - return newStorage; + storageSections.push(storageSection); + return { storageSection }; } if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) { // Is the user defined type linked to another Contract, Struct or Enum? - const dependentClass = otherClasses.find(({ name }) => { - return (name === attribute.type || name === attribute.type.split('.')[1]); - }); - if (!dependentClass) { - throw Error(`Failed to find user defined type "${attribute.type}"`); - } - if (dependentClass.stereotype === umlClass_1.ClassStereotype.Struct) { - const variables = parseVariables(dependentClass, otherClasses, [], storages, []); - const newStorage = { + const typeClass = findTypeClass(attribute.type, attribute, otherClasses); + if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) { + const variables = parseVariables(typeClass, otherClasses, [], storageSections, [], mapping, arrayItems); + const storageSection = { id: storageId++, name: attribute.type, - type: StorageType.Struct, + type: StorageSectionType.Struct, variables, + mapping, + }; + storageSections.push(storageSection); + return { storageSection }; + } + else if (typeClass.stereotype === umlClass_1.ClassStereotype.Enum) { + return { + storageSection: undefined, + enumValues: typeClass.attributes.map((a) => a.name), }; - storages.push(newStorage); - return newStorage; } return undefined; } @@ -256,31 +292,105 @@ const parseReferenceStorage = (attribute, umlClass, otherClasses, storages) => { // get the UserDefined type from the mapping // note the mapping could be an array of Structs // Could also be a mapping of a mapping - const result = attribute.type.match(/=\\>((?!mapping)\w*)[\\[]/); + const result = attribute.type.match(/=\\>((?!mapping)[\w$.]*)[\\[]/); // If mapping of user defined type if (result !== null && result[1] && !(0, exports.isElementary)(result[1])) { - // Find UserDefined type - const typeClass = otherClasses.find(({ name }) => name === result[1] || name === result[1].split('.')[1]); - if (!typeClass) { - throw Error(`Failed to find user defined type "${result[1]}" in attribute type "${attribute.type}"`); - } + // Find UserDefined type can be a contract, struct or enum + const typeClass = findTypeClass(result[1], attribute, otherClasses); if (typeClass.stereotype === umlClass_1.ClassStereotype.Struct) { - const variables = parseVariables(typeClass, otherClasses, [], storages, []); - const newStorage = { + let variables = parseVariables(typeClass, otherClasses, [], storageSections, [], true, arrayItems); + const storageSection = { id: storageId++, name: typeClass.name, - type: StorageType.Struct, + type: StorageSectionType.Struct, + mapping: true, variables, }; - storages.push(newStorage); - return newStorage; + storageSections.push(storageSection); + return { storageSection }; } } return undefined; } return undefined; }; -exports.parseReferenceStorage = parseReferenceStorage; +exports.parseStorageSectionFromAttribute = parseStorageSectionFromAttribute; +/** + * Adds missing storage variables to a fixed-size or dynamic array by cloning them from the first variable. + * @param arrayLength the length of the array + * @param arrayItems the number of items to display at the start and end of an array + * @param variables mutable array of storage variables that are appended to + */ +const addArrayVariables = (arrayLength, arrayItems, variables) => { + const arraySlotSize = variables[0].byteSize; + const itemsPerSlot = Math.floor(32 / arraySlotSize); + const slotsPerItem = Math.ceil(arraySlotSize / 32); + const firstFillerItem = itemsPerSlot > 0 ? arrayItems * itemsPerSlot : arrayItems; + const lastFillerItem = itemsPerSlot > 0 + ? arrayLength - + (arrayItems - 1) * itemsPerSlot - // the number of items in all but the last row + (arrayLength % itemsPerSlot || itemsPerSlot) - // the remaining items in the last row or all the items in a slot + 1 // need the items before the last three rows + : arrayLength - arrayItems - 1; + // Add variable from index 1 for each item in the array + for (let i = 1; i < arrayLength; i++) { + const fromSlot = itemsPerSlot > 0 ? Math.floor(i / itemsPerSlot) : i * slotsPerItem; + const toSlot = itemsPerSlot > 0 ? fromSlot : fromSlot + slotsPerItem; + // add filler variable before adding the first of the last items of the array + if (i === lastFillerItem && firstFillerItem < lastFillerItem) { + const fillerFromSlot = itemsPerSlot > 0 + ? Math.floor(firstFillerItem / itemsPerSlot) + : firstFillerItem * slotsPerItem; + variables.push({ + id: variableId++, + attributeType: umlClass_1.AttributeType.UserDefined, + type: '----', + fromSlot: fillerFromSlot, + toSlot: toSlot, + byteOffset: 0, + byteSize: (toSlot - fillerFromSlot + 1) * 32, + getValue: false, + displayValue: false, + dynamic: false, + }); + } + // Add variables for the first arrayItems and last arrayItems + if (i < firstFillerItem || i > lastFillerItem) { + const byteOffset = itemsPerSlot > 0 ? (i % itemsPerSlot) * arraySlotSize : 0; + const slotValue = fromSlot === 0 ? variables[0].slotValue : undefined; + // add array variable + const newVariable = { + ...variables[0], + id: variableId++, + fromSlot, + toSlot, + byteOffset, + slotValue, + // These will be added in a separate step + parsedValue: undefined, + referenceSectionId: undefined, + enumValues: undefined, + }; + newVariable.parsedValue = (0, slotValues_1.parseValue)(newVariable); + variables.push(newVariable); + } + } +}; +/** + * Finds an attribute's user defined type that can be a Contract, Struct or Enum + * @param userType User defined type that is being looked for. This can be the base type of an attribute. + * @param attribute the attribute in the class that is user defined. This is just used for logging purposes + * @param otherClasses + */ +const findTypeClass = (userType, attribute, otherClasses) => { + // Find associated UserDefined type + // TODO this just matches on name and doesn't take into account imports + const typeClass = otherClasses.find(({ name }) => name === userType || name === userType.split('.')[1]); + if (!typeClass) { + throw Error(`Failed to find user defined type "${userType}" in attribute "${attribute.name}" of type "${attribute.attributeType}""`); + } + return typeClass; +}; // Calculates the storage size of an attribute in bytes const calcStorageByteSize = (attribute, umlClass, otherClasses) => { if (attribute.attributeType === umlClass_1.AttributeType.Mapping || @@ -291,7 +401,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { // Fixed sized arrays are read from right to left until there is a dynamic dimension // eg address[][3][2] is a fixed size array that uses 6 slots. // while address [2][] is a dynamic sized array. - const arrayDimensions = attribute.type.match(/\[\w*]/g); + const arrayDimensions = attribute.type.match(/\[[\w$.]*]/g); // Remove first [ and last ] from each arrayDimensions const dimensionsStr = arrayDimensions.map((a) => a.slice(1, -1)); // fixed-sized arrays are read from right to left so reverse the dimensions @@ -300,7 +410,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { let dimension = dimensionsStrReversed.shift(); const fixedDimensions = []; while (dimension && dimension !== '') { - const dimensionNum = (0, exports.findDimensionLength)(umlClass, dimension); + const dimensionNum = (0, exports.findDimensionLength)(umlClass, dimension, otherClasses); fixedDimensions.push(dimensionNum); // read the next dimension for the next loop dimension = dimensionsStrReversed.shift(); @@ -311,9 +421,9 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { // the array length is stored in the 32 byte slot return { size: 32, dynamic: true }; } + // If a fixed sized array let elementSize; const type = attribute.type.substring(0, attribute.type.indexOf('[')); - // If a fixed sized array if ((0, exports.isElementary)(type)) { const elementAttribute = { attributeType: umlClass_1.AttributeType.Elementary, @@ -343,7 +453,11 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { }; } const lastItem = fixedDimensions.length - 1; - const lastDimensionBytes = elementSize * fixedDimensions[lastItem]; + const lastArrayLength = fixedDimensions[lastItem]; + const itemsPerSlot = Math.floor(32 / elementSize); + const lastDimensionBytes = itemsPerSlot > 0 // if one or more array items in a slot + ? Math.ceil(lastArrayLength / itemsPerSlot) * 32 // round up to include unallocated slot space + : elementSize * fixedDimensions[lastItem]; const lastDimensionSlotBytes = Math.ceil(lastDimensionBytes / 32) * 32; const remainingDimensions = fixedDimensions .slice(0, lastItem) @@ -353,16 +467,12 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { dynamic: false, }; } - // If a Struct or Enum + // If a Struct, Enum or Contract reference + // TODO need to handle User Defined Value Types when they are added to Solidity if (attribute.attributeType === umlClass_1.AttributeType.UserDefined) { // Is the user defined type linked to another Contract, Struct or Enum? - const attributeClass = otherClasses.find(({ name }) => { - return (name === attribute.type || name === attribute.type.split('.')[1]); - }); - if (!attributeClass) { - throw Error(`Failed to find user defined struct or enum "${attribute.type}"`); - } - switch (attributeClass.stereotype) { + const attributeTypeClass = findTypeClass(attribute.type, attribute, otherClasses); + switch (attributeTypeClass.stereotype) { case umlClass_1.ClassStereotype.Enum: return { size: 1, dynamic: false }; case umlClass_1.ClassStereotype.Contract: @@ -372,7 +482,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { return { size: 20, dynamic: false }; case umlClass_1.ClassStereotype.Struct: let structByteSize = 0; - attributeClass.attributes.forEach((structAttribute) => { + attributeTypeClass.attributes.forEach((structAttribute) => { // If next attribute is an array, then we need to start in a new slot if (structAttribute.attributeType === umlClass_1.AttributeType.Array) { structByteSize = Math.ceil(structByteSize / 32) * 32; @@ -381,13 +491,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { else if (structAttribute.attributeType === umlClass_1.AttributeType.UserDefined) { // UserDefined types can be a struct or enum, so we need to check if it's a struct - const userDefinedClass = otherClasses.find(({ name }) => { - return (name === structAttribute.type || - name === structAttribute.type.split('.')[1]); - }); - if (!userDefinedClass) { - throw Error(`Failed to find user defined type "${structAttribute.type}" in struct ${attributeClass.name}`); - } + const userDefinedClass = findTypeClass(structAttribute.type, structAttribute, otherClasses); // If a struct if (userDefinedClass.stereotype === umlClass_1.ClassStereotype.Struct) { @@ -422,6 +526,7 @@ const calcStorageByteSize = (attribute, umlClass, otherClasses) => { return { size: 20, dynamic: false }; case 'string': case 'bytes': + return { size: 32, dynamic: true }; case 'uint': case 'int': case 'ufixed': @@ -457,49 +562,206 @@ const isElementary = (type) => { case 'fixed': return true; default: - const result = type.match(/[u]*(int|fixed|bytes)([0-9]+)/); + const result = type.match(/^[u]?(int|fixed|bytes)([0-9]+)$/); return result !== null; } }; exports.isElementary = isElementary; -const calcSlotKey = (variable) => { +const calcSectionOffset = (variable, sectionOffset = '0') => { if (variable.dynamic) { - return (0, utils_1.keccak256)((0, utils_1.toUtf8Bytes)(ethers_1.BigNumber.from(variable.fromSlot).toHexString())); + const hexStringOf32Bytes = (0, utils_1.hexZeroPad)(ethers_1.BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString(), 32); + return (0, utils_1.keccak256)(hexStringOf32Bytes); } - return ethers_1.BigNumber.from(variable.fromSlot).toHexString(); -}; -exports.calcSlotKey = calcSlotKey; -// recursively offset the slots numbers of a storage item -const offsetStorageSlots = (storage, slots, storages) => { - storage.variables.forEach((variable) => { - variable.fromSlot += slots; - variable.toSlot += slots; - if (variable.referenceStorageId) { - // recursively offset the referenced storage - const referenceStorage = storages.find((s) => s.id === variable.referenceStorageId); - if (!referenceStorage.arrayDynamic) { - (0, exports.offsetStorageSlots)(referenceStorage, slots, storages); - } - else { - referenceStorage.slotKey = (0, exports.calcSlotKey)(variable); - } - } - }); + return ethers_1.BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString(); }; -exports.offsetStorageSlots = offsetStorageSlots; -const findDimensionLength = (umlClass, dimension) => { +exports.calcSectionOffset = calcSectionOffset; +const findDimensionLength = (umlClass, dimension, otherClasses) => { const dimensionNum = parseInt(dimension); if (Number.isInteger(dimensionNum)) { return dimensionNum; } - else { - // Try and size array dimension from declared constants - const constant = umlClass.constants.find((constant) => constant.name === dimension); - if (!constant) { - throw Error(`Could not size fixed sized array with dimension "${dimension}"`); - } + // Try and size array dimension from declared constants + const constant = umlClass.constants.find((constant) => constant.name === dimension); + if (constant) { return constant.value; } + // Try and size array dimension from file constants + const fileConstant = otherClasses.find((umlClass) => umlClass.name === dimension && + umlClass.stereotype === umlClass_1.ClassStereotype.Constant); + if (fileConstant?.constants[0]?.value) { + return fileConstant.constants[0].value; + } + throw Error(`Could not size fixed sized array with dimension "${dimension}"`); }; exports.findDimensionLength = findDimensionLength; +/** + * Calculate if the storage slot value for the attribute should be displayed in the storage section. + * + * Storage sections with true mapping should return false. + * Mapping types should return false. + * Elementary types should return true. + * Dynamic Array types should return true. + * Static Array types should return false. + * UserDefined types that are Structs should return false. + * UserDefined types that are Enums or alias to Elementary type or contract should return true. + * + * @param attributeType + * @param dynamic flags if the variable is of dynamic size + * @param mapping flags if the storage section is referenced by a mapping + * @param storageSectionType + * @return displayValue true if the slot value should be displayed. + */ +const calcDisplayValue = (attributeType, dynamic, mapping, storageSectionType) => mapping === false && + (attributeType === umlClass_1.AttributeType.Elementary || + (attributeType === umlClass_1.AttributeType.UserDefined && + storageSectionType !== StorageSectionType.Struct) || + (attributeType === umlClass_1.AttributeType.Array && dynamic)); +/** + * Calculate if the storage slot value for the attribute should be retrieved from the chain. + * + * Storage sections with true mapping should return false. + * Mapping types should return false. + * Elementary types should return true. + * Array types should return true. + * UserDefined should return true. + * + * @param attributeType the type of attribute the storage variable is for. + * @param mapping flags if the storage section is referenced by a mapping + * @return getValue true if the slot value should be retrieved. + */ +const calcGetValue = (attributeType, mapping) => mapping === false && attributeType !== umlClass_1.AttributeType.Mapping; +/** + * Recursively adds variables for dynamic string, bytes or arrays + * @param storageSection + * @param storageSections + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +const addDynamicVariables = async (storageSection, storageSections, url, contractAddress, arrayItems, blockTag) => { + for (const variable of storageSection.variables) { + try { + if (!variable.dynamic) + continue; + // STEP 1 - add slots for dynamic string and bytes + if (variable.type === 'string' || variable.type === 'bytes') { + if (!variable.slotValue) { + debug(`WARNING: Variable "${variable.name}" of type "${variable.type}" has no slot value`); + continue; + } + const size = (0, slotValues_1.dynamicSlotSize)(variable); + if (size > 31) { + const maxSlotNumber = Math.floor((size - 1) / 32); + const variables = []; + // For each dynamic slot + for (let i = 0; i <= maxSlotNumber; i++) { + // If the last slot then get the remaining bytes + const byteSize = i === maxSlotNumber ? size - 32 * maxSlotNumber : 32; + // Add variable for the slot + variables.push({ + id: variableId++, + fromSlot: i, + toSlot: i, + byteSize, + byteOffset: 0, + type: variable.type, + contractName: variable.contractName, + attributeType: umlClass_1.AttributeType.Elementary, + dynamic: false, + getValue: true, + displayValue: true, + }); + } + // add unallocated variable + const unusedBytes = 32 - (size - 32 * maxSlotNumber); + if (unusedBytes > 0) { + const lastVariable = variables[variables.length - 1]; + variables.push({ + ...lastVariable, + byteOffset: unusedBytes, + }); + variables[maxSlotNumber] = { + id: variableId++, + fromSlot: maxSlotNumber, + toSlot: maxSlotNumber, + byteSize: unusedBytes, + byteOffset: 0, + type: 'unallocated', + attributeType: umlClass_1.AttributeType.UserDefined, + contractName: variable.contractName, + name: '', + dynamic: false, + getValue: true, + displayValue: false, + }; + } + const newStorageSection = { + id: storageId++, + name: `${variable.type}: ${variable.name}`, + offset: (0, exports.calcSectionOffset)(variable, storageSection.offset), + type: variable.type === 'string' + ? StorageSectionType.String + : StorageSectionType.Bytes, + arrayDynamic: true, + arrayLength: size, + variables, + mapping: false, + }; + variable.referenceSectionId = newStorageSection.id; + // get slot values for new referenced dynamic string or bytes + await (0, slotValues_1.addSlotValues)(url, contractAddress, newStorageSection, arrayItems, blockTag); + storageSections.push(newStorageSection); + } + continue; + } + if (variable.attributeType !== umlClass_1.AttributeType.Array) + continue; + // STEP 2 - add slots for dynamic arrays + // find storage section that the variable is referencing + const referenceStorageSection = storageSections.find((ss) => ss.id === variable.referenceSectionId); + if (!referenceStorageSection) + continue; + // recursively add dynamic variables to referenced array. + // this could be a fixed-size or dynamic array + await (0, exports.addDynamicVariables)(referenceStorageSection, storageSections, url, contractAddress, arrayItems, blockTag); + if (!variable.slotValue) { + debug(`WARNING: Dynamic array variable "${variable.name}" of type "${variable.type}" has no slot value`); + continue; + } + // Add missing dynamic array variables + const arrayLength = ethers_1.BigNumber.from(variable.slotValue).toNumber(); + if (arrayLength > 1) { + // Add missing array variables to the referenced dynamic array + addArrayVariables(arrayLength, arrayItems, referenceStorageSection.variables); + // // For the newly added variables + // referenceStorageSection.variables.forEach((variable, i) => { + // if ( + // referenceStorageSection.variables[0].attributeType !== + // AttributeType.Elementary && + // i > 0 + // ) { + // // recursively add storage section for Array and UserDefined types + // const references = parseStorageSectionFromAttribute( + // baseAttribute, + // umlClass, + // otherClasses, + // storageSections, + // mapping, + // arrayItems + // ) + // variable.referenceSectionId = references.storageSection?.id + // variable.enumValues = references?.enumValues + // } + // }) + } + // Get missing slot values to the referenced dynamic array + await (0, slotValues_1.addSlotValues)(url, contractAddress, referenceStorageSection, arrayItems, blockTag); + } + catch (err) { + throw Error(`Failed to add dynamic vars for section "${storageSection.name}", var type "${variable.type}" with value "${variable.slotValue}" from slot ${variable.fromSlot} and section offset ${storageSection.offset}`, { cause: err }); + } + } +}; +exports.addDynamicVariables = addDynamicVariables; //# sourceMappingURL=converterClasses2Storage.js.map \ No newline at end of file diff --git a/lib/converterStorage2Dot.d.ts b/lib/converterStorage2Dot.d.ts index 3c63b6ba..b87fdc1f 100644 --- a/lib/converterStorage2Dot.d.ts +++ b/lib/converterStorage2Dot.d.ts @@ -1,7 +1,11 @@ -import { Storage } from './converterClasses2Storage'; -export declare const convertStorages2Dot: (storages: Storage[], options: { +import { StorageSection } from './converterClasses2Storage'; +export declare const convertStorages2Dot: (storageSections: readonly StorageSection[], options: { data: boolean; + backColor: string; + shapeColor: string; + fillColor: string; + textColor: string; }) => string; -export declare function convertStorage2Dot(storage: Storage, dotString: string, options: { +export declare function convertStorage2Dot(storageSection: StorageSection, dotString: string, options: { data: boolean; }): string; diff --git a/lib/converterStorage2Dot.js b/lib/converterStorage2Dot.js index 31c11321..99d8a384 100644 --- a/lib/converterStorage2Dot.js +++ b/lib/converterStorage2Dot.js @@ -2,23 +2,25 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.convertStorage2Dot = exports.convertStorages2Dot = void 0; const converterClasses2Storage_1 = require("./converterClasses2Storage"); +const umlClass_1 = require("./umlClass"); const debug = require('debug')('sol2uml'); -const convertStorages2Dot = (storages, options) => { +const convertStorages2Dot = (storageSections, options) => { let dotString = ` digraph StorageDiagram { rankdir=LR -color=black arrowhead=open -node [shape=record, style=filled, fillcolor=gray95 fontname="Courier New"]`; +bgcolor="${options.backColor}" +edge [color="${options.shapeColor}"] +node [shape=record, style=filled, color="${options.shapeColor}", fillcolor="${options.fillColor}", fontcolor="${options.textColor}", fontname="Courier New"]`; // process contract and the struct storages - storages.forEach((storage) => { + storageSections.forEach((storage) => { dotString = convertStorage2Dot(storage, dotString, options); }); // link contract and structs to structs - storages.forEach((slot) => { + storageSections.forEach((slot) => { slot.variables.forEach((storage) => { - if (storage.referenceStorageId) { - dotString += `\n ${slot.id}:${storage.id} -> ${storage.referenceStorageId}`; + if (storage.referenceSectionId) { + dotString += `\n ${slot.id}:${storage.id} -> ${storage.referenceSectionId}`; } }); }); @@ -28,34 +30,50 @@ node [shape=record, style=filled, fillcolor=gray95 fontname="Courier New"]`; return dotString; }; exports.convertStorages2Dot = convertStorages2Dot; -function convertStorage2Dot(storage, dotString, options) { +function convertStorage2Dot(storageSection, dotString, options) { // write storage header with name and optional address - dotString += `\n${storage.id} [label="${storage.name} \\<\\<${storage.type}\\>\\>\\n${storage.address || storage.slotKey || ''}`; + dotString += `\n${storageSection.id} [label="${storageSection.name} \\<\\<${storageSection.type}\\>\\>\\n${storageSection.address || storageSection.offset || ''}`; dotString += ' | {'; - const startingVariables = storage.variables.filter((s) => s.byteOffset === 0); + const startingVariables = storageSection.variables.filter((s) => s.byteOffset === 0); + // for each slot displayed, does is have any variables with parsed data? + const displayData = startingVariables.map((startVar) => storageSection.variables.some((variable) => variable.fromSlot === startVar.fromSlot && variable.parsedValue)); + const linePad = '\\n\\ '; // write slot numbers - dotString += '{ slot'; + const dataLine = options.data ? linePad : ''; + dotString += + storageSection.offset || storageSection.mapping + ? `{ offset${dataLine}` + : `{ slot${dataLine}`; startingVariables.forEach((variable, i) => { + const dataLine = options.data && displayData[i] ? linePad : ''; if (variable.fromSlot === variable.toSlot) { - dotString += `| ${variable.fromSlot} `; + dotString += ` | ${variable.fromSlot}${dataLine}`; } else { - dotString += `| ${variable.fromSlot}-${variable.toSlot} `; + dotString += ` | ${variable.fromSlot}-${variable.toSlot}${dataLine}`; } }); // write slot values if available if (options.data) { - dotString += '} | {value'; + dotString += `} | {value${dataLine}`; startingVariables.forEach((variable, i) => { - dotString += ` | ${variable.value || ''}`; + if (displayData[i]) { + dotString += ` | ${variable.slotValue || ''}${linePad}`; + } + else { + dotString += ` | `; + } }); } - const contractVariablePrefix = storage.type === converterClasses2Storage_1.StorageType.Contract ? '\\.' : ''; - dotString += `} | { type: ${contractVariablePrefix}variable (bytes)`; + const contractVariablePrefix = storageSection.type === converterClasses2Storage_1.StorageSectionType.Contract + ? '\\.' + : ''; + const dataLine2 = options.data ? `\\ndecoded data` : ''; + dotString += `} | { type: ${contractVariablePrefix}variable (bytes)${dataLine2}`; // For each slot startingVariables.forEach((variable) => { // Get all the storage variables in this slot - const slotVariables = storage.variables.filter((s) => s.fromSlot === variable.fromSlot); + const slotVariables = storageSection.variables.filter((s) => s.fromSlot === variable.fromSlot); const usedBytes = slotVariables.reduce((acc, s) => acc + s.byteSize, 0); if (usedBytes < 32) { // Create an unallocated variable for display purposes @@ -66,20 +84,22 @@ function convertStorage2Dot(storage, dotString, options) { byteSize: 32 - usedBytes, byteOffset: usedBytes, type: 'unallocated', + attributeType: umlClass_1.AttributeType.UserDefined, dynamic: false, - noValue: true, + displayValue: false, + getValue: false, contractName: variable.contractName, - variable: '', + name: '', }); } const slotVariablesReversed = slotVariables.reverse(); // For each variable in the slot slotVariablesReversed.forEach((variable, i) => { if (i === 0) { - dotString += ` | { ${dotVariable(variable, storage.name)} `; + dotString += ` | { ${dotVariable(variable, storageSection.name)} `; } else { - dotString += ` | ${dotVariable(variable, storage.name)} `; + dotString += ` | ${dotVariable(variable, storageSection.name)} `; } }); dotString += '}'; @@ -89,12 +109,17 @@ function convertStorage2Dot(storage, dotString, options) { return dotString; } exports.convertStorage2Dot = convertStorage2Dot; -const dotVariable = (storage, contractName) => { - const port = storage.referenceStorageId !== undefined ? `<${storage.id}>` : ''; - const contractNamePrefix = storage.contractName !== contractName ? `${storage.contractName}.` : ''; - const variable = storage.variable - ? `: ${contractNamePrefix}${storage.variable}` +const dotVariable = (variable, contractName) => { + const port = variable.referenceSectionId !== undefined ? `<${variable.id}>` : ''; + const contractNamePrefix = variable.contractName !== contractName + ? `${variable.contractName}.` + : ''; + const variableValue = variable.parsedValue + ? `\\n\\ ${variable.parsedValue}` + : ''; + const variableName = variable.name + ? `: ${contractNamePrefix}${variable.name}` : ''; - return `${port} ${storage.type}${variable} (${storage.byteSize})`; + return `${port} ${variable.type}${variableName} (${variable.byteSize})${variableValue}`; }; //# sourceMappingURL=converterStorage2Dot.js.map \ No newline at end of file diff --git a/lib/filterClasses.d.ts b/lib/filterClasses.d.ts index 036c5bc2..27c94cfc 100644 --- a/lib/filterClasses.d.ts +++ b/lib/filterClasses.d.ts @@ -7,7 +7,7 @@ import { ClassOptions } from './converterClass2Dot'; * @param options sol2uml class options * @return umlClasses filtered list of UML classes of type `UMLClass` */ -export declare const filterHiddenClasses: (umlClasses: UmlClass[], options: ClassOptions) => UmlClass[]; +export declare const filterHiddenClasses: (umlClasses: readonly UmlClass[], options: ClassOptions) => UmlClass[]; /** * Finds all the UML classes that have an association with a list of base contract names. * The associated classes can be contracts, abstract contracts, interfaces, libraries, enums, structs or constants. @@ -16,7 +16,7 @@ export declare const filterHiddenClasses: (umlClasses: UmlClass[], options: Clas * @param depth limit the number of associations from the base contract. * @return filteredUmlClasses list of UML classes of type `UMLClass` */ -export declare const classesConnectedToBaseContracts: (umlClasses: UmlClass[], baseContractNames: string[], depth?: number) => UmlClass[]; +export declare const classesConnectedToBaseContracts: (umlClasses: readonly UmlClass[], baseContractNames: readonly string[], depth?: number) => UmlClass[]; /** * Finds all the UML classes that have an association with a base contract name. * The associated classes can be contracts, abstract contracts, interfaces, libraries, enums, structs or constants. @@ -26,7 +26,7 @@ export declare const classesConnectedToBaseContracts: (umlClasses: UmlClass[], b * @param depth limit the number of associations from the base contract. * @return filteredUmlClasses list of UML classes of type `UMLClass` */ -export declare const classesConnectedToBaseContract: (umlClasses: UmlClass[], baseContractName: string, weightedDirectedGraph: WeightedDiGraph, depth?: number) => { +export declare const classesConnectedToBaseContract: (umlClasses: readonly UmlClass[], baseContractName: string, weightedDirectedGraph: WeightedDiGraph, depth?: number) => { [contractName: string]: UmlClass; }; -export declare const topologicalSortClasses: (umlClasses: UmlClass[]) => UmlClass[]; +export declare const topologicalSortClasses: (umlClasses: readonly UmlClass[]) => UmlClass[]; diff --git a/lib/filterClasses.js b/lib/filterClasses.js index 177b3743..a87ab845 100644 --- a/lib/filterClasses.js +++ b/lib/filterClasses.js @@ -4,6 +4,7 @@ exports.topologicalSortClasses = exports.classesConnectedToBaseContract = export const js_graph_algorithms_1 = require("js-graph-algorithms"); const umlClass_1 = require("./umlClass"); const associations_1 = require("./associations"); +const debug = require('debug')('sol2uml'); /** * Filter out any UML Class types that are to be hidden. * @param umlClasses array of UML classes of type `UMLClass` @@ -85,7 +86,7 @@ function loadWeightedDirectedGraph(umlClasses) { continue; } const isTarget = umlClasses.find((u) => u.id === targetUmlClass.id); - console.log(`isTarget ${isTarget} Adding edge from ${sourceUmlClass.name} with id ${sourceUmlClass.id} to ${targetUmlClass.name} with id ${targetUmlClass.id} and type ${targetUmlClass.stereotype}`); + debug(`isTarget ${isTarget} Adding edge from ${sourceUmlClass.name} with id ${sourceUmlClass.id} to ${targetUmlClass.name} with id ${targetUmlClass.id} and type ${targetUmlClass.stereotype}`); weightedDirectedGraph.addEdge(new js_graph_algorithms_1.Edge(sourceUmlClass.id, targetUmlClass.id, 1)); } } diff --git a/lib/parserEtherscan.d.ts b/lib/parserEtherscan.d.ts index 2c355778..30c30b24 100644 --- a/lib/parserEtherscan.d.ts +++ b/lib/parserEtherscan.d.ts @@ -1,7 +1,11 @@ import { ASTNode } from '@solidity-parser/parser/dist/src/ast-types'; import { UmlClass } from './umlClass'; +export interface Remapping { + from: RegExp; + to: string; +} export declare const networks: readonly ["mainnet", "ropsten", "kovan", "rinkeby", "goerli", "sepolia", "polygon", "testnet.polygon", "arbitrum", "testnet.arbitrum", "avalanche", "testnet.avalanche", "bsc", "testnet.bsc", "crono", "fantom", "testnet.fantom", "moonbeam", "optimistic", "kovan-optimistic", "gnosisscan"]; -export type Network = typeof networks[number]; +export type Network = (typeof networks)[number]; export declare class EtherscanParser { protected apikey: string; network: Network; @@ -37,11 +41,25 @@ export declare class EtherscanParser { * @param contractAddress Ethereum contract address with a 0x prefix */ getSourceCode(contractAddress: string): Promise<{ - files: { + files: readonly { code: string; filename: string; }[]; contractName: string; compilerVersion: string; + remappings: Remapping[]; }>; } +/** + * Parses Ethersan's remappings config in its API response + * @param rawMappings + */ +export declare const parseRemappings: (rawMappings: string[]) => Remapping[]; +/** + * Parses a single mapping. For example + * "@openzeppelin/=lib/openzeppelin-contracts/" + * This is from Uniswap's UniversalRouter in the Settings section after the source files + * https://etherscan.io/address/0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B#code + * @param mapping + */ +export declare const parseRemapping: (mapping: string) => Remapping; diff --git a/lib/parserEtherscan.js b/lib/parserEtherscan.js index bd9f38d6..54b5bdd0 100644 --- a/lib/parserEtherscan.js +++ b/lib/parserEtherscan.js @@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.EtherscanParser = exports.networks = void 0; +exports.parseRemapping = exports.parseRemappings = exports.EtherscanParser = exports.networks = void 0; const axios_1 = __importDefault(require("axios")); const parser_1 = require("@solidity-parser/parser"); const converterAST2Classes_1 = require("./converterAST2Classes"); @@ -110,12 +110,12 @@ class EtherscanParser { * @return Promise with an array of UmlClass objects */ async getUmlClasses(contractAddress) { - const { files, contractName } = await this.getSourceCode(contractAddress); + const { files, contractName, remappings } = await this.getSourceCode(contractAddress); let umlClasses = []; for (const file of files) { debug(`Parsing source file ${file.filename}`); const node = await this.parseSourceCode(file.code); - const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, file.filename); + const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, file.filename, remappings); umlClasses = umlClasses.concat(umlClass); } return { @@ -130,12 +130,12 @@ class EtherscanParser { * @return Promise string of Solidity code */ async getSolidityCode(contractAddress) { - const { files, contractName, compilerVersion } = await this.getSourceCode(contractAddress); + const { files, contractName, compilerVersion, remappings } = await this.getSourceCode(contractAddress); // Parse the UmlClasses from the Solidity code in each file let umlClasses = []; for (const file of files) { const node = await this.parseSourceCode(file.code); - const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, file.filename); + const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, file.filename, remappings); umlClasses = umlClasses.concat(umlClass); } // Sort the classes so dependent code is first @@ -209,6 +209,7 @@ class EtherscanParser { if (!Array.isArray(response?.data?.result)) { throw new Error(`Failed to ${description}. No result array in HTTP data: ${JSON.stringify(response?.data)}`); } + let remappings; const results = response.data.result.map((result) => { if (!result.SourceCode) { throw new Error(`Failed to ${description}. Most likely the contract has not been verified on Etherscan.`); @@ -223,6 +224,8 @@ class EtherscanParser { parableResultString = result.SourceCode.slice(1, -1); } const sourceCodeObject = JSON.parse(parableResultString); + // Get any remapping of filenames from the settings + remappings = (0, exports.parseRemappings)(sourceCodeObject.settings?.remappings); // The getsource response from Etherscan is inconsistent so we need to handle both shapes const sourceFiles = sourceCodeObject.sources ? Object.entries(sourceCodeObject.sources) @@ -239,6 +242,8 @@ class EtherscanParser { // if multiple Solidity source files with no Etherscan bug in the SourceCode field if (result?.SourceCode?.sources) { const sourceFiles = Object.values(result.SourceCode.sources); + // Get any remapping of filenames from the settings + remappings = (0, exports.parseRemappings)(result.SourceCode.settings?.remappings); return sourceFiles.map(([filename, code]) => ({ code: code.content, filename, @@ -254,6 +259,7 @@ class EtherscanParser { files: results.flat(1), contractName: response.data.result[0].ContractName, compilerVersion: response.data.result[0].CompilerVersion, + remappings, }; } catch (err) { @@ -268,4 +274,31 @@ class EtherscanParser { } } exports.EtherscanParser = EtherscanParser; +/** + * Parses Ethersan's remappings config in its API response + * @param rawMappings + */ +const parseRemappings = (rawMappings) => { + if (!rawMappings) + return []; + return rawMappings.map((mapping) => (0, exports.parseRemapping)(mapping)); +}; +exports.parseRemappings = parseRemappings; +/** + * Parses a single mapping. For example + * "@openzeppelin/=lib/openzeppelin-contracts/" + * This is from Uniswap's UniversalRouter in the Settings section after the source files + * https://etherscan.io/address/0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B#code + * @param mapping + */ +const parseRemapping = (mapping) => { + const equalIndex = mapping.indexOf('='); + const from = mapping.slice(0, equalIndex); + const to = mapping.slice(equalIndex + 1); + return { + from: new RegExp('^' + from), + to, + }; +}; +exports.parseRemapping = parseRemapping; //# sourceMappingURL=parserEtherscan.js.map \ No newline at end of file diff --git a/lib/parserFiles.d.ts b/lib/parserFiles.d.ts index 9f60fdf8..3fbcb19f 100644 --- a/lib/parserFiles.d.ts +++ b/lib/parserFiles.d.ts @@ -1,6 +1,6 @@ import { ASTNode } from '@solidity-parser/parser/dist/src/ast-types'; import { UmlClass } from './umlClass'; -export declare const parseUmlClassesFromFiles: (filesOrFolders: string[], ignoreFilesOrFolders: string[], subfolders?: number) => Promise; -export declare function getSolidityFilesFromFolderOrFiles(folderOrFilePaths: string[], ignoreFilesOrFolders: string[], subfolders?: number): Promise; -export declare function getSolidityFilesFromFolderOrFile(folderOrFilePath: string, ignoreFilesOrFolders?: string[], depthLimit?: number): Promise; +export declare const parseUmlClassesFromFiles: (filesOrFolders: readonly string[], ignoreFilesOrFolders: readonly string[], subfolders?: number) => Promise; +export declare function getSolidityFilesFromFolderOrFiles(folderOrFilePaths: readonly string[], ignoreFilesOrFolders: readonly string[], subfolders?: number): Promise; +export declare function getSolidityFilesFromFolderOrFile(folderOrFilePath: string, ignoreFilesOrFolders?: readonly string[], depthLimit?: number): Promise; export declare function parseSolidityFile(fileName: string): ASTNode; diff --git a/lib/parserFiles.js b/lib/parserFiles.js index b4d7aaf3..01725dfe 100644 --- a/lib/parserFiles.js +++ b/lib/parserFiles.js @@ -16,7 +16,7 @@ const parseUmlClassesFromFiles = async (filesOrFolders, ignoreFilesOrFolders, su for (const file of files) { const node = await parseSolidityFile(file); const relativePath = (0, path_1.relative)(process.cwd(), file); - const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, relativePath, true); + const umlClass = (0, converterAST2Classes_1.convertAST2UmlClasses)(node, relativePath, [], true); umlClasses = umlClasses.concat(umlClass); } return umlClasses; @@ -82,7 +82,7 @@ function getSolidityFilesFromFolderOrFile(folderOrFilePath, ignoreFilesOrFolders else { error = new Error(`Failed to get Solidity files under folder or file ${folderOrFilePath}`, { cause: err }); } - console.error(error.stack); + console.error(error); reject(error); } }); diff --git a/lib/slotValues.d.ts b/lib/slotValues.d.ts index 9d063345..a67e62b1 100644 --- a/lib/slotValues.d.ts +++ b/lib/slotValues.d.ts @@ -1,19 +1,49 @@ import { BigNumberish } from '@ethersproject/bignumber'; +import { StorageSection, Variable } from './converterClasses2Storage'; +/** + * Adds the slot values to the variables in the storage section. + * This can be rerun for a section as it will only get if the slot value + * does not exist. + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * If contract is proxied, use proxy and not the implementation contract. + * @param storageSection is mutated with the slot values added to the variables + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +export declare const addSlotValues: (url: string, contractAddress: string, storageSection: StorageSection, arrayItems: number, blockTag: BigNumberish) => Promise; +export declare const parseValue: (variable: Variable) => string; /** * Get storage slot values from JSON-RPC API provider. * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slots array of slot numbers to retrieve values for. + * @param slotKeys array of 32 byte slot keys as BigNumbers. * @param blockTag block number or `latest` + * @return slotValues array of 32 byte slot values as hexadecimal strings */ -export declare const getStorageValues: (url: string, contractAddress: string, slots: BigNumberish[], blockTag?: BigNumberish | 'latest') => Promise; +export declare const getSlotValues: (url: string, contractAddress: string, slotKeys: readonly BigNumberish[], blockTag?: BigNumberish | 'latest') => Promise; /** * Get storage slot values from JSON-RPC API provider. * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slot slot number to retrieve the value for. + * @param slotKey 32 byte slot key as a BigNumber. * @param blockTag block number or `latest` + * @return slotValue 32 byte slot value as hexadecimal string + */ +export declare const getSlotValue: (url: string, contractAddress: string, slotKey: BigNumberish, blockTag: BigNumberish | 'latest') => Promise; +/** + * Calculates the number of string characters or bytes of a string or bytes type. + * See the following for how string and bytes are stored in storage slots + * https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#bytes-and-string + * @param variable the variable with the slotValue that is being sized + * @return bytes the number of bytes of the dynamic slot. If static, zero is return. */ -export declare const getStorageValue: (url: string, contractAddress: string, slot: BigNumberish, blockTag?: BigNumberish | 'latest') => Promise; +export declare const dynamicSlotSize: (variable: { + name?: string; + type?: string; + slotValue?: string; +}) => number; +export declare const convert2String: (bytes: string) => string; +export declare const escapeString: (text: string) => string; diff --git a/lib/slotValues.js b/lib/slotValues.js index 2c3afe44..392fbcc4 100644 --- a/lib/slotValues.js +++ b/lib/slotValues.js @@ -3,65 +3,286 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getStorageValue = exports.getStorageValues = void 0; +exports.escapeString = exports.convert2String = exports.dynamicSlotSize = exports.getSlotValue = exports.getSlotValues = exports.parseValue = exports.addSlotValues = void 0; const bignumber_1 = require("@ethersproject/bignumber"); const axios_1 = __importDefault(require("axios")); +const umlClass_1 = require("./umlClass"); +const utils_1 = require("ethers/lib/utils"); +const SlotValueCache_1 = require("./SlotValueCache"); const debug = require('debug')('sol2uml'); +/** + * Adds the slot values to the variables in the storage section. + * This can be rerun for a section as it will only get if the slot value + * does not exist. + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * If contract is proxied, use proxy and not the implementation contract. + * @param storageSection is mutated with the slot values added to the variables + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +const addSlotValues = async (url, contractAddress, storageSection, arrayItems, blockTag) => { + const valueVariables = storageSection.variables.filter((variable) => variable.getValue && !variable.slotValue); + if (valueVariables.length === 0) + return; + // for each variable, add all the slots used by the variable. + const slots = []; + valueVariables.forEach((variable) => { + for (let i = 0; variable.fromSlot + i <= variable.toSlot; i++) { + if (variable.attributeType === umlClass_1.AttributeType.Array && + i >= arrayItems && + i < variable.toSlot - arrayItems) { + continue; + } + slots.push(variable.fromSlot + i); + } + }); + // remove duplicate slot numbers + const uniqueFromSlots = [...new Set(slots)]; + // Convert slot numbers to BigNumbers and offset dynamic arrays + let slotKeys = uniqueFromSlots.map((fromSlot) => { + if (storageSection.offset) { + return bignumber_1.BigNumber.from(storageSection.offset).add(fromSlot); + } + return bignumber_1.BigNumber.from(fromSlot); + }); + // Get the contract slot values from the node provider + const values = await (0, exports.getSlotValues)(url, contractAddress, slotKeys, blockTag); + // For each slot value retrieved + values.forEach((value, i) => { + // Get the corresponding slot number for the slot value + const fromSlot = uniqueFromSlots[i]; + // For each variable in the storage section + for (const variable of storageSection.variables) { + if (variable.getValue && variable.fromSlot === fromSlot) { + debug(`Set slot value ${value} for section "${storageSection.name}", var type ${variable.type}, slot ${variable.fromSlot} offset ${storageSection.offset}`); + variable.slotValue = value; + // parse variable value from slot data + if (variable.displayValue) { + variable.parsedValue = (0, exports.parseValue)(variable); + } + } + // if variable is past the slot that has the value + else if (variable.toSlot > fromSlot) { + break; + } + } + }); +}; +exports.addSlotValues = addSlotValues; +const parseValue = (variable) => { + if (!variable.slotValue) + return undefined; + const start = 66 - (variable.byteOffset + variable.byteSize) * 2; + const end = 66 - variable.byteOffset * 2; + const variableValue = variable.slotValue.substring(start, end); + try { + // Contracts, structs and enums + if (variable.attributeType === umlClass_1.AttributeType.UserDefined) { + return parseUserDefinedValue(variable, variableValue); + } + if (variable.attributeType === umlClass_1.AttributeType.Elementary) + return parseElementaryValue(variable, variableValue); + // dynamic arrays + if (variable.attributeType === umlClass_1.AttributeType.Array && + variable.dynamic) { + return (0, utils_1.formatUnits)('0x' + variableValue, 0); + } + return undefined; + } + catch (err) { + throw Error(`Failed to parse variable ${variable.name} of type ${variable.type}, value "${variableValue}"`, { cause: err }); + } +}; +exports.parseValue = parseValue; +const parseUserDefinedValue = (variable, variableValue) => { + // TODO need to handle User Defined Value Types introduced in Solidity + // https://docs.soliditylang.org/en/v0.8.18/types.html#user-defined-value-types + // https://blog.soliditylang.org/2021/09/27/user-defined-value-types/ + // using byteSize is crude and will be incorrect for aliases types like int160 or uint160 + if (variable.byteSize === 20) { + return (0, utils_1.getAddress)('0x' + variableValue); + } + // this will also be wrong if the alias is to a 1 byte type. eg bytes1, int8 or uint8 + if (variable.byteSize === 1) { + // assume 1 byte is an enum so convert value to enum index number + const index = bignumber_1.BigNumber.from('0x' + variableValue).toNumber(); + // lookup enum value if its available + return variable?.enumValues ? variable?.enumValues[index] : undefined; + } + // we don't parse if a struct which has a size of 32 bytes + return undefined; +}; +const parseElementaryValue = (variable, variableValue) => { + // Elementary types + if (variable.type === 'bool') { + if (variableValue === '00') + return 'false'; + if (variableValue === '01') + return 'true'; + throw Error(`Failed to parse bool variable "${variable.name}" in slot ${variable.fromSlot}, offset ${variable.byteOffset} and slot value "${variableValue}"`); + } + if (variable.type === 'string' || variable.type === 'bytes') { + if (variable.dynamic) { + const lastByte = variable.slotValue.slice(-2); + const size = bignumber_1.BigNumber.from('0x' + lastByte); + // Check if the last bit is set by AND the size with 0x01 + if (size.and(1).eq(1)) { + // Return the number of chars or bytes + return bignumber_1.BigNumber.from(variable.slotValue) + .sub(1) + .div(2) + .toString(); + } + // The last byte holds the length of the string or bytes in the slot + const valueHex = '0x' + variableValue.slice(0, size.toNumber()); + if (variable.type === 'bytes') + return valueHex; + return `\\"${(0, exports.convert2String)(valueHex)}\\"`; + } + if (variable.type === 'bytes') + return '0x' + variableValue; + return `\\"${(0, exports.convert2String)('0x' + variableValue)}\\"`; + } + if (variable.type === 'address') { + return (0, utils_1.getAddress)('0x' + variableValue); + } + if (variable.type.match(/^uint([0-9]*)$/)) { + const parsedValue = (0, utils_1.formatUnits)('0x' + variableValue, 0); + return (0, utils_1.commify)(parsedValue); + } + if (variable.type.match(/^bytes([0-9]+)$/)) { + return '0x' + variableValue; + } + if (variable.type.match(/^int([0-9]*)/)) { + // parse variable value as an unsigned number + let rawValue = bignumber_1.BigNumber.from('0x' + variableValue); + // parse the number of bits + const result = variable.type.match(/^int([0-9]*$)/); + const bitSize = result[1] ? result[1] : 256; + // Convert the number of bits to the number of hex characters + const hexSize = bignumber_1.BigNumber.from(bitSize).div(4).toNumber(); + // bit mask has a leading 1 and the rest 0. 0x8 = 1000 binary + const mask = '0x80' + '0'.repeat(hexSize - 2); + // is the first bit a 1? + const negative = rawValue.and(mask); + if (negative.gt(0)) { + // Convert unsigned number to a signed negative + const negativeOne = '0xFF' + 'F'.repeat(hexSize - 2); + rawValue = bignumber_1.BigNumber.from(negativeOne).sub(rawValue).add(1).mul(-1); + } + const parsedValue = (0, utils_1.formatUnits)(rawValue, 0); + return (0, utils_1.commify)(parsedValue); + } + // add fixed point numbers when they are supported by Solidity + return undefined; +}; let jsonRpcId = 0; /** * Get storage slot values from JSON-RPC API provider. * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slots array of slot numbers to retrieve values for. + * @param slotKeys array of 32 byte slot keys as BigNumbers. * @param blockTag block number or `latest` + * @return slotValues array of 32 byte slot values as hexadecimal strings */ -const getStorageValues = async (url, contractAddress, slots, blockTag = 'latest') => { +const getSlotValues = async (url, contractAddress, slotKeys, blockTag = 'latest') => { try { - debug(`About to get ${slots.length} storage values for ${contractAddress} at block ${blockTag}`); + if (slotKeys.length === 0) { + return []; + } const block = blockTag === 'latest' ? blockTag - : bignumber_1.BigNumber.from(blockTag).toHexString(); - const payload = slots.map((slot) => ({ + : (0, utils_1.hexValue)(bignumber_1.BigNumber.from(blockTag)); + // get cached values and missing slot keys from from cache + const { cachedValues, missingKeys } = SlotValueCache_1.SlotValueCache.readSlotValues(slotKeys); + // If all values are in the cache then just return the cached values + if (missingKeys.length === 0) { + return cachedValues; + } + debug(`About to get ${slotKeys.length} storage values for ${contractAddress} at block ${blockTag} from slot ${missingKeys[0].toString()}`); + // Get the values for the missing slot keys + const payload = missingKeys.map((key) => ({ id: (jsonRpcId++).toString(), jsonrpc: '2.0', method: 'eth_getStorageAt', - params: [ - contractAddress, - bignumber_1.BigNumber.from(slot).toHexString(), - block, - ], + params: [contractAddress, key, block], })); const response = await axios_1.default.post(url, payload); - console.log(response.data); if (response.data?.error?.message) { - throw new Error(response.data.error.message); + throw Error(response.data.error.message); } - if (response.data.length !== slots.length) { - throw new Error(`Requested ${slots.length} storage slot values but only got ${response.data.length}`); + if (response.data.length !== missingKeys.length) { + throw Error(`Requested ${missingKeys.length} storage slot values but only got ${response.data.length}`); } const responseData = response.data; const sortedResponses = responseData.sort((a, b) => bignumber_1.BigNumber.from(a.id).gt(b.id) ? 1 : -1); - return sortedResponses.map((data) => '0x' + data.result.toUpperCase().slice(2)); + const missingValues = sortedResponses.map((data) => { + if (data.error) { + throw Error(`json rpc call with id ${data.id} failed to get storage values: ${data.error?.message}`); + } + return '0x' + data.result.toUpperCase().slice(2); + }); + // add new values to the cache and return the merged slot values + return SlotValueCache_1.SlotValueCache.addSlotValues(slotKeys, missingKeys, missingValues); } catch (err) { - throw new Error(`Failed to get ${slots.length} storage values for ${contractAddress} from ${url}`, { cause: err }); + throw Error(`Failed to get ${slotKeys.length} storage values for contract ${contractAddress} from ${url}`, { cause: err }); } }; -exports.getStorageValues = getStorageValues; +exports.getSlotValues = getSlotValues; /** * Get storage slot values from JSON-RPC API provider. * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slot slot number to retrieve the value for. + * @param slotKey 32 byte slot key as a BigNumber. * @param blockTag block number or `latest` + * @return slotValue 32 byte slot value as hexadecimal string */ -const getStorageValue = async (url, contractAddress, slot, blockTag = 'latest') => { - debug(`About to get storage slot ${slot} value for ${contractAddress}`); - const values = await (0, exports.getStorageValues)(url, contractAddress, [slot], blockTag); - debug(`Got slot ${slot} value: ${values[0]}`); +const getSlotValue = async (url, contractAddress, slotKey, blockTag) => { + debug(`About to get storage slot ${slotKey} value for ${contractAddress}`); + const values = await (0, exports.getSlotValues)(url, contractAddress, [slotKey], blockTag); return values[0]; }; -exports.getStorageValue = getStorageValue; +exports.getSlotValue = getSlotValue; +/** + * Calculates the number of string characters or bytes of a string or bytes type. + * See the following for how string and bytes are stored in storage slots + * https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#bytes-and-string + * @param variable the variable with the slotValue that is being sized + * @return bytes the number of bytes of the dynamic slot. If static, zero is return. + */ +const dynamicSlotSize = (variable) => { + try { + if (!variable?.slotValue) + throw Error(`Missing slot value.`); + const last4bits = '0x' + variable.slotValue.slice(-1); + const last4bitsNum = bignumber_1.BigNumber.from(last4bits).toNumber(); + // If the last 4 bits is an even number then it's not a dynamic slot + if (last4bitsNum % 2 === 0) + return 0; + const sizeRaw = bignumber_1.BigNumber.from(variable.slotValue).toNumber(); + // Adjust the size to bytes + return (sizeRaw - 1) / 2; + } + catch (err) { + throw Error(`Failed to calculate dynamic slot size for variable "${variable?.name}" of type "${variable?.type}" with slot value ${variable?.slotValue}`, { cause: err }); + } +}; +exports.dynamicSlotSize = dynamicSlotSize; +const convert2String = (bytes) => { + if (bytes === + '0x0000000000000000000000000000000000000000000000000000000000000000') { + return ''; + } + const rawString = (0, utils_1.toUtf8String)(bytes); + return (0, exports.escapeString)(rawString); +}; +exports.convert2String = convert2String; +const escapeString = (text) => { + return text.replace(/(?=[<>&"])/g, '\\'); +}; +exports.escapeString = escapeString; //# sourceMappingURL=slotValues.js.map \ No newline at end of file diff --git a/lib/sol2uml.js b/lib/sol2uml.js index 954eece3..84973640 100755 --- a/lib/sol2uml.js +++ b/lib/sol2uml.js @@ -13,12 +13,10 @@ const writerFiles_1 = require("./writerFiles"); const path_1 = require("path"); const squashClasses_1 = require("./squashClasses"); const diff_1 = require("./diff"); +const slotValues_1 = require("./slotValues"); +const ethers_1 = require("ethers"); const clc = require('cli-color'); const program = new commander_1.Command(); -const version = (0, path_1.basename)(__dirname) === 'lib' - ? require('../package.json').version // used when run from compile js in /lib - : require('../../package.json').version; // used when run from TypeScript source files under src/ts via ts-node -program.version(version); const debugControl = require('debug'); const debug = require('debug')('sol2uml'); program @@ -42,7 +40,15 @@ The Solidity code can be pulled from verified source code on Blockchain explorer .default('mainnet') .env('ETH_NETWORK')) .addOption(new commander_1.Option('-k, --apiKey ', 'Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key').env('SCAN_API_KEY')) + .option('-bc, --backColor ', 'Canvas background color. "none" will use a transparent canvas.', 'white') + .option('-sc, --shapeColor ', 'Basic drawing color for graphics, not text', 'black') + .option('-fc, --fillColor ', 'Color used to fill the background of a node', 'gray95') + .option('-tc, --textColor ', 'Color used for text', 'black') .option('-v, --verbose', 'run with debugging statements', false); +const version = (0, path_1.basename)(__dirname) === 'lib' + ? require('../package.json').version // used when run from compile js in /lib + : require('../../package.json').version; // used when run from TypeScript source files under src/ts via ts-node +program.version(version); program .command('class', { isDefault: true }) .description('Generates a UML class diagram from Solidity source code.') @@ -126,6 +132,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k .env('NODE_URL') .default('http://localhost:8545')) .option('-bn, --block ', 'Block number to get the contract storage values from.', 'latest') + .option('-a, --array ', 'Number of slots to display at the start and end of arrays.', '2') .action(async (fileFolderAddress, options, command) => { try { const combinedOptions = { @@ -138,12 +145,12 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k } let { umlClasses, contractName } = await (0, parserGeneral_1.parserUmlClasses)(fileFolderAddress, combinedOptions); contractName = combinedOptions.contract || contractName; - const storages = (0, converterClasses2Storage_1.convertClasses2Storages)(contractName, umlClasses, combinedOptions.contractFile); + const arrayItems = parseInt(combinedOptions.array); + const storageSections = (0, converterClasses2Storage_1.convertClasses2StorageSections)(contractName, umlClasses, arrayItems, combinedOptions.contractFile); if ((0, regEx_1.isAddress)(fileFolderAddress)) { // The first storage is the contract - storages[0].address = fileFolderAddress; + storageSections[0].address = fileFolderAddress; } - debug(storages); if (combinedOptions.data) { let storageAddress = combinedOptions.storage; if (storageAddress) { @@ -157,16 +164,24 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k } storageAddress = fileFolderAddress; } - const storage = storages.find((so) => so.name === contractName); - if (!storageAddress) - throw Error(`Could not find the "${contractName}" contract in list of parsed storages`); - await (0, converterClasses2Storage_1.addStorageValues)(combinedOptions.url, storageAddress, storage, combinedOptions.block); + let block = combinedOptions.block; + if (block === 'latest') { + const provider = new ethers_1.ethers.providers.JsonRpcProvider(combinedOptions.url); + block = await provider.getBlockNumber(); + debug(`Latest block is ${block}. All storage slot values will be from this block.`); + } + // Get slot values for each storage section + for (const storageSection of storageSections) { + await (0, slotValues_1.addSlotValues)(combinedOptions.url, storageAddress, storageSection, arrayItems, block); + // Add storage variables for dynamic arrays, strings and bytes + await (0, converterClasses2Storage_1.addDynamicVariables)(storageSection, storageSections, combinedOptions.url, storageAddress, arrayItems, block); + } } - const dotString = (0, converterStorage2Dot_1.convertStorages2Dot)(storages, combinedOptions); + const dotString = (0, converterStorage2Dot_1.convertStorages2Dot)(storageSections, combinedOptions); await (0, writerFiles_1.writeOutputFiles)(dotString, contractName || 'storageDiagram', combinedOptions.outputFormat, combinedOptions.outputFileName); } catch (err) { - console.error(err.stack); + console.error(err); process.exit(2); } }); diff --git a/lib/squashClasses.d.ts b/lib/squashClasses.d.ts index f57a390c..4eff7cca 100644 --- a/lib/squashClasses.d.ts +++ b/lib/squashClasses.d.ts @@ -1,8 +1,8 @@ import { UmlClass } from './umlClass'; /** * Flattens the inheritance hierarchy for each base contract. - * @param umlClasses array of UML classes of type `UMLClass` + * @param umlClasses array of UML classes of type `UMLClass`. The new squashed class is added to this array. * @param baseContractNames array of contract names to be rendered in squashed format. - * @return squashUmlClasses array of UML classes of type `UMLClass` + * @return squashUmlClasses array of UML classes of type `UMLClass` that are to be rendered */ -export declare const squashUmlClasses: (umlClasses: UmlClass[], baseContractNames: string[]) => UmlClass[]; +export declare const squashUmlClasses: (umlClasses: UmlClass[], baseContractNames: readonly string[]) => UmlClass[]; diff --git a/lib/squashClasses.js b/lib/squashClasses.js index 05ceb7ba..d97ed50f 100644 --- a/lib/squashClasses.js +++ b/lib/squashClasses.js @@ -29,9 +29,9 @@ const crypto = __importStar(require("crypto")); const debug = require('debug')('sol2uml'); /** * Flattens the inheritance hierarchy for each base contract. - * @param umlClasses array of UML classes of type `UMLClass` + * @param umlClasses array of UML classes of type `UMLClass`. The new squashed class is added to this array. * @param baseContractNames array of contract names to be rendered in squashed format. - * @return squashUmlClasses array of UML classes of type `UMLClass` + * @return squashUmlClasses array of UML classes of type `UMLClass` that are to be rendered */ const squashUmlClasses = (umlClasses, baseContractNames) => { let removedClassIds = []; diff --git a/lib/writerFiles.js b/lib/writerFiles.js index fc2f6330..6b72da58 100644 --- a/lib/writerFiles.js +++ b/lib/writerFiles.js @@ -162,7 +162,6 @@ async function writePng(svg, filename) { cause: err, }); } - console.log(`Generated png file ${pngFilename}`); } exports.writePng = writePng; //# sourceMappingURL=writerFiles.js.map \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4ef6310b..c069ffae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sol2uml", - "version": "2.4.3", + "version": "2.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "sol2uml", - "version": "2.4.3", + "version": "2.5.0", "license": "MIT", "dependencies": { "@aduh95/viz.js": "^3.7.0", diff --git a/package.json b/package.json index 1be594c8..44343c8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sol2uml", - "version": "2.4.3", + "version": "2.5.0", "description": "Solidity contract visualisation tool.", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/src/contracts/TestStorage.sol b/src/contracts/TestStorage.sol deleted file mode 100644 index 932aaab0..00000000 --- a/src/contracts/TestStorage.sol +++ /dev/null @@ -1,354 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.16; - -struct SubOneSlot { - address account; // 20 bytes - bool flag; // 1 byte - int8 count; // 1 byte -} - -struct OneSlot { - address account; // 20 bytes - uint88 sum; // 11 bytes - uint8 count; // 1 bytes -} - -struct SubTwoSlots { - address account1; - address account2; - bool flag1; - bool flag2; -} - -struct ContractLevelStruct0 { - uint256 param1; - bool param2; -} - -struct ContractLevelStruct1 { - uint256 param1; - address param2; - uint8 param3; - bytes1 param4; -} - -struct ContractLevelStruct2 { - ContractLevelStruct0 param1; - ContractLevelStruct1 param2; -} - -struct ContractLevelStruct11 { - ContractLevelStruct1 param1; -} - -enum Severity { - Low, - Medium, - High -} - -uint256 constant FileConstant = 5; - -interface IERC20 { - function totalSupply() external view returns (uint256); - - function balanceOf(address account) external view returns (uint256); - - function transfer(address recipient, uint256 amount) external returns (bool); - - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 amount) external returns (bool); - - function transferFrom( - address sender, - address recipient, - uint256 amount - ) external returns (bool); -} - -contract GrandParent { - bool initGP; - address grandParent; - - constructor() { - initGP = true; - grandParent = 0xfF1b44f1FCCebc4890B5E00a1EA9259d00a40fEb; - } -} - -contract Parent is GrandParent { - bool initP; - address parent; - - constructor() { - initP = true; - parent = 0xeC20607aa654D823DD01BEB8780a44863c57Ed07; - } -} - -contract Parent2 is GrandParent { - bool initP2; - address parent2; - - constructor() { - initP2 = true; - parent2 = 0xb985439AFa9314dCB002E191e230A5936493479B; - } -} - -contract TestStorage is Parent, Parent2 { - struct TwoSlots { - bytes32 hash1; - bytes32 hash2; - } - - struct FixedArray { - uint16 num1; - bytes30[2] data; - uint16 num2; - } - - struct FlagsStruct { - bool flag1; - bool[2] flags; - bool flag2; - } - - enum Status { - Open, - Resolved - } - - uint256 public constant N_COINS = 2; - uint256 public constant MAX_COINS = 3; - address public immutable superUser; - - address owner = 0x2f2Db75C5276481E2B018Ac03e968af7763Ed118; - IERC20 token = IERC20(0x34f08F2A3f4a86531e9C4139Fde571a62689AFEC); - address[] tokensDyn = [ - 0x1000000000000000000000000000000000000001, - 0x2000000000000000000000000000000000000002, - 0x3000000000000000000000000000000000000003, - 0x4000000000000000000000000000000000000004 - ]; - IERC20[2] tokenPair = [ - IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), - IERC20(0x945Facb997494CC2570096c74b5F66A3507330a1) - ]; - address[12] dozenTokens = [ - 0xa57Bd00134B2850B2a1c55860c9e9ea100fDd6CF, - 0x1000000000000000000000000000000000000001, - 0x2000000000000000000000000000000000000002, - 0x3000000000000000000000000000000000000003, - 0x4000000000000000000000000000000000000004, - 0x5000000000000000000000000000000000000005, - 0x6000000000000000000000000000000000000006, - 0x7000000000000000000000000000000000000007, - 0x8000000000000000000000000000000000000008, - 0x9000000000000000000000000000000000000009, - 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 - ]; - address[N_COINS] coins = [ - 0x1000000000000000000000000000000000000001, - 0x2000000000000000000000000000000000000002 - ]; - address[N_COINS][3][N_COINS] multiDimension; - uint256[MAX_COINS] maxCoins = [1234, 567, 8910]; - IERC20[N_COINS] tokens = [ - IERC20(0x1000000000000000000000000000000000000001), - IERC20(0x2000000000000000000000000000000000000002) - ]; - // address[2 * N_COINS] doubleTokens; - - uint256 totalSupply = 123123123123456789012345678; - uint128 rate1 = 123 * 1e18; - uint128 rate2 = 456 * 1e18; - // fixed float1 = 1.0234; - // ufixed float2 = 99.9999; - // ufixed128x18 float3 = 0.001; - // fixed128x18 float4 = 12345.0123; - bytes32 hash = 0xe9b69cd5563a8bfbffb0fa4f422862013492d43fe7fb62d771a0147b6e891d13; - bool public flag1 = true; - bool private flag2 = true; - bool internal flag3 = true; - bool public flag4 = false; - bool[2] public flags = [true, true]; - bool public flag5 = true; - bool[2][2] public flags2x2 = [[true, false], [true, true]]; - bool public flag6 = true; - bool[2][3] public flags2x3 = [[true, false], [false, true], [true, true]]; - bool public flag7 = true; - bool[3][2] public flags3x2 = [[true, false, true], [false, true, false]]; - - bool[33][2] public flags33x2 = [[true, true, false, true], [true, false, true, true]]; - bool[2][33] public flags2x33 = [[true, true], [true, false], [false, true], [false, false]]; - bool public flag8 = true; - bool[2][] public flags2xDyn = [ - [true, true], - [true, true], - [false, true], - [true, false], - [false, false], - [true, true] - ]; - bool public flag9 = true; - bool[][2] public flagsDynx2 = [ - [true, false, false, true, true], - [true, false, true, false, true] - ]; - bool[][16] public flagsDynx16; - bool[][32] public flagsDynx32; - bool[32][] public flags32xDyn; - bool[][4][3] public flagsDynx4x3 = [ - [[true, false, true], [false, true, false], [false, false, true], [true, true, true]], - [[false, false, false], [true, true, true], [false, true, false], [true, false, true]] - ]; - bool[33][2][2] public bool_33x2x2; - bool public flag10 = true; - bytes30[2][6] public bytes30_2x6; - bytes30[6][2] public bytes30_6x2; - bytes32[] public bytes32Dyn = [ - bytes32(0xFF00128251ec233d387a0af31db13f8318b61e40975c27476e1c1a02b79700FF), - 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD - ]; - bool public flag11 = true; - uint32[FileConstant] public timestamps = [1060001, 1160111, 1260223, 1360333, 1660445]; - TwoSlots[2] public twoSlots2x; - TwoSlots[3][4] public twoSlots3x4; - TwoSlots[4][3] public twoSlots4x3; - TwoSlots[][3] public twoSlotsDynx3; - TwoSlots[3][] public twoSlots3xDyn; - TwoSlots[][] public twoSlotsDynxDyn; - TwoSlots[][4][3] public twoSlotsDynx4x3; - TwoSlots[3][4][] public twoSlotsDynx3x4xDyn; - Status public status = Status.Open; - Severity public severity = Severity.High; - SubOneSlot public subSlot = SubOneSlot(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5, true, -121); - uint8 public oneByteNumber = 253; - OneSlot public oneSlot = OneSlot(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5, 1234567890, 253); - SubTwoSlots public subTwoSlot = - SubTwoSlots( - 0xF4dDc5FF5AbA6E8739E5E056340827c573d191Ec, - 0xe63dfF84aa562dE11B28894f0391702b814f812D, - true, - true - ); - TwoSlots public twoSlots = - TwoSlots( - 0xAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9, - 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD - ); - FixedArray public fixedArray; - FlagsStruct public flagStruct; - int16 public arrayCount = -2000; - uint64[] public dynamicIntArray = [2000, 1, 254, 0, 254, 2, 256]; - uint256[3] public fixedIntArray = [1000, 2000, 3000]; - mapping(address => bool) public blacklist; - mapping(address => uint256) public balance; - mapping(address => ContractLevelStruct2) public mapStruct; - mapping(address => mapping(address => ContractLevelStruct2)) public mapOfMapStruct; - mapping(address => IERC20) public mapInterface; - IERC20[2] public interfaceFixedArray = [ - IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), - IERC20(0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19)]; - IERC20[] public interfaceDynArray = [ - IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), - IERC20(0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19), - IERC20(0x78BefCa7de27d07DC6e71da295Cc2946681A6c7B)]; - string public uninitialisedString; - string public emptyString = ""; - string public name = "TestStorage contract"; - string public short = "Less than 31 bytes"; - string public exactly32 = "exactly 32 bytes so uses 2 slots"; - string public long2 = "more than 31 bytes so data is stored dynamically in 2 slots"; - string public long3 = - "more than sixty four (64) bytes so data is stored dynamically in three slots"; - - // The following can be publically changed for testing purposes - string public testString = "This can be publically changed by anyone"; - bytes public testBytes = bytes("0xEB1000001FD"); - uint256 public testUint256 = 0xFEDCBA9876543210; - int256 public testInt256 = -1023; - address public testAddress; - - constructor(address _superUser) { - superUser = _superUser; - fixedArray.num1 = 65535; - fixedArray.num2 = 65001; - fixedArray.data = [ - bytes30(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF), - 0xFFF000000F00000000000000000000000000000000000000000000000FFF - ]; - - blacklist[0x2f2Db75C5276481E2B018Ac03e968af7763Ed118] = true; - blacklist[0xdb2C46Ed8E850668b942d9Bd6D2ae8803c6789DF] = false; - balance[0x2f2Db75C5276481E2B018Ac03e968af7763Ed118] = 0x1234566789ABCDEF; - - twoSlots2x[0] = TwoSlots( - 0xFFFF00000F000000000000000000000000000000000000000000000000FFFFFF, - 0xFFFFF0000FF0000000000000000000000000000000000000000000000FFFFFFF - ); - twoSlots2x[1] = TwoSlots( - 0xFF0000000F0000000000000000000000000000000000000000000000000000FF, - 0xFFF000000FF00000000000000000000000000000000000000000000000000FFF - ); - - twoSlots3x4[0][0] = TwoSlots( - 0xFF000000000000000000000000000000000000000000000000000000000000FF, - 0xFFF000000F000000000000000000000000000000000000000000000000000FFF - ); - twoSlots3x4[0][1] = TwoSlots( - 0xAF0000000000000000000000000000000000000000000000000000000000000F, - 0xAF0000000F0000000000000000000000000000000000000000000000000000FF - ); - twoSlots3x4[0][2] = TwoSlots( - 0xABC0000000000000000000000000000000000000000000000000000000000321, - 0xABC000000F000000000000000000000000000000000000000000000000000456 - ); - twoSlots3x4[1][0] = TwoSlots( - 0xDEF0000000000000000000000000F000000000000000000000000000000000F1, - 0xDEF000000F000000000000000000F00000000000000000000000000000000FF1 - ); - twoSlots3x4[1][1] = TwoSlots( - 0x1000000000000000000000000000F00000000000000000000000000000000001, - 0x300000000F000000000000000000F00000000000000000000000000000000003 - ); - twoSlots3x4[3][2] = TwoSlots( - 0xB00000000000000000000000000000000000000000000000000000F00000000D, - 0xF00000000F00000000000000000000000000000000000000000000F00000000F - ); - - twoSlots3xDyn.push(); - twoSlots3xDyn[0][0] = TwoSlots( - 0xF00000000000000000000000000000000000000000000000000000000000000F, - 0xF0000000F00000000000000000000000000000000000000000000000000000FF - ); - twoSlots3xDyn[0][1] = TwoSlots( - 0xFF00000000000000000F0000000000000000000000000000000000000000000F, - 0xFF000000F0000000000F000000000000000000000000000000000000000000FF - ); - - testAddress = address(this); - } - - function setTestString(string memory _testStr) public { - testString = _testStr; - } - - function setTestBytes(bytes memory _testBytes) public { - testBytes = _testBytes; - } - - function setTestUint256(uint256 _testUint256) public { - testUint256 = _testUint256; - } - - function setTestInt256(int256 _testInt256) public { - testInt256 = _testInt256; - } - - function setTestAddress(address _testAddress) public { - testAddress = _testAddress; - } -} diff --git a/src/contracts/storage/BasicStorage.sol b/src/contracts/storage/BasicStorage.sol new file mode 100644 index 00000000..45d92199 --- /dev/null +++ b/src/contracts/storage/BasicStorage.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +enum Severity { + Low, + Medium, + High +} + +interface ITrade { + function buy(address buy, address sell, uint256 buyAmount) external; + function sell(address buy, address sell, uint256 sellAmount) external; +} + +contract BasicStorage { + // boolean + bool someBool = true; + // enum + Severity severity = Severity.Medium; + // numbers + uint8 public tinyNumber = 250; + uint256 public fullSlotNumber = 20000e18; + int16 public smallNegativeNumber = -10000; + uint128 public halfSlotNumber = 10000e18; + // addresses + address public owner = address(this); + ITrade public exchange = ITrade(0x1C727a55eA3c11B0ab7D3a361Fe0F3C47cE6de5d); + // fixed-size bytes + bytes1 public oneByte = bytes1(0xFF); + bytes8 public eightBytes = bytes8(0x8100FF81C300FF01); + bytes32 public fullSlotBytes = 0xe9b69cd5563a8bfbffb0fa4f422862013492d43fe7fb62d771a0147b6e891d13; + // strings + string public name = "BasicStorage contract"; + bytes public data = hex"FFEEDDCCBBAA9988770011"; +} diff --git a/src/contracts/storage/DynamicArrayStorage.sol b/src/contracts/storage/DynamicArrayStorage.sol new file mode 100644 index 00000000..b5a5b77f --- /dev/null +++ b/src/contracts/storage/DynamicArrayStorage.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract DynamicArrayStorage { + + uint256[] public numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + uint256[] public empty; + uint56[] public sevenByteNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + address[] public tokens = [0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xdAC17F958D2ee523a2206206994597C13D831ec7, 0x6B175474E89094C44Da98b954EedeAC495271d0F]; +} diff --git a/src/contracts/storage/FixedArrayStorage.sol b/src/contracts/storage/FixedArrayStorage.sol new file mode 100644 index 00000000..b9dcbe59 --- /dev/null +++ b/src/contracts/storage/FixedArrayStorage.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract FixedArrayStorage { + + uint72[5] public five9ByteNumbers = [1, 2**8-1, 2**16-1, 2**32-1, 2**72-1]; + uint56[21] public twentyOne7ByteNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + address[3] public tokens = [0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xdAC17F958D2ee523a2206206994597C13D831ec7, 0x6B175474E89094C44Da98b954EedeAC495271d0F]; + bytes32[50] public gap; +} diff --git a/src/contracts/storage/MappingStorage.sol b/src/contracts/storage/MappingStorage.sol new file mode 100644 index 00000000..a73e0d5a --- /dev/null +++ b/src/contracts/storage/MappingStorage.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +enum Severity { + Low, + Medium, + High +} + +struct ExampleStruct { + bool flag; + address token; + Severity severity; + address[] dynamicAddressArray; + uint256[2] staticIntArray; + string someString; + mapping(address => uint256) claimed; +} + +contract MappingStorage { + + // Classic token mapping of user address to token balance + mapping(address => uint256) public balances; + + // Mapping of user address to spender address to transfer limit + mapping(address => mapping(address => uint256)) public allowances; + + mapping(uint256 => address[]) public grantRecipients; + + mapping(address => ExampleStruct) public mapToStruct; +} diff --git a/src/contracts/storage/MultiDynamicArrayStorage.sol b/src/contracts/storage/MultiDynamicArrayStorage.sol new file mode 100644 index 00000000..7b022a9c --- /dev/null +++ b/src/contracts/storage/MultiDynamicArrayStorage.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract MultiDynamicArrayStorage { + + uint256[][] public twoDimDynIntArray; + uint256[][][] public threeDimDynIntArray; + + constructor() { + twoDimDynIntArray.push([11111, 11122]); + twoDimDynIntArray.push([222111, 222222, 222333]); + twoDimDynIntArray.push(); + twoDimDynIntArray.push([254]); + twoDimDynIntArray.push([0]); + twoDimDynIntArray.push([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + twoDimDynIntArray.push([11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]); + twoDimDynIntArray.push([12, 1212, 121212, 12121212]); + + threeDimDynIntArray.push(); + threeDimDynIntArray[0].push([111111, 111122]); + threeDimDynIntArray[0].push([112211, 112222, 112233]); + threeDimDynIntArray.push(); + threeDimDynIntArray[1].push([221111, 221122, 221133, 221144]); + threeDimDynIntArray[1].push([222211, 222222, 222233, 222244, 222255]); + threeDimDynIntArray[1].push(); + threeDimDynIntArray[1].push(); + threeDimDynIntArray[1].push(); + threeDimDynIntArray[1].push([11, 12, 13]); + threeDimDynIntArray.push(); + threeDimDynIntArray[2].push([331111]); + threeDimDynIntArray[2].push(); + threeDimDynIntArray[2].push([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + threeDimDynIntArray.push(); + } +} diff --git a/src/contracts/storage/MultiFixedArrayStorage.sol b/src/contracts/storage/MultiFixedArrayStorage.sol new file mode 100644 index 00000000..b3a9f258 --- /dev/null +++ b/src/contracts/storage/MultiFixedArrayStorage.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract MultiFixedArrayStorage { + uint256[3][2] public twoByThreeNumbers = [[1, 2, 3], [4, 5, 6]]; + bool[2][3] public threeByTwoBool = [[true, false], [false, true], [true, false]]; + bool[3][2] public twoByThreeBool = [[true, false, true], [false, true, false]]; +} diff --git a/src/contracts/storage/StringStorage.sol b/src/contracts/storage/StringStorage.sol new file mode 100644 index 00000000..32453ab5 --- /dev/null +++ b/src/contracts/storage/StringStorage.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract StringStorage { + + // strings + string public uninitString; + string public emptyString = ""; + string public name = "StringStorage contract"; + string public exactly31 = "exactly 31 chars so uses 1 slot"; + string public exactly32 = "32 char so uses one dynamic slot"; + string public long2 = "more than 32 bytes so data is stored dynamically in 2 slots"; + string public long3 = + "more than sixty four (64) bytes so data is stored dynamically in three slots"; +} diff --git a/src/contracts/storage/StructStorage.sol b/src/contracts/storage/StructStorage.sol new file mode 100644 index 00000000..0f79e38d --- /dev/null +++ b/src/contracts/storage/StructStorage.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +enum Severity { + Low, + Medium, + High +} + +struct ExampleStruct { + bool flag; + address token; + Severity severity; + address[] dynamicAddressArray; + int64[] dynamicIntArray; + uint256[2] staticIntArray; + string someString; +} + +contract StructStorage { + + ExampleStruct public exampleStruct; + ExampleStruct[] public dynamicStructs; + + constructor() { + exampleStruct.flag = true; + exampleStruct.token = 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656; + exampleStruct.severity = Severity.High; + exampleStruct.dynamicAddressArray = [0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D, 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6, 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF]; + exampleStruct.dynamicIntArray.push(1); + exampleStruct.dynamicIntArray.push(-1); + exampleStruct.dynamicIntArray.push(1023); + exampleStruct.staticIntArray[0] = 11; + exampleStruct.staticIntArray[1] = 22; + exampleStruct.someString = "more than sixty four (64) bytes so data is stored dynamically in three slots"; + + dynamicStructs.push(); + dynamicStructs[0].flag = true; + dynamicStructs[0].token = 0x22E2219F098Ab128F11BE752Da4fC8e1c6BA2F3f; + dynamicStructs[0].severity = Severity.Medium; + dynamicStructs[0].dynamicAddressArray = [0x2A0b4Bdb2492eC4D8eA0015A6784ACC98216c5D2]; + dynamicStructs[0].dynamicIntArray.push(1111); + dynamicStructs[0].dynamicIntArray.push(2222); + dynamicStructs[0].staticIntArray[0] = 33; + dynamicStructs[0].staticIntArray[1] = 44; + dynamicStructs[0].someString = "a short string"; + } +} diff --git a/src/contracts/storage/TestStorage.sol b/src/contracts/storage/TestStorage.sol new file mode 100644 index 00000000..dcd6d9e0 --- /dev/null +++ b/src/contracts/storage/TestStorage.sol @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract Structs { + struct SubOneSlot { + address account; // 20 bytes + bool flag; // 1 byte + int8 count; // 1 byte + } + + struct OneSlot { + address account; // 20 bytes + uint88 sum; // 11 bytes + uint8 count; // 1 bytes + } + + struct SubTwoSlots { + address account1; + address account2; + bool flag1; + bool flag2; + } +} + +struct ContractLevelStruct0 { + uint256 param1; + bool param2; +} + +struct ContractLevelStruct$_1 { + uint256 param1; + address param2; + uint8 param3; + bytes1 param4; +} + +struct ContractLevelStruct2 { + ContractLevelStruct0 param1; + ContractLevelStruct$_1 param2; +} + +struct ContractLevelStruct11 { + ContractLevelStruct$_1 param1; +} + +struct DynamicStruct { + bool flag; + address token; + IERC20 asset; + Severity severity; + Severity[3] staticSeverities; + Severity[] dynamicSeverities; + address[] dynamicAddressArray; + address[2] staticAddressArray; + int64[] dynamicIntArray; + int64[5] staticIntArray; + string shortString; + string longString; + ContractLevelStruct$_1 struct1; + ContractLevelStruct$_1[2] staticStruct1; + ContractLevelStruct$_1[] dynamicStruct1; + mapping(address => uint256) balance; + mapping(address => ContractLevelStruct$_1) mappedStruct1; +} + +enum Severity { + Low, + Medium, + High +} + +uint256 constant FileConstant = 5; + +interface IERC20 { + function totalSupply() external view returns (uint256); + + function balanceOf(address account) external view returns (uint256); + + function transfer(address recipient, uint256 amount) external returns (bool); + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 amount) external returns (bool); + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); +} + +contract GrandParent { + bool initGP = true; + address grandParent = 0xfF1b44f1FCCebc4890B5E00a1EA9259d00a40fEb; + address[2] assets = [0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990, 0xe688b84b23f322a994A53dbF8E15FA82CDB71127]; + uint256[] grandNumbers = [1, 2, 3, 4]; + string grandParentName = "Grand parent name that takes more than one slot"; + bytes grandParentData = hex"FFEEDDCCBBAA9988776655443322110011"; +} + +contract Parent is GrandParent { + bool initP = true; + address parent = 0xeC20607aa654D823DD01BEB8780a44863c57Ed07; + string parentName = "Parent"; + bytes parentData = hex"F0123456789ABCDEFF"; +} + +contract Parent2 is GrandParent { + bool initP2 = true; + address parent2 = 0xb985439AFa9314dCB002E191e230A5936493479B; + uint56[21] smallNumbers = [1, 2, 3, 4, 5, 6, 7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20 ,21]; + bytes data; +} + +contract TestStorage is Parent, Parent2 { + struct TwoSlots { + bytes32 hash1; + bytes32 hash2; + } + + struct FixedArray { + uint16 num1; + bytes30[2] data; + uint16 num2; + } + + struct FlagsStruct { + bool flag1; + bool[2] flags; + bool flag2; + } + + enum Status { + Open, + Resolved + } + + uint256 public constant N_COINS = 2; + uint256 public constant MAX_COINS = 3; + address public immutable superUser; + + address owner = 0x2f2Db75C5276481E2B018Ac03e968af7763Ed118; + IERC20 token = IERC20(0x34f08F2A3f4a86531e9C4139Fde571a62689AFEC); + address[] tokensDyn = [ + 0xfF000000000000000000000000000000000000A1, + 0xff000000000000000000000000000000000000b2, + 0xFf000000000000000000000000000000000000c3, + 0xFF000000000000000000000000000000000000d4 + ]; + IERC20[2] tokenPair = [ + IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), + IERC20(0x945Facb997494CC2570096c74b5F66A3507330a1) + ]; + address[12] dozenTokens = [ + 0xa57Bd00134B2850B2a1c55860c9e9ea100fDd6CF, + 0x2000000000000000000000000000000000000001, + 0x2000000000000000000000000000000000000002, + 0x2000000000000000000000000000000000000003, + 0x2000000000000000000000000000000000000004, + 0x2000000000000000000000000000000000000005, + 0x2000000000000000000000000000000000000006, + 0x2000000000000000000000000000000000000007, + 0x2000000000000000000000000000000000000008, + 0x2000000000000000000000000000000000000009, + 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + ]; + address[N_COINS] coins = [ + 0x3000000000000000000000000000000000000001, + 0x3000000000000000000000000000000000000002 + ]; + address[N_COINS][3][N_COINS] multiDimension; + uint256[MAX_COINS] maxCoins = [1234, 567, 8910]; + IERC20[N_COINS] tokens = [ + IERC20(0x4000000000000000000000000000000000000001), + IERC20(0x4000000000000000000000000000000000000002) + ]; + // address[2 * N_COINS] doubleTokens; + + uint256 totalSupply = 123123123123456789012345678; + uint128 rate1 = 123 * 1e18; + uint128 rate2 = 456 * 1e18; + // fixed float1 = 1.0234; + // ufixed float2 = 99.9999; + // ufixed128x18 float3 = 0.001; + // fixed128x18 float4 = 12345.0123; + bytes32 hash = 0xe9b69cd5563a8bfbffb0fa4f422862013492d43fe7fb62d771a0147b6e891d13; + bool public flag1 = true; + bool private flag2 = true; + bool internal flag3 = true; + bool public flag4 = false; + bool[2] public flags = [true, true]; + bool[2][3] public flags2x3 = [[true, false], [false, true], [true, true]]; + bool[3][2] public flags3x2 = [[true, false, true], [false, true, false]]; + + bool[33][2] public flags33x2 = [[true, true, false, true], [true, false, true, true]]; + bool[2][33] public flags2x33 = [[true, true], [true, false], [false, true], [false, false]]; + bool[] public flagsDyn = [true, true, true, false, true, false, true]; + bool[][] public flagsDynDyn; + bool[][][] public flagsDynDynDyn; + bool[2][] public flags2xDyn = [ + [true, true], + [true, true], + [false, true], + [true, false], + [false, false], + [true, true] + ]; + bool[][2] public flagsDynx2; + bool[][16] public flagsDynx16; + bool[][32] public flagsDynx32; + bool[32][] public flags32xDyn; + bool[][4][3] public flagsDynx4x3 = [ + [[true, false, true], [false, true, false], [false, false, true], [true, true, true]], + [[false, false, false], [true, true, true], [false, true, false], [true, false, true]] + ]; + bool[33][2][2] public bool_33x2x2; + bytes30[2][6] public bytes30_2x6; + bytes30[6][2] public bytes30_6x2; + bytes32[] public bytes32Dyn = [ + bytes32(0xFF00128251ec233d387a0af31db13f8318b61e40975c27476e1c1a02b79700FF), + 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + ]; + uint32[FileConstant] public timestamps = [1060001, 1160111, 1260223, 1360333, 1660445]; + + uint72[5] public five9ByteNumbers = [1, 2**8-1, 2**16-1, 2**32-1, 2**72-1]; + uint72[22] public twentyTwo9ByteNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]; + + uint128[7] public sevenHalfNumbers = [0, 1, 2, 3, 4, 5, 6]; + uint128[9] public nineHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + uint128[11] public elevenHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + uint128[12] public twelveHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + uint128[13] public thirteenHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + uint128[14] public fourteenHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; + uint128[15] public fifteenHalfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; + uint256[50] public gap; + uint256[5] public fixedIntArray = [1000, 2000, 3000, 4000, 5000]; + + TwoSlots[2] public twoSlots2x; + TwoSlots[3][4] public twoSlots3x4; + TwoSlots[4][3] public twoSlots4x3; + TwoSlots[][3] public twoSlotsDynx3; + TwoSlots[3][] public twoSlots3xDyn; + TwoSlots[][] public twoSlotsDynxDyn; + TwoSlots[][4][3] public twoSlotsDynx4x3; + TwoSlots[3][4][] public twoSlotsDynx3x4xDyn; + Structs.SubTwoSlots[] public twoContractStruct; + + // enums + Status public status = Status.Open; + Status[] public dynamicStatuses = [Status.Open, Status.Resolved, Status.Open]; + Severity public severity = Severity.High; + Severity[4] public staticSeverities = [Severity.High, Severity.Low, Severity.Medium, Severity.High]; + + Structs.SubOneSlot public subSlot = Structs.SubOneSlot(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5, true, -121); + uint8 public oneByteNumber = 253; + Structs.OneSlot public oneSlot = Structs.OneSlot(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5, 1234567890, 253); + Structs.SubTwoSlots public subTwoSlot = + Structs.SubTwoSlots( + 0xF4dDc5FF5AbA6E8739E5E056340827c573d191Ec, + 0xe63dfF84aa562dE11B28894f0391702b814f812D, + true, + true + ); + TwoSlots public twoSlots = + TwoSlots( + 0xAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9, + 0xEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + ); + FixedArray public fixedArray; + FlagsStruct public flagStruct = FlagsStruct(true, [true, true], true); + int16 public arrayCount = -2000; + + uint256[] public numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + uint256[] public empty; + uint56[] public sevenByteNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; + uint72[] public nineByteNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + uint64[] public dynamicInt64Array = [2000, 1, 254, 1e19, 254, 2, 256]; + uint128[] public dynamicInt128Array = [1e38, 2e38, 3e38]; + uint136[] public dynamicInt136Array = [1e40, 2e39, 3e39, 4e39]; + uint256[] public dynamicInt256Array = [1e77, 2e76, 3e76, 4e76, 5e76]; + uint256[][] public dynamicDynIntArray; + uint256[][][] public dynamicDynDynIntArray; + + mapping(address => bool) public blacklist; + mapping(address => uint256) public balance; + mapping(address => ContractLevelStruct2) public mapStruct; + mapping(address => mapping(address => ContractLevelStruct2)) public mapOfMapStruct; + mapping(address => Structs.SubTwoSlots) public mapContractStruct; + mapping(address => IERC20) public mapInterface; + + DynamicStruct public dynamicStruct; + DynamicStruct[] public dynDynamicStruct; + DynamicStruct[2] public staticDynamicStruct; + mapping(address => DynamicStruct) public mapppedDynamicStruct; + + IERC20[2] public interfaceFixedArray = [ + IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), + IERC20(0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19)]; + IERC20[] public interfaceDynArray = [ + IERC20(0xe2f2a5C287993345a840Db3B0845fbC70f5935a5), + IERC20(0x30647a72Dc82d7Fbb1123EA74716aB8A317Eac19), + IERC20(0x78BefCa7de27d07DC6e71da295Cc2946681A6c7B)]; + + string public uninitialisedString; + string public emptyString = ""; + string public name = "TestStorage contract"; + string public short = "Less than 31 bytes"; + string public exactly31 = "exactly 31 chars so uses 1 slot"; + string public exactly32 = "32 char so uses one dynamic slot"; + string public long2 = "more than 32 bytes so data is stored dynamically in 2 slots"; + string public long3 = + "more than sixty four (64) bytes so data is stored dynamically in three slots"; + + // The following can be publicly changed for testing purposes + string public testString = "This can be publicly changed by anyone"; + + string public uninitialisedBytes; + string public emptyBytes = hex""; + bytes public testBytes = hex"FFEEDDCCBBAA9988770011"; + bytes public exactly31Bytes = hex"ec0b854938343f85eb39a6648b9e449c2e4aee4dc9b4e96ab592f9f497d051"; + bytes public exactly32Bytes = hex"27f12abfe35860a9a927b465bb3d4a9c23c8428174b83f278fe45ed7b4da2662"; + bytes public long3Bytes = hex"f34d3c319f536deb74ed8f1f3205d9aefef7487c819e77d3351630820dbff1118cc7ee599e5d59fee88c83157bd897847c5911dc7d317b3175e0b085198349973fff"; + uint256 public testUint256 = 0xFEDCBA9876543210; + int256 public testInt256 = -1023; + address public testAddress; + uint public $some_number = 254; + uint[] public $some_numbers = [0, 1, 2, 3, 4]; + + constructor(address _superUser) { + superUser = _superUser; + + multiDimension[0][0][0] = 0xFfffFfFFFfFFFFfFFfFFFfFFFfFFfFFFfFfFfFf1; + multiDimension[0][0][1] = 0xfffFFFFFFFfFFFFfFFfFfFFffFfFfFffFFFFfFf2; + + flags33x2[0][32] = true; + + flagsDynx2[0] = [true, false, false, true, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, false, true, false, false, true, true, false, true, true, false, false, true, true, true, false, true]; + flagsDynx2[1] = [true, false, true, false, true, true, true, true, false, true]; + + flagsDynDyn.push([true, true, false, true, true]); + flagsDynDyn.push([true, false, true]); + flagsDynDyn.push(); + flagsDynDyn.push([true, true]); + + flagsDynDynDyn.push(); + flagsDynDynDyn[0].push([true, true, false, true]); + flagsDynDynDyn.push(); + flagsDynDynDyn[1].push([true, false, false, true, true]); + flagsDynDynDyn.push(); + flagsDynDynDyn[2].push(); + flagsDynDynDyn[2][0].push(true); + flagsDynDynDyn[2][0].push(false); + flagsDynDynDyn[2][0].push(true); + + flagsDynx16[0].push(true); + flagsDynx16[0].push(true); + flagsDynx16[0].push(false); + flagsDynx16[0].push(true); + flagsDynx16[1].push(true); + flagsDynx16[1].push(false); + flagsDynx16[1].push(true); + flagsDynx16[2].push(true); + flagsDynx16[2].push(true); + flagsDynx16[15].push(true); + + flagsDynx32[0].push(true); + flagsDynx32[0].push(true); + flagsDynx32[1].push(true); + + flags32xDyn.push([true, false, true]); + flags32xDyn.push([true, false, true, false, true]); + + bool_33x2x2[0][0][0] = true; + bool_33x2x2[1][1][1] = true; + + bytes30_2x6[0][0] = 0xBBB000000000000000000000000000000000000000000000000000000BBB; + bytes30_6x2[0][0] = 0xCCC000000000000000000000000000000000000000000000000000000CCC; + + fixedArray.num1 = 65535; + fixedArray.num2 = 65001; + fixedArray.data = [ + bytes30(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF), + 0xFFF000000F00000000000000000000000000000000000000000000000FFF + ]; + + dynamicDynIntArray.push([11111, 11122]); + dynamicDynIntArray.push([222111, 222222, 222333]); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push(); + dynamicDynIntArray.push([12, 1212, 121212, 12121212]); + + dynamicDynDynIntArray.push(); + dynamicDynDynIntArray[0].push([111111, 111122]); + dynamicDynDynIntArray[0].push([112211, 112222, 112233]); + dynamicDynDynIntArray.push(); + dynamicDynDynIntArray[1].push([221111, 221122, 221133, 221144]); + dynamicDynDynIntArray[1].push([222211, 222222, 222233, 222244, 222255]); + dynamicDynDynIntArray.push(); + dynamicDynDynIntArray[2].push([331111]); + + blacklist[0x2f2Db75C5276481E2B018Ac03e968af7763Ed118] = true; + blacklist[0xdb2C46Ed8E850668b942d9Bd6D2ae8803c6789DF] = false; + balance[0x2f2Db75C5276481E2B018Ac03e968af7763Ed118] = 0x1234566789ABCDEF; + + dynamicStruct.flag = true; + dynamicStruct.token = 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656; + dynamicStruct.asset = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); + dynamicStruct.severity = Severity.High; + dynamicStruct.staticSeverities = [Severity.High, Severity.Low, Severity.Medium]; + dynamicStruct.dynamicSeverities = [Severity.High, Severity.Medium]; + dynamicStruct.dynamicAddressArray = [0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D, 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6, 0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF]; + dynamicStruct.staticAddressArray = [0x853d955aCEf822Db058eb8505911ED77F175b99e, 0x104592a158490a9228070E0A8e5343B499e125D0]; + dynamicStruct.dynamicIntArray.push(1); + dynamicStruct.dynamicIntArray.push(-1); + dynamicStruct.dynamicIntArray.push(1023); + dynamicStruct.staticIntArray = [int64(1), 2, 3, 4, 5]; + dynamicStruct.shortString = "exactly 31 chars so uses 1 slot"; + dynamicStruct.longString = "over 31 charaters so is dynamic length using two slots"; + dynamicStruct.struct1 = ContractLevelStruct$_1({ + param1: 1e18, + param2: 0x6243d8CEA23066d098a15582d81a598b4e8391F4, + param3: 127, + param4: bytes1(0xFF) + }); + dynamicStruct.staticStruct1[0] = ContractLevelStruct$_1({ + param1: 2e18, + param2: 0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919, + param3: 6, + param4: bytes1(0xFF) + }); + + dynDynamicStruct.push(); + dynDynamicStruct[0].flag = true; + dynDynamicStruct[0].token = 0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656; + dynDynamicStruct[0].asset = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); + dynDynamicStruct[0].severity = Severity.High; + dynDynamicStruct[0].staticSeverities = [Severity.Low, Severity.Medium, Severity.High]; + dynDynamicStruct[0].dynamicSeverities = [Severity.Medium, Severity.Medium]; + dynDynamicStruct[0].dynamicAddressArray = [0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF]; + dynDynamicStruct[0].staticAddressArray = [0x853d955aCEf822Db058eb8505911ED77F175b99e, 0x104592a158490a9228070E0A8e5343B499e125D0]; + dynDynamicStruct[0].dynamicIntArray.push(200); + dynDynamicStruct[0].dynamicIntArray.push(-200); + dynDynamicStruct[0].dynamicIntArray.push(32000); + dynDynamicStruct[0].staticIntArray = [int64(1000), 2000, 3000, 4000, 5000]; + dynDynamicStruct[0].shortString = "string < 31 chars"; + dynDynamicStruct[0].longString = "a string that is over 31 characters in length"; + dynDynamicStruct[0].struct1 = ContractLevelStruct$_1({ + param1: 3e18, + param2: 0x36F944B7312EAc89381BD78326Df9C84691D8A5B, + param3: 2, + param4: bytes1(0x89) + }); + dynDynamicStruct[0].staticStruct1[0] = ContractLevelStruct$_1({ + param1: 4e18, + param2: 0x4fB30C5A3aC8e85bC32785518633303C4590752d, + param3: 255, + param4: bytes1(0x91) + }); + + staticDynamicStruct[1].flag = true; + staticDynamicStruct[1].token = 0xD37EE7e4f452C6638c96536e68090De8cBcdb583; + staticDynamicStruct[1].asset = IERC20(0x4fB30C5A3aC8e85bC32785518633303C4590752d); + staticDynamicStruct[1].severity = Severity.Medium; + staticDynamicStruct[1].staticSeverities = [Severity.Medium, Severity.Low, Severity.High]; + staticDynamicStruct[1].dynamicSeverities = [Severity.Medium, Severity.High, Severity.Low, Severity.High]; + staticDynamicStruct[1].dynamicAddressArray = [0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF]; + staticDynamicStruct[1].staticAddressArray = [0x853d955aCEf822Db058eb8505911ED77F175b99e, 0x104592a158490a9228070E0A8e5343B499e125D0]; + staticDynamicStruct[1].dynamicIntArray.push(10000); + staticDynamicStruct[1].dynamicIntArray.push(-10000); + staticDynamicStruct[1].dynamicIntArray.push(64000); + staticDynamicStruct[1].staticIntArray = [int64(100), 200, 300, 400, 500]; + staticDynamicStruct[1].shortString = "string < 31 characters"; + staticDynamicStruct[1].longString = "a string that is over 31 characters long"; + staticDynamicStruct[1].struct1 = ContractLevelStruct$_1({ + param1: 5e18, + param2: 0x6B175474E89094C44Da98b954EedeAC495271d0F, + param3: 2, + param4: bytes1(0x89) + }); + staticDynamicStruct[1].staticStruct1[1] = ContractLevelStruct$_1({ + param1: 6e18, + param2: 0xdAC17F958D2ee523a2206206994597C13D831ec7, + param3: 129, + param4: bytes1(0x99) + }); + + twoSlots2x[0] = TwoSlots( + 0xFFFF00000F000000000000000000000000000000000000000000000000FFFFFF, + 0xFFFFF0000FF0000000000000000000000000000000000000000000000FFFFFFF + ); + twoSlots2x[1] = TwoSlots( + 0xFF0000000F0000000000000000000000000000000000000000000000000000FF, + 0xFFF000000FF00000000000000000000000000000000000000000000000000FFF + ); + + twoSlots3x4[0][0] = TwoSlots( + 0xFF000000000000000000000000000000000000000000000000000000000000FF, + 0xFFF000000F000000000000000000000000000000000000000000000000000FFF + ); + twoSlots3x4[0][1] = TwoSlots( + 0xAF0000000000000000000000000000000000000000000000000000000000000F, + 0xAF0000000F0000000000000000000000000000000000000000000000000000FF + ); + twoSlots3x4[0][2] = TwoSlots( + 0xABC0000000000000000000000000000000000000000000000000000000000321, + 0xABC000000F000000000000000000000000000000000000000000000000000456 + ); + twoSlots3x4[1][0] = TwoSlots( + 0xDEF0000000000000000000000000F000000000000000000000000000000000F1, + 0xDEF000000F000000000000000000F00000000000000000000000000000000FF1 + ); + twoSlots3x4[1][1] = TwoSlots( + 0x1000000000000000000000000000F00000000000000000000000000000000001, + 0x300000000F000000000000000000F00000000000000000000000000000000003 + ); + twoSlots3x4[3][2] = TwoSlots( + 0xB00000000000000000000000000000000000000000000000000000F00000000D, + 0xF00000000F00000000000000000000000000000000000000000000F00000000F + ); + + twoSlots3xDyn.push(); + twoSlots3xDyn[0][0] = TwoSlots( + 0xF00000000000000000000000000000000000000000000000000000000000000F, + 0xF0000000F00000000000000000000000000000000000000000000000000000FF + ); + twoSlots3xDyn[0][1] = TwoSlots( + 0xFF00000000000000000F0000000000000000000000000000000000000000000F, + 0xFF000000F0000000000F000000000000000000000000000000000000000000FF + ); + + testAddress = address(this); + } + + function setTestString(string memory _testStr) public { + testString = _testStr; + } + + function setTestBytes(bytes memory _testBytes) public { + testBytes = _testBytes; + } + + function setTestUint256(uint256 _testUint256) public { + testUint256 = _testUint256; + } + + function setTestInt256(int256 _testInt256) public { + testInt256 = _testInt256; + } + + function setTestAddress(address _testAddress) public { + testAddress = _testAddress; + } +} diff --git a/src/ts/SlotValueCache.ts b/src/ts/SlotValueCache.ts new file mode 100644 index 00000000..ae8f3b1a --- /dev/null +++ b/src/ts/SlotValueCache.ts @@ -0,0 +1,77 @@ +import { BigNumber, BigNumberish } from '@ethersproject/bignumber' + +const debug = require('debug')('sol2uml') + +// key is the storage slot number in hexadecimal format with a leading 0x. eg 0x0, 0x1... +type SlotCache = { [key: string]: string } + +/** + * Singleton that caches a mapping of slot keys to values. + * Assumes all data is read from the same block and contract + */ +export class SlotValueCache { + // Singleton of cached slot keys mapped to values + private static slotCache: SlotCache = {} + + /** + * @param slotKeys array of slot numbers or slot keys in hexadecimal format + * @return cachedValues array of the slot values that are in the cache. + * @return missingKeys array of the slot keys that are not cached in hexadecimal format. + */ + public static readSlotValues(slotKeys: readonly BigNumberish[]): { + cachedValues: string[] + missingKeys: string[] + } { + const cachedValues: string[] = [] + const missingKeys: string[] = [] + slotKeys.forEach((slotKey, i) => { + const key = BigNumber.from(slotKey).toHexString() + if (this.slotCache[key]) { + cachedValues.push(this.slotCache[key]) + } else { + missingKeys.push(key) + } + }) + + return { cachedValues, missingKeys } + } + + /** + * Adds the missing slot values to the cache and then returns all slot values from + * the cache for each of the `slotKeys`. + * @param slotKeys array of slot numbers or keys in hexadecimal format. + * @param missingKeys array of the slot keys that are not cached in hexadecimal format. + * @param missingValues array of slot values in hexadecimal format. + * @return values array of slot values for each of the `slotKeys`. + */ + public static addSlotValues( + slotKeys: readonly BigNumberish[], + missingKeys: readonly string[], + missingValues: readonly string[] + ): string[] { + if (missingKeys?.length !== missingValues?.length) { + throw Error( + `${missingKeys?.length} keys does not match ${missingValues?.length} values` + ) + } + missingKeys.forEach((key, i) => { + if (!this.slotCache[key]) { + debug(`cached slot ${key} with ${missingValues[i]}`) + this.slotCache[key] = missingValues[i] + } + }) + return slotKeys.map((slotKey) => { + const key = BigNumber.from(slotKey).toHexString() + // it should find the slot value in the cache. if not it'll return undefined + return this.slotCache[key] + }) + } + + /** + * Used for testing purposes to clear the cache. + * This allows tests to run against different contracts and blockTags + */ + public static clear() { + this.slotCache = {} + } +} diff --git a/src/ts/__tests__/etherscanParser.test.ts b/src/ts/__tests__/etherscanParser.test.ts index af9998ac..5484cc2b 100644 --- a/src/ts/__tests__/etherscanParser.test.ts +++ b/src/ts/__tests__/etherscanParser.test.ts @@ -1,4 +1,4 @@ -import { EtherscanParser } from '../index' +import { EtherscanParser, parseRemapping, renameFile } from '../index' jest.setTimeout(20000) // timeout for each test in milliseconds @@ -45,4 +45,20 @@ describe('Etherscan', () => { ) } }) + describe('remapping filename ', () => { + test.each` + fileName | mapping | expected + ${'@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'} | ${'@openzeppelin/=lib/openzeppelin-contracts/'} | ${'lib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sol'} + ${'@openzeppelin/IERC20.sol'} | ${'@openzeppelin/=lib/openzeppelin-contracts/'} | ${'lib/openzeppelin-contracts/IERC20.sol'} + ${'@openzeppelin/lib/openzeppelin-contracts/IERC20.sol'} | ${'@openzeppelin/=lib/openzeppelin-contracts/'} | ${'lib/openzeppelin-contracts/lib/openzeppelin-contracts/IERC20.sol'} + ${'node_modules/@openzeppelin/contracts-upgradeable/proxy/Initializable.sol'} | ${'node_modules/='} | ${'@openzeppelin/contracts-upgradeable/proxy/Initializable.sol'} + ${'Interface.sol'} | ${'face=xxx'} | ${'Interface.sol'} + ${'./contracts/Interface.sol'} | ${'node_modules/='} | ${'./contracts/Interface.sol'} + ${'Interface.sol'} | ${'I=III'} | ${'IIInterface.sol'} + ${'Interface.sol'} | ${'i=xxx'} | ${'Interface.sol'} + `('$mapping ', ({ fileName, mapping, expected }) => { + const remapping = parseRemapping(mapping) + expect(renameFile(fileName, [remapping])).toEqual(expected) + }) + }) }) diff --git a/src/ts/__tests__/fileParser.test.ts b/src/ts/__tests__/fileParser.test.ts index db5e95fb..f3a1c64d 100644 --- a/src/ts/__tests__/fileParser.test.ts +++ b/src/ts/__tests__/fileParser.test.ts @@ -6,7 +6,7 @@ describe('Parser', () => { const files = await getSolidityFilesFromFolderOrFile( './src/contracts' ) - expect(files).toHaveLength(28) + expect(files).toHaveLength(36) }) test('get Solidity files from folder with no sol files', async () => { diff --git a/src/ts/__tests__/slotValues.test.ts b/src/ts/__tests__/slotValues.test.ts index 8ae07825..17d29644 100644 --- a/src/ts/__tests__/slotValues.test.ts +++ b/src/ts/__tests__/slotValues.test.ts @@ -1,5 +1,13 @@ -import { getStorageValue, getStorageValues } from '../slotValues' +import { + dynamicSlotSize, + escapeString, + getSlotValue, + getSlotValues, + parseValue, +} from '../slotValues' import { BigNumber } from 'ethers' +import { AttributeType } from '../umlClass' +import { SlotValueCache } from '../SlotValueCache' const emissionController = '0xBa69e6FC7Df49a3b75b565068Fb91ff2d9d91780' const usdc = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' @@ -9,16 +17,19 @@ if (!process.env.NODE_URL) throw Error('Must export env var NODE_URL') const url = process.env.NODE_URL describe('Slot Values', () => { + beforeEach(() => { + SlotValueCache.clear() + }) test('Emissions controller first slot latest', async () => { expect( - await getStorageValue(url, emissionController, 1, '15272562') + await getSlotValue(url, emissionController, 1, '15272562') ).toEqual( '0x00000000000000000000000000000000000000000000000000000AB700000A96' ) }) test('Emissions controller first slot on deployment', async () => { expect( - await getStorageValue( + await getSlotValue( url, emissionController, BigNumber.from(1), @@ -30,29 +41,30 @@ describe('Slot Values', () => { }) test('Emissions controller first slot before deployment', async () => { expect( - await getStorageValue(url, emissionController, 1, 13761570) + await getSlotValue(url, emissionController, 1, 13761570) ).toEqual( '0x0000000000000000000000000000000000000000000000000000000000000000' ) }) test('USDC second slot', async () => { - expect(await getStorageValue(url, usdc, 0)).toEqual( + expect(await getSlotValue(url, usdc, 0, 'latest')).toEqual( '0x000000000000000000000000FCB19E6A322B27C06842A71E8C725399F049AE3A' ) }) test('mUSD proxy admin slot', async () => { expect( - await getStorageValue( + await getSlotValue( url, musd, - '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103' + '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103', + 'latest' ) ).toEqual( '0x0000000000000000000000005C8EB57B44C1C6391FC7A8A0CF44D26896F92386' ) }) test('Emissions Controller batch', async () => { - const values = await getStorageValues( + const values = await getSlotValues( url, emissionController, [ @@ -80,7 +92,7 @@ describe('Slot Values', () => { ]) }) test('Emissions Controller reserve slot order', async () => { - const values = await getStorageValues( + const values = await getSlotValues( url, emissionController, [2, 1, 0], @@ -93,4 +105,113 @@ describe('Slot Values', () => { '0x0000000000000000000000000000000000000000000000000000000000000001', ]) }) + describe('calc dynamic slot size for string value', () => { + test.each` + slotValue | expected + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${0} + ${'0x1000000000000000000000000000000000000000000000000000000000000000'} | ${0} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00'} | ${0} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0'} | ${0} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE'} | ${0} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${0} + ${'0x0000000000000000000000000000000000000000000000000000000000000002'} | ${0} + ${'0x0000000000000000000000000000000000000000000000000000000000000003'} | ${1} + ${'0x0000000000000000000000000000000000000000000000000000000000000009'} | ${4} + ${'0x000000000000000000000000000000000000000000000000000000000000000B'} | ${5} + ${'0x0000000000000000000000000000000000000000000000000000000000000015'} | ${10} + ${'0x000000000000000000000000000000000000000000000000000000000000003F'} | ${31} + ${'0x0000000000000000000000000000000000000000000000000000000000000040'} | ${0} + ${'0x0000000000000000000000000000000000000000000000000000000000000041'} | ${32} + ${'0x0000000000000000000000000000000000000000000000000000000000000042'} | ${0} + ${'0x0000000000000000000000000000000000000000000000000000000000000043'} | ${33} + ${'0x0000000000000000000000000000000000000000000000000000000000000101'} | ${128} + ${'0x0000000000000000000000000000000000000000000000000000000000100001'} | ${524288} + `('$value', ({ slotValue, expected }) => { + expect(dynamicSlotSize({ slotValue })).toEqual(expected) + }) + }) + describe('parse value from slot data', () => { + const baseVariable = { + id: 1, + fromSlot: 0, + toSlot: 0, + attributeType: AttributeType.Elementary, + getValue: true, + displayValue: true, + dynamic: false, + } + test.each` + value | type | byteOffset | byteSize | dynamic | expected + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${'bool'} | ${0} | ${1} | ${false} | ${'false'} + ${'0x0000000000000000000000000000000000000000000000000000000000000100'} | ${'bool'} | ${0} | ${1} | ${false} | ${'false'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00'} | ${'bool'} | ${0} | ${1} | ${false} | ${'false'} + ${'0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'} | ${'bool'} | ${31} | ${1} | ${false} | ${'false'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'bool'} | ${0} | ${1} | ${false} | ${'true'} + ${'0x0000000000000000000000000000000000000000000000000000000000000100'} | ${'bool'} | ${1} | ${1} | ${false} | ${'true'} + ${'0x0100000000000000000000000000000000000000000000000000000000000000'} | ${'bool'} | ${31} | ${1} | ${false} | ${'true'} + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${'string'} | ${0} | ${32} | ${false} | ${'\\"\\"'} + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${'string'} | ${0} | ${32} | ${true} | ${'\\"\\"'} + ${'0x5400000000000000000000000000000000000000000000000000000000000002'} | ${'string'} | ${0} | ${32} | ${true} | ${'\\"T\\"'} + ${'0x5465737453746F7261676520636F6E7472616374000000000000000000000028'} | ${'string'} | ${0} | ${32} | ${true} | ${'\\"TestStorage contract\\"'} + ${'0x65786163746C7920333220636861727320736F2075736573203220736C6F7473'} | ${'string'} | ${0} | ${32} | ${false} | ${'\\"exactly 32 chars so uses 2 slots\\"'} + ${'0x20746872656520736C6F74730000000000000000000000000000000000000000'} | ${'string'} | ${20} | ${12} | ${false} | ${'\\" three slots\\"'} + ${'0x0000000000000000000000000000000000000000000000000000000000000041'} | ${'string'} | ${0} | ${32} | ${true} | ${'32'} + ${'0x0000000000000000000000000000000000000000000000000000000000000099'} | ${'string'} | ${0} | ${32} | ${true} | ${'76'} + ${'0x0000000000000000000000000000000000000000000000000000000000000101'} | ${'string'} | ${0} | ${32} | ${true} | ${'128'} + ${'0xFFEEDDCCBBAA9988770011000000000000000000000000000000000000000016'} | ${'bytes'} | ${0} | ${32} | ${true} | ${'0xFFEEDDCCBBAA9988770011'} + ${'0xEC0B854938343F85EB39A6648B9E449C2E4AEE4DC9B4E96AB592F9F497D0513E'} | ${'bytes'} | ${0} | ${32} | ${true} | ${'0xEC0B854938343F85EB39A6648B9E449C2E4AEE4DC9B4E96AB592F9F497D051'} + ${'0x2619EC68B255542E3DA68C054BFE0D7D0F27B7FDBEFC8BBCCDD23188FC71FE7F'} | ${'bytes'} | ${0} | ${32} | ${false} | ${'0x2619EC68B255542E3DA68C054BFE0D7D0F27B7FDBEFC8BBCCDD23188FC71FE7F'} + ${'0x2619EC68B255542E3DA68C054BFE0D7D0F27B7FDBEFC8BBCCDD23188FC71FE7F'} | ${'bytes32'} | ${0} | ${32} | ${false} | ${'0x2619EC68B255542E3DA68C054BFE0D7D0F27B7FDBEFC8BBCCDD23188FC71FE7F'} + ${'0x2619EC68B255542E3DA68C054BFE0D7D0F27B7FDBEFC8BBCCDD23188FC71FE7F'} | ${'bytes1'} | ${0} | ${1} | ${false} | ${'0x7F'} + ${'0x000000000000000000000000E2F2A5C287993345A840DB3B0845FBC70F5935A5'} | ${'address'} | ${0} | ${20} | ${false} | ${'0xe2f2a5C287993345a840Db3B0845fbC70f5935a5'} + ${'0xE2F2A5C287993345A840DB3B0845FBC70F5935A5000000000000000000000000'} | ${'address'} | ${12} | ${20} | ${false} | ${'0xe2f2a5C287993345a840Db3B0845fbC70f5935a5'} + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${'uint256'} | ${0} | ${32} | ${false} | ${'0'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'uint256'} | ${0} | ${32} | ${false} | ${'1'} + ${'0x000000000000000000000000000000000000000000000000000000000000000A'} | ${'uint256'} | ${0} | ${32} | ${false} | ${'10'} + ${'0x000000000000000000000000000000000000000000000000FEDCBA9876543210'} | ${'uint256'} | ${0} | ${32} | ${false} | ${'18,364,758,544,493,064,720'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'uint'} | ${0} | ${32} | ${false} | ${'1'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'uint8'} | ${0} | ${1} | ${false} | ${'1'} + ${'0x00000000000000000000000000000000000000000000000000000000000000FF'} | ${'uint8'} | ${0} | ${1} | ${false} | ${'255'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00'} | ${'uint8'} | ${0} | ${1} | ${false} | ${'0'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01'} | ${'uint8'} | ${0} | ${1} | ${false} | ${'1'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'} | ${'uint8'} | ${0} | ${1} | ${false} | ${'255'} + ${'0x0000000000000000000000000000000000000000000000000000000000000000'} | ${'int256'} | ${0} | ${32} | ${false} | ${'0'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'int256'} | ${0} | ${32} | ${false} | ${'1'} + ${'0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'} | ${'int256'} | ${0} | ${32} | ${false} | ${'57,896,044,618,658,097,711,785,492,504,343,953,926,634,992,332,820,282,019,728,792,003,956,564,819,967'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'} | ${'int256'} | ${0} | ${32} | ${false} | ${'-1'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC01'} | ${'int256'} | ${0} | ${32} | ${false} | ${'-1,023'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'int8'} | ${0} | ${1} | ${false} | ${'1'} + ${'0x00000000000000000000000000000000000000000000000000000000000000FF'} | ${'int8'} | ${0} | ${1} | ${false} | ${'-1'} + ${'0x0000000000000000000000000000000000000000000000000000000000000080'} | ${'int8'} | ${0} | ${1} | ${false} | ${'-128'} + ${'0x0000000000000000000000000000000000000000000000000000000000000001'} | ${'uint'} | ${0} | ${32} | ${false} | ${'1'} + ${'0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'} | ${'int'} | ${0} | ${32} | ${false} | ${'-1'} + `( + '$type offset $byteOffset size $byteSize $value', + ({ byteOffset, byteSize, value, type, dynamic, expected }) => { + expect( + parseValue({ + ...baseVariable, + byteSize, + byteOffset, + type, + slotValue: value, + dynamic, + }) + ).toEqual(expected) + } + ) + }) + describe('escape string', () => { + test.each` + text | expected + ${'"Some string"'} | ${'\\"Some string\\"'} + ${'size < 31'} | ${'size \\< 31'} + ${'size >= 31'} | ${'size \\>= 31'} + ${'1 & 2'} | ${'1 \\& 2'} + ${'"<&>'} | ${'\\"\\<\\&\\>'} + ${'""<<&&>>>'} | ${'\\"\\"\\<\\<\\&\\&\\>\\>\\>'} + `('text $text', ({ text, expected }) => { + expect(escapeString(text)).toEqual(expected) + }) + }) }) diff --git a/src/ts/__tests__/storage.test.ts b/src/ts/__tests__/storage.test.ts index 92fa2fa2..52456757 100644 --- a/src/ts/__tests__/storage.test.ts +++ b/src/ts/__tests__/storage.test.ts @@ -5,10 +5,46 @@ import { ClassStereotype, UmlClass, } from '../umlClass' -import { calcStorageByteSize, isElementary } from '../converterClasses2Storage' +import { + calcSectionOffset, + calcStorageByteSize, + isElementary, +} from '../converterClasses2Storage' import { formatBytes32String, parseBytes32String } from 'ethers/lib/utils' +import { BigNumber } from 'ethers' describe('storage parser', () => { + describe('is elementary type?', () => { + test.each` + type | expected + ${'address'} | ${true} + ${'bool'} | ${true} + ${'int'} | ${true} + ${'uint'} | ${true} + ${'int256'} | ${true} + ${'uint256'} | ${true} + ${'uint8'} | ${true} + ${'int8'} | ${true} + ${'uint32'} | ${true} + ${'int32'} | ${true} + ${'bytes'} | ${true} + ${'bytes32'} | ${true} + ${'bytes1'} | ${true} + ${'bytes31'} | ${true} + ${'string'} | ${true} + ${'address[]'} | ${false} + ${'address[2]'} | ${false} + ${'bool[]'} | ${false} + ${'bool[MAX]'} | ${false} + ${'int8[]'} | ${false} + ${'int8[10]'} | ${false} + ${'mapping(address=>uint256)'} | ${false} + ${'mapping(address=>ContractLevelStruct2)'} | ${false} + ${'IERC20'} | ${false} + `('$type', ({ type, expected }) => { + expect(isElementary(type)).toEqual(expected) + }) + }) describe('calc storage bytes size of', () => { const defaultClassProperties: ClassProperties = { name: 'test', @@ -46,124 +82,135 @@ describe('storage parser', () => { }), ] test.each` - type | expected - ${'address'} | ${20} - ${'bool'} | ${1} - ${'int'} | ${32} - ${'uint'} | ${32} - ${'int256'} | ${32} - ${'uint256'} | ${32} - ${'uint8'} | ${1} - ${'int8'} | ${1} - ${'uint32'} | ${4} - ${'int32'} | ${4} - ${'bytes'} | ${32} - ${'bytes32'} | ${32} - ${'bytes1'} | ${1} - ${'bytes31'} | ${31} - ${'string'} | ${32} - `('elementary type $type', ({ type, expected }) => { - const umlClass = new UmlClass(defaultClassProperties) - const attribute: Attribute = { - attributeType: AttributeType.Elementary, - type, - name: 'varName', + type | expectedSize | expectedDynamic + ${'address'} | ${20} | ${false} + ${'bool'} | ${1} | ${false} + ${'int'} | ${32} | ${false} + ${'uint'} | ${32} | ${false} + ${'int256'} | ${32} | ${false} + ${'uint256'} | ${32} | ${false} + ${'uint8'} | ${1} | ${false} + ${'int8'} | ${1} | ${false} + ${'uint32'} | ${4} | ${false} + ${'int32'} | ${4} | ${false} + ${'bytes'} | ${32} | ${true} + ${'bytes32'} | ${32} | ${false} + ${'bytes1'} | ${1} | ${false} + ${'bytes31'} | ${31} | ${false} + ${'string'} | ${32} | ${true} + `( + 'elementary type $type', + ({ type, expectedSize, expectedDynamic }) => { + const umlClass = new UmlClass(defaultClassProperties) + const attribute: Attribute = { + attributeType: AttributeType.Elementary, + type, + name: 'varName', + } + const { size, dynamic } = calcStorageByteSize( + attribute, + umlClass, + [] + ) + expect(size).toEqual(expectedSize) + expect(dynamic).toEqual(expectedDynamic) } - const { size } = calcStorageByteSize(attribute, umlClass, []) - expect(size).toEqual(expected) - }) + ) // TODO implement support for sizing expressions. eg // ${'address[N_COINS * 2]'} | ${128} test.each` - type | expected - ${'address[]'} | ${32} - ${'address[1]'} | ${32} - ${'address[2]'} | ${64} - ${'address[3]'} | ${96} - ${'address[4]'} | ${128} - ${'address[2][2]'} | ${128} - ${'address[32]'} | ${1024} - ${'address[][2]'} | ${64} - ${'address[2][]'} | ${32} - ${'address[][10]'} | ${320} - ${'address[][][2]'} | ${64} - ${'address[][4][3]'} | ${384} - ${'address[][3][][2]'} | ${64} - ${'address[3][2][]'} | ${32} - ${'address[][2][2][2]'} | ${256} - ${'address[][2][]'} | ${32} - ${'address[N_COINS]'} | ${64} - ${'address[N_COINS][N_COINS]'} | ${128} - ${'IERC20[]'} | ${32} - ${'IERC20[1]'} | ${32} - ${'IERC20[2]'} | ${64} - ${'IERC20[3]'} | ${96} - ${'IERC20[4]'} | ${128} - ${'uint8[33][2][2]'} | ${256} - ${'bytes32[]'} | ${32} - ${'bytes1[1]'} | ${32} - ${'bytes1[2]'} | ${32} - ${'bytes1[16]'} | ${32} - ${'bytes1[17]'} | ${32} - ${'bytes1[32]'} | ${32} - ${'bytes1[33]'} | ${64} - ${'bytes16[2]'} | ${32} - ${'bytes17[2]'} | ${64} - ${'bytes30[2]'} | ${64} - ${'bytes30[6][2]'} | ${384} - ${'bytes30[2][6]'} | ${384} - ${'bytes128[4]'} | ${512} - ${'bytes256[2]'} | ${512} - ${'bytes256[]'} | ${32} - ${'bytes32[1]'} | ${32} - ${'bytes32[2]'} | ${64} - ${'bool[1]'} | ${32} - ${'bool[16]'} | ${32} - ${'bool[32]'} | ${32} - ${'bool[33]'} | ${64} - ${'bool[2][3]'} | ${3 * 32} - ${'bool[3][2]'} | ${2 * 32} - ${'bool[2][]'} | ${32} - ${'bool[][2]'} | ${2 * 32} - ${'bool[][16]'} | ${16 * 32} - ${'bool[][32]'} | ${32 * 32} - ${'bool[][33]'} | ${33 * 32} - ${'bool[33][3]'} | ${3 * 2 * 32} - ${'bool[][2][3]'} | ${3 * 2 * 32} - ${'bool[][][2][3]'} | ${3 * 2 * 32} - ${'bool[][2][]'} | ${32} - ${'bool[][][3]'} | ${3 * 32} - ${'bool[33][2]'} | ${2 * 2 * 32} - ${'bool[33][2][2]'} | ${2 * 2 * 2 * 32} - ${'bool[][4][3]'} | ${3 * 4 * 32} - ${'bool[][64][64]'} | ${64 * 64 * 32} - ${'bool[64][][64]'} | ${64 * 32} - ${'bool[64][64][]'} | ${32} - ${'uint120[2]'} | ${32} - ${'uint120[3]'} | ${64} - ${'uint120[4]'} | ${64} - ${'uint120[6]'} | ${96} - ${'TwoSlots[3][4]'} | ${4 * 3 * 2 * 32} - ${'TwoSlots[4][3]'} | ${3 * 4 * 2 * 32} - ${'TwoSlots[][3]'} | ${3 * 32} - ${'TwoSlots[3][]'} | ${32} - ${'TwoSlots[][]'} | ${32} - ${'TwoSlots[][4][3]'} | ${3 * 4 * 32} - ${'TwoSlots[4][3][]'} | ${32} - `('array type $type', ({ type, expected }) => { + type | expectedSize | expectedDynamic + ${'address[]'} | ${32} | ${true} + ${'address[1]'} | ${32} | ${false} + ${'address[2]'} | ${64} | ${false} + ${'address[3]'} | ${96} | ${false} + ${'address[4]'} | ${128} | ${false} + ${'address[2][2]'} | ${128} | ${false} + ${'address[32]'} | ${1024} | ${false} + ${'address[][2]'} | ${64} | ${false} + ${'address[2][]'} | ${32} | ${true} + ${'address[][10]'} | ${320} | ${false} + ${'address[][][2]'} | ${64} | ${false} + ${'address[][4][3]'} | ${384} | ${false} + ${'address[][3][][2]'} | ${64} | ${false} + ${'address[3][2][]'} | ${32} | ${true} + ${'address[][2][2][2]'} | ${256} | ${false} + ${'address[][2][]'} | ${32} | ${true} + ${'address[N_COINS]'} | ${64} | ${false} + ${'address[N_COINS][N_COINS]'} | ${128} | ${false} + ${'IERC20[]'} | ${32} | ${true} + ${'IERC20[1]'} | ${32} | ${false} + ${'IERC20[2]'} | ${64} | ${false} + ${'IERC20[3]'} | ${96} | ${false} + ${'IERC20[4]'} | ${128} | ${false} + ${'uint8[33][2][2]'} | ${256} | ${false} + ${'bytes32[]'} | ${32} | ${true} + ${'bytes1[1]'} | ${32} | ${false} + ${'bytes1[2]'} | ${32} | ${false} + ${'bytes1[16]'} | ${32} | ${false} + ${'bytes1[17]'} | ${32} | ${false} + ${'bytes1[32]'} | ${32} | ${false} + ${'bytes1[33]'} | ${64} | ${false} + ${'bytes16[2]'} | ${32} | ${false} + ${'bytes17[2]'} | ${64} | ${false} + ${'bytes30[2]'} | ${64} | ${false} + ${'bytes30[6][2]'} | ${384} | ${false} + ${'bytes30[2][6]'} | ${384} | ${false} + ${'bytes128[4]'} | ${512} | ${false} + ${'bytes256[2]'} | ${512} | ${false} + ${'bytes256[]'} | ${32} | ${true} + ${'bytes32[1]'} | ${32} | ${false} + ${'bytes32[2]'} | ${64} | ${false} + ${'bool[1]'} | ${32} | ${false} + ${'bool[16]'} | ${32} | ${false} + ${'bool[32]'} | ${32} | ${false} + ${'bool[33]'} | ${64} | ${false} + ${'bool[2][3]'} | ${3 * 32} | ${false} + ${'bool[3][2]'} | ${2 * 32} | ${false} + ${'bool[2][]'} | ${32} | ${true} + ${'bool[][2]'} | ${2 * 32} | ${false} + ${'bool[][16]'} | ${16 * 32} | ${false} + ${'bool[][32]'} | ${32 * 32} | ${false} + ${'bool[][33]'} | ${33 * 32} | ${false} + ${'bool[33][3]'} | ${3 * 2 * 32} | ${false} + ${'bool[][2][3]'} | ${3 * 2 * 32} | ${false} + ${'bool[][][2][3]'} | ${3 * 2 * 32} | ${false} + ${'bool[][2][]'} | ${32} | ${true} + ${'bool[][][3]'} | ${3 * 32} | ${false} + ${'bool[33][2]'} | ${2 * 2 * 32} | ${false} + ${'bool[33][2][2]'} | ${2 * 2 * 2 * 32} | ${false} + ${'bool[][4][3]'} | ${3 * 4 * 32} | ${false} + ${'bool[][64][64]'} | ${64 * 64 * 32} | ${false} + ${'bool[64][][64]'} | ${64 * 32} | ${false} + ${'bool[64][64][]'} | ${32} | ${true} + ${'uint120[2]'} | ${32} | ${false} + ${'uint120[3]'} | ${64} | ${false} + ${'uint120[4]'} | ${64} | ${false} + ${'uint120[6]'} | ${96} | ${false} + ${'TwoSlots[3][4]'} | ${4 * 3 * 2 * 32} | ${false} + ${'TwoSlots[4][3]'} | ${3 * 4 * 2 * 32} | ${false} + ${'TwoSlots[][3]'} | ${3 * 32} | ${false} + ${'TwoSlots[3][]'} | ${32} | ${true} + ${'TwoSlots[][]'} | ${32} | ${true} + ${'TwoSlots[][4][3]'} | ${3 * 4 * 32} | ${false} + ${'TwoSlots[4][3][]'} | ${32} | ${true} + ${'uint56[21]'} | ${32 * 6} | ${false} + ${'uint72[7]'} | ${32 * 3} | ${false} + `('array type $type', ({ type, expectedSize, expectedDynamic }) => { const umlCLass = new UmlClass(defaultClassProperties) const attribute: Attribute = { attributeType: AttributeType.Array, type, name: 'arrayName', } - const { size } = calcStorageByteSize( + const { size, dynamic } = calcStorageByteSize( attribute, umlCLass, otherClasses ) - expect(size).toEqual(expected) + expect(size).toEqual(expectedSize) + expect(dynamic).toEqual(expectedDynamic) }) describe('structs', () => { const otherClasses: UmlClass[] = [ @@ -270,14 +317,14 @@ describe('storage parser', () => { ] test.each` types | expected - ${['address', 'address', 'address']} | ${96} - ${['address', 'bytes12', 'bytes12', 'address']} | ${64} + ${['address', 'address', 'address']} | ${32 + 32 + 32} + ${['address', 'bytes12', 'bytes12', 'address']} | ${32 + 32} ${['IERC20']} | ${32} - ${['IERC20', 'IERC20', 'IERC20']} | ${96} - ${['IERC20[2]']} | ${64} - ${['IERC20[3]']} | ${96} - ${['IERC20[2]', 'IERC20[]']} | ${96} - ${['IERC20[2]', 'IERC20[3]']} | ${160} + ${['IERC20', 'IERC20', 'IERC20']} | ${32 * 3} + ${['IERC20[2]']} | ${32 * 2} + ${['IERC20[3]']} | ${32 * 3} + ${['IERC20[2]', 'IERC20[]']} | ${32 * 2 + 32} + ${['IERC20[2]', 'IERC20[3]']} | ${32 * 2 + 32 * 3} ${['IERC20', 'bytes12', 'bytes12', 'IERC20']} | ${64} ${['bytes31', 'bytes2', 'bytes31']} | ${96} ${['uint256', 'bytes32']} | ${64} @@ -285,26 +332,26 @@ describe('storage parser', () => { ${['bool[12]', 'uint8[12]']} | ${64} ${['bytes30', 'bytes30', 'bytes30']} | ${96} ${['uint256[]', 'bytes32[2]']} | ${96} - ${['uint256[2]', 'bytes32[2]']} | ${128} - ${['bool', 'bool[2]', 'bool']} | ${96} - ${['bool', 'bool[33]', 'bool']} | ${128} - ${['uint16', 'bytes30[2]', 'uint16']} | ${128} + ${['uint256[2]', 'bytes32[2]']} | ${32 * 2 + 32 * 2} + ${['bool', 'bool[2]', 'bool']} | ${32 + 32 + 32} + ${['bool', 'bool[33]', 'bool']} | ${32 + 64 + 32} + ${['uint16', 'bytes30[2]', 'uint16']} | ${32 + 64 + 32} ${['ContractLevelStruct0']} | ${64} ${['ContractLevelStruct1']} | ${64} - ${['ContractLevelStruct2']} | ${192} - ${['ContractLevelStruct2[2]', 'address']} | ${416} - ${['ContractLevelStruct0', 'ContractLevelStruct1']} | ${128} + ${['ContractLevelStruct2']} | ${32 * 6} + ${['ContractLevelStruct2[2]', 'address']} | ${32 * 6 * 2 + 32} + ${['ContractLevelStruct0', 'ContractLevelStruct1']} | ${64 + 64} ${['ContractLevelStruct0[]', 'address']} | ${64} - ${['ContractLevelStruct1[2]', 'address']} | ${160} - ${['ContractLevelStruct1[2]', 'ContractLevelStruct0[3]']} | ${320} - ${['ContractLevelStruct2[]', 'address']} | ${64} - ${['address', 'ContractLevelStruct2[]']} | ${64} - ${['bool', 'ContractLevelStruct0', 'bool']} | ${128} + ${['ContractLevelStruct1[2]', 'address']} | ${64 * 2 + 32} + ${['ContractLevelStruct1[2]', 'ContractLevelStruct0[3]']} | ${64 * 2 + 64 * 3} + ${['ContractLevelStruct2[]', 'address']} | ${32 + 32} + ${['address', 'ContractLevelStruct2[]']} | ${32 + 32} + ${['bool', 'ContractLevelStruct0', 'bool']} | ${32 + 64 + 32} ${['enum0']} | ${32} ${['enum0', 'enum1']} | ${32} ${['enum0', 'enum1', 'bytes30']} | ${32} ${['enum0', 'enum1', 'bytes31']} | ${64} - ${['enum0', 'enum1', 'bytes30[2]']} | ${96} + ${['enum0', 'enum1', 'bytes30[2]']} | ${32 + 32 * 2} ${['bool', 'enum0', 'bool']} | ${32} `('struct with types $types', ({ types, expected }) => { const testAttributes: Attribute[] = [] @@ -335,11 +382,13 @@ describe('storage parser', () => { type: 'ContractLevelStruct', name: 'structName', } - const { size } = calcStorageByteSize(attribute, umlCLass, [ - ...otherClasses, - testStruct, - ]) + const { size, dynamic } = calcStorageByteSize( + attribute, + umlCLass, + [...otherClasses, testStruct] + ) expect(size).toEqual(expected) + expect(dynamic).toEqual(false) }) }) }) @@ -357,4 +406,28 @@ describe('storage parser', () => { ) }) }) + describe('calc dynamic array starting slot', () => { + const variable = { + id: 0, + toSlot: 2, + byteSize: 32, + byteOffset: 0, + type: 'array', + attributeType: AttributeType.Array, + dynamic: true, + getValue: false, + displayValue: false, + } + test.each` + slot | expected + ${1} | ${'0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6'} + ${BigNumber.from(1)} | ${'0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6'} + ${'1'} | ${'0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6'} + ${'0x01'} | ${'0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6'} + `('slot $slot', ({ slot, expected }) => { + expect(calcSectionOffset({ ...variable, fromSlot: slot })).toEqual( + expected + ) + }) + }) }) diff --git a/src/ts/associations.ts b/src/ts/associations.ts index dfc3de8f..16e3422a 100644 --- a/src/ts/associations.ts +++ b/src/ts/associations.ts @@ -4,30 +4,13 @@ import { Association, Import, UmlClass } from './umlClass' export const findAssociatedClass = ( association: Association, sourceUmlClass: UmlClass, - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], searchedAbsolutePaths: string[] = [] ): UmlClass | undefined => { - let umlClass = umlClasses.find((targetUmlClass) => { - // is the source class link via the association to the target class? - if (isAssociated(association, sourceUmlClass, targetUmlClass)) - return true + const umlClass = umlClasses.find((targetUmlClass) => + isAssociated(association, sourceUmlClass, targetUmlClass) + ) - // Not linked so now try linking to target under the node_modules folder. - // eg remove node_modules from node_modules/@openzeppelin/contracts-upgradeable/proxy/Initializable.sol - // is the target class under node_modules? - if (targetUmlClass.relativePath.match(/^node_modules\//)) { - // clone the target and updated absolutePath and relativePath so it's no longer under node_modules - const clonedTargetClass = new UmlClass(targetUmlClass) - clonedTargetClass.absolutePath = - targetUmlClass.absolutePath.replace(/^node_modules\//, '') - clonedTargetClass.relativePath = - targetUmlClass.relativePath.replace(/^node_modules\//, '') - // is the source class link via the association to the target class? - return isAssociated(association, sourceUmlClass, clonedTargetClass) - } - // could not find a link from the source to target via the association - return false - }) // If a link was found if (umlClass) return umlClass @@ -82,7 +65,7 @@ const isAssociated = ( const findImplicitImport = ( association: Association, sourceUmlClass: UmlClass, - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], searchedRelativePaths: string[] ): UmlClass | undefined => { // Get all implicit imports. That is, imports that do not explicitly import contracts or interfaces. diff --git a/src/ts/converterAST2Classes.ts b/src/ts/converterAST2Classes.ts index 5c47001b..61827a1e 100644 --- a/src/ts/converterAST2Classes.ts +++ b/src/ts/converterAST2Classes.ts @@ -34,6 +34,7 @@ import { isStructDefinition, isUsingForDeclaration, } from './typeGuards' +import { Remapping } from './parserEtherscan' const debug = require('debug')('sol2uml') @@ -43,12 +44,14 @@ let umlClasses: UmlClass[] * Convert solidity parser output of type `ASTNode` to UML classes of type `UMLClass` * @param node output of Solidity parser of type `ASTNode` * @param relativePath relative path from the working directory to the Solidity source file + * @param remappings used to rename relative paths * @param filesystem flag if Solidity source code was parsed from the filesystem or Etherscan * @return umlClasses array of UML class definitions of type `UmlClass` */ export function convertAST2UmlClasses( node: ASTNode, relativePath: string, + remappings: Remapping[], filesystem: boolean = false ): UmlClass[] { const imports: Import[] = [] @@ -134,13 +137,14 @@ export function convertAST2UmlClasses( } } else { // this has come from Etherscan + const remappedFile = renameFile(childNode.path, remappings) const importPath = - childNode.path[0] === '.' + remappedFile[0] === '.' ? // Use Linux paths, not Windows paths, to resolve Etherscan files - posix.join(codeFolder.toString(), childNode.path) - : childNode.path + posix.join(codeFolder.toString(), remappedFile) + : remappedFile debug( - `codeFolder ${codeFolder} childNode.path ${childNode.path}` + `codeFolder ${codeFolder} childNode.path ${childNode.path} remapped to ${remappedFile}` ) const newImport = { absolutePath: importPath, @@ -154,8 +158,13 @@ export function convertAST2UmlClasses( : [], } debug( - `Added Etherscan import ${newImport.absolutePath} with class names: ${newImport.classNames}` + `Added Etherscan import ${newImport.absolutePath} with:` ) + newImport.classNames.forEach((className) => { + debug( + `\t alias ${className.className}, name ${className.className}` + ) + }) imports.push(newImport) } } else if (childNode.type === 'FileLevelConstant') { @@ -712,7 +721,7 @@ function parseTypeName(typeName: TypeName): [string, AttributeType] { * @param params defined in ASTNode as `VariableDeclaration` * @return parameters or `returnParameters` of the `Operator` of type `Parameter` */ -function parseParameters(params: VariableDeclaration[]): Parameter[] { +function parseParameters(params: readonly VariableDeclaration[]): Parameter[] { if (!params || !params) { return [] } @@ -749,3 +758,24 @@ function parseContractKind(kind: string): ClassStereotype { throw Error(`Invalid kind ${kind}`) } } + +/** + * Used to rename import file names. For example + * @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * to + * lib/openzeppelin-contracts/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol + * @param fileName file name in the Solidity code + * @param mappings an array of remappings from Etherscan's settings + */ +export const renameFile = (fileName: string, mappings: Remapping[]): string => { + let renamedFile = fileName + for (const mapping of mappings) { + if (renamedFile.match(mapping.from)) { + const beforeFileName = renamedFile + renamedFile = renamedFile.replace(mapping.from, mapping.to) + debug(`remapping ${beforeFileName} to ${renamedFile}`) + break + } + } + return renamedFile +} diff --git a/src/ts/converterClass2Dot.ts b/src/ts/converterClass2Dot.ts index c2fff9fd..1b2fe25f 100644 --- a/src/ts/converterClass2Dot.ts +++ b/src/ts/converterClass2Dot.ts @@ -24,6 +24,10 @@ export interface ClassOptions { hideAbstracts?: boolean hideFilename?: boolean hideSourceContract?: boolean + backColor?: string + shapeColor?: string + fillColor?: string + textColor?: string } export const convertClass2Dot = ( diff --git a/src/ts/converterClasses2Dot.ts b/src/ts/converterClasses2Dot.ts index 54f8b341..198d9b42 100644 --- a/src/ts/converterClasses2Dot.ts +++ b/src/ts/converterClasses2Dot.ts @@ -26,9 +26,10 @@ export function convertUmlClasses2Dot( let dotString: string = ` digraph UmlClassDiagram { rankdir=BT -color=black arrowhead=open -node [shape=record, style=filled, fillcolor=gray95]` +bgcolor="${classOptions.backColor}" +edge [color="${classOptions.shapeColor}"] +node [shape=record, style=filled, color="${classOptions.shapeColor}", fillcolor="${classOptions.fillColor}", fontcolor="${classOptions.textColor}"]` // Sort UML Classes by folder of source file const umlClassesSortedByCodePath = sortUmlClassesByCodePath(umlClasses) diff --git a/src/ts/converterClasses2Storage.ts b/src/ts/converterClasses2Storage.ts index 9aaf8298..9aac801f 100644 --- a/src/ts/converterClasses2Storage.ts +++ b/src/ts/converterClasses2Storage.ts @@ -1,17 +1,19 @@ import { Attribute, AttributeType, ClassStereotype, UmlClass } from './umlClass' import { findAssociatedClass } from './associations' -import { getStorageValues } from './slotValues' -import { keccak256, toUtf8Bytes } from 'ethers/lib/utils' +import { hexZeroPad, keccak256 } from 'ethers/lib/utils' import { BigNumber } from 'ethers' import path from 'path' import { BigNumberish } from '@ethersproject/bignumber' +import { addSlotValues, dynamicSlotSize, parseValue } from './slotValues' const debug = require('debug')('sol2uml') -export enum StorageType { +export enum StorageSectionType { Contract = 'Contract', Struct = 'Struct', Array = 'Array', + Bytes = 'Bytes', + String = 'String', } export interface Variable { @@ -21,64 +23,47 @@ export interface Variable { byteSize: number byteOffset: number type: string + attributeType: AttributeType dynamic: boolean - variable?: string + name?: string contractName?: string - noValue: boolean - value?: string - referenceStorageId?: number - enumId?: number + displayValue: boolean + getValue?: boolean + slotValue?: string + parsedValue?: string + referenceSectionId?: number + enumValues?: string[] } -export interface Storage { +export interface StorageSection { id: number name: string address?: string - slotKey?: string - type: StorageType + offset?: string + type: StorageSectionType arrayLength?: number arrayDynamic?: boolean + mapping: boolean // is referenced from a mapping variables: Variable[] } let storageId = 1 let variableId = 1 -/** - * - * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy - * @param contractAddress Contract address to get the storage slot values from. - * If proxied, use proxy and not the implementation contract. - * @param storage is mutated with the storage values - * @param blockTag block number or `latest` - */ -export const addStorageValues = async ( - url: string, - contractAddress: string, - storage: Storage, - blockTag?: BigNumberish | 'latest' -) => { - const valueVariables = storage.variables.filter((s) => !s.noValue) - const slots = valueVariables.map((s) => s.fromSlot) - - const values = await getStorageValues(url, contractAddress, slots, blockTag) - valueVariables.forEach((valueVariable, i) => { - valueVariable.value = values[i] - }) -} - /** * * @param contractName name of the contract to get storage layout. * @param umlClasses array of UML classes of type `UMLClass` + * @param arrayItems the number of items to display at the start and end of an array * @param contractFilename relative path of the contract in the file system - * @return array of storage objects with consecutive slots + * @return storageSections array of storageSection objects */ -export const convertClasses2Storages = ( +export const convertClasses2StorageSections = ( contractName: string, umlClasses: UmlClass[], + arrayItems: number, contractFilename?: string -): Storage[] => { +): StorageSection[] => { // Find the base UML Class from the base contract name const umlClass = umlClasses.find(({ name, relativePath }) => { if (!contractFilename) { @@ -101,32 +86,50 @@ export const convertClasses2Storages = ( } debug(`Found contract "${contractName}" in ${umlClass.absolutePath}`) - const storages: Storage[] = [] - const variables = parseVariables(umlClass, umlClasses, [], storages, []) + const storageSections: StorageSection[] = [] + const variables = parseVariables( + umlClass, + umlClasses, + [], + storageSections, + [], + false, + arrayItems + ) - storages.unshift({ + // Add new storage section to the beginning of the array + storageSections.unshift({ id: storageId++, name: contractName, - type: StorageType.Contract, + type: StorageSectionType.Contract, variables: variables, + mapping: false, }) - return storages + adjustSlots(storageSections[0], 0, storageSections) + + return storageSections } /** - * Recursively parses the storage variables for a given contract. + * Recursively parse the storage variables for a given contract or struct. * @param umlClass contract or file level struct * @param umlClasses other contracts, structs and enums that may be a type of a storage variable. - * @param variables mutable array of storage slots that is appended to - * @param storages mutable array of storages that is appended with structs + * @param variables mutable array of storage variables that are appended to + * @param storageSections mutable array of storageSection objects + * @param inheritedContracts mutable array of contracts that have been inherited already + * @param mapping flags that the storage section is under a mapping + * @param arrayItems the number of items to display at the start and end of an array + * @return variables array of storage variables in the `umlClass` */ const parseVariables = ( umlClass: UmlClass, - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], variables: Variable[], - storages: Storage[], - inheritedContracts: string[] + storageSections: StorageSection[], + inheritedContracts: string[], + mapping: boolean, + arrayItems: number ): Variable[] => { // Add storage slots from inherited contracts first. // Get immediate parent contracts that the class inherits from @@ -153,8 +156,10 @@ const parseVariables = ( parentClass, umlClasses, variables, - storages, - inheritedContracts + storageSections, + inheritedContracts, + mapping, + arrayItems ) }) @@ -168,86 +173,140 @@ const parseVariables = ( umlClass, umlClasses ) - const noValue = - attribute.attributeType === AttributeType.Mapping || - (attribute.attributeType === AttributeType.Array && !dynamic) - // find any dependent storage locations - const referenceStorage = parseReferenceStorage( + // parse any dependent storage sections or enums + const references = parseStorageSectionFromAttribute( attribute, umlClass, umlClasses, - storages + storageSections, + mapping || attribute.attributeType === AttributeType.Mapping, + arrayItems + ) + + // should this new variable get the slot value + const displayValue = calcDisplayValue( + attribute.attributeType, + dynamic, + mapping, + references?.storageSection?.type ) + const getValue = calcGetValue(attribute.attributeType, mapping) // Get the toSlot of the last storage item - let lastToSlot = 0 - let nextOffset = 0 - if (variables.length > 0) { - const lastStorage = variables[variables.length - 1] - lastToSlot = lastStorage.toSlot - nextOffset = lastStorage.byteOffset + lastStorage.byteSize - } - let newVariable: Variable + const lastVariable = variables[variables.length - 1] + let lastToSlot = lastVariable ? lastVariable.toSlot : 0 + let nextOffset = lastVariable + ? lastVariable.byteOffset + lastVariable.byteSize + : 0 + let fromSlot + let toSlot + let byteOffset if (nextOffset + byteSize > 32) { const nextFromSlot = variables.length > 0 ? lastToSlot + 1 : 0 - newVariable = { - id: variableId++, - fromSlot: nextFromSlot, - toSlot: nextFromSlot + Math.floor((byteSize - 1) / 32), - byteSize, - byteOffset: 0, - type: attribute.type, - dynamic, - noValue, - variable: attribute.name, - contractName: umlClass.name, - referenceStorageId: referenceStorage?.id, - } + fromSlot = nextFromSlot + toSlot = nextFromSlot + Math.floor((byteSize - 1) / 32) + byteOffset = 0 } else { - newVariable = { - id: variableId++, - fromSlot: lastToSlot, - toSlot: lastToSlot, - byteSize, - byteOffset: nextOffset, - type: attribute.type, - dynamic, - noValue, - variable: attribute.name, - contractName: umlClass.name, - referenceStorageId: referenceStorage?.id, - } + fromSlot = lastToSlot + toSlot = lastToSlot + byteOffset = nextOffset } - if (referenceStorage) { - if (!newVariable.dynamic) { - offsetStorageSlots( - referenceStorage, - newVariable.fromSlot, - storages + variables.push({ + id: variableId++, + fromSlot, + toSlot, + byteSize, + byteOffset, + type: attribute.type, + attributeType: attribute.attributeType, + dynamic, + getValue, + displayValue, + name: attribute.name, + contractName: umlClass.name, + referenceSectionId: references?.storageSection?.id, + enumValues: references?.enumValues, + }) + }) + + return variables +} + +/** + * Recursively adjusts the fromSlot and toSlot properties of any storage variables + * that are referenced by a static array or struct. + * Also sets the storage slot offset for dynamic arrays, strings and bytes. + * @param storageSection + * @param slotOffset + * @param storageSections + */ +const adjustSlots = ( + storageSection: StorageSection, + slotOffset: number, + storageSections: StorageSection[] +) => { + storageSection.variables.forEach((variable) => { + // offset storage slots + variable.fromSlot += slotOffset + variable.toSlot += slotOffset + + // find storage section that the variable is referencing + const referenceStorageSection = storageSections.find( + (ss) => ss.id === variable.referenceSectionId + ) + + if (referenceStorageSection) { + referenceStorageSection.offset = storageSection.offset + + if (!variable.dynamic) { + adjustSlots( + referenceStorageSection, + variable.fromSlot, + storageSections ) - } else if (attribute.attributeType === AttributeType.Array) { - referenceStorage.slotKey = calcSlotKey(newVariable) + } else if (variable.attributeType === AttributeType.Array) { + // attribute is a dynamic array + referenceStorageSection.offset = calcSectionOffset( + variable, + storageSection.offset + ) + + adjustSlots(referenceStorageSection, 0, storageSections) } } - variables.push(newVariable) }) - - return variables } -export const parseReferenceStorage = ( +/** + * Recursively adds new storage sections under a class attribute. + * also returns the allowed enum values + * @param attribute the attribute that is referencing a storage section + * @param umlClass contract or file level struct + * @param otherClasses array of all the UML Classes + * @param storageSections mutable array of storageSection objects + * @param mapping flags that the storage section is under a mapping + * @param arrayItems the number of items to display at the start and end of an array + * @return storageSection new storage section that was added or undefined if none was added. + * @return enumValues array of allowed enum values. undefined if attribute is not an enum + */ +export const parseStorageSectionFromAttribute = ( attribute: Attribute, umlClass: UmlClass, - otherClasses: UmlClass[], - storages: Storage[] -): Storage | undefined => { + otherClasses: readonly UmlClass[], + storageSections: StorageSection[], + mapping: boolean, + arrayItems: number +): { + storageSection: StorageSection + enumValues?: string[] +} => { if (attribute.attributeType === AttributeType.Array) { // storage is dynamic if the attribute type ends in [] - const result = attribute.type.match(/\[(\w*)]$/) + const result = attribute.type.match(/\[([\w$.]*)]$/) const dynamic = result[1] === '' const arrayLength = !dynamic - ? findDimensionLength(umlClass, result[1]) + ? findDimensionLength(umlClass, result[1], otherClasses) : undefined // get the type of the array items. eg @@ -264,22 +323,44 @@ export const parseReferenceStorage = ( } else { baseAttributeType = AttributeType.UserDefined } + const baseAttribute: Attribute = { visibility: attribute.visibility, - name: baseType, + name: attribute.name, type: baseType, attributeType: baseAttributeType, } - const { size: arrayItemSize } = calcStorageByteSize( - baseAttribute, - umlClass, - otherClasses - ) + const { size: arrayItemSize, dynamic: dynamicBase } = + calcStorageByteSize(baseAttribute, umlClass, otherClasses) + // If more than 16 bytes, then round up in 32 bytes increments const arraySlotSize = arrayItemSize > 16 ? 32 * Math.ceil(arrayItemSize / 32) : arrayItemSize + // If base type is not an Elementary type + // This can only be Array and UserDefined for base types of arrays. + let references + if (baseAttributeType !== AttributeType.Elementary) { + // recursively add storage section for Array and UserDefined types + references = parseStorageSectionFromAttribute( + baseAttribute, + umlClass, + otherClasses, + storageSections, + mapping, + arrayItems + ) + } + + const displayValue = calcDisplayValue( + baseAttribute.attributeType, + dynamicBase, + mapping, + references?.storageSection?.type + ) + const getValue = calcGetValue(attribute.attributeType, mapping) + const variables: Variable[] = [] variables[0] = { id: variableId++, @@ -288,76 +369,84 @@ export const parseReferenceStorage = ( byteSize: arrayItemSize, byteOffset: 0, type: baseType, - dynamic, - noValue: false, + attributeType: baseAttributeType, + dynamic: dynamicBase, + getValue, + displayValue, + referenceSectionId: references?.storageSection?.id, + enumValues: references?.enumValues, } + + // If a fixed size array. + // Note dynamic arrays will have undefined arrayLength if (arrayLength > 1) { - // For fixed length arrays. Dynamic arrays will have undefined arrayLength - for (let i = 1; i < arrayLength; i++) { - variables.push({ - id: variableId++, - fromSlot: Math.floor((i * arraySlotSize) / 32), - toSlot: Math.floor(((i + 1) * arraySlotSize - 1) / 32), - byteSize: arrayItemSize, - byteOffset: (i * arraySlotSize) % 32, - type: baseType, - dynamic, - noValue: false, - }) - } - } + // Add missing fixed array variables from index 1 + addArrayVariables(arrayLength, arrayItems, variables) - // recursively add storage - if (baseAttributeType !== AttributeType.Elementary) { - const referenceStorage = parseReferenceStorage( - baseAttribute, - umlClass, - otherClasses, - storages - ) - variables[0].referenceStorageId = referenceStorage?.id + // For the newly added variables + variables.forEach((variable, i) => { + if ( + i > 0 && + baseAttributeType !== AttributeType.Elementary && + variable.type !== '----' // ignore any filler variables + ) { + // recursively add storage section for Array and UserDefined types + references = parseStorageSectionFromAttribute( + baseAttribute, + umlClass, + otherClasses, + storageSections, + mapping, + arrayItems + ) + variable.referenceSectionId = references?.storageSection?.id + variable.enumValues = references?.enumValues + } + }) } - const newStorage: Storage = { + const storageSection = { id: storageId++, name: `${attribute.type}: ${attribute.name}`, - type: StorageType.Array, + type: StorageSectionType.Array, arrayDynamic: dynamic, arrayLength, variables, + mapping, } - storages.push(newStorage) + storageSections.push(storageSection) - return newStorage + return { storageSection } } if (attribute.attributeType === AttributeType.UserDefined) { // Is the user defined type linked to another Contract, Struct or Enum? - const dependentClass = otherClasses.find(({ name }) => { - return ( - name === attribute.type || name === attribute.type.split('.')[1] - ) - }) - if (!dependentClass) { - throw Error(`Failed to find user defined type "${attribute.type}"`) - } + const typeClass = findTypeClass(attribute.type, attribute, otherClasses) - if (dependentClass.stereotype === ClassStereotype.Struct) { + if (typeClass.stereotype === ClassStereotype.Struct) { const variables = parseVariables( - dependentClass, + typeClass, otherClasses, [], - storages, - [] + storageSections, + [], + mapping, + arrayItems ) - const newStorage = { + const storageSection = { id: storageId++, name: attribute.type, - type: StorageType.Struct, + type: StorageSectionType.Struct, variables, + mapping, } - storages.push(newStorage) + storageSections.push(storageSection) - return newStorage + return { storageSection } + } else if (typeClass.stereotype === ClassStereotype.Enum) { + return { + storageSection: undefined, + enumValues: typeClass.attributes.map((a) => a.name), + } } return undefined } @@ -365,36 +454,32 @@ export const parseReferenceStorage = ( // get the UserDefined type from the mapping // note the mapping could be an array of Structs // Could also be a mapping of a mapping - const result = attribute.type.match(/=\\>((?!mapping)\w*)[\\[]/) + const result = attribute.type.match(/=\\>((?!mapping)[\w$.]*)[\\[]/) // If mapping of user defined type if (result !== null && result[1] && !isElementary(result[1])) { - // Find UserDefined type - const typeClass = otherClasses.find( - ({ name }) => - name === result[1] || name === result[1].split('.')[1] - ) - if (!typeClass) { - throw Error( - `Failed to find user defined type "${result[1]}" in attribute type "${attribute.type}"` - ) - } + // Find UserDefined type can be a contract, struct or enum + const typeClass = findTypeClass(result[1], attribute, otherClasses) + if (typeClass.stereotype === ClassStereotype.Struct) { - const variables = parseVariables( + let variables = parseVariables( typeClass, otherClasses, [], - storages, - [] + storageSections, + [], + true, + arrayItems ) - const newStorage = { + const storageSection = { id: storageId++, name: typeClass.name, - type: StorageType.Struct, + type: StorageSectionType.Struct, + mapping: true, variables, } - storages.push(newStorage) + storageSections.push(storageSection) - return newStorage + return { storageSection } } } return undefined @@ -402,11 +487,109 @@ export const parseReferenceStorage = ( return undefined } +/** + * Adds missing storage variables to a fixed-size or dynamic array by cloning them from the first variable. + * @param arrayLength the length of the array + * @param arrayItems the number of items to display at the start and end of an array + * @param variables mutable array of storage variables that are appended to + */ +const addArrayVariables = ( + arrayLength: number, + arrayItems: number, + variables: Variable[] +) => { + const arraySlotSize = variables[0].byteSize + const itemsPerSlot = Math.floor(32 / arraySlotSize) + const slotsPerItem = Math.ceil(arraySlotSize / 32) + const firstFillerItem = + itemsPerSlot > 0 ? arrayItems * itemsPerSlot : arrayItems + const lastFillerItem = + itemsPerSlot > 0 + ? arrayLength - + (arrayItems - 1) * itemsPerSlot - // the number of items in all but the last row + (arrayLength % itemsPerSlot || itemsPerSlot) - // the remaining items in the last row or all the items in a slot + 1 // need the items before the last three rows + : arrayLength - arrayItems - 1 + + // Add variable from index 1 for each item in the array + for (let i = 1; i < arrayLength; i++) { + const fromSlot = + itemsPerSlot > 0 ? Math.floor(i / itemsPerSlot) : i * slotsPerItem + const toSlot = itemsPerSlot > 0 ? fromSlot : fromSlot + slotsPerItem + + // add filler variable before adding the first of the last items of the array + if (i === lastFillerItem && firstFillerItem < lastFillerItem) { + const fillerFromSlot = + itemsPerSlot > 0 + ? Math.floor(firstFillerItem / itemsPerSlot) + : firstFillerItem * slotsPerItem + variables.push({ + id: variableId++, + attributeType: AttributeType.UserDefined, + type: '----', + fromSlot: fillerFromSlot, + toSlot: toSlot, + byteOffset: 0, + byteSize: (toSlot - fillerFromSlot + 1) * 32, + getValue: false, + displayValue: false, + dynamic: false, + }) + } + // Add variables for the first arrayItems and last arrayItems + if (i < firstFillerItem || i > lastFillerItem) { + const byteOffset = + itemsPerSlot > 0 ? (i % itemsPerSlot) * arraySlotSize : 0 + const slotValue = + fromSlot === 0 ? variables[0].slotValue : undefined + // add array variable + const newVariable: Variable = { + ...variables[0], + id: variableId++, + fromSlot, + toSlot, + byteOffset, + slotValue, + // These will be added in a separate step + parsedValue: undefined, + referenceSectionId: undefined, + enumValues: undefined, + } + newVariable.parsedValue = parseValue(newVariable) + variables.push(newVariable) + } + } +} + +/** + * Finds an attribute's user defined type that can be a Contract, Struct or Enum + * @param userType User defined type that is being looked for. This can be the base type of an attribute. + * @param attribute the attribute in the class that is user defined. This is just used for logging purposes + * @param otherClasses + */ +const findTypeClass = ( + userType: string, + attribute: Attribute, + otherClasses: readonly UmlClass[] +): UmlClass => { + // Find associated UserDefined type + // TODO this just matches on name and doesn't take into account imports + const typeClass = otherClasses.find( + ({ name }) => name === userType || name === userType.split('.')[1] + ) + if (!typeClass) { + throw Error( + `Failed to find user defined type "${userType}" in attribute "${attribute.name}" of type "${attribute.attributeType}""` + ) + } + return typeClass +} + // Calculates the storage size of an attribute in bytes export const calcStorageByteSize = ( attribute: Attribute, umlClass: UmlClass, - otherClasses: UmlClass[] + otherClasses: readonly UmlClass[] ): { size: number; dynamic: boolean } => { if ( attribute.attributeType === AttributeType.Mapping || @@ -418,7 +601,7 @@ export const calcStorageByteSize = ( // Fixed sized arrays are read from right to left until there is a dynamic dimension // eg address[][3][2] is a fixed size array that uses 6 slots. // while address [2][] is a dynamic sized array. - const arrayDimensions = attribute.type.match(/\[\w*]/g) + const arrayDimensions = attribute.type.match(/\[[\w$.]*]/g) // Remove first [ and last ] from each arrayDimensions const dimensionsStr = arrayDimensions.map((a) => a.slice(1, -1)) // fixed-sized arrays are read from right to left so reverse the dimensions @@ -428,7 +611,11 @@ export const calcStorageByteSize = ( let dimension = dimensionsStrReversed.shift() const fixedDimensions: number[] = [] while (dimension && dimension !== '') { - const dimensionNum = findDimensionLength(umlClass, dimension) + const dimensionNum = findDimensionLength( + umlClass, + dimension, + otherClasses + ) fixedDimensions.push(dimensionNum) // read the next dimension for the next loop dimension = dimensionsStrReversed.shift() @@ -441,9 +628,9 @@ export const calcStorageByteSize = ( return { size: 32, dynamic: true } } + // If a fixed sized array let elementSize: number const type = attribute.type.substring(0, attribute.type.indexOf('[')) - // If a fixed sized array if (isElementary(type)) { const elementAttribute: Attribute = { attributeType: AttributeType.Elementary, @@ -483,7 +670,13 @@ export const calcStorageByteSize = ( } } const lastItem = fixedDimensions.length - 1 - const lastDimensionBytes = elementSize * fixedDimensions[lastItem] + const lastArrayLength = fixedDimensions[lastItem] + const itemsPerSlot = Math.floor(32 / elementSize) + const lastDimensionBytes = + itemsPerSlot > 0 // if one or more array items in a slot + ? Math.ceil(lastArrayLength / itemsPerSlot) * 32 // round up to include unallocated slot space + : elementSize * fixedDimensions[lastItem] + const lastDimensionSlotBytes = Math.ceil(lastDimensionBytes / 32) * 32 const remainingDimensions = fixedDimensions .slice(0, lastItem) @@ -493,21 +686,17 @@ export const calcStorageByteSize = ( dynamic: false, } } - // If a Struct or Enum + // If a Struct, Enum or Contract reference + // TODO need to handle User Defined Value Types when they are added to Solidity if (attribute.attributeType === AttributeType.UserDefined) { // Is the user defined type linked to another Contract, Struct or Enum? - const attributeClass = otherClasses.find(({ name }) => { - return ( - name === attribute.type || name === attribute.type.split('.')[1] - ) - }) - if (!attributeClass) { - throw Error( - `Failed to find user defined struct or enum "${attribute.type}"` - ) - } + const attributeTypeClass = findTypeClass( + attribute.type, + attribute, + otherClasses + ) - switch (attributeClass.stereotype) { + switch (attributeTypeClass.stereotype) { case ClassStereotype.Enum: return { size: 1, dynamic: false } case ClassStereotype.Contract: @@ -517,7 +706,7 @@ export const calcStorageByteSize = ( return { size: 20, dynamic: false } case ClassStereotype.Struct: let structByteSize = 0 - attributeClass.attributes.forEach((structAttribute) => { + attributeTypeClass.attributes.forEach((structAttribute) => { // If next attribute is an array, then we need to start in a new slot if (structAttribute.attributeType === AttributeType.Array) { structByteSize = Math.ceil(structByteSize / 32) * 32 @@ -528,19 +717,11 @@ export const calcStorageByteSize = ( AttributeType.UserDefined ) { // UserDefined types can be a struct or enum, so we need to check if it's a struct - const userDefinedClass = otherClasses.find( - ({ name }) => { - return ( - name === structAttribute.type || - name === structAttribute.type.split('.')[1] - ) - } + const userDefinedClass = findTypeClass( + structAttribute.type, + structAttribute, + otherClasses ) - if (!userDefinedClass) { - throw Error( - `Failed to find user defined type "${structAttribute.type}" in struct ${attributeClass.name}` - ) - } // If a struct if ( userDefinedClass.stereotype === @@ -581,6 +762,7 @@ export const calcStorageByteSize = ( return { size: 20, dynamic: false } case 'string': case 'bytes': + return { size: 32, dynamic: true } case 'uint': case 'int': case 'ufixed': @@ -623,60 +805,288 @@ export const isElementary = (type: string): boolean => { case 'fixed': return true default: - const result = type.match(/[u]*(int|fixed|bytes)([0-9]+)/) + const result = type.match(/^[u]?(int|fixed|bytes)([0-9]+)$/) return result !== null } } -export const calcSlotKey = (variable: Variable): string | undefined => { +export const calcSectionOffset = ( + variable: Variable, + sectionOffset = '0' +): string => { if (variable.dynamic) { - return keccak256( - toUtf8Bytes(BigNumber.from(variable.fromSlot).toHexString()) + const hexStringOf32Bytes = hexZeroPad( + BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString(), + 32 ) + return keccak256(hexStringOf32Bytes) } - return BigNumber.from(variable.fromSlot).toHexString() -} - -// recursively offset the slots numbers of a storage item -export const offsetStorageSlots = ( - storage: Storage, - slots: number, - storages: Storage[] -) => { - storage.variables.forEach((variable) => { - variable.fromSlot += slots - variable.toSlot += slots - if (variable.referenceStorageId) { - // recursively offset the referenced storage - const referenceStorage = storages.find( - (s) => s.id === variable.referenceStorageId - ) - if (!referenceStorage.arrayDynamic) { - offsetStorageSlots(referenceStorage, slots, storages) - } else { - referenceStorage.slotKey = calcSlotKey(variable) - } - } - }) + return BigNumber.from(variable.fromSlot).add(sectionOffset).toHexString() } export const findDimensionLength = ( umlClass: UmlClass, - dimension: string + dimension: string, + otherClasses: readonly UmlClass[] ): number => { const dimensionNum = parseInt(dimension) if (Number.isInteger(dimensionNum)) { return dimensionNum - } else { - // Try and size array dimension from declared constants - const constant = umlClass.constants.find( - (constant) => constant.name === dimension - ) - if (!constant) { + } + + // Try and size array dimension from declared constants + const constant = umlClass.constants.find( + (constant) => constant.name === dimension + ) + if (constant) { + return constant.value + } + + // Try and size array dimension from file constants + const fileConstant = otherClasses.find( + (umlClass) => + umlClass.name === dimension && + umlClass.stereotype === ClassStereotype.Constant + ) + if (fileConstant?.constants[0]?.value) { + return fileConstant.constants[0].value + } + throw Error( + `Could not size fixed sized array with dimension "${dimension}"` + ) +} + +/** + * Calculate if the storage slot value for the attribute should be displayed in the storage section. + * + * Storage sections with true mapping should return false. + * Mapping types should return false. + * Elementary types should return true. + * Dynamic Array types should return true. + * Static Array types should return false. + * UserDefined types that are Structs should return false. + * UserDefined types that are Enums or alias to Elementary type or contract should return true. + * + * @param attributeType + * @param dynamic flags if the variable is of dynamic size + * @param mapping flags if the storage section is referenced by a mapping + * @param storageSectionType + * @return displayValue true if the slot value should be displayed. + */ +const calcDisplayValue = ( + attributeType: AttributeType, + dynamic: boolean, + mapping: boolean, + storageSectionType?: StorageSectionType +): boolean => + mapping === false && + (attributeType === AttributeType.Elementary || + (attributeType === AttributeType.UserDefined && + storageSectionType !== StorageSectionType.Struct) || + (attributeType === AttributeType.Array && dynamic)) + +/** + * Calculate if the storage slot value for the attribute should be retrieved from the chain. + * + * Storage sections with true mapping should return false. + * Mapping types should return false. + * Elementary types should return true. + * Array types should return true. + * UserDefined should return true. + * + * @param attributeType the type of attribute the storage variable is for. + * @param mapping flags if the storage section is referenced by a mapping + * @return getValue true if the slot value should be retrieved. + */ +const calcGetValue = ( + attributeType: AttributeType, + mapping: boolean +): boolean => mapping === false && attributeType !== AttributeType.Mapping + +/** + * Recursively adds variables for dynamic string, bytes or arrays + * @param storageSection + * @param storageSections + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +export const addDynamicVariables = async ( + storageSection: StorageSection, + storageSections: StorageSection[], + url: string, + contractAddress: string, + arrayItems: number, + blockTag: BigNumberish +) => { + for (const variable of storageSection.variables) { + try { + if (!variable.dynamic) continue + // STEP 1 - add slots for dynamic string and bytes + if (variable.type === 'string' || variable.type === 'bytes') { + if (!variable.slotValue) { + debug( + `WARNING: Variable "${variable.name}" of type "${variable.type}" has no slot value` + ) + continue + } + const size = dynamicSlotSize(variable) + if (size > 31) { + const maxSlotNumber = Math.floor((size - 1) / 32) + const variables: Variable[] = [] + + // For each dynamic slot + for (let i = 0; i <= maxSlotNumber; i++) { + // If the last slot then get the remaining bytes + const byteSize = + i === maxSlotNumber ? size - 32 * maxSlotNumber : 32 + // Add variable for the slot + variables.push({ + id: variableId++, + fromSlot: i, + toSlot: i, + byteSize, + byteOffset: 0, + type: variable.type, + contractName: variable.contractName, + attributeType: AttributeType.Elementary, + dynamic: false, + getValue: true, + displayValue: true, + }) + } + + // add unallocated variable + const unusedBytes = 32 - (size - 32 * maxSlotNumber) + if (unusedBytes > 0) { + const lastVariable = variables[variables.length - 1] + variables.push({ + ...lastVariable, + byteOffset: unusedBytes, + }) + + variables[maxSlotNumber] = { + id: variableId++, + fromSlot: maxSlotNumber, + toSlot: maxSlotNumber, + byteSize: unusedBytes, + byteOffset: 0, + type: 'unallocated', + attributeType: AttributeType.UserDefined, + contractName: variable.contractName, + name: '', + dynamic: false, + getValue: true, + displayValue: false, + } + } + + const newStorageSection: StorageSection = { + id: storageId++, + name: `${variable.type}: ${variable.name}`, + offset: calcSectionOffset( + variable, + storageSection.offset + ), + type: + variable.type === 'string' + ? StorageSectionType.String + : StorageSectionType.Bytes, + arrayDynamic: true, + arrayLength: size, + variables, + mapping: false, + } + variable.referenceSectionId = newStorageSection.id + + // get slot values for new referenced dynamic string or bytes + await addSlotValues( + url, + contractAddress, + newStorageSection, + arrayItems, + blockTag + ) + + storageSections.push(newStorageSection) + } + + continue + } + if (variable.attributeType !== AttributeType.Array) continue + + // STEP 2 - add slots for dynamic arrays + + // find storage section that the variable is referencing + const referenceStorageSection = storageSections.find( + (ss) => ss.id === variable.referenceSectionId + ) + if (!referenceStorageSection) continue + + // recursively add dynamic variables to referenced array. + // this could be a fixed-size or dynamic array + await addDynamicVariables( + referenceStorageSection, + storageSections, + url, + contractAddress, + arrayItems, + blockTag + ) + + if (!variable.slotValue) { + debug( + `WARNING: Dynamic array variable "${variable.name}" of type "${variable.type}" has no slot value` + ) + continue + } + + // Add missing dynamic array variables + const arrayLength = BigNumber.from(variable.slotValue).toNumber() + if (arrayLength > 1) { + // Add missing array variables to the referenced dynamic array + addArrayVariables( + arrayLength, + arrayItems, + referenceStorageSection.variables + ) + + // // For the newly added variables + // referenceStorageSection.variables.forEach((variable, i) => { + // if ( + // referenceStorageSection.variables[0].attributeType !== + // AttributeType.Elementary && + // i > 0 + // ) { + // // recursively add storage section for Array and UserDefined types + // const references = parseStorageSectionFromAttribute( + // baseAttribute, + // umlClass, + // otherClasses, + // storageSections, + // mapping, + // arrayItems + // ) + // variable.referenceSectionId = references.storageSection?.id + // variable.enumValues = references?.enumValues + // } + // }) + } + + // Get missing slot values to the referenced dynamic array + await addSlotValues( + url, + contractAddress, + referenceStorageSection, + arrayItems, + blockTag + ) + } catch (err) { throw Error( - `Could not size fixed sized array with dimension "${dimension}"` + `Failed to add dynamic vars for section "${storageSection.name}", var type "${variable.type}" with value "${variable.slotValue}" from slot ${variable.fromSlot} and section offset ${storageSection.offset}`, + { cause: err } ) } - return constant.value } } diff --git a/src/ts/converterStorage2Dot.ts b/src/ts/converterStorage2Dot.ts index 5948bdbc..6b745503 100644 --- a/src/ts/converterStorage2Dot.ts +++ b/src/ts/converterStorage2Dot.ts @@ -1,28 +1,40 @@ -import { Storage, StorageType, Variable } from './converterClasses2Storage' +import { + StorageSection, + StorageSectionType, + Variable, +} from './converterClasses2Storage' +import { AttributeType } from './umlClass' const debug = require('debug')('sol2uml') export const convertStorages2Dot = ( - storages: Storage[], - options: { data: boolean } + storageSections: readonly StorageSection[], + options: { + data: boolean + backColor: string + shapeColor: string + fillColor: string + textColor: string + } ): string => { let dotString: string = ` digraph StorageDiagram { rankdir=LR -color=black arrowhead=open -node [shape=record, style=filled, fillcolor=gray95 fontname="Courier New"]` +bgcolor="${options.backColor}" +edge [color="${options.shapeColor}"] +node [shape=record, style=filled, color="${options.shapeColor}", fillcolor="${options.fillColor}", fontcolor="${options.textColor}", fontname="Courier New"]` // process contract and the struct storages - storages.forEach((storage) => { + storageSections.forEach((storage) => { dotString = convertStorage2Dot(storage, dotString, options) }) // link contract and structs to structs - storages.forEach((slot) => { + storageSections.forEach((slot) => { slot.variables.forEach((storage) => { - if (storage.referenceStorageId) { - dotString += `\n ${slot.id}:${storage.id} -> ${storage.referenceStorageId}` + if (storage.referenceSectionId) { + dotString += `\n ${slot.id}:${storage.id} -> ${storage.referenceSectionId}` } }) }) @@ -36,47 +48,68 @@ node [shape=record, style=filled, fillcolor=gray95 fontname="Courier New"]` } export function convertStorage2Dot( - storage: Storage, + storageSection: StorageSection, dotString: string, options: { data: boolean } ): string { // write storage header with name and optional address - dotString += `\n${storage.id} [label="${storage.name} \\<\\<${ - storage.type - }\\>\\>\\n${storage.address || storage.slotKey || ''}` + dotString += `\n${storageSection.id} [label="${storageSection.name} \\<\\<${ + storageSection.type + }\\>\\>\\n${storageSection.address || storageSection.offset || ''}` dotString += ' | {' - const startingVariables = storage.variables.filter( + const startingVariables = storageSection.variables.filter( (s) => s.byteOffset === 0 ) + // for each slot displayed, does is have any variables with parsed data? + const displayData = startingVariables.map((startVar) => + storageSection.variables.some( + (variable) => + variable.fromSlot === startVar.fromSlot && variable.parsedValue + ) + ) + + const linePad = '\\n\\ ' // write slot numbers - dotString += '{ slot' + const dataLine = options.data ? linePad : '' + dotString += + storageSection.offset || storageSection.mapping + ? `{ offset${dataLine}` + : `{ slot${dataLine}` startingVariables.forEach((variable, i) => { + const dataLine = options.data && displayData[i] ? linePad : '' if (variable.fromSlot === variable.toSlot) { - dotString += `| ${variable.fromSlot} ` + dotString += ` | ${variable.fromSlot}${dataLine}` } else { - dotString += `| ${variable.fromSlot}-${variable.toSlot} ` + dotString += ` | ${variable.fromSlot}-${variable.toSlot}${dataLine}` } }) // write slot values if available if (options.data) { - dotString += '} | {value' + dotString += `} | {value${dataLine}` startingVariables.forEach((variable, i) => { - dotString += ` | ${variable.value || ''}` + if (displayData[i]) { + dotString += ` | ${variable.slotValue || ''}${linePad}` + } else { + dotString += ` | ` + } }) } const contractVariablePrefix = - storage.type === StorageType.Contract ? '\\.' : '' - dotString += `} | { type: ${contractVariablePrefix}variable (bytes)` + storageSection.type === StorageSectionType.Contract + ? '\\.' + : '' + const dataLine2 = options.data ? `\\ndecoded data` : '' + dotString += `} | { type: ${contractVariablePrefix}variable (bytes)${dataLine2}` // For each slot startingVariables.forEach((variable) => { // Get all the storage variables in this slot - const slotVariables = storage.variables.filter( + const slotVariables = storageSection.variables.filter( (s) => s.fromSlot === variable.fromSlot ) const usedBytes = slotVariables.reduce((acc, s) => acc + s.byteSize, 0) @@ -89,10 +122,12 @@ export function convertStorage2Dot( byteSize: 32 - usedBytes, byteOffset: usedBytes, type: 'unallocated', + attributeType: AttributeType.UserDefined, dynamic: false, - noValue: true, + displayValue: false, + getValue: false, contractName: variable.contractName, - variable: '', + name: '', }) } const slotVariablesReversed = slotVariables.reverse() @@ -100,9 +135,12 @@ export function convertStorage2Dot( // For each variable in the slot slotVariablesReversed.forEach((variable, i) => { if (i === 0) { - dotString += ` | { ${dotVariable(variable, storage.name)} ` + dotString += ` | { ${dotVariable( + variable, + storageSection.name + )} ` } else { - dotString += ` | ${dotVariable(variable, storage.name)} ` + dotString += ` | ${dotVariable(variable, storageSection.name)} ` } }) dotString += '}' @@ -114,14 +152,19 @@ export function convertStorage2Dot( return dotString } -const dotVariable = (storage: Variable, contractName: string): string => { +const dotVariable = (variable: Variable, contractName: string): string => { const port = - storage.referenceStorageId !== undefined ? `<${storage.id}>` : '' + variable.referenceSectionId !== undefined ? `<${variable.id}>` : '' const contractNamePrefix = - storage.contractName !== contractName ? `${storage.contractName}.` : '' + variable.contractName !== contractName + ? `${variable.contractName}.` + : '' + const variableValue = variable.parsedValue + ? `\\n\\ ${variable.parsedValue}` + : '' - const variable = storage.variable - ? `: ${contractNamePrefix}${storage.variable}` + const variableName = variable.name + ? `: ${contractNamePrefix}${variable.name}` : '' - return `${port} ${storage.type}${variable} (${storage.byteSize})` + return `${port} ${variable.type}${variableName} (${variable.byteSize})${variableValue}` } diff --git a/src/ts/filterClasses.ts b/src/ts/filterClasses.ts index 64558efe..0f9f9c63 100644 --- a/src/ts/filterClasses.ts +++ b/src/ts/filterClasses.ts @@ -9,6 +9,8 @@ import { ClassStereotype, UmlClass } from './umlClass' import { findAssociatedClass } from './associations' import { ClassOptions } from './converterClass2Dot' +const debug = require('debug')('sol2uml') + /** * Filter out any UML Class types that are to be hidden. * @param umlClasses array of UML classes of type `UMLClass` @@ -16,7 +18,7 @@ import { ClassOptions } from './converterClass2Dot' * @return umlClasses filtered list of UML classes of type `UMLClass` */ export const filterHiddenClasses = ( - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], options: ClassOptions ): UmlClass[] => { return umlClasses.filter( @@ -45,8 +47,8 @@ export const filterHiddenClasses = ( * @return filteredUmlClasses list of UML classes of type `UMLClass` */ export const classesConnectedToBaseContracts = ( - umlClasses: UmlClass[], - baseContractNames: string[], + umlClasses: readonly UmlClass[], + baseContractNames: readonly string[], depth?: number ): UmlClass[] => { let filteredUmlClasses: { [contractName: string]: UmlClass } = {} @@ -78,7 +80,7 @@ export const classesConnectedToBaseContracts = ( * @return filteredUmlClasses list of UML classes of type `UMLClass` */ export const classesConnectedToBaseContract = ( - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], baseContractName: string, weightedDirectedGraph: WeightedDiGraph, depth: number = 1000 @@ -107,7 +109,9 @@ export const classesConnectedToBaseContract = ( return filteredUmlClasses } -function loadWeightedDirectedGraph(umlClasses: UmlClass[]): WeightedDiGraph { +function loadWeightedDirectedGraph( + umlClasses: readonly UmlClass[] +): WeightedDiGraph { const weightedDirectedGraph = new WeightedDiGraph( // the number vertices in the graph UmlClass.idCounter + 1 @@ -126,7 +130,7 @@ function loadWeightedDirectedGraph(umlClasses: UmlClass[]): WeightedDiGraph { continue } const isTarget = umlClasses.find((u) => u.id === targetUmlClass.id) - console.log( + debug( `isTarget ${isTarget} Adding edge from ${sourceUmlClass.name} with id ${sourceUmlClass.id} to ${targetUmlClass.name} with id ${targetUmlClass.id} and type ${targetUmlClass.stereotype}` ) weightedDirectedGraph.addEdge( @@ -138,7 +142,9 @@ function loadWeightedDirectedGraph(umlClasses: UmlClass[]): WeightedDiGraph { return weightedDirectedGraph } -export const topologicalSortClasses = (umlClasses: UmlClass[]): UmlClass[] => { +export const topologicalSortClasses = ( + umlClasses: readonly UmlClass[] +): UmlClass[] => { const directedAcyclicGraph = loadDirectedAcyclicGraph(umlClasses) const topologicalSort = new TopologicalSort(directedAcyclicGraph) @@ -153,7 +159,7 @@ export const topologicalSortClasses = (umlClasses: UmlClass[]): UmlClass[] => { return sortedUmlClasses.filter((umlClass) => umlClass !== undefined) } -const loadDirectedAcyclicGraph = (umlClasses: UmlClass[]): DiGraph => { +const loadDirectedAcyclicGraph = (umlClasses: readonly UmlClass[]): DiGraph => { const directedAcyclicGraph = new DiGraph(UmlClass.idCounter) // the number vertices in the graph for (const sourceUmlClass of umlClasses) { diff --git a/src/ts/parserEtherscan.ts b/src/ts/parserEtherscan.ts index 73024160..087f4ec4 100644 --- a/src/ts/parserEtherscan.ts +++ b/src/ts/parserEtherscan.ts @@ -10,6 +10,11 @@ import { parseSolidityVersion } from './utils/regEx' require('axios-debug-log') const debug = require('debug')('sol2uml') +export interface Remapping { + from: RegExp + to: string +} + export const networks = [ 'mainnet', 'ropsten', @@ -33,7 +38,7 @@ export const networks = [ 'kovan-optimistic', 'gnosisscan', ] -export type Network = typeof networks[number] +export type Network = (typeof networks)[number] export class EtherscanParser { readonly url: string @@ -100,10 +105,11 @@ export class EtherscanParser { * @param contractAddress Ethereum contract address with a 0x prefix * @return Promise with an array of UmlClass objects */ - async getUmlClasses( - contractAddress: string - ): Promise<{ umlClasses: UmlClass[]; contractName: string }> { - const { files, contractName } = await this.getSourceCode( + async getUmlClasses(contractAddress: string): Promise<{ + umlClasses: UmlClass[] + contractName: string + }> { + const { files, contractName, remappings } = await this.getSourceCode( contractAddress ) @@ -112,7 +118,11 @@ export class EtherscanParser { for (const file of files) { debug(`Parsing source file ${file.filename}`) const node = await this.parseSourceCode(file.code) - const umlClass = convertAST2UmlClasses(node, file.filename) + const umlClass = convertAST2UmlClasses( + node, + file.filename, + remappings + ) umlClasses = umlClasses.concat(umlClass) } @@ -131,14 +141,18 @@ export class EtherscanParser { async getSolidityCode( contractAddress: string ): Promise<{ solidityCode: string; contractName: string }> { - const { files, contractName, compilerVersion } = + const { files, contractName, compilerVersion, remappings } = await this.getSourceCode(contractAddress) // Parse the UmlClasses from the Solidity code in each file let umlClasses: UmlClass[] = [] for (const file of files) { const node = await this.parseSourceCode(file.code) - const umlClass = convertAST2UmlClasses(node, file.filename) + const umlClass = convertAST2UmlClasses( + node, + file.filename, + remappings + ) umlClasses = umlClasses.concat(umlClass) } @@ -222,9 +236,10 @@ export class EtherscanParser { * @param contractAddress Ethereum contract address with a 0x prefix */ async getSourceCode(contractAddress: string): Promise<{ - files: { code: string; filename: string }[] + files: readonly { code: string; filename: string }[] contractName: string compilerVersion: string + remappings: Remapping[] }> { const description = `get verified source code for address ${contractAddress} from Etherscan API.` @@ -249,6 +264,7 @@ export class EtherscanParser { ) } + let remappings: Remapping[] const results = response.data.result.map((result: any) => { if (!result.SourceCode) { throw new Error( @@ -265,6 +281,12 @@ export class EtherscanParser { parableResultString = result.SourceCode.slice(1, -1) } const sourceCodeObject = JSON.parse(parableResultString) + + // Get any remapping of filenames from the settings + remappings = parseRemappings( + sourceCodeObject.settings?.remappings + ) + // The getsource response from Etherscan is inconsistent so we need to handle both shapes const sourceFiles = sourceCodeObject.sources ? Object.entries(sourceCodeObject.sources) @@ -288,6 +310,10 @@ export class EtherscanParser { // if multiple Solidity source files with no Etherscan bug in the SourceCode field if (result?.SourceCode?.sources) { const sourceFiles = Object.values(result.SourceCode.sources) + // Get any remapping of filenames from the settings + remappings = parseRemappings( + result.SourceCode.settings?.remappings + ) return sourceFiles.map( ([filename, code]: [string, { content: string }]) => ({ code: code.content, @@ -305,6 +331,7 @@ export class EtherscanParser { files: results.flat(1), contractName: response.data.result[0].ContractName, compilerVersion: response.data.result[0].CompilerVersion, + remappings, } } catch (err) { if (err.message) { @@ -320,3 +347,29 @@ export class EtherscanParser { } } } + +/** + * Parses Ethersan's remappings config in its API response + * @param rawMappings + */ +export const parseRemappings = (rawMappings: string[]): Remapping[] => { + if (!rawMappings) return [] + return rawMappings.map((mapping: string) => parseRemapping(mapping)) +} + +/** + * Parses a single mapping. For example + * "@openzeppelin/=lib/openzeppelin-contracts/" + * This is from Uniswap's UniversalRouter in the Settings section after the source files + * https://etherscan.io/address/0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B#code + * @param mapping + */ +export const parseRemapping = (mapping: string): Remapping => { + const equalIndex = mapping.indexOf('=') + const from = mapping.slice(0, equalIndex) + const to = mapping.slice(equalIndex + 1) + return { + from: new RegExp('^' + from), + to, + } +} diff --git a/src/ts/parserFiles.ts b/src/ts/parserFiles.ts index f0254e8d..89fc1806 100644 --- a/src/ts/parserFiles.ts +++ b/src/ts/parserFiles.ts @@ -10,8 +10,8 @@ import { UmlClass } from './umlClass' const debug = require('debug')('sol2uml') export const parseUmlClassesFromFiles = async ( - filesOrFolders: string[], - ignoreFilesOrFolders: string[], + filesOrFolders: readonly string[], + ignoreFilesOrFolders: readonly string[], subfolders: number = -1 ): Promise => { const files = await getSolidityFilesFromFolderOrFiles( @@ -27,7 +27,7 @@ export const parseUmlClassesFromFiles = async ( const relativePath = relative(process.cwd(), file) - const umlClass = convertAST2UmlClasses(node, relativePath, true) + const umlClass = convertAST2UmlClasses(node, relativePath, [], true) umlClasses = umlClasses.concat(umlClass) } @@ -35,8 +35,8 @@ export const parseUmlClassesFromFiles = async ( } export async function getSolidityFilesFromFolderOrFiles( - folderOrFilePaths: string[], - ignoreFilesOrFolders: string[], + folderOrFilePaths: readonly string[], + ignoreFilesOrFolders: readonly string[], subfolders: number = -1 ): Promise { let files: string[] = [] @@ -55,7 +55,7 @@ export async function getSolidityFilesFromFolderOrFiles( export function getSolidityFilesFromFolderOrFile( folderOrFilePath: string, - ignoreFilesOrFolders: string[] = [], + ignoreFilesOrFolders: readonly string[] = [], depthLimit: number = -1 ): Promise { debug(`About to get Solidity files under ${folderOrFilePath}`) @@ -122,7 +122,7 @@ export function getSolidityFilesFromFolderOrFile( ) } - console.error(error.stack) + console.error(error) reject(error) } }) diff --git a/src/ts/parserGeneral.ts b/src/ts/parserGeneral.ts index 03790a09..d679b83a 100644 --- a/src/ts/parserGeneral.ts +++ b/src/ts/parserGeneral.ts @@ -20,8 +20,14 @@ export interface ParserOptions { export const parserUmlClasses = async ( fileFolderAddress: string, options: ParserOptions -): Promise<{ umlClasses: UmlClass[]; contractName?: string }> => { - let result: { umlClasses: UmlClass[]; contractName?: string } = { +): Promise<{ + umlClasses: UmlClass[] + contractName?: string +}> => { + let result: { + umlClasses: UmlClass[] + contractName?: string + } = { umlClasses: [], } if (isAddress(fileFolderAddress)) { diff --git a/src/ts/slotValues.ts b/src/ts/slotValues.ts index ba502f6d..057ca1c7 100644 --- a/src/ts/slotValues.ts +++ b/src/ts/slotValues.ts @@ -1,12 +1,222 @@ import { BigNumber, BigNumberish } from '@ethersproject/bignumber' import axios from 'axios' +import { StorageSection, Variable } from './converterClasses2Storage' +import { AttributeType } from './umlClass' +import { + commify, + formatUnits, + getAddress, + hexValue, + toUtf8String, +} from 'ethers/lib/utils' +import { SlotValueCache } from './SlotValueCache' const debug = require('debug')('sol2uml') interface StorageAtResponse { jsonrpc: '2.0' id: string - result: string + result?: string + error?: { code: number; message: string } +} + +/** + * Adds the slot values to the variables in the storage section. + * This can be rerun for a section as it will only get if the slot value + * does not exist. + * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy + * @param contractAddress Contract address to get the storage slot values from. + * If contract is proxied, use proxy and not the implementation contract. + * @param storageSection is mutated with the slot values added to the variables + * @param arrayItems the number of items to display at the start and end of an array + * @param blockTag block number or `latest` + */ +export const addSlotValues = async ( + url: string, + contractAddress: string, + storageSection: StorageSection, + arrayItems: number, + blockTag: BigNumberish +) => { + const valueVariables = storageSection.variables.filter( + (variable) => variable.getValue && !variable.slotValue + ) + if (valueVariables.length === 0) return + + // for each variable, add all the slots used by the variable. + const slots: BigNumberish[] = [] + valueVariables.forEach((variable) => { + for (let i = 0; variable.fromSlot + i <= variable.toSlot; i++) { + if ( + variable.attributeType === AttributeType.Array && + i >= arrayItems && + i < variable.toSlot - arrayItems + ) { + continue + } + slots.push(variable.fromSlot + i) + } + }) + // remove duplicate slot numbers + const uniqueFromSlots = [...new Set(slots)] + + // Convert slot numbers to BigNumbers and offset dynamic arrays + let slotKeys = uniqueFromSlots.map((fromSlot) => { + if (storageSection.offset) { + return BigNumber.from(storageSection.offset).add(fromSlot) + } + return BigNumber.from(fromSlot) + }) + + // Get the contract slot values from the node provider + const values = await getSlotValues(url, contractAddress, slotKeys, blockTag) + + // For each slot value retrieved + values.forEach((value, i) => { + // Get the corresponding slot number for the slot value + const fromSlot = uniqueFromSlots[i] + + // For each variable in the storage section + for (const variable of storageSection.variables) { + if (variable.getValue && variable.fromSlot === fromSlot) { + debug( + `Set slot value ${value} for section "${storageSection.name}", var type ${variable.type}, slot ${variable.fromSlot} offset ${storageSection.offset}` + ) + variable.slotValue = value + // parse variable value from slot data + if (variable.displayValue) { + variable.parsedValue = parseValue(variable) + } + } + // if variable is past the slot that has the value + else if (variable.toSlot > fromSlot) { + break + } + } + }) +} + +export const parseValue = (variable: Variable): string => { + if (!variable.slotValue) return undefined + const start = 66 - (variable.byteOffset + variable.byteSize) * 2 + const end = 66 - variable.byteOffset * 2 + const variableValue = variable.slotValue.substring(start, end) + + try { + // Contracts, structs and enums + if (variable.attributeType === AttributeType.UserDefined) { + return parseUserDefinedValue(variable, variableValue) + } + + if (variable.attributeType === AttributeType.Elementary) + return parseElementaryValue(variable, variableValue) + + // dynamic arrays + if ( + variable.attributeType === AttributeType.Array && + variable.dynamic + ) { + return formatUnits('0x' + variableValue, 0) + } + + return undefined + } catch (err) { + throw Error( + `Failed to parse variable ${variable.name} of type ${variable.type}, value "${variableValue}"`, + { cause: err } + ) + } +} + +const parseUserDefinedValue = ( + variable: Variable, + variableValue: string +): string => { + // TODO need to handle User Defined Value Types introduced in Solidity + // https://docs.soliditylang.org/en/v0.8.18/types.html#user-defined-value-types + // https://blog.soliditylang.org/2021/09/27/user-defined-value-types/ + + // using byteSize is crude and will be incorrect for aliases types like int160 or uint160 + if (variable.byteSize === 20) { + return getAddress('0x' + variableValue) + } + // this will also be wrong if the alias is to a 1 byte type. eg bytes1, int8 or uint8 + if (variable.byteSize === 1) { + // assume 1 byte is an enum so convert value to enum index number + const index = BigNumber.from('0x' + variableValue).toNumber() + // lookup enum value if its available + return variable?.enumValues ? variable?.enumValues[index] : undefined + } + // we don't parse if a struct which has a size of 32 bytes + return undefined +} + +const parseElementaryValue = ( + variable: Variable, + variableValue: string +): string => { + // Elementary types + if (variable.type === 'bool') { + if (variableValue === '00') return 'false' + if (variableValue === '01') return 'true' + throw Error( + `Failed to parse bool variable "${variable.name}" in slot ${variable.fromSlot}, offset ${variable.byteOffset} and slot value "${variableValue}"` + ) + } + if (variable.type === 'string' || variable.type === 'bytes') { + if (variable.dynamic) { + const lastByte = variable.slotValue.slice(-2) + const size = BigNumber.from('0x' + lastByte) + // Check if the last bit is set by AND the size with 0x01 + if (size.and(1).eq(1)) { + // Return the number of chars or bytes + return BigNumber.from(variable.slotValue) + .sub(1) + .div(2) + .toString() + } + + // The last byte holds the length of the string or bytes in the slot + const valueHex = '0x' + variableValue.slice(0, size.toNumber()) + if (variable.type === 'bytes') return valueHex + return `\\"${convert2String(valueHex)}\\"` + } + if (variable.type === 'bytes') return '0x' + variableValue + return `\\"${convert2String('0x' + variableValue)}\\"` + } + if (variable.type === 'address') { + return getAddress('0x' + variableValue) + } + if (variable.type.match(/^uint([0-9]*)$/)) { + const parsedValue = formatUnits('0x' + variableValue, 0) + return commify(parsedValue) + } + if (variable.type.match(/^bytes([0-9]+)$/)) { + return '0x' + variableValue + } + if (variable.type.match(/^int([0-9]*)/)) { + // parse variable value as an unsigned number + let rawValue = BigNumber.from('0x' + variableValue) + + // parse the number of bits + const result = variable.type.match(/^int([0-9]*$)/) + const bitSize = result[1] ? result[1] : 256 + // Convert the number of bits to the number of hex characters + const hexSize = BigNumber.from(bitSize).div(4).toNumber() + // bit mask has a leading 1 and the rest 0. 0x8 = 1000 binary + const mask = '0x80' + '0'.repeat(hexSize - 2) + // is the first bit a 1? + const negative = rawValue.and(mask) + if (negative.gt(0)) { + // Convert unsigned number to a signed negative + const negativeOne = '0xFF' + 'F'.repeat(hexSize - 2) + rawValue = BigNumber.from(negativeOne).sub(rawValue).add(1).mul(-1) + } + const parsedValue = formatUnits(rawValue, 0) + return commify(parsedValue) + } + // add fixed point numbers when they are supported by Solidity + return undefined } let jsonRpcId = 0 @@ -15,53 +225,77 @@ let jsonRpcId = 0 * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slots array of slot numbers to retrieve values for. + * @param slotKeys array of 32 byte slot keys as BigNumbers. * @param blockTag block number or `latest` + * @return slotValues array of 32 byte slot values as hexadecimal strings */ -export const getStorageValues = async ( +export const getSlotValues = async ( url: string, contractAddress: string, - slots: BigNumberish[], + slotKeys: readonly BigNumberish[], blockTag: BigNumberish | 'latest' = 'latest' ): Promise => { try { - debug( - `About to get ${slots.length} storage values for ${contractAddress} at block ${blockTag}` - ) + if (slotKeys.length === 0) { + return [] + } const block = blockTag === 'latest' ? blockTag - : BigNumber.from(blockTag).toHexString() - const payload = slots.map((slot) => ({ + : hexValue(BigNumber.from(blockTag)) + + // get cached values and missing slot keys from from cache + const { cachedValues, missingKeys } = + SlotValueCache.readSlotValues(slotKeys) + + // If all values are in the cache then just return the cached values + if (missingKeys.length === 0) { + return cachedValues + } + + debug( + `About to get ${ + slotKeys.length + } storage values for ${contractAddress} at block ${blockTag} from slot ${missingKeys[0].toString()}` + ) + // Get the values for the missing slot keys + const payload = missingKeys.map((key) => ({ id: (jsonRpcId++).toString(), jsonrpc: '2.0', method: 'eth_getStorageAt', - params: [ - contractAddress, - BigNumber.from(slot).toHexString(), - block, - ], + params: [contractAddress, key, block], })) const response = await axios.post(url, payload) - console.log(response.data) + if (response.data?.error?.message) { - throw new Error(response.data.error.message) + throw Error(response.data.error.message) } - if (response.data.length !== slots.length) { - throw new Error( - `Requested ${slots.length} storage slot values but only got ${response.data.length}` + if (response.data.length !== missingKeys.length) { + throw Error( + `Requested ${missingKeys.length} storage slot values but only got ${response.data.length}` ) } const responseData = response.data as StorageAtResponse[] const sortedResponses = responseData.sort((a, b) => BigNumber.from(a.id).gt(b.id) ? 1 : -1 ) - return sortedResponses.map( - (data) => '0x' + data.result.toUpperCase().slice(2) + const missingValues = sortedResponses.map((data) => { + if (data.error) { + throw Error( + `json rpc call with id ${data.id} failed to get storage values: ${data.error?.message}` + ) + } + return '0x' + data.result.toUpperCase().slice(2) + }) + // add new values to the cache and return the merged slot values + return SlotValueCache.addSlotValues( + slotKeys, + missingKeys, + missingValues ) } catch (err) { - throw new Error( - `Failed to get ${slots.length} storage values for ${contractAddress} from ${url}`, + throw Error( + `Failed to get ${slotKeys.length} storage values for contract ${contractAddress} from ${url}`, { cause: err } ) } @@ -72,24 +306,69 @@ export const getStorageValues = async ( * @param url of Ethereum JSON-RPC API provider. eg Infura or Alchemy * @param contractAddress Contract address to get the storage slot values from. * If proxied, use proxy and not the implementation contract. - * @param slot slot number to retrieve the value for. + * @param slotKey 32 byte slot key as a BigNumber. * @param blockTag block number or `latest` + * @return slotValue 32 byte slot value as hexadecimal string */ -export const getStorageValue = async ( +export const getSlotValue = async ( url: string, contractAddress: string, - slot: BigNumberish, - blockTag: BigNumberish | 'latest' = 'latest' + slotKey: BigNumberish, + blockTag: BigNumberish | 'latest' ) => { - debug(`About to get storage slot ${slot} value for ${contractAddress}`) + debug(`About to get storage slot ${slotKey} value for ${contractAddress}`) - const values = await getStorageValues( + const values = await getSlotValues( url, contractAddress, - [slot], + [slotKey], blockTag ) - debug(`Got slot ${slot} value: ${values[0]}`) return values[0] } + +/** + * Calculates the number of string characters or bytes of a string or bytes type. + * See the following for how string and bytes are stored in storage slots + * https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#bytes-and-string + * @param variable the variable with the slotValue that is being sized + * @return bytes the number of bytes of the dynamic slot. If static, zero is return. + */ +export const dynamicSlotSize = (variable: { + name?: string + type?: string + slotValue?: string +}): number => { + try { + if (!variable?.slotValue) throw Error(`Missing slot value.`) + const last4bits = '0x' + variable.slotValue.slice(-1) + const last4bitsNum = BigNumber.from(last4bits).toNumber() + // If the last 4 bits is an even number then it's not a dynamic slot + if (last4bitsNum % 2 === 0) return 0 + + const sizeRaw = BigNumber.from(variable.slotValue).toNumber() + // Adjust the size to bytes + return (sizeRaw - 1) / 2 + } catch (err) { + throw Error( + `Failed to calculate dynamic slot size for variable "${variable?.name}" of type "${variable?.type}" with slot value ${variable?.slotValue}`, + { cause: err } + ) + } +} + +export const convert2String = (bytes: string): string => { + if ( + bytes === + '0x0000000000000000000000000000000000000000000000000000000000000000' + ) { + return '' + } + const rawString = toUtf8String(bytes) + return escapeString(rawString) +} + +export const escapeString = (text: string): string => { + return text.replace(/(?=[<>&"])/g, '\\') +} diff --git a/src/ts/sol2uml.ts b/src/ts/sol2uml.ts index 92671247..c308c28d 100644 --- a/src/ts/sol2uml.ts +++ b/src/ts/sol2uml.ts @@ -9,8 +9,8 @@ import { } from './filterClasses' import { Command, Option } from 'commander' import { - addStorageValues, - convertClasses2Storages, + addDynamicVariables, + convertClasses2StorageSections, } from './converterClasses2Storage' import { convertStorages2Dot } from './converterStorage2Dot' import { isAddress } from './utils/regEx' @@ -18,16 +18,12 @@ import { writeOutputFiles, writeSolidity } from './writerFiles' import { basename } from 'path' import { squashUmlClasses } from './squashClasses' import { diffCode } from './diff' +import { addSlotValues } from './slotValues' +import { ethers } from 'ethers' const clc = require('cli-color') const program = new Command() -const version = - basename(__dirname) === 'lib' - ? require('../package.json').version // used when run from compile js in /lib - : require('../../package.json').version // used when run from TypeScript source files under src/ts via ts-node -program.version(version) - const debugControl = require('debug') const debug = require('debug')('sol2uml') @@ -71,8 +67,30 @@ The Solidity code can be pulled from verified source code on Blockchain explorer 'Blockchain explorer API key. eg Etherscan, Arbiscan, Optimism, BscScan, CronoScan, FTMScan, PolygonScan or SnowTrace API key' ).env('SCAN_API_KEY') ) + .option( + '-bc, --backColor ', + 'Canvas background color. "none" will use a transparent canvas.', + 'white' + ) + .option( + '-sc, --shapeColor ', + 'Basic drawing color for graphics, not text', + 'black' + ) + .option( + '-fc, --fillColor ', + 'Color used to fill the background of a node', + 'gray95' + ) + .option('-tc, --textColor ', 'Color used for text', 'black') .option('-v, --verbose', 'run with debugging statements', false) +const version = + basename(__dirname) === 'lib' + ? require('../package.json').version // used when run from compile js in /lib + : require('../../package.json').version // used when run from TypeScript source files under src/ts via ts-node +program.version(version) + program .command('class', { isDefault: true }) .description('Generates a UML class diagram from Solidity source code.') @@ -260,6 +278,11 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k 'Block number to get the contract storage values from.', 'latest' ) + .option( + '-a, --array ', + 'Number of slots to display at the start and end of arrays.', + '2' + ) .action(async (fileFolderAddress, options, command) => { try { const combinedOptions = { @@ -280,17 +303,18 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k ) contractName = combinedOptions.contract || contractName - const storages = convertClasses2Storages( + const arrayItems = parseInt(combinedOptions.array) + const storageSections = convertClasses2StorageSections( contractName, umlClasses, + arrayItems, combinedOptions.contractFile ) if (isAddress(fileFolderAddress)) { // The first storage is the contract - storages[0].address = fileFolderAddress + storageSections[0].address = fileFolderAddress } - debug(storages) if (combinedOptions.data) { let storageAddress = combinedOptions.storage @@ -309,20 +333,42 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k storageAddress = fileFolderAddress } - const storage = storages.find((so) => so.name === contractName) - if (!storageAddress) - throw Error( - `Could not find the "${contractName}" contract in list of parsed storages` + let block = combinedOptions.block + if (block === 'latest') { + const provider = new ethers.providers.JsonRpcProvider( + combinedOptions.url ) - await addStorageValues( - combinedOptions.url, - storageAddress, - storage, - combinedOptions.block - ) + block = await provider.getBlockNumber() + debug( + `Latest block is ${block}. All storage slot values will be from this block.` + ) + } + + // Get slot values for each storage section + for (const storageSection of storageSections) { + await addSlotValues( + combinedOptions.url, + storageAddress, + storageSection, + arrayItems, + block + ) + // Add storage variables for dynamic arrays, strings and bytes + await addDynamicVariables( + storageSection, + storageSections, + combinedOptions.url, + storageAddress, + arrayItems, + block + ) + } } - const dotString = convertStorages2Dot(storages, combinedOptions) + const dotString = convertStorages2Dot( + storageSections, + combinedOptions + ) await writeOutputFiles( dotString, @@ -331,7 +377,7 @@ WARNING: sol2uml does not use the Solidity compiler so may differ with solc. A k combinedOptions.outputFileName ) } catch (err) { - console.error(err.stack) + console.error(err) process.exit(2) } }) diff --git a/src/ts/squashClasses.ts b/src/ts/squashClasses.ts index ea7528db..25e3e30e 100644 --- a/src/ts/squashClasses.ts +++ b/src/ts/squashClasses.ts @@ -5,13 +5,13 @@ const debug = require('debug')('sol2uml') /** * Flattens the inheritance hierarchy for each base contract. - * @param umlClasses array of UML classes of type `UMLClass` + * @param umlClasses array of UML classes of type `UMLClass`. The new squashed class is added to this array. * @param baseContractNames array of contract names to be rendered in squashed format. - * @return squashUmlClasses array of UML classes of type `UMLClass` + * @return squashUmlClasses array of UML classes of type `UMLClass` that are to be rendered */ export const squashUmlClasses = ( umlClasses: UmlClass[], - baseContractNames: string[] + baseContractNames: readonly string[] ): UmlClass[] => { let removedClassIds: number[] = [] for (const baseContractName of baseContractNames) { @@ -61,7 +61,7 @@ const recursiveSquash = ( squashedClass: UmlClass, inheritedContractNames: string[], baseClass: UmlClass, - umlClasses: UmlClass[], + umlClasses: readonly UmlClass[], startPosition: number ): { currentPosition: number; removedClassIds: number[] } => { let currentPosition = startPosition @@ -158,7 +158,7 @@ const hash = (operator: Operator): string => { return hash.update(data).digest('hex') } -const reduceOperators = (operators: Operator[]): Operator[] => { +const reduceOperators = (operators: readonly Operator[]): Operator[] => { const hashes = new Set(operators.map((o) => o.hash)) const operatorsWithNoHash = operators.filter((o) => !o.hash) diff --git a/src/ts/writerFiles.ts b/src/ts/writerFiles.ts index 33273197..0683d86d 100644 --- a/src/ts/writerFiles.ts +++ b/src/ts/writerFiles.ts @@ -182,6 +182,4 @@ export async function writePng(svg: any, filename: string): Promise { cause: err, }) } - - console.log(`Generated png file ${pngFilename}`) }