以太坊是一个分布式计算网络,提供了运行智能合约的分布式平台。
智能合约是在以太坊虚拟机上运行的应用程序。
以太坊拥有多种高级语言可以用来编写智能合约,最流行的是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);
}
}