定义:
智能合约是一段程序(代码和数据的集合),可以部署在支持智能合约的区块链网络(比如以太坊)上运行。.
智能合约是确定性的“单线程”的程序,确定性的程序在所有节点上运行的结果是一样的,区块链用来确定智能合约运行的顺序,用分布式共识的方法确定最终的数据完全一样,也就是有“可验证性”。.
程序版本Pragma
添加版本可以避免编译器和代码版不兼容造成的问题。
pragma solidity ^0.4.0;
源文件不能用低于0.4.0版本的编译器编译,也不能用0.5.0版本及以上版本的编译器编译。
pragma solidity >=0.4.0 <0.6.0;
源代码是为Solidity version 0.4.0及以上版本编写的,但不包括version 0.6.0及以上版本。
pragma指令只对自己的源文件起作用,如果把文件B导入到文件A,文件B的pragma将不会自动应用于文件A。
import
import “filename”;
意思是从“filename”文化导入所有全局符号。
import * as symbolName from “filename”;
意思是创建一个新的全局符号symbolName,它的成员都是来自“filename”的全局符号
导入文件x,要使用import “./x”,或import “x”
值类型
整数型(int、uint)
有符号(int) 和无符号(uint) 整数,以8比特为单位,从 int8 到 int256,从 uint8 到 uint256。当没有明确说明⻓度时,默认使用256比特。
布尔型(bool)
布尔值 true 或 false,可以使用逻辑操作符!(否)、&&(与)、| | (或)、 == (等于)和!=(不等于)。
定点数类型(fixed、ufixed)
有符号(fixed)和无符号(ufixed)的定点数类型,和浮点类型不一致。
地址类型:Address
包括address和address payable, address 保存-个20字节的值,两者可以相互转化。
payable地址类型可使用.balance方法获得余额,
也可使用.transfer 方法将余额转到另一个地址。(已被舍弃)可以使用call()():如address.call.value(amount)(“”)
其他方法包括:
.send( )
.call( )
.delegatecall( )
.callcode ( )
运算符
算数运算符
比较运算符
逻辑运算符
位运算符
赋值运算符
条件运算符
地址类型
地址类型:在solidity中,地址类型表示以太坊地址,长度为20字节,通过在变量前写上address来表示这是一个地址变量。地址可以使用.balance方法获得余额,也可以使用.transfer方法将余额转到另一个地址。
引用类型
数组
数组可以是固定大小的,也可以是动态长度的数值, 可以包含任意类型的数据(这里是可以定义任意数据类型的数组),数组是存储同类型元素的有序集合。数组中的元素由索引访问,索引值从0开始。
声明数组格式是:type [arraySize] arrayName
例:
类型为T
固定长度数组,为K的数组声明为:T [K] 数组名 ,
动态数组,长度为动态,声明为:T [] 数组名
push: 它用来附加新的元素到数组末尾,这个函数将返回新的数组长度,只存在于动态数组中。
length:表示元素数量或者说⻓度。
枚举
枚举在编程语言中常作为一个集合存在,在solidity中,枚举将一个变量的取值限制为几个预定义值中的一个,是一种用户自定义类型。枚举类型用enum关键字来定义
如:
enum FreshJuiceSize{SMALL,MEDIUM,LARGE}
定义了一个名称为FreshJuiceSize的枚举类型,{}中的SMALL,MEDIUM,LARGE被称为枚举元素,变量的取值也只限于这里列举出来的枚举元素。
枚举类型.枚举元素 格式来引用枚举类型里的值。
结构体Struct
定义结构体
要定义结构,使⽤用struct关键字。struct关键字定义了了一个新的数据类型,包含多个成员。
struct语句句的格式如下:
struct struct_name {
type1 type_name_1;
type2 type_name_2;
type3 type_name_3;
}
访问结构体成员,使⽤用成员访问操作符(name .type_name_2)
映射
映射类型在声明时的形式为
mapping(_KeyType => _ValueType)
_KeyType可以是除了映射、变⻓数组、合约、枚举以及结构体以外的几乎所有类型。 _ValueType可以是包括映射类型在内的任何类型。映射本质上是存储和查找数据所用的键-值对。
函数
函数由关键字 function 声明,后面跟函数名、参数、可⻅性、修饰符、返回值的定义。
function funcationName(<parameters>)
{public|private|internal|external} [pure|constant|view|payable] [returns (<return types>)]
funcationName: 定义函数的名字。用于其他合约或者本合约内部调用函数
parameters: 在函数名之后,我们需要指定传递给函数的参数,包括参数的名称和类型
构造函数
构造函数是使用construct关键字声明的特殊函数,用于初始化合约的状态变量。合约中构造函数是可选的,可以省略。
构造函数有以下重要特性:
一个合约只能有一个构造函数。
构造函数在创建合约时执行一次,用于初始化合约状态。
在执行构造函数之后,合约最终代码被部署到区块链。合约最终代码包括公共函数和可通过公共函数访问的代码。构造函数代码或仅由构造函数使用的任何内部方法不包括在最终代码中。
构造函数可以是公共的,也可以是内部的。
内部构造函数将合约标记为抽象合约。
如果没有定义构造函数,则使用默认构造函数。
Contract Test{
constructor() public {}
}
contract X{
uint data;
constructor (uint _data){
data=_data;
}
}
可见性
public、private、internal、external用于设定函数的可见范围。
public:public 是默认值,设定为 public 的函数可以被其他合约和本合约内部调用。
external:类似于 public 函数,唯一的区别在于不能在合约内部调用,除非在调用时申明关键字 this。
internal:internal 函数只能被合约内部的函数调用。
private:类似于 internal 函数,唯一的区别在于 private 函数不能被当前合约所派生的子合约调用。
状态可变性
view/constant: 当函数被定义为 view,意味着它不对任何状态进行修改,只能读取数据不能更改数据。constant 和 view 是一样的。(0.5.0之后)
pure:pure 函数不会读取或写入任何数据。函数只能处理参数,返回值给调用方。
payable:payable 函数用于接受外部的支付。
全局变量
状态变量–变量值永久保存在合约存储空间中的变量。
局部变量–变量值仅在函数执行过程中有效,函数退出后,变量无效。
全局变量–保存在全局命名空间,用于获取区块链相关信息的特殊变量。
msg.sender指的是当前调用者(或智能合约)的 address 被所有函数调用,存储数据至映射的方法和将数据存储在数组相似合约在区块链上需要被调用,所以 msg.sender 总是存在
变量作用域
从solidity 0.5.0 之后,变量将会从它们被声明之后可⻅,直到一对{}块的结束。变量值仅在函数执行过程中有效的变量,函数退出后,变量无效。函数参数是局部变量。
函数重载
同一个作用域内,相同函数名可以定义多个函数。这些函数的参数(参数类型或参数数量)必须不一样。仅仅是返回值不一样不被允许。
数据位置
Solidity 提供4种类型的数据位置 Storage Memory Calldata Stack
Storage:
该存储位置存储永久数据,可以被合约中的所有函数访问。保存在存储区(Storage)中的变量,以智能合约的状态存储,并且在函数调用之间保持久性。与其他数据位置相比,存储区数据位置的成本较。
Memory:
内存位置是临时数据,比存储位置便宜。它只能在函数中访问。通常,内存数据用于保存临时变量,以便在函数执行期间进行计算。一旦函数执⾏完毕,它的内容就会被丢弃。
calldata:只读的不可修改的且不会永久存储的位置,存储所有传入的函数执行数据,包括函数参数。外部函数的参数(非返回参数)的数据位置被强制指定为calldata。
- 状态变量存储在Storage,无需声明存储在Storage
- 局部变量必须申明,否则自动储存为Storage
- 函数参数存储在Memory
Stack:
堆栈是由EVM (Ethereum虚拟机)维护的非持久性数据。EVM使用堆栈数据位置在执行期间加载变量。堆栈位置最多有1024个级别的限制。
函数修饰器
函数修饰符用于修改函数的行为。
例如,向函数添加条件限制。创建不带参数的函数修饰符,
modifier onlyOwner {
require (msg.sender ==owner);
_;
}
创建带参数的函数修饰符
modifier costs (uint price){
if(msg.value>=price){
_;
}
}
错误处理
assert(bool condition) -如果不满足条件,此方法调用将导致一个无效的操作码,对状态所做的任何更改将被还原。这个方法是用来处理内部错误的。
require(bool condition) −如果不不满足条件,此方法调用将恢复到原始状态。此方法用于检查输入或外部组件的错误。
require(bool condition, string memory message)−如果不满足条件,此方法调用将恢复到原始状态。此方法用于检查输入或外部组件的错误。它提供了一个提供自定义消息的选项。
revert()−此方法将中止执行并将所做的更改还原为执行前状态。(可用于调用函数前的前置条件判断)
revert(string memory reason) −此方法将中止执行并将所做的更改还原为执行前状态。它提供了一个提供自定义消息的选项。
合约继承
solidity支持单继承和多继承
当一个合约从多个合约继承时,在区块链上只有一个合约被创建,所有基类合约的代码被编译到创建的合约中。
派生合约可以访问父合约的所有非私有成员,包括内部方法和状态变量。
可以使用super关键字或父合约名称调用父合约的函数。
在多重继承的情况下,使用super的父合约函数调用,优先选择被最多继承的合约。
继承的本质代码拷贝,合约继承的语法是is,一个合约可以继承多个合约,用逗号分开。如果继承的合约之间也有父子关系,那么合约要按照先父到子的顺序排序。
比如:
contract X {}
contractAis X {}
contractCis X,A {}
注意:子合约不能访问父合约的private私有成员子合约可以访问父合约所有的非私有成员(包括internal的函数和状态变量):
继承合约要使用is语法,父合约的函数不会被真实的用到所以要使用virtual标签,相应的,子合约要用到override。
contract A{
functionfoo () public pure virtual returns (string memory) {
return"A";
}
}
contractBis A {
//Override A.foo()
functionfoo () public pure virtual override returns (string memory){
return"B";
}
}
如果同时继承两个或多个合约,要用逗号隔开,当调用相同名称的父合约函数时,系统会首先偏向于调用最右边的父合约函数,所以super.foo()会在A.foo()和B.foo()之间进行选择并选择了后者,所以以下代码会返回B:
contract C is A,B {
functionfoo () public pure override (A,B) returns (string memory){
return super.foo() ;
}
}
接口
solidity 0.6.x版本之后,接口类似于抽象合约,,可以使用interface关键字创建,接口只能包含抽象函数,不能包含函数实现。接口的函数只能是外部类型,接口不能有构造函数,接口不能有状态变量。接口可以包含enum,struct定义,可以使用interface_name.访问。
循环语句
while (表达式) {
被执⾏行行语句句(如果表示为真)
}
do {
被执⾏行行语句句(如果表示为真)
} while (表达式);
for (初始化; 测试条件; 迭代语句句) {
被执⾏行行语句句(如果表示为真)
}
continue – 跳出本次循环,继续执⾏行行接下来的循环
break – 跳出循环(或跳出代码块)
库
库类似于合约,但主要作用是代码重用。库中包含了可以被合约调用的函数。
在Solidity中,对库的使用有一定的限制。
以下是库的主要特征:
如果库函数不修改状态,则可以直接调用它们。这意味着纯函数或视图函数只能从库外部调用。
库不能被销毁,因为它被认为是无状态的。
库不能有状态变量。
库不能继承任何其他元素。
库不能被继承。