分類彙整:programming

關於做 Side Projects:改善點子的7個小技巧

身為軟體工程師,我通常一有空就會做些 side projects。

該做什麼樣的主題比較好呢?

久了之後慢慢發現幾個改善的點子的小技巧。跟大家分享一下。

1. 不要做大家都說「還不錯」的

不要做那種你跟別人聊過之後,大家都說「聽起來還不錯」的點子。

這種點子就是沒人在乎的點子,沒有解決任何人的問題。

做那種大家聽了都皺眉頭的點子。大家聽了都覺得有點困惑的點子。大家都覺得是在浪費時間的點子。

這種點子通常會有非常少數人聽了眼睛為之一亮。他們不會說「聽起來還不錯」,他們會說「真的嗎?可以現在就給我用嗎?」

他們甚至願意當場就付錢。執行這種點子。

先從解決極少數人真正的問題開始,然後從那裡往下走。

相關文章:http://www.paulgraham.com/startupideas.html

2. 反過來開始做

據說 Amazon 內部開發產品的時候,會先從寫「產品發表會的新聞稿」開始。

在寫宣傳稿的同時,會希望把產品描述的吸引人、貼近用戶,並且先省略技術細節。

同樣的道理,在做 side projects 的時候,有時候會不太確定自己在幹嘛?

這個時候可以反過來開始做,先寫「產品上線發佈文案」。

也就是專案做好之後,你會在 FB 或是 PTT 之類的地方,貼文章跟親朋好友介紹的那種貼文。

介紹文章寫完之後,通常思緒跟目標也會跟著清楚許多。

相關關鍵字:Amazon Working Backwards

3. 克服一個挑戰…很困難,克服兩個挑戰…幾乎不可能

不管執行哪種點子,幾乎都會碰上一些挑戰。

比方說經營某種平台,需要有供需兩方,那一開始到底如何起頭?(雞生蛋、蛋生雞問題)

比方說某種創新服務,需要用戶帶著手機在指定地點做某件事(培養使用者某種全新的習慣)。

克服一個挑戰就更困難了,但花費足夠精力的話,至少還有點可能。

如果是個需要同時克服兩個以上挑戰才能 work 的點子,執行起來真的會非常非常困難。

稍微轉換方向,或是簡化它。先從一次面對一個挑戰開始吧。

4. 先睡一覺,明天再說

你有沒有那種經驗,就是你朋友興高采烈的找你討論某個點子,你也熱血的急著動手做了。

結果過幾天你發現他只是一頭熱,他自己不再有興趣了,而你這段時間都是在浪費時間?

其實一個人做自己的專案也一樣,不管當下有多熱情,最好都… 睡一覺醒來再說。

把這個想法簡單寫進文件裡面。頂多稍微設計一下架構。

隔天起床還是一樣覺得很熱血嗎?那不妨… 再睡個幾天再說。

不要因為這種一頭熱把生活中其他事情排開。

大多時候你會發現,睡醒之後你對昨天的爛點子其實沒什麼興趣了。

做那種不管過了多久,都還是每天起床,會覺得非做不可的事情。

5. 洗澡的時候…縈繞心頭的事情

點子這種東西想破頭通常也沒用。為了做而做也很沒意思。

想想看有沒有在洗澡的時候常常會浮現的念頭。

那種時不時會出現,你總覺得好像可以動手做的事情。

下次這種念頭又偷偷出現的時候,不要再忽略它。

相關文章:http://www.paulgraham.com/top.html

6. 不能只比現有的 solution 好一點點,要好很多

不論想做的新產品/服務是什麼,它都必須要比現有的解決方案… 好非常多才行。

如果只是好一點點的話,用戶是不會買單的,畢竟轉換需要成本,大家會寧可用已經習慣的。

如果你要做的東西只比現存方案好一點點,你心想「試試看,推推看,也許可以吸引到大家改用我的產品」那你就錯了。

不如想清楚一點、多下一點決心:直接做一個需要多花10倍心力,但是屌打現有方案的東西吧!

相關關鍵字:Elon Musk. It can’t be just a little better than the competition. it has to be great.

7. 點子是很脆弱的,一捏就死

仔細想想就會覺得,幾乎什麼點子都不可行。

每個點子都明顯有很困難的地方,根本通通都不可行。

否定總是最簡單的。

這個做不到、那個不可行、然後沒有錢執行這個跟那個。

真的是這樣嗎?

新點子、想法、創意,這種東西永遠都是非常脆弱。用力摸一下就粉碎的東西。

不如養成一個習慣:不論是聽起來再怎麼糟糕的爛點子,都留給它一點點點點的生存空間。

再讓它存活一下下。過陣子再想一下…真的不可行嗎?…確定嗎?

真的有這麼確定?你到底是為何可以這麼確定?

再保持野心一下下,再調查研究一下下,也許你會發現,其實有你可以動手的空間。

相關文章:http://fortune.com/2011/10/24/jonathan-ive-on-steve-jobs-and-the-fragility-of-ideas/

(完)

清單力量 Open Source:跟網友一起蒐集清單資料的系統

我最近開發了一套 open source,叫做 ListPower,清單力量。

ListPower 讓你可以針對特定主題,與社群一起蒐集整理資料,感受到清單力量的強大!就像這樣:

我把它 open source 開源釋出到 github:

https://github.com/howtomakeaturn/ListPower

歡迎有興趣的工程師自行把玩看看!

起源

前陣子因為很常去咖啡廳工作,所以做了一個網站,讓網友們可以一起蒐集適合工作的咖啡廳資料。

https://cafenomad.tw/

網站背後的「清單核心系統」有六大功能,讓網友們可以一起蒐集資料:

  • 新增
  • 編修
  • 評分
  • 留言
  • 照片
  • 標籤

我當時發現,這六大功能其實不只可以用來蒐集咖啡廳資料,蒐集其他主題的資料也可以。

首次嘗試 open source

所以我首次嘗試把核心的系統程式碼做成 open source 開源軟體,放在 github 供人下載使用:

https://github.com/howtomakeaturn/nomadic

陸續有幾個團隊想要使用這套系統,所以找我用「安裝+客製化開發」的方式接案來開發。

UrSchool

Screenshot from 2019-03-31 14-31-18

第一個是對教育產業有熱情的 UrSchool 團隊,希望能有一套系統,可以整理教師資訊:

https://urschool.org/

他們整理、建檔了多個學校的教師資訊,然後找了各校的學生幫忙評分、留言、貢獻資訊。

網站也順利上線蒐集了不少資料!

SportsMap 運動地圖

Screenshot from 2019-03-31 14-22-41

第二個是對運動產業有熱情的 SportsMap 團隊,希望能有一套系統,整理球隊、運動場地資訊:

(目前網站資料以羽球為主)

https://isportsmap.com/taipei/all-club

https://isportsmap.com/taipei/all-place

https://isportsmap.com/taipei/map

他們整理、建檔了各縣市的羽球場地資訊,然後找了各社群的球友幫忙評分、留言、貢獻資訊。

這些網站上線之後,獲得相關社群不錯的迴響,也藉由 SEO/SMO 穩定獲得網站流量。

新版 open source

「清單核心系統」是在 2016 年為了 Cafe Nomad 所開發,距今已有一些年紀。

不論是 UI 介面跟底層架構都需要更新(Laravel 5.3 升級 5.8,Bootstrap 3.4 升級 4.3)。

資料基礎模型架構也需要改寫,從 JSON-based 架構改寫成 Entity-Attribute-Value (EAV) model 架構。

整套系統重寫很花時間,但目前最核心的功能已經重寫完畢。

新系統用起來、看起來會像這樣:

[適合下班後小酌的酒吧清單]

https://listbox.app/list/qKnP10

[網站與手機APP接案公司清單]

https://listbox.app/list/zj4djZ

程式碼也作為開源軟體 open source 釋出到 github:

https://github.com/howtomakeaturn/ListPower

結語

如果你有想要蒐集的資料,歡迎聯絡我討論看看,也許可以在 https://listbox.app/ 直接建清單出來。

除此之外,我也可以用接案的方式替客戶安裝這套系統。

如果你需要獨立安裝這套系統,或是對這套系統有任何問題,也歡迎找我討論看看:

[email protected]

https://www.facebook.com/chuanhao.you

根據你的需求,應該可以找到快速安裝、架站的方式,或是持續客製化修改、頻繁討論的方式。

可以來研究看看用什麼方式安裝、開發,還有相關的報價、收費怎麼計算會比較適合。

(完)

分離式客製化開發:兼顧模組化與客製化的 Laravel 彈性架構

接案的時候,常常很想把一套系統寫完之後,一口氣賣給多個客戶。

偏偏每個客戶都有各自不同的客製化需求,沒辦法真的都拿同一個專案去佈署。

只好把一個專案複製成好幾份,然後分別客製化給不同客戶。

但這樣又會出現維護問題:各專案的差異越變越大。

之後就算更新了核心功能、修理了核心 bug,也很難一口氣替所有客戶升級。

如果各專案的差異是無法避免的,那有沒有辦法至少,讓核心系統與客製化的程式碼分開來放呢?

最好能讓各專案的檔案結構都像這樣:

System/
Customization/

如此一來,雖然 Customization/ 內的 code 不能重複使用,至少 System/ 內的 code 可以。

可以將 System/ 內的 code 更新進其他專案,也可以拿強化後的核心系統去接未來的新案子。

我把這種檔案結構稱為「分離式客製化架構」,下文用 SCA (Separated Customization Architecture) 代稱。

這篇文章跟大家分享在 Laravel 底下如何使用這種 SCA 架構。

前置需求

使用 SCA 架構非常簡單,只要安裝 laravel-modules 套件就可以了。

laravel-modules 套件非常單純,它只是運用了 Laravel 原生的 Package Development 觀念,幫助你把 app/ 與 resources/ 資料夾底下的程式碼移進另外的 Modules/ 資料夾而已。

Modules/ 內可以有一或多個資料夾,每個資料夾就跟一個獨立 laravel app 一樣,有自己的 controllers, views, entities, routes, migrations… 等等內容。

