《Solidity 教程》合約結構

在 Solidity 語言中,合約類似於其他面向物件程式設計語言中的類

每個合約中可以包含 狀態變數、 函數、 函數 、事件 Event、 結構體、 和 枚舉類型 的聲明,且合約可以從其他合約繼承。還有一些特殊的合約,例如如介面.

本節用於幫助我們了解合約包含哪些內容,做一個簡單的入門,不會有細節的解釋與範例

Section3. 類型Section6. 合約 章節會比本節包含更多的說明

State Variables

狀態變數是永久地存儲在合約中的值

Azuki.sol 中使用:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Azuki is Ownable, ERC721A, ReentrancyGuard {
  uint256 public immutable maxPerAddressDuringMint;
  uint256 public immutable amountForDevs;
  uint256 public immutable amountForAuctionAndDev;

  mapping(address => uint256) public allowlist;
    //...
    uint256 public constant AUCTION_START_PRICE = 1 ether;
  uint256 public constant AUCTION_END_PRICE = 0.15 ether;
  uint256 public constant AUCTION_PRICE_CURVE_LENGTH = 340 minutes;
  uint256 public constant AUCTION_DROP_INTERVAL = 20 minutes;
  uint256 public constant AUCTION_DROP_PER_STEP =
    (AUCTION_START_PRICE - AUCTION_END_PRICE) /
      (AUCTION_PRICE_CURVE_LENGTH / AUCTION_DROP_INTERVAL);
    //...
    string private _baseTokenURI;
}

這些變數宣告便是該合約中所有的狀態變數,下一章節會解釋各種狀態變數如何使用

Functions

函數是代碼的可執行單元, 函數通常在合約內部定義,但也可以在合約外定義,在 Azuki.sol 中我們可以看到所有函數都被包在 Contract Azuki 裡面

Azuki.sol 中使用到的函數類型包含:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Azuki is Ownable, ERC721A, ReentrancyGuard {
    function publicSaleMint(uint256 quantity, uint256 callerPublicSaleKey) {
        //...
    }
    
    function auctionMint(uint256 quantity) external payable callerIsUser {
        //...
    }
    
    function isPublicSaleOn(
        uint256 publicPriceWei,
        uint256 publicSaleKey,
        uint256 publicSaleStartTime
      ) public view returns (bool) {
        //...
    }
    
    function getOwnershipData(uint256 tokenId)
        external
        view
        returns (TokenOwnership memory){
        //...
    }
    
    function endAuctionAndSetupNonAuctionSaleInfo(
        uint64 mintlistPriceWei,
        uint64 publicPriceWei,
        uint32 publicSaleStartTime
      ) external onlyOwner {
        //...
    }
    
    function _baseURI() internal view virtual override returns (string memory) {
        //...
    }
    
    function withdrawMoney() external onlyOwner nonReentrant {
        //...
    }
}

只看函數宣告的話,我們並不能了解這個函數的確切用法,但卻可以了解這個函數:

  • 能夠在哪使用 - 我們可以函數宣告中有些函數帶有 external , public , internal 這表示這個函數的可見性,其中 private 也是一種方式,不過這邊沒有使用到

  • 需要提供什麼類型參數 - () 內所有的參數,可以看到其聲明方法與第一部分狀態變數雷同,以第一個函數 publicSaleMint 為例,他需要 caller 提供 quantitycallerPublicSaleKey 的值,且其類型必須是 uint256

  • 會回傳什麼類型的資料 - returns() 內的參數,以 isPublicSaleOn 為例,這個函數會回傳 bool 也就是 True 或 False

Functions Modifiers

函數修改器可以用來以聲明的方式修改函數語義, Azuki.sol 中使用:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Azuki is Ownable, ERC721A, ReentrancyGuard {
    //...
    modifier callerIsUser() {
    require(tx.origin == msg.sender, "The caller is another contract");
    _;
  }
    //...
}

以上述函數部分 auctionMint 為例,後面 callerIsUser 就是修改器用法

Events

事件是能方便地調用乙太坊虛擬機日誌功能的介面, Azuki.sol 中沒有使用到事件,不過這邊還是簡單說明一下用法

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); // 觸發事件
    }
}

Struct Types

結構體是可以將幾個變數分組的自定義類型, Azuki.sol 中使用:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Azuki is Ownable, ERC721A, ReentrancyGuard {
    //...
    struct SaleConfig {
    uint32 auctionSaleStartTime;
    uint32 publicSaleStartTime;
    uint64 mintlistPrice;
    uint64 publicPrice;
    uint32 publicSaleKey;
  }
    //...
}

Enum Types

枚舉可用來創建由一定數量的「常量值」構成的自定義類型, Azuki.sol 中沒有使用到事件,不過這邊還是簡單說明一下用法

pragma solidity >=0.4.0 <0.9.0;

contract Upchain {
    enum State { Created, Locked, InValid } // 枚举
}