5.13.4 众筹者
众筹你的想法
有时一个好主意需要大量资金和集体努力。你可以要求捐款,但捐赠者更倾向于给予他们更确定的项目,这些项目将获得牵头和适当的资金。这是一个众所周知的例子,你会建立一个目标和达成目标的最后期限。如果你未完成你的目标,那么捐赠就会被退回,从而减少捐助者的风险。由于代码是开放和可审计的,所以不需要一个集中的可信赖的平台,因此每个人都要支付的费用只是gas费用。
众筹通常会给予奖励。这将需要您获取每个人的联系信息并跟踪谁拥有什么。但是,由于你刚刚创建了自己的令牌,为什么不用它来跟踪奖品?捐赠者可以在捐赠后立即拥有东西。他们可以安全地存储,但是如果他们不想要奖品,他们也可以出售或交易。如果您的想法是实物,那么在项目完成后您必须做的所有事情是将产品发送给发回您的令牌的所有人。如果项目是数字化的,则令牌本身可以立即用于用户参与或获得项目的入口。
代码
这种特殊的人气合约的工作方式是你为你的令牌设定一个汇率,然后捐赠者将立即用他们的ether换取一定比例的令牌。您还将选择资金目标和期限:一旦该期限结束,您可以ping合同,如果达成目标,将把众筹的ether给您,否则可以退给捐助者。即使项目没有达到目标,捐助者也会保留他们的令牌,作为他们帮助你的证明。
contract token { mapping (address => uint) public coinBalanceOf; function token() {} function sendCoin(address receiver, uint amount) returns(bool sufficient) { } }
contract Crowdsale {
address public beneficiary;
uint public fundingGoal; uint public amountRaised; uint public deadline; uint public price;
token public tokenReward;
Funder[] public funders;
event FundTransfer(address backer, uint amount, bool isContribution);
/* data structure to hold information about campaign contributors */
struct Funder {
address addr;
uint amount;
}
/* at initialization, setup the owner */
function Crowdsale(address _beneficiary, uint _fundingGoal, uint _duration, uint _price, token _reward) {
beneficiary = _beneficiary;
fundingGoal = _fundingGoal;
deadline = now + _duration * 1 minutes;
price = _price;
tokenReward = token(_reward);
}
/* The function without name is the default function that is called whenever anyone sends funds to a contract */
function () {
uint amount = msg.value;
funders[funders.length++] = Funder({addr: msg.sender, amount: amount});
amountRaised += amount;
tokenReward.sendCoin(msg.sender, amount / price);
FundTransfer(msg.sender, amount, true);
}
modifier afterDeadline() { if (now >= deadline) _ }
/* checks if the goal or time limit has been reached and ends the campaign */
function checkGoalReached() afterDeadline {
if (amountRaised >= fundingGoal){
beneficiary.send(amountRaised);
FundTransfer(beneficiary, amountRaised, false);
} else {
FundTransfer(0, 11, false);
for (uint i = 0; i < funders.length; ++i) {
funders[i].addr.send(funders[i].amount);
FundTransfer(funders[i].addr, funders[i].amount, false);
}
}
suicide(beneficiary);
}
}
设置参数
在我们进一步之前,让我们从设置人群的参数开始:
var _beneficiary = eth.accounts[1]; // create an account for this
var _fundingGoal = web3.toWei(100, "ether"); // raises 100 ether
var _duration = 30; // number of minutes the campaign will last
var _price = web3.toWei(0.02, "ether"); // the price of the tokens, in ether
var _reward = token.address; // the token contract address.
受益人将收到募集资金的新地址。资金目标是集齐的ether的数量。截止日期是以平均12秒的blocktimes来衡量的,因此默认值约为4周。价格是棘手的:但是只要更改捐赠者每捐一个ether将获得的令牌数量为2。最后奖励应该是您在最后块中创建的令牌合约的地址。
在这个例子中,你是在人群中出售所有令牌数量的一半,以换取100个以太。非常仔细地确定这些参数,因为它们将在下一部分指南中发挥非常重要的作用。
部署
你知道这个练习:如果你使用的是solC编译器,删除换行符并在终端上复制以下命令:
var crowdsaleCompiled = eth.compile.solidity(' contract token { mapping (address => uint) public coinBalanceOf; function token() {} function sendCoin(address receiver, uint amount) returns(bool sufficient) { } } contract Crowdsale { address public beneficiary; uint public fundingGoal; uint public amountRaised; uint public deadline; uint public price; token public tokenReward; Funder[] public funders; event FundTransfer(address backer, uint amount, bool isContribution); /* data structure to hold information about campaign contributors */ struct Funder { address addr; uint amount; } /* at initialization, setup the owner */ function Crowdsale(address _beneficiary, uint _fundingGoal, uint _duration, uint _price, token _reward) { beneficiary = _beneficiary; fundingGoal = _fundingGoal; deadline = now + _duration * 1 minutes; price = _price; tokenReward = token(_reward); } /* The function without name is the default function that is called whenever anyone sends funds to a contract */ function () { Funder f = funders[++funders.length]; f.addr = msg.sender; f.amount = msg.value; amountRaised += f.amount; tokenReward.sendCoin(msg.sender, f.amount/price); FundTransfer(f.addr, f.amount, true); } modifier afterDeadline() { if (now >= deadline) _ } /* checks if the goal or time limit has been reached and ends the campaign */ function checkGoalReached() afterDeadline { if (amountRaised >= fundingGoal){ beneficiary.send(amountRaised); FundTransfer(beneficiary, amountRaised, false); } else { FundTransfer(0, 11, false); for (uint i = 0; i < funders.length; ++i) { funders[i].addr.send(funders[i].amount); FundTransfer(funders[i].addr, funders[i].amount, false); } } suicide(beneficiary); } }');
var crowdsaleContract = web3.eth.contract(crowdsaleCompiled.Crowdsale.info.abiDefinition);
var crowdsale = crowdsaleContract.new(
_beneficiary,
_fundingGoal,
_duration,
_price,
_reward,
{
from:web3.eth.accounts[0],
data:crowdsaleCompiled.Crowdsale.code,
gas: 1000000
}, function(e, contract){
if(!e) {
if(!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
} })
如果您正在使用在线编译器,将合约代码复制到在线solidity编译器,然后获取标记为Geth Deploy的文本框的内容。由于您已经设置了参数,因此您不需要更改任何文本,只需将生成的文本粘贴到您的geth窗口中即可。
等待三十秒钟,您会看到如下消息:
Contract mined! address: 0xdaa24d02bad7e9d6a80106db164bad9399a0423e
如果您收到该警报,那么您的代码应该在线。你可以随时仔细检查一下:
eth.getCode(crowdsale.address)
现在为您创建的合约提供必要的令牌,以便自动将奖励分配给贡献者!
token.sendCoin.sendTransaction(crowdsale.address, 5000,{from: eth.accounts[0]})
打包交易后,您可以通过以下方式检查人群地址拥有的令牌数量以及所有其他变量:
"Current crowdsale must raise " + web3.fromWei(crowdsale.fundingGoal.call(), "ether") + " ether in order to send it to " + crowdsale.beneficiary.call() + "."
设置观察者
您希望在人群收到新的资金时收到警报,因此粘贴此代码:
var event = crowdsale.FundTransfer({}, '', function(error, result){
if (!error)
if (result.args.isContribution) {
console.log("\n New backer! Received " + web3.fromWei(result.args.amount, "ether") + " ether from " + result.args.backer )
console.log( "\n The current funding at " +( 100 * crowdsale.amountRaised.call() / crowdsale.fundingGoal.call()) + "% of its goals. Funders have contributed a total of " + web3.fromWei(crowdsale.amountRaised.call(), "ether") + " ether.");
var timeleft = Math.floor(Date.now() / 1000)-crowdsale.deadline();
if (timeleft>3600) { console.log("Deadline has passed, " + Math.floor(timeleft/3600) + " hours ago")
} else if (timeleft>0) { console.log("Deadline has passed, " + Math.floor(timeleft/60) + " minutes ago")
} else if (timeleft>-3600) { console.log(Math.floor(-1*timeleft/60) + " minutes until deadline")
} else { console.log(Math.floor(-1*timeleft/3600) + " hours until deadline")
}
} else {
console.log("Funds transferred from crowdsale account: " + web3.fromWei(result.args.amount, "ether") + " ether to " + result.args.backer )
}
});
注册合同
你现在已经设置好了 任何人现在可以通过简单地发送ether到crowdsale地址来贡献,但为了使其更简单,让我们注册您的销售名称。首先,为你的crowdsale选一个名字:
var name = "mycrowdsale"
检查是否可用并注册:
registrar.addr(name)
registrar.reserve.sendTransaction(name, {from: eth.accounts[0]});
等待先前的交易被打包,然后:
registrar.setAddress.sendTransaction(name, crowdsale.address, true,{from: eth.accounts[0]});
为crowdsale做贡献
为crowdsale做贡献非常简单,甚至不需要实例化合同。这是因为crowdsale响应简单的crowdsale存款,所以向crowdsale发送ether的任何人都将自动获得奖励。任何人都可以通过简单的执行这个命令来做出贡献:
var amount = web3.toWei(5, "ether") // decide how much to contribute
eth.sendTransaction({from: eth.accounts[0], to: crowdsale.address, value: amount, gas: 1000000})
或者,如果你想让别人发送它,他们甚至可以使用名称注册商来做贡献:
eth.sendTransaction({from: eth.accounts[0], to: registrar.addr("mycrowdsale"), value: amount, gas: 500000})
现在等待一分钟,您可以通过执行以下命令来检查合同是否收到ether:
web3.fromWei(crowdsale.amountRaised.call(), "ether") + " ether"
token.coinBalanceOf.call(eth.accounts[0]) + " tokens"
token.coinBalanceOf.call(crowdsale.address) + " tokens"
收回资金
一旦到了截止日期,有人必须唤起合约,将资金发送给受益人或回到资助者(如果失败)。发生这种情况是因为ethereum没有一个有效的循环或计时器等等,所以任何未来的交易必须由某人ping。
crowdsale.checkGoalReached.sendTransaction({from:eth.accounts[0], gas: 2000000})
您可以使用以下代码来检查您的帐户:
web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") + " ether"
web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") + " ether"
token.coinBalanceOf.call(eth.accounts[0]) + " tokens"
token.coinBalanceOf.call(eth.accounts[1]) + " tokens"
一旦完成了工作,crowdsale实例被设置为自我毁灭,所以如果截止日期结束,每个人都获得奖品。合约不再存在,你运行下面代码来验证:
eth.getCode(crowdsale.address)
所以你获得了100个ether,并成功地将你的原始硬币分配给了众多的捐助者。接下来你能做些什么呢?