CurveAdapter is using outdated Curve Registry Exchange Contract, which was never deployed to Arbitrum.
Curve adapter is using exchange_with_best_rate
function of Curve Registry Exchange Contract. According to curve doc, this contract was deployed to Mainnet on Dec 2022, and got outdated.
The contract was never deployed to Arbitrum.
And even on mainnet, it has very few available pools (only supports USDC <-> USDT, WETH <-> USDT, WBTC <-> USDT, considering all in-scope tokens)
Any operation involving CurveAdapter will revert
Manual review
Use CurveRouterNG, which also got deployed to Arbitrum
Some market making leaves are not fully ERC-7201 compatible.
The following is ERC-7201 formula
However, the following storage locations don't apply & ~bytes32(uint256(0xff))
:
ASSET_SWAP_STRATEGY_LOCATION
COLLATERAL_LOCATION
CREDIT_DELEGATION_LOCATION
DEX_SWAP_STRATEGY_LOCATION
MARKET_FEE_LOCATION
MARKET_LOCATION
MARKET_MAKING_ENGINE_CONFIGURATION_LOCATION
SWAP_LOCATION
USD_TOKEN_SWAP_CONFIG_LOCATION
VAULT_LOCATION
WITHDRAWAL_REQUEST_LOCATION
Moreover, all storages don't apply NatSpec tag annotation
Incompatibility with ERC-7201, unlike what's stated in the doc
Manual Review
Apply 0xff
negation mask
Apply NatSpec tag annotation to storage data
autoDeleverageStartThreshold
and autoDeleverageExponentZ
to 0MarketMakingEngineConfigurationBranch.configureMarket
prevents setting market's autoDeleverageStartThreshold
and autoDeleverageExponentZ
to 0, even though 0 is totally valid value for those parameters
According to Market.getAutoDeleverageFactor
, one can derive the following formula:
where
is autoDeleverageStartThreshold
is autoDeleverageEndThreshold
is autoDeleverageExponentZ
For the above formula, 0 is a totally valid value for autoDeleverageStartThreshold
and autoDeleverageExponentZ
. For example:
If you want to set autoDeleverageFactor
to 1 for all possible scenarios, you can set to autoDelverageExponentZ
to 1
If you want always set non-1 autoDeleverageFactor
for all possible scenarios, you should set autoDeleverageStartThreshold
to 0
However, MarketMakingEngineConfigurationBranch.configureMarket
prevents such setup:
One will have to set autoDeleverageStartThreshold
and autoDeleverageExponentZ
to dust values (1e-18) to achieve the same behavior.
However this approach will waste gas and bring precision error.
Remove ZeroInput checks for autoDeleverageStartThreshold
and autoDeleverageExponentZ
In StabilityBranch, user can initiate a request to swap usd token to vault asset. Later, system keeper can fulfill the swap request. However, usd token used when initiating a swap is a usd token engine of corresponding vault, while usd token usded in fulfill swap is a parameter of the function. This inconsistency might bring swap request failure. Refund request can also fail due to similar reason.
When user initiates a swap, current vault engine's usd token is transferred from user to market making engine:
However, when the swap request is fulfilled, a usd token corresponding to engine
parameter of the system keeper is burned:
When multiple usd tokens are used, different usd tokens might be used during initiateSwap
and fulfillSwap
.
In this case, fulfillSwap
might revert with insufficient balance error.
refundSwap
can also fail due to similar reason i.e. refundSwap
uses engine
parameter instead of vault's engine.
Manual Review
Use vault.engine
instead of function parameter engine
in StabilityBranch.fulfillSwap
and StabilityBranch.refundSwap
Vault.recalculateVaultsCreditCapacity
does not check vaults and markets live statusVault.recalculateVaultsCreditCapacity
is an important function to update states of connected vaults and markets. However, it does not take live status into consideration. It can lead to invalid accounting of vaults and markets.
Vault.recalculateVaultsCreditCapacity
updates connected vaults, markets and credit delegation's state vars such as:
market's realized debt and realized debt per vault share
market's unrealized debt and unrealized debt per vault share
usdc credit per vault share
weth reward distribution per vault
update credit delegation's last distributed values
However, the function does not check wether vaults and markets are live. For example, the following code snippet loads vault even if it's not live
Also, the following code snippet does not check market's live status:
Let's consider a scenario when a vault needs to be delisted from the protocol. One needs to do the following:
Call MarketMakingEngineConfigurationBranch.updateVaultConfiguration
with isLive = false
Get all market IDs that were previuosly connected to the vault (unsure how to do that onchain, maybe someone needs to manage an excel sheet or dig into EVM storage)
For all the above market IDs, call MarketMakingEngineConfigurationBranch.connectVaultsAndMarkets
with vaultIds
excluding the given vault's ID
This approach is tedious and error-prone.
If a market is overlooked in the above approach and still uses unlisted vault as connected one, the above vulnerability will lead to incorrect accounting of vault's total credit capacity and market's total credit capacity.
Furthermore, since CreditDelegationBranch.updateVaultCreditCapacity(uint128 vaultId)
is external, anyone can call this function and update paused vault's state.
Manual Review
Use loadLive
instead of load
, or
Introduce a safe approach to unlist a vault/market on-chain programmatically
expectedAssetOut
does not consider protocol feeWhen initiating usd token swap, expectedAssetOut
does not consider protocol fee. However, when fulfilling swap, assetOutAmount
is deducted by swap fee and base fee. This inconsistency can bring unwanted revert due to slippage check.
Users can request swapping usd token to vault's underlying asset. When a swap request is initated, the protocol checks if expected asset amount out is more than requested minimum amount out.
However, when fulfilling swap, amountOut
will be less than expectedAssetOut
due to fee deduction:
This inconsistency might bring unwanted revert on fulfilling swap due to slippage check failure.
Users will lose fund because when swap request fails, they have to pay protocol fee.
Expected amount out should consider fees on initiateSwap
.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.