laravel-modules 套件就只是幫你讀取各個模組的內容而已,沒有其他黑魔法。

如此一來,你的 Laravel 專案的檔案結構會變成這樣:

Modules/
app/
resources/
...

注意到了嗎?它剛好對應到前面說的檔案結構:

System/
Customization/

Modules/ 就是 System/,而 app/ 與 resources/ 就是 Customization/。

那麼實際上該如何開發呢?以下直接舉例說明。

情境:你開發了一套 Awesome 通用系統來接案

首先你根據過去的經驗開發了一套 Awesome 電商官網模組,打算用它來接許多案子,賺大錢。

你的 laravel 檔案結構長這樣:

Modules/Awesome/
app/
resources/

Awesome 模組的 controllers, views, entities, routes, migrations… 等等內容都在 Modules/Awesome/ 裡面,就跟一個獨立 laravel app 一樣。

而 app 與 resources 資料夾內目前只有 laravel 預設檔案,你沒寫半行 code 在裡面。

你拿 Awesome 系統開始接案,接著客戶出現。他們有以下的客製化需求。

情境:客戶想修改 UI

客戶要求對首頁畫面微調。

這個時候請利用 laravel 原生的 Overriding Package Views 機制,你直接新增一個檔案:

resources/views/vendor/awesome/homepage.blade.php

就在這個檔案內隨心所欲的重寫或是亂寫吧!它會直接取代原本的這個檔案:

Modules/Awesome/Resources/views/homepage.blade.php

如果不只是畫面微調,這種模板覆蓋不夠用,那就可以視為修改系統的 business logic,請接著往下讀。

情境:客戶想修改系統的 business logic

客戶想修改結帳前的購物車頁面與功能。

此功能原本的 routing 定義在 Modules/Awesome/Http/routes.php 內:

Route::group(['middleware' => 'web', 'namespace' => 'Modules\Awesome\Http\Controllers'], function()
{
    Route::get('/shopping/cart', 'ShoppingController@cart');
    // blah blah...
});

請在原本 laravel 預設的 routes/web.php 內新增:

Route::get('/shopping/cart', 'ShoppingController@cart');

接著建立 app/Http/Controllers/ShoppingController.php 檔案,去繼承原先在 Modules/Awesome/ 內的 controller:

namespace App\Http\Controllers;

use Modules\Awesome\Http\Controllers\ShoppingController as BaseController;
use Illuminate\Http\Request;

class ShoppingController extends BaseController
{
    function cart(Request $request)
    {
        // 隨你修改商業邏輯 ...

        // 繼續用原有模板與覆蓋機制
        // return view('awesome::shopping.cart');

        // 或是在 resources/views/ 內建一個新的也行
        return view('the-new-cart');
    }
}

這樣就會直接取代原本的功能了!

最棒的是,這只是用上了基本的 OOP 類別繼承觀念,以及 laravel 的特性:「重複的話,後定義的 route 會覆蓋先定義的 route」。

有需要的話,domain model 內的類別也可以用同樣手法繼承來擴充。

比如說原本有個 Modules/Awesome/Cart.php 類別:

namespace Modules\Awesome;

use Illuminate\Database\Eloquent\Model;

class Cart extends Model
{
    // blah ...
}

你可以建立 app/Cart.php 這樣的子類別:

namespace App;

use Modules\Awesome\Cart as BaseCart;

class Cart extends BaseCart
{
    // blah ...
}

在新建立的 controller 內就可以使用這些擴充後的新 domain model 類別。

用這種手法可以自由新增程式碼!而且全是在 app/ 或 resources/ 資料夾內新增,完全沒碰到 Modules 內的東西。

情境:客戶想開發新功能

開發全新功能就更簡單了,直接在 routes/web.php 內增加 routing 後,在 app/ 與 resources/ 內增加你需要的檔案就行了。

就跟平常開發 laravel 專案一樣!

有需要的話,就在新的檔案裡面引入或是繼承 Modules/ 內的類別就行了。

情境:需要更新資料庫 schema

laravel-modules 有提供指令幫忙產生 migration 檔案。

所以屬於 Awesome 系統的 migration 檔案會在 Modules/Awesome/Database/Migrations/ 底下。

如果客製化時需要更新 schema ,就用 laravel 原生的 migration 指令在 database/migrations/ 內建立 migration file 即可。

連 migration file 都可以完全分離在 System/ 與 Customization/ 裡面!

結論

SCA 的特色在於它不預設你打算把專案模組化或是客製化到哪種程度。

你可以為了客戶在 Customization/ 內先寫完功能。之後有空再完全重構或是部份重構進 System/。

也可以遇到需求就先強化 System/,寫到不適合的地方再開始從 Customization/ 接手客製化。

你可以視情況來來回回,以一種混合的方式寫 code。

你可以邊寫邊決定,事後隨時反悔也沒問題,所以 SCA 非常適合接案類型的軟體開發。

針對 SCA 還有許多進階議題可以討論,本文只談最基本的觀念與技巧,但應該足以解決大部份的問題。

