DEV Community

Justin Paul
Justin Paul

Posted on

HelpChain: Securely Empowering Change with Stellar Blockchain, Transparent Donations, and Carbon Offset NFTs

This is a submission for the Build Better on Stellar: Smart Contract Challenge : Build a dApp

What I Built

HelpChain is a decentralized application (dApp) built on the Stellar blockchain, designed to redefine how charitable activities are conducted by introducing unprecedented levels of transparency, accountability, and efficiency. The platform facilitates seamless donations, volunteering opportunities, and environmental conservation efforts, all secured by the immutable ledger of blockchain technology.

*Project Architecture - *

Image description

Core Features and Functionalities

Multi-Role System:

Admin: The central authority responsible for managing all aspects of the platform, including user roles, fund listings, and the activation of smart contracts.
Volunteer: Individuals who can register to participate in volunteering activities and contribute to the decision-making process by voting on causes.
Fund Manager: Users tasked with the oversight of fund allocation, ensuring that resources are distributed efficiently and in accordance with the platform's governance rules.
Helper: Users who can donate to listed causes and engage in the voting process to determine fund allocation.
Guest: Non-registered users who can browse available causes and make donations without needing to create an account.

Decentralized Voting and Fund Allocation:

Cause Listings: Admins have the capability to list various charitable causes, each with a detailed description and funding goal.

Voting Mechanism: Registered users can vote on their preferred causes, with each vote being recorded on the Stellar blockchain. This ensures a transparent and tamper-proof voting process.

Automated Fund Distribution: At the conclusion of each voting period, a custom Stellar smart contract automatically transfers the required funds from the pooled account to the cause that receives the highest votes. This eliminates manual intervention, reducing the possibility of human error or fraud.

Blockchain-Powered Transparency:

Transaction Recording: Every transaction, including donations, fund distributions, and NFT purchases, is logged on the Stellar blockchain. This ensures that all financial activities are transparent and publicly verifiable, fostering trust among users.

Immutable Records: The use of blockchain technology guarantees that all records are immutable, meaning they cannot be altered once they are committed to the ledger. This feature is critical for maintaining the integrity of the platform.

Carbon Offset NFTs:

Environmental Impact: Users can purchase Carbon Offset NFTs, which represent a commitment to plant a tree. Each NFT purchase is linked to an actual tree-planting initiative, with details about the tree, location, and planting status recorded on the blockchain.

NFT Minting and Tracking: The NFTs are minted on the Stellar network and include metadata that tracks the carbon offset value and the tree planting activities. This allows users to tangibly see the environmental impact of their contributions.

Volunteer Registration and Matching:

Skill-Based Matching: Volunteers can register on the platform by providing details about their skills and experience. The platform uses these details to match volunteers with causes that require their specific expertise.
Token Rewards: Upon successful completion of their volunteering activities, volunteers are rewarded with HELPXLM tokensโ€”a custom Stellar-based token. These tokens can be redeemed within the HelpChain ecosystem, providing a tangible incentive for participation.

HELPXLM Token Economy:

Token Creation: The HELPXLM token is a custom token created on the Stellar network specifically for the HelpChain platform. It serves as the primary medium of exchange and reward within the ecosystem.

HELPXLM Distribution account Public Key - GCYKTRAHF2FYLWR3WFDHHK6EZIR5XXU4NYHU6KIP33SRTAB6NDP6ZXLH

Code for creating token & making trustline

const StellarSdk = require("stellar-sdk");
const fetch = require("node-fetch");
const server = new StellarSdk.Horizon.Server(
  "https://horizon-testnet.stellar.org"
);
// Your issuing account credentials
const issuingKeys = StellarSdk.Keypair.fromSecret(
  "SCSRRIH3EELED562X366LOJHRCZZF4COJ25633H5HMFP46GFJSDYPKVP"
);

// Create a new random keypair for the distribution account
const distributionKeys = StellarSdk.Keypair.random();

console.log("Distribution Account Public Key:", distributionKeys.publicKey());
console.log("Distribution Account Secret Key:", distributionKeys.secret());

// Fund the distribution account with testnet XLM using the friendbot
const fundAccount = async () => {
  try {
    const response = await fetch(
      `https://friendbot.stellar.org?addr=${distributionKeys.publicKey()}`
    );
    const data = await response.json();
    console.log("Funded new account:", data);
  } catch (error) {
    console.error("Error funding distribution account:", error);
  }
};

