Skip to content

CryptoSmartNow/bs_solana2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

How It Works

If you are familiar with the older EVM (Ethereum) version of Bitsave, you know it used a "Factory" pattern where the main contract deployed unique "Child Contracts" for every single user to hold their funds.

Solana works fundamentally differently. On Solana, the logic (the Program) is completely separated from the data (the State). We don't deploy new contracts for users. Instead, a single, highly optimized Anchor program securely partitions and manages data using Program Derived Addresses (PDAs).

There are three core state accounts you need to know about:

  1. GlobalState: This is the master configuration account. It exists once per protocol deployment. It holds global variables like the total_value_locked, supported token mints, and protocol fees.
  2. UserVault: When a user registers with Bitsave, the protocol derives a unique UserVault PDA mathematically linked to their wallet address. This account acts as their personal profile on the protocol.
  3. Saving: Instead of stuffing all of a user's savings plans into a massive, expensive list, each specific savings goal (e.g., "Car Fund", "Vacation") gets its very own PDA. This account stores the exact amount saved, the maturity date, early withdrawal penalties, and validity state.

User Flow

When a user interacts with the protocol, they follow a standard lifecycle:

1. Joining the Protocol (join_bitsave)

Before a user can save, they must join. They call this instruction to pay a small registration fee (in SOL) to the protocol admin. In return, the program initializes their personalized UserVault PDA.

2. Creating a Savings Plan (create_saving)

The user specifies a name for their goal, a maturity time (when they are allowed to withdraw without penalties), and an amount.

  • They can save native SOL or any supported SPL Token.
  • The funds are securely moved from their personal wallet into a Protocol Vault Account that only the Bitsave program can control (specifically, a PDA derived for their vault).
  • A new Saving PDA is created to track this specific goal.

3. Topping Up (increment_saving)

If a user wants to add more funds to a goal before it matures, they call this instruction. It pulls more funds from their wallet into the protocol vault and updates the total balance in the Saving account.

4. Reaching the Goal (withdraw_saving)

When the user is ready, they call withdraw.

  • If they waited until maturity: The program returns their full saved funds back to their wallet.
  • If they withdrew early: The program applies the penalty percentage they agreed to upon creation, deducts it from their savings, and sends the remaining balance back to their wallet.
  • In both cases, the Saving PDA is closed, and its rent is returned to the user.

Interconnection & Security

  • No Middlemen: The Bitsave program does not hold user funds directly in its main execution account. Instead, it creates accounts that are mathematically owned by the user's UserVault PDA.
  • Explicit Validation: To prevent sophisticated attacks, the program explicitly verifies that any provided SPL Token accounts match the exact mint and ownership expected by the user's vault before interacting with them.
  • Cross-Program Invocations (CPIs): When funds move, Bitsave doesn't do the accounting itself. It securely asks the native Solana System Program (for SOL) or the SPL Token Program (for tokens) to perform the transfer.
  • Mathematical Proofs: You cannot access someone else's savings. Because a Saving PDA is derived using [User Wallet Address + Vault Address + Saving Name], the Solana runtime guarantees that only the user holding the private keys to that wallet can interact with or withdraw those specific funds.


Next.js / React Integration Guide

Integrating the Bitsave protocol into a modern frontend is straightforward using Anchor's TypeScript client.

1. Prerequisites & Setup

Install the required Solana and Anchor libraries in your Next.js/React project:

npm install @solana/web3.js @coral-xyz/anchor @solana/wallet-adapter-react @solana/spl-token

You will also need the IDL (Interface Description Language) file generated by Anchor (found in target/idl/bitsave.json after running anchor build). This file tells the frontend exactly how to talk to the smart contract.

2. Initializing the Provider

In your React component, use the wallet adapter to get the user's connection and initialize the Anchor Program.

import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
import { AnchorProvider, Program, setProvider } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import idl from "./path/to/bitsave.json"; // Your IDL file
import { Bitsave } from "./path/to/bitsave_types"; // Optional: TypeScript types

const PROGRAM_ID = new PublicKey(
  "8p4LcCZUsg53vjBP6F2cuWUuZNtHqX8v2EF72oQFkoLn",
);

export function useBitsaveProgram() {
  const { connection } = useConnection();
  const wallet = useAnchorWallet();

  if (!wallet) return null;

  const provider = new AnchorProvider(connection, wallet, {
    preflightCommitment: "processed",
  });
  setProvider(provider);

  const program = new Program(
    idl as any,
    PROGRAM_ID,
    provider,
  ) as Program<Bitsave>;

  return program;
}

3. Deriving PDAs on the Client

Before you can send a transaction, your frontend needs to calculate the specific PDA addresses for the user.

import { PublicKey } from "@solana/web3.js";

// Get the Global State Address
const [globalStatePDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("global_state")],
  PROGRAM_ID,
);

// Get the current User's Vault Address
const [userVaultPDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("user_vault"), wallet.publicKey.toBuffer()],
  PROGRAM_ID,
);

// Get a specific Savings Plan Address by its name
const savingName = "New Car Fund";
const [savingPDA] = PublicKey.findProgramAddressSync(
  [Buffer.from("saving"), userVaultPDA.toBuffer(), Buffer.from(savingName)],
  PROGRAM_ID,
);

4. Executing Transactions

Use the program.methods API to construct and send transactions.

Example: Joining the Protocol

const joinProtocol = async () => {
  const program = useBitsaveProgram();

  try {
    const tx = await program.methods
      .joinBitsave()
      .accounts({
        globalState: globalStatePDA,
        userVault: userVaultPDA,
        user: wallet.publicKey,
        adminAccount: new PublicKey("ADMIN_WALLET_ADDRESS_HERE"),
      })
      .rpc();

    console.log("Successfully joined! Tx:", tx);
  } catch (error) {
    console.error("Failed to join:", error);
  }
};

Example: Creating a Saving Plan (Native SOL)

import { BN } from "@coral-xyz/anchor";
import { SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
  TOKEN_PROGRAM_ID,
  ASSOCIATED_TOKEN_PROGRAM_ID,
} from "@solana/spl-token";

const createSolSaving = async (
  name: string,
  amountInSol: number,
  daysToMaturity: number,
) => {
  const program = useBitsaveProgram();

  const amount = new BN(amountInSol * LAMPORTS_PER_SOL);
  const maturityTime = new BN(
    Math.floor(Date.now() / 1000) + daysToMaturity * 24 * 60 * 60,
  );
  const penaltyPercentage = 10;
  const safeMode = false;

  try {
    const tx = await program.methods
      .createSaving(name, maturityTime, penaltyPercentage, safeMode, amount)
      .accounts({
        globalState: globalStatePDA,
        userVault: userVaultPDA,
        saving: savingPDA,
        user: wallet.publicKey,
        adminAccount: new PublicKey("ADMIN_WALLET_ADDRESS_HERE"),
        tokenMint: PublicKey.default,
        userTokenAccount: wallet.publicKey,
        vaultTokenAccount: userVaultPDA,
        systemProgram: SystemProgram.programId,
        tokenProgram: TOKEN_PROGRAM_ID,
        associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
      })
      .rpc();

    console.log("Saving plan created! Tx:", tx);
  } catch (error) {
    console.error("Failed to create saving:", error);
  }
};

5. Fetching and Displaying State

const fetchUserData = async () => {
  const program = useBitsaveProgram();

  try {
    // Fetch the user's main profile
    const vaultData = await program.account.userVault.fetch(userVaultPDA);
    console.log("User Vault Owner:", vaultData.owner.toBase58());

    // Fetch a specific savings plan details
    const savingData = await program.account.saving.fetch(savingPDA);
    console.log("Amount Saved:", savingData.amount.toString());
    console.log(
      "Maturity Time:",
      new Date(savingData.maturityTime.toNumber() * 1000).toLocaleString(),
    );
  } catch (error) {
    console.error("Error fetching data. User might not be registered.", error);
  }
};

About

bitsave solana migration

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors