Solidity Smart Contract Entwicklung (Einführung)

1. Versionsangabe

Die Versionsnummer des Compilers muss am Anfang der Vertragsdatei angegeben werden, um bei zukünftigen Versionserweiterungen des Vertrags einen inkompatiblen Compiler einzuführen. Die Syntax lautet:

pragma solidity 版本号

Die Versionsnummer sollte dem Format "0.x.0" oder "x.0.0" folgen, zum Beispiel:

// 代表不允许低于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. Codekommentare

Ähnlich wie bei Java //werden einzeilige Kommentare durch doppelte Schrägstriche dargestellt. Fügen Sie ein Sternchen zwischen doppelten Schrägstrichen hinzu, um mehrzeilige Kommentare darzustellen, zum Beispiel:

// 单行注释

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

Es können auch Dokumentationskommentare hinzugefügt werden, die Syntax lautet:

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

Zum Beispiel:

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. Datentyp

3.1 Werttypen

3.1.1 Boolescher Typ

Boolesche Typen verwenden das Schlüsselwort bool. Sein Wert ist wahr oder falsch. Die vom Typ Boolean unterstützten Operatoren sind:

Name Symbol
Logik u &&
logisch bzw ||
logisches NICHT !
gleich ==
Nicht gleichzusetzen mit !=

3.1.2 Ganzzahl

Ganze Zahlen werden durch int oder uint dargestellt. uint steht für unsigned integer. Sie alle unterstützen uint8, uint16, ..., uint256 (in 8-Bit-Schritten). Standardmäßig int256 oder uint256, wenn keine Anzahl von Bits angegeben ist.

Auf Ganzzahltypen unterstützte Operatoren:

Name Symbol
Vergleichsoperator <= < == != >= >
bitweiser Operator & | ^ ~
arithmetischer Operator + - * / ++ -- % ** << >>

Hinweis: Wenn der Divisor 0 oder Modulo 0 ist, wird eine Laufzeitausnahme ausgelöst.

3.1.3 Fließkommatyp mit fester Länge

Der Gleitkommatyp mit fester Länge wird durch fixed oder ufixed dargestellt. Derzeit unterstützt Solidity jedoch die Deklaration von Gleitkommavariablen mit fester Länge und kann Variablen dieses Typs keine Werte zuweisen.

3.1.4 Adresstyp

Das Schlüsselwort des Adresstyps wird durch address repräsentiert, das im Allgemeinen verwendet wird, um Verträge oder Konten zu speichern, und seine Länge beträgt im Allgemeinen 20 Byte. Variablen vom Adresstyp können > >= < <= == !=Operatoren verwenden.

Variablen des Adresstyps können auch die folgenden Mitgliedsvariablen und Mitgliedsmethoden verwenden:

Mitgliedsvariablen beschreiben Beispiel
Gleichgewicht Adressguthaben x.balance
überweisen Geld an aktuelle Adresse überweisen x.transfer(10)
schicken Überweisung an die aktuelle Adresse: Im Gegensatz zur Überweisung wird bei dieser Methode die Vertragsausführung nicht aufgrund einer Ausnahme beendet, wenn die Ausführung fehlschlägt x.senden(10)
anrufen, delegierenanrufen Rufen Sie die Vertragsfunktion auf, der erste Parameter ist die Funktionssignatur. Im Gegensatz zu Call verwendet Delegatecall nur den im Bibliotheksvertrag hinterlegten Code x.call(“mul”, 2, 3)

Es ist erwähnenswert, dass, wenn ein Vertrag A einen anderen Vertrag B aufruft, dies gleichbedeutend mit der Übergabe der Kontrolle an Vertrag B ist. Es ist auch möglich, dass Vertrag B wiederum Vertrag A aufruft. Daher ist es notwendig, am Ende des Anrufs die Änderung der Zustandsvariablen von Vertrag A vorzubereiten.

3.1.5 Byte-Array fester Länge

Byte-Arrays mit fester Länge werden durch bytes1, bytes2, ..., bytes32 dargestellt. Wenn keine Länge angegeben ist, wird standardmäßig bytes1 verwendet. Die von Byte-Arrays fester Länge unterstützten Operatoren sind:

Name Symbol
Vergleichsoperator <= < == != >= >
bitweiser Operator & | ^ ~ << >>
Indexzugriff x[i]代表访问第i个字节的数据

Das Bytearray mit fester Länge enthält ein Längenattribut, mit dem die Länge des Bytearrays abgefragt wird.

Obwohl byte[] zur Darstellung von Byte-Arrays verwendet werden kann, wird im Allgemeinen empfohlen, stattdessen Bytes zu verwenden.