我自己用這種方式接案開發了幾個月,效果非常不錯,分享給各位參考。

有任何問題歡迎留言讓我知道,或是寄信到 [email protected] 與我討論。

(完)

軟體工程接案技巧:「週薪式兼職」報價

最近接網站開發的案子,特別是新創團隊的案子,我發現用週工時的兼職方式報價,比報固定價格的傳統接案方式好很多。

報價方式會像這樣:

  1. 接案者每週工作 N ~ M 個小時
  2. 每週收費為新台幣 X 元
  3. 預估案件大概 Y 週會做完
  4. 每 Z 週結帳一次

N、M、X、Y、Z 的原理如下:

  • 根據案件的困難程度、急迫程度來決定 N、M、X
  • 要讓案主知道估計的總花費,所以要估一個 Y
  • 接著由雙方的互信與方便程度決定 Z

這種報價方式看似含糊曖昧、對雙方來說都有風險。實際上卻正好相反,它比傳統報價方式符合雙方需求得多,也比較符合現實狀況。

舉例來說,我最近一次替新創團隊做網站的接案對話如下。我先說明報價方式:

111

接著補充說明一些:

222

案主很爽快的答應了,後續也進行得非常順利。這種兼職方式解決了傳統報價方式會遇到的3個問題:

  1. 需求與規格不必在事前定得非常清楚
  2. 消除案主的訂金恐懼、消除接案者的尾款恐懼
  3. 以連續多個較小的承諾培養互信,取代一次性的大承諾

需求與規格不必在事前定得非常清楚

軟體工程幾乎不可能準確規劃規格與細節,因此案件總金額、總工時根本估不準。

按照傳統的方式,雙方只能各憑經驗,討論出一個價格。這價格簡直是兩邊各猜一個數字之後妥協的,一點都不準,價格很少是公平的。常常導致接案者覺得自己被凹了,或是是案主覺得自己被當肥羊宰了。

其實,對很多案件來說,特別是新創團隊的案件,這種固定價格式的接案本來就不合理。

現在創業的商業方法論,講求執行、靈敏。趕快先開始,過幾天有新想法、新功能想做,再調整開發的優先順序。需求與規格本來就會跟一開始不同。

接案者要案主一開始就把規格訂得非常清楚,實在是強人所難。

而這種情況下,案主在一開始就想確認一個絕對數字的預算,也是強人所難。

除此之外,光是為了報價,就必須花一堆時間確認規格、需求。這些開會討論的時間成本很高,卻又不可能跟案主說「我給報價是要收費的」。

於是接案者為了保護自己,會演化出一種習慣:防禦式報價。

那就是把情況都往壞的方向預想,一律只給偏高金額的報價。這對整個接案市場都不是什麼健康的事情。

週薪式兼職不會出現防禦式報價。只要大致溝通一下需求,接案方大概了解情況,馬上就可以開始動工。

消除案主的訂金恐懼、消除接案者的尾款恐懼

對案主來說,什麼都沒拿到就要先付訂金,還經常是總額的1/3到1/2,壓力不小。

對工程師來說,一大部份費用卡在尾款,還有可能一拖再拖,結不了案,同樣可怕。

每 Z 週結帳一次,雙方觀察對方的時間多了許多,不用再賭人品。

以連續多個較小承諾培養互信,取代一次性的大承諾

傳統接案方式,雙方可能從頭到尾都彼此怕怕的,因為兩個沒合作過的人,要在一開始就互相綁定一個大承諾,也不知道案件會不會進行順利、對方人品到底怎麼樣。

而週薪式兼職,對案主來說,最糟的情況就是幾週之後工程師擺爛失聯了,損失幾週的錢。就算這樣也比傳統報價方式搞砸時,損失50%總金額的訂金好。

對工程師來說,最糟的情況就是定期結帳時,案主不付錢失聯了,損失幾週的錢。就算這樣也比傳統報價方式搞砸時,損失50%總金額的尾款好。

這種方式不但可以逐步培養互信,也對溝通很有幫助。

因為雙方每 Z 週必須真實面對彼此一次(付錢/要錢的時候,就是真實面對彼此的時候)。有什麼誤會都會被迫儘早說開,不會拖拖拖好幾個月,到要結案才發現雙方距離彼此期待差超多。屆時的誤解跟憤怒都會高到無法溝通了。

結語與經驗分享

本文一開始提到的接案經驗,雖然規模很小(我1人+案主方2人參與),但是執行得非常順利、非常愉快。連一開始的報價我都只看了一份 UI 的 pdf 檔一眼,5分鐘就給出報價。

我們甚至連合約都沒簽,隔天就開工,只花費 4 週就完成了這個案件。每週見面1-3次,搭配頻繁的線上討論,總金額新台幣 48,000 元。一點浪費時間的感覺都沒有,是一次非常理想的商業合作經驗。

下次接案、發案時,碰到傳統接案方式的困境的話,不妨以這種方式接案、發案,根據個別情況調整一下 N、M、X、Y、Z 參數即可。

傳統合作產生的商業糾紛,或許只是 pricing model 的問題而已。

附註:為什麼工時是區間、不是定額?

