Solidity函數聲明和類型
- ?函數的值類型有兩類:內部(internal)類型和外部(external)類型
- 內部函數只可以在當前合約內部被調用(即在當前代碼塊內,包括內部庫函數和繼承函數),因為他們不能在當前合約的上下文中的外部被執行。調用一個內部函數是通過跳轉到它的入口標簽來實現的,就像在當前合約的內部調用一個函數
- 外部函數是由一個地址和一個函數的簽名組成,可以通過外部函數調用傳遞或者返回
- 調用內部函數,直接使用名字f
- 調用外部函數:this.f(當前合約),a.f(外部合約)
- 除了使用外部函數調用外,還可以使用繼承機制
例子(外部函數調用)
contract C{uint a;function f() public{}
}
contract D{function g() public{C c = new C();c.f();}
}
例子(繼承機制)
- 可以使用internal函數/public函數,不可以使用private函數
- D繼承C合約,只會把交叉的函數編譯上傳
contract C{uint a;function f() public{}
}
contract D is C{function g() public{f();}
}
函數的可見性
- 函數的可見性可以指定為external、public、internal或者private;對于狀態變量,不可以設置為external,默認是internal
- external:外部函數作為合約接口的一部分,意味著我們可以從其他合約和交易中調用。一個外部函數f不可以從內部調用(即f不起作用,但是this.f可以)。當接收到大量的數據的時候,外部函數有時候會更有效率
- public:public函數是合約接口的一部分,相當于定義了一個view類型的可以返回參數的函數,可以在內部或者通過消息調用。對于public狀態變量,會自動生成一個getter函數
例子
uint public a;//將a聲明為public,就相當于為a定義了如下的函數形式
function a() public view returns (uint){return a;
}
- internal:這些函數和狀態變量只能是內部訪問(即從當前合約內部或者從其他派生的合約訪問),不可以使用this
- private:private函數和狀態變量僅在當前定義他們的合約中使用,并且不能被派生合約使用。
函數狀態可變性
- payable:允許從消息調用中接收以太幣Ether
- constant:和view相同,一般只修飾狀態變量,不允許賦值(除了初始化之外)
constructor()public payable{}//創建合約的同時需要往合約上面轉錢
- pure:純函數,不允許修改或者訪問狀態
- view:不允許修改狀態
函數狀態可變性
- 修改狀態變量
- 產生事件
- 創建其他合約
- 使用selfdestruct(自殺/自毀)
- 通過調用發送以太幣
- 調用任何沒有標記為view或者pure的函數
- 使用低級調用
- 使用包含特定操作嗎的內聯匯編
以下是被認為從狀態中進行讀取
- 讀取狀態變量
- 訪問this.balance或者<address>.balance
- 訪問block、tx、msg中任意成員(除了msg.sig和msg.data之外)
- 調用任何未標記為pure的函數
- 使用包含某些操作碼的內聯匯編
函數修飾器 modifier
- 使用修飾器modifier可以輕松改變函數的行為。例如,他們可以在執行函數之前自動檢查某個條件。修飾器modifier是合約的可繼承屬性,并可能被派生合約覆蓋
- 如果同一個函數有多個修飾器modifier,他們之間可以使用空格隔開,修飾器modifier會依次檢查執行。
代碼
contract Purchase{address public seller;constructor() public{seller = msg.sender;}modifier onlySeller(){require(msg.sender == seller,"Only seller");_;}function f() public view onlySeller returns(uint){return 200;}
}
效果展示
- msg.sender是合約的部署者,只有合約的部署者調用f函數,返回200,剩余的人會返回錯誤信息,only seller
- require(msg.sender == seller,"Only seller");逗號之前是檢查條件,之后是返回報錯信息,檢查條件在代碼流程之前執行
- _;將代碼流程嵌套到這里,指代檢查條件執行之后,執行代碼的邏輯。(函數體)
回退函數(fallback)
- 回退函數(fallback function)是合約中的特殊函數;沒有名字,不能有參數也不能有返回值
- 如果在一個到合約的調用中,沒有其他函數與給定的函數標示符匹配(或者沒有提供調用函數),你們這個函數(fallbacj函數)會被執行
- 每當合約收到以太幣(沒有任何數據),回退函數就會執行。此外,為了接收以太幣,fallback函數必須標記為payable。如果不存在這樣的函數,則合約不能通過常規交易接收以太幣
- 在上下文中只有很少的gas可以用來完成回退函數的調用,所以使fallback函數的調用盡量廉價很重要
代碼
pragma solidity >0.4.99 <0.6.0;
contract Sink{function() external payable{}
}
contract Test{function() external {x=1;}uint x;
}contract Caller{function calllTest(Test test)public returns(bool){(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));require(success);address payable testPayable = address(uint160(address(test)));return testPayable.send(2 ether);}
}
注解
- 調用合約中不存在的函數和轉錢的時候會調用回退函數
- 回退函數適用在合約5.0版本以上
事件(event)
- 事件是以太坊EVM提供的一種日志基礎設施。事件可以用來做操作記錄,存儲為日志。也可以用來實現一些交互功能,比如通知UI,返回函數的調用結果
- 當定義的事件被觸發時,可以將事件存儲到EVM的交易日志中,日志是區塊鏈中的一種特殊的數據結構;日志和合約相互關聯,與合約的存儲合并存入到區塊鏈條中,只要某個區塊可以訪問,其相關的日志就可以訪問,但是在合約中是不可以之間訪問日志和事件數據
- 可以通過日志實現簡單支付驗證SPV,如果一個外部實體提供了一個帶有這種證明的合約,它可以檢查日志是否真實存在于區塊鏈中
異常處理
- 適用狀態恢復異常來處理異常。這樣的異常將會撤銷對于當前的調用(及其所有的子調用)中的狀態所做的所有的更改,并且向調用者返回錯誤
- 函數的assert和require用于判斷條件,并且在不滿足條件的時候拋出異常
- assert()一般只應用于測試內部的錯誤,并且檢查異常
- require()應用于確保滿足有效的條件(如輸入或合約的狀態變量),或者驗證調用外部合約的返回值
- revert()用于拋出異常,它可以標記一個錯誤并且將當前調用回退
單位
- 以太幣單位之間的換算就是在數字后邊加上wei、finney、szabo或者ether來實現,如果沒有單位,缺省為wei
時間
- 秒是缺省單位,在時間單位之間,數字后面帶有seconds、minutes、hours、days、weeks和years的可以進行換算
?但是這些后綴不可以直接用在變量的后邊,如果需要使用到時間單位(例如days)來將輸入變量轉換為時間,可以使用如下方式
function f(uint start,uint daysAfter)public{if (now >= start + daysAfter * 1 days)
}
?