Platform: Code4rena
Start Date: 21/08/2023
Pot Size: $36,500 USDC
Total HM: 1
Participants: 43
Period: 7 days
Judge: Dravee
Id: 277
League: ETH
Rank: 18/43
Findings: 1
Award: $247.50
π Selected for report: 0
π Solo Findings: 0
π Selected for report: 0xSmartContract
Also found by: 0xmystery, Udsen, carrotsmuggler, catellatech, ihtishamsudo, moneyversed, oakcobalt, pfapostol, plainshift, rjs
247.503 USDC - $247.50
The Shell Protocol consists of smart contracts built on the Arbitrum One platform using the EVM. In contrast to typical DeFi protocols that utilize large, specialized smart contracts, Shell serves as a central point for a flexible network of services. Its structure streamlines the process of combining multiple smart contracts or creating fresh ones, and allows users to group numerous services together in a single transaction.
The current audit have only one contract in scope which is EvolvingProteus.sol
.
What is Evolving Proteus?
It's is explained on C4 Contest Page, furthermore, it will be more summarized here in detailed manner as below
Evolving Proteus is a financial mechanism designed to dynamically adjust liquidity concentration in a pool over time. It combines features of Balancer liquidity bootstrapping pools and Uniswap v3. Pool creators define a starting and ending liquidity concentration, as well as initial and final timestamps for the evolving liquidity. The mechanism updates its concentration every block, offering utility in various contexts.
To get the better understanding of the protocol, it is better to look and understand the codebase and itβs smart contract that is in scope.
EvolvingProteus.sol
It implements a liquidity pool with a bonding curve that evolves over time. The bonding curve is defined by two parameters, "a" and "b", which are calculated based on the prices of two tokens in the pool. The curve evolves from an initial state to a final state over a specified duration.
EvolvingProteus.sol
EvolvingProteus
includes functions for swapping tokens, depositing tokens into the pool, and withdrawing tokens from the pool. Each of these functions adjusts the state of the pool and the bonding curve accordingly.
EvolvingProteus.sol
To my understanding LibConfig
library and is used to calculate various parameters and values related to a mathematical curve. This curve is defined by changing prices over time. The main purpose of this code is to create and manipulate configurations for this curve.
The basic function of swapGivenInputAmount and swapGivenOutputAmount is dealing with swapping one reserve token for another while maintaining invariants. These functions ensure that the utility of the system is preserved and that the swap doesn't lead to negative balances or incorrect amounts.
xBalance
, and yBalance
are below a certain threshold.xBalance
, and yBalance
are below a certain threshold._swap
function with the negated output amount and other parameters.These two functions handle the depositing of assets into a pool and the corresponding minting of LP tokens. These functions ensure that the utility of the pool is scaled proportionally to the deposited or minted amounts while considering the current balances of reserve tokens and the total supply of LP tokens.
xBalance
, yBalance
, and totalSupply
are below a certain threshold._reserveTokenSpecified
is called internally with adjusted deposited amount.
-The Results of _reserveTokenSpecified
must be positive (indicating a valid minting amount).depositGivenOutputAmount
is similar to depositGivenInputAmount for first two steps but_lpTokenSpecified
function is called internally with the adjusted minted amountThese two functions manage the extraction of assets from a pool and the concurrent reduction of LP tokens through burning. They guarantee that the pool's overall value aligns with the withdrawn or burned quantities, all while factoring in the existing reserve token balances and the total supply of LP tokens.
It handles the process of making swaps in a liquidity pool. Imagine you have a pool with two types of tokens, let's call them X and Y. Swaps can happen in four ways: you can add X, remove X, add Y, or remove Y from the pool. This function helps manage these swaps and ensures that the pool remains balanced and fair.
Inputs: When you want to make a swap, you provide some information: whether fees should be added or subtracted (feeDirection), how much you want to swap (specifiedAmount), what the initial balances of X and Y are (xi and yi), and which token you're swapping (specifiedToken).
Fee and Rounding: The amount you want to swap is adjusted to account for fees using a process called rounding. This adjusted amount is stored as roundedSpecifiedAmount.
Calculating New Balances: Next, the function calculates what the new balances of X and Y will be after the swap. This calculation considers the utility of the pool, which is like a measure of how well the pool is functioning.
Finalizing the Swap: Depending on whether you're swapping X or Y, the function calculates how much of the other token needs to be swapped to keep things fair. It also checks if these swaps maintain the proper balance of tokens in the pool.
Result: The function provides you with the actual amount that will be swapped in the other token (the non-specified one). This helps you understand exactly what the swap will involve.
These functions work together to manage the mechanisms of a liquidity pool and facilitate token swaps while considering factors such as fees, utility, and token balances. Let's provide a summary for each function:
Function: _reserveTokenSpecified
Function: _lpTokenSpecified
Function: _getUtilityFinalLp
Function: _findFinalPoint
Function: _getUtility
Functions: _getPointGivenXandUtility
, _getPointGivenYandUtility
Function: _checkAmountWithBalance
Function: _checkBalances
Function: _applyFeeByRounding
some potential architectural improvements that can be made to enhance its precision and efficiency:
Library Separation:
The contract could benefit from separating the configuration-related functions into their own standalone library. This would help in keeping the code modular and easier to read. The LibConfig
library could be extracted into its own file, making it more maintainable.
Rounding and Overflow Handling: The contract currently uses integer and fixed-point arithmetic extensively. To prevent potential rounding errors and overflow issues, consider using the SafeMath library for arithmetic operations. SafeMath prevents integer underflows and overflows by reverting the transaction if they are detected.
Parameter Validation and Error Handling: While the contract has error checks for maximum and minimum price values, it might be helpful to provide more detailed error messages in the require statements. This can make it easier to identify the exact condition that triggered the error.
Constants and Magic Numbers: The contract has a number of magic numbers and constants that are used in calculations. It's a good practice to define these constants as named variables at the top of the contract for better readability and maintainability.
Efficient Use of Storage:
In some calculations, the uint256
variables are used even though smaller data types like uint128
could suffice. Using the smallest data type that can accommodate the value helps to save storage costs.
Code Comments and Documentation: While the contract has some comments, adding more explanatory comments throughout the code can make it easier for other developers (and future you) to understand the logic and purpose of various functions.
Optimization of Calculations: Some calculations involve repeated operations. Consider caching results when possible to improve efficiency.
Gas Optimization: Careful consideration should be given to gas costs. The contract involves complex mathematical operations that could potentially be gas-intensive. It's important to ensure that the contract remains cost-effective for users.
Testing: Rigorous testing,invariant testing , should be performed on the contract to verify its correctness and robustness. This is especially important given the complexity of the mathematical calculations involved.
There aren't any specific Centralisations risks in the codebase, however, there are some general risks that are mentioned below:
Configuration Parameters and Initialization: The initial parameters of the contract (py_init, px_init, py_final, px_final, duration) are set during deployment. These parameters dictate how the curve evolves over time. If these parameters are set in a way that creates unfair advantages for certain users or parties, it could lead to centralization of control over the AMM.
Curve Equations and Calculations: The curve equations used in the contract's utility calculations and point extrapolations are complex. If there are any vulnerabilities or inaccuracies in these equations, it could be exploited by certain participants, potentially leading to centralization of control over the AMM.
Constraints on Ratios and Balances: The contract includes checks on the ratio between balances and the amount being swapped or deposited/withdrawn. If these checks are not properly implemented or if they are biased toward certain participants, it could lead to centralization.
Day1 : Reading The Docs And Info Provided about Protocol on C4 Audit Page
Day2: Thoroughly and Deeply Studied Code
Day3: Tried Understanding Code And Finding Weak Spots
Day4: Writing Report
30-32 Hours
32 hours
#0 - c4-pre-sort
2023-08-31T05:42:57Z
0xRobocop marked the issue as sufficient quality report
#1 - c4-judge
2023-09-11T20:35:11Z
JustDravee marked the issue as grade-a