跟案主見面開會完之後的閒話家常,算不算工時?
工程師通勤去開會的時間,算不算工時?
工程師吃晚餐、出門散步的時候腦中在想演算法,算不算工時?
案主要求的技術工具太有趣,工程師自行額外看了一堆進階文件,算不算工時?

每件小事都要定義清楚算不算工時,有點不切實際,對雙方來說都很煩,而且斤斤計較這些東西有點傷感情。區間讓案主、接案者雙方都更自在。

不如承認這點曖昧性,兩邊都知道會有彈性,彼此守住一些議價的立場就好了。而且是區間的話,你也不用一直拿手錶計時了。每週抓固定幾個時段工作就差不多了。案主也不用每次開會要一直看手錶了。

除此之外,區間工時可以保護接案者的時薪高低的彈性。未來再接案時,你會有立場在 X/N 到 X/M 附近的範圍報價。

(完)

虛擬貓咪原始碼&智慧合約入門筆記

最近想多了解智慧合約的實際應用狀況,希望能跟區塊鏈上的智慧合約做簡單互動。

於是找了目前最成功的應用之一「虛擬貓咪」來研究。

分享一下目前的研究心得。

在開始之前,可以先閱讀以下兩個連結,對於閱讀本文會非常有幫助:

https://medium.com/loom-network/how-to-code-your-own-cryptokitties-style-game-on-ethereum-7c8ac86a4eb3

https://medium.com/loom-network/your-crypto-kitty-isnt-forever-why-dapps-aren-t-as-decentralized-as-you-think-871d6acfea

智慧合約不是完全公開透明的

以虛擬貓咪為例,智慧合約本身看似可以在此取得:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code

但貓咪基因科學的合約是額外佈署的,不但可以由作者動態修改,而且還只有 opcode 可讀。

同樣的道理,您也可以利用這種「設定外部合約」並且不公佈那份外部合約的 Solidity 原始碼,來達成這種保密效果,還讓合約保持可以更新、升級的空間。

智慧合約不是完全去中心化的

除此之外,虛擬貓咪智慧合約定義了 CEO、CFO、COO 三個管理員角色。這三個角色各自有額外權力,甚至可以凍結整個合約的運行。

不僅如此,區塊鏈上的資料看似永恆,但其實除了原始團隊,沒人可以解讀基因編碼。因此一大部份 value 還是來自傳統 web server。所以虛擬貓咪根本沒辦法脫離作者團隊獨立存活。

如何用 web3.js 讀取 address balance?以我與虛擬貓咪為例

要讀取每個地址的以太幣餘額,最簡單的方式就是直接透過 etherscan.io

比如說,我的個人餘額可以在此查看:https://etherscan.io/address/0xcb0418eae76e14c214d79b8305ca34669075cba6

而虛擬貓咪這份合約地址的餘額,可以在此查看:https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d

以工程師的立場,會希望用程式去跟區塊鏈互動。以在網頁內使用 web3.js 為例,安裝了 MetaMask 來連線到區塊鏈之後,可以用以下程式碼讀取地址餘額:

  var web3Provider = web3.currentProvider;

  var web3 = new Web3(web3Provider);

  var tonyAddress = '0xcb0418eae76e14c214d79b8305ca34669075cba6';

  var cryptoKittiesContractAddress = '0x06012c8cf97bead5deae237070f9587f8e7a266d';

  web3.eth.getBalance(tonyAddress, function(error, result){
    console.log('tony balance: ' + result);
  });

  web3.eth.getBalance(cryptoKittiesContractAddress, function(error, result){
    console.log('crypto kitties balance: ' + result);
  });

如何用 web3.js 讀取智慧合約的 public variable?以虛擬貓咪為例

試著讀取智慧合約中三個公開變數的值。最簡單的方式一樣是透過 etherscan.io

可以在這裡看到三個管理員(CEO、COO、CFO)的地址: https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#readContract

用 web3.js 來讀取的話,可以先去 etherscan.io 取得合約的 ABI:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code

接著像這樣從區塊鏈上讀出值:

  var web3Provider = web3.currentProvider;

  var web3 = new Web3(web3Provider);

  var cryptoKittiesContractAddress = '0x06012c8cf97bead5deae237070f9587f8e7a266d';

  var AbiOfContract = [{"constant":true,"inputs":[{"name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cfoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_preferredTransport","type":"string"}],"name":"tokenMetadata","outputs":[{"name":"infoUrl","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"promoCreatedCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ceoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_STARTING_PRICE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setSiringAuctionAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pregnantKitties","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_kittyId","type":"uint256"}],"name":"isPregnant","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_AUCTION_DURATION","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"siringAuction","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setGeneScienceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCEO","type":"address"}],"name":"setCEO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCOO","type":"address"}],"name":"setCOO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_kittyId","type":"uint256"},{"name":"_startingPrice","type":"uint256"},{"name":"_endingPrice","type":"uint256"},{"name":"_duration","type":"uint256"}],"name":"createSaleAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"sireAllowedToAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_matronId","type":"uint256"},{"name":"_sireId","type":"uint256"}],"name":"canBreedWith","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"kittyIndexToApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_kittyId","type":"uint256"},{"name":"_startingPrice","type":"uint256"},{"name":"_endingPrice","type":"uint256"},{"name":"_duration","type":"uint256"}],"name":"createSiringAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"val","type":"uint256"}],"name":"setAutoBirthFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"},{"name":"_sireId","type":"uint256"}],"name":"approveSiring","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCFO","type":"address"}],"name":"setCFO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_genes","type":"uint256"},{"name":"_owner","type":"address"}],"name":"createPromoKitty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"secs","type":"uint256"}],"name":"setSecondsPerBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_CREATION_LIMIT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newContractAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setSaleAuctionAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"count","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_v2Address","type":"address"}],"name":"setNewAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"secondsPerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"name":"ownerTokens","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_matronId","type":"uint256"}],"name":"giveBirth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawAuctionBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"cooldowns","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"kittyIndexToOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"cooAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"autoBirthFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"erc721Metadata","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_genes","type":"uint256"}],"name":"createGen0Auction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_kittyId","type":"uint256"}],"name":"isReadyToBreed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PROMO_CREATION_LIMIT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_contractAddress","type":"address"}],"name":"setMetadataAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"saleAuction","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getKitty","outputs":[{"name":"isGestating","type":"bool"},{"name":"isReady","type":"bool"},{"name":"cooldownIndex","type":"uint256"},{"name":"nextActionAt","type":"uint256"},{"name":"siringWithId","type":"uint256"},{"name":"birthTime","type":"uint256"},{"name":"matronId","type":"uint256"},{"name":"sireId","type":"uint256"},{"name":"generation","type":"uint256"},{"name":"genes","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_sireId","type":"uint256"},{"name":"_matronId","type":"uint256"}],"name":"bidOnSiringAuction","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"gen0CreatedCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"geneScience","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_matronId","type":"uint256"},{"name":"_sireId","type":"uint256"}],"name":"breedWithAuto","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"matronId","type":"uint256"},{"indexed":false,"name":"sireId","type":"uint256"},{"indexed":false,"name":"cooldownEndBlock","type":"uint256"}],"name":"Pregnant","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"approved","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"kittyId","type":"uint256"},{"indexed":false,"name":"matronId","type":"uint256"},{"indexed":false,"name":"sireId","type":"uint256"},{"indexed":false,"name":"genes","type":"uint256"}],"name":"Birth","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newContract","type":"address"}],"name":"ContractUpgrade","type":"event"}];

  var contractAbi = web3.eth.contract(AbiOfContract);

  var myContract = contractAbi.at(cryptoKittiesContractAddress);

  myContract.ceoAddress(function(error, result){
    console.log('ceo address: ' + result);
  });

  myContract.cooAddress(function(error, result){
    console.log('coo address: ' + result);
  });

  myContract.cfoAddress(function(error, result){
    console.log('cfo address: ' + result);
  });

如何用 web3.js 呼叫智慧合約的 public function?以虛擬貓咪為例

呼叫公開函式的話,最簡單的方法是透過 etherscan.io。

以虛擬貓咪的 getKitty 函式為例,如果要找出 ID 為 1 的那隻創世貓咪的資料,只要在欄位內輸入送出即可:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#readContract

