Dry goods | read the solidity programming

Source | Solidity Programming

Author | Ritesh Modi

Editor | George

Produced | Blockchain Base Camp (blockchain_camp)

The concept of Solidity's grammar was first proposed by Gavin Wood in 2014, and later by the Ethereum team Solidity led by Christian Reitwiessner. The language is one of five languages ​​designed for the Ethereum Virtual Machine (EVM) (in addition to Solidity, it also includes Serpent, LLL, Vyper (in experiment) and Mutan (deprecated)), which is currently the most popular on EVM. language.

If you don't understand the Solidity language, you can't talk about how to "play the Ethereum."

This article will focus on Solidity and its concepts, and how to write efficient smart contracts. Don't be too long, it's dry goods!

Ethereum virtual machine

Solidity is the programming language for the Ethereum Virtual Machine (EVM). The Ethereum blockchain helps to extend its capabilities by writing and executing code called smart contracts.

The EVM executes the code as part of the smart contract. Smart contracts are written in the Solidity language. However, EVM does not understand the advanced structure of Solidity. EVM can understand low-level instructions called bytecode.

The compiler is required to convert the Solidity code to a bytecode that the EVM understands. The compiler that comes with Solidity is called the Solidity compiler or solc, which is responsible for doing this conversion.

From writing code in Solidity to executing code in EVM, the whole process is shown below:

Solidity and Solidity files

Solidity is a programming language that is very close to JavaScript. The similarities between JavaScript and C can be found in Solidity. Solidity is a statically type, case-sensitive object-oriented programming (OOP) language. Although it is object oriented, it supports limited object-oriented features. This means that at compile time, the data type of the variable should be defined and known. Functions and variables should be defined in a way that is object-oriented. In Solidity, Cat is different from CAT, cat, or any other variant of cat. The terminator of a Solidity statement is a semicolon (;).

Write the Solidity code in a Solidity file with a .sol extension. Solidity files are human-readable text files that can be opened in any editor, including Notepad.

The Solidity file consists of the following four advanced structures:

  • Precompiled instruction
  • Comment
  • Import
  • Contract/library/interface

1, precompiled instructions

The precompiled directive is usually the first line of code in any Solidity file. Pragma is an instruction that specifies the current Solidity file compiler version.

Solidity is a new language and is constantly improving. Whenever new features or improvements are introduced, a new version will be released.

With the help of the pragma directive, you can choose the appropriate compiler version for your code, as shown in the following code:

Pragma Solidity ^0.4.19;

Although not mandatory, it is a good practice to use the pragma as the first statement in the Solidity file.

The syntax of the pragma instruction is as follows:

Pragma Solidity <<version number>>;

Note that the instructions are case-sensitive, and the subsequent valid version numbers and statements end with a semicolon.

The version number consists of two numbers – the major version number and the minor version number.

The major version number in the above example is 4, and the minor version number is 19. Typically, there are few or no major changes in the minor version, but there may be significant changes between major versions. You should choose the version that best meets your needs.

Characters, also known as caret characters, are optional in the version number, but play an important role in determining the version number according to the following rules:

  • The ^ character is the latest version in the main version. Therefore, ^0.4.0 refers to the latest version with version number 4, currently 0.4.19.
  • The ^ character is not specific to any other major version other than the major version number provided.
  • Solidity files can only be compiled with a compiler with a major version number of 4, and cannot be compiled with compilers of other major version numbers.

It is a better practice to use a precise compiler version instead of using ^ to specify to compile the Solidity code. When using the ^ character in a pragma, if your new version of the compiler changes, your code may be deprecated. For example, the throw statement has been deprecated and it is recommended to use newer structures such as assert, require, and revert in newer versions. You don't want to be surprised to find out that your code behaves differently than before.

2, notes

The functionality of annotation code is provided in any programming language, as is Solidity. Solidity has the following three ways to comment:

  • Single line comment
  • Multi-line comment
  • Ethereum Natural Specification (Natspec)

Single-line comments are represented by double slashes, and multi-line comments are represented by /* and */. Natspec comes in two formats: // for single lines, /** and */ for the beginning and end of multiline comments, respectively. Natspec is used for files and it has its own specifications.

