Dria

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

Update state requests or Purchase requests occurring at the end of the phase will not process

Description

BuyerAgent can make two requests either purchase request or StateUpdate request.

First, he should make a BuyerAgent::oraclePurchaseRequest() request to buy all the items he needs, then call BuyerAgent::purchase() after His task gets completed by Oracle coordinator to buy the items he wants.

Then, in case of buying new items success he should make BuyerAgent::oracleStateRequest() to update his state after buying items, then call BuyerAgent::updateState() to change his state.

The problem here is that there is no check when BuyerAgent::oraclePurchaseRequest() or BuyerAgent::oracleStateRequest() get requested. There is just a check that enforces firing both of them on a given Phase.

We will explain the problem in the purchasing process, but it also existed in updating the state process.

When requesting to purchase items, we check that we are at a Round that is at Buy Phase, and when doing the actual purchase the Round should be the same as the Round we call BuyerAgent::oraclePurchaseRequest() as well as the phase should be Buy.

swan/BuyerAgent.sol#L189-L195 | swan/BuyerAgent.sol#L222-L230

function oraclePurchaseRequest(bytes calldata _input, bytes calldata _models) external onlyAuthorized {
// check that we are in the Buy phase, and return round
>> (uint256 round,) = _checkRoundPhase(Phase.Buy);
>> oraclePurchaseRequests[round] =
swan.coordinator().request(SwanBuyerPurchaseOracleProtocol, _input, _models, swan.getOracleParameters());
}
// -------------------
function purchase() external onlyAuthorized {
// check that we are in the Buy phase, and return round
>> (uint256 round,) = _checkRoundPhase(Phase.Buy);
// check if the task is already processed
uint256 taskId = oraclePurchaseRequests[round];
>> if (isOracleRequestProcessed[taskId]) {
revert TaskAlreadyProcessed();
}
}

For BuyerAgent::purchase() to process, we should be at the same Round as well as at the Phas, which is Buy in that example, when we fired BuyerAgent::oraclePurchaseRequest().

the flow is as follows:

  1. BuyerAgent::oraclePurchaseRequest()

  2. Generators will generate output in LLM Coordinator

  3. Validators will validate in LLM Coordinator

  4. Task marked as completed

  5. BuyerAgent::purchase()

There is time will be taken for generators and validators to make the output (complete the task).

So if BuyerAgent::oraclePurchaseRequest gets fired before the end of Buying Phase with little time, this will make the Buy ends and enters Withdraw phase before Generators and Validaotrs Complete that task, resulting in losing Fees paid by the BuyerAgent when requesting the purchase request.

Proof of Concept

  • BuyerAgent wants to buy a given item

  • His Round is 10 and we are at the Buy phase know

  • The buying phase is about to end there is just one Hour left

  • BuyerAgent Fired BuyerAgent::oraclePurchaseRequest()

  • Fees got paid and we are waiting for the task to complete

  • Generators and Validators took 6 Hours to complete this task

  • Know, the BuyerAgent Round is 10 and the Phase is Withdraw

  • calling BuyerAgent::purchase() will fail as we are not in the Buy Phase

The problem is that there is no time left for requesting and firing, if the request occur at the end of the Phase, finalizing the request either purach or update state will fail, as the phase will end.

We are doing Oracle and off-chain computations for the given task, and the time to finalize (complete) a task differs from one task to another according to difficulty.

There are three things here that make this problem occur.

  1. If the Operator is not active, the BuyerAgent should call the request himself.

  2. If Completing the Task process takes too much time, this can occur for Tasks that require a lot of validations, or difficulty is high

  3. if there is a High Demand for a given request the Operator may finalize some of them at the end.

Recommendations

Don't allow requests for all the phase ranges.

For example, In case we Have 7 days for Buy phase, we should Stop requesting purchase requests at the end of 2 days to not make requests occur at the last period of the phase resulting in an insolvable state if it gets completed after 2 days.

This is just an example. The period to stop requested should be determined according to the task itself (num of Generators/Validators needed and its difficulty).

Updates

Lead Judging Commences

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

There is no guarantee that task will be completed and buyerAgent will get a response to its purchaseRequest before the round ends, but that was already paid for

Appeal created

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

There is no guarantee that task will be completed and buyerAgent will get a response to its purchaseRequest before the round ends, but that was already paid for

Support

FAQs

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