目錄
目錄
1、什麼是ICO?
ICO是以初始產生的數字加密貨幣作為投資回報的一種籌措資金的方式,它的概念源自證券界的Initial Public Offering(IPO,首次公開發行)。
相較於傳統意義上的IPO,ICO具有可以縮短投融資鏈、降低投融資門檻、流動性佳、全球性投資等優勢。常見的ICO裡,數字貨幣和區塊鏈專案向早期愛好者出售專案代幣。專案團隊通過ICO獲取技術開發和市場擴充資金;而專案愛好者通過ICO支援專案,同時也可在對應代幣進入交易市場後選擇交易退出。
當你有一個好的想法,需要大家的資金來資助你。你可以使用眾籌合約來發起請求捐款。眾籌合約的基本思路是,你設定一個眾籌目標,在達到目標的最後期限時,如果沒有完成眾籌,所有的捐款將被退回,因此減少了捐贈者的風險。由於程式碼是開放的,可被審計的,也就不需要一個集中的、可信的平臺來擔保,每個捐款的人,只需要支付一定的gas
。
2、眾籌的獎勵-代幣
一般來說,那些籌集資金的人在資金籌集和資金管理不善之後,根本就不能說這筆錢是如何使用的,這常常導致專案根本無法交付任何東西。這時我們可以使用智慧合適中投票的方式來做決定,這樣對所有人都是公平的。(這個例子不在本文中介紹,可以參考連結)
在下面的例子裡,我們在眾籌中,主要解決兩個重要的問題:如何管理和儲存用於獎勵的代幣;籌集獎金後如何使用。
傳統的眾籌或獎勵記錄通常有一箇中央資料庫,來儲存、跟蹤所有捐助者的過程:誰錯過了眾籌的最後期限了,誰在眾籌過程中捐贈了多少等。與之相反,在區塊鏈中我們將以分散的方式來做這件事,只需建立一個標記來記錄眾籌的每一條記錄、獎勵了多少代幣,後面每個捐贈者都可以得到一個他們可以交易、出售或保留的代幣。如果要給予實物獎勵,生產者只需要交換實物產品的代幣。捐贈者也可以將代幣做為紀念品保留,不管這個眾籌專案有沒有達到它的目標,都可以收藏。
3、眾籌合約的完善
3.1、設定眾籌合約中使用的代幣
下面是一段簡單的 代幣程式碼
,用於發行給捐贈者,注意我們沒有設定代幣的總量,而是一直在增發,在實際使用過程中,可以根據需求自行做限制:
pragma solidity 0.4.16;
/**
* 一個簡單的代幣合約。
*/
contract token {
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 2; //代幣單位,展示的小數點後面多少個0,和以太幣一樣後面是是18個0
uint256 public totalSupply; //代幣總量
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value); //轉帳通知事件
/* 初始化合約,並且把初始的所有代幣都給這合約的建立者
* @param _owned 合約的管理者
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
*/
function token(address _owned, string tokenName, string tokenSymbol) {
//合約的管理者獲得的代幣總量
balanceOf[_owned] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
/**
* 轉帳,具體可以根據自己的需求來實現
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數量
*/
function transfer(address _to, uint256 _value){
//從傳送者減掉髮送額
balanceOf[msg.sender] -= _value;
//給接收者加上相同的量
balanceOf[_to] += _value;
//通知任何監聽該交易的客戶端
Transfer(msg.sender, _to, _value);
}
/**
* 增加代幣,並將代幣傳送給捐贈新使用者
* @param _to address 接受代幣的地址
* @param _amount uint256 接受代幣的數量
*/
function issue(address _to, uint256 _amount) public{
totalSupply = totalSupply + _amount;
balanceOf[_to] += _amount;
//通知任何監聽該交易的客戶端
Transfer(this, _to, _amount);
}
}
上面的程式碼,在Mist
中是執行的效果如下:
3.2、眾籌合約的基本設定
在眾籌合約中,下面幾個變數可以用於設定眾籌以太幣總量、眾籌截止時間、以太幣和代幣的兌換比例,如果不使用單位進行宣告換算,預設在以太坊中,所有的單位都是wei
,1 ether=10^18 wei
:
fundingGoal = fundingGoalInEthers * 1 ether; //眾籌以太幣總量
deadline = now + durationInMinutes * 1 minutes; //眾籌截止時間,單位是分鐘
price = 500 finney; //1個以太幣可以買 2 個代幣
在初始化眾籌合約建構函式的時候,我們會將眾籌合約的帳戶地址
,傳遞給代幣做為管理地址,這裡使用的是關鍵字this
表示當前合約的地址,也可以傳遞給某個人,初始建立時獎勵給這個人指定量的代幣。
function Crowdsale(
uint fundingGoalInEthers,
uint durationInMinutes,
string tokenName,
string tokenSymbol
) token(this, tokenName, tokenSymbol)
3.3、讓眾籌合約接收以太幣
在 solidity
中,有一個未命名函式當合約收到以太幣的時候會預設執行。我們加上 payable
關鍵字,就可以給這個合約打款(存入以太幣),然後我們可以根據之前的以太幣與代幣的兌換比例,獎勵給某人一定量的代幣,並進行統計。
/**
* 預設函式
*
* 預設函式,可以向合約直接打款
*/
function () payable {
//判斷是否關閉眾籌
require(!crowdsaleClosed);
uint amount = msg.value;
//捐款人的金額累加
balance[msg.sender] += amount;
//捐款總額累加
amountRaised += amount;
//轉帳操作,轉多少代幣給捐款人
issue(msg.sender, amount / price * 10 ** uint256(decimals));
FundTransfer(msg.sender, amount, true);
}
上面的程式碼中如果眾籌合約關閉(crowdsaleClosed = true
),則不繼續執行,無法交易,這樣做的原因是,如果眾籌合約無論是成功或是已經結束,避免其他人賠錢打款,造成後期的不必要糾紛。
3.4、檢測眾籌合約是否完成
檢測眾籌合約是否達到的程式碼如下,如果已籌集的資金額,達到了眾籌的目標值,則結束眾籌,並通知客戶端做記錄
/**
* 檢測眾籌目標是否已經達到
*/
function checkGoalReached() afterDeadline {
if (amountRaised >= fundingGoal){
//達成眾籌目標
fundingGoalReached = true;
GoalReached(beneficiary, amountRaised);
}
//關閉眾籌
crowdsaleClosed = true;
}
3.5、眾籌結束後的操作
同時我們還提供了另一個方法,當眾籌失敗的時候,捐贈者可以取回自己的捐助,如果眾籌成功,受益人可以獲得眾籌到的以太幣。
/**
* 收回資金
*
* 檢查是否達到了目標或時間限制,如果有,並且達到了資金目標,
* 將全部金額傳送給受益人。如果沒有達到目標,每個貢獻者都可以退出
* 他們貢獻的金額
*/
function safeWithdrawal() afterDeadline {
//如果沒有達成眾籌目標
if (!fundingGoalReached) {
//獲取合約呼叫者已捐款餘額
uint amount = balance[msg.sender];
if (amount > 0) {
//返回合約發起者所有餘額
msg.sender.transfer(amount);
FundTransfer(msg.sender, amount, false);
balance[msg.sender] = 0;
}
}
//如果達成眾籌目標,並且合約呼叫者是受益人
if (fundingGoalReached && beneficiary == msg.sender) {
//將所有捐款從合約中給受益人
beneficiary.transfer(amountRaised);
FundTransfer(beneficiary, amount, false);
}
}
4、如何使用眾籌合約
示例中所使用的眾籌合約程式碼:
pragma solidity 0.4.16;
/**
* 一個簡單的代幣合約。
*/
contract token {
string public standard = 'https://mshk.top';
string public name; //代幣名稱
string public symbol; //代幣符號比如'$'
uint8 public decimals = 2; //代幣單位,展示的小數點後面多少個0,和以太幣一樣後面是是18個0
uint256 public totalSupply; //代幣總量
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value); //轉帳通知事件
/* 初始化合約,並且把初始的所有代幣都給這合約的建立者
* @param _owned 合約的管理者
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
*/
function token(address _owned, string tokenName, string tokenSymbol) {
//合約的管理者獲得的代幣總量
balanceOf[_owned] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
/**
* 轉帳,具體可以根據自己的需求來實現
* @param _to address 接受代幣的地址
* @param _value uint256 接受代幣的數量
*/
function transfer(address _to, uint256 _value){
//從傳送者減掉髮送額
balanceOf[msg.sender] -= _value;
//給接收者加上相同的量
balanceOf[_to] += _value;
//通知任何監聽該交易的客戶端
Transfer(msg.sender, _to, _value);
}
/**
* 增加代幣,並將代幣傳送給捐贈新使用者
* @param _to address 接受代幣的地址
* @param _amount uint256 接受代幣的數量
*/
function issue(address _to, uint256 _amount) public{
totalSupply = totalSupply + _amount;
balanceOf[_to] += _amount;
//通知任何監聽該交易的客戶端
Transfer(this, _to, _amount);
}
}
/**
* 眾籌合約
*/
contract Crowdsale is token {
address public beneficiary = msg.sender; //受益人地址,測試時為合約建立者
uint public fundingGoal; //眾籌目標,單位是ether
uint public amountRaised; //已籌集金額數量, 單位是wei
uint public deadline; //截止時間
uint public price; //代幣價格
bool public fundingGoalReached = false; //達成眾籌目標
bool public crowdsaleClosed = false; //眾籌關閉
mapping(address => uint256) public balance; //儲存眾籌地址
//記錄已接收的ether通知
event GoalReached(address _beneficiary, uint _amountRaised);
//轉帳通知
event FundTransfer(address _backer, uint _amount, bool _isContribution);
/**
* 初始化建構函式
*
* @param fundingGoalInEthers 眾籌以太幣總量
* @param durationInMinutes 眾籌截止,單位是分鐘
* @param tokenName 代幣名稱
* @param tokenSymbol 代幣符號
*/
function Crowdsale(
uint fundingGoalInEthers,
uint durationInMinutes,
string tokenName,
string tokenSymbol
) token(this, tokenName, tokenSymbol){
fundingGoal = fundingGoalInEthers * 1 ether;
deadline = now + durationInMinutes * 1 minutes;
price = 500 finney; //1個以太幣可以買 2 個代幣
}
/**
* 預設函式
*
* 預設函式,可以向合約直接打款
*/
function () payable {
//判斷是否關閉眾籌
require(!crowdsaleClosed);
uint amount = msg.value;
//捐款人的金額累加
balance[msg.sender] += amount;
//捐款總額累加
amountRaised += amount;
//轉帳操作,轉多少代幣給捐款人
issue(msg.sender, amount / price * 10 ** uint256(decimals));
FundTransfer(msg.sender, amount, true);
}
/**
* 判斷是否已經過了眾籌截止限期
*/
modifier afterDeadline() { if (now >= deadline) _; }
/**
* 檢測眾籌目標是否已經達到
*/
function checkGoalReached() afterDeadline {
if (amountRaised >= fundingGoal){
//達成眾籌目標
fundingGoalReached = true;
GoalReached(beneficiary, amountRaised);
}
//關閉眾籌
crowdsaleClosed = true;
}
/**
* 收回資金
*
* 檢查是否達到了目標或時間限制,如果有,並且達到了資金目標,
* 將全部金額傳送給受益人。如果沒有達到目標,每個貢獻者都可以退出
* 他們貢獻的金額
*/
function safeWithdrawal() afterDeadline {
//如果沒有達成眾籌目標
if (!fundingGoalReached) {
//獲取合約呼叫者已捐款餘額
uint amount = balance[msg.sender];
if (amount > 0) {
//返回合約發起者所有餘額
msg.sender.transfer(amount);
FundTransfer(msg.sender, amount, false);
balance[msg.sender] = 0;
}
}
//如果達成眾籌目標,並且合約呼叫者是受益人
if (fundingGoalReached && beneficiary == msg.sender) {
//將所有捐款從合約中給受益人
beneficiary.transfer(amountRaised);
FundTransfer(beneficiary, amount, false);
}
}
}
使用之前的教程《Go-Ethereum 1.7.2 結合 Mist 0.9.2 實現代幣智慧合約的例項》,建立四個帳號,分別是為 主帳號
、 張三
、 李四
、 王五
,然後使用已經通過 挖礦
得到以太幣的 主帳號
分別給 張三
、 李四
、 王五
每人 100以太幣
.
開啟 Mist
,連線上 geth
以後,點選 合約
-> 部署合約
,選擇 張三
來建立這個合約,將程式碼貼入,然後右側選擇 Crowdsale
,Funding goal in ethers
眾籌以太幣的總數我們設定為 100
,Duration in minutes
眾籌截止時間,我們設定為 20
分鐘,Token name
代幣的名稱設定為 陌上花開
,Token Symbol
代幣符號設定為 $
。
合約建立成功以後,點選右上方的 合約
,然後點選 陌上花開
眾籌合約,進行詳情頁,點選右側的 存入以太幣
,我們分別使用 李四
購買 70 ether
的代幣、王五
購買 50 ether
的代幣.
購買成功以後,可以看到,在 錢包
頁面 李四
、王五
和帳戶上都多了兩個圖示,說明他們購買的代幣已經到帳,並且相關的餘額也發生了變化。而 張三
的比特幣是 99,98 ether
,因為只要執行合約就需要花費一些 gas
。
之前我們設定的代幣小數後面 2個0
,而1個以太幣可以購買2個代幣,看下圖 李四
、王五
兩個人購買的數量上是對的。
在 眾籌合約截止時間以後
,我們呼叫 afterDeadline
方法,來判斷眾籌目標是否達成。如果嚴謹一些應該只允許管理員來操作這個方法,因為是測試,寫的隨意一些,任何人都可以呼叫。呼叫成功後,在眾籌合約的詳情頁,Funding goal reached
和 Crowdsale closed
的值都為Yes
。
如果眾籌失敗或者是在眾籌過程中,捐贈人反悔了,想要收回捐贈的以太幣,可以呼叫 safeWithdrawal
方法,取回之前捐贈的以太幣。如果眾籌成功後,是無法取回的。
張三
的比特幣是99,98 ether
,在眾籌結束後。呼叫 safeWithdrawal
方法,會將眾籌合約中的所有比特幣,轉到 張三
的帳戶地址下。
一個簡單的眾籌合約做完了。通過這個例子,可以發散思維做其他很多智慧合約,比如實名/匿名投票、淘寶交易合約等。
5、擴充套件閱讀
博文作者:迦壹
部落格地址:Go-Ethereum 1.7.2 結合 Mist 0.9.2 實現眾籌合約的例項
轉載宣告:可以轉載, 但必須以超連結形式標明文章原始出處和作者資訊及版權宣告,謝謝合作!