學(xué)習(xí)資料:GitHub - AmazingAng/WTFSolidity: 我最近在重新學(xué)solidity,鞏固一下細(xì)節(jié),也寫一個“Solidity極簡入門”,供小白們使用(編程大佬可以另找教程),每周更新1-3講。
變量類型
- 數(shù)值類型(Value Type):包括布爾型,整數(shù)型等等,這類變量賦值時候直接傳遞數(shù)值。
- 引用類型(Reference Type):包括數(shù)組和結(jié)構(gòu)體,這類變量占空間大,賦值時候直接傳遞地址(類似指針)。
-
映射類型(Mapping Type):?
Solidity
里的哈希表。 -
函數(shù)類型(Function Type):
Solidity
文檔里把函數(shù)歸到數(shù)值類型,但我覺得他跟其他類型差別很大,所以單獨(dú)分一類。
一、數(shù)值類型
1.布爾型(bool):true 或者 false
2.整型(uint):uint、uint8、uint16、uint32、uint256
????????整型數(shù)值沒有負(fù)數(shù)
3.地址類型(address):地址類型(address)存儲一個 20 字節(jié)的值(以太坊地址的大小)
4.定長字節(jié)數(shù)組:字節(jié)數(shù)組bytes
分兩種,一種定長(byte
,?bytes8
,?bytes32
),另一種不定長
5.枚舉型(enum):枚舉(enum
)是solidity
中用戶定義的數(shù)據(jù)類型
二、引用類型
1.數(shù)組(Array):數(shù)組(Array
)是solidity
常用的一種變量類型,用來存儲一組數(shù)據(jù)(整數(shù),字節(jié),地址等等)。數(shù)組分為固定長度數(shù)組和可變長度數(shù)組兩種
數(shù)組成員:
-
length
: 數(shù)組有一個包含元素數(shù)量的length
成員,memory
數(shù)組的長度在創(chuàng)建后是固定的。 -
push()
:?動態(tài)數(shù)組
和bytes
擁有push()
成員,可以在數(shù)組最后添加一個0
元素。 -
push(x)
:?動態(tài)數(shù)組
和bytes
擁有push(x)
成員,可以在數(shù)組最后添加一個x
元素。 -
pop()
:?動態(tài)數(shù)組
和bytes
擁有pop()
成員,可以移除數(shù)組最后一個元素。
2.結(jié)構(gòu)體(struct):Solidity
支持通過構(gòu)造結(jié)構(gòu)體的形式定義新的類型
// 結(jié)構(gòu)體用法
struct Student{
uint256 id;
uint256 score;
}
三、映射類型
Mapping:聲明映射的格式為mapping(_KeyType => _ValueType)
映射的規(guī)則:
-
規(guī)則1:映射的
_KeyType
只能選擇solidity
默認(rèn)的類型,比如uint
,address
等,不能用自定義的結(jié)構(gòu)體。 -
規(guī)則2:映射的存儲位置必須是
storage
,因此可以用于合約的狀態(tài)變量,函數(shù)中的storage
變量。不能用于public
函數(shù)的參數(shù)或返回結(jié)果中,因為mapping
記錄的是一種關(guān)系 (key - value pair)。 -
規(guī)則3:如果映射聲明為
public
,那么solidity
會自動給你創(chuàng)建一個getter
函數(shù),可以通過Key
來查詢對應(yīng)的Value
。 -
規(guī)則4:給映射新增的鍵值對的語法為
_Var[_Key] = _Value
,其中_Var
是映射變量名,_Key
和_Value
對應(yīng)新增的鍵值對
四、函數(shù)類型
1.function
:聲明函數(shù)時的固定用法,想寫函數(shù),就要以function關(guān)鍵字開頭。
2.(<parameter types>)
:圓括號里寫函數(shù)的參數(shù),也就是要輸入到函數(shù)的變量類型和名字。
3.{internal|external|public|private}
:函數(shù)可見性說明符,一共4種。沒標(biāo)明函數(shù)類型的,默認(rèn)internal
。
-
public
: 內(nèi)部外部均可見。(也可用于修飾狀態(tài)變量,public變量會自動生成?getter
函數(shù),用于查詢數(shù)值). -
private
: 只能從本合約內(nèi)部訪問,繼承的合約也不能用(也可用于修飾狀態(tài)變量)。 -
external
: 只能從合約外部訪問(但是可以用this.f()
來調(diào)用,f
是函數(shù)名) -
internal
: 只能從合約內(nèi)部訪問,繼承的合約可以用(也可用于修飾狀態(tài)變量)。
4.[pure|view|payable]
:決定函數(shù)權(quán)限/功能的關(guān)鍵字。
pure和view都不需要付gas
- pure:不能讀取也不能寫入
- view:只能讀取
- payable:可支付
5.[returns ()]
:函數(shù)返回的變量類型和名稱。
常量
一、constant
constant變量必須在聲明的時候初始化,之后再也不能改變。嘗試改變的話,編譯不通過。
二、immutable
immutable
變量可以在聲明時或構(gòu)造函數(shù)中初始化,因此更加靈活。
常用方法
1.修飾器(modifier)
修飾器(modifier
)是solidity特有的語法,類似于面向?qū)ο缶幊讨械?code>decorator,聲明函數(shù)擁有的特性,并減少代碼冗余。
// 定義modifier
modifier onlyOwner {
require(msg.sender == owner); // 檢查調(diào)用者是否為owner地址
_; // 如果是的話,繼續(xù)運(yùn)行函數(shù)主體;否則報錯并revert交易
}
2.構(gòu)造函數(shù)(constructor)
構(gòu)造函數(shù)(constructor
)是一種特殊的函數(shù),每個合約可以定義一個,并在部署合約的時候自動運(yùn)行一次。它可以用來初始化合約的一些參數(shù),例如初始化合約的owner
地址:
address owner; // 定義owner變量
// 構(gòu)造函數(shù)
constructor() public {
owner = msg.sender; // 在部署合約的時候,將owner設(shè)置為部署者的地址
}
3.事件(events)
Solidity
中的事件(event
)是EVM
上日志的抽象,它具有兩個特點:
- 響應(yīng):應(yīng)用程序(
ether.js
)可以通過RPC
接口訂閱和監(jiān)聽這些事件,并在前端做響應(yīng)。 - 經(jīng)濟(jì):事件是
EVM
上比較經(jīng)濟(jì)的存儲數(shù)據(jù)的方式,每個大概消耗2,000-5,000?gas
不等。相比之下,存儲一個新的變量至少需要20,000?gas
。
事件的聲明由event
關(guān)鍵字開頭,然后跟事件名稱,括號里面寫好事件需要記錄的變量類型和變量名。以ERC20
代幣合約的Transfer
事件為例:
event Transfer(address indexed from, address indexed to, uint256 value);
4.繼承(inheritance)
規(guī)則
virtual
: 父合約中的函數(shù),如果希望子合約重寫,需要加上virtual
關(guān)鍵字。
override
:子合約重寫了父合約中的函數(shù),需要加上override
關(guān)鍵。
簡單繼承
contract Baba is Yeye{}
多重繼承
contract Erzi is Yeye, Baba{}
修飾器的繼承
用法同函數(shù)繼承
構(gòu)造函數(shù)的繼承
- 在繼承時聲明父構(gòu)造函數(shù)的參數(shù),例如:
contract B is A(1)
- 在子合約的構(gòu)造函數(shù)中聲明構(gòu)造函數(shù)的參數(shù)
5.異常(errors)
error:方便高效省gas
error TransferNotOwner(); // 自定義error,在執(zhí)行當(dāng)中,error必須搭配revert(回退)命令使用。
function transferOwner1(uint256 tokenId, address newOwner) public {
if(_owners[tokenId] != msg.sender){
revert TransferNotOwner();
}
_owners[tokenId] = newOwner;
}
require:gas隨著描述異常的字符串長度增加
使用方法:require(檢查條件,”異常的描述”)
,當(dāng)檢查條件
不成立的時候,就會拋出異常。
我們用require
命令重寫一下上面的transferOwner
函數(shù):
function transferOwner2(uint256 tokenId, address newOwner) public {
require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
_owners[tokenId] = newOwner;
}
assert命令一般用于程序員寫程序debug,因為他不能解釋拋出異常的原因(比require
少個字符串)。他的用法很簡單,assert(檢查條件)
,當(dāng)檢查條件
不成立的時候,就會拋出異常。
function transferOwner3(uint256 tokenId, address newOwner) public {
assert(_owners[tokenId] == msg.sender);
_owners[tokenId] = newOwner;
}
6.安全數(shù)學(xué)(SafeMath)
SafeMath用來防止溢出,有四個方法 —?add
,?sub
,?mul
, 以及?div。
using SafeMath for uint256;
uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10
7.import
solidity
支持利用import
關(guān)鍵字導(dǎo)入其他源代碼中的合約,讓開發(fā)更加模塊化。
// 通過文件相對位置import
import './Yeye.sol';
8.接收ETH
Solidity
支持兩種特殊的回調(diào)函數(shù),receive()
和fallback()
,他們主要在兩種情況下被使用:
- 接收
ETH
- 處理合約中不存在的函數(shù)調(diào)用(代理合約
proxy contract
)
接收ETH函數(shù) receive
receive()
只用于處理接收ETH
。一個合約最多有一個receive()
函數(shù),聲明方式與一般函數(shù)不一樣,不需要function
關(guān)鍵字:receive() external payable { ... }
receive()
函數(shù)不能有任何的參數(shù),不能返回任何值,必須包含external
和payable
。
當(dāng)合約接收ETH的時候,receive()
會被觸發(fā)。
回退函數(shù) fallback
fallback()
函數(shù)會在調(diào)用合約不存在的函數(shù)時被觸發(fā)。可用于接收ETH,也可以用于代理合約proxy contract
。fallback()
聲明時不需要function
關(guān)鍵字,必須由external
修飾,一般也會用payable
修飾,用于接收ETH:fallback() external payable { ... }
。
receive
和fallback
都能夠用于接收ETH
,他們觸發(fā)的規(guī)則如下:
觸發(fā)fallback() 還是 receive()?
接收ETH
|
msg.data是空?
/ \
是 否
/ \
receive()存在? fallback()
/ \
是 否
/ \
receive() fallback()
9.發(fā)送ETH
transfer
- 用法是
transfer(發(fā)送ETH數(shù)額)
。 -
transfer()
的gas
限制是2300
,足夠用于轉(zhuǎn)賬,但對方合約的fallback()
或receive()
函數(shù)不能實現(xiàn)太復(fù)雜的邏輯。 -
transfer()
如果轉(zhuǎn)賬失敗,會自動revert
(回滾交易)。
send
- 用法是
send(發(fā)送ETH數(shù)額)
。 -
send()
的gas
限制是2300
,足夠用于轉(zhuǎn)賬,但對方合約的fallback()
或receive()
函數(shù)不能實現(xiàn)太復(fù)雜的邏輯。 -
send()
如果轉(zhuǎn)賬失敗,不會revert
。 -
send()
的返回值是bool
,代表著轉(zhuǎn)賬成功或失敗,需要額外代碼處理一下。
call
- 用法是
call{value: 發(fā)送ETH數(shù)額}("")
。 -
call()
沒有gas
限制,可以支持對方合約fallback()
或receive()
函數(shù)實現(xiàn)復(fù)雜邏輯。 -
call()
如果轉(zhuǎn)賬失敗,不會revert
。 -
call()
的返回值是(bool, data)
,其中bool
代表著轉(zhuǎn)賬成功或失敗,需要額外代碼處理一下。
10.call
call
?是address
類型的低級成員函數(shù),它用來與其他合約交互。它的返回值為(bool, data)
,分別對應(yīng)call
是否成功以及目標(biāo)函數(shù)的返回值。
call的使用規(guī)則
call
的使用規(guī)則如下:
目標(biāo)合約地址.call(二進(jìn)制編碼);
其中二進(jìn)制編碼
利用結(jié)構(gòu)化編碼函數(shù)abi.encodeWithSignature
獲得:
abi.encodeWithSignature("函數(shù)簽名", 逗號分隔的具體參數(shù))
函數(shù)簽名
為"函數(shù)名(逗號分隔的參數(shù)類型)"
。例如abi.encodeWithSignature("f(uint256,address)", _x, _addr)
。
另外call
在調(diào)用合約時可以指定交易發(fā)送的ETH
數(shù)額和gas
:
目標(biāo)合約地址.call{value:發(fā)送ETH數(shù)額, gas:gas數(shù)額}(二進(jìn)制編碼);
11.ABI編碼解碼
abi.encode
將給定參數(shù)利用ABI規(guī)則編碼。ABI
被設(shè)計出來跟智能合約交互,他將每個參數(shù)轉(zhuǎn)填充為32字節(jié)的數(shù)據(jù),并拼接在一起。
abi.decode
abi.decode
用于解碼abi.encode
生成的二進(jìn)制編碼,將它還原成原本的參數(shù)。
12.函數(shù)選擇器(Selector)
?selector
定義為函數(shù)簽名
的哈希的前4個字節(jié)
函數(shù)簽名,為"函數(shù)名(逗號分隔的參數(shù)類型)"
。舉個例子,mint
的函數(shù)簽名為"mint(address)"
。在智能合約中,不同的函數(shù)有不同的函數(shù)簽名,因此我們可以通過函數(shù)簽名來確定要調(diào)用哪個函數(shù)。
13.try-catch
在solidity
中,try-catch
只能被用于external
函數(shù)或創(chuàng)建合約時constructor
(被視為external
函數(shù))的調(diào)用?;菊Z法如下:
try externalContract.f() {
// call成功的情況下 運(yùn)行一些代碼
} catch {
// call失敗的情況下 運(yùn)行一些代碼
}
其中externalContract.f()
時某個外部合約的函數(shù)調(diào)用,try
模塊在調(diào)用成功的情況下運(yùn)行,而catch
模塊則在調(diào)用失敗時運(yùn)行。
應(yīng)用
1.ERC20
IERC20
是ERC20
代幣標(biāo)準(zhǔn)的接口合約,規(guī)定了ERC20
代幣需要實現(xiàn)的函數(shù)和事件。
事件
IERC20
定義了2
個事件:Transfer
事件和Approval
事件,分別在轉(zhuǎn)賬和授權(quán)時被釋放
/**
* @dev 釋放條件:當(dāng) `value` 單位的貨幣從賬戶 (`from`) 轉(zhuǎn)賬到另一賬戶 (`to`)時.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev 釋放條件:當(dāng) `value` 單位的貨幣從賬戶 (`owner`) 授權(quán)給另一賬戶 (`spender`)時.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
函數(shù)
IERC20
定義了6
個函數(shù),提供了轉(zhuǎn)移代幣的基本功能,并允許代幣獲得批準(zhǔn),以便其他鏈上第三方使用。
-
totalSupply()
返回代幣總供給 -
balanceOf()
返回賬戶余額 -
transfer()
轉(zhuǎn)賬 -
allowance()
返回授權(quán)額度 -
approve()
授權(quán) -
transferFrom()
授權(quán)轉(zhuǎn)賬
2.erc721
IERC721事件
IERC721
包含3個事件,其中Transfer
和Approval
事件在ERC20
中也有。文章來源:http://www.zghlxwxcb.cn/news/detail-784666.html
-
Transfer
事件:在轉(zhuǎn)賬時被釋放,記錄代幣的發(fā)出地址from
,接收地址to
和tokenid
。 -
Approval
事件:在授權(quán)時釋放,記錄授權(quán)地址owner,被授權(quán)地址
approved和
tokenid`。 -
ApprovalForAll
事件:在批量授權(quán)時釋放,記錄批量授權(quán)的發(fā)出地址owner
,被授權(quán)地址operator
和授權(quán)與否的approved
。
IERC721函數(shù)
-
balanceOf
:返回某地址的NFT持有量balance
。 -
ownerOf
:返回某tokenId
的主人owner
。 -
transferFrom
:普通轉(zhuǎn)賬,參數(shù)為轉(zhuǎn)出地址from
,接收地址to
和tokenId
。 -
safeTransferFrom
:安全轉(zhuǎn)賬(如果接收方是合約地址,會要求實現(xiàn)ERC721Receiver
接口)。參數(shù)為轉(zhuǎn)出地址from
,接收地址to
和tokenId
。 -
approve
:授權(quán)另一個地址使用你的NFT。參數(shù)為被授權(quán)地址approve
和tokenId
。 -
getApproved
:查詢tokenId
被批準(zhǔn)給了哪個地址。 -
setApprovalForAll
:將自己持有的該系列NFT批量授權(quán)給某個地址operator
。 -
isApprovedForAll
:查詢某地址的NFT是否批量授權(quán)給了另一個operator
地址。 -
safeTransferFrom
:安全轉(zhuǎn)賬的重載函數(shù),參數(shù)里面包含了data
。
3.erc1155
面是ERC1155
的元數(shù)據(jù)接口合約IERC1155MetadataURI
:文章來源地址http://www.zghlxwxcb.cn/news/detail-784666.html
/**
* @dev ERC1155的可選接口,加入了uri()函數(shù)查詢元數(shù)據(jù)
*/
interface IERC1155MetadataURI is IERC1155 {
/**
* @dev 返回第`id`種類代幣的URI
*/
function uri(uint256 id) external view returns (string memory);
IERC1155事件
-
TransferSingle
事件:單類代幣轉(zhuǎn)賬事件,在單幣種轉(zhuǎn)賬時釋放。 -
TransferBatch
事件:批量代幣轉(zhuǎn)賬事件,在多幣種轉(zhuǎn)賬時釋放。 -
ApprovalForAll
事件:批量授權(quán)事件,在批量授權(quán)時釋放。 -
URI
事件:元數(shù)據(jù)地址變更事件,在uri
變化時釋放。
IERC1155函數(shù)
-
balanceOf()
:單幣種余額查詢,返回account
擁有的id
種類的代幣的持倉量。 -
balanceOfBatch()
:多幣種余額查詢,查詢的地址accounts
數(shù)組和代幣種類ids
數(shù)組的長度要相等。 -
setApprovalForAll()
:批量授權(quán),將調(diào)用者的代幣授權(quán)給operator
地址。。 -
isApprovedForAll()
:查詢批量授權(quán)信息,如果授權(quán)地址operator
被account
授權(quán),則返回true
。 -
safeTransferFrom()
:安全單幣轉(zhuǎn)賬,將amount
單位id
種類的代幣從from
地址轉(zhuǎn)賬給to
地址。如果to
地址是合約,則會驗證是否實現(xiàn)了onERC1155Received()
接收函數(shù)。 -
safeBatchTransferFrom()
:安全多幣轉(zhuǎn)賬,與單幣轉(zhuǎn)賬類似,只不過轉(zhuǎn)賬數(shù)量amounts
和代幣種類ids
變?yōu)閿?shù)組,且長度相等。如果to
地址是合約,則會驗證是否實現(xiàn)了onERC1155BatchReceived()
接收函數(shù)。
到了這里,關(guān)于WEB3之路(一)-- solidity學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!