跳到主要內容

【開發智能合約 — Solidity系列】實作篇Ep.6 -關於可視範圍(Visibility)

圖片來源

 

為什麼要特別介紹可視範圍呢? 試想,假如我們的合約裡有些非常重要的內容只能侷限於合約內使用,此時就可以運用可視範圍的技巧,將某些重要的功能、狀態鎖定在合約內使用,不隨意開放給外部調用,避免汙染內部,但有些又是共用的內容及功能時,我們就可以利用公開的可視範圍讓相同的功能能夠重複使用。

合約中又可以依照不同的分責區分可視範圍,在Solidity的世界中主要會分為兩大類型的可視範圍,分別是狀態變數(State Variable)跟功能(Function)。

狀態變數(State Variable)的可視範圍

public

圖片來源

這種可視範圍最為寬鬆,除了內部合約以外,繼承與外部合約也都能夠進行調用。

contract Base {
// [Scope]
// - inside Base Contract
// - inside contracts that inherit this contract
// - by other contracts
string public publicMsg = "public state variables";
function testScope() public virtual {
// ✔️ 可以存取「Base」的public 變數
require(bytes(publicMsg).length > 0, "cannot access public state variable");
}
} contract Extend is Base { function testScope() public view override {

// ✔️ 可以存取「Base」的public 變數
require(bytes(publicMsg).length > 0, "cannot access public state variable");
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
// ✔️ 可以存取「Base」的public變數
b.publicMsg;
}
}

internal

圖片來源

除了自身的合約以外,繼承的合約也能夠調用,但外部合約是不可視的。

contract Base {
// [Scope]
// - inside Base Contract
// - inside contracts that inherit this contract
string internal internalMsg = "internal state variables";
function testScope() public virtual {
// ✔️ 可以存取「Base」的internal 變數
require(bytes(internalMsg).length > 0, "cannot access internal state variable");
}
} contract Extend is Base { function testScope() public view override {
// ✔️ 可以存取「Base」的internal 變數
require(bytes(internalMsg).length > 0, "cannot access internal state variable");
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();

// ❌ 無法存取「Base」的internal變數
// @notice 語法檢測錯誤: not found or not visible after argument-dependent lookup in contract Base.
// b.internalMsg;
}
}

private

圖片來源

這種可視範圍僅侷限於自身的合約之中,不可被外部合約調用,包括繼承的合約也是不可視的。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7.0;
contract Base {
// State variables
// [Scope]
// - inside Base Contract
string private privateMsg = "private state variables";
function testScope() public virtual {
// ✔️ 可以存取「Base」的private 變數
require(bytes(privateMsg).length > 0, "cannot access private state variable");
}
} contract Extend is Base {
function testScope() public view override {
// ❌ 無法使用基礎合約的私有狀態變數
// @notice 語法檢測錯誤: DeclarationError: Undeclared identifier.
// require(bytes(privateMsg).length > 0, "cannot access private state variable");
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
// ❌ 無法存取「Base」的private變數
// @notice 語法檢測錯誤: not found or not visible after argument-dependent lookup in contract Base.
// b.privateMsg;
}
}

功能(Function)的可視範圍

Function的部分比較特殊,擴增了external,這也是我們在傳統的程式語言中比較少見的一種應用,以下就讓我們來好好介紹這個部分。

external

僅外部的合約可以進行調用,內部以及繼承的合約皆不可視,主要用意是當我們不確定這個功能是否內部也會調用時,可以宣告為external,一旦宣告為external之後,內部要調用就得使用this.xxx()才能調用,另外參數的部分是使用Calldata作為來源,會減少許多不必要的Copy。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7.0;
contract Base {
// [Scope]
// - by other contracts
function externalJob() external pure returns (string memory) {
return "external job called";
}
function testScope() public virtual {
// ❌ 僅能由外部合約進行呼叫
// @notice 語法檢測錯誤: DeclarationError: Undeclared identifier. Did you mean "internalJob" or "externalJob"?
// externalJob();
// ✔️ 但可以使用這種方式在內部進行調用
this.externalJob();
}
} contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
b.externalJob();
}
}

public

可視範圍就如同狀態變數的public一樣,除了內部合約以外,繼承與外部合約也都能夠進行調用。

contract Base {
// [Scope]
// - inside this contract
// - inside contracts that inherit this contract
// - by other contracts
function publicJob() public pure returns (string memory) {
return "public job called";
}
function testScope() public virtual {
// ✔️ 可以存取「Base」的public Function
publicJob();
}
} contract Extend is Base { function testScope() public view override {
// ✔️ 可以存取「Base」的public Function
publicJob();
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
// ✔️ 可以存取「Base」的public Function
b.publicJob();
}
}

internal

可視範圍一樣同狀態變數的internal,除了自身的合約以外,繼承的合約也能夠調用,但外部合約是不可視的。

contract Base {
// [Scope]
// - inside this contract
// - inside contracts that inherit this contract
function internalJob() internal pure returns (string memory) {
return "internal job called";
}
function testScope() public virtual {
// ✔️ 可以存取「Base」的internal Function
internalJob();
}
} contract Extend is Base { function testScope() public view override {
// ✔️ 可以存取「Base」的internal Function
internalJob();
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
// ❌ 無法存取「Base」的internal Function
// @notice 語法檢測錯誤: not found or not visible after argument-dependent lookup in contract Base.
// b.internalJob();
}
}

private

這種可視範圍僅侷限於自身的合約之中,不可被外部合約調用,包括繼承的合約也是不可視的。

contract Base {
// [Scope]
// - inside Base Contract
function privateJob() private pure returns(string memory) {
return "private job called";
}
function testScope() public virtual {
// ✔️ 可以存取「Base」的private Function
privateJob();
}
} contract Extend is Base { function testScope() public view override {
// ❌ 無法使用基礎合約的私有成員Function
// @notice 語法檢測錯誤: DeclarationError: Undeclared identifier
// privateJob();
}
}
contract External {
function testScope() public {
// 宣告Base合約並呼叫其開放外部的Job
Base b = new Base();
// ❌ 無法存取「Base」的private Function
// @notice 語法檢測錯誤: not found or not visible after argument-dependent lookup in contract Base.
// b.privateJob();
}

結語

以上是這次篇章介紹的可視範圍用法,撰寫一份安全的合約時,我們務必注意什麼能開放,開放到什麼程度,這些都與可視範圍息息相關,後續維護時也較能明確的界定範圍,不至於全部開放導致混亂,或者全部封閉導致一堆重複的內容。

今天的範例都在這裡「📦 solidity-remix-toturial/Ep6」歡迎自行取用。

📚 更多關於Solidity的文章請看這裡…

喜歡撰寫文章的你,不妨來了解一下:

Web3.0時代下為創作者、閱讀者打造的專屬共贏平台 — 為什麼要加入? 歡迎加入一起練習寫作,賺取知識,累積財富!

資源參考

留言

這個網誌中的熱門文章

java西元民國轉換_各種不同格式

C#資料庫操作(新增、修改、刪除、查詢)

【Excel好好玩】 自己的資產自己管!善用Google Sheet來幫我們評估貸款

這次介紹的主題是關於Excel的貸款還款計畫試算,我們人生中總會遇到需要大筆金額的花費,但當資金不夠時就得進行貸款,而貸款之前如果我們能夠審慎評估,並分析自己的還款能力之後在進行凍作,相信風險會小很多,因此就自己動動手來使用Google Sheet進行試算吧! 基本資料 ● 貸款總額: 1000000 ● 貸款期數: 84月 ● 年利率: 2.11% ● 月利率: 0.18% P.S 月利率 = 年利率 / 12 重要函式 PMT : 這是Google Sheet內建的重要年金計算公式,我們可以善用這個公式來計算固定利率及期數的固定攤還本息。因為PMT函式計算出的結果為負數,所以前面加上-號轉成正數。 動手做 首先我們在Excel表上列出我們的基本資料 圖片來源 其中月利率的部分就使用公式「=B4/12」 接著我們填上第一列的期數跟餘額 圖片來源 =B2 =B3 使用關鍵PMT函數來計算本息的部分 因為PMT函式計算出的結果為負數,所以前面加上-號轉成正數。 -PMT(貸款利率(月利率), 貸款期數, 貸款總額) =-PMT($B$5,$B$3,$B$2) 圖片來源 計算利息 利息 = 貸款餘額 x 月利率 =B8*$B$5 圖片來源 計算本金 本金 = 本息 - 利息 =C8-D8 圖片來源 製作第二列餘額的部分 餘額的部分 = 上一期的餘額 - 上一期的本金 圖片來源 接著拖曳該兩列往下拉,即可查看每一期的利息與本金 圖片來源 結語 雖然市面上已經有很多貸款銀行都提供了試算功能,但如果我們想要進一步管理自己的資產時,就需要將每一期的金額給計算出來,因此才會將公式運用在Excel表,讓我們的資產管理表能夠結合負債,進一步評估我們理財行動的下一步,希望這樣的經驗可以幫助到正在理財道路上打拼的夥伴,讓我們透過有效的管理,幫助荷包長大吧! 喜歡撰寫文章的你,不妨來了解一下: Web3.0時代下為創作者、閱讀者打造的專屬共贏平台 — 為什麼要加入? 歡迎加入一起練習寫作,賺取知識,累積財富!