Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

USDC from vault settlements is potentially allocated to deregistered `engines`

Summary

When an engine is deregistered via configureEngine(), there's no validation to ensure it has no connected vaults. This allows disabled engines to continue receiving USDC allocations from vault debt settlements during the period between engine deregistration and vault migration, leading to potential fund misallocation.

Vulnerability Details

During engine deregistration via configureEngine(engine, _, false), there's no check to ensure the engine isn't still connected to any vaults.

function configureEngine(address engine, address usdToken, bool shouldBeEnabled) external onlyOwner {
// ... input validation ...
if (!shouldBeEnabled) {
// @audit-info No check for connected vaults
marketMakingEngineConfiguration.isRegisteredEngine[engine] = false;
marketMakingEngineConfiguration.usdTokenOfEngine[engine] = address(0);
}
// ...
}

Here, only the engine's isRegisteredEngine flag is set to false and its usdToken erased which creates a window of vulnerability between:

  1. Engine being deregistered

  2. Vaults being updated to point to a new engine

Here is the Vault-engine connection in Vault.Data:

struct Data {
// ...
address engine; // @audit-info Reference to connected engine
// ...
}

It is true that the owner can invoke setVaultEngine() right after and update the engine for any vaults affected through the following function call:

function setVaultEngine(uint128 vaultId, address engine) external onlyOwner { //@audit-ok
Vault.Data storage vaultData = Vault.load(vaultId);
>> vaultData.engine = engine;
emit LogSetVaultEngine(vaultId, engine);
}

However, these two transations cannot happen at the same time.

During this window, a keeper can call settleVaultsDebt(). If the vault is in debt, it swap its assets to USDC which is then allocated to the vault's engine in the following line:

// allocate the usdc acquired to back the engine's usd token
UsdTokenSwapConfig.load().usdcAvailableForEngine[vault.engine] += ctx.usdcOutX18.intoUint256();

However, since the engine just got deregistered, this value is still allocated to it.

Now, notice that the setVaultEngine() handles a single vault at a time given by vaultId. So if one engine is tied to multiple vaults, the likelihood of this issue increases as the keeper can invoke settleVaultsDebt() on any vaultsIds.

Issue Scenario:

  1. Vault V is connected to Engine A

  2. Owner deregisters Engine A via configureEngine(engineA, _, false)

  3. Before the owner can migrate Vault V to a new engine:

    • A keeper calls settleVaultsDebt()

    • USDC from the settlement is allocated to Engine A's usdcAvailableForEngine balance

  4. Even though Engine A is deregisterd, it has received new USDC allocations.

Impact

  • USDC from vault settlements can be allocated to disabled engines

  • System accounting becomes inconsistent as disabled engines continue accumulating balances

Tools Used

Manual Review

Recommendations

  1. The configureEngine() function should be modified to prevent disabling an engine that still has connected vaults

  2. Alternatively, the system could be designed to automatically migrate vault connections when disabling an engine.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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