用 web3.js 來讀取的話,則可以像這樣來呼叫函式(以分別抓出1、2、3 三隻貓咪為例):

  var web3Provider = web3.currentProvider;

  var web3 = new Web3(web3Provider);

  var cryptoKittiesContractAddress = '0x06012c8cf97bead5deae237070f9587f8e7a266d';

  var AbiOfContract = [{"constant":true,"inputs":[{"name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cfoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"},{"name":"_preferredTransport","type":"string"}],"name":"tokenMetadata","outputs":[{"name":"infoUrl","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"promoCreatedCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ceoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_STARTING_PRICE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setSiringAuctionAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pregnantKitties","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_kittyId","type":"uint256"}],"name":"isPregnant","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_AUCTION_DURATION","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"siringAuction","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setGeneScienceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCEO","type":"address"}],"name":"setCEO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCOO","type":"address"}],"name":"setCOO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_kittyId","type":"uint256"},{"name":"_startingPrice","type":"uint256"},{"name":"_endingPrice","type":"uint256"},{"name":"_duration","type":"uint256"}],"name":"createSaleAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"sireAllowedToAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_matronId","type":"uint256"},{"name":"_sireId","type":"uint256"}],"name":"canBreedWith","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"kittyIndexToApproved","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_kittyId","type":"uint256"},{"name":"_startingPrice","type":"uint256"},{"name":"_endingPrice","type":"uint256"},{"name":"_duration","type":"uint256"}],"name":"createSiringAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"val","type":"uint256"}],"name":"setAutoBirthFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"},{"name":"_sireId","type":"uint256"}],"name":"approveSiring","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_newCFO","type":"address"}],"name":"setCFO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_genes","type":"uint256"},{"name":"_owner","type":"address"}],"name":"createPromoKitty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"secs","type":"uint256"}],"name":"setSecondsPerBlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"withdrawBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GEN0_CREATION_LIMIT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"newContractAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setSaleAuctionAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"count","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_v2Address","type":"address"}],"name":"setNewAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"secondsPerBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"name":"ownerTokens","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_matronId","type":"uint256"}],"name":"giveBirth","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawAuctionBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"cooldowns","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"kittyIndexToOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_tokenId","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"cooAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"autoBirthFee","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"erc721Metadata","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_genes","type":"uint256"}],"name":"createGen0Auction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_kittyId","type":"uint256"}],"name":"isReadyToBreed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PROMO_CREATION_LIMIT","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_contractAddress","type":"address"}],"name":"setMetadataAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"saleAuction","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getKitty","outputs":[{"name":"isGestating","type":"bool"},{"name":"isReady","type":"bool"},{"name":"cooldownIndex","type":"uint256"},{"name":"nextActionAt","type":"uint256"},{"name":"siringWithId","type":"uint256"},{"name":"birthTime","type":"uint256"},{"name":"matronId","type":"uint256"},{"name":"sireId","type":"uint256"},{"name":"generation","type":"uint256"},{"name":"genes","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_sireId","type":"uint256"},{"name":"_matronId","type":"uint256"}],"name":"bidOnSiringAuction","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"gen0CreatedCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"geneScience","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_matronId","type":"uint256"},{"name":"_sireId","type":"uint256"}],"name":"breedWithAuto","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"matronId","type":"uint256"},{"indexed":false,"name":"sireId","type":"uint256"},{"indexed":false,"name":"cooldownEndBlock","type":"uint256"}],"name":"Pregnant","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"approved","type":"address"},{"indexed":false,"name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"kittyId","type":"uint256"},{"indexed":false,"name":"matronId","type":"uint256"},{"indexed":false,"name":"sireId","type":"uint256"},{"indexed":false,"name":"genes","type":"uint256"}],"name":"Birth","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newContract","type":"address"}],"name":"ContractUpgrade","type":"event"}];

  var contractAbi = web3.eth.contract(AbiOfContract);
  var myContract = contractAbi.at(cryptoKittiesContractAddress);

  myContract.getKitty(1, function(error, result){
    console.log('kitty info: ' + result);
  });

  myContract.getKitty(2, function(error, result){
    console.log('kitty info: ' + result);
  });

  myContract.getKitty(3, function(error, result){
    console.log('kitty info: ' + result);
  });

心得感想

像這樣的智慧合約,究竟有多少應用場景?

一個 dapp 該去中心化到什麼程度?智慧合約的未來到底有多少潛力?實在沒人有把握。

還是老話一句,建議有心參與的朋友,保持懷疑精神,花時間了解更多,建立自己的觀點跟看法,不要輕信任何人的說法。

(完)

Ethereum 智慧合約佈署初體驗:數據與成本分享

正式佈署一份智慧合約是什麼感覺?

它的佈署速度、佈署成本又是多少呢?

我親自跑了一次流程。分享一下實際的數據與心得。

發行個人專屬加密貨幣:阿川幣

我直接拿官網的範例程式碼來修改參數,發行阿川幣:

https://ethereum.org/token

官網提供兩份範本,一份只有基本功能,一份具備進階功能。

以太錢包預估了基本合約的佈署成本是 0.000715604 ether(約合新台幣 22 元):

erc20

而進階合約的佈署成本是 0.001010363 ether(約合新台幣 31 元):

advanced

我選擇佈署基本合約就好。輸入參數之後,會發現成本微幅提昇到 0.000777146 ether(約合新台幣 24 元):

with-parameters

按下 DEPLOY 按鈕,會確認密碼,並顯示預估成本 0.000777146 ether 是因為預估耗用 777,146 gas,並且多準備 0.0001 ether 作為緩衝:

gas-usage

送出後,以太錢包會顯示 etherscan.io 網站上的交易連結:

https://etherscan.io/tx/0xcedd9b10deb6ca64b44a547244045383d4563f916849c005308453d939f69c51

大約 6 分鐘後,網頁上的交易狀態顯示為 success:

6-min-success

不過以太錢包內還沒有同步顯示完成。再過 4 分鐘,也就是大約 10 分鐘後,以太錢包上才顯示 created:

10-min-success

進入帳號底下查看,會看到 2,100 萬枚阿川幣正確顯示,代表合約佈署完成了。

我本來有 0.01 ether,現在剩 0.009222854 ether,所以的確是被扣款 0.000777146 ether,以太錢包的預估成本是準確的:

final-balance

並且可以查看合約的地址,合約的原始碼也在裡面:

https://etherscan.io/address/0x03f6a68baf85840a513a71f49f5f8fb1edcf27f6

心得感想

這次初體驗,以個人用戶來說,我認為佈署速度不算太慢(約 6 – 10 分鐘),手續費也還行(約合新台幣 24 元),都還 OK。

所以佈署這種範例程式碼等級的合約,不困難,也不昂貴。

可以以此為基礎,再進一步研發更複雜的智慧合約。

至於智慧合約的未來到底有多少潛力?其實沒人有把握。

