Basic of Solidity (solidity基础)

Table of contents

1.first contract

· Declare the compiler version

· Define the contract

·Contract constructor

· Define variables

· Define function

2.data type

· Value Types

· Reference Types

· Mapping Types


Solidity is a programming language for writing smart contracts. Smart contracts are automated contracts executed on the blockchain platform, which define the rules and conditions between participating parties and ensure that these rules are executed in the contract.

Solidity was originally designed for the Ethereum blockchain platform, but has also been widely adopted by other Ethereum Virtual Machine (EVM) compatible blockchain platforms. It provides a rich set of libraries and functions that enable developers to implement complex logic and business processes in smart contracts.

Smart contracts written in Solidity can implement a variety of functions, including creating and managing digital assets (such as tokens), implementing multi-party signatures, performing voting and elections, creating decentralized applications (DApps), and more.

1.first contract

A contract usually consists of state variables (contract data) and contract functions . We use the simplest Counter counter as the entry contract

· Declare the compiler version

The first thing to do when writing a contract is to declare the compiler version and tell the compiler what version of the compiler to use to compile

pragma solidity >=0.8.0; //Compile the Counter contract with a version greater than or equal to 0.8.0

· Define the contract

Solidity uses contractto define a contract, which is very similar to classes ( ) in other languages class. The contract itself is also a data type, called the contract type. In addition, the contract can also define events, custom types, etc.

contract Counter { //Defines a contract named Counter 
}

·Contract constructor

A constructor is a special function executed when creating a contract, used to initialize the contract, constructora constructor declared by the keyword.

If there is no initialization code compiler will add a default constructor constructor() public {}.

The initialization of the state variable can also be specified at the time of declaration. When not specified, it defaults to 0.

contract Base {
    uint x;
    address owner;
    constructor(uint _x) public {
       x = _x;
       owner = msg.sender;
    }
}

· Define variables

Solidity is a statically typed language. When defining each variable, you need to declare the type of the variable. Define variables in the format: 变量类型 变量可见性 变量名. Variable visibility is optional, and default values ​​will be used when declaration visibility is not shown internal.

uint public counter; //declare a variable named counter, the type is uint (256-bit unsigned integer)

A variable in a contract is assigned a storage unit on the blockchain. In Ethereum, all variables constitute the state of the entire blockchain network, so variables in contracts are usually called state variables .

But there are two special "variables", they do not allocate storage on the chain:

1) constant

constantIt is used to declare a constant, which does not occupy the storage space of the contract. The corresponding expression value is used to replace the constant name at compile time, that is, the constantmodified state variable can only be assigned to the variable with an expression that has a definite value at compile time.

contract C {
    uint constant x = 32**22 + 8;
    string constant text = "abc";
}
2) immutable

Immutable variables are assigned in the constructor, and the constructor is executed at deployment time, so this is a runtime assignment.

It is used in Solidity immutableto define an immutable variable, which does not occupy the state variable storage space. When deployed, the value of the variable will be appended to the runtime bytecode, so it is much cheaper than using state variables, and it also brings More security (to ensure that this value can no longer be modified), so immutable variables are very useful in many cases, such as saving the creator address, associated contract address, etc.

contract Example {    
    uint immutable decimals;
    uint immutable maxBalance;
    constructor(uint _decimals, address _reference) public {
       decimals = _decimals;
       maxBalance = _reference.balance; 
    }
​
}

· Define function

The function keyword is used to define a function

  function count() public { //The function named count() adds 1 to the counter state variable 
        counter = counter + 1; 
    }

Due to the change of state variables, calling this function will modify the state of the blockchain. At this time, we need to call this function through a transaction . The caller provides Gas for the transaction, and the verifier (miner) collects Gas to package the transaction. After the chain consensus, counterthe variable is really counted as adding 1;

We can also define the parameters and return values ​​of the function as needed and specify whether the function should modify the state. A function definition form can be expressed as follows:

function function name(<parameter type> <parameter name>) <visibility> <state mutability> [returns(<return type>)]{ 
}
1) Function return value

In Solidity, the return value is treated in the same way as the parameter. The return value in the code resultis also called the output parameter. We can assign it directly in the function body, or directly returnprovide the return in the statement. The return value can only specify other parameters. Type, name omitted:

function addAB(uint a, uint b) public returns (uint) {
      ....
    return counter + a + b;
}

At the same time, Solidity supports functions with multiple return values:

function f() public pure returns (uint, bool, uint) { 
        return (7, true, 2); 
    } 
     function g() public { 
        *// get return value* 
        (uint x, bool b, uint y) = f (); 
     }     
}
2) View function

A function decorated with view is called a view function. It can only read the state, but cannot modify the state. When calling the view function, only the node currently linked needs to be executed, and the result can be returned

function cal(uint a, uint b) public view returns (uint) { 
    return a * (b + 42) + now; 
}//cal() function does not modify the state, it does not need to submit transactions, and does not need to spend transaction fees

If the view function is called in a function that modifies the state, the view function will consume Gas, such as:

 function set(uint a, uint b) public returns (uint) {
            return cal(a, b);
    }

Because the gas price is 0 when the view function is called externally, and in the function of modifying the state, the gas price is set with the transaction

3) Pure functions

A function decorated with pure is called a pure function, it can neither read nor modify the state, it is only used for calculation

function f(uint a, uint b) public pure returns (uint) {
        return a * (b + 42);
    }

2.data type

· Value Types

Value type variables represent data that can be represented by 32 bytes, including the following types, which are always copied when assigning or passing parameters.

1) Integers

When a value is to be represented, it is usually expressed with an integer

int/unit

int/uint represent signed and unsigned integers of different digits. The keyword uint8is supported uint256, uintand intthe default is uint256and int256.

The number at the end of the keyword is in steps of 8, indicating the size of the space occupied by the variable. The range of integer values ​​is related to the space. For example, the value range uint32of the type is 0 to 2^32-1(2 to the 32nd power minus 1), when there is no integer variable When assigning a value, the default value 0 will be used.

The operators supported by integer types include the following:

  • Comparison operators: <=(less than or equal to), < (less than), ==(equal to), != (not equal to), >=(greater than or equal to), > (greater than)

  • Bitwise operators: &(and), | (or), ^(exclusive or), ~(bit inversion)

  • Arithmetic operators: +(plus sign), -(subtraction), - (minus sign), *, /, % (remainder), **(power)

  • Shift: <<(shift left), >>(shift right)

2 ) Address type address

In the Solidity contract program, the address type is used to represent our account. In addition, the contract and ordinary addresses can be addressrepresented by the type. There are two types of addresses:

  • address: holds a 20-byte value (the size of an Ethereum address).

  • address payable: Indicates the payable address. In terms of the address format, it is actually addressexactly the same as , which is also 20 bytes. It has two member functions transferand sendcan transfer money to this address. When you need to transfer money to an address, you can use the following code to addressconvert to address payable:

    address payable ap = payable(addr);

[Note]: Make this distinction, the displayed expression, an address can accept ETH, which means that it has the logic to process ETH (EOA account itself can transfer ETH); if no distinction is made, when we transfer ETH to an address, It happens that if the latter is a contract address and there is no logic to deal with ETH, then ETH will be locked on the contract address forever, and no one can withdraw and use it.

Some member functions of the address type :

<address>.balance: Return the balance of the address, the balance is in wei (uint256).

<address payable>.transfer(uint256 amount): It is used to send the value of amountether (wei) to the address. The transfer function only uses a fixed gas of 2300, and an exception is thrown when the transmission fails.

<address payable>.send(uint256 amount) returns (bool): sendSame transferas the function, it also uses a fixed gas of 2300, but returns when the sending fails falsewithout throwing an exception.

eg:

contract testAddr { 
   
   // If the balance of the contract is greater than or equal to 10, and x is less than 10, transfer 10 wei to x 
    function testTrasfer(address payable x) public { 
       address myAddress = address(this);//convert the contract to an address type 
       if (x.balance < 10 && myAddress.balance >= 10) { //.balance gets balance 
           x.transfer(10); //transfer 
       } 
    } 
}
3) Contract type

A contract is a type through which we can create a contract (that is, deploy a contract), and then interact with the functions in the contract. For example, calling a function of a contract can create another contract:

pragma solidity ^0.8.0;
​
contract Hello {
  function sayHi() public view returns  (uint) {
      return 10;
  }
}
​
contract HelloCreator {
    uint public x;
    Hello public h;
​
•    function createHello() public returns (address) {
•        h = new Hello();
•        return address(h);
  }
}

Contract type data members:

For some contract c there is

(1) type(C).name: Get the name of the contract.

(2) type(C).creationCode: Obtain the bytecode for creating the contract.

(3) type(C).runtimeCode: Obtain the bytecode when the contract is running.

[Question] : How to distinguish contract and external address

A: To distinguish whether an address is a contract address or an external account address, the key is to see if there is any code associated with the address. EVM provides an opcode EXTCODESIZEto obtain the code size (length) associated with the address. If it is an external account address, no code is returned. Therefore, we can use the following methods to determine the contract address and external account address.

function isContract(address addr) internal view returns (bool) {
  uint256 size;
  assembly { size := extcodesize(addr) }
  return size > 0;
  }

If it is judged outside the contract, you can use web3.eth.getCode()(a Web3 API), or the corresponding JSON-RPC method - eth_getcode. getCode() is used to obtain the code of the contract corresponding to the parameter address. If the parameter is an external account address, it will return "0x"; if the parameter is a contract, it will return the corresponding bytecode. The following two lines of code correspond to no code and There is output of the code.

>web3.eth.getCode(“0xa5Acc472597C1e1651270da9081Cc5a0b38258E3”) 
“0x”
>web3.eth.getCode(“0xd5677cf67b5aa051bb40496e68ad359eb97cfbf8”) “0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056” 

By comparing getCode()the output content, you can determine which address it is.

· Reference Types

The reference type is used to represent a complex type, which occupies more than 32 bytes. When declaring a variable of the reference type, the location of the variable needs to be specified, and the cost of copying is very high. Therefore, the method of reference can be used to pass multiple different names. Variables point to a value, including arrays and structures .

When defining a reference type, there is an additional property to identify where the data is stored:

memory (memory): The variable exists at runtime, and its life cycle only exists during the function call, and the gas overhead is small.

storage (storage): save state variables, as long as the contract exists, it will always be saved in the blockchain, and the gas cost is the largest.

calldata (call data): a special data location for storing function parameters, which is used to receive external data. It is an unmodifiable, non-persistent function parameter storage area with minimal gas overhead.

[Note]: When assigning values ​​of different reference types, a copy will be made only when assigning values ​​in different data locations, and a reference is usually added in the same data location .

1) array

def

Arrays, like most languages, add a type after a type []to indicate that a set of values ​​​​of that type can be stored. There are two types of arrays: fixed length and dynamic length

    // The default location of state variables is storage 
    uint [10] tens; // Fixed-length array 
    uint [] numbers; // Dynamic-length array 
    address [10] admins; // admins has up to 10 addresses 
    
    // as parameters , use calldata 
    function copy(uint[] calldata arrs) public { 
        numbers = arrs; // When assigning values, variables in different data locations will be copied. 
    } 
    
    // As parameter, use memory 
    function handle(uint[] memory arrs) internal { 
    } 
}

The initialization of the array can be carried out at the time of declaration, and can also be declared with the new keyword to create a memory array based on the runtime length. When using new to create a memory array, the corresponding space will be allocated in the memory according to the length; if the variable is stored In , it means to allocate a starting space, which can be expanded in the later running process

Array members:

  • length: Indicates the length of the current array (read-only).

  • push(): Used to add a new zero-initialized element to the end of the array, and return a reference to the element in order to modify the content of the element, such as: x.push().t = 2or x.push() = b, only valid for dynamic arrays in storage.

  • push(x): Add the given element to the end of the array. No return value, only valid for dynamic arrays in storage

  • pop(): Delete elements from the end of the array, the length of the array will be reduced by 1, and delete will be implicitly called on the removed elements to release unused space in time and save gas. pop() has no return value and is only valid for dynamic arrays in storage.

Special array types:

string: A string can also be a character array, but the push&pop method of the array is not supported

bytes: An array of dynamically allocated bytes, similar to byte[], but the gas cost of bytes is lower. bytes can also be used to express strings, but is usually used for raw byte data; supports push&pop

    //声明
    bytes bs;
    bytes bs0 = "12abcd";
​
    string str1 = "TinyXiong";
    string name;

[Note]: The string s is converted into bytes(s)a byte, and the subscript access bytes(s)[i]is not the corresponding character, but the corresponding UTF-8 encoding; stringthe function provided by the Solidity language itself is relatively weak, and does not provide some practical functions

Array gas consumption :

function sum() public {
        uint len = numbers.length;
        for (uint i = 0; i < len; i++) {
            total += numbers[i];
        }

Analysis of the above sum() function shows that the gas consumption numbersincreases linearly with the elements. If numbersthere are too many elements, sum()the gas consumption will exceed the block gas limit and cannot be executed. Common solutions:

  1. Move non-essential computations off-chain.

  2. Find a way to control the length of the array.

  3. Find a way to calculate in sections, so that the calculation workload of each section can be controlled.

2) Structure

Solidity uses structthe keyword to define a custom composite type

At the same time, you need to define its type for each member. In addition to using basic types as members, you can also use arrays , structures, and maps as members:

struct Student {
    string name;
    mapping(string=>uint) score;
    int age;
}

Struct declaration assignment

// Declare variables without initializing 
  Person person; 
​//
Can only be used as a state variable, assigning Person in the order of members (the order in which the structure is declared) 
person = Person(address(0x0), false, 18) ; 
// Declare Person memory in the function 
person = Person(address(0x0), false, 18) ; 
​//
Use named variable initialization, you can not assign Person in the order of member definition 
person = Person({account: address(0x0), gender: false, age: 18}) ; 
​//
Declare Person memory inside the function 
person = Person({account: address(0x0), gender: false, age: 18}) ;

· Mapping Types

A mapping relationship storage structure of key-value pairs, defined as mapping, similar in function to Java's Map and Python's Dict.

Guess you like

Origin blog.csdn.net/weixin_46516647/article/details/131610003
Recommended