-
-
Save arvindkalra/264c87af561b58aed361336fe4fa7e27 to your computer and use it in GitHub Desktop.
GoFundMe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[{"constant":true,"inputs":[],"name":"getTotalApprovalRequests","outputs":[{"name":"total","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getNumberOfSpendings","outputs":[{"name":"numSpendings","type":"uint256"},{"name":"numSpendingsOpen","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"index","type":"uint256"},{"name":"to","type":"address"},{"name":"amount","type":"uint256"},{"name":"receipt","type":"string"},{"name":"isOpen","type":"bool"}],"name":"spendDonations","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getCampaign","outputs":[{"name":"campaignName","type":"string"},{"name":"owner","type":"address"},{"name":"supportingDocuments","type":"string"},{"name":"totalDonations","type":"uint256[4]"},{"name":"times","type":"uint256[2]"},{"name":"campaignType","type":"string"},{"name":"requiredDonation","type":"uint256"},{"name":"donors","type":"address[]"},{"name":"donorsOpen","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"index","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"isOpen","type":"bool"}],"name":"donateTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"index","type":"uint256"}],"name":"getPendingShareBack","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_donationTime","type":"uint256"},{"name":"_spendTime","type":"uint256"},{"name":"_requiredDonation","type":"uint256"},{"name":"_supportingDocuments","type":"string"},{"name":"_campaignType","type":"string"},{"name":"_campaignName","type":"string"}],"name":"startCampaign","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getMyCampaigns","outputs":[{"name":"indices","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getApprovalReqeustDetail","outputs":[{"name":"amount","type":"uint256"},{"name":"approved","type":"bool"},{"name":"spendReceipt","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"campaignIndex","type":"uint256"},{"name":"spendingIndex","type":"uint256"},{"name":"isOpen","type":"bool"}],"name":"getSpendingDetail","outputs":[{"name":"amount","type":"uint256"},{"name":"to","type":"address"},{"name":"receipt","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"apporvalRequestId","type":"uint256"}],"name":"approveExpense","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"index","type":"uint256"},{"name":"donor","type":"address"}],"name":"getDonation","outputs":[{"name":"amount","type":"uint256"},{"name":"amountOpen","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"index","type":"uint256"}],"name":"endCampaign","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"erc20Address","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"creator","type":"address"},{"indexed":false,"name":"campaignName","type":"string"},{"indexed":false,"name":"index","type":"uint256"},{"indexed":false,"name":"requiredDonation","type":"uint256"}],"name":"CampaignCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"index","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"donor","type":"address"}],"name":"TokensDonated","type":"event"}] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
*Submitted for verification at Etherscan.io on 2020-02-14 | |
*/ | |
pragma solidity >=0.4.22 <0.6.0; | |
interface ERC20 { | |
function balanceOf(address _owner) view external returns (uint balance); | |
function transferFrom(address _from, address _to, uint _value) external returns (bool success); | |
function approve(address _spender, uint _value) external returns (bool success); | |
function allowance(address _owner, address _spender) external view returns (uint remaining); | |
function decimals() external view returns(uint digits); | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
} | |
contract GoFundMe { | |
address contractOwner; | |
uint256 totalCampaigns; | |
ERC20 erc20; | |
uint256 decimal; | |
struct SpendData { | |
uint256 amount; | |
string spendReceipt; | |
address to; | |
bool approved; | |
} | |
struct SpendingApprovals { | |
uint256 campaignId; | |
uint256 expenditureId; | |
bool isOpen; | |
} | |
mapping(address => SpendingApprovals[]) recipientsToApprovals; | |
struct Campaign { | |
string campaignName; | |
address owner; | |
string supportingDocuments; | |
uint256 donationEndTime; | |
uint256 spendingEndTime; | |
string campaignType; | |
uint256 requiredDonation; | |
uint256 totalDonation; | |
uint256 totalDonationOpen; | |
mapping(address => uint256) donations; | |
mapping(address => uint256) donationsOpen; | |
mapping(address => uint256) percentageDonation; | |
mapping(address => uint256) percentageDonationsOpen; | |
address[] donors; | |
address[] donorsOpen; | |
bool campaignEnded; | |
bool moneyReturned; | |
uint256 totalSpent; | |
mapping(uint256 => SpendData) expenditures; | |
uint256 numberOfSpendings; | |
uint256 totalSpentOpen; | |
mapping(uint256 => SpendData) expendituresOpen; | |
uint256 numberOfSpendingsOpen; | |
} | |
Campaign[] campaigns; | |
mapping(address => uint256[]) addressToCampaignIndices; | |
event CampaignCreated(address creator, string campaignName, uint256 index, uint256 requiredDonation); | |
event TokensDonated(uint256 index, uint256 amount, address donor); | |
constructor(address erc20Address) public { | |
contractOwner = msg.sender; | |
totalCampaigns = 0; | |
erc20 = ERC20(erc20Address); | |
decimal = 2; | |
} | |
function startCampaign(uint256 _donationTime, uint256 _spendTime, uint256 _requiredDonation, string memory _supportingDocuments, string memory _campaignType, string memory _campaignName) public{ | |
uint256 currentIndex = totalCampaigns++; | |
Campaign memory campaign; | |
campaign.owner = msg.sender; | |
campaign.campaignName = _campaignName; | |
campaign.campaignType = _campaignType; | |
campaign.donationEndTime = now + _donationTime; | |
campaign.spendingEndTime = now + _spendTime; | |
campaign.requiredDonation = _requiredDonation; | |
campaign.supportingDocuments = _supportingDocuments; | |
campaign.campaignEnded = false; | |
campaign.moneyReturned = false; | |
campaign.numberOfSpendings = 0; | |
campaign.totalSpent = 0; | |
campaign.numberOfSpendingsOpen = 0; | |
campaign.totalSpentOpen = 0; | |
campaigns.push(campaign); | |
addressToCampaignIndices[msg.sender].push(currentIndex); | |
emit CampaignCreated(msg.sender, _campaignName, currentIndex, _requiredDonation); | |
} | |
function getCampaign(uint256 index) public view returns( | |
string memory campaignName, | |
address owner, | |
string memory supportingDocuments, | |
uint256[4] totalDonations, | |
uint256[2] memory times, | |
string memory campaignType, | |
uint256 requiredDonation, | |
address[] memory donors, | |
address[] memory donorsOpen | |
){ | |
Campaign memory currentCampaign = campaigns[index]; | |
campaignName = currentCampaign.campaignName; | |
owner = currentCampaign.owner; | |
supportingDocuments = currentCampaign.supportingDocuments; | |
totalDonations[0] = currentCampaign.totalDonation; | |
totalDonations[1] = currentCampaign.totalDonationOpen; | |
totalDonations[2] = currentCampaign.totalSpent; | |
totalDonations[3] = currentCampaign.totalSpentOpen; | |
times[0] = currentCampaign.donationEndTime; | |
times[1] = currentCampaign.spendingEndTime; | |
campaignType = currentCampaign.campaignType; | |
requiredDonation = currentCampaign.requiredDonation; | |
donors = currentCampaign.donors; | |
donorsOpen = currentCampaign.donorsOpen; | |
} | |
function donateTokens(uint256 index, uint256 amount, bool isOpen) public { | |
Campaign storage currentCampaign = campaigns[index]; | |
require(now <= currentCampaign.donationEndTime, "This campaign is not accepting donations now"); | |
safeTransfer(msg.sender, address(this), amount); | |
if(isOpen == true){ | |
currentCampaign.totalDonationOpen += amount; | |
if(currentCampaign.donationsOpen[msg.sender] == 0){ | |
currentCampaign.donorsOpen.push(msg.sender); | |
} | |
currentCampaign.donationsOpen[msg.sender] += amount; | |
}else{ | |
currentCampaign.totalDonation += amount; | |
if(currentCampaign.donations[msg.sender] == 0){ | |
currentCampaign.donors.push(msg.sender); | |
} | |
currentCampaign.donations[msg.sender] += amount; | |
} | |
emit TokensDonated(index, amount, msg.sender); | |
} | |
function getMyCampaigns() public view returns(uint256[] indices){ | |
indices = addressToCampaignIndices[msg.sender]; | |
} | |
function getDonation(uint256 index, address donor) public view returns(uint256 amount, uint256 amountOpen){ | |
Campaign storage currentCampaign = campaigns[index]; | |
amount = currentCampaign.donations[donor]; | |
amountOpen = currentCampaign.donationsOpen[donor]; | |
} | |
function safeTransfer(address _from, address _to, uint256 _amount) private { | |
require(erc20.balanceOf(_from) >= _amount, "Sender Does Not Hold Enough Tokens"); | |
require(erc20.allowance(_from, address(this)) >= _amount, "Sender Has Not Provided Enough Allowance to the Contract"); | |
erc20.transferFrom(_from, _to, _amount); | |
} | |
function endCampaign(uint256 index) public { | |
Campaign storage currentCampaign = campaigns[index]; | |
require(now > currentCampaign.donationEndTime, "Campaign can not be closed yet"); | |
require(currentCampaign.owner == msg.sender, "Not the Owner of this campaign"); | |
calculatePercentage(index); | |
} | |
function calculatePercentage(uint256 index) private { | |
Campaign storage currentCampaign = campaigns[index]; | |
require(currentCampaign.campaignEnded == false, "Campaign has already ended"); | |
address[] memory donors = currentCampaign.donors; | |
uint256 totalDonation = currentCampaign.totalDonation; | |
for(uint256 i = 0; i < donors.length; i++){ | |
currentCampaign.percentageDonation[donors[i]] = (totalDonation * 10 ** decimal) / currentCampaign.donations[donors[i]]; | |
} | |
donors = currentCampaign.donorsOpen; | |
totalDonation = currentCampaign.totalDonationOpen; | |
for(i = 0; i < donors.length; i++){ | |
currentCampaign.percentageDonationsOpen[donors[i]] = (totalDonation * 10 ** decimal) / currentCampaign.donationsOpen[donors[i]]; | |
} | |
currentCampaign.campaignEnded = true; | |
} | |
function spendDonations(uint256 index, address to, uint256 amount, string receipt, bool isOpen) public{ | |
Campaign storage currentCampaign = campaigns[index]; | |
require(now <= currentCampaign.spendingEndTime, "Spend Time is Closed"); | |
SpendData memory currentSpend; | |
currentSpend.to = to; | |
currentSpend.spendReceipt = receipt; | |
currentSpend.amount = amount; | |
currentSpend.approved = false; | |
SpendingApprovals memory currentApproval; | |
currentApproval.campaignId = index; | |
uint256 expenditureId; | |
if(isOpen == true){ | |
require(currentCampaign.totalSpentOpen + amount <= currentCampaign.totalDonationOpen, "You have used all your donation"); | |
expenditureId = currentCampaign.numberOfSpendingsOpen++; | |
currentCampaign.expendituresOpen[expenditureId] = currentSpend; | |
currentCampaign.totalSpentOpen += amount; | |
currentApproval.isOpen = true; | |
currentApproval.expenditureId = expenditureId; | |
}else{ | |
//TODO Write a check for KYC | |
require(currentCampaign.totalSpent + amount <= currentCampaign.totalDonation, "You have used all your donation"); | |
expenditureId = currentCampaign.numberOfSpendingsOpen++; | |
currentCampaign.expenditures[expenditureId] = currentSpend; | |
currentCampaign.totalSpent += amount; | |
currentApproval.isOpen = false; | |
currentApproval.expenditureId = expenditureId; | |
} | |
recipientsToApprovals[to].push(currentApproval); | |
} | |
function approveExpense(uint256 apporvalRequestId) public{ | |
SpendingApprovals storage currentApproval = recipientsToApprovals[msg.sender][apporvalRequestId]; | |
Campaign storage currentCampaign = campaigns[currentApproval.campaignId]; | |
if(currentApproval.isOpen){ | |
SpendData storage currentSpend = currentCampaign.expendituresOpen[currentApproval.expenditureId]; | |
} else{ | |
currentSpend = currentCampaign.expenditures[currentApproval.expenditureId]; | |
} | |
require(currentSpend.approved == false, "Expense Already Approved"); | |
require(currentSpend.to == msg.sender, "Sender is not recipient of this Expense"); | |
currentSpend.approved = true; | |
erc20.transfer(currentSpend.to, currentSpend.amount); | |
} | |
function getTotalApprovalRequests() public view returns(uint256 total){ | |
total = recipientsToApprovals[msg.sender].length; | |
} | |
function getApprovalReqeustDetail(uint256 id) public view returns(uint256 amount, bool approved, string spendReceipt){ | |
SpendingApprovals storage currentApproval = recipientsToApprovals[msg.sender][id]; | |
Campaign storage currentCampaign = campaigns[currentApproval.campaignId]; | |
SpendData storage currentSpend; | |
if(currentApproval.isOpen == true){ | |
currentSpend = currentCampaign.expendituresOpen[currentApproval.expenditureId]; | |
}else{ | |
currentSpend = currentCampaign.expenditures[currentApproval.expenditureId]; | |
} | |
amount = currentSpend.amount; | |
approved = currentSpend.approved; | |
spendReceipt = currentSpend.spendReceipt; | |
} | |
function getNumberOfSpendings(uint256 index) public view returns(uint256 numSpendings, uint256 numSpendingsOpen){ | |
Campaign storage currentCampaign = campaigns[index]; | |
numSpendings = currentCampaign.numberOfSpendings; | |
numSpendingsOpen = currentCampaign.numberOfSpendingsOpen; | |
} | |
function getSpendingDetail(uint256 campaignIndex, uint256 spendingIndex, bool isOpen) public view returns(uint256 amount, address to, string receipt){ | |
Campaign storage currentCampaign = campaigns[campaignIndex]; | |
SpendData storage currentSpend = currentCampaign.expenditures[spendingIndex]; | |
if(isOpen == true){ | |
currentSpend = currentCampaign.expendituresOpen[spendingIndex]; | |
} | |
amount = currentSpend.amount; | |
to = currentSpend.to; | |
receipt = currentSpend.spendReceipt; | |
} | |
function getPendingShareBack(uint256 index) public { | |
Campaign storage currentCampaign = campaigns[index]; | |
require(now > currentCampaign.spendingEndTime, "The Campaign Owner is still spending money"); | |
require(currentCampaign.moneyReturned == false, "Share is already returned"); | |
uint256 leftAmount = currentCampaign.totalDonation - currentCampaign.totalSpent; | |
address[] memory donors = currentCampaign.donors; | |
for(uint256 i = 0; i < donors.length; i++){ | |
uint256 donorPercentage = getDonationPercentage(index, donors[i], false); | |
uint256 toBeReturned = (leftAmount * 10 ** decimal) / donorPercentage; | |
erc20.transfer(donors[i], toBeReturned); | |
} | |
leftAmount = currentCampaign.totalDonationOpen - currentCampaign.totalSpentOpen; | |
donors = currentCampaign.donorsOpen; | |
for(i = 0; i < donors.length; i++){ | |
donorPercentage = getDonationPercentage(index, donors[i], true); | |
toBeReturned = (leftAmount * 10 ** decimal) / donorPercentage; | |
erc20.transfer(donors[i], toBeReturned); | |
} | |
currentCampaign.moneyReturned = true; | |
} | |
function getDonationPercentage(uint256 index, address donor, bool isOpen) private view returns(uint256 percentage){ | |
if(isOpen == true){ | |
percentage = campaigns[index].percentageDonationsOpen[donor]; | |
}else{ | |
percentage = campaigns[index].percentageDonation[donor]; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment