Skip to content

Instantly share code, notes, and snippets.

@learner-long-life
Created May 23, 2017 18:02
Show Gist options
  • Select an option

  • Save learner-long-life/a4b178b2f151e590fa9a8baff86a399e to your computer and use it in GitHub Desktop.

Select an option

Save learner-long-life/a4b178b2f151e590fa9a8baff86a399e to your computer and use it in GitHub Desktop.
How to Participate in a Crowdfunding Contract

How To Create and Participate in a Crowdfunding Contract

Background

We are going to test paying into an open source crowdfunding contract which has been uploaded here:

https://rinkeby.etherscan.io/address/0xd42a26792b8ae963622186b84f2bcdddd02c2d31

The contract source code and all related utilities can be found here: https://github.com/invisible-college/democracy/blob/master/ZcashEscrow.solc

The contract was uploaded on 2017-05-21 23:56:21 EDT

INFO [05-21|23:56:21] Submitted contract creation              fullhash=0x65daa9c4b2e2ebde13fff85a30c89947e397a8b53d7d9ebe74ec185938f43380 contract=0xd42a26792b8ae963622186b84f2bcdddd02c2d31
I

and initialized on 2017-05-23 11:45:04

INFO [05-23|11:45:04] Submitted transaction                    fullhash=0x4fa35b8c96982620deed958703d1221955920ac0d48433ec4147dff2e931b4cc recipient=0xd42a26792b8ae963622186b84f2bcdddd02c2d31
INFO [05-23|11:45:05] Imported new chain segment               blocks=1    txs=0   mgas=0.000   elapsed=2.106ms   mgasps=0.000   number=236254 hash=6eb319…6f82f8

with the following parameters:

instance.initialize(web3.toWei(100, "ether"), [eth.accounts[1]], 1495684800, {from: eth.coinbase, gas: 250000}, function(err, data, something) { console.log("err: " + err); console.log("data: " + data); console.log("something: " + something); })

That is, we want to raise 100 ETH by May 25th 0:00 EDT (UNIX epoch time 1495684800). If the goal has been reached by that time, it will go through to a beneficiary (a test account that I own) at 0x83fa180e1557b6598b15d2de154bbef657f8fafe.

The contract arbiter gets 1% of the raised funds rounded to the nearest wei, only if the contract goes through. If you run this contract on your own (you become the arbiter), you can modify the source code to give yourself a different percentage if you choose.

This is a preamble to running a real crowdfund on Ethereum mainnet for a distributed seed funding for Lean Startup founders. https://www.gofundme.com/leanstartupfundingexperiments

How To Pay In

That's great, but how do you participate in historical greatness?

First, you'll need an Ethereum node that you can connect to. For our tutorial, we'll assume you first followed the instructions here:

https://gist.github.com/cryptogoth/10a98e8078cfd69f7ca892ddbdcf26bc

That is, you're running a Rinkeby testnet node on localhost:8545, you have about 3 Rinkeby ETH in an account that you know the password to, you started geth with the personal module loaded, and you attached a geth console to it.

In your geth console,

> geth attach
Welcome to the Geth JavaScript console!

instance: Geth/v1.6.1-stable-021c3c28/darwin-amd64/go1.8.1
coinbase: 0xb2e9fe08ca9a0323103883fe12c9609ed380f475
at block: 227656 (Sun, 21 May 2017 23:55:27 EDT)
 datadir: /Users/ppham/.rinkeby
 modules: admin:1.0 clique:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

Load the ABI, which you can also get from the Github repo above:

abi = [{"constant":true,"inputs":[],"name":"isBeforeDeadline","outputs":[{"name":"retVal","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"safeKill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"payoutsETH","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributionsETH","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"deadlineEpoch","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"payoutETH","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"description","type":"bytes32"}],"name":"bump","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"amountRaisedBTC","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getNow","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"description","type":"bytes32"}],"name":"payIn","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"backer_history","outputs":[{"name":"ethAddr","type":"address"},{"name":"amount","type":"uint256"},{"name":"timestamp","type":"uint256"},{"name":"description","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"amountRaisedETH","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"creationTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getContribution","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"feeWithdrawn","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"open","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"safeWithdrawal","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_fundingGoalInWei","type":"uint256"},{"name":"_beneficiary","type":"address"},{"name":"_deadlineEpoch","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"beneficiary","type":"address"},{"indexed":false,"name":"amountRaised","type":"uint256"}],"name":"GoalReached","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"beneficiary","type":"address"},{"indexed":false,"name":"amountRaised","type":"uint256"}],"name":"PayOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ReturnContribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"BuyIn","type":"event"}]
[{
    constant: true,
    inputs: [],
    name: "isBeforeDeadline",
    outputs: [{
        name: "retVal",
        type: "bool"
    }],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [],
    name: "safeKill",
    outputs: [],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "",
        type: "address"
    }],
    name: "payoutsETH",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "beneficiary",
    outputs: [{
        name: "",
        type: "address"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "",
        type: "address"
    }],
    name: "contributionsETH",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "deadlineEpoch",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "payoutETH",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "fundingGoal",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "amountRaised",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "amount",
        type: "uint256"
    }, {
        name: "description",
        type: "bytes32"
    }],
    name: "bump",
    outputs: [],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "owner",
    outputs: [{
        name: "",
        type: "address"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "amountRaisedBTC",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getNow",
    outputs: [{
        name: "retVal",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "description",
        type: "bytes32"
    }],
    name: "payIn",
    outputs: [],
    payable: true,
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "",
        type: "uint256"
    }],
    name: "backer_history",
    outputs: [{
        name: "ethAddr",
        type: "address"
    }, {
        name: "amount",
        type: "uint256"
    }, {
        name: "timestamp",
        type: "uint256"
    }, {
        name: "description",
        type: "bytes32"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "amountRaisedETH",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "creationTime",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "fee",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "getContribution",
    outputs: [{
        name: "retVal",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "feeWithdrawn",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    payable: false,
    type: "function"
}, {
    constant: true,
    inputs: [],
    name: "open",
    outputs: [{
        name: "",
        type: "bool"
    }],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [],
    name: "safeWithdrawal",
    outputs: [],
    payable: false,
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "_fundingGoalInWei",
        type: "uint256"
    }, {
        name: "_beneficiary",
        type: "address"
    }, {
        name: "_deadlineEpoch",
        type: "uint256"
    }],
    name: "initialize",
    outputs: [],
    payable: false,
    type: "function"
}, {
    inputs: [],
    payable: false,
    type: "constructor"
}, {
    payable: true,
    type: "fallback"
}, {
    anonymous: false,
    inputs: [{
        indexed: false,
        name: "beneficiary",
        type: "address"
    }, {
        indexed: false,
        name: "amountRaised",
        type: "uint256"
    }],
    name: "GoalReached",
    type: "event"
}, {
    anonymous: false,
    inputs: [{
        indexed: false,
        name: "beneficiary",
        type: "address"
    }, {
        indexed: false,
        name: "amountRaised",
        type: "uint256"
    }],
    name: "PayOut",
    type: "event"
}, {
    anonymous: false,
    inputs: [{
        indexed: false,
        name: "backer",
        type: "address"
    }, {
        indexed: false,
        name: "amount",
        type: "uint256"
    }],
    name: "ReturnContribution",
    type: "event"
}, {
    anonymous: false,
    inputs: [{
        indexed: false,
        name: "backer",
        type: "address"
    }, {
        indexed: false,
        name: "amount",
        type: "uint256"
    }],
    name: "BuyIn",
    type: "event"
}]

Use the ABI to get a handle on the contract at the specified address

> instance = web3.eth.contract(abi).at("0xd42a26792b8ae963622186b84f2bcdddd02c2d31")
{
  abi: [{
      constant: true,
      inputs: [],
      name: "isBeforeDeadline",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [],
      name: "safeKill",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [{...}],
      name: "payoutsETH",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "beneficiary",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [{...}],
      name: "contributionsETH",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "deadlineEpoch",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "payoutETH",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "fundingGoal",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "amountRaised",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}, {...}],
      name: "bump",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "owner",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "amountRaisedBTC",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "getNow",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}],
      name: "payIn",
      outputs: [],
      payable: true,
      type: "function"
  }, {
      constant: true,
      inputs: [{...}],
      name: "backer_history",
      outputs: [{...}, {...}, {...}, {...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "amountRaisedETH",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "creationTime",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "fee",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "getContribution",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "feeWithdrawn",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: true,
      inputs: [],
      name: "open",
      outputs: [{...}],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [],
      name: "safeWithdrawal",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      constant: false,
      inputs: [{...}, {...}, {...}],
      name: "initialize",
      outputs: [],
      payable: false,
      type: "function"
  }, {
      inputs: [],
      payable: false,
      type: "constructor"
  }, {
      payable: true,
      type: "fallback"
  }, {
      anonymous: false,
      inputs: [{...}, {...}],
      name: "GoalReached",
      type: "event"
  }, {
      anonymous: false,
      inputs: [{...}, {...}],
      name: "PayOut",
      type: "event"
  }, {
      anonymous: false,
      inputs: [{...}, {...}],
      name: "ReturnContribution",
      type: "event"
  }, {
      anonymous: false,
      inputs: [{...}, {...}],
      name: "BuyIn",
      type: "event"
  }],
  address: "0xd42a26792b8ae963622186b84f2bcdddd02c2d31",
  transactionHash: null,
  BuyIn: function(),
  GoalReached: function(),
  PayOut: function(),
  ReturnContribution: function(),
  allEvents: function(),
  amountRaised: function(),
  amountRaisedBTC: function(),
  amountRaisedETH: function(),
  backer_history: function(),
  beneficiary: function(),
  bump: function(),
  contributionsETH: function(),
  creationTime: function(),
  deadlineEpoch: function(),
  fee: function(),
  feeWithdrawn: function(),
  fundingGoal: function(),
  getContribution: function(),
  getNow: function(),
  initialize: function(),
  isBeforeDeadline: function(),
  open: function(),
  owner: function(),
  payIn: function(),
  payoutETH: function(),
  payoutsETH: function(),
  safeKill: function(),
  safeWithdrawal: function()
}

Unlock your account (temporarily) so that you can spend from it.

> personal.unlockAccount(eth.accounts[0])
Unlock account 0xb2e9fe08ca9a0323103883fe12c9609ed380f475
Passphrase:
true

Finally, pay into the crowdfund! You can substitute your own cool moniker / hacker pseudonym for "Ethereum Backer 1". When we make a nice web interface for the crowdfund, these monikers will be displayed, possibly in descending order of donation or contribution.

> instance.payIn("Ethereum Backer 1", {from: eth.accounts[0], to: instance.address, value: web3.toWei(1, "ether"), gas: 1816077})
"0x3376fe6d4560dbf262f0266696fd1adb850afb46ec4eb8550c1e833faf92220a"

Now verify the contract page, and hit refresh impatiently for 15 seconds until your transaction gets picked up.

https://rinkeby.etherscan.io/address/0xd42a26792b8ae963622186b84f2bcdddd02c2d31

You've done it! Thanks for your contribution of play money, and also testing an open source project that will help other people crowdfund as well.

What Happens Next?

If the crowdfund succeeds on May 25th 0:00 EDT, you'll see the payment go through to the beneficiary on the contract page. I'll send a tweet about it, and we'll all celebrate. I'll write another testnet contract that will let you retrieve back your testnet donation if you want it. Otherwise, I'll donate it to some plucky orphans who want to learn Ethereum programming to bootstrap themselves out of poverty.

If the crowdfund doesn't succeed by that time, we'll be a little sad, but hey, it's a testnet contract. Move on to the next step.

How to Withdraw

This will only work after the crowdfunding deadline. As with Kickstarter-style crowdfunding, you can't go withdrawing your contribution willy-nilly. First, unlock your personal account again, since it relocks every few seconds.

> personal.unlockAccount(eth.accounts[0])
Unlock account 0xb2e9fe08ca9a0323103883fe12c9609ed380f475
Passphrase:
true
> instance.safeWithdrawal({from: backerAddress, gas: 180000})

The gas amounts are estimates. Other amounts may work as well. If the gas amount is missing, the transaction will appear to go through, but no amounts will actually transfer.

That's it! Have fun.

You can tweet any questions to @InvisibleLearn or find me (cryptogoth) on Github.

@aiThanet
Copy link

aiThanet commented May 1, 2018

Could you explain about the secure way to participate in mainnet with my unlocked wallet on my mainnet node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment