2020年07月12日12:04:35
2019年09月26日10:01:43
solidity编程语言了解、实践-1
1.pragma
版本标识指令,用来启动编译器检查。
使用规范:
pragma solidity ^0.5.2;
这样的话,源文件不允许低于0.5.2版本的编译器编译,也不允许高于0.6.0版本的编译器编译。(第二个条件因使用^被添加)
2.合约结构
2.1 介绍
每个合约可以包括状态变量,函数,事件Event,结构体,枚举类型,合约可以从其他合约继承。
2.2 状态变量
指永久地存储在合约中的值。
合约内——方法外
pragma solidity >=0.4.0 <0.7.0;
contract TinyStorage {
uint storedXlbData; // 状态变量
// ...
}
2.3 函数
函数是合约中代码的可执行单元。
pragma solidity >=0.4.0 <0.7.0;
contract TinyAuction {
function Mybid() public payable { // 定义函数
// ...
}
}
函数接受参数和返回值。
2.4 函数修饰器 modifier
修饰符可以用来以声明的方式来修改函数语义。
pragma solidity >=0.4.22 <0.7.0;
contract MyPurchase {
address public seller;
modifier onlySeller() { // 修饰器
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
function abort() public onlySeller { // 修饰器用法
// ...
}
}
2.4 事件Event
事件是能方便地调用以太坊虚拟机日志功能的接口。
pragma solidity >=0.4.21 <0.7.0;
contract TinyAuction {
event HighestBidIncreased(address bidder, uint amount); // 事件
function bid() public payable {
// ...
emit HighestBidIncreased(msg.sender, msg.value); // 触发事件
}
}
事件编写:
pragma solidity >=0.4.21 <0.7.0;
contract ClientReceipt {
event Deposit(
address indexed _from,
bytes32 indexed _id,
uint _value
);
function deposit(bytes32 _id) public payable {
// 事件使用 emit 触发事件。
// 我们可以过滤对 `Deposit` 的调用,从而用 Javascript API 来查明对这个函数的任何调用(甚至是深度嵌套调用)。
emit Deposit(msg.sender, _id, msg.value);
}
}
使用 JavaScript API 调用事件的用法如下:
var abi = /* abi 由编译器产生 */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...xlb67" /* 地址 */);
var event = clientReceipt.Deposit();
// 监听变化
event.watch(function(error, result) {
// 结果包含 非索引参数 以及 主题 topic
if (!error)
console.log(result);
});
// 或者通过传入回调函数,立即开始听监
var event = clientReceipt.Deposit(function(error, result) {
if (!error)
console.log(result);
});
上面的输出如下所示(有删减):
{
"returnValues": {
"_from": "0x1111…FFFFCCCC",
"_id": "0x50…sd5adb20",
"_value": "0x420042"
},
"raw": {
"data": "0x7f…91385",
"topics": ["0xfd4…b4ead7", "0x7f…1a91385"]
}
}
2.5 结构体
结构体是可以将几个变量分组的自定义类型。
pragma solidity >=0.4.0 <0.7.0;
contract TinyBallot {
struct Voter { // 结构体
uint weight;
bool voted;
address delegate;
uint vote;
}
}
2.6 枚举类型
枚举可用来创建一定数量的“常量值”构成的自定义类型。
pragma solidity >=0.4.0 <0.7.0;
contract Upchain {
enum State { Created, Locked, InValid } // 枚举
}
3.类型
“undefined”或“null”值的概念在Solidity中不存在,但是新声明的变量总是有一个默认值,具体的默认值跟类型相关。
3.1 值类型
3.1.1 bool
:true或者false
3.1.2 整形
int
/ uint
:分别表示有符号和无符号的不同位数的整型变量。 支持关键字 uint8
到 uint256
(无符号,从 8 位到 256 位)以及 int8
到 int256
,以 8
位为步长递增。uint
和 int
分别是 uint256
和 int256
的别名。
3.2 地址类型
地址类型有两种形式,他们大致相同:
address
:保存一个20字节的值(以太坊地址的大小)。ddress payable
:可支付地址,与address
相同,不过有成员函数transfer
和send
。
这种区别背后的思想是 address payable
可以接受以太币的地址,而一个普通的 address
则不能。
类型转换:
允许从 address payable
到 address
的隐式转换,而从 address
到 address payable
的转换是不可以的( 执行这种转换的唯一方法是使用中间类型,先转换为 uint160
)
大部分情况下你不需要关心
address
与address payable
之间的区别,并且到处都使用address
。 例如,如果你在使用取款模式, 你可以(也应该)保存地址为address
类型, 因为可以在msg.sender
对象上调用transfer
函数, 因为msg.sender
是address payable
。
address
和address payable
的区别是在 0.5.0 版本引入的,同样从这个版本开始,合约类型不再继承自地址类型,不过如果合约有可支付的回退( payable fallback )函数,合约类型仍然可以显示转换为address
或address payable
。
3.2.1 balance tranfer
可以使用 balance
属性来查询一个地址的余额, 也可以使用 transfer
函数向一个可支付地址(payable address)发送 Ether (以 wei 为单位)
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
如果当前合约的余额不够多,则 transfer
函数会执行失败,或者如果以太转移被接收帐户拒绝, transfer
函数同样会失败而进行回退。
3.2.2 send
send
是 transfer
的低级版本。如果执行失败,当前的合约不会因为异常而终止,但 send
会返回 false
。
3.2.3 call
, delegatecall
和 staticcall
为了与不符合应用二进制接口Application Binary Interface(ABI) 的合约交互,或者要更直接地控制编码,提供了函数 call
,delegatecall
和 staticcall
。 它们都带有一个bytes memory
参数和返回执行成功状态(bool
)和数据(bytes memory
)。
所有三个函数
call
,delegatecall
和staticcall
都是非常低级的函数,应该只把它们当作 最后一招 来使用,因为它们破坏了 Solidity 的类型安全性。
3.3 合约类型
每一个contract定义都有他自己的类型。
您可以隐式地将合约转换为从他们继承的合约。 合约可以显式转换为 address
类型。
只有当合约具有可支付回退函数时,才能显式和 address payable
类型相互转换 转换仍然使用 address(x)
执行,而不是使用 address payable(x)
。
在版本0.5.0之前,合约直接从地址类型派生的, 并且
address
和address payable
之间没有区别。
3.4 字符串字面常量及类型
字符串字面常量是指由双引号或单引号引起来的字符串("foo"
或者 'bar'
)。 不像在 C 语言中那样带有结束符;"foo"
相当于 3 个字节而不是 4 个。 和整数字面常量一样,字符串字面常量的类型也可以发生改变,但它们可以隐式地转换成 bytes1
,……,bytes32
,如果合适的话,还可以转换成 bytes
以及 string
。
3.5 映射
映射类型在声明时的形式为 mapping(_KeyType => _ValueType)
。 其中 _KeyType
可以是任何基本类型,即可以是任何的内建类型加上bytes
and string
。 而用户定义的类型或复杂的类型如:合约类型、枚举、映射、结构体、即除bytes
and string
之外的数组类型 是不可以作为 _KeyType
的类型的。
_ValueType
可以是包括映射类型在内的任何类型。
映射可以视作 哈希表 ,它们在实际的初始化过程中创建每个可能的 key, 并将其映射到字节形式全是零的值:一个类型的 默认值。然而下面是映射与哈希表不同的地方: 在映射中,实际上并不存储 key,而是存储它的 keccak256
哈希值,从而便于查询实际的值。
正因为如此,映射是没有长度的,也没有 key 的集合或 value 的集合的概念。
pragma solidity >=0.4.0 <0.7.0;
contract MappingExample {
mapping(address => uint) public balances;
function update(uint newBalance) public {
balances[msg.sender] = newBalance;
}
}
contract MappingLBC {
function f() public returns (uint) {
MappingExample m = new MappingExample();
m.update(100);
return m.balances(this);
}
}