solidity高階語法
阿新 • • 發佈:2018-11-17
高階語法
1. 自動推導 var
強烈不建議使用!!
為了方便,並不總是需要明確指定一個變數的型別,編譯器會通過第一個向這個物件賦予的值的型別來進行推斷
uint24 x = 0x123;
var y = x;
-
由var引發的血案…
需要特別注意:由於型別推斷是根據第一個變數進行的賦值。所以下面的程式碼將是一個無限迴圈,因為一個uint8的i的將小於2000。
for (var i = 0; i < 2000; i++)
{
//uint8 -> 255
//無限迴圈
}
pragma solidity ^0.4.25;
contract Test{
function Hi() view returns (uint, uint){
uint count = 0;
var i = 0;
for (; i < 257; i++) {
count++;
if(count >= 260){
break;
}
}
return (count, i);
}
}
/*
結果:
0: uint256: 260
1: uint256: 3
分析:當 迴圈到 i =255 時,count = 256,再往後 i 又從0開始了!
i , count
255 , 256
0 , 257
1 , 258
2 , 259
3 , 260
*/
2.全域性函式/變數
2.1 區塊和交易的屬性
函式 | 含義 |
---|---|
block.blockhash(uint blockNumber) | 雜湊值(byte32) |
block.coinbase | (address) 當前塊礦工的地址。 |
block.difficulty | (uint)當前塊的難度 |
block.gaslimit | (uint)當前塊的gaslimit |
block.number | (uint)當前區塊的塊號 |
block.timestamp | (uint)當前塊的時間戳 |
msg.data | (bytes)完整的呼叫資料(calldata) |
msg.gas | (uint)當前還剩的gas |
msg.sender | (address)當前呼叫發起人的地址 |
msg.sig | (bytes4)呼叫資料的前四個位元組(函式識別符號) |
msg.value | (uint)這個訊息所附帶的貨幣量,單位為wei |
now (uint)當前塊的時間戳 | 等同於block.timestamp |
tx.gasprice | (uint) 交易的gas價格 |
tx.origin | (address)交易的傳送者(完整的呼叫鏈) |
- 示例:
pragma solidity ^0.4.25;
contract Test {
bytes32 public blockhash;
address public coinbase;
uint public difficulty;
uint public gaslimit;
uint public blockNum;
uint public timestamp;
bytes public calldata;
uint public gas;
address public sender;
bytes4 public sig;
uint public msgValue;
uint public now;
uint public gasPrice;
address public txOrigin;
function letgo (){
//給定區塊號的雜湊值,只支援最近256個區塊,且不包含當前區塊
blockhash = block.blockhash(block.number - 1);
coinbase = block.coinbase ;//當前塊礦工的地址。
difficulty = block.difficulty;//當前塊的難度。
gaslimit = block.gaslimit;// (uint)當前塊的gaslimit。
blockNum = block.number;// (uint)當前區塊的塊號。
timestamp = block.timestamp;// (uint)當前塊的時間戳。
calldata = msg.data;// (bytes)完整的呼叫資料(calldata)。
gas = msg.gas;// (uint)當前還剩的gas。
sender = msg.sender; // (address)當前呼叫發起人的地址。
sig = msg.sig;// (bytes4)呼叫資料的前四個位元組(函式識別符號)。
msgValue = msg.value;// (uint)這個訊息所附帶的貨幣量,單位為wei。
now = now;// (uint)當前塊的時間戳,等同於block.timestamp
gasPrice = tx.gasprice;// (uint) 交易的gas價格。
txOrigin = tx.origin;// (address)交易的傳送者(完整的呼叫鏈)
}
}
2.2 貨幣單位
- 一個字面量的數字,可以使用字尾
wei
,finney
,szabo
或ether
來在不同面額中轉換。 - 不含任何字尾的預設單位是
wei
。如1ether
== 1000finney
的結果是true
。
pragma solidity ^0.4.25;
contract EthUnit{
uint a = 1 ether;
uint b = 10 ** 18 wei;
uint c = 1000 finney;
uint d = 1000000 szabo;
function f1() constant public returns (bool){
return a == b;
}
function f2() constant public returns (bool){
return a == c;
}
function f3() constant public returns (bool){
return a == d;
}
function f4() constant public returns (bool){
return 1 ether == 100 wei;
}
}
2.3 時間單位
- seconds,minutes,hours,days,weeks,years均可做為字尾,預設是seconds為單位。
- 1 = 1 seconds
- 1 minutes = 60 seconds
- 1 hours = 60 minutes
- 1 days = 24 hours
- 1 weeks = 7 days
- 1 years = 365 days
pragma solidity ^0.4.25;
contract TimeUnit{
function f1() pure public returns (bool) {
return 1 == 1 seconds;
}
function f2() pure public returns (bool) {
return 1 minutes == 60 seconds;
}
function f3() pure public returns (bool) {
return 1 hours == 60 minutes;
}
function f4() pure public returns (bool) {
return 1 days == 24 hours;
}
function f5() pure public returns (bool) {
return 1 weeks == 7 days;
}
function f6() pure public returns (bool) {
return 1 years == 365 days;
}
}
3.事件(Event)
- 可以用來記錄日誌
pragma solidity ^0.4.0;
contract ClientReceipt {
//定義,注意,需要加分號,相當於一句語句,與struct和enum不同。
event Deposit(
address indexed _from,
uint indexed _id,
uint _value
);
function deposit(uint _id) {
//使用
Deposit(msg.sender, _id, msg.value);
}
}
4.訪問函式
- 編譯器為自動為所有的
public的狀態變數
建立訪問函式。下面的合約例子中,編譯器會生成一個名叫data的無參,返回值是uint的型別的值data。狀態變數的初始化可以在定義時完成。
pragma solidity ^0.4.25;
contract C{
uint public data = 10;
}
contract D{
C c = new C();
function getDataUsingAccessor() returns (uint){
return c.data();
}
}
- 訪問函式有外部(external)可見性。如果通過內部(internal)的方式訪問,比如直接訪問,你可以直接把它當一個變數進行使用,但如果使用外部(external)的方式來訪問,如通過this.,那麼它必須通過函式的方式來呼叫。
pragma solidity ^0.4.25;
contract viewTest{
uint public c = 10;
function accessInternal() internal view returns (uint){
return c;
}
//合約內部也能夠呼叫用external修飾的函式, 但是要使用外部呼叫方式this
function accessExternal() view external returns (uint){
return this.c();
}
}
5.錯誤處理
傳統方法:採用 throw 和 if … throw 模式==(已過時)==,例如合約中有一些功能,只能被授權為擁有者的地址才能呼叫
if(msg.sender != owner) {
throw;
}
等價於如下任意一種形式:
if(msg.sender != owner) {
revert();
}
assert(msg.sender == owner);
require(msg.sender == owner);
示例:
pragma solidity ^0.4.21;
contract HasAnOwner {
address public owner;
uint public a ;
constructor() public {
owner = msg.sender;
}
function useSuperPowers() public {
require(msg.sender == owner);
a = 11111;
/*
if (msg.sender != owner){
throw;
}
*/
a = 10;
// do something only the owner should be allowed to do
}
}
6.修飾器(modifier)
修改器(Modifiers)可以用來輕易的改變一個函式的行為。比如用於在函式執行前檢查某種前置條件。修改器是一種合約屬性,可被繼承,同時還可被派生的合約重寫(override)。下面我們來看一段示例程式碼:
pragma solidity ^0.4.21;
contract HasAnOwner {
address public owner;
uint public a ;
constructor() public {
owner = msg.sender;
}
modifier ownerOnly(address addr) {
require(addr == owner);
//程式碼修飾器所修飾函式的程式碼
_;
}
function useSuperPowers() ownerOnly(msg.sender) public {
//require(addr == owner);
a = 10;
// do something only the owner should be allowed to do
}
}
7.元組 (tuple)
元組是一個數據集合,類似於字典但是無法修改資料,使用圓括號包括多種資料型別。
pragma solidity ^0.4.25;
contract Test {
struct Student {
string name;
uint age;
uint score;
string sex;
}
//兩種賦值方式
Student public stu1 = Student("lily", 18, 90, "girl");
Student public stu2 = Student({name:"Jim", age:20, score:80, sex:"boy"});
Student[] public Students;
function assign() public {
Students.push(stu1);
Students.push(stu2);
stu1.name = "Lily";
}
//1. 返回一個Student結構
function getLily() public view returns(string, uint, uint, string) {
Student memory lily = Students[0];
return (lily.name, lily.age, lily.score, lily.sex);
}
}
8. 內建數學函式
ripemd160
keccak256
addmod
ecrecover
9.繼承
- 繼承關鍵字:is,語法:contract Son is Father{ }
- 合約 繼承 另一個合約 來 達到複用的目的
- 繼承 父合約的所有成員變數(state var - 狀態變數)
- 繼承 父合約的所有方法
- 繼承的本質:程式碼複製,將
父合約
程式碼複製到子合約
pragma solidity ^0.4.25;
//父合約
contract Father{
string public fatherName = "James";
}
//子合約
contract Son is Father{
string public sonName = "Jun";
function testFather() public{
fatherName = "James01";
sonName = "Jun01";
}
}
- super和this關鍵字
- super 在合約中指向父合約
- this 在合約中指向本合約
pragma solidity ^0.4.25;
//父合約
contract Father{
string public fatherName = "James";
function printName() public view returns(string){
return fatherName;
}
}
//子合約
contract Son is Father{
string public sonName = "Jun";
function testFather() public{
this.sonName = "Jun01";
super.fatherName = "James01";
super.printName();
}
}
- 注意:父子同名的成員都會存在,只是訪問方式有變化
pragma solidity ^0.4.25;
contract Father{
string public fatherName = "James";
function outName() public view returns(string){
return fatherName;
}
}
contract Son is Father{
string public fatherName = "James2";
string public sonName = "Jun";
function outName() public view returns(string){
return super.outName();
}
}
- 訪問 父合約所有的非私有成員 (包括 internal 的函式 和 成員變數)
contract Father{
uint public stateVar;//成員變數
//public 方法
function aPublicFun() public pure returns(string){
return "publicFunc invoked";
}
function aInternalFun() internal pure returns(string){
return "internalFunc invoked";
}
function aExternalFun() external pure returns(string){
return "externalFunc invoked";
}
function aPrivateFun() private pure returns(string){
return "privateFunc invoked";
}
}
// 子合約 繼承 父合約
contract Son is Father{
function testFather() public{
//不能訪問`private`
//aPrivateFun();
//訪問父類的`public`方法
aPublicFun();
//訪問父類的狀態變數
stateVar = 10;
//訪問父類的`internal`方法
aInternalFun();
//訪問父類的'external'方法,需要通過this關鍵字
this.aExternalFun();
}
// 呼叫某合約訪問許可權是external的方法
function testExternal(Father fObj)public view returns(string){
return fObj.aExternalFun();
}
}
10.繼承中同名成員
問題:如果父合約 與 子合約 中 有 相同名字 的 成員變數 或 方法的話,會怎麼樣?
答:不管同名與否,父合約所有成員都會被 “繼承” 到 子合約中。只是子合約呼叫時有些區別。
- 訪問就近原則:子合約中 呼叫 同名成員 都是子合約中的,如:
pragma solidity ^0.4.25;
contract Father{
string public fatherName = "James";
}
contract Son is Father{
string public fatherName = "James2";
function outName() public view returns(string){
return fatherName; // James2
}
}
- 父合約與子合約
- 最遠繼承原則:在繼承鏈中,由於繼承實現是程式碼複製。如果出現函式名重寫,最終使用的是繼承鏈上哪個合約定義的程式碼呢?實際執行時,依據的是 最遠繼承原則(most derived)
pragma solidity ^0.4.0;
contract Base1{
function data() public view returns(uint){
return 1;
}
}
contract Base2{
function data() public view returns(uint){
return 2;
}
}
contract MostDerived1 is Base1, Base2{ // extends Base2 (most derived)
function call() public view returns(uint){
return data(); // Base2.data()
}
}
contract MostDerived2 is Base2, Base1{ // extends Base1 (most derived)
function call() public view returns(uint){
return data(); // Base1.data()
}
}
- 可以指定某個父合約
pragma solidity ^0.4.0;
contract owned {
function owned() { owner = msg.sender; }
address owner;
}
contract mortal is owned {
event mortalCalled(string);
function kill() {
mortalCalled("mortalCalled");
if (msg.sender == owner) selfdestruct(owner);
}
}
contract SpecifyBase is mortal {
event SpecifyBase(string);
function kill() {
SpecifyBase("do own cleanup");
mortal.kill();
}
}
11.外部呼叫
外部呼叫,後面專案會用到
pragma solidity ^0.4.24;
contract C1 {
uint public num = 10;
function setValue(uint a) public {
num = a;
}
}
//手動賦值
contract C2 {
C1 c1;
function call (address addr) public {
c1 = C1(addr);
c1.setValue(100);
//addr.setValue(1000); 不允許
}
}
//合約自動呼叫
contract C3 {
C1 c1;
function C3() {
address c1Addr = new C1();
c1 = C1(c1Addr);
//或者 直接使用C1來承接亦可
//c1 = new C1();
}
function getValue() public constant returns(uint) {
return c1.num();
}
//注意constant 的坑!!!!,
//函式加上constant之後,執行修改狀態變數,但是修改不會生效,好坑!!!
function setValue(uint num) public {
c1.setValue(num);
}
}
官方示例
pragma solidity ^0.4.0;
contract InfoFeed {
function info() public payable returns (uint ret) {
return 42;
}
}
contract Consumer {
InfoFeed feed;
function setFeed(address addr) public {
feed = InfoFeed(addr);
}
function callFeed() public {
feed.info.value(10).gas(800)();
}
}