Smart Contracts
Staking Contract

DogWithCapStaking Contract

Detailed technical documentation for the main staking contract.

Contract Information

Contract: DogWithCapStaking
Version: V8 (Simplified Final)
License: MIT
Solidity: ^0.8.19

Deployed Addresses:

Network: WicChain Testnet
Chain ID: 6689
Address: 0x0EdA695A21E38C1953B80578fd84Ea4923Fe5D6c

Architecture

Inheritance

DogWithCapStaking is ReentrancyGuard, Pausable, Ownable

Inherited Contracts:

  • ReentrancyGuard - Protection against reentrancy attacks
  • Pausable - Emergency pause functionality
  • Ownable - Access control for admin functions

Dependencies

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

Core Data Structures

StakePosition

struct StakePosition {
    uint256 amount;                // Fixed stake amount
    uint256 claimedInterest;       // Total interest claimed
    uint64 startTime;              // Position creation time
    uint64 lastClaimTime;          // Last claim timestamp
    bool isActive;                 // Always true (permanent)
}

Key Points:

  • Amount never changes (permanent staking)
  • Interest accumulates over time
  • Each position has unique ID
  • Multiple positions per user allowed

UserInfo

struct UserInfo {
    uint256 totalStaked;           // Sum of all positions
    uint256 unclaimedRewards;      // Referral rewards
    uint64 firstStakeTime;         // First stake timestamp
    address referrer;              // Who referred user
    MilestoneLevel milestone;      // Current level
    bool hasEverStaked;            // Has user staked
}

ContractStats

struct ContractStats {
    uint256 totalStaked;           // Total across all users
    uint256 totalUsers;            // Unique user count
    uint256 rewardPool;            // Available rewards
    uint256 totalPositions;        // Total positions created
    uint256 totalInterestPaid;     // Interest distributed
    uint256 totalReferralsPaid;    // Referral rewards paid
}

MilestoneVesting

struct MilestoneVesting {
    uint256 totalAmount;           // Total bonus amount
    uint256 claimedAmount;         // Amount already claimed
    uint64 vestingStart;           // Start timestamp
    uint64 vestingEnd;             // End timestamp
    uint64 lastClaimTime;          // Last claim time
    bool isActive;                 // Vesting active
}

Constants

// Rate precision
uint256 public constant RATE_PRECISION = 100000;
 
// Referral rate (5%)
uint256 public constant REFERRAL_RATE = 5000;
 
// Minimum stake amount (100 WICC)
uint256 public constant MIN_STAKE_AMOUNT = 100 * 10**18;
 
// Cooldown between actions (60 seconds)
uint256 public constant CLAIM_COOLDOWN = 60;
 
// Interest rate bounds
uint256 public constant MIN_DAILY_INTEREST_RATE = 50;   // 0.05%
uint256 public constant MAX_DAILY_INTEREST_RATE = 1000; // 1%

State Variables

Token Configuration

IERC20 public immutable stakingToken;

Dynamic Interest Rate

uint256 public dailyInterestRate = 200; // 0.2% default
InterestRateChange[] public interestRateHistory;

Cash Out System

uint256 public nextCashOutRequestId = 1;
uint256 public minCashOutAmount = 10 * 10**18;
uint256 public maxCashOutAmount = 1000000 * 10**18;
uint256 public cashOutFeeRate = 100; // 0.1%
bool public cashOutEnabled = true;
uint256 public totalCashOutTokensInContract = 0;

Storage Mappings

// User data
mapping(address => UserInfo) public users;
mapping(address => UserReferralData) private userReferralData;
mapping(address => mapping(uint256 => StakePosition)) public userPositions;
 
// Position tracking
mapping(address => uint256[]) public userPositionIds;
mapping(uint256 => address) public positionOwner;
 
// Milestone vesting
mapping(address => mapping(uint8 => MilestoneVesting)) public milestoneVesting;
mapping(address => mapping(uint8 => bool)) public milestoneAchieved;
 
// Rate limiting
mapping(address => uint256) public lastActionTime;
 
// Referrals
mapping(address => address[]) public userReferees;

Core Functions

stake()

function stake(
    uint256 amount,
    address referrer
) external nonReentrant whenNotPaused rateLimited 
  returns (uint256 positionId)

Description: Creates a new permanent staking position.

Parameters:

  • amount - Amount of WICC tokens to stake
  • referrer - Optional referrer address (0x0 if none)

Returns:

  • positionId - Unique ID of created position

Requirements:

  • Amount >= 100 WICC
  • User must approve tokens first
  • Cannot refer yourself
  • Must wait for cooldown

Emits:

  • PositionCreated(user, positionId, amount, referrer, timestamp)
  • ReferralReward(referrer, referee, stakeAmount, rewardAmount, ...)
  • UserStatsUpdate(user, totalStaked, totalPositions, milestone, ...)

claimPositionInterest()

function claimPositionInterest(uint256 positionId) 
    external 
    nonReentrant 
    whenNotPaused 
    rateLimited 
    validPosition(positionId)
    returns (uint256 claimedAmount)

Description: Claims accumulated interest from a specific position.

Parameters:

  • positionId - ID of position to claim from

Returns:

  • claimedAmount - Amount of interest claimed

Requirements:

  • Position must exist and be active
  • At least 24 hours since last claim
  • Sufficient reward pool
  • Cooldown not active

Emits:

  • PositionClaimed(user, positionId, interestClaimed, totalClaimed, paidDays, timestamp)

claimAllPositions()

function claimAllPositions() 
    external 
    nonReentrant 
    whenNotPaused 
    rateLimited

Description: Claims interest from all active positions in one transaction.

Gas Optimization: More efficient than claiming individually.

Emits:

  • PositionClaimed for each position with claimable interest

claimReferralRewards()

function claimReferralRewards() 
    external 
    nonReentrant 
    whenNotPaused 
    rateLimited

Description: Claims all accumulated referral commission rewards.

Requirements:

  • Must have unclaimed rewards > 0
  • Sufficient reward pool
  • Cooldown not active

Emits:

  • ReferralRewardsClaimed(user, claimedAmount, remainingRewards, timestamp)

claimMilestoneVesting()

function claimMilestoneVesting(uint8[] calldata milestoneLevels) 
    external 
    nonReentrant 
    whenNotPaused 
    rateLimited

Description: Claims vested amounts from milestone bonuses.

Parameters:

  • milestoneLevels - Array of milestone levels to claim (1-4)

Vesting Type: Linear vesting with no cliff

Emits:

  • MilestoneVestingClaimed(user, level, claimedAmount, remainingAmount, timestamp)

View Functions

getUserInfo()

function getUserInfo(address userAddr)
    external
    view
    returns (
        uint256 totalStaked,
        uint256 unclaimedReferralRewards,
        uint256 totalPendingInterest,
        uint256 totalVestingAmount,
        uint256 totalClaimableVesting,
        address referrer,
        MilestoneLevel milestone,
        uint256 totalPositions
    )

Returns complete user state including:

  • Total staked across all positions
  • Pending rewards (referral + interest + vesting)
  • Referrer information
  • Current milestone level
  • Position count

getPosition()

function getPosition(address user, uint256 positionId)
    external
    view
    returns (
        uint256 amount,
        uint256 claimedInterest,
        uint256 pendingInterest,
        uint256 startTime,
        uint256 lastClaimTime,
        uint256 stakingDays,
        bool isActive
    )

Returns position details including:

  • Staked amount (unchanging)
  • Interest claimed so far
  • Currently pending interest
  • Time information
  • Activity status

getContractStats()

function getContractStats()
    external
    view
    returns (
        uint256 totalStaked,
        uint256 totalUsers,
        uint256 rewardPool,
        uint256 totalPositions,
        uint256 contractBalance,
        uint256 totalInterestPaid,
        uint256 totalReferralsPaid,
        uint256 pendingCashOutAmount,
        uint256 currentDailyRate
    )

Returns platform-wide statistics.

Admin Functions

⚠️

These functions can only be called by contract owner.

setDailyInterestRate()

function setDailyInterestRate(uint256 newRate, string calldata reason) 
    external 
    onlyOwner

Description: Updates the daily interest rate for all positions.

Parameters:

  • newRate - New rate in basis points (200 = 0.2%)
  • reason - Explanation for rate change

Constraints:

  • Rate must be between 0.05% and 1%
  • Change is recorded in history
  • Affects all existing and future positions

Emits:

  • DailyInterestRateChanged(oldRate, newRate, changedBy, reason, timestamp)

depositRewards()

function depositRewards(uint256 amount) 
    external 
    onlyOwner

Description: Adds tokens to the reward pool.

Use Case: Ensure platform can pay interest and rewards.

pause() / unpause()

function pause() external onlyOwner
function unpause() external onlyOwner

Description: Emergency pause/resume all user operations.

Pauses: All staking, claiming, and reward functions.

Security Features

Reentrancy Protection

modifier nonReentrant() 

All state-changing functions use ReentrancyGuard.

Rate Limiting

modifier rateLimited() {
    require(
        block.timestamp >= lastActionTime[msg.sender] + CLAIM_COOLDOWN,
        "Cooldown active"
    );
    _;
    lastActionTime[msg.sender] = block.timestamp;
}

Prevents:

  • Spam transactions
  • Manipulation attempts
  • Network congestion

Access Control

modifier onlyOwner()

Admin functions restricted to contract owner.

Pausable

Emergency pause mechanism for critical situations.

Gas Optimization

Tips for Users

  1. Batch Claims: Use claimAllPositions() instead of individual claims
  2. Timing: Claim during low network activity
  3. Approval: Approve unlimited to avoid repeated approvals
  4. Cooldown: Wait for full cooldown before next action

Expected Gas Costs

FunctionEstimated GasCost (estimate)
approve~50,0000.05βˆ’0.05 - 0.20
stake~150,0000.15βˆ’0.15 - 0.60
claimPositionInterest~100,0000.10βˆ’0.10 - 0.40
claimAllPositions~200,0000.20βˆ’0.20 - 0.80
claimReferralRewards~80,0000.08βˆ’0.08 - 0.30
claimMilestoneVesting~120,0000.12βˆ’0.12 - 0.50

Actual costs depend on network congestion and gas prices.

Testing

Example Test Cases

describe("DogWithCapStaking", () => {
  it("Should stake tokens successfully", async () => {
    await token.approve(staking.address, amount);
    await staking.stake(amount, referrer);
    // Assert position created
  });
 
  it("Should enforce minimum stake", async () => {
    await expect(
      staking.stake(99, ADDRESS_ZERO)
    ).to.be.revertedWith("AmountTooLow");
  });
 
  it("Should prevent self-referral", async () => {
    await expect(
      staking.stake(amount, user.address)
    ).to.be.revertedWith("SelfReferral");
  });
 
  it("Should calculate interest correctly", async () => {
    // Stake
    // Wait 1 day
    // Claim
    // Assert correct amount
  });
});

Upgrade Path

⚠️

This contract is NOT upgradeable. All functionality is fixed at deployment.

If upgrades needed:

  • Deploy new contract
  • Migrate users manually
  • Or implement proxy pattern in V9

Next Steps

Resources