Advanced Staking Features
This guide covers advanced features and calculations available in the Steer Protocol staking system.
APR Calculations
The Annual Percentage Rate (APR) calculation in Steer Protocol considers multiple factors:
// Basic APR calculation
const apr = steerClient.staking.calculateAPR(
stakingPool,
rewardTokenPrice,
totalStakedUSD
);
Components of APR Calculation
Daily Emissions
// For single reward pools
const dailyRewardUSD = pool.dailyEmissionRewardA * rewardTokenPriceUSD;
const baseAPR = (dailyRewardUSD * 365 * 100) / totalStakedUSD;
// For dual reward pools
const dailyRewardBUSD = pool.dailyEmissionRewardB * rewardTokenPriceUSD;
const dualAPR = baseAPR + (dailyRewardBUSD * 365 * 100) / totalStakedUSD;Time-Weighted Considerations
- Rewards are distributed linearly over time
- APR adjusts based on remaining reward period
- Considers
periodFinish
timestamp
LP Token Value Calculations
For Steer vault LP tokens, value calculation involves multiple steps:
const lpValue = await steerClient.staking.getSteerLpTokenValue({
stakingPool: pool,
amount: lpTokenAmount,
token0UsdPrice: price0,
token1UsdPrice: price1
});
Underlying Token Calculation Process
Get Token Amounts
const tokensResponse = await withdrawClient.getTokensFromLp(
pool.staking.address,
lpTokenAmount
);Apply Token Prices
const token0Value = (token0Amount * token0Price) / (10 ** token0Decimals);
const token1Value = (token1Amount * token1Price) / (10 ** token1Decimals);
const totalValue = token0Value + token1Value;
Total Value Locked (TVL) Calculations
Calculate the total value locked in a staking pool:
const tvl = await steerClient.staking.getTotalStakingAmountInUsd(
pool,
token0Price,
token1Price
);
TVL Calculation Process
Get Total Supply
const totalSupply = await steerClient.staking.totalSupply(poolAddress);
Calculate Total Value
const value = await steerClient.staking.getSteerLpTokenValue({
stakingPool: pool,
amount: totalSupply,
token0UsdPrice,
token1UsdPrice
});
Transaction Preparation
For advanced integration scenarios, you can prepare transactions without executing them:
1. Prepare Stake Transaction
const stakeTx = await steerClient.staking.prepareStakeTx({
stakingPool: poolAddress,
amount: stakeAmount
});
if (stakeTx.success) {
const { address, abi, functionName, args } = stakeTx.data;
// Use these parameters with your own contract interaction logic
}
2. Prepare Withdraw Transaction
const withdrawTx = await steerClient.staking.prepareWithdrawTx({
stakingPool: poolAddress,
amount: withdrawAmount
});
3. Prepare Reward Claim Transaction
const claimTx = await steerClient.staking.prepareGetRewardTx({
stakingPool: poolAddress
});
Chain-Specific Features
Handle chain-specific requirements and validations:
// Validate chain support
const chainId = await publicClient.getChainId();
if (chainId !== pool.chainId) {
throw new Error(`Chain ID mismatch: expected ${pool.chainId}, got ${chainId}`);
}
// Get chain-specific pools
const chainPools = await steerClient.staking.getStakingPools({
chainId: 137, // Polygon
protocol: StakingProtocol.Forge
});
Error Handling and Validation
Implement comprehensive error handling:
async function safeStake(pool: StakingPool, amount: bigint) {
try {
// 1. Validate chain
const chainId = await publicClient.getChainId();
if (chainId !== pool.chainId) {
throw new Error('Invalid chain');
}
// 2. Check if pool is active
const now = Math.floor(Date.now() / 1000);
if (parseInt(pool.periodFinish) <= now) {
throw new Error('Pool expired');
}
// 3. Validate amount
const balance = await steerClient.staking.balanceOf(
pool.stakingPool,
userAddress
);
if (!balance.success) {
throw new Error('Failed to get balance');
}
// 4. Execute stake
const stakeTx = await steerClient.staking.stake({
stakingPool: pool.stakingPool,
amount
});
if (!stakeTx.success) {
throw new Error(stakeTx.error || 'Stake failed');
}
return stakeTx.data;
} catch (error) {
console.error('Stake error:', error);
throw error;
}
}
Performance Optimization
Tips for optimizing staking operations:
Batch Queries
// Get multiple pools data in parallel
const [totalSupply, userBalance, rewards] = await Promise.all([
steerClient.staking.totalSupply(poolAddress),
steerClient.staking.balanceOf(poolAddress, userAddress),
steerClient.staking.earned(poolAddress, userAddress)
]);Cache Pool Data
- Cache static pool data
- Implement refresh strategies for dynamic data
- Use websocket subscriptions for real-time updates
Efficient Filtering
// Combine multiple filters in one call
const filteredPools = await steerClient.staking.getStakingPools({
chainId: 137,
protocol: StakingProtocol.Forge,
isLive: true,
isDualRewards: true,
minDailyEmissionA: 1000
});
Best Practices
Value Calculations
- Always use proper decimal handling
- Consider price impact for large amounts
- Implement price feed validation
Transaction Management
- Implement proper nonce handling
- Consider gas price strategies
- Handle transaction replacement
State Management
- Track transaction status
- Implement proper error recovery
- Maintain user position history