Secureum Book
  • 🛡️Secureum Bootcamp
    • 🛡️Secureum Bootcamp
    • 🙌Participate
    • 📜History
  • 📚LEARN
    • Introduction
      • 🔷1. Ethereum Basics
        • 1.1 Ethereum: Concept, Infrastructure & Purpose
        • 1.2 Properties of the Ethereum Infrastructure
        • 1.3 Ethereum vs. Bitcoin
        • 1.4 Ethereum Core Components
        • 1.5 Gas Metering: Solving the Halting Problem
        • 1.6 web2 vs. web3: The Paradigm Shift
        • 1.7 Decentralization
        • 1.8 Cryptography, Digital Signature & Keys
        • 1.9 Ethereum State & Account Types
        • 1.10 Transactions: Properties & Components
        • 1.11 Contract Creation
        • 1.12 Transactions, Messages & Blockchain
        • 1.13 EVM (Ethereum Virtual Machine) in Depth
        • 1.14 Transaction Reverts & Data
        • 1.15 Block Explorer
        • 1.16 Mainnet & Testnets
        • 1.17 ERCs & EIPs
        • 1.18 Legal Aspects in web3: Pseudonymity & DAOs
        • 1.19 Security in web3
        • 1.20 web2 Timescales vs. web3 Timescales
        • 1.21 Test-in-Prod. SSLDC vs. Audits
        • Summary: 101 Keypoints
      • 🌀2. Solidity
        • 2.1 Solidity: Influence, Features & Layout
        • 2.2 SPDX & Pragmas
        • 2.3 Imports
        • 2.4 Comments & NatSpec
        • 2.5 Smart Contracts
        • 2.6 State Variables: Definition, Visibility & Mutability
        • 2.7 Data Location
        • 2.8 Functions
        • 2.9 Events
        • 2.10 Solidity Typing
        • 2.11 Solidity Variables
        • 2.12 Address Type
        • 2.13 Conversions
        • 2.14 Keywords & Shorthand Operators
        • 2.15 Solidity Units
        • 2.16 Block & Transaction Properties
        • 2.17 ABI Encoding & Decoding
        • 2.18 Error Handling
        • 2.19 Mathematical & Cryptographic Functions
        • 2.20 Control Structures
        • 2.21 Style & Conventions
        • 2.22 Inheritance
        • 2.23 EVM Storage
        • 2.24 EVM Memory
        • 2.25 Inline Assembly
        • 2.26 Solidity Version Changes
        • 2.27 Security Checks
        • 2.28 OpenZeppelin Libraries
        • 2.29 DAppSys Libraries
        • 2.30 Important Protocols
        • Summary: 201 Keypoints
      • 🔏3. Security Pitfalls & Best Practices
        • 3.1 Solidity Versions
        • 3.2 Access Control
        • 3.3 Modifiers
        • 3.4 Constructor
        • 3.5 Delegatecall
        • 3.6 Reentrancy
        • 3.7 Private Data
        • 3.8 PRNG & Time
        • 3.9 Math & Logic
        • 3.10 Transaction Order Dependence
        • 3.11 ecrecover
        • 3.12 Unexpected Returns
        • 3.13 Ether Accounting
        • 3.14 Transaction Checks
        • 3.15 Delete Mappings
        • 3.16 State Modification
        • 3.17 Shadowing & Pre-declaration
        • 3.18 Gas & Costs
        • 3.19 Events
        • 3.20 Unary Expressions
        • 3.21 Addresses
        • 3.22 Assertions
        • 3.23 Keywords
        • 3.24 Visibility
        • 3.25 Inheritance
        • 3.26 Reference Parameters
        • 3.27 Arbitrary Jumps
        • 3.28 Hash Collisions & Byte Level Issues
        • 3.29 Unicode RTLO
        • 3.30 Variables
        • 3.31 Pointers
        • 3.32 Out-of-range Enum
        • 3.33 Dead Code & Redundant Statements
        • 3.34 Compiler Bugs
        • 3.35 Proxy Pitfalls
        • 3.36 Token Pitfalls
        • 3.37 Special Token Pitfalls
        • 3.38 Guarded Launch Pitfalls
        • 3.39 System Pitfalls
        • 3.40 Access Control Pitfalls
        • 3.41 Testing, Unused & Redundand Code
        • 3.42 Handling Ether
        • 3.43 Application Logic Pitfalls
        • 3.44 Saltzer & Schroeder's Design Principles
        • Summary: 201 Keypoints
      • 🗜️4. Audit Techniques & Tools
        • 4.1 Audit
        • 4.2 Analysis Techniques
        • 4.3 Specification, Documentation & Testing
        • 4.4 False Positives & Negatives
        • 4.5 Security Tools
        • 4.6 Audit Process
        • Summary: 101 Keypoints
      • ☝️5. Audit Findings
        • 5.1 Criticals
        • 5.2 Highs
        • 5.3 Mediums
        • 5.4 Lows
        • 5.5 Informationals
        • Summary: 201 Keypoints
  • 🌱CARE
    • CARE
      • CARE Reports
  • 🚩CTFs
    • A-MAZE-X CTFs
      • Secureum A-MAZE-X
      • Secureum A-MAZE-X Stanford
      • Secureum A-MAZE-X Maison de la Chimie Paris
