一個簡單的合約申明
pragma solidity ^0.4.21
語言的版本
contract SimpleStorage{
}
類似于類的概念
注意: constant 是定義恒常數,不能改變成員變量。但是,這個版本中,只是警示用,實際程序可以修改的。returns(uint) 定義返回類型。
最新版本中,constant變為view,但是仍然可以改參數;pure是加強版的view,意思是函數中不能修改,也不能讀取成員變量,例如:用于加密庫之類。
合約的執行
個人簡單的理解:
當部署在區塊鏈中時,會申明出這個合約中的方法,然后大家就可以通過執行這些方法來進行相應的交易。
每一個函數執行都是有一定的費用的,簡單理解為小費,單位gas,在以太坊的ether是最小的虛擬貨幣單位,會有價格波動,所以用gas表示函數執行所擁有的小費。比如說,一個函數固定執行一次會有2146gas,但是gas的price是隨ether的價格變動而變動的。這樣,每一個函數執行的費用就不會隨著貨幣價值的變化而需要修改相應的數量了。
類型
- uint/int ,目前不存在浮點數類型
- address,類似于一個objecct類型,默認值為0x0(包含錢包地址,智能合約的地址等)。.balance地址上的金額,.transfer轉錢
一些單位跟常用變量
uint salary = 1 ether; //金額的最小單位wei,本質上就是等于整型的1
uint constant dur = 30 days ;// 時間單位,這里的constant是有效的,申明常變量,但是修飾函數,是沒用的
uint lastPday = now;//
塊的概念,相當于靜態變量(全局變量)。在區塊鏈中,就是挖到的交易塊
當執行某個函數時,包含調用用戶信息等。
.sender 誰調用了這個函數
.gas 這個函數附帶的gas消費
調用函數,給合約塞錢
- 關鍵字 payable 只有添加這個關鍵字,才能在執行這個函數時,給這個合約充錢。
- this.balance 這里的this就是指代合約的地址,return this.balance 就是返回執行之后合約上的錢。
- 通過分析2,可以推測函數的執行,雖然是用戶點擊執行,但是真正執行環境是contract。
給一個地址轉錢
注意: 當執行函數時,是執行多少語句就要花費多少gas,如果說遍歷很大的數據時,就很貴了。所以如果這個函數出現了異常或者沒有按照理想的情況執行,這些gas是拿不回來的。執行revert()函數,能夠終止當前函數,并不消耗gas。而throw異常的話,執行到throw前的gas就拿不回來了。
固定執行的用戶
變量作用域與js很像
會有變量提升
計算順序,注意點
在solidity中,除法是做整除,沒有小數點。so
當計算a(b-c)/d時,如果計算順序是a((b-c)/d),會導致誤差變大。
構造函數
在合約發布的時候,就執行。
這里的意思是,保存發布這個合約的人。
注意: 構造函數與合約名要一致。
assert && require
assert函數用于確定運行中的代碼滿足某要求。
require函數用于要求輸入的起始條件。
數組
可固定,也可以動態數組。
uint[2] a;//固定長度
uint[] a;//動態長度,這時長度為0,所以無法用a[0]=1進行賦值
a.push(1);//用于增加元素,之后,就可以用a[0]進行獲取
delete a[i];
a[i] = a[a.length-1];
a.length -=1;//刪除數組中的值,并且把最后一個數放到刪除的位置上,長度減一
struct結構
這個就類似于C語言里面的,構造類型。
Employee[] employees;//相當于一個新的類型
Employee(employee,salary,now);//這就新建了一個Employee類型對象了
可視度,函數默認為public
但是一個函數的輸入或者輸出是自己建的struct,那么函數可視度需要為private。
數據存儲
storage 是在區塊鏈上,永久存在
memory 臨時空間,當函數運行之后,就會釋放
calldata 也是臨時空間,類似memory
這里的存儲類似于JS,對于一個對象來說,存儲的是內存地址。
注意點: 由于函數返回的是memory上的數據。
這里的_findEmployee函數返回相應的狀態變量的拷貝。因為函數返回是memory,而狀態變量是storage。
這時當修改employee上的值時,存在storage上的狀態變量是不會改變的。
為了減少gas的消耗,mapping
就是一個hash數據結構。
只有四種類型做key
由于不能進行遍歷,所以下面的語句報錯。
解決方法:將uint totalSalary變為狀態變量,然后在每次增加或者移除一個employee時,便操作這個totalSalary的值。
命名參數
對于輸出也可以直接進行賦值
可視度
internal像protect。
external就是只能是外部用戶或者其他合約來調用,而當前合約其他內部函數,是無法調用的。為了能夠使用,可以用this.fn1();這樣就相當于外部調用,代價會貴一點。
因為在區塊鏈上所有的數據都是公開的。合約的成員變量都是肉眼可見的。
繼承
Parent is owned 繼承
抽象合約
抽象合約不能部署在區塊鏈上,繼承抽象合約的合約需要重新定義其中的函數,類似于重寫。
INTERFACE
必須實現繼承的interface上的函數,否則因為還有抽象函數在里面,合約不能發布。
多繼承
super.func1();//用于綁定上級繼承的函數func1
如果多繼承的執行函數出現環,就無法進行線性化。
modifier,使得代碼工程化
address owner = 0xdfsdfdff;
modifier onlyOwner{
require(msg.sender == owner)
-;//相當于替代修飾的函數剩下的語句,也可以加參數
}
function removeEm(address employ) onlyowner{
}//這樣修飾之后就相當于加了一個輸入限制
modifier參數來自于當前函數的傳入參數或者全局的狀態變量。
這里的modifier中的a=1;執行在修飾的函數return之前。
SAFE math
在solidity中,運算是比較危險的。
因為這個數字大部分都跟錢有關系,所以要避免數據的溢出等異常。
但是這樣做的話,很麻煩。所以我們加入一些第三方庫,來幫我們確保safe math。
例如:
- zeppelin-solidity
import './SafeMath.sol';//進行導入第三方的library
如果要自己編寫一個第三方庫:
當引入第三方庫后,就可以直接使用里面定義的方法了。但是這樣也會很麻煩,相當于每次進行操作時,都會調用這個函數。
利用一個語法,using SafeMath for uint8; 這樣每當調用SafeMath中的sub函數時,直接用a.sub(100)替代SafeMath.sub(a,100),因為a是uint8類型,可以直接穿透到參數里面。