3.1.6 Literale Konstanten

  • Adressbuchstabe

Hexadezimale Literale, die den Adressprüfsummentest bestehen, wie 0x692a70d2e424a56d2c6c27aa97d1a86395877b3a, sind vom Typ Adresse.

  • numerische Literalkonstante

Solidity unterstützt Literalkonstanten für ganze Zahlen und Dezimalzahlen mit beliebiger Genauigkeit. 1.oder .1sind gültige Dezimalliterale. Es kann auch in wissenschaftlicher Notation ausgedrückt werden, wie zum Beispiel: 2e10.

Wenn einer Variablen vom numerischen Typ eine numerische Literalkonstante zugewiesen wird, wird sie in einen nicht-literalen Konstantentyp konvertiert.

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

Die zweite Zeile des obigen Codes weist der Variablen b die numerische Literalkonstante zu 1. Da die Variable b eine nicht wörtliche Konstante ist, können verschiedene Arten von wörtlichen Konstanten und nicht wörtlichen Konstanten nicht verwendet werden, sodass die dritte Zeile einen Fehler meldet.
Bildbeschreibung hier einfügen

  • String-Literal-Konstante

Zeichenfolgenliterale verwenden einfache oder doppelte Anführungszeichen als Folge von Zeichen. Zeichenfolgenliterale können implizit in Bytes konvertiert werden. Zum Beispiel:

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

3.1.7 Funktionstypen

Solidity unterstützt die Zuweisung einer Funktion zu einer Variablen, die auch als Parameter an andere Funktionen oder als Rückgabewert einer Funktion übergeben werden kann.

Deklarationssyntax: function (Parametertyp) [intern|extern] [pure|constant|view|payable] [returns (Rückgabewerttyp)]

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;
    }
}

Vorsichtsmaßnahmen:

  • Wenn der Funktionstyp nicht zurückkehren muss, müssen Sie den gesamten Rückgabeteil löschen;
  • Wenn nicht angegeben internal|external, ist der Standardwert die interne Funktion. Interne Funktionen können im aktuellen Vertrag oder Unterverträgen verwendet werden;
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);
    }
}

Wenn es sich um eine externe externe Funktion handelt, kann sie über einen Funktionsaufruf übergeben oder über eine Funktion zurückgegeben werden.

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 Andere Typen

3.2.1 Arrays

Wenn die Länge des Arrays bei der Deklaration des Arrays angegeben wird, ist die Größe des Arrays fest; wird die Länge bei der Deklaration des Arrays nicht angegeben, kann sich die Länge des Arrays ändern.

  • So deklarieren Sie ein Array:
// 定义固定长度的数组
uint[3] a; 

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

// 通过字面量定义数组,数组长度也是可变的
uint[] c = [uint(1), 2, 3];
  • Hinweis:
    1) Wenn es sich um einen Array-Typ einer Zustandsvariablen handelt, können Sie Speicher oder Speicher nicht manuell angeben, und der Standardwert ist ein Speicher-Array;
    2) Wenn es sich um einen Array-Typ einer lokalen Variablen handelt, können Sie Speicher oder Speicher angeben , aber wenn es als Speicher-Array angegeben ist, muss die Array-Variable initialisiert werden;
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;

    }
}

➡ Welche Funktionen haben Speicher- und Speicherschlüsselwörter?
Arbeitsspeicher und Speicher stellen den Speicherort der Daten dar, also ob die Daten im Arbeitsspeicher oder im Speicher abgelegt sind (der Speicher kann hier einfach als Platte des Computers verstanden werden). Wenn es sich um eine Zustandsvariable und eine lokale Variable handelt, ist der Standardort Speicher, wenn es sich um einen formalen Parameter handelt, ist der Standardort Speicher. Wenn es sich um einen externen Funktionsparameter handelt, ist seine Datenposition außerdem calldata, und die Wirkung ist ähnlich der des Gedächtnisses. Die Angabe des Datenortes ist sehr wichtig, sie beeinflusst das Zuweisungsverhalten. Wenn beispielsweise die Zustandsvariable einer lokalen Speichervariablen zugewiesen wird, wird tatsächlich ein Verweis auf die Variable übergeben.

Ein weiterer erwähnenswerter Punkt ist, dass Arrays mit fester Länge nur Arrays mit fester Länge zugewiesen werden können und die Größe der beiden Arrays gleich sein muss; Arrays mit fester Länge können nicht Arrays mit variabler Länge zugewiesen werden und meldet einen Fehler: For storage dynamic arrays Bildbeschreibung hier einfügen
, You can change the size of the array through its lengthproperties, so dass der Code in Zeile 15 unten kompiliert und übergeben wird. Bei speicherdynamischen Arrays lengthkann die Größe des Arrays jedoch nicht durch Attribute geändert werden, sodass der Code in Zeile 14 unten einen Fehler meldet:
Bildbeschreibung hier einfügen
Der Code in Zeile 14 oben wird auffordern TypeError: Expression has to be an lvalue.. lvalue kann als Wert auf der linken Seite des Gleichheitszeichens verstanden werden. Die obige Fehlermeldung erinnert uns daran, dass der Wert links vom Gleichheitszeichen a1.lengthein lvalue sein muss, also ein Wert, der geändert werden kann. Offensichtlich a1.lengthkein lvalue.

Das Hinzufügen von Array-Elementen kann per Index hinzugefügt werden, zum Beispiel:

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

Wenn es sich um ein Speicher-Array mit variabler Länge und Byte-Typ handelt, können Sie auch pushArray-Elemente über die Methode hinzufügen, die die letzte Länge des Arrays zurückgibt.

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);
    }   
}

Erwähnenswert ist auch, dass Solidität aufgrund von EVM-Einschränkungen die Rückgabe dynamischer Inhalte durch externe Funktionsaufrufe nicht unterstützt. Zum Beispiel der folgende Code:

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

Die EVM wird beim Kompilieren des Vertrags keinen Fehler melden, aber wenn wir die Testfunktion des Vertragsbeispiels aufrufen, wird die folgende Fehlermeldung ausgegeben call to SimpleContract.test errored: VM error: invalid opcode..
Bildbeschreibung hier einfügen
Wenn die Testfunktion jedoch über web3 aufgerufen wird, gibt sie einige dynamische Inhalte zurück.

Wenig Wissen: Sie können sich Bytes als ein Array von Bytes vorstellen, oder Sie können Bytes als Zeichenfolgentyp verwenden. Allerdings kann string nicht auf jedes Zeichen in der Zeichenfolge durch oder zugreifen. Wenn Sie auf ein Zeichen in der Zeichenfolge zugreifen müssen, können Sie die Zeichenfolge zuerst in Bytes umwandeln, zum Beispiel: uint size = bytes("123"). lengthlength索引

3.2.3 Struktur

Strukturen können verwendet werden, um komplexe Datenstrukturen zu speichern. Sein Definitionsformat:

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

Der Variablentyp in der Struktur kann ein beliebiger Typ sein, er darf sich jedoch nicht selbst enthalten.
Bildbeschreibung hier einfügen
Wenn der Strukturtyp data einer lokalen Variablen in der Funktion zugewiesen wird, erstellt dieser Prozess keine Kopie des Strukturobjekts und weist der lokalen Variablen die Referenz des Strukturobjekts zu (d. h. Referenzübergabe).

3.2.4 Aufzählung

Ähnlich wie die Struktur ist auch die Aufzählung ein benutzerdefinierter Typ in Solidität, und sein Definitionsformat ist:

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

Ein Aufzählungstyp muss mindestens einen Aufzählungswert enthalten. Aufzählungswerte können beliebige gültige Bezeichner sein (z. B. müssen mit einem Buchstaben oder Unterstrich beginnen).

enum Week {
    
    
    Monday,
    Tuesday,
    ...
}

Der Typ des Aufzählungswerts ist standardmäßig uint8. Der erste Aufzählungswert wird als 0 geparst, der zweite Aufzählungswert wird als 1 geparst und so weiter. Aufzählungstypen können nicht implizit in andere Typen konvertiert werden, es können nur explizite Konvertierungen durchgeführt werden.

Weekend weekend = Weekend(0);

Der obige Code 0konvertiert den Wert in einen Aufzählungstyp, der Weekend.Mondaydem Aufzählungswert entspricht. Wenn der Wert den maximalen Enumerationswert überschreitet, meldet eine explizite Konvertierung einen Fehler beim Ausführen des Vertrags.

3.2.5 Kartierung

Verglichen mit dem Map-Sammlungstyp von Java wird der Mapping-Typ verwendet, um ein Paar verwandter Daten zu speichern, und sein Definitionsformat ist:

mapping(_keyType => _valueType) 变量名;

_keyType kann kein Mapping-, Array-, Aufzählungs- oder Strukturtyp variabler Länge sein; _valueType kann ein beliebiger Typ sein.

In der Karte wird tatsächlich der keccak256-Hash des Schlüssels gespeichert. Mapping hat keine Länge und es gibt kein Sammlungskonzept von Schlüssel und Wert.

Wenn die lokale Variable den Zuordnungstyp verwendet, muss der Datenspeicherort der Variablen der Speicher sein und die Variable muss initialisiert werden.

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

3.2.6 Tupeltypen

Ein Tupel ähnelt einem Array fester Länge, bei dem es sich um eine Liste von Objekten mit einer festen Anzahl von Elementen handelt. Aber im Gegensatz zu Arrays können Tupel unterschiedliche Elementtypen haben. Im Allgemeinen können Sie mehrere Werte durch Tupel in einer Funktion zurückgeben und eine Variable vom Typ Element verwenden, um den Rückgabewert der Funktion zu erhalten.

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

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

Eine Zuordnung zwischen Tupeln ist möglich.

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

Der obige Code interagiert mit den Werten von x und y durch Zuweisung zwischen Tupeln.

Wenn das Tupel nur ein Element hat, darf das Komma nach dem Element nicht weggelassen werden.

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

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

Viertens der Betreiber

4.1 Grundlegende Operatoren

  • Rechenzeichen:+ - * / % ++ --
  • Logische Operatoren:&& || !
  • Vergleichsoperatoren:> < >= <= == !=
  • Bitweise Operatoren:& | ^(异或) ~(取反) <<(左移) >>(右移) >>>(右移后左边补0)
  • Aufgabenverwalter:= += -= *= /= %=
  • Ternärer Operator:condition expression ? value1 : value2

4.2 Lvalue-Variablen

Eine Lvalue-Variable lvalue, also eine Variable, der ein Wert zugewiesen werden kann. Wenn der Ausdruck lvalue-Variablen enthält, können seine Operatoren abgekürzt werden. Zum Beispiel:

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

Ein Schlüsselwort, das eng mit lvalues ​​verwandt ist delete. Wenn beispielsweise delete aa eine Ganzzahl ist, delete abedeutet dies, den Wert von a auf seinen Anfangszustand zurückzusetzen; wenn a ein dynamisches Array ist, delete aentspricht dies dem Setzen der Länge des Arrays auf 0; wenn a ein statisches Array ist, dann Der Wert delete aim Array ist Jedes Element wird zurückgesetzt; wenn a ein Strukturtyp ist, delete awird jedes Attribut in der Struktur zurückgesetzt.

Hinweis: Löschen hat keine Auswirkung auf Zuordnungen.

4.3 Typkonvertierung

Wenn zwei Variablen desselben Typs, aber mit unterschiedlicher Genauigkeit bearbeitet werden, wird die niedrige Genauigkeit automatisch in den Typ mit hoher Genauigkeit umgewandelt.

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

Im obigen Code ist der Typ von Variable a int8 und der Typ von Variable b ist int256, also werden die beiden Variablen addiert und die niedrige Genauigkeit wird automatisch in hohe Genauigkeit konvertiert, sodass das Ergebnis int256 ist. In ähnlicher Weise können Variablen mit niedriger Genauigkeit auch Variablen mit hoher Genauigkeit zugewiesen werden, sodass der folgende Code ebenfalls in Ordnung ist.

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

Wenn Sie jedoch Typen mit hoher Genauigkeit in Typen mit niedriger Genauigkeit umwandeln müssen, müssen Sie wie folgt umwandeln:

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

Es sollte beachtet werden, dass eine explizite Typkonvertierung zu einigen unvorhersehbaren Ergebnissen führen kann, wie zum Beispiel:

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

Wenn Sie den obigen Code ausführen, ist das Endergebnis nicht das, was wir erwartet haben. Wenn Sie die explizite Typkonvertierung verwenden, müssen Sie sich daher über den Konvertierungsprozess im Klaren sein.

Ähnlich wie bei Javascript können Sie auch das Schlüsselwort var verwenden, wenn Sie Variablen definieren, zum Beispiel:

int a = 10;
var b = a;

Da im obigen Code der Typ der Variablen a int256 ist, ist der Typ der Variablen b auch int256. Und sobald der Typ der Variablen b bestimmt ist, ändert sich ihr Typ nicht mehr. Daher kann das folgende Programm nicht kompiliert werden:

bool c = false;
b = c;

Außerdem unterstützt Solidity nicht die Verwendung des Schlüsselworts var für Funktionsparameter oder inverse Parameter.
Bildbeschreibung hier einfügen

5. Prozesskontrolle

Solidity unterstützt den größten Teil der Syntax von Javascript, wie if...else, while...do, do...while, for und so weiter.

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;
    }  
    
}

Zusammenfassung

Dieser Teil führt hauptsächlich in die grundlegende Syntax der solidity-Sprache ein, und der nächste Teil führt weiter in die erweiterte Syntax von solidity ein.

Supongo que te gusta

Origin blog.csdn.net/zhongliwen1981/article/details/117952660
Recomendado
Clasificación