一、
1、solidity源文件布局
pragma (版本杂注)
2、import
3、solidity值类型
4、solidity引用类型
5、solidity地址类型
address
地址类型存储一个 20 字节的值(以太坊地址的大小);地址类型也有成员变量,并作为所有合约的基础
address payable(v0.5.0引入)
与地址类型基本相同,不过多出了 transfer 和 send 两个成员变量
两者区别和转换
Payable 地址是可以发送 ether 的地址,而普通 address 不能
允许从 payable address 到 address 的隐式转换,而反过来的直接转换是不可能的(唯一方法是通过uint160来进行中间转换)
从0.5.0版本起,合约不再是从地址类型派生而来,但如果它有payable的回退函数,那同样可以显式转换为 address 或者 address payable 类型
二、
1、地址类型成员变量
<address>.balance (uint256)
该地址的 ether 余额,以Wei为单位
<address payable>.transfer(uint256 amount)
向指定地址发送数量为 amount 的 ether(以Wei为单位),失败时抛出异常,发送 2300 gas 的矿工费,不可调节
<address payable>.send(uint256 amount) returns (bool)
向指定地址发送数量为 amount的 ether(以Wei为单位),失败时返回 false,发送 2300 gas 的矿工费用,不可调节
注意:推荐使用transfer而不是send
<address>.call(bytes memory) returns (bool, bytes memory)
发出底层函数 CALL,失败时返回 false,发送所有可用 gas,可调节
<address>.delegatecall(bytes memory) returns (bool, bytes memory)
发出底层函数 DELEGATECALL,失败时返回 false,发送所有可用 gas,可调节
<address>.staticcall(bytes memory) returns (bool, bytes memory)
发出底层函数 STATICCALL ,失败时返回 false,发送所有可用 gas,可调节。静态调用,调用别人方法的过程中没有动态的改变,如果改变则报错。
2、地址成员变量用法
address payable x = address(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10)
x.transfer(10); //谁调用transfer就给谁发
nameReg.call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
3、字符数组(Byte Arrays)
三、
1、枚举(Enum)
pragma solidity >=0.4.0 <0.6.0;
contract Purchase {
enum State { Created, Locked, Inactive }
}
2、数组(Array)
3、数组示例
pragma solidity >=0.4.16 <0.6.0;
contract C {
//pure表示什么都不改的纯函数
function f(uint len) public pure {
//memory必须指定数组大小
uint[] memory a = new uint[](7);
//分配空间是传进来的len
bytes memory b = new bytes(len);
assert(a.length == 7);
assert(b.length == len);
a[6] = 8;
}
}
四、
1、结构(Struct)
pragma solidity >=0.4.0 <0.6.0;
contract Ballot {
struct Voter {
uint weight;
bool voted;
uint vote;
}
}
2、映射(Mapping)
声明一个映射:mapping(_KeyType => _ValueType)
_KeyType可以是任何基本类型。这意味着它可以是任何内置值类型加上字符数组和字符串。不允许使用用户定义的或复杂的类型,如枚举,映射,结构以及除bytes和string之外的任何数组类型。
_ValueType可以是任何类型,包括映射。
pragma solidity >=0.4.0 <0.6.0;
contract MappingExample {
mapping(address => uint) public balances;
//自己可以更改自己的余额
function update(uint newBalance) public {
balances[msg.sender] = newBalance;
} }
contract MappingUser {
function f() public returns (uint) {
MappingExample m = new MappingExample();
//合约调用另一合约
m.update(100);
return m.balances(address(this));
} }
2、Solidity数据位置
3、数据位置总结
六、
1、有问题的合约
ragma solidity ^0.4.0;
contract C {
//没有指定位置时,a自动指向最开始的存储空间,调用f()后传入了数据,a存储的是可变数组的长度,故每次调用都增加1,这是solidity的坑
uint public a;
uint[] public data;
function f() public {
uint[] x;
x.push(2);
data = x;
}
}
七、
1、有问题的合约
pragma solidity ^0.4.0;
contract C {
uint[] x;
function f(uint[] memoryArray) public {
x = memoryArray;
//修改:uint[] memory y = x;
uint[] y = x;
y[7];
//给可变长度数组指定长度,修改:删除y.length = 2;
y.length = 2;
delete x;
//memoryArray是memory类型,y是storage类型,前者不能直接赋给y
y = memoryArray;
delete y;
g(x);
h(x);
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) public {} }
memory类型,只要指定后就不可以再变了
2、问题合约(猜数字游戏)
pragma solidity >0.4.22;
contract Honeypot{
uint luckyNum = 52;
//时间标记
uint public last;
struct Guess{ address player; uint number; }
//每个人猜测的数据存储
Guess[] public guessHistory;
address owner = msg.sender;
//可以转币的方法
function guess(uint _num) public payable{
Guess newGuess;
newGuess.player = msg.sender;
newGuess.number = _num;
//把刚才的gas写入历史
guessHistory.push( newGuess );
//如果猜对则返回你转入数额的两倍
if( _num == luckyNum )
msg.sender.transfer( msg.value * 2 );
//更新当前时间
last = now;
}
}
八、
1、Solidity函数声明和类型
函数的值类型有两类:- 内部(internal)函数和 外部(external) 函数
2、solidity的继承方式,将下图中红圈中的private改为public或internal则可使用。
3、Solidity函数可见性
函数的可见性可以指定为 external,public ,internal 或者 private;对于状态变量,不能设置为 external ,默认是 internal。
4、问题合约
pragma solidity ^0.4.0;
contract C {
uint private data;
function f(uint a) private returns(uint b) { return a + 1; }
function setData(uint a) public { data = a; }
function getData() public returns(uint) { return data; }
function compute(uint a, uint b) internal returns (uint) { return a+b; }
}
contract D {
function readData() public {
C c = new C();
uint local = c.f(7); // 错误:成员 `f` 不可见
c.setData(3);
local = c.getData();
local = c.compute(3, 5); // 错误:成员 `compute` 不可见
}
}
contract E is C {
function g() public {
C c = new C();
uint val = c.compute(3, 5);
}
}
九、
1、Solidity函数状态可变性
2、Solidity函数状态可变性
以下情况被认为是修改状态:
3、 Solidity函数状态可变性
以下被认为是从状态中进行读取:
4、函数修饰器(modifier)
modifier onlySeller() {
require(msg.sender == seller, "Only seller");
//短横线表示先执行前面的require再执行后面的函数
_;
}
十、
1、回退函数(fallback)
2、事件(event)
3、Solidity异常处理
4、 Solidity中的单位
以太币 Ether 单位之间的换算就是在数字后边加上 wei、 finney、 szabo 或 ether 来实现的,如果后面没有单位,缺省为 Wei。例如 2 ether == 2000 finney 的逻辑判断值为 true
5、 Solidity中的时间单位
秒是缺省时间单位,在时间单位之间,数字后面带有 seconds、 minutes、 hours、 days、 weeks 和 years 的可以进行换算,基本换算关系如下:
这些后缀不能直接用在变量后边。如果想用时间单位(例如 days)来将输入变量换算为时间,你可以用如下方式来完成:
function f(uint start, uint daysAfter) public {
if (now >= start + daysAfter * 1 days) { // ... }
}
十一、总结
solidity的讲解和操作。