Strategy & Contract Overview
Introduction
The GammaSwap vault contract seamlessly integrates liquidity provision into a Uniswap V3 pool while hedging the corresponding position in terms of one of the pool's tokens, designated as the assetToken
within the smart contract. This hedge is achieved through a liquidity loan on GammaSwap, utilizing the same token pair as the Uniswap V3 pool. The primary objective of this combined strategy is to maintain a delta-neutral position relative to the chosen token from the Uniswap V3 pool.
For instance, consider a Vault configured for the WETH/USDC pool, where WETH is designated as the assetToken
. When the price of WETH rises and the Uniswap V3 liquidity position rebalances fully into USDC, the GammaSwap hedge appreciates in terms of WETH. Conversely, when the price of WETH declines and the Uniswap V3 position rebalances fully into WETH, the GammaSwap hedge decreases in value in terms of WETH. As a result, the combined position remains relatively stable in WETH value, regardless of price fluctuations.
The parameters governing the hedge are static from period to period although possible to be changed as necessary to improve vault's efficiency.
The strategy was originally described in the following x tweet

Contracts Description
The primary contract facilitating liquidity provision into Uniswap V3 and hedging via GammaSwap is the GammaVault contract. Users who deposit funds into the GammaVault receive ERC-20 tokens that represent their proportional share of the funds deployed in the strategy. However, direct interaction with the GammaVault is restricted. Only the manager address
is authorized to engage with the GammaVault in relation to its trading strategy logic.
The other relevant contracts are called the funding contracts. They consist of two ERC-4626 contracts: one responsible for managing deposits into the GammaVault and the other for handling withdrawals. The manager
, which can be either an externally owned account (EOA) or another contract, is tasked with executing key functions within the GammaVault, such as initiating deposits, withdrawals, and rebalancing the strategy.
The GammaVault operates through three possible lifecycle events:
Deposit β Rebalancing β Withdrawal
1) Deposit Event: Funds are transferred into the GammaVault and allocated to the trading strategy. In return, shares of the GammaVault are minted and sent to the funding contract responsible for managing deposits.
2) Rebalancing Event: The range of the Uniswap V3 liquidity position is updated (widened/shortened or set to a new range), and the GammaSwap hedge is recalibrated with a new strike price. If the hedge generates profits, these are reinvested into the Uniswap V3 position. Conversely, if the hedge incurs losses, the required amounts are drawn from the Uniswap V3 position to cover them. The delta neutrality of the strategy is dictated by the parameters defined by the manager. Consequently, the hedge may experience gains or losses that exceed the threshold required to maintain delta neutrality.
3) Withdrawal Event: Funds are extracted from the Uniswap V3 liquidity position, and the hedge is reduced proportionally to preserve the hedge ratio defined by the manager. If the hedge yields profits, these are distributed to withdrawing users. Conversely, if the hedge incurs losses, tokens withdrawn from the Uniswap V3 position are used to cover the deficit. All tokens intended for withdrawal are converted into the designated assetToken (e.g., WETH in the WETH/USDC example) and transferred to the withdrawal contract.

Funding Contracts
The Deposit Contract is an ERC-4626 contract designed to accept deposits in the designated assetToken
(e.g., WETH). In return, it issues proportional ERC-20 tokens (Deposit Contract tokens) to represent the depositor's share of all assetToken
holdings within the Deposit Contract.
When the GammaVault manager invokes the processDepositsAndWithdrawals()
function on the GammaVault, the Deposit Contract transfers its entire balance of assetToken
(e.g., WETH) to the GammaVault. The GammaVault utilizes these funds to add liquidity to Uniswap V3, performing any required token swaps before depositing. It simultaneously increases its hedge position on GammaSwap to maintain the desired strategy. Following this process, the GammaVault mints proportional shares of its ERC-20 tokens (GammaVault tokens) and transfers them to the Deposit Contract.
Users can then claim their share of GammaVault tokens by exchanging their Deposit Contract tokens. This is achieved by calling the claim()
function on the Deposit Contract, which burns the user's Deposit Contract tokens in return for the corresponding GammaVault tokens.
The Withdraw Contract functions similarly to the Deposit Contract (it is, in fact, an instance of the same contract) but operates in reverse. Users deposit GammaVault tokens into the Withdraw Contract and receive ERC-20 tokens (Withdraw Contract tokens) representing their proportional share of the withdrawal queue.
When the GammaVault manager calls the processDepositsAndWithdrawals()
function on the GammaVault, the Withdraw Contract transfers its GammaVault token holdings to the GammaVault. The GammaVault calculates the corresponding liquidity to withdraw from Uniswap V3, adjusts the GammaSwap hedge proportionally to maintain the hedge ratio, and converts all funds, including any profits or losses, back into the assetToken
(e.g., WETH). The GammaVault then transfers the resulting funds to the Withdraw Contract.
Users can claim their share of the assetToken
from the Withdraw Contract by calling the claim()
function. This burns their Withdraw Contract tokens in exchange for the corresponding share of assetToken
(e.g., WETH), completing the withdrawal process.
One may notice that deposits and withdrawals are handled using the same function processDepositsAndWithdrawals()
. That is because during the call of the function the GammaVault will net out deposits and withdrawals and only process the remaining amount, which is either a deposit or a withdrawal. This strategy reduces market impact costs of adding or removing liquidity from the strategy.
Funding Periods
Deposits and withdrawals in the GammaVault are divided into funding periods, each funding period ends in a net deposit or net withdrawal when the GammaVault manager calls the processDepositsAndWithdrawals()
function. During a given period, only funds allocated to that specific period in the Deposit or Withdraw contract are processed by the GammaVault. Each funding period is represented by an integer that increments by 1 with every completed deposit or withdrawal operation. While the GammaVault processes funds exclusively for the active period, users retain the flexibility to claim their funds from any previous period at any time by passing the respective period number to the claim()
function of the funding contract. Also, prior to processing a funding period, users may withdraw their deposited assets at any time using their funding contract tokens.
Deposit and Withdraw ERC-20 tokens are valid only for the current funding period. Once the period concludes, these tokens effectively expireβbalances in the funding contracts (via the balanceOf() function) return zero. This indicates that the funds associated with that period have already been processed, allowing users to claim their corresponding GammaVault ERC-20 tokens if depositing, or their assetToken
(e.g., WETH) if withdrawing.
To transition a funding contract to the next period, the GammaVault first invokes the collectAssets()
function on the respective funding contract to gather all deposited tokens. Once processing is complete, the GammaVault calls the disburseClaims()
function to confirm that any eligible assets have been transferred to their corresponding destination and to signal the funding contract to advance to the next period.
Whenever the price moves out of range or close enough to the edge of the range that warrants rebalancing, the Vault will rebalance at the end of the period right before processing the deposit/withdrawal.

GammaVault Contract
The GammaVault contract is an ERC-20 token contract designed to manage liquidity provision in a Uniswap V3 pool while hedging the associated LP position using a liquidity loan on GammaSwap. The number of ERC-20 tokens minted by the GammaVault reflects the amount of liquidity deposited into the contract. The size of the hedge established on GammaSwap is determined by the total liquidity allocated to the Uniswap V3 pool and the hedgeSize
parameter of the GammaVault, which is configured by the GammaVault manager. The hedgeSize parameter represents a percentage of total liquidity in UniswapV3. This percentage is the size of the GammaSwap loan in terms of liquidity invariant units.
Depositing into the Uniswap V3 LP Position
Since the Deposit Contract supplies only one type of token to the GammaVault, while the Uniswap V3 pool requires a pair of tokens in a specific ratio, the GammaVault must rebalance the deposited assets to match the required proportions. This rebalancing process is handled by the _depositAsset()
function.
The _depositAsset()
function determines the amount of WETH needed to fulfill the requirements of the Uniswap V3 LP range and executes the necessary swaps using GammaSwap's universal router. These swaps are performed iteratively in a loop to account for potential market impact and slippage when interacting with Uniswap V3. Because the exact market conditions during swapping cannot be predicted, each iteration assumes minimal slippage. However, in practice, slippage does occur, leaving a residual imbalance in the token ratios. The function recalculates the shortfall after each swap and continues swapping until only a negligible amount remains unmatched.
This iterative process ensures that liquidity is efficiently and accurately deposited into the Uniswap V3 pool and is employed whenever the GammaVault needs to add liquidity to the pool.
Depositing Process
The depositing process follows these general steps:
Collect Fees: Retrieve and deposit any fees earned from the Uniswap V3 position.
Claim Tokens: Collect the
assetToken
from the Deposit Contract.Check for Asset Deposits:
If no
assetTokens
were deposited from the Deposit Contract, increase the hedge size to reflect the increased size from fee collection.Notify the Deposit Contract to transition to the next period by invoking
disburseClaims()
, and terminate the process.
Deposit into Uniswap V3:
If
assetTokens
were deposited, add liquidity to the Uniswap V3 LP position at the current strategy range by callingdepositAsset()
.
Calculate New Shares: Determine the additional shares to mint based on the newly deposited liquidity.
Adjust Hedge: Compute the current liquidity in the Uniswap V3 LP position and increase the GammaSwap hedge to account for the updated
totalLiquidity
in Uniswap V3.Mint Shares: Mint new shares of the GammaVault to the Deposit Contract.
Finalize and Transition: Call
disburseClaims()
on the Deposit Contract to signify the completion of the deposit process and initiate the transition to the next period.
Withdrawal Process
The withdrawal process adheres to the following steps:
Collect Tokens: Retrieve the deposited GammaVault ERC-20 tokens.
Collect Fees: Gather and deposit any fees accrued from the Uniswap V3 position.
Check for Deposited Tokens:
If no GammaVault ERC-20 tokens are present in the GammaVault, increase the GammaSwap hedge size to reflect the increased size of the LP position from fee collection.
Notify the Withdraw Contract to transition to the next period by invoking
disburseClaims()
, and terminate the process.
Calculate Liquidity Share: If GammaVault ERC-20 tokens are deposited, compute the proportion of Uniswap V3 strategy liquidity represented by the tokens.
Adjust Hedge: Reduce the hedge size to reflect the expected decrease in liquidity.
If the hedge incurs a loss, repay it using the existing Uniswap V3 liquidity.
If the hedge generates profits, withdraw the profit to the GammaVault.
Remove Liquidity: Extract the portion of Uniswap V3 liquidity corresponding to the requested withdrawal amount.
Convert and Transfer Assets: Convert all withdrawn tokens to the designated
assetToken
(e.g., WETH) and transfer them to the Withdraw Contract.Burn Tokens: Burn the deposited GammaVault ERC-20 tokens to finalize the withdrawal.
Finalize and Transition: Call
disburseClaims()
on the Withdraw Contract to signal the completion of the withdrawal process and prompt the transition to the next period.
Rebalancing Process
The rebalancing process follows these key steps:
Net GammaSwap Hedge: Withdraw or add collateral tokens to convert hedge P/L to zero.
Collect Fees: Retrieve any fees accrued from the Uniswap V3 position.
Remove Liquidity: Withdraw all liquidity from the Uniswap V3 pool and burn the associated Uniswap V3 LP NFT.
Update Parameters: Adjust the range and hedge parameters based on the updated inputs provided by the GammaVault manager.
Reallocate Liquidity: Redeploy the withdrawn liquidity, along with the collected fees, into the newly defined Uniswap V3 range.
Adjust Hedge: Revise the hedge to reflect the updated liquidity value in the Uniswap V3 pool. This may involve increasing or decreasing the hedge size as necessary to align with the new liquidity levels.
Rebalancing resets the strategy to center the range around the current price while updating the hedge to have the same strike price relative to the mid point price of the LP range. For example, if the strike price is set to always 50% of the midpoint price of the range. Then, when the midpoint price changes from 2,000 USDC to 1,500 USDC after a rebalancing, the strike price of the GammaSwap hedge will change from 1,000 USDC to 750 USDC.
The GammaSwap Hedge
GammaSwap loans are structured as liquidity removed from a UniswapV2-style AMM and held as collateral in the GammaPool contract associated with the AMM. The collateral in the GammaPool is designed to fully secure the liquidity loan. As the price fluctuates, the value of the liquidity loan decreases relative to the collateral held in the GammaPool. This change in value, often referred to as impermanent loss, is the source of profit for the liquidity borrower in the context of GammaSwap.

GammaSwap applies an interest rate to liquidity borrowers, which is equal to the swap fees accrued in the AMM plus a small additional percentage based on the utilization rate of the GammaPool. To account for this interest, loans on GammaSwap must be overcollateralized. Borrowers are required to deposit additional tokens as collateral to cover the accrued interest. This means that the tokens held as collateral exceed the amount withdrawn from the AMM. These extra tokens, provided by the borrower, are held alongside the liquidity removed from the AMM.
When closing the loan the tokens held as collateral are used to repay the liquidity debt back to the AMM.

Each loan is represented by a unique tokenId
and is tracked in the tokensHeld
array within a Loan struct in the GammaPool. If a GammaSwap loan incurs lossesβsuch as when the price change is insufficient to generate impermanent loss that offsets the interest rateβthe borrower absorbs the loss, which is deducted from their additional collateral deposit.

Furthermore, rebalancing collateral tokens at the start of the loan increases the delta of the loan, amplifying potential profits as the price moves. However, rebalancing at the start of the loan also reduces the collateral value relative to the loan. To maintain the position, the borrower must deposit an even greater amount of tokens to ensure the loan remains adequately collateralized.

