Crowdfund your idea (在以太坊上,如何寫眾籌合約)

原文: https://www.ethereum.org/crowdsale

翻譯: terryc007

版本: v1.0

Crowdfund your idea

為你的idea眾籌

Sometimes a good idea takes a lot of funds and collective effort. You could ask for donations, but donors prefer to give to projects they are more certain will get traction and proper funding. This is an example where a crowdfunding would be ideal: you set up a goal and a deadline for reaching it. If you miss your goal, the donations are returned, therefore reducing the risk for donors. Since the code is open and auditable, there is no need for a centralized, trusted platform and therefore the only fees everyone will pay are just the gas fees.

有時(shí)要實(shí)現(xiàn)一個(gè)好的想法需要很多資金,集體的努力。你可以尋求捐款,但是捐助者更喜歡給那些,他們認(rèn)為更具吸引力,資金需求恰當(dāng)?shù)捻?xiàng)目捐贈(zèng)。這有個(gè)例子,在眾籌上是完美的: 你設(shè)置眾籌的目標(biāo),到期時(shí)間。如果目標(biāo)沒有完成,捐款會(huì)退回給捐贈(zèng)者,這減低捐贈(zèng)者的風(fēng)險(xiǎn)。因?yàn)榇a是公開的,可審計(jì)的,不需要中心的,可信的平臺(tái),因此每個(gè)捐贈(zèng)者只需要支付gas費(fèi)用。

TOKENS AND DAOS

Tokens 跟 DAOS

In this example we will make a better crowdfunding by solving two important problems: how rewards are managed and kept, and how the money is spent after the funds are raised.

在這個(gè)例子,我們會(huì)進(jìn)行一個(gè)更好的眾籌,它可以解決兩個(gè)重要的問題: 如何管理獎(jiǎng)勵(lì),以及在完成資金募集后,如何使用這些錢?

Rewards in crowdfundings are usually handled by a central unchangeable database that keeps track of all donors: anyone who missed the deadline for the campaign cannot get in anymore and any donor who changed their mind can't get out. Instead we are going to do this the decentralized way and just create a token to keep track of rewards, anyone who contributes gets a token that they can trade, sell or keep for later. When the time comes to give the physical reward the producer only needs to exchange the tokens for real products. Donors get to keep their tokens, even if the project doesn't achieve its goals, as a souvenir.

在眾籌中,通常用一個(gè)中心不可修改的數(shù)據(jù)庫來處理所有捐贈(zèng)者的獎(jiǎng)勵(lì)。任何人只要錯(cuò)過截止時(shí)間,他們就無法再參與,任何捐贈(zèng)者一旦完成捐贈(zèng)就沒法退出。取而代之,我們將用一個(gè)去中心化的方式來處理這個(gè)事情。 通過創(chuàng)建一個(gè)token來跟蹤獎(jiǎng)勵(lì),任何捐贈(zèng)者會(huì)獲得一個(gè)token,隨后,他們可以交易,賣掉或保留它。 當(dāng)?shù)搅税l(fā)送實(shí)物獎(jiǎng)勵(lì)產(chǎn)品時(shí),只能通過token來換取實(shí)物獎(jiǎng)品。即使項(xiàng)目沒有完成目標(biāo),他們也可以保留token,把它當(dāng)做紀(jì)念品。

Also, generally those who are funding can't have any say on how the money is spent after the funds are raised and mismanagement often causes projects never to deliver anything at all. In this project we will use a Democratic Organization that will have to approve any money coming out of the system. This is often called a crowdsale or crowd equity and is so fundamental that in some cases the token can be the reward itself, especially in projects where a group of people gather together to build a common public good.

同樣,通常捐贈(zèng)者對(duì)于募集到的資金,什么也不能說,缺乏管理經(jīng)常使得這些項(xiàng)目永遠(yuǎn)無法交付產(chǎn)品。在這個(gè)項(xiàng)目中,我們將會(huì)使用一個(gè)民主的組織。要從這個(gè)組織里面拿錢,就必須要通過該組織允許。這個(gè)通常被稱為crowdsalecrowd equity。因?yàn)檫@是非常根本的東西,所以在一些情況下,token可以實(shí)現(xiàn)自我獎(jiǎng)勵(lì),特別是在那些聚集很多人一起構(gòu)建一個(gè)公共產(chǎn)品的的項(xiàng)目中。

Get the necessary contracts
  • If you are just testing, switch the wallet to the testnet and start mining.
  • First of all, create a fixed supply token. For this example, we are going to create a supply of 100, use the name gadgets, the box emoji (??) as a symbol and 0decimal places. Deploy it and save the address.
  • Now create a shareholder association. In this example we are going to use the address of the token we just created as the Shares Address, a minimum quorum of 10, and 1500 minutes (25 hours) as the voting time. Deploy this contract and save the address.
  • 如果要測(cè)試,把錢包切換到測(cè)試網(wǎng)絡(luò),在啟動(dòng)挖礦。
  • 首先,創(chuàng)建一個(gè)總量固定的token。在這個(gè)例子中,我們創(chuàng)建100個(gè),名字叫g(shù)adgets, emoji 箱子表情??作為token的符號(hào), 小數(shù)位設(shè)置為0, 部署它,然后保存token的合約地址。
  • 現(xiàn)在創(chuàng)建一個(gè)股東協(xié)會(huì)。在這個(gè)例子中,我們使用token合約的地址作為股份地址,最少參股人10個(gè),投票時(shí)間為1500分鐘(25小時(shí))。 部署這個(gè)合約,并保存這個(gè)合約地址。

THE CODE

Now copy this code and let's create the crowdsale:

代碼

現(xiàn)在復(fù)制這些代碼,然后創(chuàng)建crowdsale:

pragma solidity ^0.4.18;

interface token {
    function transfer(address receiver, uint amount) external;
}

contract Crowdsale {
    address public beneficiary;
    uint public fundingGoal;
    uint public amountRaised;
    uint public deadline;
    uint public price;
    token public tokenReward;
    mapping(address => uint256) public balanceOf;
    bool fundingGoalReached = false;
    bool crowdsaleClosed = false;

    event GoalReached(address recipient, uint totalAmountRaised);
    event FundTransfer(address backer, uint amount, bool isContribution);

    /**
     * Constructor function
     *
     * Setup the owner
     */
    function Crowdsale(
        address ifSuccessfulSendTo,
        uint fundingGoalInEthers,
        uint durationInMinutes,
        uint etherCostOfEachToken,
        address addressOfTokenUsedAsReward
    ) public {
        beneficiary = ifSuccessfulSendTo;
        fundingGoal = fundingGoalInEthers * 1 ether;
        deadline = now + durationInMinutes * 1 minutes;
        price = etherCostOfEachToken * 1 ether;
        tokenReward = token(addressOfTokenUsedAsReward);
    }

    /**
     * Fallback function
     *
     * The function without name is the default function that is called whenever anyone sends funds to a contract
     */
    function () payable public {
        require(!crowdsaleClosed);
        uint amount = msg.value;
        balanceOf[msg.sender] += amount;
        amountRaised += amount;
        tokenReward.transfer(msg.sender, amount / price);
       emit FundTransfer(msg.sender, amount, true);
    }

    modifier afterDeadline() { if (now >= deadline) _; }

    /**
     * Check if goal was reached
     *
     * Checks if the goal or time limit has been reached and ends the campaign
     */
    function checkGoalReached() public afterDeadline {
        if (amountRaised >= fundingGoal){
            fundingGoalReached = true;
            emit GoalReached(beneficiary, amountRaised);
        }
        crowdsaleClosed = true;
    }


    /**
     * Withdraw the funds
     *
     * Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
     * sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
     * the amount they contributed.
     */
    function safeWithdrawal() public afterDeadline {
        if (!fundingGoalReached) {
            uint amount = balanceOf[msg.sender];
            balanceOf[msg.sender] = 0;
            if (amount > 0) {
                if (msg.sender.send(amount)) {
                   emit FundTransfer(msg.sender, amount, false);
                } else {
                    balanceOf[msg.sender] = amount;
                }
            }
        }

        if (fundingGoalReached && beneficiary == msg.sender) {
            if (beneficiary.send(amountRaised)) {
               emit FundTransfer(beneficiary, amountRaised, false);
            } else {
                //If we fail to send the funds to beneficiary, unlock funders balance
                fundingGoalReached = false;
            }
        }
    }
}

CODE HIGHLIGHTS

Notice that in the Crowdsale function (the one that is called upon contract creation), how the variables deadline and fundingGoal are set:

代碼強(qiáng)調(diào)

注意Crowdslae函數(shù)中變量deadlinefundingGoal是如何設(shè)置的。

fundingGoal = fundingGoalInEthers * 1 ether;
deadline = now + durationInMinutes * 1 minutes;
price = etherCostOfEachToken * 1 ether;

Those are some of the special keywords in solidity that help you code, allowing you to evaluate some things like 1 ether == 1000 finney or 2 days == 48 hours. Inside the system all ether amounts are kept track in wei, the smallest divisible unit of ether. The code above converts the funding goal into wei by multiplying it by 1,000,000,000,000,000,000 (which is what the special keyword ether converts into). The next line creates a timestamp that is exactly X minutes away from today by also using a combination of the special keywords now and minutes. For more global keywords, check the solidity documentation on Globally available variables.

在solidity語言中,這是一些特殊關(guān)鍵字,來幫助你計(jì)算一些東西,像1 ETH == 1000 finney 或 2 天 == 48 小時(shí)。在以太坊系統(tǒng),所有的ETH數(shù)量都采用wei來計(jì)量, 它是ETH最小的單位。 上面的代碼把眾籌的目的額度,通過乘以1,000,000,000,000,000,000( 由關(guān)鍵字ether 會(huì)轉(zhuǎn)化而來),轉(zhuǎn)化成以wei來計(jì)量。下一行通用使用特別關(guān)鍵字nowminutes來創(chuàng)建一個(gè)基于當(dāng)前的時(shí)間戳。 要查看全局關(guān)鍵字,請(qǐng)查看solidity文檔中的全局變量。

The following line will instantiate a contract at a given address:

下面這行代碼會(huì)用給定的地址實(shí)例化一個(gè)合約對(duì)象。

tokenReward = token(addressOfTokenUsedAsReward);

Notice that the contract understands what a token is because we defined it earlier by starting the code with:

注意, 合約知道token是什么,因?yàn)樵陂_頭,已經(jīng)定義了它。

interface token { function transfer(address receiver, uint amount){  } }

This doesn't fully describe how the contract works or all the functions it has, but describes only the ones this contract needs: a token is a contract with a transferfunction, and we have one at this address.

這并沒有完整描敘這個(gè)合約是如何工作的,或它是里面所有的函數(shù),但有這個(gè)合約僅需要描述的一件事情: 一個(gè)token是一個(gè)合約,它有一個(gè)transfer 函數(shù), 我們?cè)谶@個(gè)地址上有這么一個(gè)token合約。

HOW TO USE

如何使用

Go to contracts and then deploy contract:

去“合約”頁面,然后部署這合約:

Crowdsale deployment
  • Put the address of the organization you just created in the field if successful, send to.
  • Put 250 ethers as the funding goal
  • If you are just doing it for a test or demonstration, put the crowdsale duration as 3-10 minutes, but if you are really raising funds you can put a larger amount, like 45,000 (31 days).
  • The ether cost of each token should be calculated based on how many tokens you are putting up for sale (a maximum of how many you added as "initial supply" of your token on the previous step). In this example, put 5 ethers.
  • The address of the token you created should be added to the token reward address
  • 輸入你剛創(chuàng)建的組織地址,如果眾籌成功,會(huì)發(fā)ETH發(fā)送到這個(gè)地址。
  • 設(shè)置眾籌目標(biāo)為250個(gè)ETH
  • 如果你只是測(cè)試或演示,把眾籌時(shí)間設(shè)置為3 ~ 10分鐘,但如果你是真正的籌集資金,你可以把值設(shè)的更大一點(diǎn),比如45000(31天)
  • 每個(gè)token兌換多少個(gè)ETH,應(yīng)基于你發(fā)售的token數(shù)量來計(jì)算(最大值是token的總發(fā)行量)。在這個(gè)例子中,設(shè)置為5個(gè)ETH.
  • 你創(chuàng)建的token的地址,它會(huì)被加入到token獎(jiǎng)勵(lì)地址。

