Plian
English
English
  • Overview
  • Links
  • Downloads
  • FAQ
    • Node Status
    • Full Balance
    • Flow and FAQ on Becoming a Validator
    • Delegation
    • Staking Rewards
    • Monitor
    • PIScan
    • PIWallet
    • Cross-Chain Transactions
    • DApps
  • About The Blockchain
    • Multichain Framework
    • PDBFT 2.0 Consensus Algorithm
    • Validator Nodes
    • Building with Plian
  • Using The Blockchain
    • Overview
    • Delegating or Staking PI
    • Validator Node Guides
      • Express Node Setup Guide
      • Setting Up a Validator Node
      • Registering Your Node as Validator
      • Submitting Your Vote as Validator
      • Revealing Your Vote
      • Removing Your Node From the Candidate List
      • Alternate Installation Instructions
        • Building From the Source
        • Running Pchain in Docker
        • Installation Instructions for Ubuntu
        • Installation Instructions for Mac OS X
        • Installation Instructions for Windows 10
      • What is FullBalance
    • Token Swaps
    • Plian Javascript Console
    • PIwallet
      • Desktop Wallet
        • Light Wallet Mode
        • Local Node Mode
      • Mobile Wallets
      • How to Deploy a Smart Contract
    • PMetamask
    • MetaMask
      • How to import PIwallet address to MetaMask?
      • How to connect Metamask to Plian
      • How to import wallet address from MetaMask into PIWallet
      • How to deploy smart contract on Plian via Metamask + Remix
    • Advanced Features
      • Command Line Options
      • Management APIs
      • JSON RPC API
      • How to Sync and Run Testnet
    • How to buy Plian $PI
  • For Developers
    • PWeb3 Javascript API
    • How to Create Your Own Subchain
    • Writing Smart Contracts With Solidity
      • Introduction to Smart Contracts
      • Installing the Solidity Compiler
      • Solidity by Example
      • Solidity in Depth
        • Layout of a Solidity Source File
        • Structure of a Contract
        • Types
        • Units and Globally Available Variables
        • Expressions and Control Structures
        • Contracts
        • Solidity Assembly
        • Miscellaneous
      • Security Considerations
      • Resources
      • Using the compiler
      • Contract Metadata
      • Contract ABI Specification
      • Yul
      • Style Guide
      • Common Patterns
      • List of Known Bugs
      • Contributing
      • Frequently Asked Questions
      • LLL
      • Index
Powered by GitBook
On this page
  • Layout of a Solidity Source File
  • Pragmas
  • Importing other Source Files

Was this helpful?

  1. For Developers
  2. Writing Smart Contracts With Solidity
  3. Solidity in Depth

Layout of a Solidity Source File

PreviousSolidity in DepthNextStructure of a Contract

Last updated 4 years ago

Was this helpful?

Layout of a Solidity Source File

Source files can contain an arbitrary number of , import directives and pragma directives.

Pragmas

The pragma keyword can be used to enable certain compiler features or checks. A pragma directive is always local to a source file, so you have to add the pragma to all your files if you want enable it in all of your project. If you import another file, the pragma from that file will not automatically apply to the importing file.

Version Pragma

Source files can (and should) be annotated with a so-called version pragma to reject being compiled with future compiler versions that might introduce incompatible changes. We try to keep such changes to an absolute minimum and especially introduce changes in a way that changes in semantics will also require changes in the syntax, but this is of course not always possible. Because of that, it is always a good idea to read through the changelog at least for releases that contain breaking changes, those releases will always have versions of the form 0.x.0 or x.0.0.

The version pragma is used as follows:

Such a source file will not compile with a compiler earlier than version 0.4.0 and it will also not work on a compiler starting from version 0.5.0 (this second condition is added by using ^). The idea behind this is that there will be no breaking changes until version 0.5.0, so we can always be sure that our code will compile the way we intended it to. We do not fix the exact version of the compiler, so that bugfix releases are still possible.

It is possible to specify much more complex rules for the compiler version, the expression follows those used by .

Using the version pragma will not change the version of the compiler. It will also not enable or disable features of the compiler. It will just instruct the compiler to check whether its version matches the one required by the pragma. If it does not match, the compiler will issue an error.

Experimental Pragma

The second pragma is the experimental pragma. It can be used to enable features of the compiler or language that are not yet enabled by default. The following experimental pragmas are currently supported:

ABIEncoderV2

