DEV Community

Cover image for Building a Basic Python Staking Contract on Xian Blockchain
crosschainer
crosschainer

Posted on

Building a Basic Python Staking Contract on Xian Blockchain

Introduction

Staking is a core concept in blockchain applications that allows users to lock up tokens in a contract to earn rewards over time. Implementing a staking contract requires careful consideration of token compatibility, reward distribution, security, and withdrawal conditions.

In this article, we’ll walk through the process of designing a staking smart contract on Xian Blockchain that can work with any token using importlib. We'll also enforce an interface to ensure that only valid tokens with required functions can be used.


Thought Process Behind Staking Contract Logic

Before writing any code, it’s important to understand the core functionalities required for a staking contract:

  1. Token Compatibility: The contract should work with any token, enforcing an interface to ensure the token supports transfer_from and transfer.
  2. Depositing Tokens (Staking): Users should be able to deposit their tokens into the contract.
  3. Reward Calculation: Users earn rewards based on their staked amount and the duration of staking.
  4. Withdrawal of Staked Tokens: Users should be able to withdraw their tokens.
  5. Security Considerations: Prevent reentrancy, ensure accurate balance updates, and enforce access control.

Designing the Staking Contract

We will design the staking contract with the following key elements:

  • Using importlib to load the token contract dynamically.
  • Using now to get the transaction datetime.
  • Enforcing an interface to ensure token compatibility.
  • Tracking stakes and rewards using state variables.

1. Define the Staking Contract

staking_token = Variable()
staked_balances = Hash(default_value=0)
stake_timestamps = Hash(default_value=0)
reward_rate = Variable()  # Rate at which rewards accumulate

token_interface = [importlib.Func('transfer_from', args=('amount', 'to',
    'main_account')), importlib.Func('transfer', args=('amount', 'to')),
    importlib.Func('balance_of', args=('address',))]

@construct
def seed(token_contract: str, initial_reward_rate: float):
    """Initialize the staking contract with a token and reward rate."""
    token = importlib.import_module(token_contract)
    importlib.enforce_interface(token, token_interface)

    staking_token.set(token_contract)
    reward_rate.set(initial_reward_rate)

@export
def stake(amount: float):
    """Allows users to stake tokens."""
    assert amount > 0, "Stake amount must be greater than zero"

    token.transfer_from(ctx.caller, ctx.this, amount)

    staked_balances[ctx.caller] += amount
    stake_timestamps[ctx.caller] = now

@export
def withdraw():
    """Allows users to withdraw staked tokens and rewards."""
    amount = staked_balances[ctx.caller]
    assert amount > 0, "No staked tokens to withdraw"

    token = importlib.import_module(staking_token.get())

    staking_duration = now - stake_timestamps[ctx.caller]
    reward = amount * reward_rate.get() * staking_duration / (24 * 60 * 60)  # Reward per day

    token.transfer(ctx.caller, amount + reward)

    staked_balances[ctx.caller] = 0
    stake_timestamps[ctx.caller] = 0
Enter fullscreen mode Exit fullscreen mode

Understanding the Logic

1. Token Compatibility

  • We use importlib.import_module(staking_token.get()) to dynamically load the token contract.
  • The contract enforces an interface using importlib.enforce_interface(token, token_interface) to ensure compatibility.

2. Staking Tokens

  • Users call stake(amount), which transfers tokens from their wallet to the staking contract.
  • The contract updates the user's staked balance and timestamps the deposit.

3. Reward Calculation

  • The contract calculates rewards based on the staking duration and a reward rate.
  • Rewards accumulate in a time-based fashion, ensuring longer stakes get higher rewards.

4. Withdrawing Staked Tokens

  • Users call withdraw() to retrieve their staked tokens + rewards.
  • The contract calculates rewards and transfers the total amount back to the user.
  • Staked balances and timestamps are reset to prevent double claims.

Conclusion

By using importlib, we can create a flexible staking contract that works with any token while enforcing a standard interface. This ensures security, compatibility, and flexibility for different token ecosystems.

πŸš€ Next Steps:

  • Implement additional security checks such as cooldown periods before withdrawing.
  • Allow reward customization based on different staking tiers.
  • Introduce a penalty mechanism for early withdrawals.

πŸ’‘ Got ideas for improving staking mechanisms? Drop a comment below! πŸ”

Top comments (0)