The entire specification is available at https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format.

Let's take a look at the comments for Solidity with the following code:

// This is a one-line comment in Solidity

/* This is a multi-line comment in Solidity.

Multiple consecutive lines as a whole to comment */

In Remix, the pragma instructions and comments are shown below:

3, import statement

The import keyword helps us import other Solidity files to access their code in the current Solidity files and code. This helps us write modular Solidity code. The syntax for using import is as follows:

Import<<filename>>;

The file name can be a fully explicit or implicit path. The slash "/" is used to separate the directory from other directories and files, "." is used to reference the current directory, and ".." is used to reference the parent directory. This is very similar to how Linux bash references files. This shows a typical import statement. Also, note the semicolon at the end of the following code:

Import 'CommonLibrary.sol';

4, the contract

In addition to pragmas, import, and comments, we can also define global or top-level contracts, libraries, and interfaces. We'll delve into contracts, libraries, and interfaces in the following chapters. This chapter assumes that you know that you can declare multiple contracts, libraries, and interfaces in the same Solidity file. The library, contract, and interface keywords shown in the following figure are case sensitive:

Contract structure

The main purpose of Solidity is to write smart contracts for Ethereum. Smart contracts are the basic unit of EVM deployment and execution. Although several chapters later in this book are devoted to writing and developing smart contracts, this chapter will discuss the basic structure of smart contracts.

Technically, a smart contract consists of two structures—variables and functions. There are several aspects to variables and functions, and this is what this book will discuss. This section focuses on the general structure of smart contracts using the Solidity language.

The contract consists of the following structures:

  • State variables
  • Structure definition
  • Modifier definition
  • Event statement
  • Enumeration definition
  • Function definition

A typical contract consists of all the structures mentioned above. Note that in the figure below, each of these structures consists of several other structures:

1, state variables

A variable in programming is a storage unit that can contain a value. These values ​​can be changed at runtime. Variables can be used in multiple places in the code, and they all reference the stored values. Solidity provides two types of variables – state variables and memory variables.

State variables are one of the most important features of a Solidity contract. State variables are permanently stored by miners in the blockchain/Ethernet ledger. In contracts, variables that are not declared within any function are called state variables. The state variable stores the current value of the contract. The memory of a state variable is statically allocated and cannot be changed (the amount of memory allocated) during the life of the contract. The type of each state variable must be statically defined. The Solidity compiler must determine the memory allocation details for each state variable, so the data type of the state variable must be declared.

State variables also have associated qualifiers, which can be any of the following:

Internal : By default, if no content is specified, the state variable has an internal qualifier. This means that this variable can only be used in the current contract function and any contract that inherits them. These variables cannot be modified by external access, but you can view them.

An example of an internal state variable is as follows: int internal StateVariable;

Private : This qualifier is like an additional constraint on the internal. Private state variables can only be used in contracts that declare them. They cannot be used even in a derivative contract.

An example of a private state variable is as follows: int private private StateVariable;

Public : This qualifier allows state variables to be accessed directly. The Solidity compiler generates a getter function for each public state variable.

An example of a public state variable is as follows: int public stateInt Variable;

Constant : This qualifier makes the state variable immutable. The initial value must be assigned when the variable is declared. In fact, the compiler replaces the reference to the variable with the specified value in all code.

An example of a constant state variable is as follows: bool constant hasIncome = true;

As mentioned earlier, each state variable has an associated data type. The data type helps us determine the memory requirements of the variable and determine the values ​​that can be stored in it. For example, a state variable of type uint8 is called an unsigned integer and will be assigned a predetermined memory size, with values ​​ranging from 0 to 255. Any other value is considered external, and the compiler and runtime do not accept storing it in variables.

Solidity offers the following multiple out-of-the-box data types:

  • Bool
  • Uint/int
  • Bytes
  • Address
  • Mapping
  • Enum
  • Struct
  • Bytes/String

With enum and struct, you can declare custom user data types.

2, structure

Structures or structures help to implement custom user data types. A structure is a composite data type that consists of variables of several different data types. They are very similar to contracts, but they don't contain any code. They only contain variables.