// Function to create the trustline for XLMCSH token
const createTrustline = async () => {
  try {
    // Load the distribution account
    const account = await server.loadAccount(distributionKeys.publicKey());

    // Create the asset
    const asset = new StellarSdk.Asset("HELPXLM", issuingKeys.publicKey());

    // Build the transaction to create a trustline
    const transaction = new StellarSdk.TransactionBuilder(account, {
      fee: StellarSdk.BASE_FEE,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.changeTrust({
          asset: asset,
        })
      )
      .setTimeout(100)
      .build();

    // Sign the transaction
    transaction.sign(distributionKeys);

    // Submit the transaction
    const result = await server.submitTransaction(transaction);
    console.log("Trustline created for HELPXLM.");
    console.log(result);
  } catch (error) {
    console.error("An error occurred while creating the trustline:", error);
  }
};

// Function to issue the XLMCSH token
const issueToken = async () => {
  try {
    // Load issuing account
    const issuingAccount = await server.loadAccount(issuingKeys.publicKey());

    // Create the asset
    const asset = new StellarSdk.Asset("HELPXLM", issuingKeys.publicKey());

    // Build the transaction to issue the asset to the distribution account
    const transaction = new StellarSdk.TransactionBuilder(issuingAccount, {
      fee: StellarSdk.BASE_FEE,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.payment({
          destination: distributionKeys.publicKey(),
          asset: asset,
          amount: "1000", // Amount to issue
        })
      )
      .setTimeout(100)
      .build();

    // Sign the transaction
    transaction.sign(issuingKeys);

    // Submit the transaction
    const result = await server.submitTransaction(transaction);
    console.log("Success! XLMCSH Token issued.");
    console.log(result);
  } catch (error) {
    console.error("An error occurred while issuing the token:", error);
  }
};

// Function to verify the token issuance
const verifyToken = async () => {
  try {
    const account = await server.loadAccount(distributionKeys.publicKey());
    console.log("Balances for account: " + account.accountId());
    account.balances.forEach((balance) => {
      console.log(
        "Type:",
        balance.asset_type,
        ", Code:",
        balance.asset_code,
        ", Balance:",
        balance.balance
      );
    });
  } catch (e) {
    console.error("Error fetching account balances:", e);
  }
};

// Main function to execute the steps
const main = async () => {
  await fundAccount();
  await createTrustline();
  await issueToken();
  await verifyToken();
};

main();

Enter fullscreen mode Exit fullscreen mode

Token Utility: HELPXLM tokens can be used to purchase Carbon Offset NFTs, vote on causes, and participate in exclusive platform events. The token's value is backed by the growing community and the continuous addition of new features and utilities.

Demo

Experience HelpChain in action by visiting the live demo: HelpChain Live Demo

For a detailed walkthrough of the platform, watch our video demo: HelpChain Video Demo

My Code

The complete source code for HelpChain is open-source and available on GitHub:

HelpChain GitHub Repository Frontend

HelpChain GitHub Repository Backend

This repository includes a comprehensive README file with detailed installation instructions, a technical overview of the architecture, and usage guidelines. The project is licensed under the MIT License, encouraging collaboration and further development by the community.

Key Components of the Codebase:
Smart Contracts:

Voting Contract: A Stellar smart contract that manages the voting process, ensuring that each vote is securely recorded and counted. The contract also handles the automatic distribution of funds to the winning cause.

#![no_std]
use soroban_sdk::{contractimpl, Env, Symbol, Map};

pub struct VotingContract;

#[derive(Debug)]
pub enum Error {
    VotingClosed,
    AlreadyVoted,
    InvalidCause,
}

#[contractimpl]
impl VotingContract {
    pub fn init(env: Env) {
        // Initialize the voting process with some causes.
        let causes: Map<Symbol, u32> = Map::new(&env);
        env.storage().set(&Symbol::short("causes"), &causes);
    }

    pub fn vote(env: Env, voter: Symbol, cause: Symbol) -> Result<(), Error> {
        let mut causes: Map<Symbol, u32> = env.storage().get_unchecked(&Symbol::short("causes"));

        if !causes.contains_key(&cause) {
            return Err(Error::InvalidCause);
        }

        // Check if voting is still open (Assume some voting period logic here).
        if !self::is_voting_open(&env) {
            return Err(Error::VotingClosed);
        }

        // Check if the voter has already voted.
        let voted_key = Symbol::short(&format!("voted_{}", voter.to_string()));
        if env.storage().has(&voted_key) {
            return Err(Error::AlreadyVoted);
        }

        // Increment the vote count for the chosen cause.
        let count = causes.get(&cause).unwrap_or(0);
        causes.set(&cause, count + 1);
        env.storage().set(&Symbol::short("causes"), &causes);

        // Mark the voter as having voted.
        env.storage().set(&voted_key, &true);

        Ok(())
    }

    pub fn get_winner(env: Env) -> Option<Symbol> {
        let causes: Map<Symbol, u32> = env.storage().get_unchecked(&Symbol::short("causes"));
        causes.into_iter().max_by_key(|&(_, count)| count).map(|(cause, _)| cause)
    }

    fn is_voting_open(env: &Env) -> bool {
        // Logic to determine if voting is still open. Could be based on block time or an explicit flag.
        true
    }
}

Enter fullscreen mode Exit fullscreen mode

Fund Distribution Contract: A contract that ensures the safe and transparent transfer of funds from the pooled account to the cause that garners the most votes. It is designed to execute only when certain conditions are met, such as the end of a voting period.

#![no_std]
use soroban_sdk::{contractimpl, Env, Symbol, Address, Map};

pub struct FundDistributionContract;

#[derive(Debug)]
pub enum Error {
    NoWinner,
    InsufficientFunds,
    TransferFailed,
}

#[contractimpl]
impl FundDistributionContract {
    pub fn distribute_funds(env: Env, pool_account: Address, voting_contract: Address) -> Result<(), Error> {
        // Call the voting contract to get the winner cause.
        let winner: Option<Symbol> = env.invoke_contract(&voting_contract, Symbol::short("get_winner"), ());

        if winner.is_none() {
            return Err(Error::NoWinner);
        }

        let winner_cause = winner.unwrap();

        // Logic to determine the amount of funds for the winning cause.
        let fund_amount = self::get_fund_amount_for_cause(&env, &winner_cause);

        // Ensure the pool account has enough funds.
        let pool_balance = env.ledger().balance(&pool_account);
        if pool_balance < fund_amount {
            return Err(Error::InsufficientFunds);
        }

        // Transfer funds from the pool account to the winning cause's account.
        let cause_account = self::get_account_for_cause(&env, &winner_cause);
        if !env.ledger().transfer(&pool_account, &cause_account, fund_amount) {
            return Err(Error::TransferFailed);
        }

        Ok(())
    }

    fn get_fund_amount_for_cause(env: &Env, cause: &Symbol) -> u128 {
        // Logic to determine the amount of funds required for the cause.
        1000 // Example value
    }

    fn get_account_for_cause(env: &Env, cause: &Symbol) -> Address {
        // Logic to get the account address for the cause.
        Address::new([0; 32]) // Example address
    }
}

Enter fullscreen mode Exit fullscreen mode

NFT Minting Contract: This contract manages the creation and distribution of Carbon Offset NFTs, including the integration of tree-planting data into the blockchain.

#![no_std]
use soroban_sdk::{contractimpl, Env, Symbol, Address, Bytes, Map, Vec};

pub struct NFTMintingContract;

#[derive(Debug)]
pub enum Error {
    NFTAlreadyExists,
    MintingFailed,
}

#[contractimpl]
impl NFTMintingContract {
    pub fn mint_nft(env: Env, owner: Address, nft_id: Bytes, metadata: Map<Symbol, Bytes>) -> Result<(), Error> {
        let nft_key = Symbol::short(&format!("nft_{}", nft_id.to_string()));

        // Check if the NFT already exists.
        if env.storage().has(&nft_key) {
            return Err(Error::NFTAlreadyExists);
        }

        // Mint the NFT and store it on-chain with associated metadata.
        let nft = Map::new(&env);
        nft.set(&Symbol::short("owner"), &owner.to_bytes());
        nft.set(&Symbol::short("metadata"), &metadata.to_bytes());

        env.storage().set(&nft_key, &nft);

        // Emit a mint event (optional).
        env.events().publish(
            &Symbol::short("MintNFT"),
            &Vec::from_slice(&env, &[owner.to_bytes(), nft_id.clone()]),
        );

        Ok(())
    }

    pub fn get_nft(env: Env, nft_id: Bytes) -> Option<Map<Symbol, Bytes>> {
        let nft_key = Symbol::short(&format!("nft_{}", nft_id.to_string()));
        env.storage().get(&nft_key)
    }

    pub fn transfer_nft(env: Env, new_owner: Address, nft_id: Bytes) -> Result<(), Error> {
        let nft_key = Symbol::short(&format!("nft_{}", nft_id.to_string()));

        if let Some(mut nft) = env.storage().get::<Map<Symbol, Bytes>>(&nft_key) {
            nft.set(&Symbol::short("owner"), &new_owner.to_bytes());
            env.storage().set(&nft_key, &nft);
            Ok(())
        } else {
            Err(Error::MintingFailed)
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Backend Infrastructure:

Node.js Server: The backend is built using Node.js, providing a robust and scalable environment for managing API requests, user authentication, and smart contract interactions.

Stellar SDK: Integrated with the Stellar SDK to facilitate seamless communication with the Stellar blockchain, enabling the dApp to execute transactions, mint tokens, and interact with smart contracts.

PostgreSQL Database: Used for storing user data, transaction histories, and other critical information that does not need to be on-chain, ensuring a balance between performance and transparency.

Frontend Interface:

React.js: The frontend is developed using React.js, providing a responsive and intuitive user interface that is easy to navigate and interact with.

Web3 Integration: The frontend includes Web3.js integration, enabling users to interact with the blockchain directly from the browser, including sending transactions, voting on causes, and purchasing NFTs.

Responsive Design: The UI is fully responsive, ensuring that the platform is accessible across devices, including desktops, tablets, and smartphones.

Journey

Motivation
The inspiration for HelpChain arose from the need to address the prevalent issues in the charitable sector, such as lack of transparency, mismanagement of funds, and limited accountability. Traditional donation platforms often fall short in providing donors with a clear understanding of how their contributions are utilized. HelpChain aims to solve these problems by using blockchain technology to create a platform where every action is visible, every transaction is secure, and every contribution is impactful.

Implementation and Smart Contract Design
Stellar Blockchain Integration
HelpChain leverages the Stellar blockchain for its fast, low-cost transactions, making it ideal for handling frequent, micro-level transactions like donations. Stellar's consensus protocol also ensures that transactions are confirmed within seconds, providing a seamless user experience. By using Stellar, HelpChain is able to offer a platform that is both efficient and accessible, without the high fees typically associated with blockchain transactions.

Smart Contract Architecture

Voting Contract:

Design: The voting contract is designed to be lightweight yet secure, ensuring that each vote is recorded immutably on the blockchain. The contract includes logic to prevent double voting and ensures that the voting results are accurate and tamper-proof.

Execution: Upon the end of a voting period, the contract automatically triggers the fund distribution process, transferring funds to the winning cause without requiring manual intervention. This automation reduces the risk of errors and fraud.

Fund Distribution Contract:

Logic: This contract is responsible for the secure transfer of funds from the platform's pooled account to the cause that wins the voting process. The contract is designed to handle large volumes of transactions, ensuring that funds are distributed quickly and efficiently.

Security: The contract includes several security measures, such as multi-signature verification, to ensure that funds are only released under the correct conditions.

NFT Minting and Management:

Carbon Offset NFTs: The NFT minting process is fully automated through a Stellar smart contract that ensures each NFT is unique and includes metadata about the tree planting initiative. The contract also includes functionality for tracking the lifecycle of each NFT, from minting to the planting of the associated tree.

Transparency: All data related to NFT transactions, including purchase details and tree planting records, are stored on the blockchain, ensuring complete transparency and accountability.

Challenges and Solutions

Throughout the development of HelpChain, several technical challenges were encountered and addressed:

Smart Contract Complexity: Developing the smart contracts to handle voting, fund distribution, and NFT minting required careful consideration of security, efficiency, and scalability. Multiple iterations of the contracts were tested to ensure they met the high standards required for a transparent and secure platform.

User Experience: One of the key challenges was designing a user interface that is intuitive and accessible to users who may not be familiar with blockchain technology. Extensive user testing and feedback were incorporated to create a streamlined and user-friendly interface.

Security Measures: Ensuring the security of user funds and data was paramount. The platform was built with robust security protocols, including encryption, multi-signature wallets, and secure key management practices, to protect against potential threats.

Learnings and Key Takeaways

Building HelpChain provided deep insights into the potential of blockchain technology to drive transparency and accountability in the charitable sector. The project demonstrated how smart contracts could automate and secure complex processes, reducing the need for intermediaries and minimizing the risk of fraud. Additionally, the integration of NFTs for environmental sustainability showcased the versatility of blockchain in addressing global challenges beyond finance.

Future Directions

HelpChain is envisioned as a continuously evolving platform with several enhancements planned for the future:

Mobile Application: Development of native mobile applications for iOS and Android to increase accessibility and user engagement.
Global Partnerships: Establishing partnerships

Top comments (0)