APY.Finance Security

A review of the security measures developed to secure the APY.Finance platform from financial vulnerabilities, hacks and exploits.

Overview

The APY.Finance platform enables users to earn low-fee and risk-optimized yield by pooling liquidity in a heavily diversified yield farming portfolio.

The yield farming portfolio is managed with a Gnosis Safe referred to as the LP Safe. The liquidity pools collect user deposits and supply them to the LP Safe for yield farming. The oracle system ensures that the yield accrued by the LP Safe is fairly distributed to users by pricing their shares as the TVL increases.

The platform can be broken down into three major systems:

  • Liquidity management

  • Portfolio management

  • Oracle management

These three systems implement the seven major functions of the platform:

  • User deposit

  • User withdraw

  • Fund LP Safe

  • Withdraw from LP Safe

  • Deploy strategy

  • Unwind strategy

  • Oracle update

The following sections will cover each system, explain the functions associated with that system, and highlight the security measures put in place to protect the functions in that system.

Figure 1: System architecture

Liquidity Management

The focus of the liquidity management system is allowing users to share the liquidity they’ve pooled together. Without this system, any liquidity sent to the LP Safe for yield farming could not be returned to the user with their fair share of earned yield.

The biggest risks to liquidity management are from arbitrage or oracle skew. These risks, if not properly mitigated, could lead to loss of funds from liquidity pools.

The liquidity management system handles:

  • User deposit

  • User withdraw

  • Fund LP Safe

  • WIthdraw from LP Safe

Liquidity management is protected with the following security features:

  • Same-asset deposit/withdraw

  • Withdrawal fee

  • Off-chain oracle

  • Reserves pools

Liquidity Functions

The two layers of liquidity management are:

  • The control of liquidity between users and liquidity pools.

  • The control of liquidity between liquidity pools and the LP Safe.

PoolTokenV2 is the interface for users and acts as both the liquidity pool and the liquidity token for users in a pool (APT).

The PoolManager is controlled by the Admin Safe and is able to increase or decrease the liquidity available to the LP Safe by funding or withdrawing from one or more PoolTokenV2. Separating liquidity management from strategy management using the PoolManager as a gateway helps mitigate malicious users from manipulating third party DeFi protocols leveraged by strategies to drain liquidity from the platform.

The same way that APT tracks a user’s share of a liquidity pool, the internal share token, mAPT, tracks a pool’s share of the capital allotted to the LP Safe. This token effectively allows the platform to treat each pool as a user depositing into the LP Safe.

User Deposit

When a user deposits, their liquidity is transferred to the APY.Finance platform and their share of the platform is tracked using the APT token.

Figure 2: User deposit flow

  1. User calls addLiquidity on a PoolTokenV2.

  2. addLiquidity transfers stablecoins from the user to the PoolTokenV2. This is considered the user’s deposit.

  3. The OracleAdapter gets the stablecoin prices and TVL by calling latestRoundData on the appropriate Chainlink aggregators.

  4. The PoolTokenV2 gets the price of the user’s stablecoin from the OracleAdapter to determine the USD value of the user’s deposit.

  5. The PoolTokenV2 uses its balance of stablecoin and the stablecoin price from the OracleAdapter to get the USD value of tokens already in the pool.

  6. The PoolTokenV2 calls getDeployedValue on mAPT to get the USD value of liquidity the PoolTokenV2 has sent to the LP Safe.

    1. The TVL from the OracleAdapter is compared to the mAPT balance of the PoolTokenV2 to calculate getDeployedValue.

  7. Once the PoolTokenV2 has the USD value of its stablecoin balance and the USD value of stablecoin that it’s sent to the LP Safe, the two values are summed to produce the total liquidity owned by the PoolTokenV2.

  8. The USD value of the user’s deposit is compared to the total liquidity to determine the amount of APT the PoolTokenV2 should mint and transfer to the user. The amount of APT minted determines the users share of the pool.

User Withdraw

When a user withdraws, they redeem an amount of APT to receive their fair share of the platform’s liquidity in stablecoin.

