Crowdfunding in sway

Last year I developed a crowdfunding in sway, a very simple dapp which you can start implementing with basic knowledge of the language.

What does this contract?

In this contract we have developed a dapp to raise funds through amounts that are relatively small but with a large number of contributors.

Let's start

The first thing we will do is to create a library that will contain the abi with all the data structures and functions we need.

Let's define the library with its name and import all the functions that we will use from the STD library.

library crowdfunding_library;

use core::ops::Eq;

use std::{
    identity::Identity,
    contract_id::ContractId,
};

Then we define the abi with the functions that we will use in the contract.

abi CrowdFunding {
    // Initliaze Campaign
    #[storage(read, write)]
    fn initialize(
      token: ContractId
    );

    // See if campaign is initalize or not.
    #[storage(read)]
    fn is_initialized() -> bool;

    // Create campaign
    #[storage(read, write)]
    fn create_campaign(
        beneficiary: Identity, 
        goal_amount: u64, 
        minimumcontribution: u64
    ) -> u64;

    // Pledge an amount to a campaign
    #[storage(read, write)]
    fn minimum_contribution(
      campaign_id: u64
    );

    // Completes a campaign and sends the total amount pledged to the campaign beneficiary
    #[storage(read, write)]
    fn complete_campaign(
      campaign_id: u64
    );
}

We implement the campaign status function.

// function status
pub enum State {
    NotInitialized: (),
    Initialized: (),
}

impl Eq for State {
    fn eq(self, other: Self) -> bool {
        match(self, other) {
            (State::Initialized, State::Initialized) => true,
            (State::NotInitialized, State::NotInitialized) => true,
            _ => false, 
        }
    }
}

Last but not least we define the structure of the campaign

// Campaing Struct.
pub struct Campaign {
    current_amount: u64,
    is_active: bool,
    author: Identity,
    beneficiary: Identity,
    goal_amount: u64
}

Now that we have the complete library we can move on to the development of the main contract.

We start by calling the library we developed and also the std library with the functions we will use.

contract;

dep crowdfunding_library;

use crowdfunding_library::*;

use std::{
    identity::Identity,
    contract_id::ContractId,
    assert::assert,
    storage::StorageMap,
    chain::auth::{AuthError, msg_sender},
    result::*,
    context::{call_frames::msg_asset_id, msg_amount, this_balance},
    token::transfer,
    revert::revert,
};

Now we define all the storage variables.

storage {
    // Tells us if it is already initialized or not
    state: State = State::NotInitialized,

    // Contract id of the accepted asset
    asset: ContractId = ContractId {
        value: 0x0000000000000000000000000000000000000000000000000000000000000000,
    },

    // The current total number of campaigns
    next_campaign_id: u64 = 0,

    // All campaigns
    campaigns: StorageMap<u64, Campaign> = StorageMap {},
}

And finally we implement all the functions we developed in the abi of the crowdfunding_library.sw library.

impl CrowdFunding for Contract {
    #[storage(read, write)]
    fn initialize(asset: ContractId) {
        // Storage state 
        assert(storage.state == State::NotInitialized);
        // Asset 
        assert(storage.asset == asset::ContractId);   
    }

    // is initialize 
    #[storage(read)]
    fn is_initialized() -> bool {
        match storage.state {
            State::Initialized => true,
            State::NotInitialized => false,
        }
    }

    // Create a new campaign
    #[storage(read, write)]
    fn create_campaign(beneficiary: Identity, goal_amount: u64, minimumcontribution: u64) -> u64 {
        assert(storage.state == State::Initial);

        let author = msg_sender().unwrap();
        let campaign = Campaign {
            current_amount: 0,
            is_active: true,
            author,
            beneficiary,
            goal_amount
        };

        let campaign_id = storage.next_campaign_id;
        storage.campaigns.insert(campaign_id, campaign);
        storage.next_campaign_id += 1;
        campaign_id

    }


    // Minimum Amount for the Contribution
    #[storage(read, write)]
    fn minimum_contribution(campaign_id: u64) {
        assert(storage.state == State::Initialized);
        assert(campaign_id < storage.next_campaign_id);
        assert(storage.asset == msg_asset_id());

        let mut campaign = storage.campaigns.get(campaign_id);
        let minimum_contribution_amount = msg_amount();

        assert(campaign.is_active);
        assert(minimum_contribution_amount > 0);

        campaign.current_amount += minimum_contribution_amount;
        storage.campaigns.insert(campaign_id, campaign);
    }

    // crowfunding completed
    #[storage(read, write)]
    fn complete_campaign(campaign_id: u64) {
        assert(storage.state == State::Initialized);
        assert(campaign_id < storage.next_campaign_id);

        let mut campaign = storage.campaigns.get(campaign_id);

        assert(campaign.is_active);
        assert(campaign.current_amount >= campaign.goal_amount);
        campaign.is_active = false;
    }
}

And that's it, we just developed a dapp to raise funds! I hope you found this little tutorial useful.

Subscribe to andrw
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.