基礎篇
Solidity是一門靜態類型的腳本語言,我們可以對照C++的語法進行快速記憶。
###1.基本保留字與基本類型
- contract:類似於class,定義一個合約,具有構造函數,僅在創建合約時被調用。
- function:定義一個函數。
- event:定義一個事件,外部Dapp可以監控這些事件,以獲知合約內的變化。
- var:聲明變量,類似於C++11的auto,可以在初始化時進行自動類型推導,之後不能更改類型。
- bool: 布爾類型,有true跟false兩種值。
- uint8、uint16、。。。uint256:無符號整型,uint是uint256的別名。
- nint8、int16、。。。int256:有符號整形,int是int256的別名。
- ufixedMxN:無符號定點小數,M表示整個類型佔用的bit數,N表示小數位數。ufixed64x7:7位小數,剩下的是整數部分
- fixedMxN:定點小數。
- address:地址類型,表示賬戶地址或者合約地址。20Byte。
addr.balance:返回uint256類型的值,表示addr賬戶的餘額(Wei)。
addr.send(uint256 N):轉給addr地址N Wei數量的以太幣,失敗是返回false。
addr.transfer(uint256 N):包裝了send方法,失敗時直接拋出異常,會導致整個交易回退。
addr.call、addr.callcode、addr.delegatecall:調用addr合約的指定的方法,區別稍後詳述。
###2.基本操作符
- 邏輯操作符:!(邏輯非)、&&(邏輯與)、||(邏輯或)。跟C++完全一致,並且&&與||同樣存在短路求值。
- 比較操作符:<、<=、>、>=、==、!=,跟C++完全一致。
- 算術操作符:+(正號)、-(負號)、 +、-、*、/、%(取餘)、<<(左移)、>>(右移)、**(冪)。除了冪,其餘的都跟C++一致。
- 位操作符:&(與)、|(或)、~(非)、^(異或)。
###3.數組、字符串、結構體、枚舉類型、mapping
- 數組
- 定長數組:編譯期長度就固定下來的數組是定長數組,這樣定義一個定常數組:T[k](例如 uint8[ 5 ] arr)。bytes1、bytes2, bytes3, …, bytes32,這些也是定長數組。bytes1可以簡寫成byte。定長數組是值類型(value-type),可以進行比較操作、位操作、索引操作。
- 動態數組:編譯期長度不固定,類似於C++中的vector,這樣定義一個動態數組:T[] (例如 int256[] arr)。
string是特殊的動態數組。普通的定長數組、動態數組都可以進行取長度操作:arr.length,以及下標索引操作,但是string暫時不支持這兩種操作。bytes也是動態數組,相當於byte[],但是比byte[]要更、廉價一些,應該儘量使用bytes。另外,動態數組、bytes還可以調用push方法,在數組末尾添加數據,返回最新的長度。
- 字符串
string本質上是經過UTF8編碼的byte數組。當前版本的solidity對string的實現十分不完整,無法支持串聯、比較、下標索引等操作,甚至連取長度都不支持。當前的string僅可以用來做mapping的key。
- 結構體
跟C++很像,這樣定義一個結構體:struct MyStruct{
bool flag;
string name;
}
MyStruct a;
結構體、數組裏未被初始化的元素,都是0。
- 枚舉
- mapping
solidity裏使用頻率比較高的類型。mapping (address => uint256) balanceOf; 定義了一個map,使用地址做索引,值位uint256.
###4.全局可用的單位、函數、對象
- 以太幣單位
1 Ether = 1000 Finny
1 Finny = 1000 Szabo
1 Szabo = 1000 Gwei
1 Gwei = 1000 Mwei
1 Mwei = 1000 Kwei
1 Kwei = 1000 wei
1 Ether = 10^18 wei- 時間單位
1 == 1 seconds
1 minutes == 60 seconds
1 hours == 60 minutes
1 days == 24 hours
1 weeks == 7 days
1 years == 365 days- block對象
block.blockhash(uint blockNumber) returns (bytes32):返回指定高度的塊的hash值,僅限最近256塊。
block.coinbase (address):當前塊的礦工
block.difficulty (uint):當前塊的難度
block.gaslimit (uint):當前塊的gaslimit
block.number (uint):當前塊高度
block.timestamp (uint): 當前塊時間戳- msg對象
msg.data (bytes):當前調用完整的原始數據
msg.gas (uint): 剩餘的gas,0.4.21版本之後棄用,替換爲gasleft。
msg.sender (address): 當前調用的發起者。
msg.sig (bytes4):調用數據的頭四字節
msg.value (uint):當前消息攜帶的以太幣,單位wei。- tx對象
tx.gasprice (uint):當前交易的gas price
tx.origin (address): 當前交易的發起者。- 數學函數、hash函數
addmod(uint x, uint y, uint k) returns (uint):
mulmod(uint x, uint y, uint k) returns (uint):
keccak256(…) returns (bytes32):
sha256(…) returns (bytes32):
sha3(…) returns (bytes32):
ripemd160(…) returns (bytes20):
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):- 異常處理函數
assert(bool condition):
require(bool condition):
revert():- 其他
gasleft() returns (uint256)
now (uint):
this
suicide
###5.contract(合約)
contract類似於class,他有:
- 構造函數。跟contract名稱相同的function即爲構造函數,在合約創建時被調用,可以有參數,無返回值。
- 自殺函數。selfdestruct(address)銷燬合約,並把合約賬戶裏的ether轉移到指定的地址,花費比調用transfer小。suicide是selfdestruct的別名。
- this。在合約內,this可以轉化成address。在合約內部調用自己的external函數也需要用到this。
- 成員變量。
- 成員函數。
- 繼承、被繼承。一個合約可以繼承其他合約。
###6.可見性:external、public、internal、private
合約內的成員變量、成員函數需要使用可見性來修飾,不修飾默認爲public(跟C++剛好相反)。- external:只能用來修飾成員函數,這樣的函數只能被外部合約調用,合約內部想要調用該函數,需要使用this.func();
- public:修飾變量,則編譯器會自動生成一個同名的getter。修飾函數,則外部可以調用該函數。
- internal:類似於protect,可被合約內部以及子類合約訪問。
- private:私有的,僅合約內部可以訪問,子類不可訪問。
###7.函數及其修飾詞
函數可以有多個返回值:
直接通過一個稍微複雜點的例子來看下solidity的函數是啥樣子的。
uint16 uCount; //合約內的成員變量
function add(uint8 a, uint8 b) public pure returns(uint8){
return a + b; //先忽略溢出。
}
function GetCount() public view returns(uint16){
return uCount;
}
function setCount(uint16 count) public {
uCount = count;
}
modifier validAddress(address addr){
assert(addr != address(0));
_;
}
modifier validAmount(uint256 amount){
reqire(amount > 0);
_;
}
function myTransferFunc(address to, uint256 amount) public validAddress(to) validAmount(amount){
//無需再檢查參數的合法性,如果不合法,會在modifier中拋出異常,進不到函數體中。
//........
//........
}
function deposit(uint256 amount) public payable returns(bool, string){
//........
return (true, "successfull");
}
function () public payable{
//.......
}
從上往下依次看,add方法很簡單,實現了兩個數相加,但是pure是啥意思?GetCount方法的view又是啥意思?SetCount爲啥沒有這兩個東西?pure:不改變合約狀態,也不讀取合約狀態的函數,開發者應當主動使用pure修飾;讀取合約狀態但是不修改合約狀態的函數,使用view修飾;SetCount改變了合約狀態,不能被這兩個中的任何一個修飾。(老版本的solidity沒有view與pure,只有一個constant,凡是不改變狀態的函數需要被constant修飾,後來細化成兩部分view + pure)。
接下來的modifier,業界普遍翻譯成函數修改器,我覺得應該叫 函數衛詞,它就像是一個衛語句,在函數運行之前過濾參數的合法性。不合法直接拋出異常,退出函數體,整個交易都不會被執行。
再往下,一個新關鍵詞:payable。被這個修飾的函數,才能夠被轉賬,否則只能是普通調用。
最後,一個沒有名字的函數,叫回退函數(fallback function)。當且僅當一個合約的回退函數被實現了且被payable修飾了,才能向這個合約地址直接轉賬。
###8.事件
事件是另一個新東西,可以讓外部dapp監控合約內的變化。這裏先簡單介紹下事件的定義以及觸發,事件的監聽後面補上。
event OnTrasnsfer(address from, address to, uint value);
function myTransferFunc(address to, uint256 amount) public validAddress(to) validAmount(amount){
//轉賬
。。。
//新版本的solidity觸發事件需要使用emit關鍵字,之前的版本不用,但是看起來像是個函數調用。
emit OnTrasnsfer(address(this), to, amount);
}