Put a gas price, click deploy and wait for your crowdsale to be created. Once the crowdsale page is created, you now need to deposit enough rewards so it can pay the rewards back. Click the address of the crowdsale, then deposit and send 50 gadgets to the crowdsale.

設(shè)置gas價(jià)格,點(diǎn)擊“部署”, 然后等待你的crowdsale合約成功創(chuàng)建。 一旦crowdsale頁面創(chuàng)建成功,你需要在合約里面充值足夠的獎(jiǎng)勵(lì)(你的token),這樣它才有給打ETH的人會(huì)送獎(jiǎng)勵(lì)。 點(diǎn)擊crowdsale的地址,然后往里面沖50個(gè)gadgets用來眾籌。

I have 100 gadgets. Why not sell them all?

This is a very important point. The crowdsale we are building will be completely controlled by the token holders. This creates the danger that someone controlling 50%+1 of all the tokens will be able to send all the funds to themselves. You can try to create special code on the association contract to prevent these hostile takeovers, or you can instead have all the funds sent to a simple address. To simplify we are simply selling off half of all the gadgets: if you want to further decentralize this, split the remaining half between trusted organizations.

我有100個(gè)gadgets,為什么不賣完?

這是一個(gè)非常重要的點(diǎn)。如果這樣做,我們構(gòu)建的眾籌會(huì)會(huì)完全被token的持有者控制。一些人控制了51%的token后,會(huì)有他們把所有的資金發(fā)送給他們自己的風(fēng)險(xiǎn)。你可以在協(xié)會(huì)合約中,創(chuàng)建一些特定的代碼來防止惡意收購,或者你可以把所有的資金發(fā)送到一個(gè)簡(jiǎn)單的地址。為簡(jiǎn)化這個(gè)事情,我們賣掉其中的一半。如果你想進(jìn)一步的去中心化,把剩下的另一半token分發(fā)到一些可信的組織。

RAISE FUNDS

募集資金

Once the crowdsale has all the necessary tokens, contributing to it is easy and you can do it from any ethereum wallet: just send funds to it. You can see the relevant code bit here:

一旦crowdsale合約有所有必要的token,捐助它是非常容易的,你可以從任何以太坊錢包給它發(fā)送資金。下面是相關(guān)的代碼

function () {
    require(!crowdsaleClosed);
    uint amount = msg.value;
    // ...

The unnamed function is the default function executed whenever a contract receives ether. This function will automatically check if the crowdsale is active, calculate how many tokens the caller bought and send the equivalent. If the crowdsale has ended or if the contract is out of tokens the contract will throwmeaning the execution will be stopped and the ether sent will be returned (but all the gas will be spent).

當(dāng)合約收到ETH時(shí),合約會(huì)執(zhí)行默認(rèn)的沒有命名的函數(shù)。這個(gè)函數(shù)會(huì)自動(dòng)檢查crowdsale是否還在進(jìn)行,計(jì)算調(diào)用者購買了多少token,并發(fā)送給他們。如果crowdsale已經(jīng)結(jié)束或如果合約token不夠,合約會(huì)拋出異常。這就意味著停止合約執(zhí)行,并退回ETH(除了被花掉的gas)。

Crowdsale error

This has the advantage that the contract prevents falling into a situation that someone will be left without their ether or tokens. In a previous version of this contract we would also self destruct the contract after the crowdsale ended: this would mean that any transaction sent after that moment would lose their funds. By creating a fallback function that throws when the sale is over, we prevent anyone losing money.

這有做有一個(gè)好處,合約可以防止一些人的丟ETH或Token。 在該合約之前的一個(gè)版本,當(dāng)眾籌結(jié)束后,我們會(huì)自我銷毀這個(gè)合約。這就意味著,當(dāng)眾籌結(jié)束后,任何發(fā)送到合約的資金都會(huì)丟失。通過創(chuàng)建一個(gè)回調(diào)函數(shù),當(dāng)眾籌結(jié)束后,它會(huì)拋出異常,來防止任何人丟失資金。

The contract has a safeWithdrawl() function, without any parameters, that can be executed by the beneficiary to access the amount raised or by the funders to get back their funds in the case of a failed fundraise.

這個(gè)合約有一個(gè)沒有參數(shù)的safeWithdrawl()函數(shù),募集資金的受益者可以執(zhí)行它來獲取這些募集到的資金,或者但眾籌失敗后,投資者執(zhí)行它拿回自己的資金。

Crowdsale execution

Extending the crowdsale

擴(kuò)展Crowdsale合約

WHAT IF THE CROWDSALE OVERSHOOTS ITS TARGET?

如果超募怎么辦?

In our code, only two things can happen: either the crowdsale reaches its target or it doesn't. Since the token amount is limited, it means that once the goal has been reached no one else can contribute. But the history of crowdfunding is full of projects that overshoot their goals in much less time than predicted or that raised many times over the required amount.

在我們的代碼里,只發(fā)生了兩件事: 募集的資金達(dá)到設(shè)定目標(biāo),或者沒有。因?yàn)閠oken的數(shù)量是有限的,這意味著一旦達(dá)到目標(biāo),沒人能再捐款了。但是在眾籌的歷史中,太多超募的項(xiàng)目,在比預(yù)期短很多時(shí)間里達(dá)成目標(biāo),或募集到的資金是所需要的好幾倍。

UNLIMITED CROWDSALE

無限制眾籌

So we are going to modify our project slightly so that instead of sending a limited set of tokens, the project actually creates a new token out of thin air whenever someone sends them ether. First of all, we need to create a Mintable token.

所以我們要對(duì)項(xiàng)目代碼稍做修改下,這樣在任何時(shí)候,只要有人給合約發(fā)送ETH, 這個(gè)項(xiàng)目實(shí)際上會(huì)憑空創(chuàng)建一個(gè)新的token。 首先,我們需要?jiǎng)?chuàng)建一個(gè) Mintable token.

Then modify the crowdsale to rename all mentions of transfer to mintToken:

然后修改這個(gè)眾籌合約,把所有用到transfer的函數(shù)重命名為mintToken:

contract token { function mintToken(address receiver, uint amount){  } }
// ...
    function () {
        // ...
        tokenReward.mintToken(msg.sender, amount / price);
        // ...
    }

Once you published the crowdsale contract, get its address and go into your Token Contract to execute a Change Ownership function. This will allow your crowdsale to call the Mint Token function as much as it wants.

一旦發(fā)布這個(gè)眾籌合約,去你的Token合約頁面,執(zhí)行Change Ownership函數(shù)。這會(huì)允許你的眾籌合約可無限制的調(diào)用Mint Token函數(shù)。

Warning: This opens you to the danger of hostile takeover. At any point during the crowdsale anyone who donates more than the amount already raised will be able to control the whole pie and steal it. There are many strategies to prevent that, but implementing will be left as an exercise to the reader:

警告: 這會(huì)有惡意收購的風(fēng)險(xiǎn)。在眾籌期間,任何人只要捐贈(zèng)的資金超過目前已募集到的資金,它就能夠控制整個(gè)合約,并竊取它。有很多策略來防止這種事情,這留給讀者當(dāng)做練習(xí)來做。

  • Modify the crowdsale such that when a token is bought, also send the same quantity of tokens to the founder's account so that they always control 50% of the project
  • Modify the Organization to create a veto power to some trusted third party that could stop any hostile proposal
  • Modify the token to allow a central trusted party to freeze token accounts, so as to require a verification that there isn't any single entity controlling a majority of them
  • 修改眾籌合約,當(dāng)token被購買時(shí),也發(fā)送同樣數(shù)量的token給創(chuàng)始人賬號(hào),這樣創(chuàng)始人總是保證控制項(xiàng)目的50%token。
  • 修改這個(gè)組織,創(chuàng)建一個(gè)否決權(quán)利,給一些可信的第三方,他們可停止任何惡意收購。
  • 修改Token,允許一個(gè)中心的可信方能夠凍結(jié)token賬號(hào),以便來核實(shí):沒有任何一個(gè)實(shí)體控制大部分token。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容