Dria

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

`LLMOracleCoordinator.respond()` & `LLMOracleCoordinator.validate()` functions will be DoS'd if USDT Is Used As A Fee Token

## Summary
`LLMOracleCoordinator.respond()` & `LLMOracleCoordinator.validate()` functions will be Dos'd if USDT token is used to reward the generators and validators if anyone of these parties hasn't spent their allowance before being rewarded for their new task.
## Vulnerability Details
- Generators and validators are rewarded for their work by increasing their `feeToken` allowances via `LLMOracleCoordinator._increaseAllowance()`:
```javascript
function _increaseAllowance(address spender, uint256 amount) internal {
feeToken.approve(spender, feeToken.allowance(address(this), spender) + amount);
}
```
- **Generators** are rewarded for their work in either one of the two following situations:
1. When there's no validation required for the task, then each generator is rewarded with the `generatorFee`:
```javascript
function respond(
uint256 taskId,
uint256 nonce,
bytes calldata output,
bytes calldata metadata
) public onlyRegistered(LLMOracleKind.Generator) onlyAtStatus(taskId, TaskStatus.PendingGeneration) {
//...
// send rewards to the generator if there is no validation
if (task.parameters.numValidations == 0) {
_increaseAllowance(msg.sender, task.generatorFee);
}
//...
}
```
2. During the `finalizeValidation()` process when they got a score greator than the acceptable value:
```javascript
function finalizeValidation(uint256 taskId) private {
//...
// 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);
}
}
//...
}
```
- **Validators** are getting their rewards during the `finalizeValidation()` process when they give a score within the acceptable calculated range:
```javascript
function finalizeValidation(uint256 taskId) private {
//...
if ((score >= _mean - _stddev) && (score <= _mean + _stddev)) {
innerSum += score;
innerCount++;
// send validation fee to the validator
_increaseAllowance(validations[taskId][v_i].validator, task.validatorFee);
}
//...
}
```
## Impact
So if the reward token is USDT that must approve to zero first before changing the allowance if the previous allowance is non-zero; then if any of the task generators or validators hasn't spent their reward allowance, this will result in DoSing the `LLMOracleCoordinator.respond()` and/or `LLMOracleCoordinator.validate()` functions, resulting in the task being uncompleted, thus `LLMOracleCoordinator.getBestResponse()` will revert for this task when called by the buyer agent to finalize the purchase , and this would require the buyer agent to make a new `oraclePurchaseRequest()` and paying fees to overwrite the old request that can't be completed due to the aforementioned issue.
## Proof of Concept
[LLMOracleCoordinator.\_increaseAllowance() function](https://github.com/Cyfrin/2024-10-swan-dria/blob/c8686b199daadcef3161980022e12b66a5304f8e/contracts/llm/LLMOracleCoordinator.sol#L396C1-L398C6)
```javascript
function _increaseAllowance(address spender, uint256 amount) internal {
feeToken.approve(spender, feeToken.allowance(address(this), spender) + amount);
}
```
## Tools Used
Manual Review.
## Recommendations
If USDT is going to be used as a feeToken in the `LLMOracleCoordinator` contract; update the `LLMOracleCoordinator._increaseAllowance()` to approve(0) first for the generator/validator before updating allowance to a new value.
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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