如果一個智能合約至少有一個未實現的函數,即某個函數缺少主體 {} 中的內容,則必須將該合約標為 abstract
,不然編譯會報錯。另外,未實現的函數需要加 virtual
,以便子合約重寫。如果我們還沒想好具體怎麼實現函數,那麼可以把合約標為 abstract
,之後讓別人補寫上。例如插入排序函數可以先用 abstract
標示:
abstract contract InsertSort{
function insertSort(uint[] memory a) public pure virtual returns(uint[] memory);
}
抽象合約範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
abstract contract Base{
string public name = "Base";
function getAlias() public pure virtual returns(string memory);
}
contract BaseImpl is Base {
function getAlias() public pure override returns(string memory){
return "BaseTmpl";
}
}
抽象合約是不能部署的,要選擇非抽象合約部署。
介面類似於抽象合約,但它不實現任何功能。
external
且不能有函數本體 {}
如果智能合約實現了某種介面(例如ERC20或ERC721),其他 Dapps 和智能合約就知道如何與它互動。因為介面提供了兩個重要的資訊:
bytes4
選擇器,以及函數簽章 <函數名稱>(每個參數型別)
。ABI json
檔轉換為介面 sol
檔。以 ERC721 介面合約 IERC721 為例,它定義了 3 個 event 和 9 個 function,所有 ERC721 標準的 NFT 都實作了這些函數。介面和常規合約的區別在於每個函數都以 ;
代替函數本體 { }
結尾。
interface IERC721 is IERC165{
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function TransferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
Transfer
事件:在轉帳時被釋放,記錄代幣的發出地址 from
,接收地址 to
和 tokenId
。Approval
事件:在授權時被釋放,記錄授權地址 owner
,被授權地址 approved
和 tokenId
。ApprovalForAll
事件:在批量授權時被釋放,記錄批量授權的發出地址 owner
,被授權地址 operator
和是否授權的 approved
。balanceOf
:傳回某地址的 NFT 持有量 balance
。ownerOf
:回傳某 tokenId
的主人 owner
。transferFrom
:普通轉賬,參數為轉出地址 from
,接收地址 to
和 tokenId
。safeTransferFrom
:安全轉帳(如果接收方是合約位址,會要求實作 ERC721Receiver
介面)。approve
:授權另一個位址使用你的 NFT。參數為被授權位址 approve
和 tokenId
。getApproved
:查詢 tokenId
被批准給了哪個位址。setApprovedForAll
:將自己持有的該系列 NFT 批次授權給某個地址 operator
。isApprovedForAll
:查詢某位址的 NFT 是否大量授權給了另一個 operator
位址。safeTransferFrom
:安全轉帳的重載函數,參數裡包含了 data
。如果我們知道一個合約實現了 IERC721
介面,我們不需要知道它具體程式碼實現,就可以與它互動。
無聊猿 BAYC
屬於 ERC721
代幣,實現了 IERC721
介面的功能。我們不需要知道它的原始碼,只要知道它的合約地址,用 IERC721
介面就可以與它互動,例如用 balanceOf()
來查詢某個地址的 BAYC
餘額,用 safeTransferFrom()
來轉帳 BAYC
。
contract interactBAYC{
// 利用BAYC位址建立介面合約變數(在 ETH 主網)
IERC721 BAYC = IERC721(0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D);
// 透過介面呼叫 BAYC 的 balanceOf() 查詢持倉量
function balanceOfBAYC(address owner) external view returns (uint256 balance){
return BAYC.balanceOf(owner);
}
// 透過介面呼叫 BAYC 的 safeTransferFrom() 安全轉賬
function safeTransferFromBAYC(address from, address to, uint256 tokenId) external {
BAYC.safeTransferFrom(from, to, tokenId);
}
}
介面範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface Base{
function getFirstName() external pure returns(string memory);
function getLastName() external pure returns(string memory);
}
contract BaseImpl is Base{
function getFirstName() external pure override returns(string memory){
return "Amazing";
}
function getLastName() external pure override returns(string memory){
return "Ang";
}
}