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

更新於 發佈於 閱讀時間約 17 分鐘

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

合約中又可以依照不同的分責區分可視範圍,在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的文章請看這裡…

留言
avatar-img
留言分享你的想法!
avatar-img
阿Han的沙龍
129會員
285內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
阿Han的沙龍的其他內容
2023/08/01
 相信常常聽到元宇宙的世界裡有一個並不陌生又有點距離的名詞「XR擴展實境」,包含了三大核心技術,分別為VR、AR與MR,其中VR與AR較為貼近我們的生活,那三者究竟有何不同呢? 就讓我們持續的看下去吧! VR 虛轉實,把假的變成真的 說到VR最直覺的就是想到VR頭盔吧,它可以帶給我
Thumbnail
2023/08/01
 相信常常聽到元宇宙的世界裡有一個並不陌生又有點距離的名詞「XR擴展實境」,包含了三大核心技術,分別為VR、AR與MR,其中VR與AR較為貼近我們的生活,那三者究竟有何不同呢? 就讓我們持續的看下去吧! VR 虛轉實,把假的變成真的 說到VR最直覺的就是想到VR頭盔吧,它可以帶給我
Thumbnail
2023/06/30
什麼是零知識證明(Zero-knowledge proof) 是一種密碼學的概念,用於在不揭示具體信息的情況下,證明某個主張的正確性。它允許一方(稱為證明者)向另一方(稱為驗證者)證明某個陳述的真實性,而無需透露任何關於陳述的具體細節..., 這根本文字天書啊,底下讓我們用白話文來說一下唄! 「我必
Thumbnail
2023/06/30
什麼是零知識證明(Zero-knowledge proof) 是一種密碼學的概念,用於在不揭示具體信息的情況下,證明某個主張的正確性。它允許一方(稱為證明者)向另一方(稱為驗證者)證明某個陳述的真實性,而無需透露任何關於陳述的具體細節..., 這根本文字天書啊,底下讓我們用白話文來說一下唄! 「我必
Thumbnail
2023/05/31
一套大型的智能合約通常都會拆分成許多小合約,並且透過匯入的方式拼裝而成,而這樣的匯入在Solidity世界中就是「Import」,就讓我們來看看「Import」到底怎麼運用吧! 假設目錄結構如下 example.sol other.sol 我們引入的方式就會是: import ‘./other.so
Thumbnail
2023/05/31
一套大型的智能合約通常都會拆分成許多小合約,並且透過匯入的方式拼裝而成,而這樣的匯入在Solidity世界中就是「Import」,就讓我們來看看「Import」到底怎麼運用吧! 假設目錄結構如下 example.sol other.sol 我們引入的方式就會是: import ‘./other.so
Thumbnail
看更多
你可能也想看
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
  Soildity是一種風格類似JavaScript的強型別高階語言,專門用來進行智能合約的開發及編程。本文敘述solidity的獨特之處。   
Thumbnail
  Soildity是一種風格類似JavaScript的強型別高階語言,專門用來進行智能合約的開發及編程。本文敘述solidity的獨特之處。   
