Dria

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

Unused fee are not returned to requester

Vulnerability Details

When request LLM generation, fee is calculated, transfer and linked with taskId as below:

function request(
bytes32 protocol,
bytes memory input,
bytes memory models,
LLMOracleTaskParameters calldata parameters
) public onlyValidParameters(parameters) returns (uint256) {
(uint256 totalfee, uint256 generatorFee, uint256 validatorFee) = getFee(parameters);
. . . . . .
feeToken.transferFrom(msg.sender, address(this), totalfee);
. . . . . .
requests[taskId] = TaskRequest({
requester: msg.sender,
protocol: protocol,
input: input,
parameters: parameters,
status: TaskStatus.PendingGeneration,
generatorFee: generatorFee,
validatorFee: validatorFee,
platformFee: platformFee,
models: models
});
}

getFee()function:

function getFee(LLMOracleTaskParameters calldata parameters)
public
view
returns (uint256 totalFee, uint256 generatorFee, uint256 validatorFee)
{
uint256 diff = (2 << uint256(parameters.difficulty));
generatorFee = diff * generationFee; // <--
validatorFee = diff * validationFee; // <--
totalFee =
platformFee + (parameters.numGenerations * (generatorFee + (parameters.numValidations * validatorFee)));
}

When request choose to have validator, when finalize validation, some of validator and generator wont be rewarded due to score conditions:

function finalizeValidation(uint256 taskId) private {
TaskRequest storage task = requests[taskId];
// 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);
}
}
// 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;
}
// now, we have the scores for each generation
// compute stddev for these and pick the ones above a threshold
uint256[] memory generationScores = new uint256[]();
for (uint256 g_i = 0; g_i < task.parameters.numGenerations; g_i++) {
generationScores[g_i] = responses[taskId][g_i].score;
}
// compute the mean and standard deviation
(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);
}
}
}

These tokens are not transfered back to the requester and stay in the contract

Impact

User are not able to get unused tokens back.

Tools Used

Manual review

Recommendations

Transfer back unused tokens for user.

Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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