還是建議有心參與的朋友,保持懷疑精神,建立自己的觀點跟看法,不要輕信任何人的說法。

(完)

搞懂為何設定 React、JSX、ES2015、Babel、Webpack 的學習筆記

學習 React、JSX、ES2015、Babel、Webpack 等等現代化的前端開發觀念時,會需要安裝很多工具、設定多個檔案、用到許多指令。

大部份教學文章只講「怎麼設定這些」而沒說明「為何要設定這些」。照著做完感覺很不放心。

最近我試著搞懂「為何要設定這些」,跟大家分享一下這個逐步搞懂的過程。

前言

這篇文章假設你想要開始用 React 開發,希望搞懂相關的前端環境與設定。

如果你想知道用 React 到底有什麼好處,可以參閱以下連結:

Introducing JSXThinking in React簡單聊一下 ONE-WAY DATA FLOW、TWO-WAY DATA BINDING 與前端框架

接下來,這篇文章會以試著寫一個陽春的 App 元件作為範例,內含 ChildA 跟 ChildB 兩個元件。

讓我們開始吧!

第一步:直接在 html 檔內開發 React 給瀏覽器執行

我們先用原始的開發方法硬搞看看!

用 script 標籤直接從 CDN 引入 react.js 跟 react-dom.js 檔案。

然後試著寫一個陽春的 App 元件,內含 ChildA 跟 ChildB 兩個元件。

這個 html 檔內容會像這樣:






Should Render Here

source code

結果瀏覽器無法執行 React 相關的程式碼!

原因有兩個:

1. 瀏覽器看不懂 ES2015 的語法(class、extends)

2. 瀏覽器看不懂那個像是把 html 寫在 js 裡面的 JSX 語法

該怎麼辦呢?

第二步:用 Babel 讓瀏覽器能看懂 ES2015 與 JSX 語法

我們可以使用 Babel 套件來來編譯 ES2015 與 JSX 的程式碼,讓瀏覽器能夠看懂。

只要在 html 內加上這行:


然後在要編譯的 script 加上 type=’text/babel’ 即可:








Should Render Here

source code

然而,這樣做卻又產生3個新的問題:

1. 有N個檔案,就要打編譯指令N次,太累了

2. 透過 script 標籤引入N個檔案,會增加伺服器負擔、也會增加用戶端等待時間

3. babel 相關的程式碼都移出瀏覽器了,react.js 跟 react-dom.js 卻還是在瀏覽器內引入

第一個問題還算簡單,可以改成輸入指定資料夾的指令,就可一次編譯:

./node_modules/.bin/babel src/ -d dist/ --presets es2015,react

但是第二三個問題就棘手得多。

有鑑於此,需要找方法一次解決這三個問題:設法一口氣全部編譯、封裝成單一檔案、並且函式庫統一用NPM在伺服器端安裝與管理吧!

第四步:使用 webpack 來解決這些整合問題

webpack 會利用 babel-loader 套件來呼叫核心的 babel-core,所以先分別安裝它們:

npm install --save-dev babel-core
npm install --save-dev babel-loader
npm install --save-dev webpack

我們不會透過命令列去使用 babel 了,既然用不到就先把它移除吧:

npm uninstall babel-cli

我們也不會用 script 標籤引入 react.js 跟 react-dom.js 檔案了。一口氣全部編譯時會用到,所以用NPM安裝它吧:

npm install --save-dev react
npm install --save-dev react-dom

前面說過 babel 版本 6 以後預設不支援 ES2015 與 JSX,現在不透過命令列設定 presets 的話,就需要建立 .babelrc 檔案去開啟支援:

{
  "presets": ["es2015", "react"]
}

然後為了讓 webpack 在一口氣全部編譯成一個檔案時,能看懂檔案跟套件之間的相依性,因此需要按照 ES2015 的 import/export 語法寫清楚。

例如原本的 App.jsx 就要改寫成這樣:

import React from 'react';
import ChildA from './ChildA.jsx';
import ChildB from './ChildB.jsx';

export default class App extends React.Component {
  render() {
    return (
      

Hello App!

); } }

src 資料夾內的檔案都要進行改寫。

最後建立 webpack.config.js 來設定檔案路徑,並指定使用 babel-loader 來幫忙編譯:

var path = require('path');

module.exports = {
  module: {
    loaders: [{
      loader: 'babel-loader',
    }]
  },
  entry: './src/index.jsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist/')
  }
}

就可以透過命令列執行 webpack了:

./node_modules/.bin/webpack 

這段指令有點長,可以把它放進 package.json 的 scripts 欄位:

  "scripts": {
    "build": "webpack"
  },

之後就可以用下列指令來進行編譯工作了:

npm run build

會得到我們要的結果:一個檔案搞定一切!

Should Render Here

source code

大功告成!終於搞定我們想要的所有效果!

結語

本篇文章提到的內容,根據不同的套件版本,實務上有多種方式去安裝、設定達到同樣效果。

但是核心觀念是共通的!

可以此為基礎,探索更多的現代前端開發方法,學習更多工具的功能與設定!

(完)

(Photo via 8 Kome, CC licensed.)