Skip to content

Liquid Staking #37

Description

@Nathanofzion

Key Points Planes Propals Of $LpZi

  • DeFi offers decentralized, accessible, and transparent financial services with potential for higher returns.
  • Zig3 v3, a Stellar-based DEX, introduces Liquid Staking on testnet, offering high APYs (20-91%).
  • Liquid Staking lets users stake assets like $Zi for rewards while keeping them liquid via $LpZi tokens.
  • Users with XLM Zi or USDC Zi liquidity pools can stake for additional rewards, enhancing DeFi flexibility.

Introduction to DeFi and Zig3 v3

DeFi, or Decentralized Finance, uses blockchain to provide financial services without intermediaries, making it accessible and transparent. Zig3 v3 is a decentralized exchange (DEX) on the Stellar network’s Soroban platform, offering features like token swapping and liquidity provision. Its upcoming Liquid Staking feature on testnet is set to revolutionize user rewards.

Liquid Staking Explained

Liquid Staking allows users to stake assets, like $Zi tokens, and receive $sLpZi, a liquid token they can use elsewhere in DeFi while earning high APYs (20-91%). This is currently a proposal for a new Staking Smart contract on testnet, with mainnet launch expected soon friendly managing asset. Users with XLM Zi or USDC Zi liquidity pools can also stake these for enhanced rewards.

Benefits and Conclusion

Zig3 v3’s Liquid Staking offers high returns and liquidity, making it a game-changer for DeFi users. It combines security via Soroban smart contracts with accessibility, aligning with DeFi’s core benefits.


The Liquid Staking Products In A Modal

Based on the Zig3 v3 Liquid Staking proposal on the Stellar Soroban platform and the requirement that users must have an equal amount of capital in a liquidity pool before staking, I’ve redesigned three user-focused product offerings. These products cater to beginner, intermediate, and advanced DeFi users, align with the high APYs (20–91%), liquidity, security, and accessibility, and enforce the prerequisite of equal capital in a liquidity pool (e.g., XLM-Zi or USDC-Zi). Each product is presented as a standalone modal with a description, key features, and a “Start Staking” button for side-by-side comparison in a user interface.

Product 1: Zi Starter Staking

Modal Title: Zi Starter Staking – Begin Your DeFi Journey
Description: Ideal for beginners, Zi Starter Staking lets you earn 20–40% APY by staking $Zi tokens after providing equal capital to an XLM-Zi liquidity pool. Receive $LpZi tokens to keep your assets liquid while earning rewards on Zig3 v3’s secure Soroban platform. Start your DeFi journey with ease and transparency.
Key Features:

  • APY: 20–40% (stable returns for new users).
  • Liquidity Pool Requirement: Equal capital in XLM-Zi pool (e.g., 50 $Zi + equivalent XLM).
  • Minimum Stake: 100 $Zi (post-liquidity pool contribution).
  • Liquidity: $LpZi tokens for trading or DeFi use.
  • Security: Soroban smart contracts with passkey ID signing.
  • Best For: DeFi newcomers seeking accessible, high-yield staking.
Button: [Start Staking]
Rationale: Targets beginners with a low entry point and conservative APY. The equal capital XLM-Zi pool requirement ensures pool participation, aligning with the query’s emphasis on liquidity pools while maintaining simplicity.

Product 2: XLM-Zi Liquidity Enhancer

Modal Title: XLM-Zi Liquidity Enhancer – Boost Your Rewards
Description: Designed for intermediate users, XLM-Zi Liquidity Enhancer offers 40–70% APY for staking XLM-Zi liquidity pool tokens after contributing equal capital to the pool. Earn $LpZi tokens for DeFi flexibility, backed by Zig3 v3’s secure Soroban smart contracts. Amplify your returns with this balanced staking option.
Key Features:

  • APY: 40–70% (higher yield for pool-based staking).
  • Liquidity Pool Requirement: Equal capital in XLM-Zi pool (e.g., 250 $Zi + equivalent XLM).
  • Minimum Stake: 500 $Zi equivalent in XLM-Zi pool tokens.
  • Liquidity: $LpZi tokens for trading, lending, or collateral.
  • Security: Audited Soroban contracts with transparent operations.
  • Best For: Users familiar with liquidity pools seeking enhanced rewards.
Button: [Start Staking]
Rationale: Focuses on intermediate users with experience in DEXs. The equal capital pool requirement leverages the query’s note on XLM-Zi pools for “twice the value” rewards, offering higher APYs with liquidity.

Product 3: USDC-Zi Pro Staking

