區(qū)塊鏈語(yǔ)法筆記
demo 鑄幣代碼
pragma solidity ^0.4;
contract Coin{
//set the "address" type variable minter
address public minter;
/*convert "address"(for storing address or key )
to the type of "uint" which is as subscrip of object balances*/
mapping (address =>uint) public balances;
// set an event so as to be seen publicly
event Sent(address from,address to,uint amount);
//constructor only run once when creating contract,unable to invoke
//"msg" is the address of creator."msg.sender" is
constructor()public{
minter=msg.sender;
}
//鑄幣
//can only be called by creator
function mint(address receiver,uint amount)public{
require(msg.sender ==minter);
balances[receiver]+=amount;
}
//轉(zhuǎn)賬
function send(address receiver,uint amount)public{
require(balances[msg.sender]>= amount);
balances[msg.sender]-=amount;
balances[receiver]+=amount;
emit Sent(msg.sender,receiver,amount);
}
}
源文件結(jié)構(gòu)
pragma 版本標(biāo)識(shí)
只對(duì)本文件有效,如果導(dǎo)入其他文件,版本標(biāo)識(shí)不會(huì)被導(dǎo)入,啟動(dòng)編譯器檢查
^0.5.2; #從0.5.2到0.6(不含)的版本
import 導(dǎo)入文件
import * as symbolName from "filename"; #等價(jià)
import "filename" as symbolName;
狀態(tài)變量
狀態(tài)變量是永久地存儲(chǔ)在合約存儲(chǔ)中的,有基本類(lèi)型.函數(shù)外的都是store狀態(tài)變量。
- bool,
- int/uint(有符號(hào)和無(wú)符號(hào))
- fixed / ufixed (有符號(hào)和無(wú)符號(hào)的定長(zhǎng)浮點(diǎn)型。)
在關(guān)鍵字 ufixedMxN 和 fixedMxN 中,M 表示該類(lèi)型占用的位數(shù),N 表示可用的小數(shù)位數(shù)。 M 必須能整除 8,即 8 到 256 位。 N 則可以是從 0 到 80 之間的任意數(shù)。 ufixed 和 fixed 分別是 ufixed128x19 和 fixed128x19 的別名
- address 地址類(lèi)型
address:保存一個(gè)20字節(jié)的值(以太坊地址的大?。?address payable :可支付地址,與 address 相同,不過(guò)有成員函數(shù) transfer 和 send
address payable 可以完成到 address 的隱式轉(zhuǎn)換,但是從 address 到 address payable 必須顯示的轉(zhuǎn)換, 通過(guò) payable(<address>) 進(jìn)行轉(zhuǎn)換
bytes1 定長(zhǎng)字節(jié)數(shù)組
bytes和string,uint[] 變長(zhǎng)字節(jié)數(shù)組
多維數(shù)組的下標(biāo)和一般是相反的,
a[2][4]表示4個(gè)子數(shù)列,每個(gè)子數(shù)列里2個(gè)元素
函數(shù)
function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
函數(shù)是代碼的可執(zhí)行單元。函數(shù)通常在合約內(nèi)部定義,但也可以在合約外定義。
函數(shù)可以作為參數(shù)傳入
可見(jiàn)性
https://solidity-by-example.org/visibility/
內(nèi)部(internal) 函數(shù)類(lèi)型,只能在當(dāng)前合約內(nèi)被調(diào)用
外部(external) 函數(shù)類(lèi)型,由一個(gè)地址和函數(shù)簽名組成,在調(diào)用時(shí)會(huì)被視作function類(lèi)型
external: 外部函數(shù)作為合約接口的一部分,可以被交易或者其他合約調(diào)用。 外部函數(shù) f 不能以內(nèi)部調(diào)用的方式調(diào)用(即 f 不起作用,但 this.f() 可以)。
public: public 函數(shù)是合約接口的一部分,可以在內(nèi)部或通過(guò)消息調(diào)用。對(duì)于 public 狀態(tài)變量, 會(huì)自動(dòng)生成一個(gè) getter 函數(shù)。
internal : 只能在當(dāng)前合約內(nèi)部或它子合約中訪問(wèn),不使用 this 調(diào)用。
private: private 函數(shù)和狀態(tài)變量?jī)H在當(dāng)前定義它們的合約中使用,并且不能被派生合約使用(如繼承)
有且僅有以下三種轉(zhuǎn)化:
-
pure函數(shù)可以轉(zhuǎn)換為view和non-payable函數(shù) -
view函數(shù)可以轉(zhuǎn)換為non-payable函數(shù) -
payable函數(shù)可以轉(zhuǎn)換為non-payable函數(shù)
參數(shù)和返回值
修飾符
https://solidity-by-example.org/function-modifier/
函數(shù)修飾符用來(lái)修飾函數(shù),比如添加函數(shù)執(zhí)行前必須的先決條件.函數(shù)修飾器通過(guò)繼承在派生合約中起作用。
modifier onlyOwner { 函數(shù)體會(huì)插入在修飾函數(shù)的下劃線_的位置
require(msg.sender == owner);
_;
}
如果一個(gè)函數(shù)中有許多修飾器,寫(xiě)法上以空格隔開(kāi),執(zhí)行時(shí)依次執(zhí)行:首先進(jìn)入第一個(gè)函數(shù)修飾器,然后一直執(zhí)行到_;接著跳轉(zhuǎn)回函數(shù)體,進(jìn)入第二個(gè)修飾器,以此類(lèi)推。到達(dá)最后一層時(shí),一次返回到上一層修飾器的_;后。
自由函數(shù)
定義在合約外的函數(shù)叫做自由函數(shù),一定是internal類(lèi)型,就像一個(gè)內(nèi)部函數(shù)庫(kù)一樣,會(huì)包含在所有調(diào)用他們的合約內(nèi),就像寫(xiě)在對(duì)應(yīng)位置一樣。但是自由函數(shù)不能直接訪問(wèn)全局變量和其他不在作用域下的函數(shù)(比如,需要通過(guò)地址引入合約,再使用合約內(nèi)的函數(shù))
view
view函數(shù)不能產(chǎn)生任何修改。由于操作碼的原因,view庫(kù)函數(shù)不會(huì)在運(yùn)行時(shí)阻止?fàn)顟B(tài)改變,不過(guò)編譯時(shí)靜態(tài)檢查器會(huì)發(fā)現(xiàn)這個(gè)問(wèn)題。
以下行為都視為修改狀態(tài):
- 修改狀態(tài)變量。
- 觸發(fā)事件。
- 創(chuàng)建其它合約。
- 使用
selfdestruct。 - 通過(guò)調(diào)用發(fā)送以太幣。
- 調(diào)用任何沒(méi)有標(biāo)記為
view或者pure的函數(shù)。 - 使用低級(jí)調(diào)用。
- 使用包含特定操作碼的內(nèi)聯(lián)匯編。
receive 函數(shù)
一個(gè)合約至多有一個(gè)receive函數(shù),形如receive() external payable { ... } ,注意沒(méi)有function 的標(biāo)識(shí),沒(méi)有參數(shù),只能是external 和payable標(biāo)識(shí),可以有函數(shù)修飾器,支持重載。
receive函數(shù)在沒(méi)有任何調(diào)用數(shù)據(jù)時(shí)執(zhí)行(如用.send()或者.transfer()給合約轉(zhuǎn)賬),如果沒(méi)有設(shè)置receive函數(shù),那么就會(huì)執(zhí)行fallback函數(shù),如果這兩個(gè)函數(shù)都不存在,合約就不能通過(guò)交易的形式獲取以太幣
回退函數(shù)
一個(gè)合約至多一個(gè)回退函數(shù),格式如:fallback () external [payable] 或者 fallback (bytes calldata _input) external [payable] returns (bytes memory _output),后者的函數(shù)參數(shù)會(huì)接收完整的調(diào)用信息(msg.data),返回的時(shí)未經(jīng)修改的數(shù)據(jù)(如為經(jīng)過(guò)ABI編碼)。
回退函數(shù)可以時(shí)virtual的,可以重載,也可以被修飾器修飾。在函數(shù)調(diào)用時(shí),如果沒(méi)有與之匹配的函數(shù)簽名或者消息調(diào)用為空且無(wú)receive函數(shù),就會(huì)調(diào)用fallbakc函數(shù)。
如果回退函數(shù)代替了receive函數(shù),那么仍然只有2300gas可用。
構(gòu)造函數(shù)
contract X {
string public name;
constructor(string memory _name) {
name = _name;
}
}
// Base contract Y
contract Y {
string public text;
constructor(string memory _text) {
text = _text;
}
}
兩種方式
contract B is X("Input to X"), Y("Input to Y") {
}
// Order of constructors called:
// 1. X
// 2. Y
// 3. C
contract C is X, Y {
// Pass the parameters here in the constructor,
// similar to function modifiers.
constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}
事件
事件是能方便地調(diào)用以太坊虛擬機(jī)日志功能的接口,分為設(shè)置事件和觸發(fā)事件
pragma solidity >=0.4.21 <0.9.0;
contract TinyAuction {
event HighestBidIncreased(address bidder, uint amount); // 事件
function bid() public payable {
// ...
emit HighestBidIncreased(msg.sender, msg.value); // 觸發(fā)事件
}
}
事件是對(duì)EVM日志的簡(jiǎn)短總結(jié),可以通過(guò)RPC接口監(jiān)聽(tīng)。觸發(fā)事件時(shí),設(shè)置好的參數(shù)就會(huì)記錄在區(qū)塊鏈的交易日志中,永久的保存,但是合約本身是不可以訪問(wèn)這些日志的。可以通過(guò)帶有日志的Merkle證明的合約,來(lái)檢查日志是否存在于區(qū)塊鏈上。由于合約中僅能訪問(wèn)最近的 256 個(gè)區(qū)塊哈希,所以還需要提供區(qū)塊頭信息。
繼承
https://solidity-by-example.org/inheritance/
當(dāng)合約繼承其他的合約時(shí),只會(huì)在區(qū)塊鏈上生成一個(gè)合約,所有相關(guān)的合約都會(huì)編譯進(jìn)這個(gè)合約,調(diào)用機(jī)制和寫(xiě)在一個(gè)合約上一致。
所以如果繼承了多個(gè)合約,希望把所有的同名函數(shù)都執(zhí)行一遍,就需要super關(guān)鍵詞。
函數(shù)重寫(xiě)
父合約中被標(biāo)記為virtual的非private函數(shù)可以在子合約中用override重寫(xiě)。
重寫(xiě)可以改變函數(shù)的標(biāo)識(shí)符,規(guī)則如下:
- 可見(jiàn)性只能單向從
external更改為public。 -
nonpayable可以被view和pure覆蓋。 -
view可以被pure覆蓋。 -
payable不可被覆蓋。 - 函數(shù)修飾器也支持重寫(xiě),且和函數(shù)重寫(xiě)規(guī)則一致
// Contracts inherit other contracts by using the keyword 'is'.
contract B is A {
// Override A.foo()
function foo() public pure virtual override returns (string memory) {
return "B";
}
}
contract C is A {
// Override A.foo()
function foo() public pure virtual override returns (string memory) {
return "C";
}
}
contract D is B, C {
// D.foo() returns "C" 執(zhí)行最遠(yuǎn)的那個(gè)父類(lèi)函數(shù)C的調(diào)用
// since C is the right most parent contract with function foo()
function foo() public pure override(B, C) returns (string memory) {
return super.foo();
}
}
接口
接口和抽象合約的作用很類(lèi)似,但是它的每一個(gè)函數(shù)都沒(méi)有實(shí)現(xiàn),而且不可以作為其他合約的子合約,只能作為父合約被繼承。
接口中所有的函數(shù)必須是external,且不包含構(gòu)造函數(shù)和全局變量。接口的所有函數(shù)都會(huì)隱式標(biāo)記為external,可以重寫(xiě)。但是多次重寫(xiě)的規(guī)則和多繼承的規(guī)則和一般函數(shù)重寫(xiě)規(guī)則一致。
- 不能實(shí)現(xiàn)任何功能
- 可以從其他接口繼承
- 所有聲明的函數(shù)必須是外部的
- 不能聲明構(gòu)造函數(shù)
- 不能聲明狀態(tài)變量
interface ICounter {
function count() external view returns (uint);
function increment() external;
}
contract MyContract {
function incrementCounter(address _counter) external {
ICounter(_counter).increment();
}
function getCount(address _counter) external view returns (uint) {
return ICounter(_counter).count();
}
}
引用類(lèi)型
引用類(lèi)型可以通過(guò)不同變量名來(lái)修改指向的同一個(gè)值。目前的引用類(lèi)型包括:結(jié)構(gòu)體、數(shù)組和映射
- memory:存儲(chǔ)在內(nèi)存里,只在函數(shù)內(nèi)部使用,函數(shù)內(nèi)不做特殊說(shuō)明為
memory類(lèi)型。 - storage:相當(dāng)于全局變量。函數(shù)外合約內(nèi)的都是
storage類(lèi)型。 - calldata:保存有函數(shù)的參數(shù),不可修改,大多數(shù)時(shí)候和
memory相似。它常作為外部函數(shù)的參數(shù),也可以當(dāng)作其他的變量使用 - 盡可能使用
calldata,因?yàn)樗炔粫?huì)復(fù)制,也不能修改,而且還可以作為函數(shù)的返回值 -
storage和memory之間的賦值或者用calldata對(duì)它們賦值,都是產(chǎn)生獨(dú)立的拷貝,不修改原來(lái)的值,其他向storage賦值是拷貝,結(jié)構(gòu)體里面的賦值是一個(gè)拷貝。 - 于數(shù)組和結(jié)構(gòu)體在函數(shù)中要有存儲(chǔ)位置聲明
基本類(lèi)型轉(zhuǎn)換
隱式轉(zhuǎn)換:隱式轉(zhuǎn)換發(fā)生在編譯時(shí)期,如果不出現(xiàn)信息丟失,其實(shí)都可以進(jìn)行隱式轉(zhuǎn)換,比如uint8可以轉(zhuǎn)成uint16。隱式轉(zhuǎn)換常發(fā)生在不同的操作數(shù)一起用操作符操作時(shí)發(fā)生。
顯式轉(zhuǎn)換:如果編譯器不允許隱式轉(zhuǎn)換,而你足夠自信沒(méi)問(wèn)題,那么就去嘗試顯示轉(zhuǎn)換,但是這很容易造成安全問(wèn)題
如果是uint或者int同類(lèi)型強(qiáng)制轉(zhuǎn)換,就是從最低位截?cái)?/p>
單位、內(nèi)置函數(shù)和變量
- 幣的單位默認(rèn)是
wei,也可以添加后綴。
1 wei == 1;
1 gwei == 1e9;
1 ether == 1e18;
區(qū)塊和交易屬性
blockhash(uint blockNumber) returns (bytes32):指定區(qū)塊的區(qū)塊哈希,但是僅可用于最新的 256 個(gè)區(qū)塊且不包括當(dāng)前區(qū)塊,否則返回0.block.chainid(uint): 當(dāng)前鏈 idblock.coinbase(address): 挖出當(dāng)前區(qū)塊的礦工地址block.difficulty(uint): 當(dāng)前區(qū)塊難度block.gaslimit(uint): 當(dāng)前區(qū)塊 gas 限額block.number(uint): 當(dāng)前區(qū)塊號(hào)block.timestamp(uint): 自 unix epoch 起始當(dāng)前區(qū)塊以秒計(jì)的時(shí)間戳gasleft() returns (uint256):剩余的 gasmsg.data(bytes): 完整的 calldatamsg.sender(address): 消息發(fā)送者(當(dāng)前調(diào)用)msg.sig(bytes4): calldata 的前 4 字節(jié)(也就是函數(shù)標(biāo)識(shí)符)msg.value(uint): 隨消息發(fā)送的 wei 的數(shù)量tx.gasprice(uint): 交易的 gas 價(jià)格-
tx.origin(address payable): 交易發(fā)起者(完全的調(diào)用鏈)錯(cuò)誤處理
assert(bool condition),require(bool condition),require(bool condition, string memory message)均是條件為假然后回滾。revert(),revert(string memory reason)立即回滾
內(nèi)部調(diào)用
內(nèi)部調(diào)用再EVM中只是簡(jiǎn)單的跳轉(zhuǎn),傳遞當(dāng)前的內(nèi)存的引用,效率很高。但是仍然要避免過(guò)多的遞歸,因?yàn)槊看芜M(jìn)入內(nèi)部函數(shù)都會(huì)占用一個(gè)堆棧槽,而最多只有1024個(gè)堆棧槽。
外部調(diào)用
- 只有
external或者public的函數(shù)才可以通過(guò)消息調(diào)用而不是單純的跳轉(zhuǎn)調(diào)用,外部函數(shù)的參數(shù)會(huì)暫時(shí)復(fù)制在內(nèi)存中。 - 注意
this不可以出現(xiàn)在構(gòu)造函數(shù)里,因?yàn)榇藭r(shí)合約還沒(méi)有完成。 - 調(diào)用時(shí)可以指定 value 和 gas 。這里導(dǎo)入合約使用的時(shí)初始化合約實(shí)例然后賦予地址。
元組的賦值行為
函數(shù)的返回值可以是元組,因此就可以用元組的形式接收,但是必須按照順序排列。在0.5.0之后,兩個(gè)元組的大小必須相同,用逗號(hào)表示間隔,可以空著省略元素。注意,不允許賦值和聲明都出現(xiàn)在元組里,比如(x, uint y) = (1, 2);不合法。
元祖就是多個(gè)不同類(lèi)型數(shù)據(jù)組成的數(shù)組
錯(cuò)誤
assert函數(shù),用于檢查內(nèi)部錯(cuò)誤,返回Panic(uint256),錯(cuò)誤代碼分別表示:
- 0x00: 由編譯器本身導(dǎo)致的Panic.
- 0x01:
assert的參數(shù)(表達(dá)式)結(jié)果為 false 。 - 0x11: 在
unchecked { … }外,算術(shù)運(yùn)算結(jié)果向上或向下溢出。 - 0x12: 除以0或者模0.
- 0x21: 不合適的枚舉類(lèi)型轉(zhuǎn)換。
- 0x22: 訪問(wèn)一個(gè)沒(méi)有正確編碼的
storagebyte數(shù)組. - 0x31: 對(duì)空數(shù)組
.pop()。 - 0x32: 數(shù)組的索引越界或?yàn)樨?fù)數(shù)。
- 0x41: 分配了太多的內(nèi)存或創(chuàng)建的數(shù)組過(guò)大。
- 0x51: 如果你調(diào)用了零初始化內(nèi)部函數(shù)類(lèi)型變量
Error(string)的異常由編譯器產(chǎn)生,有以下情況:
-
require的參數(shù)為false。 - 觸發(fā)
revert或者revert("discription") - 執(zhí)行外部函數(shù)調(diào)用合約沒(méi)有代碼。
-
payable修飾的函數(shù)(包括構(gòu)造函數(shù)和 fallback 函數(shù)),接收以太幣。 - 合約通過(guò) getter 函數(shù)接收以太幣 。
try/catch
try后面只能接外部函數(shù)調(diào)用或者是創(chuàng)建合約new ContractName的表達(dá)式,并且表達(dá)式內(nèi)部的錯(cuò)誤并不會(huì)被記錄,只有調(diào)用的外部函數(shù)內(nèi)出現(xiàn)錯(cuò)誤才會(huì)返回回滾的信息。如果有returns的話,后面接外部函數(shù)的返回值類(lèi)型,return后面是當(dāng)前合約函數(shù)的返回值。
地址
以太坊地址是給定公鑰哈希的最后 20 個(gè)字節(jié)。使用的散列算法是Keccak-256。所以對(duì)于一個(gè)唯一的私鑰 => 唯一的哈希.
所有錢(qián)包都應(yīng)該接受以大寫(xiě)或小寫(xiě)字符表示的以太坊地址
您可以將 Ether 發(fā)送到定義為的變量
address payable您不能將 Ether 發(fā)送到定義為的變量
addressmsg.sender() -> Returns : address payable
tx.origin() -> Returns : address payable
.transfer()` , `.send()` , `.call()` , `.delegatecall()` and `.staticcall() 是address payable 定義變量的用法address 具有balance方法,表示eth余額
address payable` to `address 隱式轉(zhuǎn)換可以,返過(guò)來(lái)不可以 contract NotPayable { } contract Payable { function() payable {} } contract HelloWorld { address x = address(NotPayable); //address類(lèi)型 address y = address(Payable); //address payable類(lèi)型,因?yàn)樵摵贤衟ayable類(lèi)型的回調(diào)函數(shù) function hello_world() public pure returns (string memory) { return "hello world"; } }
EVM 提供了4種 特殊的操作碼來(lái)與其他智能合約交互,其中 3 種可用作以下address類(lèi)型的方法:**call**、**delegatecode**和**staticcall**
call:
https://solidity-by-example.org/call/
address.call(bytes memory) returns (bool, bytes memory)
我是合約 A,我想為合約 B 存儲(chǔ)執(zhí)行合約 B 功能。調(diào)用 B.function() 只能更新 B 的存儲(chǔ)
callcode
address.callcode(__payload__)
*.callcode()*現(xiàn)在已棄用,取而代之的是*.delegatecall()*. 但是,仍然可以在內(nèi)聯(lián)匯編中使用它。
合約A本質(zhì)上是復(fù)制B的功能
我是合約 A,我想為我的存儲(chǔ)執(zhí)行合約 B 的功能。調(diào)用 B.function() 將更新 A 的存儲(chǔ)
delegatecall
https://solidity-by-example.org/delegatecall/
_address.**delegatecall(**bytes memory**)** returns (bool, bytes memory)
我是合約A,我想執(zhí)行合約B的功能,但是合約B可以偽裝成我。
B 的函數(shù)可以覆蓋 A 的存儲(chǔ),并在任何其他合約中偽裝成A。
msg.sender 將是 A 的地址,而不是 B
在這種情況下,合約 A 本質(zhì)上是將函數(shù)調(diào)用委托給 B。與前
callcode一種方法的不同之處在于,使用delegatecallnot only enable 覆蓋合約 A 的存儲(chǔ)。如果合約 B 調(diào)用另一個(gè)合約 C,合約 C 將看到它
msg.sender是合約 A把B的代碼在A的執(zhí)行環(huán)境中執(zhí)行,數(shù)據(jù)使用A的
staticcall
address.**staticcall(**bytes memory**)** returns (bool, bytes memory)
規(guī)格
- 低級(jí)
STATICCALL,請(qǐng)參閱操作碼 OxF4)(具有當(dāng)前合約看到的完整 msg 上下文)給定作為參數(shù)傳遞的memory(數(shù)據(jù)有效負(fù)載)。 - 返回一個(gè)元組:
當(dāng)交易在接收者字段中指定一個(gè)稱為零地址的特定地址時(shí),它打算創(chuàng)建一個(gè)新合約.向該地址發(fā)送資金實(shí)際上不會(huì)轉(zhuǎn)移任何以太幣。在以太坊網(wǎng)絡(luò)中,礦工將包含此接收者的交易解釋為創(chuàng)建新智能合約的指令。
應(yīng)付
用來(lái)接收和提取eth
contract Payable {
// Payable address can receive Ether
address payable public owner;
// Payable constructor can receive Ether
constructor() payable {
owner = payable(msg.sender);
}
//把合約里的amount提取到owener地址上啊,call在那個(gè)地址上調(diào)用,就更新那個(gè)地址的數(shù)據(jù)
// Function to withdraw all Ether from this contract.
function withdraw() public {
// get the amount of Ether stored in this contract
uint amount = address(this).balance;
// send all Ether to owner
// Owner can receive Ether since the address of owner is payable
(bool success, ) = owner.call{value: amount}("");
require(success, "Failed to send Ether");
}
// Function to transfer Ether from this contract to address from input
function transfer(address payable _to, uint _amount) public {
// Note that "to" is declared as payable
(bool success, ) = _to.call{value: _amount}("");
require(success, "Failed to send Ether");
}
}
contract ReceiveEther { 接受者接收的邏輯
/*
Which function is called, fallback() or receive()?
send Ether
|
msg.data is empty?
/ \
yes no
/ \
receive() exists? fallback()
/ \
yes no
/ \
receive() fallback()
*/
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
contract SendEther {
消息調(diào)用者把比發(fā)給to這個(gè)地址
function sendViaTransfer(address payable _to) public payable {
// This function is no longer recommended for sending Ether.
_to.transfer(msg.value);
}
消息調(diào)用者把比發(fā)給to這個(gè)地址,需要發(fā)送成功,
function sendViaSend(address payable _to) public payable {
// Send returns a boolean value indicating success or failure.
// This function is not recommended for sending Ether.
bool sent = _to.send(msg.value);
require(sent, "Failed to send Ether");
}
消息調(diào)用者把比發(fā)給to這個(gè)地址,需要發(fā)送成功,
function sendViaCall(address payable _to) public payable {
// Call returns a boolean value indicating success or failure.
// This is the current recommended method to use.
(bool sent, bytes memory data) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
}
回調(diào)函數(shù)fallback
fallback是一個(gè)不接受任何參數(shù)且不返回任何內(nèi)容的函數(shù)。
它在何時(shí)執(zhí)行
- 調(diào)用不存在的函數(shù)或
- 以太幣直接發(fā)送到合約但
receive()不存在或msg.data不為空 - fallback被
transferorsend.調(diào)用時(shí)有 2300 氣體限制
功能選擇器
調(diào)用函數(shù)時(shí),前 4 個(gè)字節(jié)calldata指定調(diào)用哪個(gè)函數(shù)。
下面的這段代碼。它用于在地址上call執(zhí)行transfer合約addr。
addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123))
調(diào)用其他合約
https://solidity-by-example.org/calling-contract/
最簡(jiǎn)單的方法就是直接調(diào)用它,比如A.foo(x, y, z).
調(diào)用其他合約的另一種方法是使用低級(jí)call.
try/Catch
contract Foo {
address public owner;
constructor(address _owner) {
require(_owner != address(0), "invalid address");
assert(_owner != 0x0000000000000000000000000000000000000001);
owner = _owner;
}
function myFunc(uint x) public pure returns (string memory) {
require(x != 0, "require failed");
return "my func was called";
}
}
contract Bar {
event Log(string message);
event LogBytes(bytes data);
function tryCatchNewContract(address _owner) public {
try new Foo(_owner) returns (Foo foo) {
// you can use variable foo here
emit Log("Foo created");
} catch Error(string memory reason) {
// catch failing revert() and require()
emit Log(reason);
} catch (bytes memory reason) {
// catch failing assert()
emit LogBytes(reason);
}
}
}
ERC20
任何遵循ERC20 標(biāo)準(zhǔn)的合約都是 ERC20 代幣。該標(biāo)準(zhǔn)提供了代幣的基本功能:如轉(zhuǎn)移代幣,授權(quán)代幣給其他人(如鏈上第三方應(yīng)用)使用。標(biāo)準(zhǔn)接口允許以太坊上的任何代幣被其他應(yīng)用程序重用,如錢(qián)包、去中心化交易所等
這是一個(gè)發(fā)幣的合約,平日里所接觸的許許多多代幣如usdt(erc20)、usdc、dai、unsiwap、chainlink、wbtc、sushi等等絕大都數(shù)都是erc20代幣,
ERC20 代幣提供以下功能
- 轉(zhuǎn)移代幣
- 允許其他人代表代幣持有者轉(zhuǎn)移代幣
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}