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
  • SPDX
  • Pragma
  1. LEARN
  2. Introduction
  3. 2. Solidity

2.2 SPDX & Pragmas

SPDX

One of the things that you will often see specified at the top of every Solidity file is what is known as the SPDX license identifier. SPDX stands for Software Package Data Exchange. In the case of Solidity it's a comment that indicates its license and it is specified as a best practice to be at the top of every file. An example looks like this

// SPDX-License-Identifier: AGPLv3

The specific license obviously depends on what the developer intends for the particular smart contract. This identifier (i.e. the license) is included by the compiler in the byte code metadata that is generated, so it becomes machine readable.

Pragma

The pragma keyword in Solidity is used to enable certain compiler features or compiler checks. An example looks something like this

pragma solidity ^0.8.0;

At a high level, there are two types of pragmas:

  1. The first kind specifies the version. There are, again, two types of versions that can be specified:\

    a. The version pragma, which indicates the specific Solidity compiler version that the developer expects to be used for that source file, and it looks like\

    pragma solidity x.y.z;

    where x, y and z are numerals that specify that compiler version.\

    This does not change the version of the compiler used nor enables or disables any features of the compiler. All it does is instructing the compiler at compilation time to check whether its version matches the one specified by the developer. This could be of several formats: it could be a very simple format, a complex one or even a floating one (which has some security implications).\

    The latest Solidity compiler versions as of now are in the 0.8 range with a different z in the pragma directive. If you look at x.y.z; a different z indicates bug fixes and a different y indicates breaking changes between the compiler versions.\

    So if we have compiler versions in the 0.5 range, then by looking at the 0.6 range it means that the 0.6.z range has at least one or more breaking changes compared to the previous versions.\

    A floating pragma is a pragma that has a caret symbol (^) prefixed to x.y.z in the directive. This indicates that the contract can be compiled with versions starting with x.y.z all the way until x.(y + 1).z. So, as an example, consider\

    pragma solidity ^0.8.3;

    It indicates that the source file can be compiled with any compiler version starting from 0.8.3 going to 0.8.4, 0.8.5 and whatever else has been released; but not 0.9.z, so the transition from 0.8 to 0.9 is what is prevented by this floating platform.\

    This allows the developer to specify a range of compiler versions that can be used with a particular contract, and that has some security implications similar to the floating pragma.\

    A range of compiler versions can be indicated with a complex practice, where you have >, >=, <, <= symbols that are used to combine multiple versions of the Solidity compiler.\

    This affects the compiler version, which in turn brings in different features that are implemented by said version. Some of those could be security features, others could be security bug fixes or optimizations.\

    All these aspects affect the security posture of the bytecode that is generated from a particular smart contract.\

    b. The ABI coder pragma. This directive allows a developer to specify the choice between Version 1 or Version 2 ABI coder.\

    The newer Version 2 was considered experimental for a while, but is now activated by default and allows the encoding/decoding of nested arrays and structs.\

    You might encounter old Solidity source code using the old directive, such as shown below\

    pragma experimental ABIEncoderV2;

    Version 2 is a strict superset of Version 1: contracts that use Version 2 can interact with other contracts that do not use it without any concern or limitations.\

    This pragma also applies to the code defined in the file where it is activated, regardless of where that code ends up eventually; what this means is that a contract whose file is using Version 1 can still contain code that uses Version 2 by inheriting it from another contract. An example of ABI Coder pragma statement is\

    pragma abicoder v1; // or v2, which is the default from version 0.8.z onwards

    The ABI coder affects encoding and decoding. The optimizations it does have certain security implications.

  2. The second pragma directive helps the developer to specify features that are considered experimental as of that point in time.\

    These features are not enabled by default and have to be explicitly specified as part of this pragma directive and within every file where it is required. As of now there is only one experimental feature, which is known as SMTChecker.\

    pragma experimental SMTChecker;

    SMT stands for Satisfiability Modulo Theory which is an approach to formal verification, and in the case of Solidity it is used to implement safety checks by what is known as querying an SMT solver.\

    There are various security checks performed by the SMT checker. The first one is where it uses the require and assert statements that are included as part of the smart contract.\

    The checker considers all the required statements specified as assumptions by the developer and it tries to prove that the conditions inside the assert statements are true.\

    If a failure can be established, then the checker provides what is known as a counter example that shows the user how this assertion can fail.\

    There are various other checks that have been added to this empty checker over time. These include the arithmetic overflow, underflow, division by zero, unreachable code and so on.\

    So SMT checker is a critical security feature that comes packaged as part of Solidity. It's implemented in the compiler itself.\

    Formal verification is considered as a fundamental part of programming languages' security, so we can imagine that this particular pragma directive affects the security and optimizations of the smart contracts that use them.

What needs to be kept in mind with the pragma directives is that they are local to the files where they are specified. So if you have a Solidity file that imports other files, the pragmas from the imported files do not automatically carry over to the file that is of concern.

Previous2.1 Solidity: Influence, Features & LayoutNext2.3 Imports

Last updated 1 year ago

📚
🌀