Solidity Smart Contract Development (Introduction)

1. Version statement

The version number of the compiler needs to be declared at the beginning of the contract file. The purpose is to introduce an incompatible compiler in future version upgrades of the contract. The syntax is:

pragma solidity 版本号

The version number should follow the format of "0.x.0" or "x.0.0", for example:

// 代表不允许低于0.4.17版本的编译器,也不允许使用高于0.5.0版本的编译器
pragma solidity ^0.4.17

// 代表支持0.4.17以上的版本,但是不能超过0.9.0版本
pragma solidity >=0.4.17 < 0.9.0;

2. Code comments

Similar to Java, //single-line comments are represented using double slashes. Add an asterisk between double slashes to represent multi-line comments, for example:

// 单行注释

/*
多行注释
多行注释
...
*/

Documentation comments can also be added, the syntax is:

/**
* @dev 
* @param
* @return 
*/

for example:

pragma solidity >=0.4.17 <0.9.0

contract Calculator {
    
    

    /**
     * @dev 实现两个数的乘法运算
     * @param a 乘数
     * @param b 被乘数
     * @return 返回乘法的结果
    */
    function mul(uint a, uint b) public pure returns(uint) {
    
    
        return a * b;
    }

} 

3. Data type

3.1 Value types

3.1.1 Boolean type

Boolean types use the bool keyword. Its value is true or false. The operators supported by the Boolean type are:

name symbol
logic and &&
logical or ||
logical NOT !
equal ==
not equal to !=

3.1.2 Integer

Integers are represented by int or uint. uint stands for unsigned integer. They all support uint8, uint16, ..., uint256 (in increments of 8 bits). Defaults to int256 or uint256 if no number of bits is specified.

Operators supported on integer types:

name symbol
comparison operator <= < == != >= >
bitwise operator & | ^ ~
arithmetic operator + - * / ++ -- % ** << >>

Note: If the divisor is 0 or modulo 0, a runtime exception will be thrown.

3.1.3 Fixed-length floating-point type

The fixed-length floating-point type is represented by fixed or ufixed. However, currently Solidity supports the declaration of fixed-length floating-point variables, and cannot assign values ​​to variables of this type.

3.1.4 Address type

The keyword of the address type is represented by address, which is generally used to store contracts or accounts, and its length is generally 20 bytes. Variables of address type can use > >= < <= == !=operators.

Variables of address type can also use the following member variables and member methods:

Member variables describe example
balance address balance x.balance
transfer Transfer money to current address x.transfer(10)
send Transfer to the current address. Unlike transfer, if the execution fails, this method will not terminate the contract execution due to an exception x.send(10)
call、delegatecall Call the contract function, the first parameter is the function signature. Unlike call, delegatecall only uses the code stored in the library contract x.call(“mul”, 2, 3)

It is worth noting that when a contract A calls another contract B, it is equivalent to handing over control to contract B. It is also possible for contract B to call contract A in turn. Therefore, at the end of the call, it is necessary to prepare for the change of the state variable of contract A.

3.1.5 Fixed-length byte array

Fixed-length byte arrays are represented by bytes1, bytes2, ..., bytes32. If no length is specified, it defaults to bytes1. The operators supported by fixed-length byte arrays are:

name symbol
comparison operator <= < == != >= >
bitwise operator & | ^ ~ << >>
index access x[i]代表访问第i个字节的数据

The fixed-length byte array contains a length attribute, which is used to query the length of the byte array.

Although byte[] can be used to represent byte arrays, it is generally recommended to use bytes instead.

3.1.6 Literal constants

  • address literal

Hexadecimal literals that pass the address checksum test like 0x692a70d2e424a56d2c6c27aa97d1a86395877b3a are of type address.

  • numeric literal constant

Solidity supports literal constants for integers and arbitrary precision decimals. 1.or .1are valid decimal literals. It can also be expressed in scientific notation, such as: 2e10.

If a numeric literal constant is assigned to a variable of numeric type, it will be converted to a non-literal constant type.

uint128 a = 2.5 + 1 + 0.5; // 编译成功
uint128 b = 1; 
uint128 c = 2.5 + b + 0.5; // 编译报错

The second line of the above code assigns the numeric literal constant 1to the variable b. Because the variable b is a non-literal constant, different types of literal constants and non-literal constants cannot be operated, so the third line reports an error.
insert image description here

  • string literal constant

String literals use single or double quotes as a sequence of characters. String literals can be implicitly converted to bytes. for example:

bytes3 b = "abc";
bytes4 b = "abcd";
bytes4 b = "abcde"; // 报错,因为bytes4只能存储4个字节数据

3.1.7 Function types

Solidity supports assigning a function to a variable, which can also be passed as a parameter to other functions, or as the return value of a function.

Declaration syntax: function (parameter type) [internal|external] [pure|constant|view|payable] [returns (return value type)]

pragma solidity ^0.4.16;

contract A {
    
    
	// 声明函数变量,并将mul函数赋给变量func
    function (uint, uint) pure returns (uint) func = mul; 
    // 函数声明
    function mul(uint a, uint b) public pure returns(uint) {
    
    
        return a * b;
    }
}

Precautions:

  • If the function type does not need to return, you need to delete the entire returns part;
  • If not specified internal|external, the default is the internal function. Internal functions can be used in the current contract or subcontracts;
pragma solidity ^0.4.16;

contract A {
    
    
    function (uint, uint) pure returns (uint) func; 
}

contract B is A {
    
    
    
    function mul(uint a, uint b) public pure returns(uint) {
    
    
        return a * b;
    }
    
    function test() public {
    
    
    	// 对父合约的函数变量进行赋值
        func = mul;
        // 通过父合约函数变量调用mul函数
        func(10, 20);
    }
}

If it is an external external function, it can be passed through a function call or returned through a function.

pragma solidity ^0.4.16;

contract A {
    
    
    struct Request {
    
    
        bytes data;
        // 外部函数类型
        function (bytes memory) external callback;
    }
    
    Request[] requests;
    
    // 该函数的第二个参数类型为外部函数类型
    function query(bytes memory data, function (bytes memory) external callback) public {
    
    
        requests.push(Request(data, callback));
    }
    
}

contract Test {
    
    
    
    function getResponse(bytes result) public {
    
    
        // TODO ...
    }
    
    function test() public {
    
    
    	// 创建合约A的实例
        A a = new A();
        // 调用合约A实例的query函数,第二个参数是函数类型
        a.query("abc", this.getResponse);
    }
    
}

3.2 Other types

3.2.1 Arrays

If the length of the array is specified when declaring the array, the size of the array is fixed; if the length is not specified when the array is declared, the length of the array can change.

  • How to declare an array:
// 定义固定长度的数组
uint[3] a; 

// 定义长度可变的数组
uint[] a;
uint[] a = new int[](3);

// 通过字面量定义数组,数组长度也是可变的
uint[] c = [uint(1), 2, 3];
  • Note:
    1) If it is an array type of a state variable, you cannot manually specify memory or storage, and the default is a storage array;
    2) If it is an array type of a local variable, you can specify memory or storage, but if it is specified as a storage array, then The array variable must be initialized;
contract SimpleContract {
    
    
    int[] aa;
    int[3] bb;
    int[] cc = new int[](3);
    int[] dd = [uint(1), 2, 3];

    public test() public {
    
     
        int[] memory a1;
        int[] storage b1 = aa;

    }
}

➡ What are the functions of memory and storage keywords?
memory and storage represent the storage location of the data, that is, whether the data is stored in the memory or in the storage (the storage here can be simply understood as the disk of the computer). If it is a state variable and a local variable, the default location is storage; if it is a formal parameter, the default location is memory. In addition, if it is an external function parameter, its data location is calldata, and the effect is similar to that of memory. The specification of the data location is very important, it will affect the assignment behavior. For example, if the state variable is assigned to a local variable of storage, what is actually passed is a reference to the variable.

Another point worth noting is that fixed-length arrays can only be assigned to fixed-length arrays, and the size of the two arrays must be the same; fixed-length arrays cannot be assigned to variable-length arrays. If the following code compiles and reports an error: For storage dynamic arrays insert image description here
, You can change the size of the array through its lengthproperties, so the code on line 15 below compiles and passes. However, for memory dynamic arrays, lengththe size of the array cannot be changed through attributes, so the code on line 14 below reports an error:
insert image description here
the code on line 14 above will prompt TypeError: Expression has to be an lvalue.. lvalue can be understood as the value on the left side of the equal sign. The above error message reminds us that the value to the left of the equal sign a1.lengthmust be an lvalue, that is, a value that can be modified. Obviously a1.lengthnot an lvalue.

Adding array elements can be added by subscript, for example:

function test() public pure {
    
    
    int[] memory arr;
    arr[0] = 100;
}  

If it is a variable-length storage array and bytes type, you can also pushadd array elements through the method, which will return the latest length of the array.

contract SimpleContract {
    
    
    int[] aa;   // Define an array of variable length
      
    function test() public {
    
    
        int[] memory bb;
        bb[0] = 100;
        // 因为aa是一个变长的storage数组,因此可以通过push方法添加元素
        aa.push(100);
        // 下面代码报错,因为bb的位置不是storage
        bb.push(100);
    }   
}

Another thing worth noting is that due to EVM limitations, solidity does not support returning dynamic content through external function calls. For example the following code:

function test() public pure returns(int[] memory) {
    
    
    int[] memory arr;
    arr[0] = 100;
    return arr;
} 

The EVM will not report an error when compiling the contract, but when we call the test function of the contract example, the following error message will be prompted call to SimpleContract.test errored: VM error: invalid opcode..
insert image description here
However, if the test function is called through web3, it returns some dynamic content.

Little knowledge: You can think of bytes as an array of bytes, or you can use bytes as a string type. However, string cannot access each character in the string through or. If you need to access a character in the string, you can first convert the string to bytes, for example: uint size = bytes("123"). lengthlength 索引; byte c = bytes("123")[0].

3.2.3 Structure

Structs can be used to store complex data structures. Its definition format:

struct 结构体名称 {
    
    
    变量类型 变量名;
    ...
}

The variable type in the structure can be any type, but it cannot contain itself.
insert image description here
If the structure type data is assigned to a local variable in the function, this process does not create a copy of the structure object, and assigns the reference of the structure object to the local variable (that is, passing by reference).

3.2.4 Enumeration

Similar to structure, enumeration is also a custom type in solidity, and its definition format is:

enum 类型名称 {
    
    
    枚举值1,
    枚举值2,
    ...
}

An enumeration type must contain at least one enumeration value. Enumeration values ​​can be any valid identifier (for example: must start with a letter or underscore).

enum Week {
    
    
    Monday,
    Tuesday,
    ...
}

The type of the enumeration value defaults to uint8. The first enumeration value will be parsed as 0, the second enumeration value will be parsed as 1, and so on. Enumerated types cannot be implicitly converted to other types, only explicit conversions can be performed.

Weekend weekend = Weekend(0);

The above code 0converts the value into an enumeration type, corresponding to Weekend.Mondaythe enumeration value. If the value exceeds the maximum enumeration value, an explicit conversion will report an error when running the contract.

3.2.5 Mapping

Compared with Java's Map collection type, the mapping type is used to store a pair of related data, and its definition format is:

mapping(_keyType => _valueType) 变量名;

_keyType cannot be mapping, variable-length array, enumeration, or structure type; _valueType can be any type.

In the map, what is actually stored is the keccak256 hash of the key. Mapping has no length, and there is no collection concept of key and value.

If the local variable uses the mapping type, then the data location of the variable must be storage, and the variable must be initialized.

contract SimpleContract {
    
    
    mapping(string => string) m1;
    
    function test() public {
    
    
        mapping(string => string) storage m2 = m1;
    }
}

3.2.6 Tuple types

A tuple is similar to a fixed-length array, which is a list of objects with a fixed number of elements. But unlike arrays, tuples can have different element types. In general, you can return multiple values ​​through tuples in a function, and use a variable of the element type to receive the return value of the function.

function f() public pure returns(uint, bool) {
    
    
    return (10, false);
}

function test() public pure {
    
     
    (uint a, bool b) = f();
}

Assignment between tuples is possible.

function test() public pure {
    
    
    uint x = 10;
    uint y = 20;
    (x, y) = (y, x);
}

The above code interacts the values ​​of x and y through assignment between tuples.

If the tuple has only one element, the comma after the element cannot be omitted.

function f() public pure returns(uint, bool) {
    
    
    return (10, false);
}

function test() public pure {
    
    
    (uint a, ) = f();
}

Fourth, the operator

4.1 Basic operators

  • Arithmetic operators:+ - * / % ++ --
  • Logical Operators:&& || !
  • Comparison operators:> < >= <= == !=
  • Bitwise operators:& | ^(异或) ~(取反) <<(左移) >>(右移) >>>(右移后左边补0)
  • Assignment operator:= += -= *= /= %=
  • Ternary operator:condition expression ? value1 : value2

4.2 Lvalue variables

An lvalue variable lvalue, that is, a variable to which a value can be assigned. If the expression contains lvalue variables, its operators can be abbreviated. for example:

function test() public {
    
    
    int a = 10;
    int b = 5;
    a += b;a -= b;a *= b;a /= b;
    a++;a--;
}

A keyword closely related to lvalues delete. For example delete a, if a is an integer, it delete ameans to restore the value of a to its initial state; if a is a dynamic array, it is delete aequivalent to setting the length of the array to 0; if a is a static array, then the value delete ain the array will be Each element is reset; if a is a structure type, delete aeach attribute in the structure will be reset.

Note: delete has no effect on mappings.

4.3 Type Conversion

If two variables of the same type but with different precisions are operated on, the low precision is automatically converted to the high precision type.

function test() public {
    
    
    int8 a = 10;
    int b = 20;
    int c = a + b;
}

In the above code, the type of variable a is int8, and the type of variable b is int256, so the two variables are added, and the low precision will be automatically converted to high precision, so the result is int256. Similarly, low-precision variables can also be assigned to high-precision variables, so the following code is also fine.

function test() public {
    
    
    int8 a = 10;
    int b = a;
}

But if you need to convert high-precision to low-precision types, you need to cast, as follows:

function test() public {
    
    
    int a = 10;
    int8 b = int8(a);
}

It should be noted that explicit type conversion may lead to some unpredictable results, such as:

function test() public {
    
    
    int a1 = -10;
    uint a2 = uint(a); // 115792089237316195423570985008687907853269984665640564039457584007913129639926
    uint32 b1 = 0x12345678;
    uint16 b2 = uint16(b1); // 22136,即0x5678的十进制表示形式
}

Running the above code, the final result is not what we expected. Therefore, if you use explicit type conversion, you must be clear about the conversion process.

Similar to javascript, you can also use the var keyword when defining variables, for example:

int a = 10;
var b = a;

In the above code, because the type of variable a is int256, the type of variable b is also int256. And once the type of variable b is determined, its type will not change again. Therefore the following program fails to compile:

bool c = false;
b = c;

In addition, solidity does not support the use of the var keyword for function parameters or inverse parameters.
insert image description here

5. Process control

Solidity supports most of the syntax of javascript, such as if...else, while...do, do...while, for and so on.

pragma solidity ^0.4.16;

contract SimpleContract {
    
    
    
    function test1(int week) public pure returns(string) {
    
    
        if (week == 1) {
    
    
            return "Monday";
        } else if (week == 2) {
    
    
            return "Tuesday";
        } else if (week == 3) {
    
    
            return "Wednesday";
        } else if (week == 4) {
    
    
            return "Thursday";
        } else if (week == 5) {
    
    
            return "Friday";
        } else if (week == 6) {
    
    
            return "Saturday";
        } else if (week == 7) {
    
    
            return "Sunday";
        } else {
    
    
            return "invalid week";
        }
    }
    
    function test2() public pure returns(int) {
    
    
        int sum = 0;
        for (int i = 0; i < 100; i++) {
    
    
            sum += i;
        }
        return sum;
    }   
    
    function test3() public pure returns(int) {
    
    
        int i = 0;
        int sum = 0;
        while (i < 100) {
    
    
            sum += i;
            i++;
        }
        return sum;
    }  
    
    function test4() public pure returns(int) {
    
    
        int i = 0;
        int sum = 0;
        do {
    
    
            if (i % 2 == 0) continue;
            sum += i;
            i++;
        } while(i < 100);
        return sum;
    }  
    
}

summary

This part mainly introduces the basic syntax of the solidity language, and the next part continues to introduce the advanced syntax of solidity.

Guess you like

Origin blog.csdn.net/zhongliwen1981/article/details/117952660