Sometimes you want to store related data together. Suppose you want to store information about employees, such as employee name, age, marital status, and bank account number. To represent these variables related to a single employee, you can declare them using the struct keyword in the structure of Solidity. The variables in the structure are defined in curly braces {} as shown:

Use the following syntax to create an instance of a structure. There is no need to explicitly call the keyword new. The keyword new is only used to create an instance of a contract or array, as shown:

You can create instances of multiple structures in a function. Structures can contain arrays and map variables, and maps and arrays can store values ​​for structures.

3, modifier

In Solidity, modifiers are always associated with functions. A modifier in a programming language is a structure that changes the behavior of executing code. Because a modifier is associated with a function in Solidity, it can change the behavior of its associated function. To make it easier to understand the modifier, you can think of it as a function that is executed before the target function is executed. Suppose you want to call the getAge function, but before executing it, you want to execute another function to check the current state of the contract, the value in the passed argument, the current value in the state variable, etc., and decide accordingly whether the target should be executed. The function getAge. This helps to write cleaner functions without confusing them with validation and validation rules. In addition, modifiers can be associated with multiple functions. This ensures clearer, more readable, and more maintainable code.

The modifier uses the modifier keyword followed by the modifier identifier, any parameters it should accept, and the code written in curly braces {}. The "_" (underscore) in the modifier indicates the execution of the objective function. You can think of this as an underscore that is replaced by an inlined objective function. Payable is an out-of-the-box modifier provided by Solidity that allows the function to accept Ethercoin when applied to any function.

The modifier keyword is declared at the contract level, as shown in the following figure:

A modifier named onlyBy() is declared at the contract level. It uses msg.sender to check the value of the incoming address, and msg.sender saves the address and stores it in the state variable.

The modifier is associated with the getAge function as shown in the following image:

The getAge function can only be executed by the same account as the address stored in the contract's _personIdentifier state variable. If any other account attempts to call the function, the function will not execute.

It's important to note that anyone can call the getAge function, but execution will only happen in a single account.

4. Events

Solidity supports events. Events in Solidity are just like events in other programming languages. Events are triggered from contracts, and anyone interested in them can capture them and respond to the execution code. Events in Solidity are primarily used to notify the calling application of the current state of the contract through the EVM's logging tool. They are used to notify the application about changes in the contract, and the application can use them to execute the relevant logic. Instead of the application, they poll for changes to specific states in the contract, and the contract can notify them via events.

The events declared in the contract are valid in the global domain and are called by functions in the contract. Use the event keyword to declare an event followed by an identifier and a list of arguments with a semicolon. The values ​​in the parameters can be used to log information or execute conditional logic. The event information and its value are stored in the block as part of the transaction. When discussing the properties of a transaction in the previous chapter, a property called LogsBloom was introduced. Events raised as part of a transaction are stored in this property.

There is no need to explicitly provide parameter variables – only the data type is sufficient, as shown in the following figure:

The name of the event can be called from any function and passed the required parameters, as shown in the following image:


5, enumeration

The enum keyword is used to declare an enumeration. Enumerations are used to declare user-defined data types in Solidity. An enumeration contains an enumerated list, a set of predefined constants.

In Solidity, constant values ​​in an enumeration can be explicitly converted to integers. Each constant value corresponds to an integer value, the first value is 0, and the value of each successive item is increased by 1.

Use the enum keyword to declare an enumeration followed by a list of enumerated values ​​in the enumeration identifier and curly braces {}. It's worth noting that enumeration declarations do not have a semicolon as a terminator, and at least one member should be declared in the list.

An example of an enumeration is as follows:

Enum gender {male, female}

You can declare and assign an enumeration variable, as shown in the following code:

Gender_gender = gender.male;

There is no mandatory requirement to define an enumeration in a Solidity contract. If you have a constant list of project constants as shown in the previous example, you should define an enumeration. This is a good example of declaring an enumeration. They make your code more readable and maintainable.

6, the function

The core of the function is Ethereum and Solidity. Ethereum maintains the current state of the state variable and executes the transaction to change the value in the state variable. When a function in a contract is called or triggered, it causes a transaction to be created. The function mechanism is to read values ​​from state variables and write values ​​to state variables. A function is a unit of code that is called on demand. A function can accept a parameter, execute its logic, and optionally return the value to the caller. The function can be named anonymously. Solidity provides a named function, and there can only be one unnamed function called the fallback function in the contract.

