Skip to main content
POST
eth_call
getEstimatedDelegatorReward
curl --request POST \
  --url https://api.example.com/eth_call
{
  "pending": {},
  "afterProximity": {},
  "afterTax": {}
}

Overview

Calculates estimated rewards for a delegator, showing the breakdown before proximity distribution, after proximity, and after tax. This is a preview function - it doesn’t modify state.

Contract Call Details

to
address
required
0x0000000000000000000000000000000000001000 (FenineSystem)
data
bytes
required
Function call: getEstimatedDelegatorReward(address,address)
delegator
address
required
The delegator address
validator
address
required
The validator address

Response

pending
uint256
Raw pending rewards before any deductions (wei)
afterProximity
uint256
Rewards after proximity distribution, including any bonus from ineligible uplines (wei)
afterTax
uint256
Final amount delegator will receive after tax deduction (wei)

Examples

const Web3 = require('web3');
const web3 = new Web3('https://rpc.fene.app');

const delegatorAddress = '0x1111111111111111111111111111111111111111';
const validatorAddress = '0x2222222222222222222222222222222222222222';

const abi = JSON.parse(await web3.getSystemContractABI());
const contract = new web3.eth.Contract(
  abi,
  '0x0000000000000000000000000000000000001000'
);

const estimate = await contract.methods.getEstimatedDelegatorReward(
  delegatorAddress,
  validatorAddress
).call();

console.log('Reward Breakdown:');
console.log('═'.repeat(50));
console.log(`Pending (raw):        ${web3.utils.fromWei(estimate.pending, 'ether')} FEN`);
console.log(`After proximity:      ${web3.utils.fromWei(estimate.afterProximity, 'ether')} FEN`);
console.log(`After tax (final):    ${web3.utils.fromWei(estimate.afterTax, 'ether')} FEN`);

// Calculate deductions
const proximityDeduction = BigInt(estimate.pending) - BigInt(estimate.afterProximity);
const taxDeduction = BigInt(estimate.afterProximity) - BigInt(estimate.afterTax);

console.log('\nDeductions:');
console.log(`Proximity paid:       ${web3.utils.fromWei(proximityDeduction.toString(), 'ether')} FEN`);
console.log(`Tax paid:             ${web3.utils.fromWei(taxDeduction.toString(), 'ether')} FEN`);

// Calculate percentages
if (BigInt(estimate.pending) > 0n) {
  const proximityPct = Number(proximityDeduction * 100n / BigInt(estimate.pending));
  const taxPct = Number(taxDeduction * 100n / BigInt(estimate.afterProximity));
  const finalPct = Number(BigInt(estimate.afterTax) * 100n / BigInt(estimate.pending));
  
  console.log('\nPercentages:');
  console.log(`Proximity:            ${proximityPct.toFixed(1)}%`);
  console.log(`Tax:                  ${taxPct.toFixed(1)}%`);
  console.log(`You keep:             ${finalPct.toFixed(1)}%`);
}

Reward Calculation Flow

Raw Pending Rewards (100%)

Proximity Distribution (~30%)
    ├─> Level 1-8 uplines (if eligible)
    └─> Residual split (50% back to claimer, 50% to validator)

After Proximity (~70% + bonuses)

Tax Deduction (10% default)
    ├─> Burn share
    └─> Dev share

Final Amount (~63% + bonuses)

Use Cases

async function shouldClaim(delegatorAddress, validatorAddress, minAmount) {
  const estimate = await contract.methods.getEstimatedDelegatorReward(
    delegatorAddress,
    validatorAddress
  ).call();
  
  const finalAmount = parseFloat(web3.utils.fromWei(estimate.afterTax, 'ether'));
  
  if (finalAmount < minAmount) {
    return {
      shouldClaim: false,
      reason: `Amount too small (${finalAmount.toFixed(4)} FEN < ${minAmount} FEN)`,
      estimate
    };
  }
  
  // Check gas cost vs reward
  const gasPrice = await web3.eth.getGasPrice();
  const estimatedGas = 300000; // Claim typically costs ~300k gas
  const gasCostWei = BigInt(gasPrice) * BigInt(estimatedGas);
  
  if (BigInt(estimate.afterTax) < gasCostWei) {
    return {
      shouldClaim: false,
      reason: 'Gas cost exceeds reward',
      gasCost: web3.utils.fromWei(gasCostWei.toString(), 'ether')
    };
  }
  
  return {
    shouldClaim: true,
    finalAmount,
    estimate
  };
}
async function getRewardBreakdownUI(delegatorAddress, validatorAddress) {
  const estimate = await contract.methods.getEstimatedDelegatorReward(
    delegatorAddress,
    validatorAddress
  ).call();
  
  if (estimate.pending == 0) {
    return { hasRewards: false };
  }
  
  const pending = parseFloat(web3.utils.fromWei(estimate.pending, 'ether'));
  const afterProximity = parseFloat(web3.utils.fromWei(estimate.afterProximity, 'ether'));
  const afterTax = parseFloat(web3.utils.fromWei(estimate.afterTax, 'ether'));
  
  return {
    hasRewards: true,
    breakdown: {
      gross: pending.toFixed(4),
      proximityPaid: (pending - afterProximity).toFixed(4),
      netBeforeTax: afterProximity.toFixed(4),
      taxPaid: (afterProximity - afterTax).toFixed(4),
      youReceive: afterTax.toFixed(4)
    },
    percentages: {
      proximityRate: ((pending - afterProximity) / pending * 100).toFixed(1),
      taxRate: ((afterProximity - afterTax) / afterProximity * 100).toFixed(1),
      finalYield: (afterTax / pending * 100).toFixed(1)
    }
  };
}
async function calculateAPY(delegatorAddress, validatorAddress) {
  const dcInfo = await contract.methods.getDelegatorInfo(
    delegatorAddress,
    validatorAddress
  ).call();
  
  if (dcInfo.status != 1) {
    return { error: 'Not actively staking' };
  }
  
  const estimate = await contract.methods.getEstimatedDelegatorReward(
    delegatorAddress,
    validatorAddress
  ).call();
  
  // Calculate time staked
  const currentBlock = await web3.eth.getBlockNumber();
  const blocksStaked = currentBlock - dcInfo.joinedAt;
  const daysStaked = (blocksStaked * 3) / (60 * 60 * 24);
  
  if (daysStaked < 1) {
    return { error: 'Need at least 1 day of data' };
  }
  
  const stake = parseFloat(web3.utils.fromWei(dcInfo.stakeAmount, 'ether'));
  const rewards = parseFloat(web3.utils.fromWei(estimate.afterTax, 'ether'));
  
  // Daily return
  const dailyReturn = rewards / daysStaked;
  const dailyYield = (dailyReturn / stake) * 100;
  
  // Annualized
  const apr = dailyYield * 365;
  const apy = (Math.pow(1 + dailyYield / 100, 365) - 1) * 100;
  
  return {
    stake,
    rewards,
    daysStaked: daysStaked.toFixed(1),
    dailyReturn: dailyReturn.toFixed(4),
    dailyYield: dailyYield.toFixed(2) + '%',
    apr: apr.toFixed(2) + '%',
    apy: apy.toFixed(2) + '%'
  };
}

Understanding the Numbers

Pending Rewards: The raw amount before any deductions. This is what you “earned” in theory.
After Proximity: After paying rewards to your uplines (max 8 levels). You may get bonuses if uplines are ineligible.
After Tax: The final amount you’ll receive. Tax typically includes burn (network deflation) and dev share.

Notes

This is an estimate. Actual rewards when claiming may differ slightly due to:
  • Additional rewards accumulated between estimation and claim
  • Changes in proximity configuration
  • Changes in tax rate
Use this before claiming to:
  1. Decide if it’s worth claiming (vs gas costs)
  2. Show users what they’ll receive
  3. Calculate yields and APY
  4. Build reward calculators

getDelegatorInfo

Get delegator stake info

getProximityConfig

See proximity distribution settings

claimDelegatorReward

Claim rewards (transaction)