I’ve played a little bit with a crowdfunding campaign corresponding to the example provided in the
ErgoScript White Paper (page 6) and found that it could be
done even with current wallet API. This post is about how to do crowdfunding on top of Ergo. The post is also proposing a concrete campaign to fund post-EIP1 crowdfunding script development.
If you want to get into details, please read next section, “The Script”. Otherwise, just read “How To Donate” and “Crowdfunding Project Proposal” sections below.
The Script
The simplest crowdfunding script, according to the
ErgoScript White Paper (page 6), is “a script for the following crowdfunding situation: a project backer (with key backerPubKey) wishes to give money to a project (with key projectPubKey), but only if the project raises enough money (at least minToRaise) from other sources by a deadline (expressed in terms of HEIGHT). To give money to the project, the backer will create an output box protected by the following script. The script contains two conditions: one for the case the deadline has passed (enabling the backer to get the money back) and one for the case it succeeded (enabling the project to spend the money if the amount is at least minToRaise before the deadline). In order to ensure enough money has been raised,
the script will search the output collection for a box with a sufficient value going to the projectPubKey. To check where the value of the output box is going, the script will read the script protecting the output box and compare it to the script corresponding to proveDlog(projectPubKey) ; this script can be obtained byprojectPubKey.propBytes.”
As currently API does not support embedding of custom environment variables (only predefined like HEIGHT), the only way to compile the script is to replace such variables in the script from the white-paper with concrete values. For example, consider that a crowdfunding campaign is successful if it is raising 500 Ergs before block number 50,000. For backerPubKey and projectPubKey we can use PK() function which accepts only P2PK serialized keys at the moment. Then the modified script from the WhitePaper becomes the following:
{
val backerPubKey = PK("9h7DHKSDgE4uvP8313GVGdsEg3AvdAWSSTG7XZsLwBfeth4aePG")
val projectPubKey = PK("9gBSqNT9LH9WjvWbyqEvFirMbYp4nfGHnoWdceKGu45AKiya3Fq")
val deadline = 50000
val minToRaise = 500L * 1000000000
val fundraisingFailure = HEIGHT >= deadline && backerPubKey
val enoughRaised = {(outBox: Box) =>outBox.value >= minToRaise
&& outBox.propositionBytes == projectPubKey.propBytes
}
val fundraisingSuccess = HEIGHT < deadline && projectPubKey && OUTPUTS.exists(enoughRaised)
fundraisingFailure || fundraisingSuccess
}
How To Donate
First of all, JSON is not supporting multi-line strings, so you need to replace line breaks with \n.
Also, quotes are to be escaped, so use " instead of ". The resulting JSON will be sent to /script/p2sAddress.
To donate to a project, first get your address from /wallet/addresses, take e.g. first of them. Put the address
into the backerPubKey, so a request to /script/p2sAddress will be like following after this step:
{"source": "{ val backerPubKey = PK(\"9...\") \n val projectPubKey = PK(\"9gBSqNT9LH9WjvWbyqEvFirMbYp4nfGHnoWdceKGu45AKiya3Fq\") \n val deadline = 50000 \n val minToRaise = 500L * 1000000000 \n val fundraisingFailure = HEIGHT >= deadline && backerPubKey \n val enoughRaised = {(outBox: Box) => outBox.value >= minToRaise && outBox.propositionBytes == projectPubKey.propBytes} \n val fundraisingSuccess = HEIGHT < deadline && projectPubKey && OUTPUTS.exists(enoughRaised) \n fundraisingFailure || fundraisingSuccess }"}
with your address instead of “9…”.
Send the string to /script/p2sAddress to get some a response like:
{
"address": "GB3kh2izpWKvyZfMboQwsEscjPaZcz9WrzGqZB4ZrkzRreiFMV6HZYWXGMK3rqCjDCoPgWGNzfnYSUhivW4a1VRYPE7uZXwKnBcqWcRkiuTx6QW55EcPcWeELUsumwdtKoFtWY583nWnKZff"
}
Copy address string (GB3… in our example) and send money to it via /wallet/payment/send , a request to the API method to send 10 Ergs (10 Billion nanoErgs) will be like following:
[
{
"address": "GB3kh2izpWKvyZfMboQwsEscjPaZcz9WrzGqZB4ZrkzRreiFMV6HZYWXGMK3rqCjDCoPgWGNzfnYSUhivW4a1VRYPE7uZXwKnBcqWcRkiuTx6QW55EcPcWeELUsumwdtKoFtWY583nWnKZff",
"value": 10000000000
}
]
That’s all!
Now wallet will automatically find the box on the blockchain, as it contains public key which belongs to the wallet in the refund condition. The wallet then periodically checks whether the box is spendable by constructing a simplest transaction with the box as an input and just one output (to the same address). After refund height (50,000 in our example) the wallet will be able to spend the box and so box value will be added to /wallet/balances.
Please note that this will be not the case after EIP-1 implementation as wallet will use narrow recognition patterns by then.
How To Collect Donations
Wallet which is associated with the project public key will find incoming boxes on the blockchain. However, it will failto make sure that boxes are spendable, as wallet currently is using a simplest transaction for that, and script is failing for such spending transaction.
Before /wallet/boxes/uncertain method being implemented, the only way for project to find incoming boxes. Then /wallet/transaction/send with manually provided (in “inputsRaw”) serialized boxes (use /utxo/byIdBinary to get serialized box by its identifier).
I did it by myself and got this transaction https://explorer.ergoplatform.com/en/transactions/3d5a1102296b6159754097f33e780cae2692d9a2ec2b6daf26219651bcc2ae48.
Please note that EIP-1 will break this workflow as well.
Crowdfunding Project Proposal
So I propose to raise 500 Ergs before block 50,000 for developing command-line scripts (in Python) for organizing and participating in crowdfunding campaigns after EIP-1 implementation. Command-line scripts are more suitable than doing requests manually, and also could be used for building graphic interfaces on top of them.
The treasury provides half of the funds, so others need to give other 250 collectively. In case of campaign failure refunds will be got automatically. As collecting pledges is not trivial at the moment, I will lead the project role, so please use the key 9gBSqNT9LH9WjvWbyqEvFirMbYp4nfGHnoWdceKGu45AKiya3Fq (controlled by me).
To give money, please follow “How To Donate” section above with replacing backerPubKey with your public key, and pledge amount with a proper value (please note that it is in nanoErgs, 1 Erg = 1000000000 nanoErgs).