The syntax for declaring a function in Solidity is as follows:

Use the function keyword followed by its identifier getAge to declare the function. It can accept multiple comma-separated parameters. The parameter identifier is optional, but the data type should be provided in the parameter list. Functions can attach modifiers, such as onlyBy().

There are a few extra qualifiers that affect the behavior and operation of the function. A function has a visibility qualifier and a qualifier associated with the operations that can be performed in the function. The following discusses visibility and keywords related to functional capabilities. Functions can also return data and use the return keyword to declare this information, and then return a list of parameters. Solidity can return multiple parameters.

A function has a visibility qualifier similar to a state variable . The visibility of a function can be any of the following:

  • Public : This visibility allows functions to be accessed directly from outside. They become part of the contract interface and can be called internally and externally.
  • Internal : By default, state variables have an internal qualifier if not specified. This means that this function can only be used with the current contract and any contracts inherited from it. These functions are not accessible from outside, they are not part of the contract interface.
  • Private : Private functions can only be used in contracts that declare them, even if they are in a derived contract. They are not part of the contract interface.
  • External : This visibility allows functions to be accessed directly from outside but not internally. These functions are part of the contract interface.

Functions can also have the following additional qualifiers that can change contract state variables:

  • Constant : These functions do not have the ability to modify the state of the blockchain. They can read state variables and return them to the caller, but they can't modify any variables, trigger events, create another contract, call other functions that can change state, and so on. Think of a constant function as a function that can read and return the value of the current state variable.
  • View : These functions are aliases for constant functions.
  • The pure :pure function further limits the capabilities of the function. Pure functions can neither be read nor written, ie they cannot access state variables. Functions declared with this qualifier should ensure that they do not access the current state and transaction variables.
  • Payable : A function declared with the payable keyword can accept Ether from the caller. If the sender does not provide Ethereum, the call will fail. If a function is marked as payable, the function can only accept Ether.

Data type in Solidity

Solidity data types can be roughly divided into the following two types:

  • Value type
  • Reference type

These two types differ in how variables are assigned and stored in the EVM. The assignment of a variable can be done by creating a new copy or simply by processing the reference. A value type maintains a separate copy of a variable, and changing a value in one variable does not affect the value in the other. However, changing the value in a reference type variable ensures that any place that references the variable gets the updated value.

1, value type

If a type saves data (values) directly in memory, the type is said to be a value type. Value types hold values ​​in their own storage space. The figure below illustrates this. In this example, the variable declaration 13 whose data type is an unsigned integer (uint) is its data (value). The variable a has a storage space 0x123 allocated by the EVM, and the location has a stored value of 13. Accessing this variable will directly get the value 13:

The value type is a type that does not exceed 32 bytes of memory. Solidity provides the following value types:

  • Bool : a boolean value that can hold true or false as its value
  • Uint : This is an unsigned integer and can only hold 0 and positive values.
  • Int : This is a signed integer that can hold negative and positive values
  • Address : This indicates the account address in the Ethereum environment.
  • Byte : This represents a fixed-size byte array (byte1 to bytes32)
  • Enum : an enumeration that can hold predefined constant values

Value transfer

If you assign a value type variable to another variable or pass a value type variable as a parameter to a function, EVM creates a new variable instance and copies the value of the original value type into the target variable. This is called value passing. Changing the value in the original or target variable does not affect the value in the other variable. These two variables will maintain their independent values, and they can change values ​​without other variables knowing.

2, the reference type

Unlike value types, reference types do not store their values ​​directly in the variables themselves. They store not the value, but the address where the value is stored. This variable holds a pointer to another memory location where the data is actually stored. Reference types can take up more than 32 bytes of memory. The reference type is shown below by illustration.

In the following example, an array variable of size 6 with a data type of uint is declared. Arrays in Solidity are counted from 0, so this array can contain 7 elements. The variable a is allocated the storage space 0x123 by the EVM, which holds the pointer value 0x456. This pointer points to the actual memory location where the array data is stored. When the variable is accessed, the EVM will reference the value of the pointer and display the value in the array index, as shown in the following image:

Solidity provides the following reference types:

  • Array : This is an array of fixed or dynamic sizes.
  • Structure : This is a custom, user-defined structure.
  • String : This is a sequence of characters. In Solidity, strings are eventually stored as bytes.
  • Mapping : Similar to a hash table or dictionary in other languages ​​that store key-value pairs.

Reference pass

When a reference type variable is assigned to another variable, or when a reference type variable is passed as a parameter to a function, EVM creates a new variable instance and copies the pointer from the original variable to the target variable. This is called reference passing. Both variables point to the same address location. Changing the value in the original or target variable also changes the value of the other variable. These two variables will share the same value, and the change of one variable is reflected in the other.

Storage and memory data location

Each variable declared and used in the contract has a data location. EVM provides the following four data structures for storing variables:

  • Storage : This is a global memory variable that can be accessed by all functions in the contract. Ethereum stores it as permanent storage on each node in the environment.
  • Memory : Local memory that is accessible to every function in the contract. It is a short-lived memory of the life cycle that is destroyed when the function is executed.
  • Call data : Stores all incoming function execution data, including function parameters. This is an unmodifiable memory location.
  • Stack : The EVM maintains a stack of variables and intermediate values ​​used to load variables and use the Ethereum instruction set. This is the working set memory of the EVM. In EVM, the depth of the stack is 1024 layers, and any depth beyond this number will throw an exception.

The data location of a variable depends on two factors:

  • The location of the variable declaration
  • Variable data type

Based on the above two factors, there are rules that dictate how to manage and determine the data location of variables. The data position also affects how the assignment operator works. The management of assignments and data locations is explained by rules.

1, rule 1

State variables are always stored in the storage data location.

2, rule 2

Variables declared as function arguments are always stored in the memory data location.

3. Rule 3

By default, variables declared in a function are stored in the memory data location. However, there are the following warnings:

  • The value of the value type variable in the function is stored in memory, and the default location of the reference type variable is stored. Note that the reference type variable declared within the function is stored in the store by default. However, it can be overwritten.
  • By overriding the default location, the reference type variable can be located in the in-memory data location. The types referenced are arrays, structs, and strings.
  • The reference type declared in the function will not be overwritten and should always point to a state variable.
  • Value type variables declared in a function cannot be overwritten and cannot be stored in a storage location.
  • Mappings are always declared at the storage location, which means that they cannot be declared within a function. Mappings cannot be declared as memory types. However, a map in a function can reference a map declared as a state variable.

4, rule 4

The function parameters provided by the caller are always stored in the calling data location.

5, rule 5

A state variable, assigned by another state variable, creates a new copy. Two value type state variables stateVar1 and stateVar2 are declared. In the getUInt function, stateVar2 is assigned to stateVar1. At this stage, the values ​​of both variables are 40. The next line of code changes the value of stateVar2 to 50 and returns stateVar1. The return value is 40, indicating that each variable maintains its own independent value, as shown in the following figure:

The state variables stateArray1 and stateArray2 of two array types are declared. In the getUInt function, stateArray2 is assigned to stateArray1. At this stage, the values ​​of the two variables are the same. The next line of code changes a value in stateArray2 to 5 and returns the value of the same position in the stateArray1 array. The return value is 4, indicating that each variable maintains its own independent value, as shown in the following figure:

6, rule 6

A new copy is always created when the value of a memory variable is assigned to a stored variable.

Declare an unsigned number of fixed array state variables stateArray. Define and initialize a fixed array localArray in local memory in the getUInt function. The next line of code assigns localArray to stateArray. At this stage, the values ​​of the two variables are the same. The next line of code changes a value in localArray to 10 and returns the same position of the stateArray1 array. The return value is 2, indicating that each variable maintains its own independent value, as shown in the following figure:

Declare a state variable stateVar of a value type with an initialization value of 20. In the getUInt function, declare the local variable localVar with a value of 40. In the next line of code, assign the value of the local variable localVar to stateVar. At this stage, the values ​​of both variables are 40. The next line of code changes the value of localVar to 50 and returns stateVar. The return value is 40, indicating that each variable maintains its own independent value, as shown in the following figure:

7. Rule 7

