一.Solidity 语言

Solidity 语言是一种面向合约的高级编程语言,用于在以太坊区块链网络上实现智能合约。Solidity 语言深受c++、Python 和 JavaScript 的影响,针对以太坊(Ethereum)虚拟机(EVM)设计。

Solidity 语言是静态类型语言支持继承、库和复杂的用户定义类型。

1、静态类型语言:变量定义时有类型声明的语言。

(1)变量的类型在编译的时候确定

(2)变量的类型在运行时不能修改,这样编译器就可以确定运行时需要的内存总量

         例如:C、Scala、Java、F#语言是静态类型语言。

2、动态类型语言:变量定义时无类型声明的语言。

(1)变量的类型在运行的时候确定

(2)变量的类型在运行可以修改

例如:python、Javascript、Ruby语言是动态类型语言。

3、强类型定义语言

强制数据类型定义的语言。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。举个例子:如果你定义了一个整型变量a,那么程序根本不可能将a当作字符串类型处理。强类型定义语言是类型安全的语言。

4、弱类型定义语言

数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。

可以使用 Solidity 语言创建区块链上运行的投票、众筹、钱包等各种类型的智能合约

以太坊/Ethereum是什么?

以太坊是一个去中心化的运行智能合约区块链平台

以太坊/Ethereum虚拟机(EVM)

Ethereum 虚拟机,也称为EVM,是以太坊/Ethereum中智能合约的运行时环境

可以用某种语言,例如 Solidity 语言,开发智能合约程序,编译成以太坊(Ethereum)虚拟机支持的字节码/bytecode,然后该程序就可以在虚拟机中执行了。

什么是智能合约?

智能合约(Smart contract )是一种旨在以信息化方式传播验证执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于1995年由Nick Szabo首次提出。

智能合约的目的是提供优于传统合约的安全方法,并减少与合约相关的其他交易成本。

简单地说,智能合约可以理解为一个自执行的协议。智能合约可以自动处理协议的履行、管理、以及支付。

例如,可以编写这样一个智能合约:本月底之前,老王转账给小张1个以太币,这个智能合约部署后,就会在月底之前,自动把老王的1个以太币转账给小张,无需人为干预。

如果你现在不理解智能合约也没关系,我们稍后会更详细地讲解。


二.Solidity安装本地编译器

安装本地编译器

安装 nodejs / npm

node官方网站下载node,推荐LTS版本,按提示完成安装,npm会同时装上。

验证Node版本:

> node -v
v10.16.3

> npm -v
6.11.3

安装 Solidity 编译器 solc

一旦安装了Node.js包管理器,就可以按照下面的步骤安装 Solidity 编译器

$ npm install -g solc@0.4.25

上面的命令将安装solcjs程序,并使其在整个系统中都可用。

验证solc安装:

$ solcjs --version

如果一切顺利,这将打印如下内容

0.4.25+commit.c082d0b4.Emscripten.clang

现在,可以使用本地的 solcjs 了,它比标准的 solidity 编译器少很多特性,但对于学习来说足够了。


三.基本语法

一个 Solidity 源文件可以包含任意数量的合约定义import指令pragma指令

让我们从一个简单的 Solidity 源程序开始。下面是一个 Solidity 源文件的例子:

Pragma

第一行是pragma指令,它告诉我们源代码是为Solidity version 0.4.25版本编写的。

pragma指令只对自己的源文件起作用,如果把文件B导入到文件A,文件B的pragma将不会自动应用于文件A。

pragma solidity ^0.4.0;

上面的pragma指令意思是,源文件不能用低于0.4.0版本的编译器编译,也不能用0.5.0版本及以上版本的编译器编译。

这里第二个条件是用^加上的,表示不超过0.5.0版本,背后的意思是,0.4.0 ~ 0.4.9 之间的小版本改动通常不会有破坏性更改,源代码应该都是兼容的。

Contract/智能合约

智能合约是位于以太坊区块链上特定地址的代码(函数)数据(状态)集合

这行代码:uint number;,声明了一个名为number的状态变量,类型为uint,setget函数可用于修改或检索变量的值。

导入文件

上面的例子没有import语句,但是Solidity 支持与JavaScript非常相似的导入语句。

下面的语句从“filename”导入所有全局符号。

import "filename";

下面的示例,创建一个新的全局符号symbolName,它的成员都是来自“filename”的全局符号。

import * as symbolName from "filename";

要从当前目录导入文件x,请使用import "./x"。如果不指定当前路径,可能会在全局“include目

;录”中引用另一个文件。

保留关键字

下面是 Solidity 语言中的保留关键字:

abstractautocasedefaultfinal
afterimmutableimplementsininline
aliasletmacromatchmutable
applynullofoverridepartial
catchpromisereferencerelocatablesealed
copyofsizeofstaticsupportsswitch
definetrytypedeftypeofunchecked


四、第一个程序

为简单起见,我们使用在线Solidity开发工具Remix IDE编译和运行Solidity程序。

第1步 – 在File explorers选项卡下,新建一个test1.sol文件,代码如下:

示例

pragma solidity ^0.5.0;
contract SolidityTest {
   constructor() public{
   }
   
   function getResult() public view returns(uint){
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

第2步 – 在Compiler选项卡下,单击 Compile 按钮,开始编译
第3步 – 在Run选项卡下,单击 Deploy 按钮进行部署
第4步 – 在Run选项卡下,选择 SolidityTest at 0x… 下拉
第5步 – 单击 getResult 按钮显示结果。

输出

0: uint256: 3

五、代码注释

Solidity 支持c风格和c++风格的注释。

  • //之后到行尾的文本,都被看作注释,编译器忽略此内容,”ctrl+/
  • /* 与 */ 之间的文本被看作注释, 编译器忽略此内容, ”shift+ctrl+a

示例

function getResult() public view returns(uint){
   // 这是一行注释,类似于c++中的注释

   /*
    * 这是多行注释
    * 类似于c语言中的注释
    */
   uint a = 1;
   uint b = 2;
   uint result = a + b;
   return result;
}

六、数据类型

在用任何语言编写程序时,都需要使用变量存储各种信息。变量是内存空间的名称,变量有不同类型,例如整型、字符串类型等等。操作系统根据变量的数据类型分配内存。

Solidity中,变量类型有以下几大类:

  • 值类型
  • 地址类型
  • 引用类型

1.值类型

类型保留字取值
布尔型booltrue/false
整型int/uint有符号整数/无符号整数。
整型int8 to int2568位到256位的带符号整型数。int256与int相同。
整型uint8 to uint2568位到256位的无符号整型。uint256和uint是一样的。
定长浮点型fixed/unfixed有符号和无符号的定长浮点型
定长浮点型fixedMxN带符号的定长浮点型,其中M表示按类型取的位数,N表示小数点。M应该能被8整除,从8到256。N可以是0到80。fixed与fixed128x18相同。
定长浮点型ufixedMxN无符号的定长浮点型,其中M表示按类型取的位数,N表示小数点。M应该能被8整除,从8到256。N可以是0到80。fixed与fixed128x18相同。

2.地址类型

地址类型表示以太坊地址,长度为20字节。地址可以使用.balance方法获得余额,也可以使用.transfer方法将余额转到另一个地址

address x = 0x212;
address myAddress = this;

if (x.balance < 10 && myAddress.balance >= 10) 
    x.transfer(10);

3.引用类型/复合数据类型

Solidity中,有一些数据类型由值类型组合而成,相比于简单的值类型,这些类型通常通过名称引用,被称为引用类型

引用类型包括:

  • 数组 (字符串与bytes是特殊的数组,所以也是引用类型)
  • struct (结构体)
  • map (映射)

七、变量

Solidity 支持三种类型的变量:

  • 状态变量 – 变量值永久保存在合约存储空间中的变量。
  • 局部变量 – 变量值仅在函数执行过程中有效的变量,函数退出后,变量无效。
  • 全局变量 – 保存在全局命名空间,用于获取区块链相关信息的特殊变量

Solidity 是一种静态类型语言,这意味着需要在声明期间指定变量类型。每个变量声明时,都有一个基于其类型的默认值。没有undefinednull的概念。

1.状态变量

变量值永久保存在合约存储空间中的变量。

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData;      // 状态变量
   constructor() public {
      storedData = 10;   // 使用状态变量
   }
}

2.局部变量

变量值仅在函数执行过程中有效的变量,函数退出后,变量无效。函数参数是局部变量。

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // 状态变量
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = a + b;
      return result; // 访问局部变量
   }
}

示例

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // 状态变量
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = a + b;
      return storedData; // 访问状态变量
   }
}

可以使用 Solidity 第一个程序中的步骤,运行上述程序。

输出

0: uint256: 10

3.全局变量

这些是全局工作区中存在的特殊变量,提供有关区块链和交易属性的信息。

名称返回
blockhash(uint blockNumber) returns (bytes32)给定区块的哈希值 – 只适用于256最近区块, 不包含当前区块。
block.coinbase (address payable)当前区块矿工的地址
block.difficulty (uint)当前区块的难度
block.gaslimit (uint)当前区块的gaslimit
block.number (uint)当前区块的number
block.timestamp (uint)当前区块的时间戳,为unix纪元以来的秒
gasleft() returns (uint256)剩余 gas
msg.data (bytes calldata)完成 calldata
msg.sender (address payable)消息发送者 (当前 caller)
msg.sig (bytes4)calldata的前四个字节 (function identifier)
msg.value (uint)当前消息的wei值
now (uint)当前块的时间戳
tx.gasprice (uint)交易的gas价格
tx.origin (address payable)交易的发送方

Solidity 变量名

在为变量命名时,请记住以下规则

  • 不应使用 Solidity 保留关键字作为变量名。例如,breakboolean变量名无效。
  • 不应以数字(0-9)开头,必须以字母或下划线开头。例如,123test是一个无效的变量名,但是_123test是一个有效的变量名。
  • 变量名区分大小写。例如,Namename是两个不同的变量。

八、变量作用域

局部变量的作用域仅限于定义它们的函数,但是状态变量可以有三种作用域类型。

  • Public – 公共状态变量可以在内部访问,也可以通过消息访问。对于公共状态变量,将生成一个自动getter函数
  • Internal – 内部状态变量只能从当前合约或其派生合约内访问。
  • Private – 私有状态变量只能从当前合约内部访问,派生合约内不能访问。

示例

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;

   function x() public returns (uint) {
      data = 3; // 内部访问
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); // 外部访问
   }
}
contract D is C {
   uint storedData; // 状态变量

   function y() public returns (uint) {
      iData = 3; // 派生合约内部访问
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = a + b;
      return storedData; // 访问状态变量
   }
}

九、运算符

1.算术运算符

Solidity 支持的算术运算符,如下表所示:

假设变量A的值为10,变量B的值为20。

序号运算符与描述
1+ (加) 求和 例: A + B = 30
2– (减) 相减 例: A – B = -10
3*** (乘)** 相乘 例: A * B = 200
4/ (除) 相除 例: B / A = 2
5% (取模) 取模运算 例: B % A = 0
6++ (递增) 递增 例: A++ = 11
7— (递减) 递减 例: A– = 9
8**(乘方) 例:2**2 = 4

示例

下面的代码展示了如何使用算术运算符。

pragma solidity ^0.5.0;

contract SolidityTest {
   constructor() public{
   }
   function getResult() public view returns(uint){
      uint a = 1; 
      uint b = 2;
      uint result = a + b; // 算术运算
      return result; 
   }
}

输出

0: uint256: 3

2.比较运算符

Solidity 支持的比较运算符,如下表所示:

序号运算符与描述
1== (等于)
2!= (不等于)
3> (大于)
4< (小于)
5>= (大于等于)
6<= (小于等于)

下面的代码展示了如何使用比较运算符。

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory _uintAsString) {

      if (_i == 0) {   // 比较运算符
         return "0";
      }
      uint j = _i;
      uint len;

      while (j != 0) {  // 比较运算符
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);// 访问局部变量
   }
}

输出

0: string: 3

3.逻辑运算符

Solidity 支持的逻辑运算符,如下表所示:

假设变量A的值为10,变量B的值为20。

序号运算符与描述
1&& (逻辑与) 如果两个操作数都非零,则条件为真。 例: (A && B) 为真
2|| (逻辑或) 如果这两个操作数中有一个非零,则条件为真。 例: (A || B) 为真
3! (逻辑非) 反转操作数的逻辑状态。如果条件为真,则逻辑非操作将使其为假。 例: ! (A && B) 为假

示例

下面的代码展示了如何使用逻辑运算符

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; // 状态变量
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = a + b;
      return integerToString(storedData); // 访问状态变量
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {

      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (!(j == 0)) {  // 逻辑运算符
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

输出

0: string: 3

4.位运算符

Solidity 支持的位运算符,如下表所示:

假设变量A的值为2,变量B的值为3。

序号运算符与描述
1& (位与) 对其整数参数的每个位执行位与操作。 例: (A & B) 为 2.
2| (位或) 对其整数参数的每个位执行位或操作。 例: (A | B) 为 3.
3^ (位异或) 对其整数参数的每个位执行位异或操作。 例: (A ^ B) 为 1.
4~ (位非) 一元操作符,反转操作数中的所有位。 例: (~B) 为 -4.
5<< (左移位)) 将第一个操作数中的所有位向左移动,移动的位置数由第二个操作数指定,新的位由0填充。将一个值向左移动一个位置相当于乘以2,移动两个位置相当于乘以4,以此类推。 例: (A << 1) 为 4.
6>> (右移位) 左操作数的值向右移动,移动位置数量由右操作数指定 例: (A >> 1) 为 1.

示例

下面的代码展示了如何使用位运算符

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 2; // 局部变量
      uint b = 2;
      uint result = a & b;  // 位与
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);// 访问局部变量
   }
}

输出

0: string: 2

5.赋值运算符

Solidity 支持的赋值运算符,如下表所示:

序号运算符与描述
1= (简单赋值) 将右侧操作数的值赋给左侧操作数 例: C = A + B 表示 A + B 赋给 C
2+= (相加赋值) 将右操作数添加到左操作数并将结果赋给左操作数。 例: C += A 等价于 C = C + A
3−= (相减赋值) 从左操作数减去右操作数并将结果赋给左操作数。 例: C -= A 等价于 C = C – A
4*= (相乘赋值) 将右操作数与左操作数相乘,并将结果赋给左操作数。 例: C *= A 等价于 C = C * A
5/= (相除赋值) 将左操作数与右操作数分开,并将结果分配给左操作数。 例: C /= A 等价于 C = C / A
6%= (取模赋值) 使用两个操作数取模,并将结果赋给左边的操作数。 例: C %= A 等价于 C = C % A

注意 – 同样的逻辑也适用于位运算符,因此它们将变成<<=>>=>>=&=|=^=

下面的代码展示了如何使用赋值运算符。

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(storedData); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      while (j != 0) {
         len++;
         j /= 10; // 赋值运算
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;// 赋值运算
      }
      return string(bstr);  // 访问局部变量
   }
}

输出

0: string: 10

6.条件运算符

Solidity 支持条件运算符。

序号运算符与描述
1? : (条件运算符 ) 如果条件为真 ? 则取值X : 否则值Y

示例

下面的代码展示了如何使用这个运算符

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 1; // 局部变量
      uint b = 2;
      uint result = (a > b? a: b);  //条件运算
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

输出

0: string: 2

十、循环语句

1.while 循环

语法

Solidity 中, while循环的语法如下:

while (表达式) {
   被执行语句(如果表示为真)
}

示例

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 10; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {

      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) { // while 循环
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

输出

0: string: 12

2.do...while 循环

语法

Solidity 中, do…while循环的语法如下:

do {
   被执行语句(如果表示为真)
} while (表达式);

注意: 不要漏掉 do 后面的分号

示例

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 10; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {

      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      do {                   // do while 循环 
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      while (_i != 0);
      return string(bstr);
   }
}

输出

0: string: 12

3.for 循环

语法

Solidity 中, for循环的语法如下:

for (初始化; 测试条件; 迭代语句) {
   被执行语句(如果表示为真)
}

示例

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }

   function getResult() public view returns(string memory){
      uint a = 10; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }

   function integerToString(uint _i) internal pure 
      returns (string memory) {
      if (_i == 0) {
         return "0";
      }
      uint j=0;
      uint len;
      for (j = _i; j != 0; j /= 10) {  //for循环的例子
         len++;         
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//访问局部变量
   }
}

输出

0: string: 12

4.break 和 continue

  • continue – 跳出本次循环,继续执行接下来的循环
  • break – 跳出循环(或跳出代码块)

break 示例

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {

      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (true) {
         len++;
         j /= 10;
         if(j==0){
            break;   // break 语句跳出循环
         }
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

输出

0: string: 3

continue 示例

pragma solidity ^0.5.0;

contract SolidityTest {
   uint storedData; 
   constructor() public{
      storedData = 10;   
   }
   function getResult() public view returns(string memory){
      uint n = 1;
      uint sum = 0;

      while( n < 10){
         n++;
         if(n == 5){
            continue; // 当n的和是5时,跳过n。
         }
         sum = sum + n;
      }
      return integerToString(sum); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {

      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;

      while (true) {
         len++;
         j /= 10;
         if(j==0){
            break;   // break跳出循环
         }
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;

      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

输出

0: string: 49	

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