Mona Alfonso

Mona Alfonso

Web Developer and Educator with 5+ years of experience in tech.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.26; 
 
contract MyContract {
    uint public myStateVariable;
 
    constructor() {
        myStateVariable = 0;
    }
 
    function myFunction(uint _newValue) public {
        myStateVariable = _newValue;
    }
 
    event ValueChanged(uint newValue);
 
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
}

1. License

// SPDX-License-Identifier: GPL-3.0 is a special comment that appears at the very beginning of a Solidity source file. It's not part of the Solidity code itself, but it serves an important purpose in terms of licensing and legal considerations. Let's break it down:

  1. SPDX: This stands for "Software Package Data Exchange". It's a standard format for communicating software license information.
  2. License-Identifier: This indicates that what follows is an identifier for the license under which the code is released.
  3. GPL-3.0: This is the actual license identifier. In this case, it refers to version 3.0 of the GNU General Public License.

The purpose of this comment is to clearly and unambiguously declare the license under which the smart contract (or Solidity code) is released. This is particularly important in the world of open-source software and blockchain, where code is often shared and reused.

Some key points about the SPDX-License-Identifier:

  • It's recommended by the Solidity compiler to include this identifier in all source files.
  • If you don't want to specify a license, you can use UNLICENSED instead. The compiler will issue a warning if this identifier is missing.
  • It doesn't affect the functionality of your code; it's purely for legal and informational purposes.
  • Other common licenses you might see include MIT, Apache-2.0, or BSD-2-Clause.
  1. Pragma

pragma solidity 0.8.26;is used to enable certain compiler features or checks. In Solidity, it's primarily used to specify the version of the compiler. solidity: This indicates that the pragma statement is specifically for the Solidity compiler.

0.8.26: This is the version number of the Solidity compiler that should be used for this contract.

  • 0 is the major version
  • 8 is the minor version
  • 26 is the patch version

Key points about this pragma statement:

  1. Version Locking: By specifying a precise version, you're ensuring that the contract will only compile with this exact version of Solidity. This helps prevent issues that might arise from different compiler versions interpreting the code differently.
  2. Compatibility: It tells other developers (or yourself in the future) which version of Solidity this contract was written for.
  3. Features and Bug Fixes: Different versions of Solidity may have different features or bug fixes. By specifying the version, you're explicitly choosing which set of features and fixes you want to work with.
  4. Security: Using a recent, stable version of Solidity is generally recommended for security reasons, as newer versions often include important bug fixes and improvements.
  5. Flexibility: You can also specify version ranges instead of a single version. For example:
  • pragma solidity ^0.8.0; (any version from 0.8.0 up to, but not including, 0.9.0)
  • pragma solidity >=0.8.0 <0.9.0; (any version from 0.8.0 up to, but not including, 0.9.0)

Compiler Behavior: The compiler will throw an error if you try to compile the contract with a version that doesn't match the pragma statement.

3. Contract Declaration

contract MyContact {
 ...
}

A contract is a fundamental building block and a core concept in Solidity. It's similar to a class in object-oriented programming languages. Here's a detailed explanation of what a contract is in Solidity:

  1. Definition: A contract in Solidity is a collection of code (its functions) and data (its state) that resides at a specific address on the Ethereum blockchain.
  2. Structure: Contracts typically contain:
  • State Variables: Data stored permanently in contract storage.
  • Functions: Executable units of code.
  • Function Modifiers: Used to change the behavior of functions.
  • Events: Used to emit logs on the blockchain.
  • Struct Types: Custom defined types.
  • Enum Types: Custom types with a finite set of values.
  1. Key Characteristics:
  • Immutability: Once deployed, a contract's code cannot be changed.
  • Autonomous: Contracts can execute automatically when specific conditions are met.
  • Interactivity: Contracts can interact with other contracts.
  1. Lifecycle:
  • Creation: Contracts are created by transactions and get a unique address.
  • Execution: Functions in contracts are executed when called via transactions.
  • Destruction: Contracts can be destroyed using the selfdestruct function.
  1. Example Structure:
contract MyContract {
 
    uint public myStateVariable;
 
    constructor() {
        myStateVariable = 0;
    }
 
    function myFunction(uint _newValue) public {
        myStateVariable = _newValue;
    }
 
    event ValueChanged(uint newValue);
 
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
}
  1. Use Cases:
  • Tokens (e.g., ERC-20, ERC-721)
  • Decentralized applications (DApps)
  • Decentralized Autonomous Organizations (DAOs)
  • Smart contract-based games
  • Decentralized Finance (DeFi) protocols
  1. Importance: Contracts are the core of Ethereum and other smart contract platforms, enabling programmable, trustless interactions and complex decentralized systems.

  2. Considerations:

  • Security: Contracts handle real value, so security is paramount.
  • Gas Costs: Each operation in a contract costs gas, which translates to real money.
  • Limitations: Contracts have limitations in terms of computation and storage due to blockchain constraints.

4. State variables

State variables are variables that are permanently stored in contract storage. They represent the state or data of a contract that persists between function calls.

In the example uint public myStateVariable;:

  • uint: This is the data type of the variable. It stands for "unsigned integer," which means a positive whole number.

  • public: This is the visibility specifier (we'll explain this in more detail below).

  • myStateVariable: This is the name of the state variable.

Visibility in Solidity: Visibility determines how a state variable or function can be accessed. There are four types of visibility in Solidity:

  1. public:
  • Can be accessed internally and externally (i.e., from within the contract and from other contracts or accounts).
  • For state variables, Solidity automatically creates a getter function.
  • Example: uint public myStateVariable;
  1. private:
  • Can only be accessed from within the current contract.
  • Not accessible from derived contracts or externally.
  • Example: uint private myPrivateVariable;
  1. internal:
  • Can be accessed only from within the current contract and contracts deriving from it.
  • This is the default visibility for state variables if none is specified.
  • Example: uint internal myInternalVariable; external:

Can only be called from other contracts and via transactions. Not applicable to state variables, only to functions. Example: function externalFunction() external { ... }

Important points about visibility:

  1. For state variables, you can use public, private, or internal.

  2. Making a variable public automatically creates a getter function, allowing other contracts to read (but not write to) the variable.

  3. Even if a variable is marked as private, it's still visible on the blockchain. "Private" only means other contracts can't access it directly.

  4. Proper use of visibility is crucial for contract security and gas optimization.

  5. It's a good practice to explicitly declare the visibility of state variables and functions to make the code more readable and to avoid potential security issues.

Example:

contract VisibilityExample {
    uint public publicVar;     // Accessible from anywhere, getter created
    uint private privateVar;   // Only accessible within this contract
    uint internal internalVar; // Accessible within this contract and derived contracts
 
    function setPrivateVar(uint _value) public {
        privateVar = _value;   // Can access privateVar within the contract
    }
}

In this example, publicVar can be read from outside the contract, privateVar can only be accessed within the contract's functions, and internalVar can be accessed within this contract and any contracts that inherit from it.

Constructors

 constructor() {
        myStateVariable = 0;
    }

A constructor in Solidity is a special function that is executed only once when a contract is deployed to the blockchain. It's used to initialize state variables and perform any setup the contract needs.

Key points about constructors in Solidity:

  1. Purpose:
  • To initialize state variables
  • To set up the initial state of the contract
  • To perform any one-time setup operations
  1. Naming:
  • The function is explicitly named constructor
  • In older versions of Solidity (pre-0.4.22), the constructor was a function with the same name as the contract
  1. Execution:
  • It's automatically executed once during contract deployment
  • It cannot be called after the contract is deployed
  1. Parameters:
  • Constructors can take parameters, which are provided when deploying the contract
  • In this example, the constructor takes no parameters: constructor()
  1. Visibility:
  • Constructors can be public or internal
  • If no visibility is specified, it's public by default
  1. Return Values:
  • Constructors cannot return values
  • They can't be view or pure
  1. In the example:
constructor() {
    myStateVariable = 0;
}
  • This constructor initializes myStateVariable to 0 when the contract is deployed
  • It doesn't take any parameters
  • It's implicitly public
  1. Example with parameters:
constructor(uint initialValue) {
    myStateVariable = initialValue;
}

This version would allow you to set the initial value of myStateVariable when deploying the contract.

  1. Use cases:
  • Setting an owner address
  • Initializing important state variables
  • Setting up relationships with other contracts
  1. Important considerations:
  • Any ETH sent during contract creation can be accessed in the constructor
  • The constructor has access to the special variable msg.sender, which is the address deploying the contract

Functions

    function myFunction(uint _newValue) public {
        myStateVariable = _newValue;
    }
  1. Function Declaration:
  • function: Keyword to declare a function in Solidity.
  • myFunction: The name of the function.
  1. Parameters:
  • (uint _newValue): This function takes one parameter.
  • uint: The data type of the parameter (unsigned integer).
  • _newValue: The name of the parameter. The underscore prefix is a common convention for function parameters.
  1. Visibility Specifier:
  • public: This function can be called both internally (within the contract) and externally (from other contracts or accounts). Other visibility options include private, internal, and external.
  1. Function Body:
  • Enclosed in curly braces { }.
  • Contains the code to be executed when the function is called.
  1. State Change:
  • myStateVariable = _newValue;: This line updates the state variable myStateVariable with the value passed to the function.

Key Points about Functions in Solidity

  1. State-Changing vs. View/Pure:
  • This function changes the contract's state by modifying myStateVariable.
  • Functions that don't modify state can be declared as view or pure.
  1. Return Values:
  • This function doesn't return a value. If it did, it would be specified after the visibility specifier, e.g., public returns (uint).
  1. Gas Consumption:
  • State-changing functions like this consume gas when called in a transaction.
  1. Function Overloading:
  • Solidity supports function overloading (multiple functions with the same name but different parameters).
  1. Modifiers:

Functions can have modifiers to add pre-conditions, e.g., function myFunction(uint _newValue) public onlyOwner { ... }.

  1. Payable Functions:
  • Functions that can receive Ether should be marked as payable.
  1. Error Handling:
  • Functions can use require(), assert(), or revert() for error checking and handling.
  1. Events:
  • Functions often emit events to log important changes.

Example with More Features

event ValueChanged(uint newValue);

function myFunction(uint _newValue) public returns (bool) { require(_newValue != 0, "Value cannot be zero"); myStateVariable = _newValue; emit ValueChanged(_newValue); return true; }

This expanded example includes error checking, event emission, and a return value, demonstrating more advanced function features in Solidity.

Events

  • Coming Soon

Modifiers

modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
  1. Modifiers are used to:
  • Add pre-conditions to functions
  • Control access to functions
  • Prevent certain types of attacks
  • Reduce code duplication
  1. Components of a Modifier
  • Keyword: modifier is used to declare a modifier.

  • Name: onlyOwner is the name of this modifier.

  • Parameters: This modifier doesn't take any parameters, but modifiers can have parameters if needed.

  • Body: The code inside the curly braces {}.

  • Placeholder: The underscore _ is a special character in modifiers.

  1. How This Modifier Works

require(msg.sender == owner, "Not the owner");

  • This line checks if the caller (msg.sender) is the owner of the contract.
  • If not, it reverts the transaction with the error message "Not the owner".
  1. Placeholder _:
  • The _ indicates where the body of the function using this modifier should be inserted.
  • If the require condition passes, the function's code will be executed at this point.
  1. Using the Modifier
  • You would use this modifier on a function like this:
function sensitiveOperation() public onlyOwner {
    // Function body
}

When sensitiveOperation is called:

The code in the onlyOwner modifier runs first. If the caller is the owner, the function body executes. If not, the transaction reverts.

Key Points About Modifiers

  • Multiple Modifiers: A function can have multiple modifiers.
  • Order Matters: If multiple modifiers are used, they are evaluated in the order specified.
  • Reusability: Modifiers can be reused across multiple functions.
  • Gas Efficiency: Modifiers can help make code more gas-efficient by reducing duplication.
  • Inheritance: Modifiers can be inherited and overridden in derived contracts.

Modifiers are a crucial tool for implementing access control and ensuring certain conditions are met before a function executes, enhancing the security and efficiency of smart contracts.