一.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 -vv10.16.3> npm -v6.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,
set
和get
函数可用于修改或检索变量的值。
导入文件
上面的例子没有import语句,但是Solidity 支持与JavaScript非常相似的导入语句。
下面的语句从“filename”导入所有全局符号。
import "filename";
下面的示例,创建一个新的全局符号symbolName
,它的成员都是来自“filename”的全局符号。
import * as symbolName from "filename";
要从当前目录导入文件x,请使用import "./x"
。如果不指定当前路径,可能会在全局“include目
;录”中引用另一个文件。
保留关键字
下面是 Solidity 语言中的保留关键字:
abstract | auto | case | default | final |
after | immutable | implements | in | inline |
alias | let | macro | match | mutable |
apply | null | of | override | partial |
catch | promise | reference | relocatable | sealed |
copyof | sizeof | static | supports | switch |
define | try | typedef | typeof | unchecked |
四、第一个程序
为简单起见,我们使用在线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.值类型
类型 | 保留字 | 取值 |
---|---|---|
布尔型 | bool | true/false |
整型 | int/uint | 有符号整数/无符号整数。 |
整型 | int8 to int256 | 8位到256位的带符号整型数。int256与int相同。 |
整型 | uint8 to uint256 | 8位到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) x.transfer(10);
3.引用类型/复合数据类型
Solidity中,有一些数据类型由值类型组合而成,相比于简单的值类型,这些类型通常通过名称引用,被称为引用类型。
引用类型包括:
- 数组 (字符串与bytes是特殊的数组,所以也是引用类型)
- struct (结构体)
- map (映射)
七、变量
Solidity 支持三种类型的变量:
- 状态变量– 变量值永久保存在合约存储空间中的变量。
- 局部变量– 变量值仅在函数执行过程中有效的变量,函数退出后,变量无效。
- 全局变量– 保存在全局命名空间,用于获取区块链相关信息的特殊变量。
Solidity 是一种静态类型语言,这意味着需要在声明期间指定变量类型。每个变量声明时,都有一个基于其类型的默认值。没有undefined
或null
的概念。
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 保留关键字作为变量名。例如,
break
或boolean
变量名无效。 - 不应以数字(0-9)开头,必须以字母或下划线开头。例如,
123test
是一个无效的变量名,但是_123test
是一个有效的变量名。 - 变量名区分大小写。例如,
Name
和name
是两个不同的变量。
八、变量作用域
局部变量的作用域仅限于定义它们的函数,但是状态变量可以有三种作用域类型。
- 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 | ” /> 输出
十、循环语句1.while 循环语法 Solidity 中, while循环的语法如下:
示例
输出
2.do…while 循环语法 Solidity 中, do…while循环的语法如下:
示例
输出
3.for 循环语法 Solidity 中, for循环的语法如下:
示例
输出
4.break 和 continue
break 示例
输出
continue 示例
输出
|