Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: medium
Valid

Implementing separate fee tracking to ensure accurate distribution of platform, generation, and validation Fees

Summary

In the LLMOracleCoordinator contract, platform, generation, and validation fees are not separately tracked, allowing the contract owner to withdraw all accumulated funds, including those intended for validators and generators. This lack of separate tracking could prevent validators and generators from receiving their designated fees, compromising the integrity of the reward distribution mechanism.

Vulnerability Details

Currently, the contract lacks dedicated variables to track the balances of platform fees. The withdrawPlatformFees function allows the contract owner to withdraw the entire token balance, which includes fees meant for validators and generators. This oversight means validators and generators may not receive their earned fees if the owner withdraws the total balance.

Impact

  • Loss of Earnings: Validators and generators may not receive their expected fees, which undermines trust in the contract and prevents fair compensation.

  • Compromised Fee Distribution: The lack of proper tracking for each fee type could result in unintended fund distribution, impacting the contract’s reliability.

Tools Used

Recommendations

To ensure proper fund distribution, implement tracking for platformFee. Here’s how:

  1. Define Separate Fee Balances:
    Add new state variables:

    uint256 public platformFeeBalance;
  2. Allocate Fees to Respective Balances:
    Update the request, finalizeValidation functions to allocate collected fees to the respective balances:

    In request function

    feeToken.transferFrom(msg.sender, address(this), totalfee);
    + platformFeeBalance += platformFee;

    In finalizeValidation function

    + uint256 restValidationFees = task.validatorFee * task.parameters.numGenerations * task.parameters.numValidations;
    // compute score for each generation
    for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
    // get the scores for this generation, i.e. the g_i-th element of each validation
    uint256[] memory scores = new uint256[]();
    for (uint256 v_i = 0; v_i < task.parameters.numValidations; v_i++) {
    scores[v_i] = validations[taskId][v_i].scores[g_i];
    }
    // compute the mean and standard deviation
    (uint256 _stddev, uint256 _mean) = Statistics.stddev(scores);
    // compute the score for this generation as the "inner-mean"
    // and send rewards to validators that are within the range
    uint256 innerSum = 0;
    uint256 innerCount = 0;
    for (uint256 v_i = 0; v_i < task.parameters.numValidations; ++v_i) {
    uint256 score = scores[v_i];
    if ((score >= _mean - _stddev) && (score <= _mean + _stddev)) {
    innerSum += score;
    innerCount++;
    // send validation fee to the validator
    _increaseAllowance(validations[taskId][v_i].validator, task.validatorFee);
    + restValidationFees -= task.validatorFee;
    }
    }
    // set score for this generation as the average of inner scores
    uint256 inner_score = innerCount == 0 ? 0 : innerSum / innerCount;
    responses[taskId][g_i].score = inner_score;
    }
    + platformFeeBalance += restValidationFees;
    ...
    // compute the mean and standard deviation
    + uint256 restGenerationFees = task.generatorFee * task.parameters.numGenerations;
    (uint256 stddev, uint256 mean) = Statistics.stddev(generationScores);
    for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
    // ignore lower outliers
    if (generationScores[g_i] >= mean - generationDeviationFactor * stddev) {
    _increaseAllowance(responses[taskId][g_i].responder, task.generatorFee);
    + restGenerationFees -= task.generatorFee;
    }
    }
    + platformFeeBalance += restGenerationFees;
  3. Update Withdrawal Function:
    Modify withdrawPlatformFees to withdraw only platformFeeBalance:

function withdrawPlatformFees() public onlyOwner {
- feeToken.transfer(owner(), feeToken.balanceOf(address(this)));
+ feeToken.transfer(owner(), platformFeeBalance);
+ platformFeeBalance = 0;
}

This approach ensures validators and generators receive their fees, while platform fees remain accessible to the contract owner, preserving the contract’s intended fee distribution logic.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`withdrawPlatformFees` withdraws the entire balance

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.