A comprehensive NFT staking platform built with Foundry, featuring ERC1155 NFTs and ERC20 reward tokens with time-based APR rewards.
- ERC1155 NFT Staking: Stake multiple NFTs with different amounts
- ERC20 Reward System: Earn NPY tokens based on staking duration
- Time-based APR: Progressive reward rates for longer staking periods
- Built with Foundry: Modern development environment with fast testing
| Staking Duration | APR Rate | Reward Token |
|---|---|---|
| Less than 1 month | 0% |
NPY |
| 1-6 months | 5% |
NPY |
| 6-12 months | 10% |
NPY |
| After 12 months | 15% |
NPY |
- Symbol: NPY
- Name: NappyToken
- Features: Mintable, Ownable, ERC20Permit
- Standard: ERC1155
- Features: Mintable, Burnable, Ownable
- Metadata: IPFS-hosted images
- Core Function: NFT staking with time-based rewards
- Reward Calculation:
(Rate Γ Time Γ Amount Γ 10^18) / (Month Γ 12 Γ 100) - Features: Stake/Unstake NFTs, automatic reward distribution
git clone git@github.com:karangoraniya/nft-staking.git
cd nft-staking
forge installforge install OpenZeppelin/openzeppelin-contractsforge build# Run all tests
forge test
# Run with verbose output
forge test -vv
# Run specific test file
forge test --match-path test/Staking.t.sol
# Run with gas reporting
forge test --gas-report# Format all Solidity files
forge fmt
# Check formatting without making changes
forge fmt --check# Create gas usage snapshots for optimization
forge snapshot
# Compare gas usage with previous snapshot
forge snapshot --diffforge coverage# Start local Ethereum node (like Ganache)
anvil
# Start with specific port
anvil --port 8545
# Start with specific accounts
anvil --accounts 10# Get contract balance
cast balance <address>
# Call contract function
cast call <contract_address> "balanceOf(address)" <user_address>
# Send transaction
cast send <contract_address> "mint(address,uint256)" <to_address> <amount> --private-key <key>
# Get transaction receipt
cast receipt <tx_hash>
# Convert values
cast to-wei 1 ether
cast from-wei 1000000000000000000# Start local node
anvil
# Deploy contracts (in separate terminal)
forge script script/NappyToken.s.sol --rpc-url http://localhost:8545 --broadcast --private-key <anvil_private_key>
forge script script/NFTMint.s.sol --rpc-url http://localhost:8545 --broadcast --private-key <anvil_private_key>
forge script script/Staking.s.sol --rpc-url http://localhost:8545 --broadcast --private-key <anvil_private_key>Create a .env file in the root directory:
# Private Keys
PRIVATE_KEY=YOUR_PRIVATE_KEY
SENDER_ADDRESS=YOUR_WALLET_ADDRESS
# RPC URLs
ETH_HOLESKY_RPC_URL=ETH_HOLESKY_RPC
BASE_SEPOLIA_RPC_URL=BASE_SEPOLIA__RPC
# API Keys for Verification, For all network
ETHERSCAN_API_KEY=ETHERSCAN_API_KEYLoad environment variables:
source .envOr use Foundry's built-in env loading:
# Foundry automatically loads .env file
forge script script/Staking.s.sol \
--rpc-url $BASE_SEPOLIA_RPC_URL \
--broadcast \
--verify \
--sender $SENDER_ADDRESS \
--private-key $PRIVATE_KEY \
--etherscan-api-key $ETHERSCAN_API_KEYforge script script/Staking.s.sol \
--rpc-url $ETH_HOLESKY_RPC_URL \
--broadcast \
--verify \
--sender $SENDER_ADDRESS \
--private-key $PRIVATE_KEY \
--etherscan-api-key $ETHERSCAN_API_KEYWhen deploying, use the --verify flag for automatic verification:
forge script script/Staking.s.sol \
--rpc-url $ETH_HOLESKY_RPC_URL \
--broadcast \
--verify \
--etherscan-api-key $ETHERSCAN_API_KEY \
--private-key $PRIVATE_KEYIf you deployed without --verify, you can verify contracts manually:
Verify NappyToken:
forge verify-contract <NAPPY_TOKEN_ADDRESS> \
src/NappyToken.sol:NappyToken \
--chain-id 17000 \
--constructor-args $(cast abi-encode "constructor(address)" "<INITIAL_OWNER_ADDRESS>") \
--etherscan-api-key $ETHERSCAN_API_KEY \
--watchVerify NFTMint:
forge verify-contract <NFT_MINT_ADDRESS> \
src/NFTMint.sol:NFTMint \
--chain-id 17000 \
--constructor-args $(cast abi-encode "constructor(address)" "<INITIAL_OWNER_ADDRESS>") \
--etherscan-api-key $ETHERSCAN_API_KEY \
--watchVerify Staking:
forge verify-contract <STAKING_ADDRESS> \
src/Staking.sol:Staking \
--chain-id 17000 \
--constructor-args $(cast abi-encode "constructor(address,address)" "<TOKEN_ADDRESS>" "<NFT_ADDRESS>") \
--etherscan-api-key $ETHERSCAN_API_KEY \
--watch- Holesky Testnet:
17000 - Base Sepolia:
84532
forge verify-check --chain-id 17000 <GUID> $ETHERSCAN_API_KEY- NappyToken: 0x149fA8e35149E1E287827B047F01F79D724f28Fa
- NFTMint: 0xE993643D43477EF5be7ff3EfAdAb7cbfAbc1B35f
- Staking: 0x8f42244d2c622Fd97e199BD81063Fa143AE07C5d
- NappyToken: 0xe733d6D6FE687665257cfF03E31CB54BD67e2367
- NFTMint: 0xe627DC5c2a89d26055CF48bDdb6525E9d8B47CbD
- Staking: 0x223769226732eBb9C6B263d7Bf3bEf44fE59E72E
test/
βββ NappyToken.t.sol # ERC20 token tests
βββ NFTMint.t.sol # ERC1155 NFT tests
βββ Staking.t.sol # Staking functionality tests
- β Token creation and minting
- β NFT minting and transfers
- β Staking mechanics
- β Reward calculations
- β Time-based rate changes
- β Access control
βββ src/
β βββ NappyToken.sol # ERC20 reward token
β βββ NFTMint.sol # ERC1155 NFT contract
β βββ Staking.sol # Main staking contract
βββ script/
β βββ NappyToken.s.sol # Token deployment script
β βββ NFTMint.s.sol # NFT deployment script
β βββ Staking.s.sol # Staking deployment script
βββ test/
β βββ NappyToken.t.sol # Token tests
β βββ NFTMint.t.sol # NFT tests
β βββ Staking.t.sol # Staking tests
βββ lib/ # Dependencies
βββ foundry.toml # Foundry configuration
βββ README.md
- Get NPY Tokens: Mint NPY tokens (owner only)
- Get NFTs: Mint ERC1155 NFTs (owner only)
- Approve Staking: Approve the staking contract to transfer your NFTs
- Stake NFTs: Call
stakeNFT(tokenId, amount) - Wait & Earn: Earn progressively higher APR based on staking duration
- Unstake: Call
unStakeNFT(tokenId, amount)to get NFTs back + rewards
- Rewards are calculated based on the time elapsed since staking
- Minimum 1 month staking required for any rewards
- Progressive APR encourages longer-term staking
- Contract owner must fund the staking contract with NPY tokens for rewards
# General Foundry help
forge --help
# Anvil help
anvil --help
# Cast help
cast --help
# Chisel help
chisel --help
# Specific command help
forge test --help
forge script --helpBuild Errors:
# Clean cache and rebuild
forge clean
forge buildTest Failures:
# Run tests with maximum verbosity to see details
forge test -vvvv
# Run specific failing test
forge test --match-test testFunctionName -vvvDeployment Issues:
# Verify RPC URL is correct
cast chain-id --rpc-url <your_rpc_url>
# Check account balance
cast balance <your_address> --rpc-url <your_rpc_url>This project is licensed under the GPL-3.0 License.
Karan J Goraniya