5 min read

Pragma

Introduction

In the Solidity programming language, pragmas are used to specify the version of the compiler that should be used to compile a smart contract. This is important because changes in the compiler version can lead to incompatibilities with existing code. To manage these changes, Solidity uses semantic versioning notation, which specifies the version number in a specific format that indicates the type of changes made to the language.

Semantic Versioning Notation

Semantic versioning notation uses three numbers separated by periods to indicate major, minor, and patch versions. Major versions are incremented when there are backward-incompatible changes, minor versions are incremented when new features are added but existing syntax remains unchanged, and patch versions are incremented for bug fixes and security patches.

In Solidity, the version number is specified using a string that follows the semantic versioning notation. For example, pragma solidity 0.8.1; specifies that the contract can be compiled with Solidity version 0.8.x. The _ character can be used as a wildcard for patch versions. For example, pragma solidity 0.8._; specifies that the contract can be compiled with any patch version of Solidity 0.8.x.

Here's an example contract written in Solidity that uses the semantic versioning notation and the wildcard character:

pragma solidity 0.8._;

contract MyContract {
    // Code here
}

Ranges

It is also possible to specify a range using the greater-than (>) and less-than (<) symbols. For example, >0.8.1 would match any version greater than 0.8.1, while <0.8.1 would match any version less than 0.8.1. It is also possible to combine these symbols to define more complex version ranges, such as >=0.8.1 <0.9.0.

When using version ranges, it is important to be aware of the potential for unexpected behavior. For example, if a contract is developed using a version range that includes a specific version of Solidity, it may be compiled using a different version of the compiler that is not fully compatible. To avoid this, it is recommended to test contracts using the specific compiler version specified in the pragma.

Semantic versioning notation also allows for the specification of version ranges using the following operators:

  • < means "less than"
  • > means "greater than"
  • <= means "less than or equal to"
  • >= means "greater than or equal to"

For example, pragma solidity >=0.6.0 <0.8.0; specifies that the contract can be compiled with any version of Solidity between 0.6.0 (inclusive) and 0.8.0 (exclusive). Similarly, pragma solidity <=0.7.3; specifies that the contract can be compiled with any version of Solidity up to and including 0.7.3. Here's are some examples of using version ranges:

This specifies that the contract can be compiled with any version of Solidity between 0.6.0 (inclusive) and 0.8.0 (exclusive):

pragma solidity >=0.6.0 <0.8.0;

contract MyContract {
    // Code here
}

This example specifies that the contract can be compiled with any version of Solidity up to and including 0.7.3:


pragma solidity <=0.7.3;

contract MyContract {
    // Code here
}

Let's say we have a smart contract that uses a library and we want to ensure that it only compiles with specific versions of Solidity. We can use the pragma statement to specify the range of Solidity versions that our contract can work with.

// Only compiles with Solidity versions greater than or equal to 0.8.0, but less than 0.9.0
pragma solidity >=0.8.0 <0.9.0;

// Importing a library
import "github.com/OpenZeppelin/openzeppelin-contracts@4.5.0/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("My Token", "MYT") {
        // Code for token creation here
    }
    // Other contract functions here
}

In this example, we use the >= and < operators to specify that our contract can be compiled with Solidity versions greater than or equal to 0.8.0 but less than 0.9.0. We also import a library called "ERC20.sol" from the OpenZeppelin repository and use it to create a new token.

Minor Version Only

Source files should be annotated with a version pragma to ensure that the contract is compiled with the correct version of the compiler. The version pragma specifies the minimum version of the compiler that the contract can be compiled with.

The caret (^) symbol specifies a range that includes the specified version and any backwards-compatible updates, including minor and patch-level updates. For example, ^0.8.1 would match 0.8.1, 0.8.2, 0.9.0, 0.9.1, and so on, but would not match 0.7.9 or 1.0.0.

For example, pragma solidity ^0.5.2; specifies that the contract can be compiled with Solidity version 0.5.2 or any later version that does not introduce breaking changes. The ^ character specifies that any breaking changes will only be introduced in a new major version of the compiler.

Here's an example of a Solidity smart contract using pragma to specify the minimum compiler version:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyContract {
  // Code here
}

In this example, the ^ character specifies that the contract can be compiled with Solidity version 0.8.x or any later version that does not introduce breaking changes. If any breaking changes are introduced in a new major version of the compiler, the contract will need to be updated to maintain compatibility.

Patch Version only

The tilde (~) symbol specifies a range that includes the specified version and any patch-level updates. For example, ~0.8.1 would match 0.8.1, 0.8.2, 0.8.3, and so on, but would not match 0.9.0 or 0.8.0.

Wildcard Patch Version

It is also possible to specify more complex rules for the compiler version, such as specifying version ranges or using the wildcard character (*). These follow the same syntax used by npm.

Here's an example in Solidity using the wildcard character *:

pragma solidity 0.8.*;

// This contract can be compiled with any patch version of Solidity 0.8.x
// It will not compile with any version of Solidity before 0.8.0 or after 0.9.0

contract MyContract {
    // code here
}

In this example, the contract can be compiled with any patch version of Solidity 0.8.x, but will not compile with any version of Solidity before 0.8.0 or after 0.9.0. The * character serves as a wildcard, allowing for any patch version of Solidity 0.8.x to be used.

Note that using the version pragma does not change the version of the compiler, nor does it enable or disable features of the compiler. It simply instructs the compiler to check whether its version matches the one required by the pragma. If it does not match, the compiler issues an error.

Conclusion

In conclusion, Solidity pragmas and semantic versioning notation provide a standard and clear way to manage the versioning of smart contracts. The pragma statement allows us to specify the Solidity version range that our contract can work with, ensuring that it will work as intended and avoiding any issues that could arise from using a newer or older version. Semantic versioning notation provides a clear and standardized way to indicate the extent of compatibility between software components, including Solidity compilers and smart contracts. By using the correct version of Solidity and specifying the version range that our contract is compatible with, we can take advantage of the latest features and improvements while avoiding potential errors and incompatibilities. In summary, understanding Solidity pragmas and semantic versioning is crucial for writing robust and reliable smart contracts.