Figure 3: User withdraw flow

  1. User calls redeem on a PoolTokenV2.

  2. The PoolTokenV2 burns the amount of APT the user is trying to redeem. The user’s APT balance represents their share of the platform and the amount of APT they redeem is the portion of their share they wish to withdraw.

  3. The OracleAdapter gets the stablecoin prices and TVL by calling latestRoundData on the appropriate Chainlink aggregators.

  4. The PoolTokenV2 uses its balance of stablecoin and the stablecoin price from the OracleAdapter to get the USD value of tokens already in the pool.

  5. The PoolTokenV2 calls getDeployedValue on mAPT to get the USD value of liquidity the PoolTokenV2 has sent to the LP Safe.

    1. The TVL from the OracleAdapter is compared to the mAPT balance of the PoolTokenV2 to calculate getDeployedValue.

  6. Once the PoolTokenV2 has the USD value of its stablecoin balance and the USD value of stablecoin that it’s sent to the LP Safe, the two values are summed to produce the total liquidity owned by the PoolTokenV2.

  7. The amount of APT redeemed is compared to the total supply of APT and the total liquidity to calculate the portion of liquidity the redeemed APT represents.

  8. The PoolTokenV2 uses the stablecoin price from the OracleAdapter to calculate the amount of stablecoin the portion of liquidity represents.

  9. The PoolTokenV2 transfers the amount of stablecoin to the user.

Fund LP Safe

The LP Safe manages the yield farming portfolio on behalf of users. It is necessary to fund the LP Safe with liquidity from the liquidity pools so it can be used in the portfolio.

When using this function, it is important that the OracleAdapter is not unlocked until the Chainlink TVL feed is verified to be accurate. Subsequent attempts to fund or withdraw from the LP Safe, without an updated TVL, can cause errors in the amount of liquidity owed to each liquidity pool.

Figure 4: Fund LP Safe flow

  1. Admin Safe multisig calls fundLpSafe on the PoolManager.

  2. The PoolManager loops through each PoolTokenV2 that will fund the LP Safe and performs the following sequence:

    1. Calculates the USD value of the stablecoin that will be transferred to the LP Safe from the PoolTokenV2.

    2. Calculates the amount of mAPT to mint for the PoolTokenV2 to track its share of liquidity controlled by the LP Safe.

      1. The TVL from the OracleAdapter is compared with the USD value of stablecoin that will be transferred from the PoolTokenV2 to calculate the amount of mAPT to mint.

    3. Transfers the stablecoin from the PoolTokenV2 to the LP Safe.

    4. Mints the calculated amount of mAPT to the PoolTokenV2 to track its share of the liquidity controlled by the LP Safe.

  3. The PoolManager calls lock on the OracleAdapter to ensure that no user deposits or withdrawals are processed until the Chainlink oracle has had time to update the TVL aggregator to a new value.

Withdraw from LP Safe

Users can only withdraw stablecoin that is held in a PoolTokenV2. If there is not enough stablecoin in a PoolTokenV2, a portion must be withdrawn from the LP Safe so it can be available to users.

When using this function, it is important that the OracleAdapter is not unlocked until the Chainlink TVL feed is verified to be accurate. Subsequent attempts to fund or withdraw from the LP Safe, without an updated TVL, can cause errors in the amount of liquidity owed to each liquidity pool.

Figure 5: Withdraw from LP Safe flow

  1. Admin Safe multisig calls withdrawFromLpSafe on the PoolManager.

  2. The PoolManager loops through each PoolTokenV2 that will withdraw liquidity from the LP Safe and performs the following sequence:

    1. Calculates the USD value of the stablecoin that will be transferred from the LP Safe to the PoolTokenV2.

    2. Calculates the amount of mAPT to burn from the PoolTokenV2 mAPT balance to account for the liquidity that has been returned to the PoolTokenV2 and is no longer controlled by the LP Safe.

      1. The TVL from the OracleAdapter is compared with the USD value of stablecoin that will be transferred from the PoolTokenV2 to calculate the amount of mAPT to mint.

    3. Transfers the stablecoin to the PoolTokenV2 from the LP Safe.

    4. Burns the calculated amount of mAPT from the PoolTokenV2 mAPT balance to account for the liquidity that has been returned to the PoolTokenV2 and is no longer controlled by the LP Safe.

  3. The PoolManager calls lock on the OracleAdapter to ensure that no user deposits or withdrawals are processed until the Chainlink oracle has had time to update the TVL aggregator to a new value.

Liquidity Security

The two main risks to liquidity management are arbitrage and oracle skew. Each of these risks have two security features, the first aims to completely protect the platform from the risk, the second aims to limit the impact of the risk if security failure occurs regardless.

Same-Asset Deposit/Withdraw

Users can only withdraw with the same type of stablecoin they used to deposit. This prevents the platform from being used to conduct zero-slippage, zero-fee arbitrage between stablecoins.

Withdrawal Fee

A substantial withdrawal fee that stays in effect for 24 hours after a deposit makes all but the most extreme arbitrage opportunities, should they exist, unprofitable. Limiting the withdrawal fee to 24 hours prevents honest users from being impacted by the security measure.

Off-chain Oracle

Chainlink is used as the off-chain oracle calculating TVL. The Chainlink TVL feed uses 9 nodes, each pulling data from at least 3 different APIs to mitigate downtime or price corruption. The values are calculated off-chain and then stored on-chain as a static value. This prevents a malicious user from skewing oracle values with flash loans or large amounts of capital and withdrawing more than their fair share of liquidity from the platform.

Reserve Pools

Each liquidity pool keeps a portion of TVL in what is referred to as the reserve pool. Users are only able to withdraw from the reserve pool, and the reserve pool can only be replenished when the Admin Safe instructs the PoolManager to withdraw funds from the LP Safe. This security measure limits the amount of liquidity that can be drained from the platform in the event that a malicious user is able to successfully cause oracle skew and withdraw more than their fair share of liquidity.

Portfolio Management

The purpose of the portfolio management system is to earn yield with pooled user liquidity. This is the part of the platform that interacts with external DeFi protocols.

The biggest risks to the portfolio management system are administrative error and access control vulnerabilities. If the administrative tools used to manage the portfolio are used incorrectly, have errors in their implementation, or if the access control for the portfolio was compromised, it could lead to a loss of funds.

The portfolio management system handles:

  • Deploy strategy

  • Unwind strategy

Portfolio management is protected with the following security features:

  • Gnosis Safe

Portfolio Functions

The core component of the portfolio management system is a Gnosis Safe referred to as the LP Safe (Liquidity Provider Safe).

After the LP Safe has been funded by the liquidity pools, LP Safe signers can build a sequence of batched transactions that will deploy the liquidity to any DeFi protocol. Once yield has been earned on the deployed liquidity, the LP Safe can also unwind liquidity from any DeFi protocol to supply it back to the liquidity pools.

The portfolio management system benefits from the significant tooling built around the Gnosis Safe contracts. The UI makes it easier for permissioned accounts to manage the platform’s yield farming and the transaction service allows for monitoring of executed transactions.

Deploy Strategy

Liquidity held by the LP Safe can be deployed to DeFi protocols for the purpose of yield farming in what is referred to as a strategy. Any signer on the LP Safe can batch transactions that, when executed, perform the deployment of liquidity.

Figure 6: Deploy strategy flow

  1. An LP Safe signer first decides on a DeFi protocol to use for a yield farming strategy.

  2. An LP Safe signer builds a transaction with the Gnosis Safe wallet to call lock on the OracleAdapter. This prevents users from making deposits and withdrawals while the LP Safe performs actions that could impact the TVL.

  3. An LP Safe signer builds a transaction with the Gnosis Safe wallet to call addAssetAllocation on the TvlManager. The asset allocation added should use a periphery contract, such as Curve.sol, to track the balance of any liquidity the LP Safe will deploy to the DeFi protocol it will use for yield farming.

  4. An LP Safe signer builds a transaction batch using the Gnosis Safe wallet that performs the sequence of transactions that adds liquidity, stakes LP tokens, and any other actions required to yield farm with the chosen DeFi protocol.

  5. After the transaction batch has been confirmed and sufficient time has elapsed for the Chainlink TVL price feed to update, an LP Safe signer builds a transaction to call unlock on the OracleAdapter to reopen user deposits and withdrawals.

Unwind Strategy

Liquidity deployed to strategies by the LP Safe can be unwound for either redeployment to another strategy or withdrawal back to liquidity pools. Any signer on the LP Safe can batch transactions that, when executed, perform the unwind of liquidity.

Figure 7: Unwind strategy flow

  1. An LP Safe signer first decides on an underperforming DeFi protocol for rebalancing or for unwinding in preparation for withdrawal back to the liquidity pool.

  2. An LP Safe signer builds a transaction with the Gnosis Safe wallet to call lock on the OracleAdapter. This prevents users from making deposits and withdrawals while the LP Safe performs actions that could impact the TVL.

  3. If a strategy is being completely unwound, an LP Safe signer might build a transaction with the Gnosis Safe wallet to call removeAssetAllocation on the TvlManager. This asset allocation will no longer be used when the oracle system calculates TVL.

  4. An LP Signer builds a transaction batch using the Gnosis Safe wallet that performs the sequence of transactions that unstakes LP tokens, removes liquidity, and any other actions required to exit the chosen DeFi protocol.

  5. After the transaction batch has been confirmed and sufficient time has elapsed for the Chainlink TVL price feed to update, an LP Safe signer builds a transaction to call unlock on the OracleAdapter to reopen user deposits and withdrawals.

Portfolio Security

Because the portfolio management system is controlled heavily by governance, the main risks come from administrative error and access control vulnerabilities. The primary way these risks are mitigated are through the use of a Gnosis Safe.

Gnosis Safe

All yield farming strategies are run through a Gnosis Safe referred to as the LP Safe. The Gnosis Safe has several features that make it ideal for mitigating risk to the platform management system.

Transaction Builder

The Gnosis Safe wallet allows signers to build transactions and interact with DeFi protocols in an intuitive way without requiring scripts. This reduces the chance of administrative error where a signer executes a faulty transaction that leads to a loss of funds.

Transaction Service

The Gnosis Safe transaction service logs transactions executed by the Safe. Being able to view past transactions that have been executed will allow signers to detect if failures have occurred and avoid executing transactions out of order, thereby reducing the chance of administrative error.

Asset Dashboard

The Gnosis Safe wallet contains a dashboard where a signer can view all the assets held by a Safe and their USD value. This assists signers in tracking the yield farming strategies currently deployed and reduces the chance of administrative error where a signer forgets to add a particular asset allocation.

Secure Code

The Gnosis Safe has been audited and is widely used in the industry without incident. This reduces the likelihood that there is an access control vulnerability in the code.

Oracle Management

The purpose of the oracle management system is to price user shares of the liquidity pools. Without the oracle system, users would never be able to recover their share of liquidity or receive their share of the yield.

The biggest risks to the oracle management system are front-running, Chainlink failure, and failure to calculate the full TVL. Any one of these risks could lead to a loss of funds. Oracles are mission critical to DeFi platforms and are also one of the most commonly exploited components. The most devastating hacks in the DeFi industry have targeted vulnerable oracles.

The oracle management system handles:

  • Chainlink oracle update

Oracle management is protected with the following security features:

  • Oracle locks

    • Failure locks

    • Asset allocation locks

    • Funding locks

    • Manual time locks

  • Manual override

Oracle Functions

The oracle management system has two major responsibilities:

  • Providing stablecoin prices

  • Calculating TVL

Both of these responsibilities are primarily powered by Chainlink nodes. Stablecoin prices are pulled from data provider APIs then pushed to aggregators. TVL is calculated by the Chainlink external adapter using the TvlManager, then pushed to the TVL aggregator. It is important to note that this TVL excludes stablecoin that is undeployed and held in the liquidity pools.

Because the TVL can comprise many different assets, farming pools, and staked tokens, the platform uses the TvlManager to track everything with a set of structs referred to as asset allocations. Each asset allocation represents a balance of underlying assets that can be priced by a data provider. Liquidity tokens, for example, often do not have a queryable price, and must therefore be broken down into the underlying assets they represent. The TvlManager uses periphery contracts, such as contracts/periphery/Curve.sol and contracts/periphery/Aave.sol, to decompose these underlying asset balances.

Because Chainlink nodes are an asynchronous off-chain process from the rest of the platform, any dependencies the platform has on Chainlink must be carefully implemented to prevent the possibility of race conditions.

Oracle updates originate with the off-chain Chainlink node. The nodes check either stablecoin prices or TVL every minute and if the value has deviated by at least 1% from the previous recorded value, or it has been more than 24 hours since the last update, a new Chainlink oracle round will start. Once enough nodes have submitted a value, the round ends and the median value is pushed to the aggregator.

The Chainlink external adapter totals the value of all the asset allocations registered with the TvlManager to calculate the TVL. It is critical that all value in the platform, except for stablecoin held in the liquidity pools, is represented as asset allocations. It is necessary to exclude the liquidity pools from the TVL to properly calculate mAPT mint amounts.

Figure 8: Oracle update flow

  1. Chainlink nodes detect a deviation of greater than 1% from the last recorded aggregator value.

  2. Oracle round begins and Chainlink nodes submit values. Stablecoins are priced using multiple data providers and TVL is calculated from asset allocations stored in the TvlManager. The following process calculates TVL:

    1. The Chainlink external adapter calls getAssetAllocationIds on the TvlManager to get a list of IDs for every registered asset allocation.

    2. The Chainlink external adapter loops through each ID and calls balanceOf, decimalsOf, and symbolOf to retrieve data for that asset allocation.

    3. The asset allocation symbol is used to look up the price of the asset from the asset allocation from one of the available data providers.

    4. The balance of the asset allocation is multiplied by the price of the asset to get the USD value of the asset allocation.

    5. All the asset allocations are summed to get the TVL.

  3. The round can complete if enough nodes have submitted values. The median value is taken and recorded in an aggregator.

Oracle Security

The risk of front-running and failure to calculate the full TVL, is a result of Chainlink’s asynchronous nature and is mitigated by oracle locks. The risk of Chainlink failure is a result of relying on a third party dependency and is mitigated by manual overrides.

Oracle Locks

Oracle locks are an essential feature used to secure the oracle management system. If a platform operation is performed that could have a significant impact on TVL, it is necessary to lock oracle dependent features until Chainlink has had the time to detect the change and run a new round. The most common scenarios for this risk is when funding or withdrawing from the LP Safe, and adding or removing asset allocations.

The reason time-limited oracle locks are used instead of checking for the last oracle update time is because there are valid situations in which a lock should be used as a precaution, but the TVL will not deviate over the threshold required for an update. To avoid the possibility of a DoS, all locks have a time expiry.

There are four different types of locks that have been used to safeguard the oracle management system:

Failure Locks

When the Chainlink aggregator returns an invalid value (either a zero or a negative number), reverts when called, or has not had an update in over 24 hours the OracleAdapter will automatically lock. These locks do not need an expiry because they result from unambiguous on-chain conditions. When the conditions resolve, the OracleAdapter will be automatically unlocked.

Asset Allocation Locks

When a new asset allocation is added, or an existing one is removed, it could impact the TVL calculation because there is a change to balances being summed by the Chainlink external adapter. The OracleAdapter automatically locks for the default lock period, which is specified in the number of blocks, when there is a change in asset allocations.

Funding Locks

When the PoolManager funds the LP Safe or withdraws from the LP Safe, it could impact the TVL calculation because the liquidity pools are not tracked by the TvlManager. The OracleAdapter automatically locks for the default lock period when there is a fund or withdraw from the LP Safe.

Manual Time Locks

There are conditions that are not automatically detectable in which the OracleAdapter should also be locked. It is possible for the LP Safe to manually lock the OracleAdapter for a specified period of blocks. During normal system operation, an LP Safe signer should ensure that the OracleAdapter is manually locked before performing batched transactions to interact with other DeFi protocols.

Manual Override

In the event of price feed corruption, the Chainlink aggregators will appear to have valid values, but the values could be skewed or at extreme ranges, such as a maximum integer value or the smallest decimal value when adjusted for decimal places. When this happens, it can lead to a loss of funds if a malicious user takes advantage of the discrepancy between the reported TVL or stablecoin price, and the actual values.

To mitigate the impact of price feed corruption, the OracleAdapter allows the Admin Safe to manually set override values for both stablecoin prices and TVL. Override values have an expiry and while they are in effect, they supersede all other OracleAdapter return values with the exception of a manual time lock.