A new copy is always created when the value of a state variable is assigned to a memory variable. Declare a state variable stateVar of a value type and initialize it to a value of 20. In the getUInt function, declare a local variable of type uint and initialize it to 40. Assign the value of the variable stateVar to the variable localVar. At this stage, the values ​​of both variables are 20. The next line of code changes the value of stateVar to 50 and returns localVar. The returned value is 20, indicating that each variable maintains its own independent value, as shown in the following figure:

Declare a fixed state variable array stateArray. In the getUInt function, define a fixed array localArray in local memory and initialize it with the value of the stateArray variable. At this stage, the values ​​of the two variables are the same. The next line of code changes a value in stateArray to 5 and returns the same position in the localArray1 array. The return value is 2, indicating that each variable maintains its own independent value, as shown in the following figure:

8, rule 8

Assigning a memory variable to a memory variable does not create a copy; however, a new copy is indeed created for the value type. The code listing shown in the following figure shows that the value type variables in memory are copied by value. The value of localVar1 is not affected by the value change of the variable localVar2:

The code listing shown in the following figure illustrates that the reference type variable in memory is copied by reference. The value of otherVar is affected by someVar variables:

Literal

Solidity provides a literal for the assignment of variables. Literals have no names, they are themselves values. Variables can change their values ​​during program execution, but literals always maintain the same value. Take a look at the following examples of various literals:

  • Examples of integer numerical quantities are 1, 10, 1 000, -1, and -100.
  • Examples of string literals are "Ritesh" and "Modi". String literals can be in single or double quotes.
  • Examples of address literals are:
  • 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
  • 0x1111111111111111111111111111111111111111.
  • The hexadecimal literal is prefixed with hex as the key. An example of a hexadecimal literal is hex "1A2B3F".
  • Solidity supports the use of dotted decimals for points. Examples include 4.5 and 0.2.

Integer

Integers help store numbers in contracts. Solidity provides the following two types of integers:

  • Signed integers: Signed integers can have both negative and positive values.
  • Unsigned integers: Unsigned integers can only hold positive values ​​and zeros. They can also be negative except for positive and zero values.

For each type, Solidity has multiple types of integers. Solidity provides a uint8 type to represent an 8-bit unsigned integer and is represented in multiples of 8 until it reaches 256. In summary, 32 different unsigned integers with multiples of 8, such as uint8, uint16, unit24, uint256, can be declared. Similarly, the data types of signed integers are the same, such as int8, int16, up to int256.

According to requirements, you should choose an integer of the appropriate size. For example, uint8 is appropriate when storing values ​​between 0 and 255, while int8 is more suitable for storage between -128 and 127. For higher values, you can use larger integers.

The default values ​​for signed and unsigned integers are zero, which are automatically initialized when they are declared. Integers are value types; however, when used as arrays, they are called reference types.

You can perform mathematical operations on integers such as addition, subtraction, multiplication, division, exponential, negative, post-increment, and pre-increment. The following image shows some of these examples:

Boolean

Like any programming language, Solidity provides a Boolean data type. The bool data type can be used to represent scenes with binary results, such as true or false, 1 or 0, and so on. Valid values ​​for this data type are true and false. It's worth noting that Booleans in Solidity cannot be converted to integers, just as they are in other programming languages. It is a value type, and any new Boolean variable assigned will create a new copy. The default value for bool in Solidity is false.

The code to declare and assign bool data types is as follows:

Bool isPaid = true;

It can be modified in the contract and can be used to pass in and out parameters and return values, as shown in the following figure:

Byte data type

Bytes are 8-bit signed integers. Everything in memory is stored in bits consisting of binary values ​​0 and 1. Solidity also provides byte data types to store binary format information. Typically, a programming language has only one data type to represent bytes. However, Solidity has multiple byte types. It provides data types ranging from bytes1 to bytes32 (inclusive) to represent different byte lengths as needed. These are called fixed-size byte arrays and are implemented as value types. The bytes1 data type represents 1 byte and the bytes2 represents 2 bytes. The default value for the byte is 0x00 and is initialized with this value. Solidity also has a byte type, which is an alias for bytes1.

A byte can be assigned in hexadecimal format as follows:

Bytes1 aa = 0x65;

