跳到主要內容

發表文章

目前顯示的是 10月, 2022的文章

【開發智能合約 - Solidity系列】實作篇Ep.12 - 合約內同名但不同用途的函數超載(Function Overloading)

  我們上一篇有介紹了「 【開發智能合約 - Solidity系列】實作篇Ep.11 - 繼承同源但不同意圖的函數覆寫(Function Overriding) 」學習到overriding這個關鍵字的概念,而今天介紹另一個非常相似的名詞overloading(超載),兩者看似很像,但本質上卻存在著非常大的差異,究竟要如何分辨呢? Overriding V.S Overloading 首先是overriding的概念,override這個單詞代表著覆蓋的意思,可以理解為「覆蓋掉xxx方法」,但為什麼沒看到在同一份合約中以override形式出現呢? 試想,同一份合約覆蓋相同方法似乎沒什麼意義對吧,有點脫褲子放屁的意義,因為最終僅會保留最後一份最新的功能函數(function),因此overriding才會被應用在繼承的情境之下。 contract Parent { /// @notice 工作 /// @dev 欲被繼承的方法應使用virtual關鍵字宣告 function doJob() public pure virtual { ... } } contract Child is Parent { /// @notice 工作 /// @dev 繼承並覆寫應使用override關鍵字宣告 function doJob() public pure override { ... } } 再者是overloading的概念,這邊可以拆成over及loading分開來看,over代表著「超過…」的意思,而loading則是「載入更多…」,合再一起看就是「超過…就載入更多…」,因此才會有同一份合約中明明相同的函數名稱,卻允許不同的參數傳入,也實現了不同的實作方法。 contract OverloadingExample { /// @notice 繪製單點圖 /// @dev 繪製x軸 function draw(uint x) public pure { } /// @notice 繪製平面圖 /// @dev 繪製x、y軸的平面圖 function draw(uint x, uint y) public pure

【Postman工具箱】 Mock Server簡介

圖片來源 相信有在開發Web應用的朋友應該對於Postman這套工具相當熟悉, 這套工具可以協助我們在產品尚未完成之前可以先進行一些基本的介接測試,甚至我們可以使用Postman去呼叫雲端的API,像是Google的語音辨識、文字翻譯、字典查詢…,這類大廠相信也都開放許多標準API(Application Programming Interface)讓普羅大眾可以使用,甚至我們可以撰寫自動化腳本去抓取我們想要的資料進行分析…等的應用。 而過往的Postman功能較為陽春, 僅支援主動呼叫設計完成的API, 因此常常被運用於自動化測試的範本設計, 講白話一點就是還沒有產品UI的一個簡易介面。 這樣仍存在著一個問題, 那就是複雜的業務邏輯開發通常責任落於後端, 而假設後端API尚未開發完成時, 行動端、網頁端的串接不就得進行等待嗎? 這樣對於開發效率來說並非好事, 因此Postman也將模擬API的功能加入到產品,也就是稱為「Mock Server」。 圖片來源 為什麼我們需要Mock Server? 圖片來源 以下幾點是開發中常常遇到的問題, 並且具有Mock Server之後能夠解決的一些問題點: - 前/後端開發不需等待, 遵照簡單快速就能搭建的Mock Server各自實作, 並且隨時討論修改介面。 - 開發功能更有信心, 畢竟我們在開發過程中常常是將腦海的設計實現, 並沒有真正運作。 - QA進行測試時能夠減少依賴, 獨立運作。 - 快速構建出產品的樣貌, 並且透過此簡易的樣貌向客戶展示。 - 自動化測試最重要的就是資料的來源, 而Mock Server省去了我們大費周章的開發成本, 用最簡單的方式構建出簡易的模擬真實環境。 實際Demo Step 1: 建立一個Mock Server 圖片來源 Step 2: 配置簡易的API 圖片來源 Step 3: 準備建立模擬API 圖片來源 Step 4: 建立成功後根據產生的URL進行測試 圖片來源 Step 5: 送出測試的URL並檢查回傳的資料 圖片來源 非常好!! 我們透過簡易的API架設就能模擬伺服器的資料,整個過程非常快速簡易, 操作也很直觀, 對於快速展示具有非常好的效果。 結語 雖然Postman提供了Mock Server很好用的一個功能, 但很可惜的是免費版具有一些請求上限的限制, 有興趣的朋友

【開發智能合約 - Solidity系列】實作篇Ep.11 - 繼承同源但不同意圖的函數覆寫(Function Overriding)

  圖片來源 我們在「 【開發智能合約 - Solidity系列】實作篇Ep.9 - 何謂繼承(Inheritance) 」有提到繼承的一些基本概念,然而在繼承的過程中我們可能會用到上游的方法,甚至加工,而方法名稱重複了,是否能被允許呢? 答案是「允許」的,就好比我們雖然繼承了父親的「處事技巧」,但在求新求變的時代中,或許傳統的老舊方法已經不適用於現代,因此就需要覆寫掉「處事技巧」這個方法,甚至基於傳統的方法之後進行擴增,而Solidity提供了繼承當然也支援了覆寫的方式,讓整個合約更加彈性。 但有幾個值得注意的是,當我們的合約欲設計為可覆寫的方法時,需要加入兩個重要的關鍵字,分別為: - virtual: 被繼承的合約方法需要標示此關鍵字之後,該方法才能被覆寫。 - override: 標示該方法以覆寫後的形式呈現。 contract Parent { /// @notice 工作 /// @dev 欲被繼承的方法應使用virtual關鍵字宣告 function doJob() public pure virtual { ... } } contract Child is Parent { /// @notice 工作 /// @dev 繼承並覆寫應使用override關鍵字宣告 function doJob() public pure override { ... } } 另外我們也可以用「擴充」的觀點來進行覆寫的功能加強。 contract Parent { event Log(string msg); /// @notice 工作 /// @dev 欲被繼承的方法應使用virtual關鍵字宣告 function doJob() public virtual { emit Log("i am parent"); } } contract Child is Parent { /// @notice 工作 /// @dev 繼承並覆寫應使用override關鍵字宣告 function doJob() public override { /// @de

【開發智能合約 - Solidity系列】概念篇: 區塊鏈也要燃料費用? 到底什麼是Gas呢?

  圖片來源 在講Gas這個概念之前,我們先以汽車為例子,不論是上高速公路還是加油,都是需要費用的,而費用的計算方式也跟我們使用的資源多寡有關,因此整個Gas就是圍繞在使用者付費的基礎之上,而計價的依據則根據Gas Price、Gas Limit最終產生出Gas fee。 相信對於Gas具備基本概念之後,我們在開發智能合約時,就能夠避免因為不理解而導致合約的不完整性,進而影響使用者的權益,這樣的計費概念其實也在營造一個網路世界的使用者付費習慣,相信在未來使用網路已經是不可避免的趨勢了,在資源有限的狀況下總不可能無限度的盡情揮霍,透過用多少付多少的概念創造經濟價值,讓生產者願意生產,使用者也願意消費,創造良好的商業環境,亦可衍生未來的新經濟模式。 關於Gas Price(燃料價格) 圖片來源 Gas Price我們就想像成汽油一公升多少錢,而在乙太鏈上的價格計算單位皆以ETH計價,也就是每單位的Gas願意付出多少ETH,通常以Gwei為單位,而1Gwei = 0.000000001Eth,因此假設Gas Price(燃料價格)為20Gwei,則換算成ETH等於「0.00000002ETH」。 什麼又是Gas Limit呢? 圖片來源 前面介紹的Gas Price(燃料價格),但我們的合約不可能是無上限的價格,因此為了合約的Gas限制就顯得特別重要,除了避免驚人的費用之外,也避免交易驗證失敗,假若上限值太低,有會導致運算資源使用不足,容易導致失敗,失敗後的手續費也是不會退還的喔,而一般標準交易的Gas Limit為「21000」,白話一點來說就是預估某段路程(台北)到某段路程(高雄)需要的里程數與油量。 Gas fee是什麼? 圖片來源 Gas Fee簡單來說就是手續費的概念,通常以使用量(Gas Limit)乘上每單位價格(Gas Price),進而計算出手續費(Gas Fee),但這樣的概念僅能是預估的估值,因為Gas Limit是合約開發時估計的上限,但實際上用量可能更少,因此建議讓Gas Limit設定高一些避免驗證失敗導致交易失敗,徒勞無功,至少Gas Limit設超過沒用完的部分還能退還。 哪裡可以查到Gas Price資訊? 查詢Gas Price就跟我們查詢油價資訊一樣,會公告目前油價「N元/1公升」,而ETH也有這樣的統計資訊,我們只要進入「 http

【開發智能合約 - Solidity系列】實作篇Ep.10 - 標準化的介面(Interfaces)

  Interface我們就將之想像成是一種標準化的規範,在產品還沒開發出來之前,我們心中想必已經有個藍圖,嗯…,這個功能需要什麼樣的功能,這時候就可以來制定介面,以「設計」為出發點而後再進入「實作」,如此一來我們在設計階段就能發現一些盲點,減少經過實作過程才發現的窘境,節省繁複修改的成本,而且介面定義出來後,對於後續的分工開發就會有極大的幫助,合約之間只要照著介面完成開發最終再拼裝起來,就會是一份快速又完整的智能合約。 來看看什麼是介面 圖片來源 以「車」為例子,車的種類有分成卡車、轎車、救護車…,但都有一個共同點就是可以駕駛移動,而這個共同點我們就可以訂定一個標準的介面,車只是一個概念,能夠驅動,因此我們只要確保每一種車都能夠驅動即可,至於要造出什麼樣的車就是各自實作囉,以下的例子就是一個簡單的介面範例,我們先定義車能夠進行驅動的簡單介面: // 汽車的介面 interface ICar { /// @notice 汽車的可以進行驅動 /// @dev !!! 這邊必須以external進行可視範圍的標示,因為對於介面來說就是外部可視 function drive() external returns (string memory); } 介面的填充實作 定義好介面之後,就是進入實作階段了,我們只要滿足「drive」這個方法即可,至於合約內部如何達成的,這不會是介面的職責,以底下的例子來說,我們會實作一台卡車,這台卡車與車本身都具有「驅動」的方法,只要能動,不管什麼類型的車,都是源於「車」。 // 汽車的介面 interface ICar { /// @notice 汽車的可以進行驅動 /// @dev !!! 這邊必須以external進行可視範圍的標示,因為對於介面來說就是外部可視 function drive() external returns (string memory); } contract Truck is ICar { function drive() public pure override returns (string memory){ return "Truck"; } } 規則 Interface的使用方式也有一些規範如下: -

【開發智能合約 - Solidity系列】實作篇Ep.9 - 何謂繼承(Inheritance)

  圖片來源 一個功能越趨完善且複雜的合約,勢必會拆成許多合約共同組成,而其實這些組成的合約之中許多的方法、元素都是重複的,因此我們可以使用Inheritance(繼承)的技巧,將共同的屬性、方法抽到某個上級合約,而其餘的合約只要繼承自上級合約,就能減少重複開發的狀況,我們都知道軟體開發的過程,只要開發的原始碼越多,維護的成本與複雜度就會越高,而這樣的技巧就是在避免當功能越來越複雜時,導致無法維護甚至難以閱讀。 基本用法 在Solidity的世界裡,繼承自誰的關鍵字就是「is」,基本結構如下: // 合約B繼承自A contract B is A { ... } 繼承自單一的來源 圖片來源 基本上最簡單的用法就是撰寫一份公共合約,而未來重複使用的子合約就繼承自公共合約即可,這是最基本的單一繼承。 首先我們先撰寫一份公共合約 可以傳入標題,並取得標題。 contract Basic { string public title; constructor(string memory _title) { title = _title; } function getTitle() public view returns(string memory) { return title; } } 我們帶入_title參數並Deploy之後,初始值就設定為「i am Basic」,並呼叫getTitle之後可以得到正確的訊息。 圖片來源 撰寫一份Basic0合約繼承自Basic並帶入初始值 contract Basic { ... } // 繼承自Basic並帶入初始化參數 contract Basic0 is Basic("i am Basic0") { } 我們在選擇合約的地方下拉換成Basic0並Deploy,接著呼叫getTitle()之後就會得到「i am Basic0」。 圖片來源 撰寫一份Basic1合約繼承自Basic並由外部帶入初始值 contract Basic { ... } // 繼承自Basic並支援外部帶入參數至Basic contract Basic1 is Basic { constructor(string

【開發智能合約 — Solidity系列】實作篇Ep.8 - 與交易有關的功能(Fallback & Receive)

  圖片來源 Solidity支援兩種特殊的函數,分別是Fallback以及Receive,一個是處理合約中不存在的功能時進行的回退機制,而另一個Receive則是負責收款後的動作,但兩者稱為特殊函數的原因主要是跟我們一般函數不同的地方於它們是屬於匿名的函數,也就是不用給定Function名稱,因此才會較為特殊,至於詳細用途我們以下再逐一介紹。 Fallback 主要處理合約中不存在的函數調用,可以用來掌握誰調用了不存在的功能,例如: 訊息警告。 那些條件會觸發Fallback? ● msg.data有資料時。 ● 呼叫合約中不存在的function時。 具有哪些限制? ● 由於Fallback可能是無意間被呼叫的,因此盡量在Fallback中減少額外的處理,避免燃料費的浪費,應盡量是少於2300 Gas ⛽。 ● function名稱直接以fallback命名。 ● 僅能將可視範圍設定為external。 哪些行為會導致Fallback消耗大量的Gas fee ⛽,請參考「 receive-ether-function 」。 範例: // SPDX-License-Identifier: MIT pragma solidity ^0.8.7.0; contract Example { // 定義事件並接收msg參數 event Log(string msg); fallback() external { // 當觸發fallback()時,撰寫fallback的訊息 emit Log("fallback"); } } 實際操作Remix來Demo fallback()的過程 圖片來源 我們在Transact那邊貼上帳號的地址,讓calldata有值,預期觸發Fallback,送出之後,由於沒有指定任何function,因此會觸發fallback機制,並發送Log訊息。 Receive receive僅負責處理接收ETH,一個合約最多只能有一個receive函數。 什麼條件下會觸發Receive? ● msg.data為空時。 ● 發送ether時。 具有哪些限制? ● 一樣會消耗Gas fee,因此不建議邏輯處理太過於複雜,應盡量是少於2300 Gas ⛽。 ● function名稱直接以rec