Platform: Code4rena
Start Date: 30/05/2023
Pot Size: $300,500 USDC
Total HM: 79
Participants: 101
Period: about 1 month
Judge: Trust
Total Solo HM: 36
Id: 242
League: ETH
Rank: 15/101
Findings: 6
Award: $4,581.46
๐ Selected for report: 1
๐ Solo Findings: 1
๐ Selected for report: Kamil-Chmielewski
Also found by: ByteBandits, Co0nan, Madalad, T1MOH, Udsen, Voyvoda, bin2chen, chaduke, jasonxiale, kutugu, said, xuwinnie, zzebra83
11.9563 USDC - $11.96
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L199 https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L342
Due to a chain of bugs the endIncentive functionality can be bricked and no one else except the owner of the position can call restake.
This report explains 2 Consequences due to passing wrong argument inside the _unstakeToken() as part of the restake flow.
Anyone can call restakeToken if the block time is after the end time of the incentive , but this would be invalid as we pass "true" as the isNotRestake
argument
inside restake here https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L342 while it should be false. Due to this
, this statement https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L374 would fail(revert) for everyone except the owner of the
position , therefore owner might lose staking rewards if there exists an automatic system which calls restake on the position NFT.
Second Bug:
The endIncentive functionality is used to end an incentive when the end time is reached and ensures there are no current stakes in the incentive. The refund amount is transferred to the minter as part of the flow. We can think of an argument that if "some" user never unstakes his NFT from the incentive ,this statement https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L199 would always revert as there is always a stake left. That argument can be tackled by the fact "anyone can restake that user into a new incentive since that one has ended that is not an issue" , but as we explained above restaking for other users is impossible and therefore endIncentive will be bricked in this case(user never unstakes his position) and the refund would bever be transferred to the minter.
Pass the isNotRestake
argument as "false" for restakeToken
Invalid Validation
#0 - c4-judge
2023-07-09T11:37:14Z
trust1995 marked the issue as duplicate of #745
#1 - c4-judge
2023-07-09T11:37:20Z
trust1995 marked the issue as satisfactory
#2 - c4-judge
2023-07-24T13:25:07Z
trust1995 marked the issue as partial-50
๐ Selected for report: 0xnev
Also found by: 0xSmartContract, Breeje, BugBusters, ByteBandits, IllIllI, Kaiziron, Madalad, MohammedRizwan, SpicyMeatball, T1MOH, kutugu, nadin, peanuts, said, shealtielanz, tsvetanovv
14.356 USDC - $14.36
Deposit function is at risk of a MEV attack due to no protection from slippage and setting block.timestamp as the deadline
Consider the following function
if (_liquidity > 0) { uint128 liquidityDifference; (liquidityDifference, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: _tokenId, amount0Desired: balance0, amount1Desired: balance1, amount0Min: 0, amount1Min: 0, deadline: block.timestamp }) );
Due to the lack of slippage protection and deadline being set to block.timestamp, there is a possibility that a user's deposit function can be delayed indefinitely and front ran. This is especially problematic if a user submits a gas fee that is low.
One scenario where this can be dangerous:
Let's say that Bob, calls the deposit function. His transaction can be delayed for a very long time by block proposers until they can get a favorable price to front run Bob.
Another scenario:
Bob calls the function deposit with a low gas fee. His transaction is delayed indefinitely until gas fees are low enough to be front ran
Manual Review
Add slippage protection and do not use block.timestamp as the deadline. Instead use deadline.
MEV
#0 - c4-judge
2023-07-10T10:16:39Z
trust1995 marked the issue as duplicate of #171
#1 - c4-judge
2023-07-10T10:16:44Z
trust1995 marked the issue as satisfactory
๐ Selected for report: MohammedRizwan
Also found by: ByteBandits, T1MOH, btk, tsvetanovv
86.4119 USDC - $86.41
_votingDelay is compared with MIN_VOTING_DELAY and MAX_VOTING_DELAY but these approximations are done using approximations from L1 , leading to wrong calculations to bound the votingDelay(1 week in block.number in L1 will be way smaller in L2 like optimism/Arbitrum)
The values for MIN_VOTING_DELAY and MAX_VOTING_DELAY are hardcoded here https://github.com/code-423n4/2023-05-maia/blob/main/src/governance/GovernorBravoDelegateMaia.sol#L24-L27 , these values are in blocks and and are calculated using the approximations from the mainnet(L1) where the lenth of each block is way more than compared to L2 , therefore the values for MIN/MAX_VOTING_DELAY will be way less than 1week and 2week respectively making the check here for voting delay https://github.com/code-423n4/2023-05-maia/blob/main/src/governance/GovernorBravoDelegateMaia.sol#L72 useless.
Use ARB's helpers instead to calculate the correct values.
Timing
#0 - c4-judge
2023-07-11T06:05:50Z
trust1995 marked the issue as satisfactory
#1 - c4-judge
2023-07-11T06:06:06Z
trust1995 marked the issue as duplicate of #728
#2 - c4-judge
2023-07-11T06:06:11Z
trust1995 marked the issue as partial-50
#3 - 0xRizwan
2023-07-26T20:03:42Z
@trust1995 Ser,
This issue seems to be different and does not point out the block period issue, it could be QA but it is not a duplicate of #728
Please have a look.
Thank you!
#4 - trust1995
2023-07-27T08:04:14Z
This finding group contains two types of findings:
Different chains have different block times ETH2 block time is different from assumed block time (15 seconds) I believe these are similar enough to be looked at as same underlying issue (block time assumptions affect voting period).
๐ Selected for report: ByteBandits
1712.1701 USDC - $1,712.17
Branch Strategies lose yield due to a wrong implementation of the time limit in BranchPort.sol. This results in missed yield for branch strategies, less capital utilization of the platform, and ultimately a loss of additional revenue for the protocol's users.
The _checkTimeLimit
function in BranchPort.sol
controls whether amounts used by a branch strategy do cumulatively not exceed the daily limit which is set for the particular strategy. It is only called from the manage
function in the same contract (https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L161).
The current implementation of _checkTimeLimit
looks like this:
function _checkTimeLimit(address _token, uint256 _amount) internal { if (block.timestamp - lastManaged[msg.sender][_token] >= 1 days) { strategyDailyLimitRemaining[msg.sender][_token] = strategyDailyLimitAmount[msg.sender][_token]; } strategyDailyLimitRemaining[msg.sender][_token] -= _amount; lastManaged[msg.sender][_token] = block.timestamp; }
I.) The current implementation does the following
The first time a strategy manages some amount and _checkTimeLimit
is called the 24h window is started (strategyDailyLimitRemaining[msg.sender][_token] is initialized to the daily limit amount and lastManaged[msg.sender][_token] is set to block.timestamp)
On a second call to use more of the daily limit (if the amount used in 1. is not the full daily amount, which is not enforced), it will set lastManaged[msg.sender][_token] again to block.timestamp. This pushes the time when the daily budget will be reset (strategyDailyLimitRemaining[msg.sender][_token] = strategyDailyLimitAmount[msg.sender][_token]) again 24 hours into the future.
Consequences of the current implementation
Due to the setting of the lastManaged[msg.sender][_token] on every call the daily budget misses its purpose as a budget reset after 24h is not guaranteed.
In the worst but likely case, a call is made by the strategy just before the current 24h time window passes to use the remaining amount. This will delay a reset of the daily limit by the maximum possible time. In consequence, a strategy misses 1 full amount of the daily budget.
The aforementioned results in a loss of yield for the strategy (assuming the strategy generates a yield), less capital utilization of the platform, and ultimately a loss of additional revenue for the protocol's users.
Assuming there are multiple strategies in the protocol, the negative effect is multiplied.
II.) The implementation that was probably intended
function _checkTimeLimit(address _token, uint256 _amount) internal { if (block.timestamp - lastManaged[msg.sender][_token] >= 1 days) { strategyDailyLimitRemaining[msg.sender][_token] = strategyDailyLimitAmount[msg.sender][_token]; lastManaged[msg.sender][_token] = block.timestamp; // <--- line moved here } strategyDailyLimitRemaining[msg.sender][_token] -= _amount; }
Here the reset of the daily budget is made after a 24h time window as expected.
What is lost, is the information "when the last time a strategy called the function" as lastManaged[msg.sender][_token] now only stores the block timestamp the last time the daily budget was reset and not when the last time the function was called. If this should still be tracked consider an additional state variable (e.g. lastDailyBudgetReset[msg.sender][_token]).
Manual review
Implement the logic as shown under "II.) The implementation that was probably intended".
Please also, consider the following comments:
To get the maximum amount out of their daily budget a strategy must make a call to the manage()
function exactly every 24h hours after the first time calling it. Otherwise, there are time frames where amounts could be retrieved but are not. That would have the strategy missing out on investments and therefore potential yield. E.g. 2nd call happens 36h (instead of 24h) after the initial call => 12 hours (1/2 of a daily budget) remains unused.
The amount also needs to be fully used within the 24h timeframe since the daily limit is overwriting and not cumulating (using strategyDailyLimitRemaining[msg.sender][_token] = strategyDailyLimitAmount[msg.sender][_token] and not strategyDailyLimitRemaining[msg.sender][_token] += strategyDailyLimitAmount[msg.sender][_token])
An alternative to the aforementioned could be to calculate the amount to grant to a strategy after an initial/last grant like the following: (time since last grant of fresh daily limit / 24h) * daily limit. This would have the effect that a strategy could use their granted limits without missing amounts due to suboptimal timing. It would also spare the strategy the necessary call every 24h which would save some gas and remove the need for setting up automation for each strategy (e.g. using Chainlink keepers). The strategy could never spend more than the cumulative daily budgets. But it may lead to sudden usage of a large amount of accumulated budget which may not be intended.
Timing
#0 - c4-judge
2023-07-10T09:26:24Z
trust1995 marked the issue as primary issue
#1 - c4-judge
2023-07-10T09:26:29Z
trust1995 marked the issue as satisfactory
#2 - c4-sponsor
2023-07-12T13:22:23Z
0xBugsy marked the issue as sponsor confirmed
#3 - c4-judge
2023-07-25T13:40:34Z
trust1995 marked the issue as selected for report
#4 - 0xLightt
2023-09-07T10:48:08Z
๐ Selected for report: 0xSmartContract
Also found by: 3kus-iosiro, Audit_Avengers, ByteBandits, IllIllI, Kamil-Chmielewski, Madalad, RED-LOTUS-REACH, Rolezn, Sathish9098, Stormreckson, Udsen, bin2chen, brgltd, ihtishamsudo, kaveyjoe, kodyvim, lukejohn, matrix_0wl, mgf15, nadin
803.432 USDC - $803.43
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IERC20hTokenBranch.sol#L22C4-L28 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IERC20hTokenRoot.sol#L47-L55
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IRootBridgeAgent.sol#L378 (uint24) https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IERC20hTokenRoot.sol#L47 (uint256)
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/ArbitrumCoreBranchRouter.sol#L21-L23 (some words may be missing in the "Branch Chain is not available" part)
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/ArbitrumBranchBridgeAgent.sol#L131-L132 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IRootBridgeAgent.sol#L98 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IRootBridgeAgent.sol#L101 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L1230-L1231 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L1320-L1321 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IBranchBridgeAgent.sol#L101 https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/interfaces/IBranchBridgeAgent.sol#L104
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/factories/ArbitrumBranchBridgeAgentFactory.sol#L55 Compare with the "initialize" function in BranchBridgeAgentFactory.sol which does this check: https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/factories/BranchBridgeAgentFactory.sol#L84
https://github.com/search?q=repo%3Acode-423n4%2F2023-05-maia%20lenght&type=code
Both functions do the same just "bridgeOutMultiple" does it for multiple local addresses. The functions process the 2 if statements in their function body in a different order. This e.g. makes it harder to compare the functions for correctness. Compare https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L248-L256 vs. https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L268-L278
There are sets of 2 functions in that contract where one function adds an item and another function toggles the item state. As add function does not check whether the item was already added, it could accidentally be added and toggled on again. A side effect of this is that the arrays that track the history of added items will also hold duplicate items. The following combinations of functions were identified: addStrategyToken + toggleStrategyToken, addPortStrategy + togglePortStrategy, addBridgeAgentFactory + toggleBridgeAgentFactory, addBridgeAgent + toggleBridgeAgent. Example of toggling on again by adding: https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L291
In https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/maia/interfaces/IERC4626PartnerManager.sol#L64 it talks about "address(partnerVault)" but in the implementation in uses "address(this)".
https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/tokens/ERC4626PartnerManager.sol#L159-L160 https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/tokens/ERC4626PartnerManager.sol#L159-L160
In both files nonces are either increased by in place calling ++ or via a dedicated "_getAndIncrement*" function. https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L278 vs. https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/RootBridgeAgent.sol#L633, https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol vs. https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol.
These state variabless are commented as "next poolId" and "next tokenId". Due to the pre-increment, they are not used as such. It is also the only occurrence in the protocol where increments are used like this. https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/factories/UlyssesFactory.sol#L85, https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-amm/factories/UlyssesFactory.sol#L141
A proposal pending state is judged by "block.number <= proposal.startBlock" which on the first look may be incorrect since the "active" state is also inclusive of the endBlock. See: https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/governance/GovernorBravoDelegateMaia.sol#L304. But the startBlock is excluded intentionally so the proposal starts 1 block later than the block when the user balance is passed. See: https://github.com/code-423n4/2023-05-maia/blob/main/src/governance/GovernorBravoDelegateMaia.sol#L367. This should be documented.
In https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L92 MIN_RESERVE_RATION is defined but it is never used in that contract. It may have been missed to be used for a second check in the "addStrategyToken" function: https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchPort.sol#L331.
Here the checks https://github.com/code-423n4/2023-05-maia/blob/main/src/hermes/bHermes.sol#L144 are already done inside the modifiers at L69 , L77, L85 . Use that insetad for better code consistency.
We should have a check in this function https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/PartnerUtilityManager.sol#L52 that none of the forfeit calls would revert (ensure userClaimedX >= amount) , this can save gas too as if the first two forfeit calls succeeded but the last one reverted that would waste gas.
better to have a check amount > userClaimedWeight at https://github.com/code-423n4/2023-05-maia/blob/main/src/maia/PartnerUtilityManager.sol#L71 with an error string so the user knows the call reverted due to insufficient claimed weight. Same for L78 and L87
The check here https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L138 is useless since reward is a uint256 which can't be negative , a check for zero value should be enough.
The check at https://github.com/code-423n4/2023-05-maia/blob/main/src/rewards/depots/MultiRewardsDepot.sol#L48 can be done without the OR operator too i.e if (_isAsset[asset] ) should be enough.
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L104 This function checks the 0 liquidity case with == while this function https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/TalosStrategyVanilla.sol#L129 checks the 0 liquidity at L139 with >
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/maia/vMaia.sol#L39-L50 the factory does not actually create vMaia contracts, it adds them to the registry
https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/talos/interfaces/ITalosBaseStrategy.sol#L37-L98 Most of the highlighted functions in the interface are variables that do not exist in the main contract
https://github.com/code-423n4/2023-05-maia/blob/main/src/talos/base/TalosBaseStrategy.sol#L409-L414
https://github.com/code-423n4/2023-05-maia/blob/main/src/uni-v3-staker/UniswapV3Staker.sol#L262 All rewards are transferred if 0 is passed
#0 - c4-judge
2023-07-09T15:46:51Z
trust1995 marked the issue as grade-a
#1 - c4-sponsor
2023-07-12T19:44:45Z
0xBugsy marked the issue as sponsor confirmed
#2 - 0xBugsy
2023-07-12T21:46:45Z
Well structured submission!
#3 - 0xLightt
2023-09-07T11:11:44Z
1953.1316 USDC - $1,953.13
Hermes is a protocol built around its main token bHermes, which is earned from staking Hermes token. bHermes is then split into 3 utility tokens, each with a separate use:
BHermes Gauge: Vote on gauges and receive a proportion of the gaugesโ revenue
BHermes Boost: Earn boosted rewards by providing liquidity to a gauge
BHermes Votes: Vote on governance proposals such as adding/removing gauges, bribes, partners, etc.
Hermes also introduces a gauge system, where the users can stake their UniV3 position NFTs and earn rewards. Moreover, these rewards can be boosted (using the BoostAggregator contract) up to a maximum of 2.5x. The new version also introduces ERC4626 deposit-only vaults where the burn rate is increased allowing users to vote once.
Maia DAO is the cornerstone of the Maia protocol. It is an aggregator for Talos and Hermes. Users can earn rewards by staking their $Maia(native) into vMaia(ERC4626 compliant token), leveraging their vMaia to participate in Maia governance, and earning bribes like in Talos (rewards from vault strategy revenue).
The utilities earned by the user are weight and governance but not boost, this is because Maiaโs treasury hosts a boost aggregator with Talos Positions to enable further accumulation of hermes.
Talos builds upon the gauge system by introducing strategies. These strategies rebalance and rerange a Uniswap V3 position portfolio based on settings set in an optimizer contract. These strategies come in 2 forms. Vanilla and staked. The Vanilla Strategy is a strategy where Uniswap V3 positions are left in pools to collect liquidity fees. The Staked Strategy,u in contrast, takes Uniswap V3 positions and stakes them into Uniswap V3 gauges where users can earn Hermes emissions instead of using the Uniswap V3 staker implementation.
UniV3 ensures that positions are staked in tick ranges with the highest liquidity to ensure the highest rewards, the need for rebalance/rerange occurs when the position deviates from the tick spacing (deviation) defined.
An important component of the Talos system is flywheel contracts. These contracts manage the token incentive distribution from Talos strategies. This helps to protect against exploits and ensure the safe and fair distribution of strategy rewards.
Ulysses is an ominchain protocol inspired by layer0 that provides capital efficiency across multiple chains. It attempts to solve the bridging trilemma problem involved with the growing demand to move tokens across multiple chains. Ulysses allows users to provide liquidity and earn fees across chains as well as allows users to seamlessly move assets across chains. It accomplishes this with a branch port system where each chain has a branch router and a branch port that communicates with the root router and the root port on the root chain (Arbitrum). The interaction between branch chains and the root chain is facilitated by a BridgeAgent that exists on each chain. The underlying cross-chain communication protocol is not Maia's own implementation. Instead, Ulysses uses Multichain anyCall v7 under the hood.
The root chain keeps a virtual account of users that manages their balances across chains. This allows users to leverage their virtual account to participate in many activities on the root chain.
When inspecting Ulysses some noteworthy patterns were discovered:
In other contests, it was frequently discussed whether frontrunning an initialize() call on a freshly deployed contract was noteworthy. And if so the severity of this was usually in question. Because usually, the project could just redeploy that contract. Reviewing the Ulysses contracts a pattern was noted that spares this discussion:
Mappings in Ulysses are named like functions and therefore accessing a value for a key feels like a โgetโ function call. This is the first code base inspected where this naming convention was found. Here is an example: https://github.com/code-423n4/2023-05-maia/blob/54a45beb1428d85999da3f721f923cbf36ee3d35/src/ulysses-omnichain/BranchBridgeAgent.sol#L122
We divided the audit into 4 parts. Hermes, Maia, Talos and Ulysses. We started with Hermes, then Maia, then Talos, and then Ulysses. This is because the first 3 are interconnected particularly due to Maia being an aggregator for both Talos and Hermes.
Ulysses-AMM
Overall, we believe that the codebase quality for Maia DAO is very good. Codebase is very mature and has clearly undergone extensive testing. We notice the employment of various standards, including ERC20, ERC721, and ERC2646. We also noticed that some sections of the codebase take inspiration from protocols such as popsicle finance and layer 0. Details are explained below
Codebase Quality Categories | Comments |
---|---|
Unit Testing | Codebase is extensively well-tested using the Foundry framework. The inclusion of fuzz tests was great to see. It is worth noting that some of the test files are too large and it would be beneficial to separate them for better readability. |
Code Comments | Comments in general were solid but there are many sections in the codebase where the comments were either outdated or incorrect. |
Documentation | The documentation overall is fantastic and does a great job of explaining the ecosystem. It would be helpful, however, if the docs explained how the ecosystem works from a basic contract level so that it is easier to digest for developers looking to integrate into the Maia ecosystem |
Organization | Codebase is very mature and well organized with clear distinctions between the 4 protocols, their respective factories, interfaces, etc |
Static Analysis | Codebase makes great use of Slither which is an industry standard tool for static analysis |
The ecosystem of Talos carries some noteworthy systemic risks:
#0 - CloudEllie
2023-07-05T19:21:25Z
Due to technical difficulties with the submission form, teams were not able to submit Analyses. I have added this Analysis manually; the original can be viewed here.
#1 - c4-judge
2023-07-11T14:46:21Z
trust1995 marked the issue as grade-a
#2 - c4-judge
2023-07-11T14:46:27Z
trust1995 marked the issue as satisfactory
#3 - 0xBugsy
2023-07-13T12:47:33Z
Very well structured report!
#4 - c4-sponsor
2023-07-13T12:47:38Z
0xBugsy marked the issue as sponsor confirmed