智能合約語(yǔ)法筆記

區(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)換為 viewnon-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):

  1. 修改狀態(tài)變量。
  2. 觸發(fā)事件。
  3. 創(chuàng)建其它合約。
  4. 使用 selfdestruct
  5. 通過(guò)調(diào)用發(fā)送以太幣。
  6. 調(diào)用任何沒(méi)有標(biāo)記為 view 或者 pure 的函數(shù)。
  7. 使用低級(jí)調(diào)用。
  8. 使用包含特定操作碼的內(nèi)聯(lián)匯編。
receive 函數(shù)

一個(gè)合約至多有一個(gè)receive函數(shù),形如receive() external payable { ... } ,注意沒(méi)有function 的標(biāo)識(shí),沒(méi)有參數(shù),只能是externalpayable標(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 可以被 viewpure 覆蓋。
  • 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ù)的返回值
  • storagememory之間的賦值或者用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)前鏈 id

  • block.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) :剩余的 gas

  • msg.data ( bytes ): 完整的 calldata

  • msg.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ò)誤代碼分別表示:

  1. 0x00: 由編譯器本身導(dǎo)致的Panic.
  2. 0x01: assert 的參數(shù)(表達(dá)式)結(jié)果為 false 。
  3. 0x11: 在unchecked { … }外,算術(shù)運(yùn)算結(jié)果向上或向下溢出。
  4. 0x12: 除以0或者模0.
  5. 0x21: 不合適的枚舉類(lèi)型轉(zhuǎn)換。
  6. 0x22: 訪問(wèn)一個(gè)沒(méi)有正確編碼的storagebyte數(shù)組.
  7. 0x31: 對(duì)空數(shù)組 .pop() 。
  8. 0x32: 數(shù)組的索引越界或?yàn)樨?fù)數(shù)。
  9. 0x41: 分配了太多的內(nèi)存或創(chuàng)建的數(shù)組過(guò)大。
  10. 0x51: 如果你調(diào)用了零初始化內(nèi)部函數(shù)類(lèi)型變量

Error(string)的異常由編譯器產(chǎn)生,有以下情況:

  1. require 的參數(shù)為 false
  2. 觸發(fā)revert或者revert("discription")
  3. 執(zhí)行外部函數(shù)調(diào)用合約沒(méi)有代碼。
  4. payable 修飾的函數(shù)(包括構(gòu)造函數(shù)和 fallback 函數(shù)),接收以太幣。
  5. 合約通過(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ā)送到定義為的變量address

  • msg.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被transfer or send.調(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);
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容