Powered by GitBook
On this page
  • Unlocked Pragma
  • Multiple Pragma
  1. LEARN
  2. Introduction
  3. 3. Security Pitfalls & Best Practices

3.1 Solidity Versions

The Solidity language has evolved considerably in the last several years. There have been many features added, some of them removed. Security has been improved in several cases, optimizations have been made.

As a result, there are many versions of Solidity that are available for projects and developers to choose from. At least one version is released every few months that make some optimizations and fixes some bugs a couple of breaking changes are introduced every year or so. As a result, the question is always about which version of the Solidity compiler to use for a particular project, so that the best combination of features and security aspects are considered.

The older compiler versions are time tested, but they have bugs. The newer versions have the bug fixes which is good, but they may also have new bugs which have been undetected so far.

The older versions have lesser features compared to the newer versions ( that usually have more features). Some of these are language level features that are visible syntactically, others are semantic changes, others are security features and some others are optimizations that are not very visible.

As a result, the choice of an optimal version of the compiler for a particular project is always a tricky thing. This has to take account not just the functionality, but also the security aspect. As a result, there is a trade-off to be made, there are risks as well as rewards. As of this point many of the projects are transitioning to the Solidity version 0.8.0 and beyond, because among other things, this version has introduced default arithmetic checks for underflow and overflows.

These aspects of security and functionality, the range of choices available across the various Solidity compiler versions, have to be kept in mind when determining which version to use for a particular project.

Unlocked Pragma

Remember that Solidity supports the concept of pragma directives and one of them is related to the Solidity compiler version, that can be used with this smart contract.

There are many aspects related to that fragment directive, but the one that is relevant from a security perspective, is the concept of that pragma being unlocked or floating and what this means is that in the pragma directive that specifies the compiler version, if the caret (^) symbol is used, then it is referred to as being unlocked.

What this means, is that the use of this caret symbol, specifies that any compiler version starting from the one specified in that pragma directive all the way to the end of that breaking version can be used to compile this smart contract. As an example, if the pragma directive is ^0.8.0 it means that any compiler version from 0.8.0 all the way to the last version in the 0.8.z range can be used according to this pragma for compiling this smart contract.

This becomes interesting from a security perspective. The use of such an unlocked or floating pragma allows one Solidity compiler version to be used for testing, but potentially, a different one that is used for compiling the contracts while being deployed.

This aspect of using a different version for testing and deployment is risky from a security perspective. That's because one could test with a totally different set of compiler features and security checks, the newer version or a different version that is used for deployment may support a different set of features and a different set of security checks, so this mismatch between testing and deployment is allowed by the use of this unlocked pragma and hence is not recommended to be used.

So what is recommended is to lock the pragma by not using the caret symbol in that pragma directive, this will enforce that the same compiler version is used for testing as well as for deployment.

Multiple Pragma

Another security aspect related to the use of the solution compiler pragma in contracts is the use of different pragmas across different contracts within a single project.

Remember that the pragma applies only to the contract where it is used so. If there are different multiple contracts that are used within a single project, then each one of them could have a different pragma specifying a different compiler version.

The reason why this is not recommended is because these different compiler versions like we just discussed can have different bugs, different bug fixes, different features and even different security checks across the versions. This will result in different components of the application having different security properties which is not desirable.

So from a security perspective, what is recommended is to use the same pragma across all the different contracts that form that smart contract application. This will result in all of them having the same set of bugs, features and security checks which can be accounted for while one is testing that smart contact application.

Previous3. Security Pitfalls & Best PracticesNext3.2 Access Control

Last updated 1 year ago

📚
🔏