More information about the GammaSwap protocol can be found in the medium article describing the GammaSwap protocol
The GammaVault, however, does not deposit additional collateral directly to overcollateralize its loans. Instead, its supplementary collateral is derived from its Uniswap V3 position, which is dynamically tracked through calls to the onLoanUpdate
function in the VaultLoanObserver contract. Since the GammaVault contract inherits the VaultLoanObserver, it is also of LoanObserver type.

The collateral associated with the Uniswap V3 position is measured in terms of the liquidity invariant units that represent the total value of all tokens in the position. This value is calculated as if the tokens were removed from Uniswap V3 and converted into a UniswapV2-style position at the current price determined by the AMM utilized by GammaSwap. This calculation happens in the _getCollateral()
function of the GammaVault contract.
Consequently, if the loan incurs losses, liquidity must be withdrawn from the Uniswap V3 position to settle the GammaSwap loan obligations (e.g. during rebalancing or withdrawals).
Information about loan observers can be found here.
Rebalancing the GammaSwap Hedge
The tokens removed from the GammaSwap loan are rebalanced to establish a positive delta relative to the assetToken
. The delta refers to the rate of P/L change as the price of the AMM changes. This means that as the price of the assetToken
rises, the GammaSwap loan becomes increasingly profitable. Conversely, if the price declines, the GammaSwap loan incurs losses until the price reaches the ratio of the collateral tokens. At this point, the profit/loss (P/L) curve of the GammaSwap loan reverses, with losses diminishing and eventually turning positive. Importantly, the GammaSwap hedge's potential losses are inherently capped, as the maximum loss corresponds to the point where the AMM of GammaSwap quotes a price that matches the ratio of tokens in the tokensHeld
array. This ensures that the hedge's losses cannot entirely deplete the liquidity held in the Uniswap V3 position.
Rebalancing of collateral is facilitated through the TokenRebalancer contract. Tokens are withdrawn from GammaSwap's AMM and sent to the TokenRebalancer contract, where they are swapped iteratively using GammaSwap's UniversalRouter contract. This process continues until the collateral tokens align with the desired ratio specified by the GammaVault manager
during the rebalancePosition()
call. Once the rebalancing is complete, the tokens are returned to the GammaPool to be held as collateral for the GammaSwap loan with the appropriate delta.
Contract Design Choices: Explanation
Separation of Funding Contracts: Funding contracts were designed as separate entities from the main GammaVault contract to minimize the impact of deposits and withdrawals on the strategyβs statistical properties. The strategy itself operates as a dynamic hedging strategy, requiring periodic rebalancing. By limiting the frequency and timing of deposits and withdrawals, the strategyβs performance is preserved. This design choice also reduces overall network gas costs for users.
Delegate Calls to Proxy Contracts: Due to the size and complexity of the GammaVault contract, key processesβsuch as rebalancing, depositing, and withdrawingβare delegated to proxy contracts via delegate calls. This modular approach ensures the contract remains manageable and efficient.
Optimization of Network Costs: Rebalancing, depositing, and withdrawing operations involve large and complex transactions, which can be expensive in terms of network gas costs. To address this, these costs are borne exclusively by the GammaVault manager, significantly reducing expenses for users. Users only interact with the funding contracts, keeping their gas costs minimal.
Foundry Tests
GammaVault Tests: Unit tests for the deposit, withdraw, and rebalance logic are located in the
./test/foundry/GammaVaultTest.t.sol
file.Funding Contract Tests: Unit tests for the funding contracts are contained in the
./test/foundry/FundingVaultTest.t.sol
file.
Risks
Price Discrepancies Between AMMs: The GammaPool and Uniswap V3 operate on different AMM mechanisms, meaning their prices may diverge. As a result, the GammaSwap hedge's profit and loss (PnL) may not fully offset the losses incurred in the Uniswap V3 position. However, these discrepancies are expected to remain minimal due to the actions of arbitrageurs. Additionally, the GammaVault manager can mitigate this risk by ensuring that executions only occur when price differences are within an acceptable range. This is achieved using a periphery contract that reverts transactions if the results of depositing, rebalancing, or withdrawing exceed a predefined threshold.
Front-Running Vulnerability: Since executions occur periodically, the contract may be susceptible to front-running. To address this, the manager will execute withdrawals, rebalancing, and deposits randomly within a designated time window, while maintaining the sequence of operations. It is also possible to skip deposit and withdrawal steps when there are no pending actions, leaving only rebalancing to be executed. To preserve the statistical integrity of the strategy, rebalancing will always occur at a random time within the predefined period.
Last updated