# Upgradeability Checks `slither-check-upgradeability` helps review contracts that use the [delegatecall proxy pattern](https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns/). ## Checks | Num | Check | What it Detects | Impact | Proxy | Contract V2 | | --- | ----------------------- | --------------------------------------------------------------------------------------- | ------------- | ----- | ----------- | | 1 | `became-constant` | [Variables that should not be constant](#variables-that-should-not-be-constant) | High | | X | | 2 | `function-id-collision` | [Functions IDs collision](#functions-ids-collisions) | High | X | | 3 | `function-shadowing` | [Functions shadowing](#functions-shadowing) | High | X | | 4 | `missing-calls` | [Missing calls to init functions](#initialize-functions-are-not-called) | High | | | 5 | `missing-init-modifier` | [initializer() is not called](#initializer-is-not-called) | High | | | 6 | `multiple-calls` | [Init functions called multiple times](#initialize-functions-are-called-multiple-times) | High | | | 7 | `order-vars-contracts` | [Incorrect vars order with the v2](#incorrect-variables-with-the-v2) | High | | X | | 8 | `order-vars-proxy` | [Incorrect vars order with the proxy](#incorrect-variables-with-the-proxy) | High | X | | 9 | `variables-initialized` | [State variables with an initial value](#state-variable-initialized) | High | | | 10 | `were-constant` | [Variables that should be constant](#variables-that-should-be-constant) | High | | X | | 11 | `extra-vars-proxy` | [Extra vars in the proxy](#extra-variables-in-the-proxy) | Medium | X | | 12 | `missing-variables` | [Variable missing in the v2](#missing-variables) | Medium | | X | | 13 | `extra-vars-v2` | [Extra vars in the v2](#extra-variables-in-the-v2) | Informational | | X | | 14 | `init-inherited` | [Initializable is not inherited](#initializable-is-not-inherited) | Informational | | | 15 | `init-missing` | [Initializable is missing](#initializable-is-missing) | Informational | | | 16 | `initialize-target` | [Initialize function that must be called](#initialize-function) | Informational | | | 17 | `initializer-missing` | [initializer() is missing](#initializer-is-missing) | Informational | | ## Usage ``` slither-check-upgradeability project ContractName ``` - `project` can be a Solidity file, or a platform (truffle/embark/..) directory ### Contract V2 If you want to check the contract and its update, use: - `--new-contract-name ContractName` - `--new-contract-filename contract_project` `--new-contract-filename` is not needed if the new contract is in the same codebase than the original one. ### Proxy If you want to check also the proxy, use: - `--proxy-name ProxyName` - `--proxy-filename proxy_project` `--proxy-filename` is not needed if the proxy is in the same codebase than the targeted contract. #### ZOS If you use zos, you will have the proxy and the contract in different directories. Likely, you will use one of the proxy from https://github.com/zeppelinos/zos. Clone the `zos`, and install the dependencies: ``` git clone https://github.com/zeppelinos/zos cd zos/packages/lib npm install rm contracts/mocks/WithConstructorImplementation.sol ``` Note: `contracts/mocks/WithConstructorImplementation.sol` must be removed as it contains a [name clash collision](https://github.com/crytic/slither/wiki#keyerror-or-nonetype-error) with `contracts/mocks/Invalid.sol` Then from your project directory: ``` slither-check-upgradeability . ContractName --proxy-filename /path/to/zos/packages/lib/ --proxy-name UpgradeabilityProxy ``` According to your setup, you might choose another proxy name than `UpgradeabilityProxy`. ## Checks Description ## Variables that should not be constant ### Configuration - Check: `became-constant` - Severity: `High` ### Description Detect state variables that should not be `constant̀€`. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; uint variable2; uint variable3; } contract ContractV2{ uint variable1; uint constant variable2; uint variable3; } ``` Because `variable2` is now a `constant`, the storage location of `variable3` will be different. As a result, `ContractV2` will have a corrupted storage layout. ### Recommendation Do not make an existing state variable `constant`. ## Functions IDs collisions ### Configuration - Check: `function-id-collision` - Severity: `High` ### Description Detect function ID collision between the contract and the proxy. ### Exploit Scenario: ```solidity contract Contract{ function gsf() public { // ... } } contract Proxy{ function tgeo() public { // ... } } ``` `Proxy.tgeo()` and `Contract.gsf()` have the same function ID (0x67e43e43). As a result, `Proxy.tgeo()` will shadow Contract.gsf()`. ### Recommendation Rename the function. Avoid public functions in the proxy. ## Functions shadowing ### Configuration - Check: `function-shadowing` - Severity: `High` ### Description Detect function shadowing between the contract and the proxy. ### Exploit Scenario: ```solidity contract Contract{ function get() public { // ... } } contract Proxy{ function get() public { // ... } } ``` `Proxy.get` will shadow any call to `get()`. As a result `get()` is never executed in the logic contract and cannot be updated. ### Recommendation Rename the function. Avoid public functions in the proxy. ## Initialize functions are not called ### Configuration - Check: `missing-calls` - Severity: `High` ### Description Detect missing calls to initialize functions. ### Exploit Scenario: ```solidity contract Base{ function initialize() public{ /// } } contract Derived is Base{ function initialize() public{ /// } } ``` `Derived.initialize` does not call `Base.initialize` leading the contract to not be correctly initialized. ### Recommendation Ensure all the initialize functions are reached by the most derived initialize function. ## initializer() is not called ### Configuration - Check: `missing-init-modifier` - Severity: `High` ### Description Detect if `Initializable.initializer()` is called. ### Exploit Scenario: ```solidity contract Contract{ function initialize() public{ /// } } ``` `initialize` should have the `initializer` modifier to prevent someone from initializing the contract multiple times. ### Recommendation Use `Initializable.initializer()`. ## Initialize functions are called multiple times ### Configuration - Check: `multiple-calls` - Severity: `High` ### Description Detect multiple calls to a initialize function. ### Exploit Scenario: ```solidity contract Base{ function initialize(uint) public{ /// } } contract Derived is Base{ function initialize(uint a, uint b) public{ initialize(a); } } contract DerivedDerived is Derived{ function initialize() public{ initialize(0); initialize(0, 1 ); } } ``` `Base.initialize(uint)` is called two times in `DerivedDerived.initialize` execution, leading to a potential corruption. ### Recommendation Call only one time every initialize function. ## Incorrect variables with the v2 ### Configuration - Check: `order-vars-contracts` - Severity: `High` ### Description Detect variables that are different between the original contract and the updated one. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; } contract ContractV2{ address variable1; } ``` `Contract` and `ContractV2` do not have the same storage layout. As a result the storage of both contracts can be corrupted. ### Recommendation Respect the variable order of the original contract in the updated contract. ## Incorrect variables with the proxy ### Configuration - Check: `order-vars-proxy` - Severity: `High` ### Description Detect variables that are different between the contract and the proxy. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; } contract Proxy{ address variable1; } ``` `Contract` and `Proxy` do not have the same storage layout. As a result the storage of both contracts can be corrupted. ### Recommendation Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. ## State variable initialized ### Configuration - Check: `variables-initialized` - Severity: `High` ### Description Detect state variables that are initialized. ### Exploit Scenario: ```solidity contract Contract{ uint variable = 10; } ``` Using `Contract` will the delegatecall proxy pattern will lead `variable` to be 0 when called through the proxy. ### Recommendation Using initialize functions to write initial values in state variables. ## Variables that should be constant ### Configuration - Check: `were-constant` - Severity: `High` ### Description Detect state variables that should be `constant̀€`. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; uint constant variable2; uint variable3; } contract ContractV2{ uint variable1; uint variable2; uint variable3; } ``` Because `variable2` is not anymore a `constant`, the storage location of `variable3` will be different. As a result, `ContractV2` will have a corrupted storage layout. ### Recommendation Do not remove `constant` from a state variables during an update. ## Extra variables in the proxy ### Configuration - Check: `extra-vars-proxy` - Severity: `Medium` ### Description Detect variables that are in the proxy and not in the contract. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; } contract Proxy{ uint variable1; uint variable2; } ``` `Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. ### Recommendation Avoid variables in the proxy. If a variable is in the proxy, ensure it has the same layout than in the contract. ## Missing variables ### Configuration - Check: `missing-variables` - Severity: `Medium` ### Description Detect variables that were present in the original contracts but are not in the updated one. ### Exploit Scenario: ```solidity contract V1{ uint variable1; uint variable2; } contract V2{ uint variable1; } ``` The new version, `V2` does not contain `variable1`. If a new variable is added in an update of `V2`, this variable will hold the latest value of `variable2` and will be corrupted. ### Recommendation Do not change the order of the state variables in the updated contract. ## Extra variables in the v2 ### Configuration - Check: `extra-vars-v2` - Severity: `Informational` ### Description Show new variables in the updated contract. This finding does not have an immediate security impact and is informative. ### Exploit Scenario: ```solidity contract Contract{ uint variable1; } contract Proxy{ uint variable1; uint variable2; } ``` `Proxy` contains additional variables. A future update of `Contract` is likely to corrupt the proxy. ### Recommendation Ensure that all the new variables are expected. ## Initializable is not inherited ### Configuration - Check: `init-inherited` - Severity: `Informational` ### Description Detect if `Initializable` is inherited. ### Recommendation Review manually the contract's initialization. Consider inheriting `Initializable`. ## Initializable is missing ### Configuration - Check: `init-missing` - Severity: `Informational` ### Description Detect if a contract `Initializable` is present. ### Recommendation Review manually the contract's initialization.. Consider using a `Initializable` contract to follow [standard practice](https://docs.openzeppelin.com/upgrades/2.7/writing-upgradeable). ## Initialize function ### Configuration - Check: `initialize-target` - Severity: `Informational` ### Description Show the function that must be called at deployment. This finding does not have an immediate security impact and is informative. ### Recommendation Ensure that the function is called at deployment. ## initializer() is missing ### Configuration - Check: `initializer-missing` - Severity: `Informational` ### Description Detect the lack of `Initializable.initializer()` modifier. ### Recommendation Review manually the contract's initialization. Consider inheriting a `Initializable.initializer()` modifier.