DEV Community

Cover image for Navigating a crowd funding smart contract workflow in an open financial network - STELLAR
PRINCE EGBEDELE
PRINCE EGBEDELE

Posted on

Navigating a crowd funding smart contract workflow in an open financial network - STELLAR

The Stellar Ecosystem

For those new to the Stellar universe; welcome aboard! This train is headed toward deeply understanding one of the most innovative blockchain ecosystems. And for our seasoned travelers who have mastered the tracks, feel free to teleport straight to our last stop, where we’ll embark on the adventure of the work of a crowdfunding smart contract with Soroban.

Have you ever heard of an open network designed for securely storing and transferring money while onboarding boundless innovation? A world where cross-border payments are as effortless as sending a message?

stellar

Our First Stop: What Is Stellar?
Stellar is a layer 1 decentralized, open-source blockchain ecosystem designed to improve traditional financial systems, making money better. It bridges the cryptocurrency world and conventional finance, enabling fast, efficient, and cost-effective cross-border payments. It also allows the creation of digital representations of value, such as fiat currencies and custom-made currencies.

WHO ARE THE VISIONARIES BEHIND STELLAR?

THE FOUNDING STORY

Unlike other ecosystems, these visionaries had an unwavering singular focus; the seamless transfer of value. In 2014, two cryptocurrency OG’s laid the foundation for Stellar, Jed McCaleb and Joyce Kim. Jed McCaleb is an experienced entrepreneur known for founding Mt. Gox and co-founding Ripple. Joyce Kim, on the other hand, is a former lawyer and venture capitalist who established the Stellar Development Foundation.

The journey began with a pivotal conversation between McCaleb and Patrick Collison, co-founder of Stripe. They discovered a shared alignment in their goals and values, particularly the potential to create a more inclusive financial system. Recognizing the transformative potential of Stellar, Stripe invested $3 million into the project, providing critical early support alongside Silicon Valley legends Keith Radios (ex-PayPal exec), Sam Altman (OpenAI and Worldcoin founder), and Naval Ravikant (AngelList). This investment wasn’t just a financial boost, it was a validation of Stellar’s potential to reshape the financial landscape.

PROBLEMS WITH PAYMENTS

One of the biggest challenges in traditional banking is fragmentation, which refers to the lack of cooperation among various financial institutions and services. This fragmentation makes it increasingly difficult to send money to individuals in different parts of the world.

Several factors contribute to this issue:

Diverse Technologies and Platforms: The financial industry is riddled with different payment systems like SWIFT, ACH, and others. Each operates independently, creating barriers to seamless transactions.

Data Isolation: Data is often siloed within specific institutions or regions, further complicating the transfer of information and funds across borders.

Regulatory Frameworks: Each country or region has its own set of regulations governing financial transactions. This lack of standardization increases the complexity and cost of cross-border payments, leading to delays and inefficiencies.

This shouldn’t be a problem anymore, as cryptocurrency is providing solutions. With a crypto wallet, you can easily send and receive money across the globe without the need for intermediaries. However, not everyone has a crypto wallet. As of 2024, the global cryptocurrency adoption rate is approximately 6.8%, a surprisingly small number, right? While some have eagerly jumped on the crypto train, others remain comfortable using their banks for transactions.

stellar

Stellar's Solution

An exciting phase lies ahead for our journey.

With Stellar, you don’t need to have a crypto wallet to make cross-border transactions, not like the general crypto solutions, Amazed? yeah.

Introducing Anchors: Anchors are trusted entities that act as a bridge between the Stellar network and traditional financial systems. They facilitate the seamless movement of money between the crypto world and the real world, making the process accessible to everyone. With Stellar, users can send money effortlessly around the globe using their bank accounts, without even realizing they're interacting with a cryptocurrency system. Here's how it works:

When a user sends money through their bank using Stellar, the anchor silently converts the money into a digital form on the Stellar blockchain. This digital currency is then transferred to the recipient’s region, where another anchor converts it back into the local fiat currency, depositing it directly into the receiver’s bank account. All of this happens behind the scenes, ensuring a smooth and transparent experience for the user.

article

With Stellar, sending money across the world becomes effortless, simple, cheap, fast, and secure.

“The only thing that is constant is change.” This principle of continuous evolution is at the heart of Stellar's journey, as the ecosystem strives to become better and bigger. Stellar isn't just about facilitating payments and transferring value anymore. There is much more to finance than just payments.

Our Next Stop: Soroban

The future of Stellar is about expanding beyond pure payments, and that journey continues with Soroban—a groundbreaking development that brings smart contracts to the Stellar blockchain. Soroban enables developers to build endless innovations on the Stellar network, unlocking new possibilities for decentralized finance (DeFi), tokenization, and beyond.

Imagine the ability to tokenize real-world assets like real estate or commodities on the Stellar blockchain. With Soroban, developers can create smart contracts that automatically handle complex transactions, manage assets, or even facilitate automated compliance. This not only broadens the scope of what Stellar can do but also opens up new avenues for financial innovation, making the ecosystem more versatile and powerful than ever before.

KEY FEATURES OF STELLAR

Some key features of Stellar include:

Stellar Consensus Protocol (SCP): Unlike traditional Proof of Work or Proof of Stake mechanisms, Stellar uses SCP, which is faster and more energy efficient, allowing for quick transaction confirmations.

Lumens (XLM): Lumens are the native digital currency of the Stellar network. They are used to pay transaction fees and maintain account balances.

Decentralized Exchange: Stellar has a built-in decentralized exchange (DEX) that allows users to trade various assets directly on the network.

Stellar features

Great job making it this far! You've built a solid understanding of the Stellar ecosystem and how its blockchain operates.

Our Last Stop: Work flow of a Crowdfunding Smart Contract on soroban

Crowdfunding DApp on Soroban with Rust: Navigation for you to Build Better

This challenge will guide you through building a crowdfunding DApp on Stellar using Soroban, whether you're a new developer, an Ethereum developer, or someone not yet familiar with Rust. Stellar, with its open financial network technology, simplifies the process. Unlike traditional crowdfunding platforms, decentralized applications (DApps) enable users to pledge funds directly to campaigns from their digital wallets, eliminating the need for intermediaries.

In this final section, we’ll walk through building a crowdfunding smart contract on the Stellar blockchain.

Bus-stop0: 📦 Install 📚​

Stellar smart contracts are small programs written in the Rust programming language.

To build and develop contracts you need only a couple prerequisites:

A Rust toolchain

Node v18: Download Node

Stellar CLI

Freighter Wallet:[ Freighter Wallet](https://freighter.app/

Installing

​Linux, macOS, or other Unix-like OS​

If you use macOS, Linux, or another Unix-like OS, the simplest method to install a Rust toolchain is to install rustup. Install rustup with the following command.

  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Enter fullscreen mode Exit fullscreen mode

Then, install soroban-cli alias by running the following command.

cargo install_soroban
Enter fullscreen mode Exit fullscreen mode

Soroban CLI is the command line interface to Soroban. It allows you to build, deploy, and interact with smart contracts, configure identities, generate key pairs, manage networks, and more.

Set Up the Soroban Contract

cargo new --lib soroban_crowdfund
  cd soroban_crowdfund
Enter fullscreen mode Exit fullscreen mode

Crowd Fund workflow

User creates a campaign.

Users can pledge, transferring their token to a campaign.

After the campaign ends, the campaign creator can claim the funds if the total amount pledged is more than the campaign goal.

Otherwise, campaign did not reach it's goal, users can withdraw their pledge

Create a Campaign:

The process begins when a user, referred to as the "campaign creator," initiates a new crowdfunding campaign.

The campaign creator specifies important details such as the fundraising goal (target amount), the deadline for the campaign, and the type of token accepted.


          #[contractimpl]
          impl Crowdfund {
              pub fn initialize(
                  e: Env,
                  recipient: Address,
                  deadline: u64,
                  target_amount: i128,
                  token: Address,
              ) {
                  assert!(
                      !e.storage().instance().has(&DataKey::Recipient),
                      "already initialized"
                  );

                  e.storage().instance().set(&DataKey::Recipient, &recipient);
                  e.storage()
                      .instance()
                      .set(&DataKey::RecipientClaimed, &false);
                  e.storage()
                      .instance()
                      .set(&DataKey::Started, &get_ledger_timestamp(&e));
                  e.storage().instance().set(&DataKey::Deadline, &deadline);
                  e.storage().instance().set(&DataKey::Target, &target_amount);
                  e.storage().instance().set(&DataKey::Token, &token);
              }
          }

Enter fullscreen mode Exit fullscreen mode

Pledge Tokens:

Users interested in supporting the campaign can pledge their tokens by transferring them to the campaign's smart contract.

Each pledge is securely stored in the contract, and users can track their contributions.

#[contractimpl]
  impl Crowdfund {
      pub fn deposit(e: Env, user: Address, amount: i128) {
          user.require_auth();
          assert!(amount > 0, "amount must be positive");
          assert!(get_state(&e) == State::Running, "sale is not running");
          let token_id = get_token(&e);
          let current_target_met = target_reached(&e, &token_id);

          let recipient = get_recipient(&e);
          assert!(user != recipient, "recipient may not deposit");

          let balance = get_user_deposited(&e, &user);
          set_user_deposited(&e, &user, &(balance + amount));

          let client = token::Client::new(&e, &token_id);
          client.transfer(&user, &e.current_contract_address(), &amount);

          let contract_balance = get_balance(&e, &token_id);

          // emit events
          events::pledged_amount_changed(&e, contract_balance);
          if !current_target_met && target_reached(&e, &token_id) {
              // only emit the target reached event once on the pledge that triggers target to be met
              events::target_reached(&e, contract_balance, get_target_amount(&e));
          }
      }
  }
Enter fullscreen mode Exit fullscreen mode

Campaign Success:

After the campaign ends, if the total amount pledged by all users exceeds the campaign's goal, the campaign creator can claim the funds.

The funds are transferred from the smart contract to the campaign creator’s account.

  #[contractimpl]
  impl Crowdfund {
      pub fn withdraw(e: Env, to: Address) {
          let state = get_state(&e);
          let recipient = get_recipient(&e);

          match state {
              State::Running => {
                  panic!("sale is still running")
              }
              State::Success => {
                  assert!(
                      to == recipient,
                      "sale was successful, only the recipient may withdraw"
                  );
                  assert!(
                      !get_recipient_claimed(&e),
                      "sale was successful, recipient has withdrawn funds already"
                  );

                  let token = get_token(&e);
                  transfer(&e, &recipient, &get_balance(&e, &token));
                  set_recipient_claimed(&e);
              }
              _ => {}
          };
      }
  }
Enter fullscreen mode Exit fullscreen mode

Campaign Failure:

If the campaign does not reach its goal by the deadline, the campaign is considered unsuccessful.
In this case, users who pledged tokens can withdraw their contributions from the smart contract.

#[contractimpl]
impl Crowdfund {
    pub fn withdraw(e: Env, to: Address) {
        let state = get_state(&e);
        let recipient = get_recipient(&e);

        match state {
            State::Expired => {
                assert!(
                    to != recipient,
                    "sale expired, the recipient may not withdraw"
                );

                // Withdraw full amount
                let balance = get_user_deposited(&e, &to);
                set_user_deposited(&e, &to, &0);
                transfer(&e, &to, &balance);

                // emit events
                let token_id = get_token(&e);
                let contract_balance = get_balance(&e, &token_id);
                events::pledged_amount_changed(&e, contract_balance);
            }
            _ => {}
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Implementing the Crowdfunding Smart Contract

Let's explain how this process can be implemented in Soroban using Rust.

Data Structures

We’ll define the data keys for storing campaign details like the recipient, deadline, target amount, token type, and pledges.

#[derive(Clone)]
#[contracttype]
pub enum DataKey {
    Deadline,
    Recipient,
    Started,
    Target,
    Token,
    User(Address),
    RecipientClaimed,
}
Enter fullscreen mode Exit fullscreen mode

Initializing a Campaign

The initialize the function sets up the campaign. It stores the recipient, deadline, target amount, and token type in the contract's storage.

This function is called only once when the campaign is created.


        #[contractimpl]
        impl Crowdfund {
            pub fn deposit(e: Env, user: Address, amount: i128) {
                user.require_auth();
                assert!(amount > 0, "amount must be positive");
                assert!(get_state(&e) == State::Running, "sale is not running");
                let token_id = get_token(&e);
                let current_target_met = target_reached(&e, &token_id);

                let recipient = get_recipient(&e);
                assert!(user != recipient, "recipient may not deposit");

                let balance = get_user_deposited(&e, &user);
                set_user_deposited(&e, &user, &(balance + amount));

                let client = token::Client::new(&e, &token_id);
                client.transfer(&user, &e.current_contract_address(), &amount);

                let contract_balance = get_balance(&e, &token_id);

                // emit events
                events::pledged_amount_changed(&e, contract_balance);
                if !current_target_met && target_reached(&e, &token_id) {
                    // only emit the target reached event once on the pledge that triggers target to be met
                    events::target_reached(&e, contract_balance, get_target_amount(&e));
                }
            }
        }
Enter fullscreen mode Exit fullscreen mode

Pledging Tokens

The deposit function allows users to pledge tokens.

Users specify the amount they want to pledge, and the smart contract securely transfers these tokens from the user to the contract.

 #[contractimpl]
  impl Crowdfund {
      pub fn deposit(e: Env, user: Address, amount: i128) {
          user.require_auth();
          assert!(amount > 0, "amount must be positive");
          assert!(get_state(&e) == State::Running, "sale is not running");
          let token_id = get_token(&e);
          let current_target_met = target_reached(&e, &token_id);

          let recipient = get_recipient(&e);
          assert!(user != recipient, "recipient may not deposit");

          let balance = get_user_deposited(&e, &user);
          set_user_deposited(&e, &user, &(balance + amount));

          let client = token::Client::new(&e, &token_id);
          client.transfer(&user, &e.current_contract_address(), &amount);

          let contract_balance = get_balance(&e, &token_id);

          // emit events
          events::pledged_amount_changed(&e, contract_balance);
          if !current_target_met && target_reached(&e, &token_id) {
              // only emit the target reached event once on the pledge that triggers target to be met
              events::target_reached(&e, contract_balance, get_target_amount(&e));
          }
      }
  }
Enter fullscreen mode Exit fullscreen mode

Checking Campaign State

The contract regularly checks the state of the campaign.

If the current time is before the deadline and the target amount has not been reached, the campaign continues to run.

If the target is reached, the campaign is marked as successful. If the deadline passes without reaching the target, the campaign is marked as expired.

fn get_state(e: &Env) -> State {
      let deadline = get_deadline(e);
      let token_id = get_token(e);
      let current_timestamp = get_ledger_timestamp(e);

      if current_timestamp < deadline {
          return State::Running;
      }

      if get_recipient_claimed(e) || target_reached(e, &token_id) {
          return State::Success;
      }

      State::Expired
  }

Enter fullscreen mode Exit fullscreen mode

Now that we've explored how crowdfunding smart contracts work on Stellar, let's dive into this beautifully crowdfunding app built on the Stellar network . With the basics learnt, it's time to clone the code example built by stellar and fork it and build something even better on Stellar.

git clone https://github.com/stellar/soroban-dapps-challenge.git 
cd soroban-dapps-challenge 
git checkout crowdfund
Enter fullscreen mode Exit fullscreen mode

Conclusion
In this tutorial, we’ve successfully guide you through building a crowdfunding DApp on Stellar using . This project demonstrates how developers can leverage what they have learnt to build better on Stellar with Soroban, using Rust for secure and efficient smart contracts.

  1. References Rust Programming Language

Soroban Documentation

Ethereum Smart Contract Tutorial

Contributors
@jesdi @prince_eniola

Top comments (0)