---
title: HackMD Dark Theme
tags: theme
description: Use `{%hackmd theme-dark %}` syntax to include this theme.
---
<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>
# Raisin Whitepaper
Raisin Whitepaper
[Name Redacted]
(a.k.a Crypdough.eth)
Raisin Labs, November ‘22
# Abstract
Raisin is a peer-to-peer fundraising protocol facilitated by code for the benefit of charities, public goods funding, and social impact causes. Raisin is a permissionless protocol enabled by smart contracts. Raisin was designed with elegance and simplicity in mind: minimizing complexity tends to help mitigate against the risk of dangerous code and makes it easy to work with externally. Raisin emphasizes composability, aiming to be the backend of choice for anyone creating their own fundraising platform. Raisin changes the game fundamentally by being the building block that every business can leverage to create fully customized, purpose-oriented fundraising platforms.
## Introduction - Getting Started
To get started and create a raisin, or a fund, the user can choose from a set of highly liquid tokens on the supported network. At the protocol level, to check whether your token is accepted by the protocol, call tokenWhitelist() with the token’s contract address as the argument. If it returns true, the token is whitelisted and is able to be used by various raisins across the platform.
Next, calling initFund() will “create” a new raisin (add a new element to the raisins array with the Raisin structure). Once your goal is reached, you can end your fund by calling endFund() and withdraw to the committed wallet with fundWithdraw(). In the less optimistic route, the fund will expire and not raise sufficient funds. In this case, the users will be able to pull their donations through the smart contract to receive a refund. In the least optimistic route, the fund will not have received any donations and emit the fund ended event.
On the other side of things, users can donate to a given cause by calling approve() on their token’s smart contract and then calling the donateToken() function on Raisin. When calling approve(), pass in the Raisin contract address as the first argument and the donation amount formatted in WEI as the second argument.
## Storage
Raisin’s storage is made up of a few different data structures such as hashmaps, a struct, and a dynamic array. The donorBal mapping has a nested k:(k:v) structure and allows for the lookup of a donors balance based on the index of the cause they donated to and an address. This mapping can be called like a function (as it is public) to query the list of raisins. The tokenWhitelist mapping is exactly what it sounds like: an allow list for tokens. If the set was large, I could justify using a merkle root, but I don’t want to allow a huge set of tokens to interact with the protocol for security reasons and in the name of preventing the moral dilemmas inherent to shitcoins. Besides, any smart contract can implement token standards in a malicious, albeit compliant fashion to the standard. Moreover, dumping on the heads of unwitting retail investors that were likely scammed in the first place is a moral hazard equal to the participation therein. We seek to curb the tail risk of low liquid assets for people raising over longer periods of time. Lastly, the partnership mapping manages the discount rate for companies that Raisin collaborates with.
The protocol has four events: FundStarted, TokenDonated, TokenAdded, and FundEnded. Each of these events are meant to emit sufficient information to front-ends for event handling and reflect changes within the state of the contract seamlessly. We include all the information from the raisin struct and position in the array on creation. Moreover, every time a token is donated, the amount and type are duly noted in an event. TokenAdded emits the log of the token’s contract and FundEnded emits the new array size, the index of the element which concluded fundraising activities, and the index associated with the element.
In storage we have a few additional stand-alone variables. There are two addresses stored: one called vault and another called governance. The former is where the proceeds of the protocol fees go to and the latter is used for moderating raisins.The latter address can call endFund() on a fund that didn’t already meet its goal and refund users in that same call. Otherwise we have a few unsigned integers: expiry, and fee. The fee is the number of basis points users pay to the protocol, and expiry is the amount of time in UNIX format that users have to raise funds.
The Raisin struct has six variables: _amount, _fundBal, _token, _raiser, _recipient, and _expires. The first is the minimum amount needed for the goal to be meaningful to the raiser (expressed usually as a percentage of the overall goal). The _fundBal is the current balance of the raisin denominated in the _token address associated with it. The _raiser is the address of the user raising the funds and the _recipient is the committed address which the tokens will be withdrawn to upon a successful fundraising campaign. Finally, _expires is the UNIX timestamp of the cause’s expiry. This structure is used in the public raisins array to store the relevant on-chain information about each individual raisin.
## Public Functions
Each public variable in solidity is automatically turned into a getter. Thus, you can retrieve the values easily located in them by calling them like you would a function. For example, if you pass in an index position to raisins(), it would return you all six fields of the Raisin structure at that position in the array if valid.
Recall that to begin a fund, you must first call initFund(). This function takes three arguments: the amount, the token, and the recipient of the funds. The amount must be non-zero and the token must be whitelisted by the protocol. If these conditions are met we create a new raisin by pushing it into the array. This function emits the FundStarted event.
Next in the lifecycle of public functions, there is donateToken(). This function takes in the token address, the index in the array, and the amount as arguments. The timestamp of the current block must not be greater than the expiry time for the project and the token needs to be the one accepted by the fund. If these conditions are met, then we can update the donorBal mapping & _fundBal with the donation amount, transfer the fee to the vault, transfer the rest to the fund, and emit the TokenDonated event.
If at any time prior to the expiry and the raiser wants to end the fund, they may do so by calling endFund(). This function takes in the array position as an argument and requires that the caller is either the fund’s raiser or that it is being called by the governance contract. If the raiser or governance contract is calling this function, then the fund will expire immediately.
Once the fund has expired, if the goal was met, then one may call fundWithdraw(). This function takes in the token address and index position in the array as arguments. This function requires that the goal was reached and that the fund is expired. The state is updated in the mappings and makes a call to approve the token for transfer by the contract, and then transfers the tokens to the recipient designated during initFund(). In the other case where not enough tokens were raised, donors will call refund(). This function takes a token and an index position as arguments. If the fund is expired and the goal is not reached, then we will update the state in the donorBal mapping to prevent reentrancy and then transfer the funds back to the donor. Last one to pull their donation also emits the fund ended event to notify the front end to remove it from the UI.
## Private / External Functions
### External Interactions
There are two functions responsible for the external interactions concerning tokens transfers and approvals. The approveTokenForContract() function allows us to approve the token for the Raisin contract while it is in possession of the token. It makes a call out to the ERC20 compliant token’s approval() function using the IERC20 interface. The function accepts the token address and amount as arguments. The other function is responsible for the transfer -- once a token is approved, we can then transfer it using the erc20Tranfer() function. It accepts the token address, the sender, the recipient, and amount as arguments.
This protocol has a handful of internal functions for optimization reasons and for state management. One such example is the manageDiscount() function. It updates the partnership mapping in storage. This works in conjunction with the calculateFee() function which will check for non-zero values in the partnership mapping to replace the global fee with on donation to causes started by certain addresses. We also have getExpiry() which dynamically calculates the expiration time of a new raisin. In addition, we also have functions that manage the governance address, whitelist tokens, change the vault, and global expiry time (with relatively uninteresting mechanics).
## Moderation & Governance
Perhaps the greatest challenge in an application layer protocol that is used to raise funds for public goods, charities, and social impact causes is the sheer number of possible causes that can come through the door while also ensuring that funds aren’t going to illegitimate causes. In designing Raisin I put a lot of thought into this by coding transparency into the protocol and by considering the many types of stakeholders that can be engaged with the protocol & the role they can play in the community. By distributing moderation across everyone who hosts a frontend, the task becomes a lot more manageable for the operators because they will only handle a subset of the total set of causes. This addresses key issues in the scaling of moderation efforts protocol wide.
From the perspective of nonprofits that raise funds regularly through Raisin, they provide a form of curation by highlighting and bringing their own causes to their platform. We need not worry about the legitimacy of individual causes if the non-profit itself is trusted to run their owns platform and verify causes. Raisin strives to steer away from classical governance structures such as pure token weighted voting. While tokens themselves are effective ways to represent votes, it’s difficult to get right, so to speak. For example, it may turn out to be easy for some parties to accumulate large amounts of tokens, influence the vote in a material way, and possibly even enact changes in contrast to the best interests of the users and wider community. Therefore, we are considering a range of alternatives based on metrics such as community engagement & participation, usage of the protocol, community reputation, and limited governance. Besides protocol level governance, there is also the potential to moderate causes at the UI level by making it easy for signed-in users to flag or vote against low quality causes, making them much harder to come by if at all.
## Interoperability
On-chain, smart contracts can communicate with one another and even call external functions! Raisers can initiate their cause from DAOs, multisigs, and or smart contracts if they can simply call Raisin’s functions! This protocol is made to be easy enough for other developers to work with directly and be able to call it from their code. Because we emphasize plug and play in our design, the most important advantage we have is that Raisin can serve a wide range of needs across the fundraising space in such a way that it is reliable, not dependent on overly complex mechanisms to work, and hassle free for the organizations that want to implement it into their stack.
Fin