DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: low
Invalid

`balanceDeployed` returns incorrect value

Summary

The balanceDeployed incorrectly includes the strategy's underlying token balance.

Vulnerability Details

The purpose of the balanceDeployed function is to provide accurate accounting for the deployed assets into the strategy. However it wrongly includes the underlying token balance of the strategy, instead of the claimable balance from the transmuter. In normal conditions, the strategy should not have any underlying tokens, because they can be retrieved only from the transmuter, but right after that are swapped back in alAssets.
Also, the assets in the total assets in the transmutter are the sum of the unexchanged alAssets and the claimable assets.

https://github.com/alchemix-finance/v2-foundry/blob/8915ef7b1714c16f9e260a4ef41c5f254d5b7f58/src/TransmuterV2.sol#L353-L370

function getUnexchangedBalance(address owner) external view override returns (uint256) {
Account storage account = accounts[owner];
if (account.occupiedTick <= satisfiedTick) {
return 0;
}
uint256 unexchangedBalance = account.unexchangedBalance;
uint256 exchanged = LiquidityMath.calculateProduct(
unexchangedBalance,
ticks.getWeight(account.occupiedTick, ticks.position)
);
@> unexchangedBalance -= exchanged;
return unexchangedBalance;
}

As shown above, the unexchanged assets does not account for the claimable assets.

https://github.com/alchemix-finance/v2-foundry/blob/8915ef7b1714c16f9e260a4ef41c5f254d5b7f58/src/TransmuterV2.sol#L377

function getClaimableBalance(address owner) external view override returns (uint256 claimableBalance) {
return _normalizeDebtTokensToUnderlying(_getExchangedBalance(owner));
}
function _getExchangedBalance(address owner) internal view returns (uint256 exchangedBalance) {
Account storage account = accounts[owner];
if (account.occupiedTick <= satisfiedTick) {
exchangedBalance = account.exchangedBalance;
exchangedBalance += account.unexchangedBalance;
return exchangedBalance;
}
exchangedBalance = account.exchangedBalance;
uint256 exchanged = LiquidityMath.calculateProduct(
account.unexchangedBalance,
ticks.getWeight(account.occupiedTick, ticks.position)
);
exchangedBalance += exchanged;
return exchangedBalance;
}

From the snippets above it is visible that unexchanged balance does not include the claimable one, so it will not provide the full amount of assets in the transmuter. Moreover, the claimable balance is not in the strategy's balance, as the function below suggests.

function balanceDeployed() public view returns (uint256) {
@> return transmuter.getUnexchangedBalance(address(this)) + underlying.balanceOf(address(this)) + asset.balanceOf(address(this));
}

Impact

The balanceDeployed function will provide inaccurate value.

Tools Used

Manual review

Recommendations

function balanceDeployed() public view returns (uint256) {
- return transmuter.getUnexchangedBalance(address(this)) + underlying.balanceOf(address(this)) + asset.balanceOf(address(this));
+ return transmuter.getUnexchangedBalance(address(this)) + transmuter.getClaimableBalance(address(this)) + asset.balanceOf(address(this));
}
Updates

Appeal created

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

balanceDeployed should include claimable

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 9 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.