Created
June 9, 2025 17:09
-
-
Save PatrickAlphaC/2acb47a0286fe45d2464fa937f00fef3 to your computer and use it in GitHub Desktop.
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
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.19; | |
/// @title CrowdFunder | |
/// @author nemild | |
contract CrowdFunder { | |
// Variables set on create by creator | |
address public creator; | |
address payable public fundRecipient; // creator may be different than recipient, and must be payable | |
uint public minimumToRaise; // required to tip, else everyone gets refund | |
string campaignUrl; | |
uint256 version = 1; | |
// Data structures | |
enum State { | |
Fundraising, | |
ExpiredRefund, | |
Successful | |
} | |
struct Contribution { | |
uint amount; | |
address payable contributor; | |
} | |
// State variables | |
State public state = State.Fundraising; // initialize on create | |
uint public totalRaised; | |
uint public raiseBy; | |
uint public completeAt; | |
Contribution[] contributions; | |
event LogFundingReceived(address addr, uint amount, uint currentTotal); | |
event LogWinnerPaid(address winnerAddress); | |
modifier inState(State _state) { | |
require(state == _state); | |
_; | |
} | |
modifier isCreator() { | |
require(msg.sender == creator); | |
_; | |
} | |
// Wait 24 weeks after final contract state before allowing contract destruction | |
modifier atEndOfLifecycle() { | |
require(((state == State.ExpiredRefund || state == State.Successful) && | |
completeAt + 24 weeks < block.timestamp)); | |
_; | |
} | |
function crowdFund( | |
uint timeInHoursForFundraising, | |
string memory _campaignUrl, | |
address payable _fundRecipient, | |
uint _minimumToRaise) | |
public | |
{ | |
creator = msg.sender; | |
fundRecipient = _fundRecipient; | |
campaignUrl = _campaignUrl; | |
minimumToRaise = _minimumToRaise; | |
raiseBy = block.timestamp + (timeInHoursForFundraising * 1 hours); | |
} | |
function contribute() | |
public | |
payable | |
inState(State.Fundraising) | |
returns(uint256 id) | |
{ | |
contributions.push( | |
Contribution({ | |
amount: msg.value, | |
contributor: payable(msg.sender) | |
}) // use array, so can iterate | |
); | |
totalRaised += msg.value; | |
emit LogFundingReceived(msg.sender, msg.value, totalRaised); | |
checkIfFundingCompleteOrExpired(); | |
return contributions.length - 1; // return id | |
} | |
function checkIfFundingCompleteOrExpired() | |
public | |
{ | |
if (totalRaised > minimumToRaise) { | |
state = State.Successful; | |
payOut(); | |
// could incentivize sender who initiated state change here | |
} else if ( block.timestamp > raiseBy ) { | |
state = State.ExpiredRefund; // backers can now collect refunds by calling getRefund(id) | |
} | |
completeAt = block.timestamp; | |
} | |
function payOut() | |
public | |
inState(State.Successful) | |
{ | |
fundRecipient.transfer(address(this).balance); | |
emit LogWinnerPaid(fundRecipient); | |
} | |
function getRefund(uint256 id) | |
inState(State.ExpiredRefund) | |
public | |
returns(bool) | |
{ | |
require(contributions.length > id && id >= 0 && contributions[id].amount != 0 ); | |
uint256 amountToRefund = contributions[id].amount; | |
contributions[id].amount = 0; | |
contributions[id].contributor.transfer(amountToRefund); | |
return true; | |
} | |
// Warning: "selfdestruct" has been deprecated. | |
// Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code | |
// and data associated with an account and only transfers its Ether to the beneficiary, unless executed | |
// in the same transaction in which the contract was created (see EIP-6780). | |
// Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. | |
// Future changes to the EVM might further reduce the functionality of the opcode. | |
// function removeContract() | |
// public | |
// isCreator() | |
// atEndOfLifecycle() | |
// { | |
// selfdestruct(msg.sender); | |
// // creator gets all money that hasn't be claimed | |
// } | |
} | |
// ** END EXAMPLE ** |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment