版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
任務(wù)四
solidity語言基礎(chǔ)>>技術(shù)起源類型與特征應(yīng)用領(lǐng)域面臨的挑戰(zhàn)contentsolidity開發(fā)環(huán)境準(zhǔn)備Soliditydevelopmentenvironmentpreparation4.1solidity基礎(chǔ)語法Soliditybasicsyntax4.2常用語句Commonlyusedstatements4.3合約contract4.4solidity開發(fā)環(huán)境準(zhǔn)備SoliditydevelopmentenvironmentpreparationPART4.1在使用solidity語言進(jìn)行開發(fā)時(shí)需要,合適的編譯器進(jìn)行開發(fā)與調(diào)試工作。本小節(jié)將介紹在線編譯器Remix。RemixIDE一般用于編譯和運(yùn)行solidity智能合約。以下是智能合約的編譯、執(zhí)行和調(diào)試的步驟。第1步:在任何瀏覽器上打開RemixIDE,選擇“StartCoding”來生成一個(gè)原型開發(fā)環(huán)境和示例程序。solidity開發(fā)環(huán)境準(zhǔn)備步驟2:打開自動(dòng)生成的示例智能合約,然后點(diǎn)擊Compiler窗口下的Compile按鈕來編譯合約。solidity開發(fā)環(huán)境準(zhǔn)備步驟3:要執(zhí)行代碼,請(qǐng)單擊“部署并運(yùn)行事務(wù)”窗口下的“部署”按鈕。solidity開發(fā)環(huán)境準(zhǔn)備步驟4:部署代碼后,單擊已部署的合約下拉列表下的“print”來運(yùn)行程序,并檢查是否單擊控制臺(tái)上的下拉列表以獲取輸出。solidity開發(fā)環(huán)境準(zhǔn)備步驟5:要進(jìn)行調(diào)試,請(qǐng)單擊控制臺(tái)中與方法調(diào)用相對(duì)應(yīng)的“調(diào)試”按鈕。在這里可以檢查每個(gè)函數(shù)調(diào)用和變量分配。solidity開發(fā)環(huán)境準(zhǔn)備solidity基礎(chǔ)語法SoliditybasicsyntaxPART4.2solidity是一種靜態(tài)類型、大小寫敏感的面向?qū)ο缶幊蹋∣OP)語言。在solidity中,嚴(yán)格區(qū)分大小寫,這也就意味著"Cat"不同于"CAT"、"cat"或"cat"的其他變體。solidity中的語句結(jié)束符是英文輸入法下的分號(hào)“;”。solidity代碼是以擴(kuò)展名為“.sol”的solidity文件編寫的。它們是人類可讀的文本文件,可以在任何編輯器(包括記事本)中作為文本文件打開。雖然它是面向?qū)ο蟮模С钟邢薜拿嫦驅(qū)ο筇匦?。這意味著變量數(shù)據(jù)類型應(yīng)在編譯時(shí)定義并已知。函數(shù)和變量的編寫方式應(yīng)與它們的定義方式相同。01020304soliditysolidity基礎(chǔ)語法01020304Pragrampragmasolidity>=0.6.12<0.9.0Commentspragmasolidity>=0.6.12<0.9.0;//Thisisasinglelinecommentinsolidity/*Thisisamulti-linecommentInsolidity.UsethiswhenmultipleconsecutivelinesShouldbecommentedasawhole*/Contracts//contracts.solpragmasolidity0.4.19;//Thisisasinglelinecommentinsolidity/*Thisisamulti-linecommentInsolidity.UsethiswhenmultipleconsecutivelinesShouldbecommentedasawhole*/contractfirstContract{}contractsecondContract{}1fbrarystringLibrary{}librarymathLibrary{}interfaceIBank{}interfaceIAccount{}solidity基礎(chǔ)語法Importimport<<filename>>;1.狀態(tài)變量(StateVariables):internal:默認(rèn)情況下,如果沒有指定任何內(nèi)容,則狀態(tài)變量具有內(nèi)部限定符。這意味著這個(gè)變量只能在當(dāng)前的合約函數(shù)和從它們繼承的任何合約中使用。這些變量無法從外部訪問以進(jìn)行修改;但是,可以查看它們。private:這個(gè)限定符類似于帶有附加約束的內(nèi)部約束。私有狀態(tài)變量只能在聲明它們的合約中使用。它們甚至在派生的合約中也不能使用。public:此限定符使?fàn)顟B(tài)變量可以直接訪問。solidity編譯器為每個(gè)公有狀態(tài)變量生成一個(gè)getter函數(shù)。constant:這個(gè)限定符使?fàn)顟B(tài)變量不可變。該值必須在聲明時(shí)分配給該變量本身。實(shí)際上,編譯器將用代碼中的賦值替換該變量的引用。2.結(jié)構(gòu)體類型(StructsTypes)//定義結(jié)構(gòu)體struct{stringname;uintage;boolisAdult;uint[]ID;}solidity基礎(chǔ)語法4.2.1合約結(jié)構(gòu)3.函數(shù)修改器(FunctionModifiers)修改器的定義是使用修改器關(guān)鍵字和修改器標(biāo)識(shí)符,它可以接受的任何參數(shù),然后在“{}”內(nèi)進(jìn)行編碼。pragmasolidity>=0.4.22<0.9.0;contractMyPurchase{addresspublicseller;modifieronlySeller(){//修改器
require(msg.sender==seller,"Onlysellercancallthis.");_;}functionabort()publiconlySeller{//修改器用法//...}}solidity基礎(chǔ)語法4.2.1合約結(jié)構(gòu)4.事件(Events)事件在全局級(jí)別的合約中聲明,并在其函數(shù)中調(diào)用。使用事件關(guān)鍵字聲明一個(gè)事件,后面跟著一個(gè)標(biāo)識(shí)符和參數(shù)列表,并以分號(hào)結(jié)束。不需要顯式提供參數(shù)變量——僅數(shù)據(jù)類型就足夠,如下列代碼所示://eventdeclarationeventageRead(address,int);事件可以通過任何函數(shù)的名稱和傳遞所需的參數(shù)來調(diào)用,如下列代碼所示://functiondefiniticnfunctiongetAge(adtresspersonIdentifier)onlyBy()payableexternalreturns(uint){human=myStruct("Ritesh",10,true,newuint[](3));//usingstructmystructgender_gender=gender.male;//usingenumageRead(personIdentifler,stateIntVariable);}solidity基礎(chǔ)語法4.2.1合約結(jié)構(gòu)5.錯(cuò)誤(Errors)solidity為應(yīng)對(duì)失敗,允許用戶定義
error
來描述錯(cuò)誤的名稱和數(shù)據(jù)。錯(cuò)誤可以在
revertstatements
中使用,跟用錯(cuò)誤字符串相比,
error
更便宜并且允許你編碼額外的數(shù)據(jù),還可以用NatSpec為用戶去描述錯(cuò)誤。GPL-3.0pragmasolidity^0.8.4;///沒有足夠的資金用于轉(zhuǎn)賬,參數(shù)`requested`表示需要的資金,`available`表示僅有的資金。errorNotEnoughFunds(uintrequested,uintavailable);contractToken{mapping(address=>uint)balances;functiontransfer(addressto,uintamount)public{uintbalance=balances[msg.sender];if(balance<amount)revertNotEnoughFunds(amount,balance);balances[msg.sender]-=amount;balances[to]+=amount;//...}}solidity基礎(chǔ)語法4.2.1合約結(jié)構(gòu)6.枚舉(EnumTypes)枚舉關(guān)鍵字用于聲明枚舉。枚舉有助于在solidity中聲明自定義的用戶定義的數(shù)據(jù)類型。枚舉由一個(gè)枚舉列表,一組預(yù)定的命名常數(shù)組成。枚舉中的常數(shù)值可以在solidity中顯式地轉(zhuǎn)換為整數(shù)。每個(gè)常量值對(duì)應(yīng)一個(gè)整數(shù)值,第一個(gè)值為0,每個(gè)連續(xù)項(xiàng)的值增加1。枚舉聲明使用枚舉關(guān)鍵字,后面跟著枚舉標(biāo)識(shí)符和{}方括號(hào)內(nèi)的枚舉值列表。需要注意的是,枚舉聲明中沒有分號(hào)作為其終止符,并且在列表中應(yīng)該至少要聲明一個(gè)成員。枚舉的一個(gè)實(shí)例如下:enumgender{male,female}枚舉變量可以聲明并賦值,如下代碼所示:gender_gender=gender.male;7.函數(shù)(Functions)//functiondefintionfunctiongetAge(address_personIdentifider)onlyBy()payableexternalreturns(uint){}solidity基礎(chǔ)語法4.2.1合約結(jié)構(gòu)1.值類型如果一個(gè)類型將數(shù)據(jù)(值)直接保存在它所擁有的內(nèi)存中,則它被稱為值類型。這些類型的值與它們一起存儲(chǔ),而不是存儲(chǔ)在其他地方。下圖說明了這一點(diǎn)。在本例中,聲明了一個(gè)數(shù)據(jù)類型為unsignedinteger(uint)的變量,其數(shù)據(jù)(值)為13。變量a擁有由EVM分配的內(nèi)存空間,它被稱為0x123,該位置的存儲(chǔ)值為13。訪問這個(gè)變量將直接為我們提供值13:solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型(1)bool:布爾值,可以保存true或false作為其值;運(yùn)算符:運(yùn)算符
||
和
&&
都遵循同樣的短路(short-circuiting)規(guī)則。就是說在表達(dá)式
f(x)
||
g(y)
中,如果
f(x)
的值為
true
,那么
g(y)
就不會(huì)被執(zhí)行,即使會(huì)出現(xiàn)一些副作用。solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型(2)int/uint:分別表示有符號(hào)和無符號(hào)的不同位數(shù)的整型變量。
支持關(guān)鍵字
uint8
到
uint256
(無符號(hào),從8位到256位)以及
int8
到
int256,以
8
位為步長(zhǎng)遞增。
uint
和
int
分別是
uint256
和
int256
的別名。運(yùn)算符:solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型(3)Address:地址類型地址類型有兩種形式,他們大致相同:address:保存一個(gè)20字節(jié)的值(以太坊地址的大?。?。addresspayable:可支付地址,與address相同,不過有成員函數(shù)transfer和send。運(yùn)算符:solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型2.引用類型與值類型不同,引用類型不會(huì)將其值直接存儲(chǔ)在變量本身中。它們存儲(chǔ)的是值存儲(chǔ)位置的地址,而不是值。該變量保存指向保存實(shí)際數(shù)據(jù)的另一個(gè)內(nèi)存位置的指針。引用類型是可以占用超過32字節(jié)內(nèi)存的類型。下面通過插圖顯示參考類型。在下面的示例中,數(shù)據(jù)類型為uint的數(shù)組變量聲明為大小為6。solidity中的數(shù)組基于零,因此這個(gè)數(shù)組可以包含7個(gè)元素。變量a具有由EVM分配的內(nèi)存空間,稱為0x123,該位置中存儲(chǔ)了一個(gè)指針值0x456。此指針是指存儲(chǔ)數(shù)組數(shù)據(jù)的實(shí)際內(nèi)存位置。當(dāng)訪問變量時(shí),EVM會(huì)取消引用指針的值,并顯示來自數(shù)組索引中的值,如下圖所示:solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型(1)數(shù)據(jù)位置數(shù)據(jù)位置不僅僅表示數(shù)據(jù)如何保存,它同樣影響著賦值行為:在存儲(chǔ)storage和內(nèi)存memory之間兩兩賦值(或者從調(diào)用數(shù)據(jù)calldata賦值),都會(huì)創(chuàng)建一份獨(dú)立的拷貝。從內(nèi)存memory到內(nèi)存memory的賦值只創(chuàng)建引用,這意味著更改內(nèi)存變量,其他引用相同數(shù)據(jù)的所有其他內(nèi)存變量的值也會(huì)跟著改變。從存儲(chǔ)storage到本地存儲(chǔ)變量的賦值也只分配一個(gè)引用。pragmasolidity>=0.5.0<0.9.0;contractTiny{uint[]x;//x的數(shù)據(jù)存儲(chǔ)位置是storage,位置可以忽略//memoryArray的數(shù)據(jù)存儲(chǔ)位置是memoryfunctionf(uint[]memorymemoryArray)public{x=memoryArray;//將整個(gè)數(shù)組拷貝到storage中,可行uint[]storagey=x;//分配一個(gè)指針(其中y的數(shù)據(jù)存儲(chǔ)位置是storage),可行y[7];//返回第8個(gè)元素,可行y.pop();//通過y修改x,可行
solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型deletex;//清除數(shù)組,同時(shí)修改y,可行//下面的就不可行了;需要在storage中創(chuàng)建新的未命名的臨時(shí)數(shù)組,//但storage是“靜態(tài)”分配的://y=memoryArray;//下面這一行也不可行,因?yàn)檫@會(huì)“重置”指針,//但并沒有可以讓它指向的合適的存儲(chǔ)位置。//deletey;g(x);//調(diào)用g函數(shù),同時(shí)移交對(duì)x的引用h(x);//調(diào)用h函數(shù),同時(shí)在memory中創(chuàng)建一個(gè)獨(dú)立的臨時(shí)拷貝}functiong(uint[]storage)internalpure{}functionh(uint[]memory)publicpure{}
solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型(2)數(shù)組數(shù)組可以在聲明時(shí)指定長(zhǎng)度,也可以動(dòng)態(tài)調(diào)整大小(長(zhǎng)度)。一個(gè)元素類型為T,固定長(zhǎng)度為k的數(shù)組可以聲明為T[k],而動(dòng)態(tài)數(shù)組聲明為T[]。舉個(gè)例子,一個(gè)長(zhǎng)度為5,元素類型為uint的動(dòng)態(tài)數(shù)組的數(shù)組(二維數(shù)組),應(yīng)聲明為uint[][5](注意這里跟其他語言比,數(shù)組長(zhǎng)度的聲明位置是反的)。比如在Java中,聲明一個(gè)包含5個(gè)元素、每個(gè)元素都是數(shù)組的方式為int[5][]。在solidity中,X[3]總是一個(gè)包含三個(gè)X類型元素的數(shù)組,即使X本身就是一個(gè)數(shù)組,這和其他語言也有所不同,比如C語言。數(shù)組下標(biāo)是從0開始的,且訪問數(shù)組時(shí)的下標(biāo)順序與聲明時(shí)相反。如:如果有一個(gè)變量為uint[][5]memoryx,要訪問第三個(gè)動(dòng)態(tài)數(shù)組的第7個(gè)元素,使用x[2][6],要訪問第三個(gè)動(dòng)態(tài)數(shù)組使用x[2]。同樣,如果有一個(gè)T類型的數(shù)組T[5]a,T也可以是一個(gè)數(shù)組,那么a[2]總會(huì)是T類型。數(shù)組元素可以是任何類型,包括映射或結(jié)構(gòu)體。對(duì)類型的限制是映射只能存儲(chǔ)在存儲(chǔ)storage中,并且公開訪問函數(shù)的參數(shù)需要是ABI類型。狀態(tài)變量標(biāo)記public的數(shù)組,solidity創(chuàng)建一個(gè)getter函數(shù)。小標(biāo)數(shù)字索引就是getter函數(shù)的參數(shù)。訪問超出數(shù)組長(zhǎng)度的元素會(huì)導(dǎo)致異常(assert類型異常)??梢允褂?push()方法在末尾追加一個(gè)新元素,其中.push()追加一個(gè)零初始化的元素并返回對(duì)它的引用。solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型3.映射可以將映射聲明為public,然后讓solidity創(chuàng)建一個(gè)getter函數(shù)。KeyType將成為getter的必須參數(shù),并且getter會(huì)返回ValueType。如果ValueType是一個(gè)映射。這時(shí)在使用getter時(shí)將需要遞歸地傳入每個(gè)KeyType參數(shù)。pragmasolidity>=0.4.0<0.9.0;contractMappingExample{mapping(address=>uint)publicbalances;functionupdate(uintnewBalance)public{balances[msg.sender]=newBalance;}}contractMappingLBC{functionf()publicreturns(uint){MappingExamplem=newMappingExample();m.update(100);returnm.balances(this);}}solidity基礎(chǔ)語法4.2.2基本數(shù)據(jù)類型數(shù)據(jù)類型可以是值類型或引用類型。一些引用類型(如結(jié)構(gòu)體和數(shù)組)也有數(shù)據(jù)位置——與它們相關(guān)聯(lián)的內(nèi)存和存儲(chǔ)。變量可以是狀態(tài)變量,也可以是函數(shù)內(nèi)部局部定義的變量。本節(jié)將重點(diǎn)介紹變量、它們的范圍規(guī)則、聲明和初始化、轉(zhuǎn)換規(guī)則的提升,以及所有合約全局可用的變量以及全局函數(shù)。var數(shù)據(jù)類型變量作用域變量轉(zhuǎn)換變量提升塊相關(guān)的全局變量事務(wù)相關(guān)的全局變量數(shù)學(xué)和密碼的全局函數(shù)處理相關(guān)的全局變量和函數(shù)與合約相關(guān)的全局變量和函數(shù)solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)1.var類型變量var是一種特殊類型,只能在函數(shù)中聲明。在var類型的合約中不能有狀態(tài)變量。用var類型聲明的變量稱為隱式類型變量,因?yàn)関ar不顯式地表示任何類型。在編譯器中它的類型是依賴的,并且由第一次賦給它的值決定。一旦確定了類型,就不能更改。編譯器為var變量提供最終數(shù)據(jù)類型,而不是開發(fā)人員提供該類型。因此,很有可能是由塊決定的類型。difficulty(uint)當(dāng)前塊編譯器可能完全不是代碼執(zhí)行所期望的類型。var不能與內(nèi)存位置的顯式使用一起使用。一個(gè)顯式內(nèi)存位置需要一個(gè)顯式變量類型。下面的代碼中顯示了var的一個(gè)示例。變量uintVar8的類型是uint8,變量uintVar16的類型是uint16,變量intVar8的類型是int8(有符號(hào)整數(shù)),變量intVar16的類型是int16(有符號(hào)整數(shù)),變量boolVar的類型是bool,變量stringVar的類型是string,變量bytesVar的類型是bytes,變量arrayInteger的類型是uint8數(shù)組,變量arrayByte的類型是bytes10數(shù)組:solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)2.變量提升變量提升是指在使用變量之前不需要聲明和初始化變量。變量聲明可以發(fā)生在函數(shù)內(nèi)的任何位置,甚至在使用它之后。這就是所謂的可變提升。solidity編譯器會(huì)提取在函數(shù)中任何地方聲明的所有變量,并將它們放在函數(shù)的頂部或開頭,我們都知道在solidity中聲明變量也會(huì)初始化它們的默認(rèn)值。這確保了變量在整個(gè)函數(shù)中都可用。pragmasolidity^0.4.19;contractvariableHoisting{functionhoistingDemo()returns(uint){firstvar=10;secondVar=20;result=firstVar+secondVar;uintfirstVar;uintsecondVar;uintresult;returnresult;}}solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)3.變量作用域作用域指的是solidity中函數(shù)和合約中變量的可用性。solidity提供了以下兩個(gè)可以聲明變量的位置:合約級(jí)全局變量——也稱為狀態(tài)變量函數(shù)級(jí)局部變量函數(shù)級(jí)的局部變量很容易理解,它們只在函數(shù)內(nèi)部的任何地方使用,而不是在外部使用。合約級(jí)全局變量是合約中所有函數(shù)都可以使用的變量,包括構(gòu)造函數(shù)、回調(diào)函數(shù)和修改器。合約級(jí)全局變量可以附加一個(gè)可見性修改器。無論可見性修改器如何,都可以在整個(gè)網(wǎng)絡(luò)中查看狀態(tài)數(shù)據(jù),這一點(diǎn)很重要。以下狀態(tài)變量只能用函數(shù)修改:public:這些狀態(tài)變量可以通過外部調(diào)用直接訪問。getter函數(shù)由編譯器隱式生成,用于讀取公共狀態(tài)變量的值。internal:這些狀態(tài)變量不能從外部調(diào)用直接訪問。它們可以從當(dāng)前合約及其派生的子合約中的函數(shù)訪問。private:這些狀態(tài)變量不能直接從外部調(diào)用中訪問。它們也不能從子合約的函數(shù)中訪問。它們只能從當(dāng)前合約中的函數(shù)訪問。solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)4.類型轉(zhuǎn)換到目前為止,我們知道solidity是一種靜態(tài)類型語言,其中變量在編譯時(shí)用特定的數(shù)據(jù)類型來定義變量。在變量的生命周期內(nèi),不能更改數(shù)據(jù)類型。這意味著它只能存儲(chǔ)對(duì)數(shù)據(jù)類型合法的值。例如,uint8可以存儲(chǔ)0到255的值。它不能存儲(chǔ)負(fù)值或大于255的值??纯聪旅娴拇a可以更好地理解這一點(diǎn):pragmasolidity^0.4.19;contractErrorDataType{functionhoistingDemo()returns(uint){uint8someVar=100;someVar=300;//error}}solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)(1)基本類型之間的轉(zhuǎn)換1)隱式轉(zhuǎn)換uint8y;uint16z;uint32x=y+z;2)顯式轉(zhuǎn)換int8y=-3;uintx=uint(y);(2)字面常量與基本類型的轉(zhuǎn)換1)整型與字面常量轉(zhuǎn)換十進(jìn)制和十六進(jìn)制字面常量可以隱式轉(zhuǎn)換為任何足以表示它而不會(huì)截?cái)嗟恼麛?shù)類型:solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)uint8a=12;//可行uint32b=1234;//可行uint16c=0x123456;//失敗,會(huì)截?cái)酁?x34562)定長(zhǎng)字節(jié)數(shù)組與字面常量轉(zhuǎn)換十進(jìn)制字面常量不能隱式轉(zhuǎn)換為定長(zhǎng)字節(jié)數(shù)組。十六進(jìn)制字面常量可以是,但僅當(dāng)十六進(jìn)制數(shù)字大小完全符合定長(zhǎng)字節(jié)數(shù)組長(zhǎng)度。不過零值例外,零的十進(jìn)制和十六進(jìn)制字面常量都可以轉(zhuǎn)換為任何定長(zhǎng)字節(jié)數(shù)組類型:bytes2a=54321;//不可行bytes2b=0x12;//不可行bytes2c=0x123;//不可行bytes2d=0x1234;//可行bytes2e=0x0012;//可行bytes4f=0;//可行bytes4g=0x0;//可行3)地址類型參考地址字面常量,通過校驗(yàn)和測(cè)試的正確大小的十六進(jìn)制字面常量會(huì)作為address類型。只有bytes20和uint160允許顯式轉(zhuǎn)換為address類型。從bytes20或其他整型顯示轉(zhuǎn)換為address類型時(shí),都會(huì)作為addresspayable類型。一個(gè)地址addressa可以通過payable(a)轉(zhuǎn)換為addresspayable類型.5.塊和事務(wù)全局變量solidity提供了對(duì)一些全局變量的訪問,這些全局變量沒有在合約中聲明,但可以從合約中的代碼中訪問。合約不能直接訪問分類賬。分類賬僅由礦工維護(hù);然而,solidity為合約提供了一些關(guān)于當(dāng)前交易和區(qū)塊的信息,以便他們可以利用它們。solidity提供了與塊和事務(wù)相關(guān)的變量。下面的代碼演示了使用全局事務(wù)、塊和消息變量的示例:pragrasolidlty^0.4.19;cantractTransactianAndMessageVariables{eventlogstring(string);eventloguint(uint);eventlogbytes(bytes);eventlogaddress(address);eventlogtyte4(bytes4);eventlogblock(bytes32);functionglobalvariable()payable{solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)logaddress(block.coinbase);//8x94d76e24f818426ae84aa484148eBdSfsaelBe7eloguint(block.difficulty);//71762765929880loguint(block.gaslinit);//6888880loguint(msg.gas);//2975428loguint(tx.gasprice);//1loguint(block.nunber);//123loguint(block.tinestanp);//1513861946loguint(nàw);//1513051946logbytes(msg.data};//ax4848d797logbyte4(nsg-slg);////ax4848d797loguint(msg.value);//aarinweiifetheraresendlogaddress(mag-sender);//exca35b7ds1545Befs4aadesasBdfe2f4teBfa733c"logaddress(tx.origin);//Baca35b7d91545Befs4BadesosBdfe2f44eBfa733c"logblock(block.blockhash(block.nunber》);//0x000000000000000}}solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)6.事務(wù)和消息全局變量solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)7.txt.origin和msg.sender的區(qū)別細(xì)心的讀者可能已經(jīng)注意到,在前面的代碼演示中,txt.origin和msg.origin都是發(fā)送端顯示相同的結(jié)果和輸出。origin全局變量指向啟動(dòng)事務(wù)的原始外部賬戶。Sender指調(diào)用該函數(shù)的直接賬戶(可以是外部賬戶,也可以是另一個(gè)合約賬戶)origin變量將始終引用外部帳戶,而msg.sender可以是合約賬戶或外部賬戶。如果在多個(gè)合約上有多個(gè)函數(shù)調(diào)用,則無論調(diào)用的合約堆棧如何,txt.origin將始終引用啟動(dòng)事務(wù)的帳戶。然而msg.sender將引用調(diào)用下一個(gè)合約的上一個(gè)賬戶(合約/外部)。8.密碼學(xué)全局變量solidity為合約函數(shù)中的哈希值提供了加密功能。有兩個(gè)哈希函數(shù)——sha2和SHA3。sha3函數(shù)將輸入轉(zhuǎn)換為基于sha3算法的哈希,sha256函數(shù)將輸入轉(zhuǎn)換為基于sha2算法的哈希。還有一個(gè)函數(shù)keccak256,它是SHA3算法的別名。建議使用keccak256或sha3函數(shù)來滿足散列需求。solidity基礎(chǔ)語法4.2.3全局變量與函數(shù)9.處理全局變量每個(gè)地址(外部擁有的或基于合約的)都有五個(gè)全局函數(shù)和一個(gè)全局變量。這些函數(shù)和變量將在關(guān)于函數(shù)的后續(xù)章節(jié)中深入探討。與該地址相關(guān)的全局變量稱為balance,它提供了該地址當(dāng)前可用的以太幣余額。其功能如下:<address>.transfer(uint256amount):此函數(shù)發(fā)送給定數(shù)量的地址,拋出失敗<address>.send(uint256amount)returns(bool):這個(gè)函數(shù)發(fā)送給定數(shù)量的wei到address,失敗時(shí)返回false<address>.callcode(...)returns(bool):此函數(shù)發(fā)出低級(jí)調(diào)用,失敗時(shí)返回false<address>.callcode(...)returns(bool):這個(gè)函數(shù)發(fā)出一個(gè)低級(jí)callcode,失敗時(shí)返回false<address>.delegatecall(...)returns(bool):這個(gè)函數(shù)發(fā)出一個(gè)低級(jí)的委派調(diào)用,失敗時(shí)返回false10.合約全局變量每個(gè)合約都有以下三個(gè)全局功能:this:當(dāng)前合約的類型,顯式轉(zhuǎn)換為地址selfdestruct:這是一個(gè)地址接收者銷毀當(dāng)前合約,將其資金發(fā)送到給定地址suicide:這是一個(gè)地址收件人別名selfdestructsolidity基礎(chǔ)語法4.2.3全局變量與函數(shù)solidity支持以下類型的運(yùn)算符:算術(shù)運(yùn)算符比較運(yùn)算符邏輯(或關(guān)系)運(yùn)算符賦值運(yùn)算符條件(或三元)運(yùn)算符讓我們看一個(gè)簡(jiǎn)單的表達(dá)式:4+5=9
這里4和5稱為操作數(shù),+稱為運(yùn)算符。運(yùn)算符solidity–算術(shù)運(yùn)算符solidity–比較運(yùn)算符solidity–邏輯運(yùn)算符solidity–位運(yùn)算符solidity–賦值運(yùn)算符
solidity–條件運(yùn)算符solidity基礎(chǔ)語法4.2.4運(yùn)算符1.solidity算術(shù)運(yùn)算符solidity支持的算術(shù)運(yùn)算符,如下表所示:假設(shè)變量A的值為10,變量B的值為20。solidity基礎(chǔ)語法4.2.4運(yùn)算符2.solidity比較運(yùn)算符solidity支持的比較運(yùn)算符,如下表所示:solidity基礎(chǔ)語法4.2.4運(yùn)算符3.solidity邏輯運(yùn)算符solidity支持的邏輯運(yùn)算符,如下表所示:假設(shè)變量A的值為10,變量B的值為20。solidity基礎(chǔ)語法4.2.4運(yùn)算符4.solidity位運(yùn)算符solidity支持的位運(yùn)算符,如下表所示:假設(shè)變量A的值為2,變量B的值為3。solidity基礎(chǔ)語法4.2.4運(yùn)算符5.solidity賦值運(yùn)算符solidity支持的賦值運(yùn)算符,如下表所示:solidity基礎(chǔ)語法4.2.4運(yùn)算符序號(hào)運(yùn)算符與描述1=(簡(jiǎn)單賦值)將右側(cè)操作數(shù)的值賦給左側(cè)操作數(shù)例:C=A+B表示A+B賦給C2+=(相加賦值)將右操作數(shù)添加到左操作數(shù)并將結(jié)果賦給左操作數(shù)。例:C+=A等價(jià)于C=C+A3?=(相減賦值)從左操作數(shù)減去右操作數(shù)并將結(jié)果賦給左操作數(shù)。例:C-=A等價(jià)于C=C–A4*=(相乘賦值)將右操作數(shù)與左操作數(shù)相乘,并將結(jié)果賦給左操作數(shù)。例:C*=A等價(jià)于C=C*A5/=(相除賦值)將左操作數(shù)與右操作數(shù)分開,并將結(jié)果分配給左操作數(shù)。例:C/=A等價(jià)于C=C/A6%=(取模賦值)使用兩個(gè)操作數(shù)取模,并將結(jié)果賦給左邊的操作數(shù)。例:C%=A等價(jià)于C=C%A6.solidity條件運(yùn)算符solidity基礎(chǔ)語法4.2.4運(yùn)算符序號(hào)運(yùn)算符與描述1?:(條件運(yùn)算符)如果條件為真?則取值X:否則值Y常用語句CommonlyusedstatementsPART4.3表達(dá)式是指產(chǎn)生單個(gè)值、對(duì)象或函數(shù)的語句(包括多個(gè)操作數(shù)和可選的0個(gè)或多個(gè)函數(shù))。操作數(shù)可以是字面符、變量、函數(shù)調(diào)用或另一個(gè)表達(dá)式本身。一個(gè)表達(dá)式的示例如下:Age>10在前面的代碼中,年齡是一個(gè)變量,10是一個(gè)整數(shù)文字。年齡和10是操作數(shù),大于符號(hào)的(>)是運(yùn)算符。此表達(dá)式返回一個(gè)布爾值(真假的),具體取決于存儲(chǔ)在年齡中的值。表達(dá)式可以更復(fù)雜,由多個(gè)操作數(shù)和運(yùn)算符組成,如下所示:((Age>10)&&(Age<20))||((Age>40)&&(Age<50))常用語句4.3.1表達(dá)式語句運(yùn)算優(yōu)先級(jí)常用語句4.3.1表達(dá)式語句表達(dá)式指的是一個(gè)語句(包含多個(gè)操作數(shù)和零個(gè)或多個(gè)操作符),它產(chǎn)生單個(gè)值、對(duì)象或函數(shù)。操作數(shù)可以是文字、變量、函數(shù)調(diào)用或另一個(gè)表達(dá)式本身。表達(dá)式的計(jì)算順序不是特定的(更準(zhǔn)確地說,表達(dá)式中某節(jié)點(diǎn)的字節(jié)點(diǎn)間的計(jì)算順序不是特定的,但它們的結(jié)算肯定會(huì)在節(jié)點(diǎn)自己的結(jié)算之前)。該規(guī)則只能保證語句按順序執(zhí)行,并對(duì)布爾表達(dá)式進(jìn)行短路處理。賦值運(yùn)算符是solidity中最常用的運(yùn)算符類型。通過使用賦值語句可以完成為變量賦予初值功能。簡(jiǎn)單的賦值語句的示例如下:a=5;b=a-1;接下來將智能合約中實(shí)現(xiàn)的賦值語句案例進(jìn)行分析。pragmasolidity^0.8.0;contractdemo{functionset(uint_a,uint_b,uint_c)publicpurereturns(uint){
uintresult=14;
result+=_a*_b-_c;
returnresult;
}}常用語句4.3.2賦值語句solidity提供了條件代碼的執(zhí)行與if…else指令。if……其他情況的一般結(jié)構(gòu)如下:if(thiscondition/expressionistrue){Executetheinstructionshereelseif(thiscondition/expressionistrue){Executetheinstructionshereelse{Executetheinstructionshere}如果和if-else是可靠性中的關(guān)鍵字,它們通知編譯器它們包含一個(gè)決策控制條件,例如,如果(a>10)。在這里,如果包含一個(gè)條件,可以計(jì)算為真或假。如果>10的計(jì)算結(jié)果為true,則應(yīng)該執(zhí)行一對(duì)雙括號(hào)({)和(})中后面的代碼指令else也是一個(gè)關(guān)鍵字,如果前面的條件都不為真,則提供一個(gè)替代路徑。它還包含一個(gè)決策控制指令,并在>10趨于為真時(shí)執(zhí)行該代碼指令。常用語句4.3.3條件分支語句例:pragmasolidity^0.4.19;contractIfElseExample{enumrequestState{created,approved,provisioned,rejected,deleted,none}functionStateManagement(uint8_state)returns(intresult){requestStatecurrentState=requestState(_state);if(currentState==requestState(1)){result=1;}elseif((currentState--requestState.approved)||(currentstate--requestSvisioned)){result=2}else{currentState==requestState.none;result=3;}}}常用語句4.3.3條件分支語句1.for語句其中一個(gè)最著名和最常用的循環(huán)是for循環(huán),我們可以在固體中使用它。afor循環(huán)的一般結(jié)構(gòu)如下:for(initializeloopcounter;checkandtestthecounter;increasethevalueofcounter;){Executemultipleinstructionshere}for是可靠性中的一個(gè)關(guān)鍵字,它通知編譯器它包含關(guān)于循環(huán)一組指令的信息。它與while循環(huán)非常相似;然而,它更簡(jiǎn)潔和可讀性,因?yàn)樗械男畔⒍伎梢栽谝恍兄胁榭础O旅娴拇a示例顯示了相同的解決方案:通過映射進(jìn)行循環(huán)。但是,它使用for循環(huán)而不是while循環(huán)。i變量被初始化,在每個(gè)迭代器中增加1,并檢查它是否小于計(jì)數(shù)器的值。一旦條件變?yōu)榧?,循環(huán)將停止;也就是說,i的值等于或大于計(jì)數(shù)器:常用語句4.3.4循環(huán)語句例:pragmasolidity^0.4.19;contractForLoopExample{mapping(uint=>uint)blockNumber;uintcounter;eventuintNumber(uint);functionSetNumber(){blockNumber[counter++]=block.number;functiongetNumbers(){for(uinti=0;i<counter;i++){uintNumber(blockNumber[i]);}}}常用語句4.3.4循環(huán)語句2.while語句有時(shí),我們需要根據(jù)一個(gè)條件重復(fù)執(zhí)行一個(gè)代碼段??煽啃詾橥瑫r(shí)循環(huán)提供了精確的目的。顯示器循環(huán)的一般形式如下:Declareandinitializeacounterwhile(checkthevalueofcounterusinganexpressionorcondition){ExecutetheinstructionshereIncrementthevalueofcounter}而是可靠性中的一個(gè)關(guān)鍵字,它通知編譯器它包含一個(gè)決策控制指令。如果此表達(dá)式的計(jì)算結(jié)果為true,則應(yīng)該執(zhí)行雙括號(hào){和}后面的代碼指令。while循環(huán)繼續(xù)執(zhí)行,直到條件變?yōu)閒alse。常用語句4.3.4循環(huán)語句例:pragmasolidity^0,4.19;contractwhileLoop{mapping(uint=>uint)blockNumber;suintcounter;eventuintNumber(uint);bytesaa;functionSetNumber(){blockNumber[counter++]=block.number;}functiongetNumbers(){uinti=0;while(i<counter){uintNumber(blockNumber[i]);i=i+1;}}}常用語句4.3.4循環(huán)語句3.do...while...語句do…while循環(huán)與while循環(huán)非常相似。Do…while的一般形式如下:DeclareandInitializeacounterdo{ExecutetheinstructionshereIncrementthevalueofcounter}while(checkthevalueofcounterusinganexpressionorcondition)在同時(shí)和現(xiàn)在要做……之間有一個(gè)細(xì)微的區(qū)別…而循環(huán)。如果您注意到,do中的條件…而放置在循環(huán)指令的末尾。如果條件為false,則根本不執(zhí)行當(dāng)循環(huán)中的指令;但是,做…中的指令執(zhí)行循環(huán)時(shí),在計(jì)算條件之前執(zhí)行一次。所以,如果您想至少執(zhí)行一次指令,那么要做的是……而循環(huán)應(yīng)該是首選的比while循環(huán)。常用語句4.3.4循環(huán)語句例:pragmasolidity^0.4.19;contractDowhileLoop{mapping(uint=>uint)blockNumber;uintcounter;eventuintNumber(uint);bytesaa;functionSetNumber(){blockNumber[counter++]-block.number;}functiongetNumbers(){uinti=0;do{uintNumber(blockNumber[i]);i=i+1;}while(i<counter);}}常用語句4.3.4循環(huán)語句循環(huán)幫助從一開始迭代,直到它在向量數(shù)據(jù)類型上出現(xiàn)。但是,有時(shí)您希望在兩者之間停止迭代,并跳出或退出循環(huán),而無需再次執(zhí)行條件測(cè)試。打破聲明可以幫助我們做到這一點(diǎn)。它通過將控件傳遞給循環(huán)之后的第一個(gè)指令來幫助我們終止循環(huán)。在下面的代碼示例中,由于使用了break語句,當(dāng)i的值為1時(shí),for循環(huán)被終止,控件移出for循環(huán)。例:pragmasolidity0,4.19;contractForLoopExampleBreak{mapping(uint=>uint)blockNumbers;uintcounter;eventuintMumber(uint);functionSetNumber(){blockNumber[counter++]=block.number;}functiongetNumbers(){for(uinti=0;i<counter;i++){if(i==1)break;uintNumber(blockNumber[i]);}}}常用語句4.3.4break語句return是solidity函數(shù)的一個(gè)組成部分,共兩種不同的語法。在下面的代碼示例中,定義了兩個(gè)函數(shù)—getBlockNumber和getBlockNumber1。該函數(shù)返回一個(gè)uint,但不命名返回變量。在這種情況下,開發(fā)人員可以顯式地使用return關(guān)鍵字從函數(shù)中返回。函數(shù)返回uint,并提供了變量的名稱。在這種情況下,開發(fā)人員可以直接從函數(shù)返回這個(gè)變量,而不使用return關(guān)鍵字,如下所示:例:pragmasolidity^0.4.19;contractReturnValues{uintcounter;functionSetNumber(){counter=block.number;}functiongetBlockNumber()returns(uint){returncounter;functiongetBlockNumber1()returns(uintresult){result=counter;}}常用語句4.3.6return語句合約contractPART4.44.4.1創(chuàng)建合約合約可以通過以太坊交易“從外部”或從solidity合約內(nèi)部創(chuàng)建合約。一些集成開發(fā)環(huán)境,例如
Remix,通過使用一些UI用戶界面使創(chuàng)建合約的過程更加順暢。在以太坊上通過編程創(chuàng)建合約最好使用JavaScriptAPIweb3.js?,F(xiàn)在,我們已經(jīng)有了一個(gè)叫做
web3.eth.Contract
的方法能夠更容易的創(chuàng)建合約。創(chuàng)建合約時(shí),合約的
構(gòu)造函數(shù)(一個(gè)用關(guān)鍵字constructor聲明的函數(shù))會(huì)執(zhí)行一次。構(gòu)造函數(shù)是可選的。只允許有一個(gè)構(gòu)造函數(shù),這意味著不支持重載。構(gòu)造函數(shù)執(zhí)行完畢后,合約的最終代碼將部署到區(qū)塊鏈上。此代碼包括所有公共和外部函數(shù)以及所有可以通過函數(shù)調(diào)用訪問的函數(shù)。部署的代碼沒有包括構(gòu)造函數(shù)代碼或構(gòu)造函數(shù)調(diào)用的內(nèi)部函數(shù)。在內(nèi)部,構(gòu)造函數(shù)參數(shù)在合約代碼之后通過
ABI編碼
傳遞,但是如果你使用web3.js則不必關(guān)心這個(gè)問題。如果一個(gè)合約想要?jiǎng)?chuàng)建另一個(gè)合約,那么創(chuàng)建者必須知曉被創(chuàng)建合約的源代碼(和二進(jìn)制代碼)。這意味著不可能循環(huán)創(chuàng)建依賴項(xiàng)。4.4.2可見性和getter函數(shù)合約1.狀態(tài)變量可見性狀態(tài)變量有3種可見性:public:對(duì)于public狀態(tài)變量會(huì)自動(dòng)生成一個(gè)getterhanshu函數(shù)(見下面)。以便其他的合約讀取他們的值。當(dāng)在用一個(gè)合約里使用是,外部方式訪問(如:this.x)會(huì)調(diào)用getter函數(shù),而內(nèi)部方式訪問(如:x)會(huì)直接從存儲(chǔ)中獲取值。Setter函數(shù)則不會(huì)被生成,所以其他合約不能直接修改其值。internal:內(nèi)部可見性狀態(tài)變量只能在它們所定義的合約和派生合同中訪問。它們不能被外部訪問。這是狀態(tài)變量的默認(rèn)可見性。private:私有狀態(tài)變量就像內(nèi)部變量一樣,但它們?cè)谂缮霞s中是不可見的。4.4.2可見性和getter函數(shù)合約2.函數(shù)可見性由于solidity有兩種函數(shù)調(diào)用:外部調(diào)用則會(huì)產(chǎn)生一個(gè)EVM調(diào)用,而內(nèi)部調(diào)用不會(huì),更進(jìn)一步,函數(shù)可以確定器被內(nèi)部及派生合約的可訪問性,這里有4種可見性:external:外部可見性函數(shù)作為合約接口的一部分,意味著我們可以從其他合約和交易中調(diào)用。一個(gè)外部函數(shù)f不能從內(nèi)部調(diào)用(即f不起作用,但this.f()可以)。public:函數(shù)是合約接口的一部分,可以在內(nèi)部或通過消息調(diào)用。internal:內(nèi)部可見性函數(shù)訪問可以在當(dāng)前合約或派生的合約訪問,不可以外部訪問。由于它們沒有通過合約的ABI向外部公開,它們可以接受內(nèi)部可見性類型的參數(shù):比如映射或存儲(chǔ)引用。private:函數(shù)和狀態(tài)變量?jī)H在當(dāng)前定義它們的合約中使用,并且不能被派生合約使用。設(shè)置為private或internal,只能防止其他合約讀取或修改信息,但它仍然可以在鏈外查看到??梢娦詷?biāo)識(shí)符的定義位置,對(duì)于狀態(tài)變量來說是在類型后面,對(duì)于函數(shù)是在參數(shù)列表和返回關(guān)鍵字中間。4.4.2可見性和getter函數(shù)合約3.Getter函數(shù)編譯器自動(dòng)為所有public狀態(tài)變量創(chuàng)建getter函數(shù)。對(duì)于下面給出的合約,編譯器會(huì)生成一個(gè)名為data的函數(shù),該函數(shù)沒有參數(shù),返回值是一個(gè)uint類型,即狀態(tài)變量data的值。狀態(tài)變量的初始化可以在聲明時(shí)完成。4.4.3函數(shù)修改器合約使用修改器(modifier)可以輕松改變函數(shù)的行為。例如,它們可以在執(zhí)行函數(shù)之前自動(dòng)檢查某個(gè)條件。修改器(modifier)是合約的可繼承屬性,并可能被派生合約覆蓋,但前提是它們被標(biāo)記為virtual.。4.4.4Constant和Immutable狀態(tài)變量合約狀態(tài)變量聲明為constant(常量)或者immutable(不可變量),在這兩種情況下,合約一旦部署之后,變量將不在修改。對(duì)于constant常量,他的值在編譯器確定,而對(duì)于immutable,它的值在部署時(shí)確定。也可以在文件級(jí)別定義constant變量(注:0.7.2之后加入的特性)。編譯器不會(huì)為這些變量預(yù)留存儲(chǔ)位,它們的每次出現(xiàn)都會(huì)被替換為相應(yīng)的常量表達(dá)式(它可能被優(yōu)化器計(jì)算為實(shí)際的某個(gè)值)。與常規(guī)狀態(tài)變量相比,常量和不可變量的gas成本要低得多。對(duì)于常量,賦值給它的表達(dá)式將復(fù)制到所有訪問該常量的位置,并且每次都會(huì)對(duì)其進(jìn)行重新求值。這樣可以進(jìn)行本地優(yōu)化。不可變變量在構(gòu)造時(shí)進(jìn)行一次求值,并將其值復(fù)制到代碼中訪問它們的所有位置。對(duì)于這些值,將保留32個(gè)字節(jié),即使它們適合較少的字節(jié)也是如此。因此,常量有時(shí)可能比不可變量更便宜。不是所有類型的狀態(tài)變量都支持用constant或immutable來修飾,當(dāng)前僅支持字符串(僅常量)和值類型.4.4.4Constant和Immutable狀態(tài)變量合約1.constant如果狀態(tài)變量聲明為constant(常量)。在這種情況下,只能使用那些在編譯時(shí)有確定值的表達(dá)式來給它們賦值。任何通過訪問storage,區(qū)塊鏈數(shù)據(jù)(例如block.timestamp,address(this).balance或者block.number)或執(zhí)行數(shù)據(jù)(msg.value或gasleft())或?qū)ν獠亢霞s的調(diào)用來給它們賦值都是不允許的。允許可能對(duì)內(nèi)存分配產(chǎn)生副作用(side-effect)的表達(dá)式,但那些可能對(duì)其他內(nèi)存對(duì)象產(chǎn)生副作用的表達(dá)式則不允許。內(nèi)建(built-in)函數(shù)keccak256,sha256,ripemd160,ecrecover,addmod和mulmod是允許的(即使他們確實(shí)會(huì)調(diào)用外部合約,keccak256除外)。允許內(nèi)存分配器的副作用的原因是它可以構(gòu)造復(fù)雜的對(duì)象,例如:查找表(lookup-table)。此功能尚不完全可用。4.4.4Constant和Immutable狀態(tài)變量合約2.immutable聲明為不可變量(immutable)的變量的限制要比聲明為常量(constant)的變量的限制少:可以在合約的構(gòu)造函數(shù)中或聲明時(shí)為不可變的變量分配任意值。不可變量只能賦值一次,并且在賦值之后才可以讀取。編譯器生成的合約創(chuàng)建代碼將在返回合約之前修改合約的運(yùn)行時(shí)代碼,方法是將對(duì)不可變量的所有引用替換為分配給它們的值。如果要將編譯器生成的運(yùn)行時(shí)代碼與實(shí)際存儲(chǔ)在區(qū)塊鏈中的代碼進(jìn)行比較,則這一點(diǎn)很重要。不可變量可以在聲明時(shí)賦值,不過只有在合約的構(gòu)造函數(shù)執(zhí)行時(shí)才被視為視為初始化。這意味著,你不能用一個(gè)依賴于不可變量的值在行內(nèi)初始化另一個(gè)不可變量。不過,你可以在合約的構(gòu)造函數(shù)中這樣做。這是為了防止對(duì)狀態(tài)變量初始化和構(gòu)造函數(shù)順序的不同解釋,特別是繼承時(shí),出現(xiàn)問題。4.4.5函數(shù)合約可以在合約內(nèi)部和外部定義函數(shù)。合約之外的函數(shù)(也稱為“自由函數(shù)”)始終具有隱式的internal可見性。它們的代碼包含在所有調(diào)用它們合約中,類似于內(nèi)部庫函數(shù)。例:pragmasolidity>=0.7.1<0.9.0;functionsum(uint[]memoryarr)purereturns(uints){for(uinti=0;i<arr.length;i++)s+=arr[i];}contractArrayExample{boolfound;functionf(uint[]memoryarr)public{//Thiscallsthefreefunctioninternally.//Thecompilerwilladditscodetothecontract.uints=sum(arr);require(s>=10);found=true;}}4.4.5函數(shù)合約1.函數(shù)參數(shù)及返回值與Javascript一樣,函數(shù)可能需要參數(shù)作為輸入;而與Javascript和C不同的是,它們可能返回任意數(shù)量的參數(shù)作為輸出。(1)函數(shù)參數(shù)(輸入?yún)?shù))函數(shù)參數(shù)的聲明方式與變量相同。不過未使用的參數(shù)可以省略參數(shù)名。例如,如果我們希望合約接受有兩個(gè)整數(shù)形參的函數(shù)的外部調(diào)用,可以像下面這樣寫:pragmasolidity>=0.4.16<0.9.0;contractSimple{uintsum;functiontaker(uinta,uintb)public{sum=a+b;}}函數(shù)參數(shù)可以當(dāng)作為本地變量,也可用在等號(hào)左邊被賦值。4.4.5函數(shù)合約(2)返回變量函數(shù)返回變量的聲明方式在關(guān)鍵詞returns之后,與參數(shù)的聲明方式相同。例如,如果我們需要返回兩個(gè)結(jié)果:兩個(gè)給定整數(shù)的和與積,我們應(yīng)該寫作:pragmasolidity>=0.4.16<0.9.0;contractSimple{functionarithmetic(uinta,uintb)publicpurereturns(uintsum,uintproduct){sum=a+b;product=a*b;}}4.4.5函數(shù)合約返回變量名可以被省略。返回變量可以當(dāng)作為函數(shù)中的本地變量,沒有顯式設(shè)置的話,會(huì)使用:ref:`默認(rèn)值<default-value>`返回變量可以顯式給它附一個(gè)值(像上面),也可以使用return語句指定,使用return語句可以一個(gè)或多個(gè)值,參閱multipleones。pragmasolidity>=0.4.16<0.9.0;contractSimple{functionarithmetic(uinta,uintb)publicpurereturns(uintsum,uintproduct){return(a+b,a*b);}}4.4.5函數(shù)合約這個(gè)形式等同于賦值給返回參數(shù),然后用return;退出。如果使用return提前退出有返回值的函數(shù),必須在用return時(shí)提供返回值。非內(nèi)部函數(shù)有些類型沒法返回,這些類型如下,以及把他們的組合:mappings(映射),內(nèi)部函數(shù)類型,指向存儲(chǔ)storage的引用類型,多維數(shù)組(僅適用于ABIcoderv1),機(jī)構(gòu)體(僅適用于ABIcoderv1).這些限制不使用與庫函數(shù),因?yàn)樗麄兪遣煌膇nternalABI.(3)返回多個(gè)值當(dāng)函數(shù)需要使用多個(gè)值,可以用語句return(v0,v1,...,vn)。參數(shù)的數(shù)量需要和聲明時(shí)候一致。4.4.5函數(shù)合約2.狀態(tài)可變性(1)View視圖函數(shù)可以將函數(shù)聲明為view類型,這種情況下要保證不修改狀態(tài)。如果編譯器的EVM目標(biāo)是拜占庭硬分叉或更新的(默認(rèn)),則操作碼STATICCALL將用于視圖函數(shù),這些函數(shù)強(qiáng)制在EVM執(zhí)行過程中保持不修改狀態(tài)。對(duì)于庫視圖函數(shù),使用DELLEGATECALL,因?yàn)闆]有組合的DELEGATECALL和STATICALL。這意味著庫視圖函數(shù)不會(huì)在運(yùn)行時(shí)檢查進(jìn)而阻止?fàn)顟B(tài)修改。這不會(huì)對(duì)安全性產(chǎn)生負(fù)面影響,因?yàn)閹齑a通常在編譯時(shí)知道,并且靜態(tài)檢查器會(huì)執(zhí)行編譯時(shí)檢查。下面的語句被認(rèn)為是修改狀態(tài):修改狀態(tài)變量。產(chǎn)生事件。創(chuàng)建其它合約。使用selfdestruct。通過調(diào)用發(fā)送以太幣。調(diào)用任何沒有標(biāo)記為view或者pure的函數(shù)。使用低級(jí)調(diào)用。使用包含特定操作碼的內(nèi)聯(lián)匯編4.4.5函數(shù)合約2.狀態(tài)可變性(1)View視圖函數(shù)可以將函數(shù)聲明為view類型,這種情況下要保證不修改狀態(tài)。如果編譯器的EVM目標(biāo)是拜占庭硬分叉或更新的(默認(rèn)),則操作碼STATICCALL將用于視圖函數(shù),這些函數(shù)強(qiáng)制在EVM執(zhí)行過程中保持不修改狀態(tài)。對(duì)于庫視圖函數(shù),使用DELLEGATECALL,因?yàn)闆]有組合的DELEGATECALL和STATICALL。這意味著庫視圖函數(shù)不會(huì)在運(yùn)行時(shí)檢查進(jìn)而阻止?fàn)顟B(tài)修改。這不會(huì)對(duì)安全性產(chǎn)生負(fù)面影響,因?yàn)閹齑a通常在編譯時(shí)知道,并且靜態(tài)檢查器會(huì)執(zhí)行編譯時(shí)檢查。下面的語句被認(rèn)為是修改狀態(tài):修改狀態(tài)變量。產(chǎn)生事件。創(chuàng)建其它合約。使用selfdestruct。通過調(diào)用發(fā)送以太幣。調(diào)用任何沒有標(biāo)記為view或者pure的函數(shù)。使用低級(jí)調(diào)用。使用包含特定操作碼的內(nèi)聯(lián)匯編。pragmasolidity>=0.5.0<0.9.0;contractC{functionf(uinta,uintb)publicviewreturns(uint){returna*(b+42)+block.timestamp;
}}4.4.5函數(shù)合約(2)Pure純函數(shù)函數(shù)可以聲明為pure,在這種情況下,承諾不讀取也不修改狀態(tài)變量。特別是,應(yīng)該可以在編譯時(shí)確定一個(gè)pure函數(shù),它僅處理輸入?yún)?shù)和msg.data,對(duì)當(dāng)前區(qū)塊鏈狀態(tài)沒有任何了解。這也意味著讀取immutable變量也不是一個(gè)pure操作。如果編譯器的EVM編譯目標(biāo)設(shè)置為Byzantium或之后的版本(默認(rèn)),則使用操作碼STATICCALL,這并不保證狀態(tài)未被讀取,但至少不被修改。除了上面解釋的狀態(tài)修改語句列表之外,以下被認(rèn)為是讀取狀態(tài):讀取狀態(tài)變量。訪問address(this).balance或者<address>.balance。訪問block,tx,msg中任意成員(除msg.sig和msg.data之外)。調(diào)用任何未標(biāo)記為pure的函數(shù)。使用包含某些操作碼的內(nèi)聯(lián)匯編。pragmasolidity>=0.5.0<0.9.0;contractC{functionf(uinta,uintb)publicpurereturns(uint){returna*(b+42);}}4.4.5函數(shù)合約3.特別的函數(shù)(1)receive接收以太函數(shù)一個(gè)合約最多有一個(gè)receive函數(shù),聲明函數(shù)為:receive()externalpayable{...}不需要function關(guān)鍵字,也沒有參數(shù)和返回值并且必須是external可見性和payable修飾.它可以是virtual的,可以被重載也可以有修改器modifier。在對(duì)合約沒有任何附加數(shù)據(jù)調(diào)用(通常是對(duì)合約轉(zhuǎn)賬)是會(huì)執(zhí)行receive函數(shù).例如通過.send()or.transfer()如果receive函數(shù)不存在,但是有payable的fallback回退函數(shù)那么在進(jìn)行純以太轉(zhuǎn)賬時(shí),fallback函數(shù)會(huì)調(diào)用.如果兩個(gè)函數(shù)都沒有,這個(gè)合約就沒法通過常規(guī)的轉(zhuǎn)賬交易接收以太(會(huì)拋出異常).更糟的是,receive函數(shù)可能只有2300gas可以使用(如,當(dāng)使用send或transfer時(shí)),除了基礎(chǔ)的日志輸出之外,進(jìn)行其他操作的余地很小。下面的操作消耗會(huì)操作2300gas:寫入存儲(chǔ)創(chuàng)建合約調(diào)用消耗大量gas的外部函數(shù)發(fā)送以太幣4.4.5函數(shù)合約(2)Fallback回退函數(shù)合約可以最多有一個(gè)回退函數(shù)。函數(shù)聲明為:fallback()external[payable]或fallback(bytescalldatainput)external[payable]returns(bytesmemoryoutput)沒有function關(guān)鍵字。必須是external可見性,它可以是virtual的,可以被重載也可以有修改器modifier。如果在一個(gè)對(duì)合約調(diào)用中,沒有其他函數(shù)與給定的函數(shù)標(biāo)識(shí)符匹配fallback會(huì)被調(diào)用.或者在沒有receive函數(shù)時(shí),而沒有提供附加數(shù)據(jù)對(duì)合約調(diào)用,那么fallback函數(shù)會(huì)被執(zhí)行。fallback函數(shù)始終會(huì)接收數(shù)據(jù),但為了同時(shí)接收以太時(shí),必須標(biāo)記為payable。如果使用了帶參數(shù)的版本,input將包含發(fā)送到合約的完整數(shù)據(jù)(等于msg.data),并且通過output返回?cái)?shù)據(jù)。返回?cái)?shù)據(jù)不是ABI編碼過的數(shù)據(jù),相反,它返回不經(jīng)過修改的數(shù)據(jù)。更糟的是,如果回退函數(shù)在接收以太時(shí)調(diào)用,可能只有2300gas可以使用,參考receive接收函數(shù)4.4.5函數(shù)合約4.函數(shù)重載合約可以具有多個(gè)不同參數(shù)的同名函數(shù),稱為“重載”(overloading),這也適用于繼承函數(shù)。以下示例展示了合約A中的重載函數(shù)f。pragmasolidity>=0.4.16<0.9.0;contractA{functionf(uintvalue)publicpurereturns(uintout){out=value;}functionf(uintvalue,boolreally)publicpurereturns(uintout){if(really)out=value;}}4.4.6事件合約1.事件成員event.selector:對(duì)于非匿名事件,這是一個(gè)bytes32值,包含事件簽名的keccak256哈希值,在默認(rèn)主題中使用。示例:pragmasolidity>=0.4.21<0.9.0;contractClientReceipt{eventDeposit(addressindexedfrom,bytes32indexedid,uintvalue);functiondeposit(bytes32id)publicpayable{//事件使用emit觸發(fā)事件。//我們可以過濾對(duì)`Deposit`的調(diào)用,從而用JavascriptAPI來查明對(duì)這個(gè)函數(shù)的任何調(diào)用(甚至是深度嵌套調(diào)用)。emitDeposit(msg.sender,id,msg.value);}}4.4.6事件合約使用JavaScriptAPI調(diào)用事件的用法如下:varabi=/*abi由編譯器產(chǎn)生*/;varClientReceipt=web3.eth.contract(abi);varclientReceipt=ClientReceipt.at("0x1234...xlb67"/*地址*/);vardepositEvent=clientReceipt.Deposit();//監(jiān)聽變化depositEvent.watch(function(error,result){//結(jié)果包含非索引參數(shù)以及主題topicif(!error)console.log(result);});//或者通過傳入回調(diào)函數(shù),立即開始聽監(jiān)vardepositEvent=clientReceipt.Deposit(function(error,result){if(!error)console.log(result);});4.4.6事件合約上面的輸出如下所示(有刪減):{"returnValues":{"from":"0x1111…FFFFCCCC","id":"0x50…sd5adb20","value":"0x420042"},"raw":{"data":"0x7f…91385","topics":["0xfd4…b4ead7","0x7f…1a91385"]}}4.4.7錯(cuò)誤和回退語句合約solidity中的錯(cuò)誤(關(guān)鍵字error)提供了一種方便且省gas的方式來向用戶解釋為什么一個(gè)操作會(huì)失敗。它們可以被定義在合約(包括接口和庫)內(nèi)部和外部。錯(cuò)誤必須與revert語句一起使用。它會(huì)還原當(dāng)前調(diào)用中的發(fā)生的所有變化,并將錯(cuò)誤數(shù)據(jù)傳回給調(diào)用者。pragmasolidity^0.8.4;errorInsufficientBalance(uint256available,uint256required);contractTestToken{mapping(address=>uint)balance;functiontransfer(addressto,uint256amount)public{if(amount>balance[msg.sender])revertInsufficientBalance({available:balance[msg.sender],required:amount});balance[msg.sender]-=amount;balance[to]+=amount;}//...}4.4.8繼承合約1.函數(shù)重寫父合約標(biāo)記為virtual函數(shù)可以在繼承合約里重寫(overridden)以更改他們的行為。重寫的函數(shù)需要使用關(guān)鍵字override修飾。重寫函數(shù)只能將覆蓋函數(shù)的可見性從external更改為public??勺冃钥梢园凑找韵马樞蚋臑楦鼑?yán)格的一種:nonpayable可以被view和pure覆蓋。view可以被pure覆蓋。payable是一個(gè)例外,不能更改為任何其他可變性。以下示例演示了可變性和可見性的變化:pragmasolidity>=0.7.0<0.9.0;contractBase{functionfoo()virtualexternalview{}}contractMiddleisBase{}contractInheritedisMiddle{
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 七年級(jí)下《皇帝的新裝》蘇教版-課件
- (高頻非選擇題25題)第1單元 中華人民共和國的成立和鞏固(解析版)
- 2019年高考語文試卷(新課標(biāo)Ⅰ卷)(解析卷)
- 2015年高考語文試卷(新課標(biāo)Ⅱ卷)(解析卷)
- 顏料產(chǎn)業(yè)智能化升級(jí)-洞察分析
- 胃石癥藥物療效評(píng)估-洞察分析
- 眼瞼水腫診療研究-洞察分析
- 土地資源價(jià)值評(píng)估方法比較-洞察分析
- 線粒體氧化應(yīng)激研究-洞察分析
- 維修行業(yè)風(fēng)險(xiǎn)管理-洞察分析
- 德語語言學(xué)導(dǎo)論智慧樹知到期末考試答案章節(jié)答案2024年中國海洋大學(xué)
- 檢驗(yàn)試劑實(shí)施方案范文
- JT-T-1078-2016道路運(yùn)輸車輛衛(wèi)星定位系統(tǒng)視頻通信協(xié)議
- 2024-2029年中國人工骨行業(yè)發(fā)展分析及發(fā)展前景與趨勢(shì)預(yù)測(cè)研究報(bào)告
- 2024年高校教師資格證資格考試試題庫及答案(各地真題)
- 扭虧增盈提質(zhì)增效方案
- 侵權(quán)法智慧樹知到期末考試答案章節(jié)答案2024年四川大學(xué)
- 期末考試卷2《心理健康與職業(yè)生涯》(解析卷)高一思想政治課(高教版2023基礎(chǔ)模塊)
- 年度安全生產(chǎn)投入臺(tái)賬(詳細(xì)模板)
- 中醫(yī)病歷書寫基本規(guī)范本
- 一年級(jí)帶拼音閱讀
評(píng)論
0/150
提交評(píng)論