Last active
November 11, 2020 07:30
-
-
Save satwikkansal/0f8158ac988d7d5ed963ccbe594c07ca to your computer and use it in GitHub Desktop.
Ethereum Smart Contracts written in Solidity for Crowdfunding
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
pragma solidity ^0.5.12; | |
/* | |
Ddescription: | |
Starting with the bank smart contract, create the notion of a next-of-kin address which gives names who can take control of the account if left inactive for 3 years. | |
▹ Add code to ensure that upon registering, a user must provide a next-of-kin address. | |
Hint: You will need a new table to store this. | |
▹ Add another table which stores when an account was last used (by calling register, deposit or payOut). | |
▹ Add a function alive() which does (almost) nothing, except that the owner of an account may use to indicate that they are still alive, thus updating the last-used timestamp. | |
▹ Add a function inherit(...), which the next-of-kin may invoke to take control of an account. The function must be given which account to take control of, and the new next-of-kin. | |
Process, | |
- constructor is called, sender becomes the bank owner. | |
- users can register via public register method, they need to provide kin address that can inherit. | |
- users can call deposit or payOut to move around their funds | |
- alive method to let the contract know user is still alive and ineligible for inheritance | |
- inherit method to inherit. | |
Imp. design considerations, | |
- In the inherit function, the next_of_kin (the address who's going to inherit the account) may or | |
may not have an account already, so both cases need to be handled. | |
*/ | |
contract Bank { | |
address payable bankOwner; | |
mapping (address => bool) public isRegistered; | |
mapping (address => uint) public accountBalance; | |
mapping (address => address) public nextOfKin; | |
mapping (address => uint256) public lastUsed; | |
constructor () public { | |
bankOwner = msg.sender; | |
} | |
function register(address payable _next_of_kin) public { | |
// Needs to be called before deposit. | |
// must be called by someone who has not yet registered | |
require(!isRegistered[msg.sender]); | |
// Register the user and set their balance to zero | |
isRegistered[msg.sender] = true; | |
accountBalance[msg.sender] = 0; | |
nextOfKin[msg.sender] = _next_of_kin; | |
lastUsed[msg.sender] = block.timestamp; | |
} | |
function deposit() public payable { | |
// Assumes only owner will deposit to their account. | |
// Must be called by a registered user | |
require(isRegistered[msg.sender]); | |
// Add balance of that user | |
accountBalance[msg.sender] = accountBalance[msg.sender] + msg.value; | |
lastUsed[msg.sender] = block.timestamp; | |
} | |
function payOut (address payable destination, uint amount) public { | |
// Only registered users may call this | |
require(isRegistered[msg.sender]); | |
// The amount may not exceed the balance of the payer | |
require(accountBalance[msg.sender] >= amount); | |
// Reduce the balance and send the money out | |
accountBalance[msg.sender] = accountBalance[msg.sender] - amount; | |
destination.transfer(amount); | |
lastUsed[msg.sender] = block.timestamp; | |
} | |
function alive () public { | |
// Only registered users may call this | |
require(isRegistered[msg.sender]); | |
lastUsed[msg.sender] = block.timestamp; | |
} | |
function inherit (address account_owner, address _new_next_of_kin) public { | |
require(isRegistered[account_owner]); | |
// Check invoker is actually next_of_kin | |
require(nextOfKin[account_owner] == msg.sender); | |
// Check that the account has been inactive for more than 3 days | |
require(lastUsed[account_owner] + 3 days < block.timestamp); | |
// Create an account for next_of_kin | |
if (!isRegistered[msg.sender]) { | |
// next_of_kin doesn't have an account yet | |
isRegistered[msg.sender] = true; | |
accountBalance[msg.sender] = accountBalance[account_owner]; | |
nextOfKin[msg.sender] = _new_next_of_kin; | |
} else { | |
// next_of_kin has an account already. | |
// transfer the account_owner's balances to his existing account | |
accountBalance[msg.sender] += accountBalance[account_owner]; | |
// update the kin, this can be optional, but sounds like a reasonable thing to do | |
nextOfKin[msg.sender] = _new_next_of_kin; | |
} | |
// Invalidate the original account since it's inherited now | |
delete accountBalance[account_owner]; | |
isRegistered[account_owner] = false; | |
delete nextOfKin[account_owner]; | |
lastUsed[msg.sender] = block.timestamp; | |
} | |
} |
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
pragma solidity ^0.5.12; | |
/* | |
Process: | |
1. The smart contract is initialized, the address initiating it is the platform owner. | |
2. start_campaign method is called which kickstarts a campaign with the configurations specified. | |
3. Users can contribute to the campaign. (via contribute method) | |
4. Users can also withdraw if they like. (via withdraw method) | |
5. Once the time is over, campaign can be closed. (via close_campaign method) This transfers the funds to | |
campaign_admin by deducting the platform_charge which is sent to the platform_owner. | |
6. Optionally, platform_owner can destroy the contract once it's done. | |
Things taken into consideration while desigining, | |
- Only one campaign can run at a time. | |
- Reusability for a newer Campaign. | |
- Allow multiple deposits by the same user. | |
The checks performed are documented in the code. | |
*/ | |
contract CrowdfundingPlatform { | |
// Platform related configurations | |
address payable public platform_owner; | |
uint8 platform_charge_percent = 5; | |
// Campaign related configurations | |
address payable public campaign_admin; | |
uint256 campaign_target; | |
uint256 campaign_expiry; | |
uint256 total_deposit; | |
enum State { NotRunning, Running, Failed } | |
State public campaign_state; | |
// Campaign contributors related configurations | |
// This list will be later used to refund in case Campaign is unsuccessful | |
mapping(address => uint256) user_deposits; | |
constructor () public { | |
// The person who initializes it is the owner | |
platform_owner = msg.sender; | |
campaign_state = State.NotRunning; | |
} | |
function start_campaign (uint256 _campaign_target, uint256 _end_timestamp) public { | |
/* | |
_campaign_target: Minimum amount that you can send to the campaign | |
_end_timestamp: uint256 timestamp denoting end time of the campaign | |
*/ | |
// Check if not expired already | |
require(block.timestamp < _end_timestamp); | |
// Check no campaign running already | |
require(campaign_state == State.NotRunning || campaign_state == State.Failed); | |
// Check if the target is +ve | |
require(campaign_target > 0); | |
// Kickstart! | |
campaign_admin = msg.sender; | |
campaign_target = _campaign_target; | |
campaign_expiry = _end_timestamp; | |
campaign_state = State.Running; | |
} | |
function contribute () external payable { | |
// Check if not expired already | |
require(block.timestamp < campaign_expiry); | |
// Allows multiple deposits by the same user | |
uint256 amount_sent = msg.value; | |
user_deposits[msg.sender] += amount_sent; | |
total_deposit += amount_sent; | |
} | |
function withdraw () public { | |
// Check if not expired already | |
require(block.timestamp < campaign_expiry); | |
address payable beneficiary = msg.sender; | |
// Check if the user has contributions | |
require(user_deposits[beneficiary] > 0); | |
uint256 user_deposit_value = user_deposits[beneficiary]; | |
beneficiary.transfer(user_deposit_value); | |
total_deposit -= user_deposit_value; | |
user_deposits[beneficiary] = 0; | |
} | |
function close_campaign () public { | |
// Check if not expired already | |
require (block.timestamp > campaign_expiry); | |
if (total_deposit > campaign_target) { | |
// Campaign was a success | |
uint256 platform_charge = platform_charge_percent * 100 / total_deposit; | |
uint256 campaign_benificary_amount = total_deposit - platform_charge; | |
platform_owner.transfer(platform_charge); | |
campaign_admin.transfer(campaign_benificary_amount); | |
campaign_state = State.NotRunning; | |
} else { | |
// It failed, open for refunds now. | |
campaign_state = State.Failed; | |
} | |
// Reset everything | |
campaign_target = 0; | |
campaign_expiry = 0; | |
total_deposit = 0; | |
} | |
function refund () public { | |
require (campaign_state == State.Failed); | |
address payable beneficiary = msg.sender; | |
// Check if the user has contributions | |
require(user_deposits[beneficiary] > 0); | |
uint256 user_deposit_value = user_deposits[beneficiary]; | |
beneficiary.transfer(user_deposit_value); | |
total_deposit -= user_deposit_value; | |
user_deposits[beneficiary] = 0; | |
} | |
function destroy() public { | |
// Check that sender is platform_owner | |
require(msg.sender == platform_owner); | |
// Verify that no campaign is runnig | |
require(campaign_state == State.NotRunning); | |
selfdestruct(platform_owner); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment