在參考一些合約的時候,初學者往往最難理解的可能是一種沒有內容的函式存在,其實這些函式被稱為接口或介面,就像是我們電腦上的 USB 插座一樣,Solidity 中的 Interface 也提供了一個「規格化、模組化」的接口使我們可以跟一些「既定的規則」進行互動,以達到方便更具規模使用。
Interface 就像是一個抽象合約,以下為 interface 的重要特徵:
-
Interface 的函數不可以有實作部分,也不能繼承其他合約、Interface
-
Interface 中函數的可視性只能是 external
-
Interface 不可有 constructor
-
Interface 不可有任何 state variables
-
Interface 可以有像是
enum
或structs
的資料結構,並且用<interface>.<data structures>
的方式訪問
舉例來說:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
contract Counter {
uint public count;
function increment() external {
count += 1;
}
}
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();
}
}
介面通常用於定義一個標準,讓之後繼承的內容都符合這套標準,例如 ERC (Ethereum Request for Comments)-721 的 interface
如下:
interface ERC721 {
event Transfer(...);
event Approval(...);
event ApprovalForAll(...);
function balanceOf(...);
function ownerOf(...);
function safeTransferFrom(...);
function safeTransferFrom(...);
function transferFrom(...);
function approve(...);
function setApprovalForAll(...);
function getApproved(...);
function isApprovedForAll(...);
}
interface ERC721Metadata {
function name(...);
function symbol(...);
function tokenURI(...);
}
自此只要我們繼承以上的 interface
就可以創建出符合 ERC-721 標準的代幣(token)。
抽象合約
合約就像是我們在物件導向程式語言中的 Classes,可以包含許多永遠存在的狀態變數 (不會因為生命週期而消亡) 和相關的成員函式。
而抽象合約(Abstract Contract)的定義是:當這個 Contracts 物件中至少有一個函式沒有包含實作部分時,並稱做抽象合約。
那我們什麼時候適合使用抽象合約呢?
一是使用 template method 時,我們可以在合約中依照已經定義好的骨架來建置我們自己的函式內容。
二是我們可以用於 debug,因為如果我們的合約裡面和抽象合約定義的內容不同時,compiler 或出現錯誤或者警告。
與 Interface 不同的是:Interface 只能是合約 ABI 能夠表示的範疇,所以才會有我們之前提到的那些規定。
而使用 Interface 的好處是在大型專案裡面,擁有一個定義完善的 Interface 可以讓彈性、效率、可擴充性、模組化發揮到最高的程度。
以下是官方文件提供的範例:針對抽象合約繼承之後便可對其函式內部進行敘述:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
abstract contract Feline {
function utterance() public pure virtual returns (bytes32);
}
contract Cat is Feline {
function utterance() public pure override returns (bytes32) {return "miaow"; }
}