The new ABI encoder is able to encode and decode arbitrarily nested arrays and structs. It produces less optimal code (the optimizer for this part of the code is still under development) and has not received as much testing as the old encoder. You can activate it using pragma experimental ABIEncoderV2;.

SMTChecker

If you use pragma experimental SMTChecker;, then you get additional safety warnings which are obtained by querying an SMT solver. The component does not yet support all features of the Solidity language and likely outputs many warnings. In case it reports unsupported features, the analysis may not be fully sound.

Importing other Source Files

Syntax and Semantics

Solidity supports import statements that are very similar to those available in JavaScript (from ES6 on), although Solidity does not know the concept of a “default export”.

At a global level, you can use import statements of the following form:

This statement imports all global symbols from “filename” (and symbols imported there) into the current global scope (different than in ES6 but backwards-compatible for Solidity). This simple form is not recommended for use, because it pollutes the namespace in an unpredictable way: If you add new top-level items inside “filename”, they will automatically appear in all files that import like this from “filename”. It is better to import specific symbols explicitly.

The following example creates a new global symbol symbolName whose members are all the global symbols from "filename".

import * as symbolName from "filename";

If there is a naming collision, you can also rename symbols while importing. This code creates new global symbols alias and symbol2 which reference symbol1 and symbol2 from inside "filename", respectively.

import {symbol1 as alias, symbol2} from "filename";

Another syntax is not part of ES6, but probably convenient:

import "filename" as symbolName;

which is equivalent to import * as symbolName from "filename";.

If you use import “filename.sol” as moduleName;, you access a contract called C from inside “filename.sol” as moduleName.C and not by using C directly.

Paths

In the above, filename is always treated as a path with / as directory separator, . as the current and .. as the parent directory. When . or .. is followed by a character except /, it is not considered as the current or the parent directory. All path names are treated as absolute paths unless they start with the current . or the parent directory ...

To import a file x from the same directory as the current file, use import "./x" as x;. If you use import "x" as x; instead, a different file could be referenced (in a global “include directory”).

It depends on the compiler (see below) how to actually resolve the paths. In general, the directory hierarchy does not need to strictly map onto your local filesystem, it can also map to resources discovered via e.g. ipfs, http or git.

Always use relative imports like import "./filename.sol"; and avoid using .. in path specifiers. In the latter case, it is probably better to use global paths and set up remappings as explained below.

Use in Actual Compilers

When invoking the compiler, you can specify how to discover the first element of a path, and also path prefix remappings. For example you can setup a remapping so that everything imported from the virtual directory github.com/ethereum/dapp-bin/library would actually be read from your local directory /usr/local/dapp-bin/library. If multiple remappings apply, the one with the longest key is tried first. An empty prefix is not allowed. The remappings can depend on a context, which allows you to configure packages to import e.g., different versions of a library of the same name.

solc:

For solc (the commandline compiler), you provide these path remappings as context:prefix=target arguments, where both the context: and the =target parts are optional (target defaults to prefix in this case). All remapping values that are regular files are compiled (including their dependencies).

This mechanism is backwards-compatible (as long as no filename contains = or :) and thus not a breaking change. All files in or below the context directory that import a file that starts with prefix are redirected by replacing prefix by target.

For example, if you clone github.com/ethereum/dapp-bin/ locally to /usr/local/dapp-bin, you can use the following in your source file:

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;

Then run the compiler:

solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

As a more complex example, suppose you rely on a module that uses an old version of dapp-bin that you checked out to /usr/local/dapp-bin_old, then you can run:

solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
     module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
     source.sol

This means that all imports in module2 point to the old version but imports in module1 point to the new version.

solc only allows you to include files from certain directories. They have to be in the directory (or subdirectory) of one of the explicitly specified source files or in the directory (or subdirectory) of a remapping target. If you want to allow direct absolute includes, add the remapping /=/.

If there are multiple remappings that lead to a valid file, the remapping with the longest common prefix is chosen.

Remix:

Remix may add other source code providers in the future.

This component has to be enabled when the Solidity compiler is built and therefore it is not available in all Solidity binaries. The explain how to activate this option. It is activated for the Ubuntu PPA releases in most versions, but not for solc-js, the Docker images, Windows binaries or the statically-built Linux binaries.

provides an automatic remapping for GitHub and automatically retrieves the file over the network. You can import the iterable mapping as above, e.g.::import “github.com/ethereum/dapp-bin/library/iterable_mapping.sol” as it_mapping;

contract definitions
npm
build instructions
Remix