以太坊是一个分布式计算网络,提供了运行智能合约的分布式平台。
智能合约是在以太坊虚拟机上运行的应用程序。
以太坊拥有多种高级语言可以用来编写智能合约,最流行的是solidity,基于javascript。
remix ide是一个智能合约开发的IDE
第一个代码
pragma solidity ^0.4.0;//向上兼容到0.5.0以下contract Demo{string str = "hello world";function getStr() public view returns(string){return str;}function setStr(string t_str) public{str = t_str;}function pureTest(string t_str) public pure returns(string){return t_str;}}
节省gas(或者说ether)的两个关键字
view修饰的函数可以读取状态变量但是不能改
pure修饰的函数不能改也不能读状态变量
int/uint类型
整形变量:int8/uint8~int256/uint256,步长为8,int/uint=int256/uint256,即256位二进制位
数组
固定长度字节数组:bytes1,bytes2…bytes32,步长为1,1byte=8bit;正好和int/uint位数对应
bytes类型的变量可以通过length属性访问长度
不定长字节数组:bytes arr=new bytes(2);
//分配两个字节数组
不定长数组长度可以改变,自动在右侧添加0x00填充
可通过push操作向后面添加数据,push的过程会自动增加数组长度
bytes arr = new bytes(2);function setArr() public{arr[0]=0x01; arr[1]=0x02;}function getArr() public view returns(uint){return arr.length;} function pushData() public {//arr.length = 3;arr.push(0x03);}
string类型
string没有length属性,也不可以通过下标的方式来获取其中元素。
注意string中存储中文一个占3个字节
string转bytes
string str="hello world";function getLength() public view returns (uint){return bytes(str).length;}function getStr() public view returns (bytes){return bytes(str);}function getChar() public view returns (bytes1){return bytes(str)[0];}function opChar() public {bytes(str)[0] = 'x';}
bytes转string
bytes byteStr = new bytes(2);function init() public {byteStr[0]=0x68; byteStr[1]=0x65;}function getStr() public view returns (string){return string(byteStr);}
bytes10转string
function bytes10ToString(bytes10 byteStr) public pure returns (string){uint count=0;for (uint i=0; i<byteStr.length; i++){if (byteStr[i]!=0x00){count++;}else{break;}}bytes memory bytesStr = new bytes(count);for (uint j=0; j<count; j++){bytesStr[j] = byteStr[j];}return string(bytesStr);}
固定长度字节数组截取
bytes8 byteStr = 0x1234567891234567;function getbyte1() public view returns (bytes1){return bytes1(byteStr);}function getbyte10() public view returns (bytes10){return bytes10(byteStr);}
固定长度字节数组转可变长度字节数组
bytes8 byteStr = 0x1234567891234567;function getDynamicStr() public view returns (bytes){bytes memory tStr = new bytes(byteStr.length);for (uint i=0; i<byteStr.length; i++){tStr[i] = byteStr[i];}return tStr;}
int/uint数组
一维固定长度数组
int[5] arr = [];
固定长度数组只可以访问length,不能改变
二维固定长度数组
int[2][3] arr;
表示有3个元素,每个元素占两个int.
当在访问下标是又变成[i][j],第i行第j列
一维可变长度数组
int[] arr = [];
可变长度数组可以修改length, push元素
二维可变长度数组
int[][] arr = [[1,2], [3,4], [5,6]];
可以修改外层长度和内层长度,但是没有push方法
地址address是用uint160来存储的
address public p;address public p = 0xca35b7d915458ef540ade6068dfe2f44e8fa733c;function getAddress() public view returns (uint160){return uint160(p);}uint public pData = 1154414090619811796818182302139415280051214250812;function getAddress2() public view returns (address){return address(pData);}
payable关键字代表可以通过这个函数给合约地址转账,默认转账单位是位
this代表合约的地址
通过合约/账户地址的属性balance可以获取合约/账户的ether
通过外部账户向合约转账
contract Demo{function getBalance() payable public {}function showBalance() public view returns (uint){return uint(this.balance);}function showRandBalance(address p) public view returns (uint) {return uint(this.balance);}}
外部账户转账给其他账户
contract Demo{function trans() public payable {address p = 0xca35b7d915458ef540ade6068dfe2f44e8fa733c;p.transfer(msg.value);}}
外部账户转账给合约
contract Demo{function showBalance() public view returns(uint){return this.balance;}function trans2() public payable {this.transfer(msg.value);}function() payable {}}
全局变量
msg.sender:合约的调用者(账户)的地址
msg.value:当前消息附带的以太币
block.number:当前区块块号
block.difficulty:当前块的困难度
mapping用法,注意输入地址的时候要用双引号
contract Demo{mapping(address => uint) idMap;mapping(uint => string) strMap;uint public pos=0;function func(string str) public {pos++;address p = msg.sender;idMap[p] = pos;strMap[pos] = str;}function getIdByAddress(address ad) public view returns(uint){return idMap[ad];}function getStrById(uint id) public view returns (string) {return strMap[id];}}
多返回值
contract Demo{function test(uint a, uint b) public view returns(uint res1, uint res2){return (a+b, a-b);}function test2(uint a, uint b) public view returns(uint res1, uint res2){res1 = a+b;res2 = a-b;}}
solidity中的构造函数只能有一个
老版本中可以用和类名相同的函数名作为构造函数
新版本中可以使用关键字constructor作为构造函数
contract Demo{int public n;// function Demo() public {//n = 5;// }constructor() public{n=6;}}
require函数,当判断条件为真,继续执行下面语句;否则下面语句都不执行
modefier关键字
contract Demo{address public owner;int public n;constructor() public {owner = msg.sender;}modifier onlyOwner() {require(owner == msg.sender);_; //动态添加代码}function func(int tn) onlyOwner {n = tn;} }
modifier代码重用示例
contract Demo{uint public level=5;uint public flag=0;modifier func(uint needLevel){require(level>=needLevel);_;}function func1() func(2){flag=1;}function func2() func(6){flag=2;}}
深入modifier, 执行顺序:x=1,x=3,x=5,x=4,x=2
contract Demo{int public x;modifier mod1() {x=1;_;x=2;}modifier mod2() {x=3;_;x=4;}function test() mod1 mod2 {x=5;}}
继承使用is关键字,对象可以连续继承,也可多继承:
contract son is father, mother
父类中的变量不加修饰的时候,子类中默认(public)可以访问
父类对象中的internal修饰的成员,子类中也可以访问。在合约内可访问,外部不可访问。
父类对象中的private修饰的成员,子合约中不可以访问
父类对象中的external修饰的成员不可以在合约中访问或调用,只能在合约外部访问或调用
说明
相对于C++
public->public
private->private
internal->protected
C++中的类在这里叫合约
constant在函数中的用法被抛弃,在全局变量中,只用于byte1-byte32,int,uint,string,表示数据不可被修改
public修饰的成员会默认生成getter方法,供外部调用
比如,其中的x方法就是自动生成的,但是当我们显示写了这个函数后,就不会自动生成默认的x方法了
contract Demo{int public x;function x() external view returns(int) {return x;}}
合约的销毁
contract Demo{address owner;int x = 0;constructor() public {owner = msg.sender;}function increase() public returns (int) {x = x+10;return x;}function destruct() public {if (owner == msg.sender){selfdestruct(owner);}}}
结构体的使用
contract Demo{struct student{string name;uint grade;}function func1() public view returns (string, uint) {student memory t = student("name1", 100);return (t.name, t.grade);} function func2() public view returns (string, uint) {student memory t = student({name:"name2", grade:99});return (t.name, t.grade);}}
不能包含本身,但是可以是动态的数组或者mapping等类型
注意memory定义的结构体,不可以直接操作结构体中的mapping
但是可以操作在合约内部默认storage定义的结构体中的mapping
storage可以看成C++中的引用
contract Demo{struct student{string name;uint grade;}student stu;function test(student storage st) internal {student storage s = st;s.grade = 100;}function func() public view returns(uint) {test(stu);return stu.grade;}}
memory类型的变量传递是通过指针来传递的
枚举类型
enum day{monday, thusday, wendesday}
综合示例:众筹功能代码
pragma solidity ^0.4.0;contract Charity{struct Payer{address payerAddress;uint payMoney;}struct Needer{address neederAddress;uint goal;uint curMoney;uint payerCount;mapping(uint => Payer) map;}uint neederCount;mapping(uint => Needer) needMap;function newNeeder(address _neederAddress, uint _goal) public {neederCount++;needMap[neederCount] = Needer(_neederAddress, _goal, 0, 0);}function pay(address _address, uint _neederPos) payable {Needer storage _needer = needMap[_neederPos];_needer.curMoney += msg.value;_needer.payerCount++;_needer.map[ _needer.payerCount] = Payer(_address, msg.value);}function trans(uint _neederPos){Needer storage _needer = needMap[_neederPos];if (_needer.curMoney <= _needer.goal){_needer.neederAddress.transfer(_needer.curMoney);}}function show(uint _neederPos) public view returns (uint, uint, uint) {Needer storage _needer = needMap[_neederPos];return(_needer.goal, _needer.curMoney, _needer.payerCount);}}