A byte can be assigned an integer value in decimal format as follows:

Bytes1 bb = 10;

A byte can be assigned a negative integer value in decimal format as follows:

Bytes1 ee = -100;

A byte can be assigned a character value as follows:

Bytes1 dd = 'a';

In the code snippet below, 256 is not suitable for putting a single byte, requiring a larger byte array:

Bytes1 cc = 256;

The code in the following image shows how to store binary, positive and negative integers, and character literals in a fixed-size byte array.

We can also perform bitwise operations on byte data types, such as and, or, xor, not, and left and right shift operations:

 

 

Array

Arrays are data types, but more specifically, they are data structures that depend on other data types. An array is a group of values ​​of the same type. Arrays help to store these values ​​together and simplify the process of iterating, sorting, and searching for elements or child elements in the group. Solidity provides a rich array structure to meet different needs.

An example of an array in Solidity is as follows:

Uint [5] intArray

Arrays in Solidity can be fixed or dynamic.

1, fixed array

A fixed array is an array that declares a predetermined size. An example of a fixed array is as follows:

Fixed arrays cannot be initialized with the new keyword. They can only be initialized inline, as shown in the following code:

They can also be initialized inline later in the function as follows:

2, dynamic array

Dynamic arrays are arrays that are not of a predetermined size when declared, but their size is determined at runtime. Take a look at the code below:

Dynamic arrays can be initialized inline or initialized with the new operator. Can be initialized at the time of declaration, as shown below:

Later, you can also initialize it in two different steps in the function:

3, a special array

Solidity provides the following two special arrays:

  • Byte array
  • String array

Byte array

A byte array is a dynamic array that can hold any number of bytes. It is different from byte []. The byte [] array occupies 32 bytes per element, and the byte array holds all bytes together.

Bytes can be declared as state variables with an initial length, as shown in the following code:

This can also be broken into the following two lines of code similar to the array discussed earlier:

Byte arrays can be assigned directly as follows:

In addition, if the data is in a storage location, you can push the value into it, as shown in the following code:

Byte arrays also provide read/write length properties as follows:

Please see the code below:

String array

The string is a dynamic data type based on the byte array discussed in the previous section. They are very similar to byte arrays with additional constraints. Strings cannot be indexed or pushed, nor have a length attribute. To perform any of these operations on a string variable, you should first convert it to a byte and then convert it back to a string after the operation.

A string can consist of single or double quotes. Strings can be declared and assigned directly as follows:

They can also be converted to bytes as follows:

4, array properties

Arrays support some basic properties. In Solidity, not all types support all of these properties because there are multiple types of arrays.

These properties are as follows:

  • Index : In addition to string types, all types of arrays support the index property for reading a single array element. Only dynamic arrays, fixed arrays, and byte types support the index attribute used to write a single array element. Strings and fixed-size byte arrays do not support writing.
  • Push : This property is only supported for dynamic arrays.
  • Length : In addition to the string type, this property is supported by all arrays in the read perspective. Only dynamic arrays and bytes support the modified length attribute.

Array structure

We have briefly introduced the theme of the structure. The structure helps users customize the data structure. The structure helps convert multiple sets of variables of different data types into a single type. The structure does not contain any programming logic or code for execution; it only contains variable declarations. A structure is a reference type and is considered a complex type in Solidity.

Structures can be defined as state variables, as shown in the following code. Defines a structure consisting of an array of string, uint, bool, and uint. There are two state variables. Save them in the store. When the first stateStructure1 state variable is initialized at the time of declaration, another stateStructure1 state variable is left to be initialized later in the function. The local structure of the memory location is declared and initialized in the getAge function. Declare another structure as a pointer to a stateStructure state variable. A third local structure is declared that points to the localStructure local structure that was created earlier. Performs a change to the localStructure property while the previously declared state structure is initialized, and finally returns the age from the pointerLocalStructure. It returns the new value assigned to localStructure as shown in the following image:

enumerate

When we discussed the layout of Solidity files earlier in this chapter, we briefly talked about the concept of enumerations. An enumeration is a value type that contains a list of predefined constant values. They pass values, and each copy maintains its own value. Enumerations cannot be declared within a function and declared within the contract's global domain namespace.

