Skip to content

Instantly share code, notes, and snippets.

@satwikkansal
Last active November 11, 2020 07:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save satwikkansal/0f8158ac988d7d5ed963ccbe594c07ca to your computer and use it in GitHub Desktop.
Save satwikkansal/0f8158ac988d7d5ed963ccbe594c07ca to your computer and use it in GitHub Desktop.
Ethereum Smart Contracts written in Solidity for Crowdfunding
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;
}
}
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