SkyStake is a decentralized staking protocol that enables users to deposit ERC-20 tokens and earn fixed rewards after completing a predetermined staking period. Built on the Ethereum blockchain with a modern Angular frontend, SkyStake provides a secure, intuitive, and transparent way to participate in token staking.
SkyStake simplifies cryptocurrency staking by offering a fixed-amount, fixed-period staking mechanism. Users lock a specific amount of tokens for a defined duration and receive guaranteed ETH rewards upon completion. No complex calculations. No variable APYs. Just straightforward, predictable yields.
Predictable Rewards — Know exactly how much you'll earn before you stake
Simple & Transparent — No hidden fees or complex mechanisms
Non-Custodial — Your tokens are secured by smart contracts, not intermediaries
User-Friendly Interface — Modern Angular dashboard with real-time status updates
Ethereum-Native — Runs on Ethereum Sepolia testnet with full MetaMask integration
Auditable — Open-source contracts built with OpenZeppelin standards
SkyStake is built as a monorepo containing two primary packages:
staking-project/
├── packages/
│ ├── contracts/ # Smart contracts (Solidity + Foundry)
│ │ ├── src/
│ │ │ ├── Stakingdapp.sol # Main staking contract
│ │ │ └── Stakingtoken.sol # ERC-20 token contract
│ │ ├── test/ # Foundry test suites
│ │ ├── script/ # Deployment scripts
│ │ └── foundry.toml # Foundry configuration
│ │
│ └── frontend/ # Angular Web3 DApp
│ ├── src/
│ │ ├── app/
│ │ │ ├── components/ # UI components (Hero, Navbar, Dashboard)
│ │ │ ├── services/ # Blockchain services (Wallet, Staking)
│ │ │ └── app.module.ts # Angular module configuration
│ │ └── main.ts
│ ├── angular.json
│ ├── package.json
│ └── tsconfig.json
│
└── README.md
Smart Contracts:
- Solidity ^0.8.35
- OpenZeppelin Contracts v5
- Foundry (Forge, Anvil, Cast)
Frontend:
- Angular 18.2
- ethers.js v6.16
- TypeScript 5.5
- SCSS Styling
Blockchain:
- Ethereum Sepolia Testnet
- MetaMask Integration
- ERC-20 Token Standard
- Connect Wallet — User connects their MetaMask wallet to the dApp
- Mint Tokens — User mints the required ERC-20 tokens from the token contract
- Approve Spending — User approves the Staking contract to spend their tokens
- Deposit (Stake) — User deposits the fixed staking amount and locks tokens
- Wait for Period — Tokens remain locked for the configured staking period
- Claim Rewards — After the period ends, user claims their ETH rewards
- Withdraw — User withdraws their original staked tokens
User Wallet
↓
[MetaMask Connection]
↓
StakingToken Contract
├── mint() → Create ERC-20 tokens
└── approve() → Grant spending approval
↓
Staking Contract
├── depositTokens() → Lock tokens & record timestamp
│ └── Returns: staked balance, staking start time
├── claimReward() → Receive ETH after staking period
│ └── Requires: stakingPeriod elapsed
└── withdrawTokens() → Unlock original tokens
The Staking contract maintains three key mappings:
- userBalances — Tracks how many tokens each user has staked
- userStakingTimestamps — Records when each user began staking
- Parameters — Stores stakingPeriod, fixedStakingAmount, rewardPerPeriod
Time: 0h
└─ User deposits 100 SKY tokens
└─ Block timestamp recorded: 1717000000
Time: 24h (Staking Period = 86,400 seconds)
└─ Dashboard shows: "1 day remaining"
└─ Progress bar: 100%
Time: 24h + 1 second
└─ claimReward() becomes available
└─ User receives: 0.01 ETH
└─ Staking timestamp resets for next cycle
Address (Sepolia): 0x... (Configure in staking.service.ts)
Key Functions:
// Deposit tokens to start staking
function depositTokens(uint256 tokenAmountToDeposit_) external
// Claim accumulated rewards after staking period
function claimReward() external
// Withdraw staked tokens (no time lock after initial deposit)
function withdrawTokens() external
// Admin: Update the staking period (seconds)
function setStakingPeriod(uint256 newStakingPeriod_) external onlyOwner
// Receive ETH for reward pool (owner only)
receive() external payable onlyOwnerEvents:
event DepositTokens(address userAddress, uint256 depositAmount_)
event WithdrawTokens(address userAddress, uint256 withdrawAmount_)
event SetStakingPeriod(uint256 newStakingPeriod_)
event etherSent(uint256 amount_)State Variables:
address public stakingToken // ERC-20 token address
uint256 public stakingPeriod // Duration in seconds (e.g., 86400 = 1 day)
uint256 public fixedStakingAmount // Amount users must stake (with decimals)
uint256 public rewardPerPeriod // ETH reward per completed period
mapping(address => uint256) userBalances // User staked balances
mapping(address => uint256) userStakingTimestamps // Staking start timesSimple ERC-20 implementation with public mint capability for testing.
Address (Sepolia): 0x... (Configure in staking.service.ts)
- Node.js 18+ & npm
- Git
- MetaMask browser extension
- Foundry (for smart contract development)
git clone https://github.com/yourusername/staking-dapp.git
cd staking-dappcd packages/contracts
# Install dependencies
npm install
# Build contracts
forge build
# Run tests
forge test
# View gas snapshots
forge snapshotcd ../frontend
# Install dependencies
npm install
# Update contract addresses
# Edit: src/app/services/staking.service.ts
# Set STAKING_ADDR and TOKEN_ADDR to your deployed contracts
# Start development server
npm startThe frontend will be available at http://localhost:4200
Run the complete test suite with Foundry:
cd packages/contracts
# Run all tests with verbose output
forge test -vv
# Run specific test file
forge test --match-path "test/Stakingdapp.t.sol" -vv
# Test with coverage
forge coverageThe test suite validates:
- Token deposit mechanisms
- Reward calculation logic
- Time-lock enforcement
- Access control (onlyOwner)
- Token transfer safety
- Edge cases and reverts
cd packages/contracts
# Create .env file
touch .env
# Add your variables
echo "SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY" >> .env
echo "PRIVATE_KEY=your_wallet_private_key" >> .env# Deploy StakingToken
forge script script/DeployStakingToken.s.sol:DeployStakingToken \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast
# Deploy Staking contract (use token address from above)
forge script script/DeployStaking.s.sol:DeployStaking \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcastCopy the deployed contract addresses and update packages/frontend/src/app/services/staking.service.ts:
const STAKING_ADDR = '0x...' // Your deployed Staking contract
const TOKEN_ADDR = '0x...' // Your deployed Token contractcd packages/frontend
# Build for production
npm run build
# Deploy to Vercel, Netlify, or your hosting provider
vercel deploy- OpenZeppelin Audited Code — Uses battle-tested OpenZeppelin contracts
- Access Control — Owner-only functions protected with
onlyOwnermodifier - Reentrancy Safe — Uses Checks-Effects-Interactions pattern
- Fixed Amount Mechanism — Prevents common staking vulnerabilities
NatSpec comments for all functions
Clear error messages in require statements
Safe ERC-20 transfer patterns
Timestamp-based time-lock mechanism
Separate token and staking contracts
For production deployment, we recommend:
- Third-party smart contract audit
- Formal verification of critical paths
- Bug bounty program
- Testnet staging with real users
Navbar — Wallet connection status, network indicator
Hero Section — Project overview and key statistics
Staking Dashboard — User staking data, claim interface
- Staked balance display
- Time remaining until reward claim
- Progress bar showing staking period completion
- Wallet token balance
- Transaction status updates
- MetaMask Connection — One-click wallet integration
- ethers.js — Type-safe blockchain interactions
- Reactive State — Angular signals for real-time updates
- Error Handling — User-friendly error messages
packages/
├── contracts/
│ ├── src/ # Smart contract source code
│ ├── test/ # Foundry test files
│ ├── script/ # Deployment scripts
│ ├── foundry.toml # Foundry configuration
│ └── .gitignore
│
└── frontend/
├── src/
│ ├── app/
│ │ ├── components/ # Reusable UI components
│ │ ├── services/ # Angular services (Wallet, Staking)
│ │ ├── app.module.ts # Root module
│ │ └── app-routing.module.ts
│ ├── assets/ # Images, fonts, styles
│ └── main.ts
├── angular.json # Angular CLI config
├── tsconfig.json # TypeScript config
├── package.json
└── .gitignore
Solidity:
- Follow Solidity style guide: https://docs.soliditylang.org/en/latest/style-guide.html
- Use
forge fmtbefore committing - NatSpec comments for public functions
TypeScript/Angular:
- Use ES6+ syntax
- Type all function parameters and returns
- Follow Angular style guide: https://angular.io/guide/styleguide
- Use meaningful variable names
# Smart Contracts
cd packages/contracts
forge build # Compile contracts
forge test # Run tests
forge fmt # Format code
forge snapshot # Gas snapshots
# Frontend
cd packages/frontend
npm start # Dev server (port 4200)
npm run build # Production build
npm run watch # Watch mode
npm test # Run testsEdit in deployment script or during construction:
constructor(
address stakingToken_, // ERC-20 token address
uint256 stakingPeriod_, // Seconds (e.g., 86400 for 1 day)
uint256 fixedStakingAmount_, // Amount with decimals (e.g., 100 * 10^18)
uint256 rewardPerPeriod_, // ETH reward (e.g., 0.01 * 10^18)
address owner_ // Contract owner
)File: packages/frontend/src/app/services/staking.service.ts
// Update these addresses after deployment
const STAKING_ADDR = '0x...' // Your Staking contract
const TOKEN_ADDR = '0x...' // Your StakingToken contractNetwork: Currently configured for Ethereum Sepolia Testnet
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Write tests for new features
- Update documentation
- Follow code style guidelines
- Ensure all tests pass before submitting PR
- Smart Contract ABI — Contract interface specifications
- Foundry Docs — Smart contract development guide
- Angular Docs — Frontend framework documentation
- ethers.js Docs — Web3 library reference
Solution: Ensure STAKING_ADDR and TOKEN_ADDR are set in staking.service.ts
Solution:
- Check if MetaMask is installed
- Verify you're on Sepolia testnet
- Click "Connect Wallet" button
Solution: Click "Approve" button to grant spending permission to the Staking contract
Solution: Wait for the configured staking period to elapse before claiming rewards
Built with by Sara Deleyto
Last Updated: June 2026
Version: 1.0.0