Thumbnail
一個功能越趨完善且複雜的合約,勢必會拆成許多合約共同組成,而其實這些組成的合約之中許多的方法、元素都是重複的,因此我們可以使用Inheritance(繼承)的技巧,將共同的屬性、方法抽到某個上級合約,而其餘的合約只要繼承自上級合約,就能減少重複開發的狀況,我們都知道軟體開發的過程,只要開發的原始碼越
Thumbnail
一個功能越趨完善且複雜的合約,勢必會拆成許多合約共同組成,而其實這些組成的合約之中許多的方法、元素都是重複的,因此我們可以使用Inheritance(繼承)的技巧,將共同的屬性、方法抽到某個上級合約,而其餘的合約只要繼承自上級合約,就能減少重複開發的狀況,我們都知道軟體開發的過程,只要開發的原始碼越
Thumbnail
為什麼要特別介紹可視範圍呢? 試想,假如我們的合約裡有些非常重要的內容只能侷限於合約內使用,此時就可以運用可視範圍的技巧,將某些重要的功能、狀態鎖定在合約內使用,不隨意開放給外部調用,避免汙染內部,但有些又是共用的內容及功能時,我們就可以利用公開的可視範圍讓相同的功能能夠重複使用。 合約中又可以依照
Thumbnail
為什麼要特別介紹可視範圍呢? 試想,假如我們的合約裡有些非常重要的內容只能侷限於合約內使用,此時就可以運用可視範圍的技巧,將某些重要的功能、狀態鎖定在合約內使用,不隨意開放給外部調用,避免汙染內部,但有些又是共用的內容及功能時,我們就可以利用公開的可視範圍讓相同的功能能夠重複使用。 合約中又可以依照
Thumbnail
每個產品在實驗室研發出來後,勢必會面臨到賣給客戶的階段,那麼當我們將產品移交給客戶時,意味著也要進行環境的安裝,但問題來了,每一個客戶的環境差異甚大,總不可能為了A客戶就建立一個A客戶的環境,因應B客戶就建立B客戶的環境,這樣隨著產品的銷售量增長也將連帶耗盡公司的資源,想必這不是我們所樂見的現象,當
Thumbnail
每個產品在實驗室研發出來後,勢必會面臨到賣給客戶的階段,那麼當我們將產品移交給客戶時,意味著也要進行環境的安裝,但問題來了,每一個客戶的環境差異甚大,總不可能為了A客戶就建立一個A客戶的環境,因應B客戶就建立B客戶的環境,這樣隨著產品的銷售量增長也將連帶耗盡公司的資源,想必這不是我們所樂見的現象,當
Thumbnail
資料型態在合約當中扮演著什麼角色呢? 我們在「【開發智能合約 — Solidity系列】實作篇Ep.2 — 合約中的基本組成元素」有介紹過狀態變數可以儲存一些變化值,而儲存什麼類型的值就是所謂的資料型態,不同的資料型態可以處理的事物也有所不同,因此我們也需要了解一些基本的資料型態以及特性之後,未來開
Thumbnail
資料型態在合約當中扮演著什麼角色呢? 我們在「【開發智能合約 — Solidity系列】實作篇Ep.2 — 合約中的基本組成元素」有介紹過狀態變數可以儲存一些變化值,而儲存什麼類型的值就是所謂的資料型態,不同的資料型態可以處理的事物也有所不同,因此我們也需要了解一些基本的資料型態以及特性之後,未來開
Thumbnail
上一篇我們談到「【開發智能合約 — Solidity系列】實作篇Ep.1 — 看懂智能合約的脈絡」,大致上理解每個檔案的每份合約都是一個個的區塊,而這一個篇章主要著重於合約中的每個區塊的組成元素進行分項說明。 今天的範例會以簡單的銀行存款與提款流程進行示範,過程中將一步步的使用上述7種元素完成功能,
Thumbnail
上一篇我們談到「【開發智能合約 — Solidity系列】實作篇Ep.1 — 看懂智能合約的脈絡」,大致上理解每個檔案的每份合約都是一個個的區塊,而這一個篇章主要著重於合約中的每個區塊的組成元素進行分項說明。 今天的範例會以簡單的銀行存款與提款流程進行示範,過程中將一步步的使用上述7種元素完成功能,
Thumbnail
本篇章會以實際的程式碼來說明開發Solidity的組成脈絡與重要的區塊,讓我們不再害怕閱讀艱澀難懂的程式碼,並且具備基礎知識以後也能看懂外面的合約。 開源程式碼常常會面臨到法律的問題,因此License的宣告就變得非常重要,而Solidity也考慮到這一塊,因此特別規劃了一塊License宣告的區塊
Thumbnail
本篇章會以實際的程式碼來說明開發Solidity的組成脈絡與重要的區塊,讓我們不再害怕閱讀艱澀難懂的程式碼,並且具備基礎知識以後也能看懂外面的合約。 開源程式碼常常會面臨到法律的問題,因此License的宣告就變得非常重要,而Solidity也考慮到這一塊,因此特別規劃了一塊License宣告的區塊
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News