Predefined constants are continually assigned, increasing the integer value from zero.

The code illustration shown below declares an enumeration that is identified as a state consisting of five constant values—created, approved, provisioned, rejected, and deleted. Assign the integer values ​​0, 1, 2, 3, 4 to them.

With the initial value provisioned, an enumeration instance named myStatus is created.

The returnEnum function returns the status and returns an integer value. It's worth noting that Web3 and Decentralized Applications (DApp) don't understand the enumerations declared in contracts. They will get an integer value corresponding to the enumeration constant.

The returnEnumInt function returns an integer value.

The passByValue function shows that an enumerated instance maintains its own local copy and is not shared with other instances.

The assignInteger function shows an example where an integer is assigned the value of an enumeration instance:

address

The address is a 20-byte data type. It is specifically designed to store the account address in Ethereum and is 160 or 20 bytes in size. It can save the contract account address as well as the externally owned account address. An address is a type of value that creates a new copy when it is assigned to another variable.

The address has a balance attribute that returns the amount of Ethereum available to the account and has some features for trading between the accounts and the contract function.

It provides the following two functions to trade Ethereum:

  • Transfer
  • Send

When sending Ether to an account, you should choose the transfer function instead of the send function. The send function returns a Boolean value depending on whether the Ethercoin send was executed successfully, and the transfer function throws an exception and returns the Ether to the caller.

It also provides the following three functions for calling contract functions:

  • Call
  • DelegateCall
  • Callcode

Mapping

Mapping is one of the most commonly used complex data types in Solidity. Mappings are similar to hash tables or dictionaries in other languages. They store key-value pairs and allow values ​​to be retrieved based on the keys provided.

Use the mapping keyword to declare the mapping, followed by the data type of the key and value separated by => notation. Maps have the same identifier as any other data type, and they can be used to access the map.

An example of a declaration map is as follows:

In the previous code, the uint data type was used to store keys and the address data type was used to store values. Names are used as identifiers for the map.

Although it is similar to hash tables and dictionaries, Solidity does not allow iterative mapping. If the key is known, the values ​​in the map can be retrieved. The next example shows how to use a map. A counter of type uint is maintained in the contract as a mapped key, and the address details are stored and retrieved with the help of functions.

To access any specific value in the map, the relevant key should be used with the map name as follows:

To store values ​​in a map, use the following syntax:

As shown below:

Although the mapping does not support iteration, there are some ways to work around this limitation. The next example illustrates one of the methods of iterative mapping. Please note that in the use of gas in Ethereum, it is usually necessary to avoid expensive operations such as iterations and loops. In this example, separate counters are maintained to track the number of entries stored in the map. This counter also acts as a key in the map. Local arrays can be constructed to store values ​​from the map. You can use a counter to execute a loop, and you can extract and store each value in the map into a local array, as shown in the following image:

A map can only be declared as a state variable of type storage. A map cannot be declared as a memory map within a function. However, if the map references a map declared in a state variable, you can declare the map in the function, as shown in the following example:

This syntax is legal because the localNames map references the Names state variable:

You can also use nested maps, which are maps that consist of maps. The next example illustrates this. In this example, there is an explicit mapping that maps uint to another map. The submap is stored as the value of the first map. The keys of the submap are of type address and the value is of type string. There is a mapping identifier that you can use to access a submap or an internal map, as shown in the following code:

To add an entry to such a nested map, you can use the following syntax:

Here, accountDetails is the mapping identifier and counter is the parent mapping key. accountDetails [counter] The map identifier retrieves the value from the parent map and returns exactly one map. Add the key to the return value and we can set the value of the internal map. Similarly, you can use the following syntax to retrieve the value of an internal map:

As shown below:

The above describes the layout of the Solidity, Solidity files, including the elements that can be declared at the top level. Structures, precompiled instructions, contracts, and contract elements are discussed from a layout perspective. In-depth discussion of value types and reference types and types of int, uint, fixed-size byte arrays, bytes, arrays, strings, structures, enumerations, addresses, booleans, and maps are discussed in more detail with examples.

Source | Solidity Programming

Author | Ritesh Modi

Editor | George

Produced | Blockchain Base Camp (blockchain_camp)