There's a number of key concepts defining Ethereum smart contracts that differ from a web2 development paradigm.
In Solidity, the contract type is a struct that organizes a set of related functions around a single purpose.
Address types are Ethereum addresses that equate to 20 bytes, and are represented in hexadecimal form beginning with the prefix 0x.
Most other types will be familiar to you as a developer - including booleans, integers, fixed-point numbers, byte arrays, and literals.
In Solidity, reference data values can be stored as storage, memory or calldata depending on the role of the data stored. In particular:
- storage keeps the data permanently on the blockchain, and is extremely expensive.
- memory values are stored only for the lifetime of the smart contract's execution, and are inexpensive to use (costing only small amounts of gas).
- calldata is a special data location that contains the function arguments, and is only available for external function call parameters.
Functions exist to get / set information based on calls initiated by external transactions. In particular, a smart contract can never run unless initiated by an external transaction - they don't execute silently in the background.
Access modifiers include:
- public: Can be accessed by all functions or callers
- external: Can be accessed only by external callers, not internal functions
- internal: Can be accessed only by this contract, or contracts deriving from it
- private: Can be accessed only from this contract itself
Other modifiers include:
- view: This guarantees that the function will not modify the state of the contract's data (or data in storage).
- pure: This guarantees that the function with neither read nor modify the state of the contract's data.
- payable: Functions and addresses declared payable can receive ether into their contracts.
Special Functions and Variables
There are a number of global variables and functions that will be helpful to remember you have access to! Some special variables are:
- block.number (uint256): The number of the most recent block.
- block.timestamp (uint256): The UNIX timestamp of the most recent block.
- block.gaslimit (uint256): The gas limit of the current block.
- msg.sender (address payable): The sender of the transaction triggering the contract.
- msg.value (uint256): The number of wei transferred with the message.
Special functions include:
- receive(): Contracts may only have one of these functions declared. It serves as the default destination when a contract is sent Ether. It cannot have arguments, return anything, and must be external and payable.
- fallback(): Contracts may only have one of these functions declared. It serves as the fallback if a call to the contract does not match any function, or if no data was supplied and the receive() function was not declared. It cannot have arguments, return anything, and must be external.
Solidity events are roughly equivalent to logging in other programming paradigms. They're emitted when a contract is executed, stored permanently on the blockchain, but aren't accessible to be modified / read by smart contracts.
You can declare and emit an event like this:
Events can be accessed in a variety of different ways:
- Events are stored in the receipts of transactions, and can be accessed there.
- You can subscribe to an event with myContract.events.TestEvent([options][, callback])
- You can request past events using a request like myContract.getPastEvents(event[, options][, callback]).
This should give you a broad overview of the key components of a smart contract! You can find more detail in the Solidity Documentation linked here.