Maia DAO Ecosystem audit details

  • Total Prize Pool: $300,500 USDC
    • HM awards: $215,000 USDC
    • ✨ Analysis awards: $12,500 USDC
    • QA awards: $6,250 USDC
    • Bot Race awards: $10,000 USDC
    • Gas report awards: $6,250 USDC
    • Judge awards: $30,000 USDC
    • Lookout awards: $20,000 USDC
    • Scout awards: $500 USDC
  • Join C4 Discord to register
  • Submit findings using the C4 form
  • Read our guidelines for more details
  • Starts May 30, 2023 20:00 UTC
  • Ends July 5, 2023 20:00 UTC

✨ All participating wardens are encouraged to submit an Analysis prior to the closing date. Guidelines and FAQ can be found here.

Automated Findings / Publicly Known Issues

Automated findings output for the audit can be found here within 24 hours of audit opening.

Note for C4 wardens: Anything included in the automated findings output is considered a publicly known issue and is ineligible for awards.

There are a couple of known issues that are intended and not in scope for this audit:

  • Rebases and Fee-On-Transfer tokens are not supported.
  • Ulysses AMM (Ulysses Pools and Ulysses Tokens) only supports tokens with 18 decimals, but Ulysses Omnichain accounting supports tokens with any decimals and converts them to 18 decimals.
  • There are contracts that allow to renounce ownership and do not override renounceOwnership function from solady library.
  • In ERC20MultiVotes, the delegateBySig uses ecrecover to allow someone to delegate their votes by signing off-chain. Using ecrecover can return a random signer. This can lead to the random address being delegated to the supplied delegatee, but because delegations need to be larger than zero this is not seen as a security issue. GovernorBravoDelegate also uses this, but it is not in scope.
  • Our protocol has permissionless factories where anyonce can create with poison erc20 tokens or add poison erc20 tokens. While contracts generated by these are not in scope, if it does affect other contracts or other balances, it is in scope.

Overview

Maia DAO V2 Ecossytem docs that explains the business logic and technical references, can be found here. Docs are split into 4 protocols, in which you will find relevant information about how each of these protocols work in the system.

Recognizing that some wardens may prefer multimedia resources, we've integrated a comprehensive video tutorial into our docs, repo, and code. Designed as an alternative to traditional text-based materials, these videos offer a direct visual approach to system comprehension. You can access these resources on our notion page here.

Maia DAO Ecosystem is made to be a significant increase in capital efficiency and utility for the Maia DAO community from the V1 ecosystem. The V2 ecosystem is made up of 4 protocols: Hermes, Maia, Talos and Ulysses.

Previous Audits by Zellic can be found in the audits folder. There are two audits, each for different parts of the codebase:

Hermes

Hermes provides significant improvements over V1, which is a soldily fork. There are three ideas behind this model:

  • Lockers direct emissions to gauges and receive the revenue of each gauge they vote for, proportional to the amount of votes for that gauge
  • Lockers can boost their liquidity farming rewards from gauges by up to 2.5x
  • Lockers receive rebases weekly proportional to inflation

One of the improvements made is that instead of veNFTs that are locked for 4 years, every lock is made permanent. Claiming rebases was also substituted with a deposit only ERC4626 tokenized vault, which instead of claiming rebases, the burn (permanent lock) rate is increased. This allows users only to vote once and remove unecessary weekly tasks to optimize their funds.

Hermes also introduced a new gauge system, that accepts any kind of yield. The first gauges to be deployed are the Uniswap V3 gauges, which allow users to stake their NFTs and receive rewards. The gauges also allow users to boost their rewards up to 2.5x.

Maia

Maia is the first implementation of the Partner bHermes Vault. It allows users to stake their Maia converting it to vMaia and receive two of the three bHermes utilities deposited in the vault. The utilities are: weight and governance. The third utility is boost, which is not claimable by users, but instead used by Maia's Treasury to host a boost aggregator with Talos Positions to enable further accumulation of hermes.

Talos

Talos is decentralized Uniswap V3 Liquidity Management protocol. It allows anyone to create and manage new LPs. These LPs always start 50/50 and if they have a manager, it can call two strategies: rebalancing and reranging. Talos LPs are Uni V3 NFT wrappers, while this is less gas efficient, it allows for easier integrations with other protocols, like staking in Uniswap V3 gauges.

Staked Talos Positions need to be attached to a boost aggregator, anyone can deploy one using Boost Aggregator Factory. This allows users to pool together and share the same boost.

Ulysses

Ulysses is devided in two separate concepts: Virtualized and Unified Liquidity. Virtualized liquidity is made possible by using anycall v7 as our messaging layer and means that an asset deposited from a specific chain, is recognized as a different asset from the "same" asset but from a different chain (ex: arb ETH is different from mainnet ETH). Unified Liquidity then unifies these tokens using a stableswap AMM and then depositing them in a Unified Liquidity Token, which is a Multi-Asset ERC4626 tokenized vault. This allows users to deposit any asset from any chain and receive a 1:1 representation of the underlying assets. These Unified Liquidity Tokens can then be used in any other protocol, like Uniswap v3.

Areas of Concern

While this audit has the whole V2 ecosytem in scope, there are specific concerns that we would like to highlight for the wardens to pay special attention to. These are:

  • omnichain accounting.
  • omnichain gas management.
  • ulysses AMM and token accounting
  • proper reward/bribe management
  • brick funds in uniswap v3 staker
  • try to game/brick/steal funds from talos positions

Contracts

Scope

There are specific concerns that we would like to highlight for the wardens to pay special attention to. We highlighted these in the Areas of Concern section above.

File SLOC Description Libraries
Contracts (59)
src/hermes/tokens/HERMES.sol 11 Hermes ERC20 token - Native token for the Hermes Incentive System solady/* solmate/*
src/maia/tokens/Maia.sol 11 Maia ERC20 token - Native token for the Maia ecosystem solady/* solmate/*
src/rewards/booster/FlywheelBoosterGaugeWeight.sol 16 Balance Booster Module for Flywheel solmate/* @hermes/*
src/rewards/depots/SingleRewardsDepot.sol 17 Single Rewards Depot - Contract for a single reward token storage
src/ulysses-omnichain/token/ERC20hTokenBranch.sol 18 ERC20 hToken Branch Contract solady/* solmate/*
src/hermes/tokens/bHermesBoost.sol 19 bHermesBoost: Earns rights to boosted Hermes yield solady/* solmate/* @ERC20/*
src/rewards/rewards/FlywheelBribeRewards.sol 19 Flywheel Accumulated Bribes Reward Stream solmate/*
src/rewards/rewards/FlywheelInstantRewards.sol 🌀 19 Flywheel Instant Rewards. solady/* solmate/*
src/rewards/FlywheelCoreInstant.sol 20 Flywheel Core Incentives Manager - Manages instant incentives distribution under the Flywheel Core system solady/* solmate/*
src/rewards/FlywheelCoreStrategy.sol 20 Flywheel Core Incentives Manager solady/* solmate/*
src/hermes/tokens/bHermesGauges.sol 22 bHermesGauges: Directs Hermes emissions. solmate/* solady/* @ERC20/*
src/hermes/tokens/bHermesVotes.sol 22 bHermesVotes: Have power over Hermes' governance solmate/* solady/* @ERC20/*
src/talos/factories/TalosStrategyVanillaFactory.sol 22 Talos Strategy Vanilla Factory @uniswap/v3-core @uniswap/v3-periphery
src/talos/factories/BoostAggregatorFactory.sol 🌀 25 Boost Aggregator Factory solmate/* @v3-staker/*
src/talos/factories/OptimizerFactory.sol 🌀 32 Optimizer Factory for Talos Optimizers
src/gauges/UniswapV3Gauge.sol 33 Uniswap V3 Gauge - Handles liquidity provider incentives for Uniswap V3 in the Base V2 Gauge implementation. solady/* @v3-staker/*
src/rewards/depots/MultiRewardsDepot.sol 33 Multiple Rewards Depot - Contract for multiple reward token storage solady/*
src/ulysses-omnichain/token/ERC20hTokenRoot.sol 39 ERC20 hToken Contract solady/* solmate/*
src/ulysses-omnichain/VirtualAccount.sol 43 VirtualAccount - Contract for managing a virtual user account on the Root Chain solady/* solmate/* @openzeppelin/contracts
src/ulysses-omnichain/factories/ERC20hTokenRootFactory.sol 🌀 45 ERC20 hToken Root Factory Contract solady/* solmate/*
src/talos/factories/TalosStrategyStakedFactory.sol 🌀 46 Talos Strategy Staked Factory @uniswap/v3-core @uniswap/v3-periphery @rewards/*
src/ulysses-omnichain/factories/ERC20hTokenBranchFactory.sol 🌀 46 ERC20hTokenBranch Factory Contract solady/*
src/maia/factories/PartnerManagerFactory.sol 47 Factory for managing PartnerManagers solady/* solmate/*
src/ulysses-amm/UlyssesRouter.sol Σ 49 Ulysses Router - Handles routing of transactions in the Ulysses AMM solady/*
src/ulysses-omnichain/factories/RootBridgeAgentFactory.sol 49 Root Bridge Agent Factory Contract solady/* solmate/*
src/talos/TalosOptimizer.sol 52 Talos Optimizer - Manages optimization variables for Talos Positions solady/*
src/governance/GovernorBravoDelegator.sol 🖥 💰 👥 53 Governor Bravo Delegate Proxy Contract
src/maia/vMaia.sol 55 vMaia: Yield bearing, boosting, voting, and gauge enabled MAIA solady/* solmate/* @hermes/*
src/gauges/factories/BribesFactory.sol 🌀 58 Gauge Bribes Factory solady/* solmate/* @gauges/* @rewards/*
src/gauges/factories/UniswapV3GaugeFactory.sol 🌀 58 Uniswap V3 Gauge Factory solady/* solmate/* @hermes/* @gauges/* @rewards/* @v3-staker/*
src/talos/TalosManager.sol ♻️ 58 Talos Strategy Manager - Manages rebalancing and reranging of Talos Positions @uniswap/v3-core
src/ulysses-omnichain/factories/ArbitrumBranchBridgeAgentFactory.sol 68 Arbitrum Branch Bridge Agent Factory Contract
src/hermes/bHermes.sol 📤 🌀 69 bHermes: Yield bearing, boosting, voting, and gauge enabled Hermes solady/* solmate/* @ERC4626/*
src/ulysses-omnichain/ArbitrumCoreBranchRouter.sol 💰 74 Arbitrum Core Branch Router Contract solmate/*
src/ulysses-amm/UlyssesToken.sol 77 Ulysses Token - tokenized Vault multi asset implementation for Ulysses pools solady/* solmate/* @ERC4626/*
src/ulysses-omnichain/BranchBridgeAgentExecutor.sol 🌀 83 Branch Bridge Agent Executor Contract solady/*
src/gauges/factories/BaseV2GaugeManager.sol Σ 84 Base V2 Gauge Factory Manager - Manages addition/removal of Gauge Factories to bHermes. solady/* @hermes/*
src/ulysses-omnichain/factories/BranchBridgeAgentFactory.sol 90 Branch Bridge Agent Factory Contract solady/* solmate/*
src/hermes/minters/BaseV2Minter.sol ♻️ 92 Base V2 Minter - Mints HERMES tokens for the B(3,3) system solady/* @ERC4626/* @hermes/* @rewards/*
src/talos/boost-aggregator/BoostAggregator.sol 92 Boost Aggregator for Uniswap V3 NFTs solady/* solmate/* @openzeppelin/contracts @uniswap/v3-periphery @hermes/* @v3-staker/*
src/ulysses-omnichain/BaseBranchRouter.sol 💰 94 Base Branch Router Contract solady/*
src/talos/TalosStrategyVanilla.sol 🌀 95 Tokenized Vault implementation for Uniswap V3 Non Fungible Positions. solady/* solmate/* @uniswap/v3-core @uniswap/v3-periphery
src/ulysses-amm/factories/UlyssesFactory.sol 💰 🌀 Σ 102 Ulysses Pool Deployer solady/* solmate/*
src/ulysses-omnichain/ArbitrumBranchPort.sol Σ 109 Arbitrum Branch Port Contract solady/* solmate/*
src/ulysses-omnichain/ArbitrumBranchBridgeAgent.sol 💰 🌀 110 Manages bridging transactions between root and Arbitrum branch solady/* solmate/*
src/talos/TalosStrategyStaked.sol 📤 🌀 ♻️ 111 Tokenized Vault implementation for a staked Uniswap V3 Non-Fungible Positions. solmate/* @uniswap/v3-core @uniswap/v3-periphery @rewards/*
src/governance/GovernorBravoInterfaces.sol 💰 112 Storage for Governor Bravo Delegate
src/rewards/rewards/FlywheelGaugeRewards.sol 120 Flywheel Gauge Reward Stream solady/* solmate/* @ERC20/* @hermes/*
src/ulysses-omnichain/CoreBranchRouter.sol 💰 145 Core Branch Router Contract solady/* solmate/*
src/ulysses-omnichain/CoreRootRouter.sol 💰 238 Core Root Router Contract solady/* solmate/*
src/ulysses-omnichain/BranchPort.sol 💰 Σ 239 Branch Port - Omnichain Token Management Contract solady/* solmate/*
src/ulysses-omnichain/RootBridgeAgentExecutor.sol 🌀 Σ 246 Root Bridge Agent Executor Contract solady/*
src/ulysses-omnichain/MulticallRootRouter.sol 💰 Σ 296 Multicall Root Router Contract solady/*
src/ulysses-omnichain/RootPort.sol 💰 🌀 298 Root Port - Omnichain Token Management Contract solady/* solmate/* @uniswap/v3-periphery
src/uni-v3-staker/UniswapV3Staker.sol Σ 335 Uniswap V3 Staker Interface with bHermes Boost. solady/* @uniswap/v3-core @uniswap/v3-periphery @openzeppelin/contracts @gauges/* @hermes/*
src/governance/GovernorBravoDelegateMaia.sol 🖥 💰 🧮 🔖 338 Governor Bravo Constants Contract
src/ulysses-amm/UlyssesPool.sol 🖥 Σ 611 Ulysses Pool - Single Sided Stableswap LP solady/* solmate/* @ERC4626/*
src/ulysses-omnichain/RootBridgeAgent.sol 💰 🌀 ♻️ Σ 740 Root Bridge Agent Contract solady/* solmate/* @uniswap/v3-core
src/ulysses-omnichain/BranchBridgeAgent.sol 💰 🌀 ♻️ Σ 819 Branch Bridge Agent Contract solady/* solmate/*
Abstracts (19)
src/rewards/depots/RewardsDepot.sol 14 Rewards Depot - Base contract for reward token storage solady/* solmate/*
src/rewards/base/BaseFlywheelRewards.sol 20 Flywheel Reward Module - Base contract for reward token distribution solady/* solmate/*
src/rewards/rewards/FlywheelAcummulatedRewards.sol 26 Flywheel Accumulated Rewards. solady/* solmate/*
src/talos/strategies/TalosStrategySimple.sol 29 Rebalacing and reranging strategies for TALOS UniswapV3 LPs @uniswap/v3-periphery @uniswap/v3-core
src/talos/factories/TalosBaseStrategyFactory.sol 41 Talos Base Strategy Factory solady/* @uniswap/v3-periphery @uniswap/v3-core
src/erc-4626/ERC4626DepositOnly.sol 50 Minimal Deposit Only ERC4626 tokenized Vault implementation solady/* solmate/*
src/erc-4626/UlyssesERC4626.sol 73 Minimal ERC4626 tokenized 1:1 Vault implementation solady/* solmate/*
src/gauges/BaseV2Gauge.sol 🌀 Σ 79 Base V2 Gauge - Base contract for handling liquidity provider incentives and voter's bribes. solady/* solmate/* @hermes/* @rewards/*
src/hermes/UtilityManager.sol 81 Utility Tokens Manager Contract solady/* solmate/*
src/erc-4626/ERC4626.sol 86 Minimal ERC4626 tokenized Vault implementation solady/* solmate/*
src/gauges/factories/BaseV2GaugeFactory.sol Σ 88 Base V2 Gauge Factory solady/* solmate/* @hermes/* @gauges/*
src/maia/PartnerUtilityManager.sol 97 Partner Utility Tokens Manager Contract solady/* @hermes/*
src/rewards/base/FlywheelCore.sol 109 Flywheel Core Incentives Manager solady/* solmate/*
src/maia/tokens/ERC4626PartnerManager.sol 📤 🌀 179 Yield bearing, boosting, voting, and gauge enabled Partner Token solady/* solmate/* @ERC4626/* @hermes/*
src/erc-4626/ERC4626MultiToken.sol Σ 186 Minimal ERC4626 tokenized Vault multi asset implementation solady/* solmate/*
src/erc-20/ERC20Boost.sol 📤 Σ 189 An ERC20 with an embedded attachment mechanism to keep track of boost solady/* solmate/* @lib/* @gauges/*
src/erc-20/ERC20MultiVotes.sol 📤 🧮 🔖 194 ERC20 Multi-Delegation Voting contract solady/* solmate/* @lib/* @gauges/*
src/talos/base/TalosBaseStrategy.sol 📤 284 Tokenized Vault implementation for Uniswap V3 Non Fungible Positions. solady/* solmate/* @openzeppelin/contracts @uniswap/v3-core @uniswap/v3-periphery
src/erc-20/ERC20Gauges.sol 📤 Σ 295 An ERC20 with an embedded "Gauge" style vote with liquid weights solady/* solmate/* @lib/* @gauges/*
Libraries (8)
src/uni-v3-staker/libraries/IncentiveId.sol 🧮 7 Incentive ID hash library @v3-staker/*
src/ulysses-omnichain/lib/AnycallFlags.sol 10 Anycall flags library
src/maia/libraries/DateTimeLib.sol 🖥 Σ 21 Library to check if it is the first Tuesday of a month.
src/uni-v3-staker/libraries/NFTPositionInfo.sol 22 Encapsulates the logic for getting info about a NFT token ID @uniswap/v3-core @uniswap/v3-periphery
src/uni-v3-staker/libraries/IncentiveTime.sol 27 Incentive Time library
src/uni-v3-staker/libraries/RewardMath.sol 37 Math for computing rewards for Uniswap V3 LPs with boost solady/*
src/talos/libraries/PoolActions.sol 76 Pool Actions - Library for conducting uniswap v3 pool actions solmate/* @uniswap/v3-core @uniswap/v3-periphery @talos/*
src/talos/libraries/PoolVariables.sol 157 Pool Variables - Library for computing liquidity and ticks for token amounts and prices solady/* solmate/* @uniswap/v3-core @uniswap/v3-periphery @talos/*
Interfaces (68)
src/erc-20/interfaces/Errors.sol 4
src/rewards/interfaces/IFlywheelInstantRewards.sol 4
src/ulysses-omnichain/interfaces/IRootBridgeAgentFactory.sol 4
src/rewards/interfaces/IRewardsDepot.sol 5
src/talos/interfaces/AutomationCompatibleInterface.sol 5
src/ulysses-omnichain/interfaces/IApp.sol 5
src/ulysses-omnichain/interfaces/IERC20hTokenBranch.sol 5
src/ulysses-omnichain/interfaces/IPortStrategy.sol 5
lib/v3-periphery/contracts/interfaces/external/IWETH9.sol 💰 6 @openzeppelin/contracts
src/hermes/interfaces/IbHermesUnderlying.sol 6
src/rewards/interfaces/IFlywheelBooster.sol 6 solmate/*
src/ulysses-omnichain/interfaces/IERC20hTokenRootFactory.sol 6
src/gauges/interfaces/IUniswapV3Gauge.sol 7
src/talos/interfaces/ITalosStrategyStaked.sol 7 @rewards/*
src/ulysses-omnichain/interfaces/IAnycallConfig.sol 💰 7
src/ulysses-omnichain/interfaces/IERC20hTokenBranchFactory.sol 7
src/ulysses-omnichain/interfaces/IWETH9.sol 💰 7
src/rewards/interfaces/IFlywheelAcummulatedRewards.sol 8 solmate/*
src/rewards/interfaces/IFlywheelRewards.sol 8 solmate/*
src/ulysses-omnichain/interfaces/IBranchBridgeAgentFactory.sol 8
src/gauges/interfaces/IUniswapV3GaugeFactory.sol 9 @rewards/* @v3-staker/*
src/rewards/interfaces/IFlywheelBribeRewards.sol 9 solmate/*
src/ulysses-amm/interfaces/IUlyssesToken.sol 9
src/rewards/interfaces/IMultiRewardsDepot.sol 10
src/talos/interfaces/ITalosManager.sol 10
src/talos/interfaces/ITalosStrategyStakedFactory.sol 10 @rewards/*
src/ulysses-omnichain/interfaces/IArbitrumBranchPort.sol 10
src/maia/interfaces/IBaseVault.sol 11
src/ulysses-amm/interfaces/IUlyssesFactory.sol 11 solmate/*
src/ulysses-omnichain/interfaces/ICoreBranchRouter.sol 💰 11
src/ulysses-omnichain/interfaces/IERC20hTokenRoot.sol 11
src/ulysses-omnichain/interfaces/IMulticall2.sol 12
src/maia/interfaces/IPartnerUtilityManager.sol 13 @hermes/*
src/talos/interfaces/IBoostAggregatorFactory.sol 13 solmate/* @v3-staker/*
src/ulysses-omnichain/interfaces/IAnycallExecutor.sol 13
src/ulysses-amm/interfaces/IUlyssesRouter.sol 14
src/talos/interfaces/IOptimizerFactory.sol 15
src/ulysses-omnichain/interfaces/IAnycallProxy.sol 💰 15
src/ulysses-omnichain/interfaces/IVirtualAccount.sol 15 @openzeppelin/contracts
src/erc-4626/interfaces/IERC4626DepositOnly.sol 16
src/gauges/interfaces/IBribesFactory.sol 16 @rewards/*
src/talos/interfaces/ITalosOptimizer.sol 17
src/erc-4626/interfaces/IUlyssesERC4626.sol 19
src/maia/interfaces/IERC4626PartnerManager.sol 20 @hermes/*
src/erc-4626/interfaces/IERC4626.sol 21
src/talos/interfaces/ITalosBaseStrategyFactory.sol 21 @uniswap/v3-periphery @uniswap/v3-core
src/maia/interfaces/IPartnerManagerFactory.sol 22 solmate/*
src/gauges/interfaces/IBaseV2GaugeFactory.sol 24 @gauges/* @hermes/*
src/gauges/interfaces/IBaseV2Gauge.sol 26 @rewards/*
src/rewards/interfaces/IFlywheelCore.sol 26 solmate/*
src/gauges/interfaces/IBaseV2GaugeManager.sol 28 @hermes/*
src/hermes/interfaces/IBaseV2Minter.sol 29 @ERC4626/* @hermes/* @rewards/*
src/hermes/interfaces/IUtilityManager.sol 29
src/rewards/interfaces/IFlywheelGaugeRewards.sol 29 solmate/* @ERC20/*
src/talos/interfaces/IBoostAggregator.sol 29 solmate/* @openzeppelin/contracts @uniswap/v3-periphery @hermes/* @v3-staker/*
src/ulysses-omnichain/interfaces/IBranchRouter.sol 💰 32
src/erc-20/interfaces/IERC20MultiVotes.sol 33
src/erc-4626/interfaces/IERC4626MultiToken.sol 35
src/ulysses-amm/interfaces/IUlyssesPool.sol 37
src/erc-20/interfaces/IERC20Boost.sol 43
src/ulysses-omnichain/interfaces/IRootRouter.sol 💰 43
src/erc-20/interfaces/IERC20Gauges.sol 54
src/ulysses-omnichain/interfaces/IBranchPort.sol 54
src/talos/interfaces/ITalosBaseStrategy.sol 69 solmate/* @openzeppelin/contracts @uniswap/v3-core @uniswap/v3-periphery @v3-staker/*
src/ulysses-omnichain/interfaces/IRootPort.sol 96
src/uni-v3-staker/interfaces/IUniswapV3Staker.sol 97 @openzeppelin/contracts @uniswap/v3-core @uniswap/v3-periphery @gauges/* @hermes/*
src/ulysses-omnichain/interfaces/IRootBridgeAgent.sol 💰 127
src/ulysses-omnichain/interfaces/IBranchBridgeAgent.sol 💰 143
Total (over 154 files): 10997

All other source contracts (not in scope)

File SLOC Description Libraries
Contracts (12)
lib/v3-core/contracts/UniswapV3PoolDeployer.sol 🧮 24
lib/v3-periphery/contracts/lens/TickLens.sol Σ 35 @uniswap/v3-core
lib/v3-periphery/contracts/lens/UniswapInterfaceMulticall.sol 35
lib/v3-core/contracts/UniswapV3Factory.sol 49
lib/v3-periphery/contracts/NonfungibleTokenPositionDescriptor.sol 105 @uniswap/v3-core
lib/v3-periphery/contracts/examples/PairFlash.sol 107 @uniswap/v3-core
lib/v3-periphery/contracts/lens/Quoter.sol 🖥 ♻️ 130 @uniswap/v3-core
lib/v3-periphery/contracts/SwapRouter.sol 💰 186 @uniswap/v3-core
lib/v3-periphery/contracts/lens/QuoterV2.sol 🖥 ♻️ 238 @uniswap/v3-core
lib/v3-periphery/contracts/NonfungiblePositionManager.sol 💰 309 @uniswap/v3-core
lib/ds-test/src/test.sol 🖥 🧮 551
lib/v3-core/contracts/UniswapV3Pool.sol Σ 685
Abstracts (11)
lib/v3-periphery/contracts/base/BlockTimestamp.sol 6
lib/v3-periphery/contracts/base/PeripheryValidation.sol 8
lib/v3-periphery/contracts/base/PeripheryImmutableState.sol 10
lib/v3-core/contracts/NoDelegateCall.sol 14
lib/v3-periphery/contracts/base/Multicall.sol 🖥 💰 👥 19
lib/v3-periphery/contracts/base/PoolInitializer.sol 💰 25 @uniswap/v3-core
lib/v3-periphery/contracts/base/PeripheryPaymentsWithFee.sol 💰 40 @openzeppelin/contracts
lib/v3-periphery/contracts/base/PeripheryPayments.sol 💰 📤 48 @openzeppelin/contracts
lib/v3-periphery/contracts/base/SelfPermit.sol 💰 48 @openzeppelin/contracts
lib/v3-periphery/contracts/base/ERC721Permit.sol 💰 🧮 🔖 61 @openzeppelin/contracts
lib/v3-periphery/contracts/base/LiquidityManagement.sol 74 @uniswap/v3-core
Libraries (32)
lib/v3-core/contracts/libraries/FixedPoint128.sol 4
lib/v3-core/contracts/libraries/FixedPoint96.sol 5
lib/v3-core/contracts/libraries/UnsafeMath.sol 🖥 8
lib/v3-periphery/contracts/libraries/ChainId.sol 🖥 8
lib/v3-periphery/contracts/libraries/TokenRatioSortOrder.sol 9
lib/v3-periphery/contracts/libraries/PositionKey.sol 🧮 10
lib/v3-core/contracts/libraries/SafeCast.sol 13
lib/v3-core/contracts/libraries/TransferHelper.sol 15
lib/v3-periphery/contracts/libraries/CallbackValidation.sol 21 @uniswap/v3-core
lib/v3-periphery/contracts/libraries/AddressStringUtil.sol 23
lib/v3-periphery/contracts/libraries/HexStrings.sol 23
lib/v3-periphery/contracts/libraries/PoolAddress.sol 🧮 34
lib/v3-periphery/contracts/libraries/Path.sol 35
lib/v3-periphery/contracts/libraries/TransferHelper.sol 35 @openzeppelin/contracts
lib/v3-periphery/contracts/libraries/SqrtPriceMathPartial.sol 36 @uniswap/v3-core
lib/v3-core/contracts/libraries/TickBitmap.sol Σ 50
lib/v3-periphery/contracts/libraries/BytesLib.sol 🖥 54
lib/v3-core/contracts/libraries/Position.sol 🧮 Σ 63
lib/v3-periphery/contracts/libraries/SafeERC20Namer.sol 65
lib/v3-periphery/contracts/libraries/PoolTicksCounter.sol 66 @uniswap/v3-core
lib/v3-core/contracts/libraries/FullMath.sol 🖥 Σ 67
lib/v3-core/contracts/libraries/SwapMath.sol Σ 76
lib/v3-core/contracts/libraries/BitMath.sol Σ 79
lib/v3-periphery/contracts/libraries/LiquidityAmounts.sol Σ 88 @uniswap/v3-core
lib/v3-periphery/contracts/libraries/OracleLibrary.sol Σ 112 @uniswap/v3-core
lib/v3-core/contracts/libraries/Tick.sol Σ 115
lib/v3-periphery/contracts/libraries/PositionValue.sol 133 @uniswap/v3-core
lib/v3-core/contracts/libraries/SqrtPriceMath.sol Σ 144
lib/v3-core/contracts/libraries/TickMath.sol 🖥 Σ 176
lib/v3-core/contracts/libraries/Oracle.sol Σ 209
lib/v3-periphery/contracts/libraries/NFTSVG.sol 🧮 385 @openzeppelin/contracts @uniswap/v3-core base64-sol/*
lib/v3-periphery/contracts/libraries/NFTDescriptor.sol Σ 456 @uniswap/v3-core @openzeppelin/contracts base64-sol/*
Interfaces (31)
lib/v3-periphery/contracts/interfaces/external/IERC1271.sol 4
lib/v3-periphery/contracts/interfaces/IMulticall.sol 💰 5
lib/v3-periphery/contracts/interfaces/IPeripheryImmutableState.sol 5
lib/v3-periphery/contracts/interfaces/IERC20Metadata.sol 7 @openzeppelin/contracts
lib/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol 8
lib/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol 8
lib/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol 8
lib/v3-periphery/contracts/interfaces/INonfungibleTokenPositionDescriptor.sol 8
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol 9
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol 9
lib/v3-periphery/contracts/interfaces/IPeripheryPayments.sol 💰 10
lib/v3-periphery/contracts/interfaces/IPoolInitializer.sol 💰 10
lib/v3-core/contracts/interfaces/IUniswapV3PoolDeployer.sol 13
lib/v3-periphery/contracts/interfaces/ITickLens.sol 13
lib/v3-periphery/contracts/interfaces/external/IERC20PermitAllowed.sol 13
lib/v3-core/contracts/interfaces/IERC20Minimal.sol 14
lib/v3-periphery/contracts/interfaces/IERC721Permit.sol 💰 14 @openzeppelin/contracts
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol 15
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol 15
lib/v3-periphery/contracts/interfaces/IPeripheryPaymentsWithFee.sol 💰 17
lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol 18
lib/v3-periphery/contracts/interfaces/IQuoter.sol 20
lib/v3-periphery/contracts/interfaces/IV3Migrator.sol 23
lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol 26
lib/v3-periphery/contracts/interfaces/ISelfPermit.sol 💰 35
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol 37
lib/v3-periphery/contracts/interfaces/ISwapRouter.sol 💰 43 @uniswap/v3-core
lib/v3-periphery/contracts/interfaces/IQuoterV2.sol 50
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol 52
lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol 52
lib/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol 💰 95 @openzeppelin/contracts
Total (over 86 files): 6080

External imports

Out of scope

Anything in lib/ and src/out-of-scope/ is out of scope for this audit.

The libraries used in lib/ are: (other libraries are for testing only)

The contracts in src/out-of-scope/ are copies of contracts of the src/governance/ folder, but with different hard-coded parameters. They will be used live, but are not in scope for this audit:

Additional Context

Uniswap v3 Gauges

Hermes V2 is based on solidly's ve(3,3), in order to keep the same logic for the voting and boosting power of the users, was added a new logic for the calculation of the boosted rewards for the uniswapV3 positions, which is based on the seconds per liquidty in active range to calculate rewards of the uniswapV3Staker. It is based on the original UniswapV3Staker logic.

The original Uniswap V3 Staker awards positons based on the difference between snapshotCumulativesInside when staked vs when unstaked times the liquidity of the position. By doing this, we get how much liquidity seconds each position is awarded. In Hermes V2 case, all of hermes incentives have a duration of 1 week, so we can calculate the liquidity seconds per week for each position, 1 week being the maximum.

The original curve logic behind calculating boosted rewards for uniswapV3 positions is based on the following formula, found here, in docs:

$Rewards Received = min(UserLiquidity,(40%*UserLiquidity)+(60%TotalLiquidityUserBoostBalance/BoostTotal))$

In order to calculate the boosted rewards for the uniswapV3 positions, we have used the following formula:

$Rewards Received = min(Position Rewards, Position Rewards * 40% + (Total Rewards For Duration Staked * User bHERMES / Total bHERMES Supply) * 60%)$

More information on Uniswap V3 Gauges and how boost calculation is made, can be found here, in docs.

Ulysses Pools AMM - Delta Algorithm

The Ulysses Pools AMM is an adaptation of the Delta Algorithm put forh by LayerZero, the original paper can be found here.

In short, Ulysses Pools AMM is a stableswap AMM that allows for the creation of N to N pools with different weights, and the ability to change the weights of the pools after creation. In order to make sure no pools is ever in deficit, only new pools can be added to exisitng pools, can never be removed.

Because of all of Ulysses pools are in the same network (Arbitrum), we can avoid the need to keep credits from each pool, unlike the original Delta Algorithm. This allows us to simplify the logic of the algorithm.

Adapted from: Figure 4, this is the pseudocode for the algorithm:

Input: Transaction amount t, destination LP ID d
 # On the source LP:
 1: aₛ ← aₛ + t
 2: bₛ,𝒹 ← bₛ,𝒹 − t
 3: for x != s do
 4:     diffₛ,ₓ ← max(0, lpₛ * wₛ,ₓ − lkbₓ,ₛ)
 5: end for
 6: Total ← ∑ₓ diffₛ,ₓ
 7: for x != s do
 8:     diffₛ,ₓ ← min(Total, t) * diffₛ,ₓ / Total
 9: end for
 10: t′ ← t - min(Total, t)
 11: for ∀x do
 12:     bₛ,ₓ ← bₛ,ₓ + diffₛ,ₓ + t′ * wₛ,ₓ
 13: end for
 14: msg = (t)
 15: Send msg to LP d

 # On the destination LP:
 16: Receive (t) from a source LP
 17: if bₛ,𝒹 < t then
 18:     Reject the transfer
 19: end if
 20: a𝒹 ← a𝒹 − t
 21: bₛ,𝒹 ← bₛ,𝒹 − t

Ulysses Pools AMM - Rebalancing Fees

Renalancing fees are a way to incentivize users to rebalance Ulysses Pools. The fees are calculated based on the difference between the current bandwidth (balance dedicated to another pool) of the pool and the target bandwidth (weight * totalsupply) of the pool. More info can be found here, in docs. The fees are calculated as follows:

Rebalancing Fee Condition
0 $b−t \leq \delta _1B$
$\frac{\lambda_1}{(\delta_1−\delta_2)B}(\delta_1 B−b+t)$ $\delta _2B \leq b−t \lt \delta _1B$
$\lambda_1+\frac{\lambda_2}{\delta_2B}(\delta_2B−b+t)$ $b−t \lt \delta _2 B$

Fee Parameters:

Parameter Value
$\lambda_1$ 0.40%
$\lambda_2$ 99.60%
$\delta_1$ 60%
$\delta_2$ 5%

Because the lambda fee parameters in contract are divided by 2, the actual values used in the contract are:

Parameter Value
$\lambda_1$ 0.20%
$\lambda_2$ 49.80%
$\delta_1$ 60%
$\delta_2$ 5%
The fee is calculated as a trapezium with a base of width and a height of height
       The formula for the area of a trapezium is (a + b) * h / 2
                          ___________
          fee1 + fee2 -->|          /|
                         |         / |
                         |________/  |
  fee1 + fee2 * amount-->|       /|  |
         -------------   |      / |  |
           max width     |     /  |  |
                         |____/   |  |
                 fee1 -->|   |    |  |
                         |   |    |  |
                         |___|____|__|_____
                             |    |  |
                    upper bound 2 |  0
                                  |
                              bandwidth

         max width = upper bound 2
         amount = upper bound 2 - bandwidth

           h = amount
           a = fee1 + (fee2 * amount / max width)
           b = fee1

           fee = (a + b) * h / 2
               = (fee1 + fee1 + (fee2 * amount / max width)) * amount / 2
               = ((fee1 * 2) + (fee2 * amount / max width)) * amount / 2

         Because lambda1 = fee1 / 2 and lambda2 = fee2 / 2

         fee = ((fee1 * 2) + (fee2 * amount / max width)) * amount / 2
             = (lambda1 * 2 * amount) + (lambda2 * amount * amount) / max width
             = amount * ((lambda1 * 2) + (lambda2 * amount / max width))


When offset (b) is 0, the trapezium is equivalent to a triangle:
       The formula for the area of a triangle is a * h / 2

                          ___________
                 fee1 -->|          /|
                         |         / |
                         |________/  |
        fee1 * amount -->|       /|  |
        -------------    |      / |  |
          max width      |     /  |  |
                         |    /   |  |
                         |___/____|__|_____
                             |    |  |
                    upper bound 1 | upper bound 2
                                  |
                              bandwidth

         max width = upper bound 1 - upper bound 2
         amount = upper bound 1 - bandwidth

           h = amount
           a = fee1 * amount / max width
           b = 0

           fee = (a + b) * h / 2
               = fee1 * amount * amount / (2 * max width)

         Because lambda1 = fee1 / 2

         fee = fee1 * amount * amount / (2 * max width)
             = lambda2 * amount * amount / max width

Ulysses Omnichain Liquidity - AnycallV7 integration

Ulysses Omnichain Liquidity System uses AnycallV7 (https://github.com/anyswap/multichain-smart-contracts/tree/main/contracts/anycall/v7) for cross-chain messaging, it is integrated using the Pay on Destination flag (0x06), meaning execution gas fees are credited to the recipient contract (Bridge Agent) deducting the gas spent from this contract's executionBudget kept in the AnycallConfig contract (https://docs.multichain.org/developer-guide/anycall-v7/estimate-fee-pay-fees-on-destination-chain)

Scoping Details

- If you have a public code repo, please share it here: [https://twitter.com/MaiaDAOEco](https://github.com/Maia-DAO/maia-ecosystem-monorepo/tree/main)
- How many contracts are in scope?:   191
- Total SLoC for these contracts?:  10997
- How many external imports are there?:  152
- How many separate interfaces and struct definitions are there for the contracts within scope?:  116
- Does most of your code generally use composition or inheritance?:   Composition
- How many external calls?:   30
- What is the overall line coverage percentage provided by your tests?:  Can't provide accurate information, so 0
- Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol?:   false
- Please describe required context:   n/a
- Does it use an oracle?:  Yes, Uniswap V3 TWAP is used to check for price deviation in Talos positions
- Does the token conform to the ERC20 standard?:  true
- Are there any novel or unique curve logic or mathematical models?: Maia DAO's implementation of the Delta Algorithm (from layerzero). Boosted Reward calculation for uniswapV3 positions from curve and uniswapV3Staker respectively.
- Does it use a timelock function?:  true
- Is it an NFT?: true
- Does it have an AMM?:   true
- Is it a fork of a popular project?:   true
- Does it use rollups?:   true
- Is it multi-chain?:  true
- Does it use a side-chain?: true

Tests

Here is an example of a full script to run the first time you build the contracts in Linux:

forge install
forge build
python3 -m venv venv
source venv/bin/activate
pip3 install pycryptodome
python3 init_code_hash.py
deactivate
forge test --gas-report
forge snapshot --diff

Here is an example of a full script to run the first time you build the contracts in Windows: This script is not tested, if you have any issues, please refer to pycruptodome's documentation or try to hash Uniswap V3 Pool's bytecode through another method.

forge install
forge build
python -m venv venv
venv\Scripts\activate.bat
pip install pycryptodome --no-binary :all:
python init_code_hash.py
deactivate
forge test --gas-report
forge snapshot --diff

Default gas prixe is 10,000, but you can change it by adding --gas-price <gas price> to the command or by setting the gas_price property in the foundry.toml file.

Tests don't compile with --via-ir, but contracts do and will be deployed with --via-ir. Compilation settings that will be used are in hardhat.config.ts.

Install and First Build

Install libraries using forge and compile contracts.

forge install
forge build

Update INIT_CODE_HASH in PoolAddress

Update PoolAddress.sol with correct INIT_CODE_HASH according to latest build. This has to be run everytime UniswapV3Pool is compiled.

The following example runs a scirpt using a python virtual environment in Linux:

python3 -m venv venv
source venv/bin/activate
pip3 install pycryptodome
python3 init_code_hash.py
deactivate

If you wish, you can install the python dependencies globally and run the script:

pip3 install pycryptodome
python3 init_code_hash.py

The following example runs a scirpt using a python virtual environment in Windows: This script is not tested, if you have any issues, please refer to pycruptodome's documentation or try to hash Uniswap V3 Pool's bytecode through the method below.

python -m venv venv
venv\Scripts\activate.bat
pip install pycryptodome --no-binary :all:
python init_code_hash.py
deactivate

If you wish to do this manually or are having issues with the previous scripts. Then please follow these steps (after building at least once):

  • Get the bytecode from out/UniswapV3Pool.sol/UniswapV3Pool.json. To do this, head over to the file and get the bytecode from the "bytecode" and then "object" field.
  • Copy this bytecode and use any tool to hash it using keccak-256 function. For example this tool, but please remember to remove the first two characters "0x" and select input type to "hex" (default is string).

Build PoolAddress and Run Tests

Default gas prixe is 10,000, but you can change it by adding --gas-price <gas price> to the command or by setting the gas_price property in the foundry.toml file.

Compile contracts again to update PoolAddress.sol bytecode and run tests.

forge test --gas-report

Compile contracts again to update PoolAddress.sol bytecode and check snapshot differece.

forge snapshot --diff

Slither

If you encounter any issues, please update slither to 0.9.3, the latest version at the moment.

To run slither from root, please specify the src directory.

slither "./src/*"

We have a slither config file that turns on optimization and adds forge remappings.