Modal Title: USDC-Zi Pro Staking – Elite DeFi Yields
Description: Tailored for advanced DeFi users, USDC-Zi Pro Staking delivers 70–91% APY by staking USDC-Zi liquidity pool tokens after providing equal capital to the pool. Use $LpZi tokens to optimize DeFi strategies, secured by Zig3 v3’s scalable Soroban platform. Maximize returns with stablecoin-backed staking.
Key Features:

  • APY: 70–91% (top-tier yields for experts).
  • Liquidity Pool Requirement: Equal capital in USDC-Zi pool (e.g., 500 $Zi + equivalent USDC).
  • Minimum Stake: 1,000 $Zi equivalent in USDC-Zi pool tokens.
  • Liquidity: $LpZi tokens for advanced DeFi applications.
  • Security: Soroban’s robust contracts with passkey ID authentication.
  • Best For: DeFi pros targeting high returns with stablecoin exposure.
Button: [Start Staking]
Rationale: Targets advanced users with significant capital and DeFi expertise. The equal capital USDC-Zi pool requirement ensures stability and high yields, aligning with the query’s flexibility and reward potential.

Side-by-Side Modal Layout (UI Concept)

Each product is displayed in a modal window for a clean, comparative interface:

  • Modal Design:
    • Header: Bold title (e.g., “Zi Starter Staking”).
    • Content: Short description (2–3 sentences) outlining the product and pool requirement.
    • Feature List: Bullet points for APY, liquidity pool requirement, minimum stake, liquidity, security, and target user.
    • Button: Prominent “Start Staking” button linking to Zig3 v3’s staking interface (e.g., passkey wallet or Soroban contract, with pool verification).
  • Layout: Arrange modals horizontally (desktop) or vertically (mobile) with consistent styling (e.g., borders, padding, hover effects). Include a note or tooltip on the liquidity pool prerequisite and testnet/mainnet status (testnet as of June 13, 2025, mainnet imminent).

Notes

  • Liquidity Pool Requirement: Each product requires users to contribute equal capital to a specified liquidity pool (XLM-Zi for Products 1 and 2, USDC-Zi for Product 3) before staking. This ensures users engage with Zig3 v3’s DEX features, as per the query’s focus on pool-based rewards.
  • APY Ranges: Segmented from 20–91% (per query and @DokCrypto
’s X post, June 13, 2025) to reflect risk/reward profiles. High APYs carry risks like smart contract bugs or market volatility.
  • Security & Accessibility: Emphasizes Soroban’s secure contracts and Zig3 v3’s passkey ID, aligning with DeFi’s transparency and inclusivity.
  • Liquidity: $LpZi tokens maintain flexibility, a core liquid staking benefit.
  • Testnet Status: Liquid Staking is on testnet as of June 13, 2025; users should note mainnet rewards are pending.
    These products cater to a range of DeFi users, enforcing equal capital in liquidity pools to enhance Zig3 v3’s ecosystem engagement while offering high returns and liquidity. For more on Zig3 v3, visit zioncoin.org.uk.

Soroban smart contracts for the three products (Zi Starter Staking, XLM-Zi Liquidity Enhancer, USDC-Zi Pro Staking) to incorporate the new requirements:

  • APY Reduction: APY decreases by 1/8 annually.
  • Termination Conditions: Staking ends when funds are fully distributed or if the user’s liquidity pool (LP) contribution falls below the product’s required amount for 1 month. Users have a 3-month grace period without yield to restore LP volume, or the contract is terminated, and they lose staked funds.
  • Minimum Stake Period: Users must stake for at least 1 year, after which they can withdraw or continue staking until funds are depleted or LP requirements are unmet.
  • Liquidity Pool Requirement: Users must maintain equal capital in a liquidity pool (XLM-Zi or USDC-Zi) before and during staking.
    The contracts use the Soroban Rust SDK and Stellar SDK, align with the provided package.json dependencies (e.g., @soroban-react/*, @stellar/stellar-sdk), and are optimized for Soroban testnet (as of June 13, 2025). I’ve also updated the UI modal layout using Next.js and Chakra UI for side-by-side (desktop) or stacked (mobile) display, with a “Start Staking” button per product.

Smart Contracts (Rust for Soroban)

I’ve designed a modular staking contract with configurable parameters for each product and a liquidity pool checker contract to enforce LP requirements. The contracts include APY reduction, termination logic, minimum staking period, and grace period handling.

1. Modular Staking Contract (liquid_staking.rs)

This contract manages staking, rewards, $LpZi issuance, withdrawals, and LP monitoring, with dynamic APY reduction and termination conditions.

use soroban_sdk::{contract, contractimpl, Address, Env, Symbol, Vec, Map, token, BytesN, panic_with_error};
use stellar_sdk::types::{Asset, Balance};

// Custom error types
#[derive(Clone, Copy)]
pub enum StakingError {
    NotInitialized = 1,
    InsufficientPoolContribution = 2,
    BelowMinimumStake = 3,
    InsufficientStake = 4,
    MinimumStakePeriodNotMet = 5,
    ContractTerminated = 6,
    GracePeriodExpired = 7,
}

// Constants
const SECONDS_PER_YEAR: u128 = 365 * 24 * 60 * 60;
const GRACE_PERIOD: u128 = 3 * 30 * 24 * 60 * 60; // 3 months
const WARNING_PERIOD: u128 = 30 * 24 * 60 * 60; // 1 month
const MIN_STAKE_PERIOD: u128 = SECONDS_PER_YEAR; // 1 year

#[contract]
pub struct LiquidStakingContract;

#[contractimpl]
impl LiquidStakingContract {
    // Initialize contract with product-specific parameters
    pub fn initialize(
        env: Env,
        admin: Address,
        zi_token: Address, // $Zi or pool token
        lpzi_token: Address, // $LpZi token
        pool_address: Address, // XLM-Zi or USDC-Zi pool
        apy_min: u32, // Initial min APY (e.g., 20%)
        apy_max: u32, // Initial max APY (e.g., 40%)
        min_stake: u128, // Minimum stake in $Zi or pool tokens
        pool_min_zi: u128, // Minimum $Zi in pool for equal capital
        total_rewards: u128, // Total reward pool for distribution
    ) {
        env.storage().instance().set(&Symbol::new(&env, "admin"), &admin);
        env.storage().instance().set(&Symbol::new(&env, "zi_token"), &zi_token);
        env.storage().instance().set(&Symbol::new(&env, "lpzi_token"), &lpzi_token);
        env.storage().instance().set(&Symbol::new(&env, "pool"), &pool_address);
        env.storage().instance().set(&Symbol::new(&env, "apy_min"), &apy_min);
        env.storage().instance().set(&Symbol::new(&env, "apy_max"), &apy_max);
        env.storage().instance().set(&Symbol::new(&env, "min_stake"), &min_stake);
        env.storage().instance().set(&Symbol::new(&env, "pool_min_zi"), &pool_min_zi);
        env.storage().instance().set(&Symbol::new(&env, "total_rewards"), &total_rewards);
        env.storage().instance().set(&Symbol::new(&env, "distributed_rewards"), &0u128);
        env.storage().instance().set(&Symbol::new(&env, "start_time"), &env.ledger().timestamp());
        env.storage().instance().set(&Symbol::new(&env, "stakes"), &Map::<Address, u128>::new(&env));
        env.storage().instance().set(&Symbol::new(&env, "stake_start_time"), &Map::<Address, u128>::new(&env));
        env.storage().instance().set(&Symbol::new(&env, "lp_warning"), &Map::<Address, u128>::new(&env));
        env.storage().instance().set(&Symbol::new(&env, "terminated"), &Map::<Address, bool>::new(&env));
    }

    // Check equal capital in liquidity pool
    pub fn check_pool_contribution(env: Env, user: Address, pool_address: Address, pool_min_zi: u128) -> bool {
        let pool_client = PoolContractClient::new(&env, &pool_address);
        let zi_balance = pool_client.get_user_zi_balance(&user);
        let other_balance = pool_client.get_user_other_balance(&user); // XLM or USDC
        zi_balance >= pool_min_zi && other_balance >= pool_min_zi
    }

    // Get current APY based on years elapsed
    fn get_current_apy(env: Env, apy_min: u32, apy_max: u32) -> (u32, u32) {
        let start_time = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "start_time"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let years_elapsed = (env.ledger().timestamp() - start_time) / SECONDS_PER_YEAR;
        let reduction_factor = 7u32.pow(years_elapsed as u32) / 8u32.pow(years_elapsed as u32); // 1/8 reduction per year
        (
            apy_min * reduction_factor,
            apy_max * reduction_factor,
        )
    }

    // Check if contract is terminated for user
    fn check_termination(env: Env, user: Address) {
        let pool_address = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "pool"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let pool_min_zi = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "pool_min_zi"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let mut lp_warning = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "lp_warning"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let mut terminated = env.storage().instance().get::<_, Map<Address, bool>>(&Symbol::new(&env, "terminated"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));

        if !Self::check_pool_contribution(env.clone(), user.clone(), pool_address, pool_min_zi) {
            let warning_start = lp_warning.get(&user).unwrap_or(0);
            let current_time = env.ledger().timestamp();
            if warning_start == 0 {
                // Start warning period
                lp_warning.set(user.clone(), current_time);
            } else if current_time - warning_start >= WARNING_PERIOD && current_time - warning_start < GRACE_PERIOD {
                // In grace period, no action needed
            } else if current_time - warning_start >= GRACE_PERIOD {
                // Terminate contract
                terminated.set(user.clone(), true);
                let mut stakes = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stakes"))
                    .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
                stakes.set(user.clone(), 0); // Clear stake
                env.storage().instance().set(&Symbol::new(&env, "stakes"), &stakes);
            }
            env.storage().instance().set(&Symbol::new(&env, "lp_warning"), &lp_warning);
            env.storage().instance().set(&Symbol::new(&env, "terminated"), &terminated);
        } else {
            // Reset warning if LP is restored
            lp_warning.set(user.clone(), 0);
            env.storage().instance().set(&Symbol::new(&env, "lp_warning"), &lp_warning);
        }
    }

    // Stake tokens
    pub fn stake(env: Env, user: Address, amount: u128) {
        user.require_auth(); // Passkey ID authentication
        let pool_address = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "pool"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let pool_min_zi = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "pool_min_zi"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let min_stake = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "min_stake"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let zi_token = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "zi_token"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let lpzi_token = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "lpzi_token"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let terminated = env.storage().instance().get::<_, Map<Address, bool>>(&Symbol::new(&env, "terminated"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));

        if terminated.get(&user).unwrap_or(false) {
            panic_with_error!(&env, StakingError::ContractTerminated);
        }
        if !Self::check_pool_contribution(env.clone(), user.clone(), pool_address, pool_min_zi) {
            panic_with_error!(&env, StakingError::InsufficientPoolContribution);
        }
        if amount < min_stake {
            panic_with_error!(&env, StakingError::BelowMinimumStake);
        }

        // Transfer tokens to contract
        let token_client = token::Client::new(&env, &zi_token);
        token_client.transfer(&user, &env.current_contract_address(), &amount);

        // Mint $LpZi tokens (1:1)
        let lpzi_client = token::Client::new(&env, &lpzi_token);
        lpzi_client.mint(&user, &amount);

        // Update stake and start time
        let mut stakes = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stakes"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let current_stake = stakes.get(&user).unwrap_or(0);
        stakes.set(user.clone(), current_stake + amount);
        env.storage().instance().set(&Symbol::new(&env, "stakes"), &stakes);
        let mut stake_start_time = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stake_start_time"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        if stake_start_time.get(&user).unwrap_or(0) == 0 {
            stake_start_time.set(user.clone(), env.ledger().timestamp());
        }
        env.storage().instance().set(&Symbol::new(&env, "stake_start_time"), &stake_start_time);
    }

    // Claim rewards
    pub fn claim_rewards(env: Env, user: Address) {
        user.require_auth();
        Self::check_termination(env.clone(), user.clone());

        let stakes = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stakes"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let stake = stakes.get(&user).unwrap_or(0);
        let apy_min = env.storage().instance().get::<_, u32>(&Symbol::new(&env, "apy_min"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let apy_max = env.storage().instance().get::<_, u32>(&Symbol::new(&env, "apy_max"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let lpzi_token = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "lpzi_token"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let total_rewards = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "total_rewards"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let distributed_rewards = env.storage().instance().get::<_, u128>(&Symbol::new(&env, "distributed_rewards"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let stake_start_time = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stake_start_time"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let last_time = stake_start_time.get(&user).unwrap_or(0);

        if distributed_rewards >= total_rewards {
            return; // No rewards left
        }

        // Calculate rewards with current APY
        let (current_apy_min, current_apy_max) = Self::get_current_apy(env.clone(), apy_min, apy_max);
        let current_time = env.ledger().timestamp();
        let time_elapsed = current_time - last_time;
        let avg_apy = (current_apy_min + current_apy_max) / 2;
        let reward = (stake * avg_apy as u128 * time_elapsed) / (SECONDS_PER_YEAR * 100);

        // Cap reward to remaining pool
        let remaining_rewards = total_rewards - distributed_rewards;
        let final_reward = if reward > remaining_rewards { remaining_rewards } else { reward };

        if final_reward > 0 {
            let lpzi_client = token::Client::new(&env, &lpzi_token);
            lpzi_client.mint(&user, &final_reward);
            env.storage().instance().set(&Symbol::new(&env, "distributed_rewards"), &(distributed_rewards + final_reward));
            let mut new_start_time = stake_start_time;
            new_start_time.set(user.clone(), current_time);
            env.storage().instance().set(&Symbol::new(&env, "stake_start_time"), &new_start_time);
        }
    }

    // Withdraw staked tokens
    pub fn withdraw(env: Env, user: Address, amount: u128) {
        user.require_auth();
        Self::check_termination(env.clone(), user.clone());

        let zi_token = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "zi_token"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let lpzi_token = env.storage().instance().get::<_, Address>(&Symbol::new(&env, "lpzi_token"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let mut stakes = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stakes"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let current_stake = stakes.get(&user).unwrap_or(0);
        let stake_start_time = env.storage().instance().get::<_, Map<Address, u128>>(&Symbol::new(&env, "stake_start_time"))
            .unwrap_or_else(|| panic_with_error!(&env, StakingError::NotInitialized));
        let start_time = stake_start_time.get(&user).unwrap_or(0);

        if env.ledger().timestamp() - start_time < MIN_STAKE_PERIOD {
            panic_with_error!(&env, StakingError::MinimumStakePeriodNotMet);
        }
        if amount > current_stake {
            panic_with_error!(&env, StakingError::InsufficientStake);
        }

        // Burn $LpZi tokens
        let lpzi_client = token::Client::new(&env, &lpzi_token);
        lpzi_client.burn(&user, &amount);

        // Return staked tokens
        let token_client = token::Client::new(&env, &zi_token);
        token_client.transfer(&env.current_contract_address(), &user, &amount);

        // Update stakes
        stakes.set(user.clone(), current_stake - amount);
        env.storage().instance().set(&Symbol::new(&env, "stakes"), &stakes);
    }
}

2. Liquidity Pool Checker Contract (pool_checker.rs)

This contract verifies equal capital contributions in the liquidity pool.

use soroban_sdk::{contract, contractimpl, Address, Env, Symbol};

#[contract]
pub struct PoolCheckerContract;

#[contractimpl]
impl PoolCheckerContract {
    pub fn verify_equal_contribution(
        env: Env,
        user: Address,
        pool_address: Address,
        required_zi: u128,
    ) -> bool {
        let pool_client = PoolContractClient::new(&env, &pool_address);
        let zi_balance = pool_client.get_user_zi_balance(&user);
        let other_balance = pool_client.get_user_other_balance(&user); // XLM or USDC
        zi_balance >= required_zi && other_balance >= required_zi
    }
}

3. Product-Specific Initialization

Each product uses the LiquidStakingContract with tailored parameters, including initial APY, minimum stake, pool requirements, and reward pool size (assumed for example purposes; adjust based on actual funding).

  • Zi Starter Staking:

    LiquidStakingContract::initialize(
        env,
        admin_address,
        zi_token_address, // $Zi token
        lpzi_token_address,
        xlm_zi_pool_address,
        20,  // apy_min (20%)
        40,  // apy_max (40%)
        100_0000000, // min_stake (100 $Zi, 7 decimals)
        50_0000000,  // pool_min_zi (50 $Zi equivalent)
        1000000_0000000, // total_rewards (example: 1M $Zi)
    );
  • XLM-Zi Liquidity Enhancer:

    LiquidStakingContract::initialize(
        env,
        admin_address,
        xlm_zi_pool_token_address, // XLM-Zi pool tokens
        lpzi_token_address,
        xlm_zi_pool_address,
        40,  // apy_min (40%)
        70,  // apy_max (70%)
        500_0000000, // min_stake (500 $Zi equivalent)
        250_0000000, // pool_min_zi (250 $Zi equivalent)
        5000000_0000000, // total_rewards (example: 5M $Zi)
    );
  • USDC-Zi Pro Staking:

    LiquidStakingContract::initialize(
        env,
        admin_address,
        usdc_zi_pool_token_address, // USDC-Zi pool tokens
        lpzi_token_address,
        usdc_zi_pool_address,
        70,  // apy_min (70%)
        91,  // apy_max (91%)
        1000_0000000, // min_stake (1000 $Zi equivalent)
        500_0000000, // pool_min_zi (500 $Zi equivalent)
        10000000_0000000, // total_rewards (example: 10M $Zi)
    );

UI Modal Layout (Next.js with Chakra UI)

The modals are updated to reflect the new staking conditions, using Next.js, Chakra UI, and Soroban React libraries for side-by-side (desktop) or stacked (mobile) display.

Modal Component (components/StakingModal.tsx)

import { Box, Button, Heading, Text, VStack, useBreakpointValue } from "@chakra-ui/react";
import { useSorobanReact } from "@soroban-react/core";
import { useContractInvoke } from "@soroban-react/contracts";
import { Address } from "@stellar/stellar-sdk";
import { FC } from "react";

interface StakingModalProps {
  title: string;
  description: string;
  apy: string;
  poolRequirement: string;
  minStake: string;
  liquidity: string;
  security: string;
  bestFor: string;
  contractAddress: string;
  terms: string;
}

const StakingModal: FC<StakingModalProps> = ({
  title,
  description,
  apy,
  poolRequirement,
  minStake,
  liquidity,
  security,
  bestFor,
  contractAddress,
  terms,
}) => {
  const { activeChain, address } = useSorobanReact();
  const { invoke } = useContractInvoke({ contractAddress });

  const handleStake = async () => {
    if (!address) {
      alert("Connect wallet to stake");
      return;
    }
    try {
      await invoke({
        method: "stake",
        args: [Address.fromString(address), BigInt(parseFloat(minStake) * 1e7)],
      });
      alert("Staking initiated!");
    } catch (error) {
      console.error("Staking failed:", error);
      alert("Staking failed. Check console for details.");
    }
  };

  return (
    <Box
      borderWidth="1px"
      borderRadius="lg"
      p={6}
      bg="white"
      shadow="md"
      _hover={{ shadow: "lg" }}
      width={{ base: "100%", md: "30%" }}
      minH={{ base: "auto", md: "450px" }}
    >
      <Heading size="md" mb={4}>{title}</Heading>
      <Text mb={4}>{description}</Text>
      <VStack align="start" spacing={2}>
        <Text><b>APY:</b> {apy}</Text>
        <Text><b>Liquidity Pool:</b> {poolRequirement}</Text>
        <Text><b>Minimum Stake:</b> {minStake} $Zi</Text>
        <Text><b>Liquidity:</b> {liquidity}</Text>
        <Text><b>Security:</b> {security}</Text>
        <Text><b>Best For:</b> {bestFor}</Text>
        <Text><b>Terms:</b> {terms}</Text>
      </VStack>
      <Button colorScheme="teal" mt={4} onClick={handleStake} width="full">
        Start Staking
      </Button>
    </Box>
  );
};

export default StakingModal;

Page Component (pages/staking.tsx)

import { Flex, VStack, Heading } from "@chakra-ui/react";
import StakingModal from "../components/StakingModal";
import { useBreakpointValue } from "@chakra-ui/react";

export default function StakingPage() {
  const direction = useBreakpointValue({ base: "column", md: "row" });

  const commonTerms = "1-year minimum stake. APY reduces by 1/8 annually. Maintain equal LP or face 3-month grace period without yield; failure terminates contract.";

  return (
    <VStack spacing={8} p={6}>
      <Heading>Zig3 v3 Liquid Staking</Heading>
      <Flex
        direction={direction}
        gap={6}
        justifyContent="center"
        alignItems={{ base: "center", md: "flex-start" }}
        width="100%"
      >
        <StakingModal
          title="Zi Starter Staking – Begin Your DeFi Journey"
          description="Ideal for beginners, earn 20–40% APY (reducing yearly) by staking $Zi after providing equal capital to an XLM-Zi pool."
          apy="20–40% (reduces 1/8 per year)"
          poolRequirement="Equal capital in XLM-Zi pool (e.g., 50 $Zi + equivalent XLM)"
          minStake="100"
          liquidity="$LpZi tokens for trading or DeFi use"
          security="Soroban smart contracts with passkey ID signing"
          bestFor="DeFi newcomers seeking accessible, high-yield staking"
          contractAddress="YOUR_ZI_STARTER_CONTRACT_ADDRESS"
          terms={commonTerms}
        />
        <StakingModal
          title="XLM-Zi Liquidity Enhancer – Boost Your Rewards"
          description="For intermediate users, earn 40–70% APY (reducing yearly) by staking XLM-Zi pool tokens after equal capital contribution."
          apy="40–70% (reduces 1/8 per year)"
          poolRequirement="Equal capital in XLM-Zi pool (e.g., 250 $Zi + equivalent XLM)"
          minStake="500"
          liquidity="$LpZi tokens for trading, lending, or collateral"
          security="Audited Soroban contracts with transparent operations"
          bestFor="Users familiar with liquidity pools seeking enhanced rewards"
          contractAddress="YOUR_XLM_ZI_ENHANCER_CONTRACT_ADDRESS"
          terms={commonTerms}
        />
        <StakingModal
          title="USDC-Zi Pro Staking – Elite DeFi Yields"
          description="For advanced users, earn 70–91% APY (reducing yearly) by staking USDC-Zi pool tokens after equal capital contribution."
          apy="70–91% (reduces 1/8 per year)"
          poolRequirement="Equal capital in USDC-Zi pool (e.g., 500 $Zi + equivalent USDC)"
          minStake="1000"
          liquidity="$LpZi tokens for advanced DeFi applications"
          security="Soroban’s robust contracts with passkey ID authentication"
          bestFor="DeFi pros targeting high returns with stablecoin exposure"
          contractAddress="YOUR_USDC_ZI_PRO_CONTRACT_ADDRESS"
          terms={commonTerms}
        />
      </Flex>
    </VStack>
  );
}

Key Changes and Features

  1. APY Reduction: APY reduces by 1/8 each year via get_current_apy, calculated as (7/8)^years_elapsed. For example, 40% APY becomes 35% after 1 year, 30.625% after 2 years, etc.
  2. Termination Logic: check_termination monitors LP contributions. If below pool_min_zi for 1 month (WARNING_PERIOD), a 3-month grace period (GRACE_PERIOD) starts without rewards. If unresolved, the contract terminates, and stakes are cleared (user loses funds).
  3. Minimum Stake Period: Enforced via MIN_STAKE_PERIOD (1 year) in withdraw. After 1 year, users can withdraw or continue staking until funds are depleted or LP requirements fail.
  4. Reward Pool: total_rewards tracks available rewards. Staking stops when distributed_rewards reaches total_rewards.
  5. LP Monitoring: check_pool_contribution ensures equal capital (simplified 1:1 value). Warning and grace periods are managed via lp_warning storage.
  6. UI Updates: Added terms field to modals, clarifying 1-year minimum, APY reduction, and LP requirements with grace period consequences.

Assumptions and Simplifications

  • Token Decimals: Assumes 7 decimals for $Zi, XLM, USDC, and pool tokens (Stellar standard).
  • APY Calculation: Uses average APY ((min + max) / 2) with linear time-based rewards. Real-world APY should use pool fees, trading volume, or oracles.
  • Pool Integration: Assumes a PoolContractClient with balance query functions. Integrate with Zig3 v3’s actual pool contract.
  • Value Equivalence: Assumes 1:1 value for LP contributions (e.g., 50 $Zi = equivalent XLM). Use a price oracle for production.
  • Reward Pool: Example total_rewards values (1M–10M $Zi) are placeholders. Set based on actual funding.
  • Testnet: Designed for Soroban testnet (per June 13, 2025).

Deployment and Integration

  • Soroban Setup:
    cargo add soroban-sdk
    soroban contract build
    soroban contract deploy --wasm liquid_staking.wasm --source-account <ADMIN_KEY> --network testnet
  • Stellar SDK: Use @stellar/stellar-sdk and @soroban-react/* for wallet integration (Freighter, Lobstr). Configure passkey-kit for passkey ID.
  • Contract Addresses: Replace YOUR_*_CONTRACT_ADDRESS with deployed IDs.
  • Testing:
    soroban contract invoke --id <CONTRACT_ID> -- stake --user <USER_ADDRESS> --amount 1000000000
  • Security: Audit for reentrancy, overflow, and LP manipulation. Query mentions audited contracts for Product 2 and passkey ID for all.
  • Reward Funding: Fund total_rewards to sustain APYs (20–91% initially, reducing).

Limitations

  • APY Logic: Simplified reduction and reward calculation. Implement dynamic APY with pool data or oracles.
  • Pool Valuation: 1:1 value assumption; use oracles for accurate pricing.
  • Grace Period UX: Contract enforces grace period, but UI should notify users of LP warnings.
  • UI Styling: Basic Chakra UI; enhance with next-themes or react-particles.
  • Mobile UX: Verify responsiveness across devices.

Next Steps

  1. Deploy to Soroban testnet and test all functions (stake, claim_rewards, withdraw, check_termination).
  2. Integrate with Zig3 v3’s pool contract for LP balance checks.
  3. Audit for mainnet deployment.
  4. Add UI notifications for LP warnings and grace periods.

Integration Time Estimate & Analysis

Time Allowed for Development

You have a modular staking contract and a liquidity pool checker contract, both written for Soroban (Stellar smart contracts). You want to integrate these into your Next.js/Chakra UI dApp, with UI modals for staking products and passkey authentication.


Integration Steps & Time Estimate

1. Smart Contract Deployment (Soroban)

  • Tasks:
    • Review and test liquid_staking.rs and pool_checker.rs locally.
    • Build and deploy contracts to Soroban testnet.
    • Record contract addresses.
  • Estimate: 1 day (including testnet deployment and troubleshooting)

2. Backend/Contract Client Integration

  • Tasks:
    • Implement or update a contract client in your dApp to interact with the new staking contract methods (stake, claim_rewards, withdraw, etc.).
    • Integrate pool checker logic for LP validation.
    • Ensure passkey authentication is used for contract calls.
  • Estimate: 2 days (depends on existing Soroban integration and passkey flow)

3. UI Integration (Next.js + Chakra UI)

  • Tasks:
    • Update or create staking modals/components as per your design.
    • Connect UI to contract client (invoke Soroban methods from UI).
    • Display staking terms, APY, pool requirements, and handle user feedback (success, error, warnings).
    • Add LP warning/grace period notifications.
  • Estimate: 2 days (if Chakra UI and Soroban React are already set up)

4. Testing & QA

  • Tasks:
    • Test all staking flows: stake, claim, withdraw, LP checks, APY reduction, termination.
    • Test passkey authentication with contract calls.
    • Test UI on desktop and mobile.
  • Estimate: 1 days

5. Documentation & Deployment

  • Tasks:
    • Update docs for new staking products and contract addresses.
    • Deploy frontend (Vercel) and ensure environment variables are set.
  • Estimate: 0.5–1 day

Total Estimated Time: 6 - 7 days

  • If you have strong Soroban and Next.js experience: 1 week is realistic.

Key Integration Points

  • Contract addresses: Replace placeholders in your UI with deployed contract IDs.
  • Contract client: Use @stellar/stellar-sdk and @soroban-react/* for contract calls.
  • Passkey auth: Ensure user.require_auth() is satisfied by your passkey login flow.
  • UI: Use Chakra UI modals as shown in your example, passing correct props and handling contract responses.
  • LP checker: Integrate pool checker contract for LP validation before staking.
  • Notifications: Add UI feedback for grace period, warnings, and termination.

Risks & Considerations

  • Soroban is evolving: APIs and best practices may change.
  • LP value checks: For production, use a price oracle instead of 1:1 assumptions.
  • Security: Audit contracts before mainnet deployment.
  • User experience: Notify users of LP warnings and grace periods in the UI.

Next Steps

  1. Deploy contracts to Soroban testnet.
  2. Integrate contract client in your dApp.
  3. Update UI modals and connect to contract methods.
  4. Test all flows and edge cases.
  5. Update documentation and deploy.

Based on the requirements with Soroban, Next.js, and Chakra UI, here’s a realistic breakdown:

Task Estimated Hours
Smart contract review, build, deploy 4
Contract client integration (frontend) 6
UI integration (modals, wiring, feedback) 6
Testing (flows, edge cases, mobile) 4
Documentation & deployment 2
Total 22-26 hours

If you are experienced: 3–4 working days.


DeFi’s accessibility is a key advantage, allowing anyone with internet access to participate, regardless of location or financial status. This is particularly transformative for underserved regions. Transparency is another pillar, with blockchain providing a public ledger for all transactions, enabling auditability and fostering trust. Users also gain control over their assets through digital wallets, unlike traditional banks where funds are held by institutions, offering freedom to move assets without third-party permission.

Finally, DeFi often provides higher returns compared to traditional finance. Lending platforms can offer interest rates exceeding savings accounts, and liquidity mining rewards users for contributing to decentralized exchanges, aligning with the potential for significant financial growth.

Introduction to Zig3 v3: A Stellar Passkey Soroban DeFi Smart Wallet

Zig3 v3 is a cutting-edge decentralized exchange (DEX) built on the Soroban platform, the smart contract layer of the Stellar network. As of recent updates, it integrates with Stellar to offer functionalities like managing balances, minting test tokens, swapping tokens, and creating liquidity pools, all interacting with Soroban smart contracts. The platform is built using Next.js for the frontend, Express.js for the backend, and MongoDB for data storage, with integration achieved via the Stellar SDK and Soroban RPC.

Zig3 v3 is designed to enhance the DeFi experience, offering features like browser extension wallets and passkey ID transaction signing, making it user-friendly for managing assets and interacting with the DEX.

Deep Dive into Liquid Staking: Definition and Mechanism

Liquid Staking is a DeFi innovation that allows users to stake their assets in a smart contract while maintaining liquidity. In traditional staking, assets are locked, limiting their use during the staking period. Liquid Staking addresses this by issuing a liquid token, such as $LpZi, in return for staked assets. This token can be traded, used as collateral, or utilized in other DeFi protocols, enabling users to earn rewards while keeping assets flexible.

On Stellar, while not a Proof-of-Stake network, passive income can be earned through lending or liquidity pools, with providers offering interest-bearing assets akin to liquid staking, as noted in recent analyses (Beginner’s Guide to Stellar (XLM)). Zig3 v3’s Liquid Staking, currently on testnet with mainnet imminent as per an X post by @DokCrypto on June 13, 2025, involves staking $Zi tokens to receive $LpZi. This feature is only available on Zig3 v3 via Passkeys ID for staking.

The user query specifies APYs ranging from 20% to 91%, which is exceptionally high, suggesting Zig3 v3 aims to attract users with significant yield opportunities. Additionally, users with XLM Zi or USDC Zi liquidity pool tokens can stake these for further rewards, creating a synergistic effect within the platform.

Benefits and Implementation Details of Zig3 v3’s Liquid Staking

Zig3 v3’s Liquid Staking offers several benefits, aligning with DeFi’s core advantages:

  • High Returns: APYs between 20% and 91% provide substantial growth potential, far exceeding traditional financial instruments.
  • Liquidity: $LpZi tokens can be used across DeFi applications, offering flexibility for users to engage in other protocols while earning rewards.
  • Security: Powered by Soroban’s robust smart contract platform, which is designed for scalability and sensibility, ensuring staked assets are secure (Stellar Soroban).
  • Accessibility: The platform is open to anyone, with no location or investment size barriers, enhancing inclusivity.

Implementation details include:

  • Staking is facilitated through Soroban smart contracts, ensuring transparency and security.
  • The testnet phase, as of June 13, 2025, is active, with mainnet launch expected soon, based on community updates.

Comparative Analysis: Liquid Staking vs. Traditional Staking

To contextualize, traditional staking locks assets, limiting their use, while liquid staking, as implemented in Zig3 v3, offers liquidity via tokens like $LpZi. This is particularly transformative for DeFi users, as it aligns with the need for flexibility. For instance, users with XLM Zi or USDC Zi liquidity pools can stake these at twice the value, enhancing reward potential, as per the user query. This is akin to practices in other blockchains, where liquid staking tokens are used as collateral, as seen in general DeFi explanations (What Is Liquid Staking?).

Conclusion and Future Outlook

Zig3 v3’s Liquid Staking feature, with its high APYs and liquidity, positions it as a leader in the DeFi space. As of June 13, 2025, it combines the benefits of decentralization, accessibility, transparency, and high returns, making it a compelling option for DeFi users. The platform’s integration with Stellar’s ecosystem and upcoming mainnet launch suggest a bright future, potentially setting new standards for staking in DeFi.

Table: Summary of Zig3 v3 Features and Liquid Staking Benefits

Feature Category Details
Zig3 v3 Features - DEX inspired by Uniswap V2, tailored for Soroban
- Liquidity pools, token swapping, Zi Airdrop
- Browser extension wallet, passkey ID signing
Liquid Staking - Stake $Zi for $LpZi, APY 20-91%
- Stake XLM Zi/USDC Zi liquidity pools for higher rewards
- Currently on testnet, mainnet imminent

This detailed analysis ensures a comprehensive understanding of Zig3 v3’s role in DeFi and its innovative Liquid Staking feature, addressing all aspects of the user query.


Key Citations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions