创作不易,对你有所帮助的话,你的三连,是对笔者(小k)最大的支持与鼓励。
转载:请备注原文链接!!!
区块链“智能合约”从基础到入门——掌握本篇就够啦!!!
一丶开发环境:
(1)Remix
——可能需要翻墙,比较慢,但是方便直接用
(2)WeBASE-Front
——代码高亮美观,使用方便,但需要自行搭建环境
二丶Solidity概述
- 智能合约Smart Contract :一套旨在以信息化方式传播、验证或执行合同的计算机协议
- 以太坊虚拟机Ethereum Virtual Machine : 一个轻量级的虚拟机,用于在网络上运行智能合约。EVM的构建是为了模拟可由物理CPU执行的操作,还负责以太坊的许多关键功能
- Solidity :以太坊推出用来编写智能合约的编程语言
三丶基本类型
(1)Int
- 用intx和uintx来申明,其中x是一个8-256之间的8的倍数,表示有多少个bit。如int8 ,uint32。
- 比较:<=,<,==,!=,>=,>
- 位运算:&,|,^,~,<<,>>
- 数值运算:加减乘除,%取余,**指数。0的0次方等于1
- type(x).min和type(x).max给出该类型的上下界
- 溢出会被截断
(2)address
可以是合约地址,也可以是账户地址
- address与address payable:储存160个bit长的信息,也就是一个地址。
- address payable 相对于address多了可以向此地址转账的功能
- .balance给出该地址的金额
- .transfer()和.send()对于一个address payable代表向其转账
address payable x = address(0x123);
address myAddress = address(this);
if(x.balance < 10 && myAddress.balance >= 10){
x.transfer(10);// or x.send(10)
申明一个地址为0x123的可接受转账的地址x
获取当前合约的地址myAddress
判断x的账户余额是否小于10并且当前合约的地址大于等于10
x.transfer(10)代表的是当前合约向x转账
(3)Bool
- 用bool x来申明
- 操作符 !,&&,||,==,!=
- &&操作和||操作是短路运算
(4)数组
- 定长数组:bytes1,bytes3, … bytes32表示储存了1个,2个…32个byte的数组
- bytes32 x:可以用下标访问
- 具有位运算
- 常规数组和C++类似:int a[10]
- 也可以申明动长数组:int a[]。动长数组有a.length返回长度, a.push(x) 在最后添加一个新元素x并返回新长度
(5)Char and String
- 用char来申明字符变量
- string相当于一个char的数组,但是目前的版本不支持使用.length以及下标访问,需要转化为bytes访问字节形式的内容
string s;
bytes(s).length
bytes(s)[7] = 'x'
(6)Mapping
- 用mapping(_keytype => _valuetype),基本上就是个哈希表
mapping(address => uint) public balance;
function update(uint newBalance) public {
balances[msg.sender] = newBalance;
}
申明一个balance从地址映射的uint的值
uint没有具体申明,默认为uint256
申明一个函数(后面详细介绍)
在整体上,智能合约不是一个完成的程序,需要被其他人调用自己才能实现真正的动作
msg.sender是调用者地址(后面会详细介绍)
balance[address] = uint256(具体问题,具体分析)
(7)Struct
struct Voter{//投票者
uint weight;//权重
bool voted;//是否投过票
address delegate;//地址
uint vote;//票数
}
四丶预定义变量
(1)block
区块的相关信息
- block.blockhash(uint blockNumber) retruns(bytes32) 指定区块的区块哈希,不推荐,建议使用blockhash(uint blockNumber)代替
- block.coinbase(address):挖出当前区块的矿工地址
- block.difficulty(uint):当前区块难度
- block.gaslimit(uint):当前区块gas限额
- block.number(uint):当前区块号
- block.timestamp(uint):自uinx epoch其当前区块以秒计的时间戳
(2)msg
- msg.data(bytes):完整的calldata
- msg.gas(uint):剩余gas,不推荐,建议使用gasleft()代替
- msg.sender(address):当前调用的消息发送者
- msg.sig(bytes4):calldata的前4个字节(也就是函数标识符)
- msg.value(uint):随消息发送的wei的数量
五丶Event
- 用来触发EVM中的log记录,使用如下语句声明和使用
event X(<parameter types>) //定义
emit X(<parameter>) //使用
event Send(address sender, address receive, uint amount); //事件
function sent() public payable{
//....
emit Send(msg.sender, msg.receive,msg.value);//触发事件
}
调用sent函数,会触发Send事件
返回一个事件:谁给谁转了多少钱
六丶控制结构
- C++/js里面的大部分结构都有,包括 if,else,while,do,for,break,continue,return,? :
- 没有goto
- 在soldity中,其他类型是不会自动转换为bool的,所以if(1){}这样是不合法的
七丶Units
(1)EtherUnits
- wei,szabo,finney,ether
- 1 ether = 1000 finney = 106 szabo = 1018 wei
(2)TimeUnits
- 一个数值后跟seconds,minutes,hours,days,weeks是一个时间单位,默认秒(1 == 1seconds)
如何要把一个变量转为单元,类似x * ether
八丶Contracts
- 合约Contract是一个代码的基本组成部分,和class类似
- 一个Contract里会包含状态变量,函数,函数修饰器和事件
- Contract也有继承与多态
(1)状态变量
- 储存在合约中的信息,如总量,个人的余额等等
contract C1{
uint data; //State Variable
}
data就是一个状态变量
(2)函数
function (<parameter types>) [internal | external | public] [pure | view | payable] [returns (<return types>)]
{}
- view:一个函数如果不会改变状态(包括send ether)则可以被申明为view
- pure:一个函数如果是view,并且不会从状态中读取数据,则可以被申明为pure
- 如果一个函数什么都不return就相当于c++中的void 函数 不写returns即可
- reutrn可以返回一个元组,用括号括起来
- internal | external | public是不可选,必须写
- pure | view | payable是可选的,可以不写
- returns是可选的,可以不写
(3)Payable
- 表示调用的时候可以发过来一个货币
- msg.sender是发币地址,msg.value是数量
function deposit() payable returns(address addr,uint amount,bool success){
return(msg.sender,msg.value,this.send(msg.value));
}
没有申明可见性,默认是public
返回发送者地址,发了多少代币,是否发送成功
this代指本合约地址
这里是指sender向此合约打款
(4)visibility
- 函数有四种可见性:external,public,internal,private
- external表示只能从外部调用,如果想从内部调用,必须用this.f
- public 则表示内外都可以
- internal 可以被自己或衍生类调用
- private则只能自己调用
- 变量的visibility不能是external
internal ,private只是针对于外部合约而言,在区块链外部还是能查看相关数据的
visibility不写,默认为public
(5)构造函数
一个合约有至多一个构造函数,在构造时会执行一次
Contract A{
bytes32 name;
constructor(bytes32 _name)public{
name = _name;
}
}
(6)Receive Ether
- 一个合约有至多一个Receive ether函数,用如下语句声明。
- receive() external payable{…}
- 没有function关键字
- 当收到ether时执行(.send()和.transfer())
//This contract keeps all Ether sent to it with no way
//to get it back
contract Sink{
event Received(address, uint);
receive() external payable{
emit Received(msg.sender, msg.value);
}
}
contract Source{
function sending(uint amount) internal payable {
address payable x = address(Sink);
x.transfer(10); // will call receive
}
}
(7)fallback
- 一个合约至多有一个fallback函数,fallback函数是缺省函数,当调用了一个该合约不存在的函数时会执行fallback。
- 用如下语句声明,同样没有function关键词
- fallback() external [payable]
(8)require
- 预定义函数
- require(bool condition)
- require(bool condition,string message)
- 如果条件不满足,会撤销更改(包括转账)
要求某种条件必须成立
如果不满足,就全部不执行,所有操作将会回退(撤销)
(9)函数修饰器function modifier
contract owned{
address owner;
mapping(address => bool) registeredAddresses;
uint price;
constructor()public {
owner = msg.sender;
}
modifier onlyOwner{
require(msg.sender == owner);
_;//如果满足上述条件,函数将会在这里接着执行
}
modifier costs(uint price) {//modifier可以接受参数
if(msg.value >= price){
_;//满足条件,函数将会在这里接着执行
}
}
function register() public payable costs(price){
registeredAddressed[msg.sender] =true;
}
function changePrice(uint _price) public onlyOwner{
price = _price;
}
}
modifier中的_处,被修饰的函数将会在此执行
(10)继承
- 用is来实现合约继承
- 继承的子合约会继承父合约所有非private的东西,包括modifier
- Contract A{…}
- Contract B is A {…}
- 如果要override一个函数,在被覆盖的函数声明时必须加virtual关键字,在覆盖的函数后加override
- 如果希望能被在此override,也要加virtual关键字
- 函数默认调用最衍生的类定义的函数
- 如果要调用父类的函数,使用super.f()
- 函数override的时候必须保持同样的可见性,或从external变成public
- 另一个参数只能变得更加严格,即non-payable(也就是无参数)可以被override为view,view可以变成pure。payable是特殊的,必须保持为payable
contract Base{
function foo() virtual external view{}
}
contract Middle is Base{}
contract Inherited is Middle{
function foo() virtual override public pure{}
}
external -> public
view -> pure
符合override后的函数修饰
(11)Abstract Contracts
- Abstract contracts是未实现的Contract。可以用来做父类
- 用来使用什么功能,更广泛的父类
abstract contract A{
function num() public pure virtual returns(uint);
}
contract B is A{
function num() public pure override returns(uint){
return 0;
}
}
(12)Interface
- 和Abstract Contract 差不多,但是更严格一点
- 不能实现任何函数
- 不能定义构造函数
- 不能定义任何变量
- 不能继承(可以被继承)