Repository: shardeum/shardeum
File: src/tx/claimReward.ts (TypeScript)

Code before:
  // special case for seed nodes:
  // they have 0 rewardStartTime and will not be rewarded but the claim tx should still be applied
  if (nodeAccount.rewardStartTime === 0) {
    nestedCountersInstance.countEvent('shardeum-staking', `seed node claim reward ${nodeAccount.id}`)
    durationInNetwork = 0
  }

  if (nodeAccount.rewarded === true) {
    nestedCountersInstance.countEvent('shardeum-staking', `applyClaimRewardTx fail already rewarded`)
    //throw new Error(`applyClaimReward failed already rewarded`)
    shardus.applyResponseSetFailed(applyResponse, `applyClaimReward failed already rewarded`)
    return
  }

  nodeAccount.rewardEndTime = tx.nodeDeactivatedTime

  //we multiply fist then devide to preserve precision
  let rewardedAmount = nodeRewardAmount * BigInt(durationInNetwork * 1000) // Convert from seconds to milliseconds
  //update total reward var so it can be logged
  rewardedAmount = rewardedAmount / nodeRewardInterval
  //re-parse reward since it was saved as hex
  nodeAccount.reward = _base16BNParser(nodeAccount.reward)
  //add the reward because nodes can cycle without unstaking
  const nodeRewardCap = _base16BNParser(network.current.nodeRewardCap)
  const finalReward = nodeAccount.reward + rewardedAmount
  if (finalReward > nodeRewardCap) {
    nestedCountersInstance.countEvent('shardeum-staking', 'applyClaimRewardTx failed nodeRewardCap exceeded')
    if (logFlags.dapp_verbose) {
      console.log(
        `applyClaimRewardTx fail nodeRewardCap exceeded: finalReward=${_readableSHM(finalReward)}, cap=${_readableSHM(
          nodeRewardCap
        )}, nominee=${tx.nominee}`
      )
    }
    shardus.applyResponseSetFailed(
      applyResponse,
      `applyClaimReward failed: nodeRewardCap exceeded for nominee ${tx.nominee} finalReward: ${_readableSHM(
        finalReward
      )}, cap: ${_readableSHM(nodeRewardCap)}`
    )
    return
  }
  nodeAccount.reward = finalReward

  nodeAccount.timestamp = txTimestamp

  nodeAccount.rewarded = true

  // update the node account historical stats
  nodeAccount.nodeAccountStats.totalReward = _base16BNParser(nodeAccount.nodeAccountStats.totalReward) + rewardedAmount
  nodeAccount.nodeAccountStats.history.push({

Review comment (suggestion):
please apply the node reward cap to the rewarded amount.     The final reward really hard to cap, because someone could choose to not unstake for a very long time and allow the reward to build up.

The cap is just for the amount of reward on a single rotation in the network which we do have a better idea about.

Code after:
  // special case for seed nodes:
  // they have 0 rewardStartTime and will not be rewarded but the claim tx should still be applied
  if (nodeAccount.rewardStartTime === 0) {
    nestedCountersInstance.countEvent('shardeum-staking', `seed node claim reward ${nodeAccount.id}`)
    durationInNetwork = 0
  }

  if (nodeAccount.rewarded === true) {
    nestedCountersInstance.countEvent('shardeum-staking', `applyClaimRewardTx fail already rewarded`)
    //throw new Error(`applyClaimReward failed already rewarded`)
    shardus.applyResponseSetFailed(applyResponse, `applyClaimReward failed already rewarded`)
    return
  }

  nodeAccount.rewardEndTime = tx.nodeDeactivatedTime

  //we multiply fist then devide to preserve precision
  let rewardedAmount = nodeRewardAmount * BigInt(durationInNetwork * 1000) // Convert from seconds to milliseconds
  //update total reward var so it can be logged
  rewardedAmount = rewardedAmount / nodeRewardInterval
  //re-parse reward since it was saved as hex
  nodeAccount.reward = _base16BNParser(nodeAccount.reward)
  //add the reward because nodes can cycle without unstaking
  const nodeRewardCap = _base16BNParser(network.current.nodeRewardCap)

  if (rewardedAmount > nodeRewardCap) {
    nestedCountersInstance.countEvent('shardeum-staking', 'applyClaimRewardTx failed nodeRewardCap exceeded')
    if (logFlags.dapp_verbose) {
      console.log(
        `applyClaimRewardTx fail nodeRewardCap exceeded: rewardedAmount=${_readableSHM(
          rewardedAmount
        )}, cap=${_readableSHM(nodeRewardCap)}, nominee=${tx.nominee}`
      )
    }
    shardus.applyResponseSetFailed(
      applyResponse,
      `applyClaimReward failed: nodeRewardCap exceeded for nominee ${tx.nominee} rewardedAmount: ${_readableSHM(
        rewardedAmount
      )}, cap: ${_readableSHM(nodeRewardCap)}`
    )
    return
  }

  nodeAccount.reward = nodeAccount.reward + rewardedAmount

  nodeAccount.timestamp = txTimestamp

  nodeAccount.rewarded = true

  // update the node account historical stats
  nodeAccount.nodeAccountStats.totalReward = _base16BNParser(nodeAccount.nodeAccountStats.totalReward) + rewardedAmount