??Solidity是傳說(shuō)中編寫智能合約的腳本語(yǔ)言,運(yùn)行在EVM中普办;用以解決區(qū)塊鏈中的任務(wù)執(zhí)行。一個(gè)目前看起來(lái)還非常稚嫩的語(yǔ)言徘钥。這里做一個(gè)結(jié)構(gòu)性介紹衔蹲。并據(jù)此展開(kāi)詳細(xì)的說(shuō)明。
Solidity語(yǔ)言特點(diǎn)
- Solidity 是一門面向合約的、為實(shí)現(xiàn)智能合約而創(chuàng)建的高級(jí)編程語(yǔ)言舆驶。
- 這門語(yǔ)言受到了 C++橱健,Python 和 Javascript 語(yǔ)言的影響,設(shè)計(jì)的目的是能在以太坊虛擬機(jī)(EVM)上運(yùn)行沙廉。
- Solidity 是靜態(tài)類型語(yǔ)言拘荡,支持繼承、庫(kù)和復(fù)雜的用戶定義類型等特性撬陵。
- 使用Solidity 語(yǔ)言珊皿,可以為各種應(yīng)用創(chuàng)建合約
- 投票;
- 眾籌巨税;
- 秘密競(jìng)價(jià)(盲拍)蟋定;
- 多重簽名的錢包;
- 以及其他應(yīng)用草添;
Slidity語(yǔ)言結(jié)構(gòu)
源代碼文件
源代碼文件與其他語(yǔ)言一樣驶兜,使用文本文件,擴(kuò)展名使用sol远寸。
-
文件的的組織操作系統(tǒng)的文件一樣抄淑,使用目錄組織;
- 文件之間使用import引用而晒,引用可以指定目錄名蝇狼。這個(gè)與ES6語(yǔ)法類似。
-
import "filename";
- 此語(yǔ)句將從 “filename” 中導(dǎo)入所有的全局符號(hào)到當(dāng)前全局作用域中倡怎。
-
import * as symbolName from "filename";
- 創(chuàng)建一個(gè)新的全局符號(hào) symbolName,其成員均來(lái)自 "filename" 中全局符號(hào)贱枣。
-
import {symbol1 as alias, symbol2} from "filename";
- 創(chuàng)建新的全局符號(hào) alias 和 symbol2监署,分別從 "filename" 引用 symbol1 和 symbol2 。
-
import "filename" as symbolName;
- 條語(yǔ)句等同于 import * as symbolName from "filename";纽哥。
目錄
- 支持"."與".."
- ".":當(dāng)前目錄
- "..":上級(jí)目錄
- 在編譯器支持目錄重映射:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
- 編譯:
solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
文件結(jié)構(gòu)
-
頭
- 版本申明
- import
-
合約
contract 合同名 {}
-
注釋
- 與javascript一樣钠乏,行注釋與塊注釋。
//
/**/
例子
- other.sol
pragma solidity ^0.6.1;
contract Other {
uint value;
}
- solidity.sol
pragma solidity ^0.6.1;
import "./other.sol";
// 行注釋
contract MySol is Other {
/**
塊注釋
*/
uint age;
}
- 編譯
solcjs --abi solidity.sol other.sol
合約contract
-
在 Solidity 中春塌,合約類似于面向?qū)ο缶幊陶Z(yǔ)言中的類晓避。 每個(gè)合約中可以包含
- 狀態(tài)變量;
- 函數(shù);
- 函數(shù)修飾器;
- 事件;
- 結(jié)構(gòu)類型;
- 枚舉類型 ;
合約可以繼承的
合約定義
contract 合約名 [is] 父合約{
// 1. 狀態(tài)變量;
// 2. 函數(shù);
// 3. 事件;
// 4. 結(jié)構(gòu)類型;
// 5. 枚舉類型;
}
狀態(tài)變量
contract MyContract {
uint varState; // 狀態(tài)變量
// ...
}
- 狀態(tài)變量:
- 類型 存儲(chǔ)名;
- 類型見(jiàn)后面說(shuō)明只壳;
函數(shù)與函數(shù)修飾
- 合約中可執(zhí)行單元
contract Purchase {
address public seller;
modifier onlySeller() { // 修飾器
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
function abort() public onlySeller { // 函數(shù)與修飾器使用
// ...
}
}
事件
- 事件是能方便地調(diào)用以太坊虛擬機(jī)日志功能的接口俏拱。
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // 事件
function bid() public payable {
// ...
emit HighestBidIncreased(msg.sender, msg.value); // 觸發(fā)事件
}
}
結(jié)構(gòu)類型
- 結(jié)構(gòu)是可以將幾個(gè)變量分組的自定義類型
- 用戶自定義復(fù)合類型
contract Ballot {
struct Voter { // 結(jié)構(gòu)
uint weight;
bool voted;
address delegate;
uint vote;
}
// .....
}
枚舉類型
- 舉可用來(lái)創(chuàng)建由一定數(shù)量的“常量值”構(gòu)成的自定義類型.
contract Purchase {
enum State { Created, Locked, Inactive } // 枚舉
}
合同繼承
- 使用is關(guān)鍵字實(shí)現(xiàn)。
pragma solidity ^0.4.16;
contract owned {
function owned() { owner = msg.sender; }
address owner;
}
// 使用 is 從另一個(gè)合約派生吼句。派生合約可以訪問(wèn)所有非私有成員锅必,包括內(nèi)部函數(shù)和狀態(tài)變量,
// 但無(wú)法通過(guò) this 來(lái)外部訪問(wèn)惕艳。
contract mortal is owned {
function kill() {
if (msg.sender == owner) selfdestruct(owner);
}
}
- 主要:
- 繼承包含抽象類與接口的定義搞隐。
抽象合約
- 語(yǔ)法沒(méi)有什么差異驹愚,主要在函數(shù)的實(shí)現(xiàn)上:
- 包含抽象函數(shù) // 沒(méi)有實(shí)現(xiàn)函數(shù)體的函數(shù)就是抽象函數(shù)
- 抽象合約:
- 主要是只有抽象函數(shù)的合約就是接口合約。
- 實(shí)現(xiàn)函數(shù)與抽象函數(shù)混雜的就是抽象合約劣纲。
contract Feline { // 可以包含實(shí)現(xiàn)的就是抽象合約逢捺,這里只有一個(gè)抽象函數(shù),實(shí)際也是接口合約癞季。
function utterance() public returns (bytes32);
}
contract Cat is Feline {
function utterance() public returns (bytes32) { return "miaow"; }
}
庫(kù)
- 使用libarary定義庫(kù)蒸甜,定義好的庫(kù)可以在合約中使用,下面是官方的例子:
- 庫(kù)的語(yǔ)法與合約差不多余佛,合約可以使用庫(kù)中數(shù)據(jù)與函數(shù)柠新。
pragma solidity ^0.4.16;
library Set {
// 我們定義了一個(gè)新的結(jié)構(gòu)體數(shù)據(jù)類型,用于在調(diào)用合約中保存數(shù)據(jù)辉巡。
struct Data { mapping(uint => bool) flags; }
// 注意第一個(gè)參數(shù)是“storage reference”類型恨憎,因此在調(diào)用中參數(shù)傳遞的只是它的存儲(chǔ)地址而不是內(nèi)容。
// 這是庫(kù)函數(shù)的一個(gè)特性郊楣。如果該函數(shù)可以被視為對(duì)象的方法憔恳,則習(xí)慣稱第一個(gè)參數(shù)為 `self` 。
function insert(Data storage self, uint value)
public
returns (bool)
{
if (self.flags[value])
return false; // 已經(jīng)存在
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public
returns (bool)
{
if (!self.flags[value])
return false; // 不存在
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
}
contract C {
Set.Data knownValues;
function register(uint value) public {
// 不需要庫(kù)的特定實(shí)例就可以調(diào)用庫(kù)函數(shù)净蚤,
// 因?yàn)楫?dāng)前合約就是“instance”钥组。
require(Set.insert(knownValues, value));
}
// 如果我們?cè)敢猓覀円部梢栽谶@個(gè)合約中直接訪問(wèn) knownValues.flags今瀑。
}
數(shù)據(jù)類型與數(shù)據(jù)
數(shù)據(jù)的定義
-
語(yǔ)法:
類型 變量名 = 初始值
- 變量名遵循Javascript的命名規(guī)則程梦。
-
刪除變量
- delete 變量名;
-
常量:
- constant:
int constant a = 2000;
數(shù)據(jù)類型與字面值
布爾類型與布爾值
- 類型關(guān)鍵字:
bool
- 布爾值:
true
與false
整數(shù)類型與整數(shù)值
- 類型關(guān)鍵值:
- int / uint (有符號(hào)與無(wú)符號(hào))
- 整數(shù)也分字節(jié)大虚佘:?jiǎn)挝皇俏唬?位一個(gè)字節(jié)屿附,根據(jù)對(duì)齊規(guī)則,必須是8的倍數(shù)哥童。
- int8/uint8
- 挺份。。贮懈。
- int256/uint256 = int/uint
- 整數(shù)值:
- 只支持10與16進(jìn)制
- 普通法表示:122
- 不能前綴0匀泊。
- 16進(jìn)制使用hex前綴轉(zhuǎn)換:hex"001122FF" 或者 0x前綴。
- 科學(xué)記數(shù)法表示:1e10
- 指數(shù)必須是整數(shù)朵你,不支持小數(shù)各聘。
- 普通法表示:122
- 只支持10與16進(jìn)制
小數(shù)類型與小數(shù)值
-
小數(shù)類型關(guān)鍵字:fixed / ufixed
- 有符號(hào)與無(wú)符號(hào)小數(shù)
- 小數(shù)還可以自帶精度表示:
- ufixedMxN / fixedMxN (M表示表示該類型占用的位數(shù),N表示可用的小數(shù)位數(shù))
- M也必須是8的倍數(shù)撬呢,最大256伦吠。
- ufixedMxN / fixedMxN (M表示表示該類型占用的位數(shù),N表示可用的小數(shù)位數(shù))
-
小數(shù)值:
- 使用小數(shù)點(diǎn)表示小數(shù)。 與整數(shù)一樣,分成普通表示與科學(xué)表示毛仪。
-
例子:
- ufixed32x2 score = 12.45;
-
注意:
- Solidity 還沒(méi)有完全支持定長(zhǎng)浮點(diǎn)型搁嗓。可以聲明定長(zhǎng)浮點(diǎn)型的變量箱靴,但不能給它們賦值或把它們賦值給其他變量腺逛。。
地址類型與地址值
- 地址類型存儲(chǔ)一個(gè) 20 字節(jié)的值(以太坊地址的大泻饣场)棍矛。
- 地址類型也有成員變量,并作為所有合約的基礎(chǔ)抛杨。
- 地址類型關(guān)鍵字:address
- 地址值表示:0x開(kāi)頭的16進(jìn)制表示够委。
- 地址變量包含多個(gè)成員(成員屬性與成員函數(shù)),用來(lái)訪問(wèn)地址相關(guān)信息:
- balance :balance 屬性來(lái)查詢一個(gè)地址的余額
- send/transfer :transfer 函數(shù)向一個(gè)地址發(fā)送 以太幣Ether (以 wei 為單位):
- 備注:地址的所有成員:
-
<address>.balance (uint256)
:- 以Wei為單位的地址類型的余額怖现。
-
<address>.transfer(uint256 amount)
:- 向地址類型發(fā)送數(shù)量為amount的Wei茁帽,失敗時(shí)拋出異常,發(fā)送 2300 gas 的礦工費(fèi)屈嗤,不可調(diào)節(jié)潘拨。
-
<address>.send(uint256 amount) returns (bool)
:- 向地址類型 發(fā)送數(shù)量為 amount 的 Wei,失敗時(shí)返回 false饶号,發(fā)送 2300 gas 的礦工費(fèi)用铁追,不可調(diào)節(jié)。
-
<address>.call(...) returns (bool)
:- 發(fā)出低級(jí)函數(shù) CALL茫船,失敗時(shí)返回 false琅束,發(fā)送所有可用 gas,可調(diào)節(jié)透硝。
-
<address>.callcode(...) returns (bool)
:- 發(fā)出低級(jí)函數(shù) CALLCODE狰闪,失敗時(shí)返回 false,發(fā)送所有可用 gas濒生,可調(diào)節(jié)。
-
<address>.delegatecall(...) returns (bool)
:- 發(fā)出低級(jí)函數(shù) DELEGATECALL幔欧,失敗時(shí)返回 false罪治,發(fā)送所有可用 gas,可調(diào)節(jié)礁蔗。
-
數(shù)組類型與值表示
- 數(shù)組關(guān)鍵字:
類型[]
- 數(shù)組的創(chuàng)建:
new
- 例子:
pragma solidity ^0.4.16;
contract C {
function f(uint len) public pure {
uint[] memory a = new uint[](7);
a[6] = 8;
}
}
- 兩個(gè)特殊的數(shù)組:
- bytes 與 string 等加以 int8[]或者 byte[]
- 例子:
pragma solidity ^0.4.16;
contract C {
function f(uint len) public pure {
uint[] a = new uint[](7);
bytes b = new bytes(len); // string b= new string(len)
// 這里我們有 a.length == 7 以及 b.length == len
a[6] = 8;
}
}
-
數(shù)組字面值
[1, 2, 3, 4]
-
數(shù)組變量賦值的注意事項(xiàng):長(zhǎng)度一致
- 下面例子無(wú)法賦值:
// 這段代碼并不能編譯觉义。
pragma solidity ^0.4.0;
contract C {
function f() public {
// 這一行引發(fā)了一個(gè)類型錯(cuò)誤,因?yàn)?unint[3] memory
// 不能轉(zhuǎn)換成 uint[] memory浴井。
uint[] x = [uint(1), 3, 4];
}
}
-
數(shù)組的成員
- length屬性:獲取數(shù)組長(zhǎng)度晒骇,還可以通過(guò)這個(gè)成員屬性修改數(shù)組的長(zhǎng)度(只對(duì)存儲(chǔ)有效,為位置在內(nèi)存的變量無(wú)效,參考后面存儲(chǔ)位置的說(shuō)明)
- push函數(shù):用來(lái)向數(shù)組末尾添加數(shù)據(jù)
- 這length對(duì)字符串?dāng)?shù)組無(wú)效洪囤。
-
多維數(shù)組:
bool[2][3] m_pairsOfFlags;
字符串類型與值表示
-
字符串也是數(shù)組徒坡,其字面值表示:
-
"foo"
:3字節(jié)字符數(shù)組。 - 字符串?dāng)?shù)組與bytes數(shù)組可以隱式轉(zhuǎn)換瘤缩。
-
-
字符串支持轉(zhuǎn)移字符
- 字符串字面常數(shù)支持轉(zhuǎn)義字符喇完,例如
\n,\xNN 和 \uNNNN
剥啤。\xNN
表示一個(gè) 16 進(jìn)制值锦溪,最終轉(zhuǎn)換成合適的字節(jié), 而\uNNNN
表示 Unicode 編碼值府怯,最終會(huì)轉(zhuǎn)換為 UTF-8 的序列刻诊。
- 字符串字面常數(shù)支持轉(zhuǎn)義字符喇完,例如
枚舉類型
-
定義枚舉類型
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
-
使用枚舉類型
ActionChoices defaultChoice = ActionChoices.GoStraight;
pragma solidity ^0.4.16;
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight() public {
choice = ActionChoices.GoStraight;
}
結(jié)構(gòu)體
- 結(jié)構(gòu)體是定義新的類型,語(yǔ)法如下:
struct Funder {
address addr;
uint amount;
}
- 結(jié)構(gòu)體不能包含自己牺丙。
存儲(chǔ)位置
-
所有的復(fù)雜類型则涯,即數(shù)組和結(jié)構(gòu)類型,都有一個(gè)額外屬性赘被,“數(shù)據(jù)位置”是整,
- 說(shuō)明數(shù)據(jù)是保存在 內(nèi)存memory 中還是 存儲(chǔ)storage 中。
- 根據(jù)上下文不同民假,大多數(shù)時(shí)候數(shù)據(jù)有默認(rèn)的位置浮入,但也可以通過(guò)在類型名后增加關(guān)鍵字 storage 或 memory 進(jìn)行修改。
- 函數(shù)參數(shù)(包括返回的參數(shù))的數(shù)據(jù)位置默認(rèn)是 memory羊异, 局部變量的數(shù)據(jù)位置默認(rèn)是 storage事秀,狀態(tài)變量的數(shù)據(jù)位置強(qiáng)制是 storage 。
-
第三種數(shù)據(jù)位置calldata
- 這是一塊只讀的野舶,且不會(huì)永久存儲(chǔ)的位置易迹,用來(lái)存儲(chǔ)函數(shù)參數(shù)。
- 外部函數(shù)的參數(shù)(非返回參數(shù))的數(shù)據(jù)位置被強(qiáng)制指定為 calldata 平道,效果跟 memory 差不多睹欲。
例子:
pragma solidity ^0.4.0;
contract C {
uint[] x; // x 的數(shù)據(jù)存儲(chǔ)位置是 storage
// memoryArray 的數(shù)據(jù)存儲(chǔ)位置是 memory
function f(uint[] memoryArray) public {
x = memoryArray; // 將整個(gè)數(shù)組拷貝到 storage 中,可行
var y = x; // 分配一個(gè)指針(其中 y 的數(shù)據(jù)存儲(chǔ)位置是 storage)一屋,可行
y[7]; // 返回第 8 個(gè)元素窘疮,可行
y.length = 2; // 通過(guò) y 修改 x,可行
delete x; // 清除數(shù)組冀墨,同時(shí)修改 y闸衫,可行
// 下面的就不可行了;需要在 storage 中創(chuàng)建新的未命名的臨時(shí)數(shù)組诽嘉, /
// 但 storage 是“靜態(tài)”分配的:
// y = memoryArray;
// 下面這一行也不可行蔚出,因?yàn)檫@會(huì)“重置”指針弟翘,
// 但并沒(méi)有可以讓它指向的合適的存儲(chǔ)位置。
// delete y;
g(x); // 調(diào)用 g 函數(shù)骄酗,同時(shí)移交對(duì) x 的引用
h(x); // 調(diào)用 h 函數(shù)稀余,同時(shí)在 memory 中創(chuàng)建一個(gè)獨(dú)立的臨時(shí)拷貝
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) public {}
}
映射(key - value)
-
映射類型在聲明語(yǔ)法為
-
mapping(_KeyType => _ValueType)
。 - 其中
_KeyType
可以是除了映射酥筝、變長(zhǎng)數(shù)組滚躯、合約、枚舉以及結(jié)構(gòu)體以外的幾乎所有類型嘿歌。 -
_ValueType
可以是包括映射類型在內(nèi)的任何類型掸掏。
-
-
映射可以視作哈希表
- 它們?cè)趯?shí)際的初始化過(guò)程中創(chuàng)建每個(gè)可能的 key, 并將其映射到字節(jié)形式全是零的值:一個(gè)類型的 默認(rèn)值宙帝。
- 然而下面是映射與哈希表不同的地方:
- 在映射中丧凤,實(shí)際上并不存儲(chǔ) key,而是存儲(chǔ)它的keccak256哈希值步脓,從而便于查詢實(shí)際的值愿待。
類型轉(zhuǎn)換
- 類型轉(zhuǎn)換一般分成顯式轉(zhuǎn)換與隱式轉(zhuǎn)換:
- 顯式轉(zhuǎn)換 : 類型(值)
運(yùn)算符與表達(dá)式
布爾運(yùn)算
! (邏輯非)
&& (邏輯與, "and" )
|| (邏輯或靴患, "or" )
== (等于)
!= (不等于)
整數(shù)運(yùn)算
- 比較運(yùn)算符:
<= 仍侥, < , == 鸳君, != 农渊, >= , > (返回布爾值)
- 位運(yùn)算符:
& 或颊, | 砸紊, ^ (異或), ~ (位取反)
- 算數(shù)運(yùn)算符:
+ 囱挑, - 醉顽, 一元運(yùn)算 - , 一元運(yùn)算 + 平挑, * 游添, / , % (取余) 通熄, ** (冪)否淤, << (左移位) , >> (右移位)
小數(shù)運(yùn)算
- 比較運(yùn)算符:
<=棠隐, <, ==檐嚣, !=助泽, >=啰扛, > (返回值是布爾型)
- 算術(shù)運(yùn)算符:
+, -嗡贺, 一元運(yùn)算 -隐解, 一元運(yùn)算 +, *诫睬, /煞茫, % (取余數(shù))
地址運(yùn)算
- 比較運(yùn)算:
<=, <摄凡, ==续徽, !=, >= 和 >
數(shù)組運(yùn)算
- 比較運(yùn)算符:
<=亲澡, <钦扭, ==, !=床绪, >=客情, > (返回布爾型)
- 位運(yùn)算符:
&, |癞己, ^ (按位異或)膀斋, ~ (按位取反), << (左移位)痹雅, >> (右移位)
- 索引訪問(wèn):
如果 x 是 bytesI 類型仰担,那么 x[k] (其中 0 <= k < I)返回第 k 個(gè)字節(jié)(只讀)。
- .length
表示這個(gè)字節(jié)數(shù)組的長(zhǎng)度(對(duì)定長(zhǎng)只讀).
流程控制
-
JavaScript 中的大部分控制結(jié)構(gòu)在 Solidity 中都是可用的练慕,除了 switch 和 goto惰匙。
- if,else铃将,
- while项鬼,
- do,
- for劲阎,
- break绘盟,continue,return悯仙,
- ? :
-
注意:
- 用于表示條件的括號(hào)不可以被省略龄毡,單語(yǔ)句體兩邊的花括號(hào)可以被省略。
- 與C和JavaScript不同锡垄, Solidity 中非布爾類型數(shù)值不能轉(zhuǎn)換為布爾類型沦零,因此 if (1) { ... } 的寫法在 Solidity 中 無(wú)效 。