Arrakis V1 Docs (old)

Legacy Arrakis V1 Developer Documentation

Introduction to Arrakis V1

Arrakis V1 vaults are generic ERC20 wrappers on Uniswap V3 Positions. Vaults with any price bounds on any Uniswap V3 pair can be deployed via the ArrakisFactory instantiating a tokenized V3 Position. When liquidity is added into the vault, Arrakis vault tokens are minted and credited to the provider. Inversely, Arrakis tokens can be burned to redeem that proportion of the pool's V3 position liquidity and fees earned. Thus, Arrakis tokens represent proportional ownership (or "shares") of the underlying Uniswap V3 position. Similar to the Uniswap V2 LP experience, anyone can add liquidity to or remove liquidity from a Arrakis Vault, and can earn their portion of the fees generated just by holding the fungible tokens. Some Arrakis vaults may have a special privileged manager role (see the Manager Functions sections).

ArrakisFactory (V1)

The ArrakisFactory smart contract governs over the creation of Arrakis Vaults. In theory, any account or smart contract can create a Arrakis Vault via the factory by calling deployVault. This creates a tokenized UniswapV3 Position with a given initial price range on the token pair and fee tier of your choice. Anyone can now participate as an LP in that range, by adding liquidity into this position and minting Arrakis tokens. Whatever account is set as the manager role is the only account that may alter the price range of all the underlying vault liquidity with executiveRebalance()

deployVault

Deploy a Arrakis Vault on the Uniswap V3 pair and with the Position parameters of your choosing.

    function deployVault(
        address tokenA,
        address tokenB,
        uint24 uniFee,
        address manager,
        uint16 managerFee,
        int24 lowerTick,
        int24 upperTick
    ) external returns (address vault)

Arguments:

  • tokenA One of the tokens in the Uniswap V3 pair

  • tokenB The other token in the Uniswap V3 pair

  • uniFee Fee tier of the Uniswap V3 pair (100, 500, 3000, 10000)

  • manager Account which is initial "manager" (ability to rebalance range). If you want vault position to be entirely immutable (position range can never change) set manager to Zero Address.

  • managerFee % cut of fees earned that accrue to manager, in Basis Points (9750 is max since 2.5% of earned fees already accrue to Arrakis Protocol).

  • lowerTick Initial lower price bound for the position, represented as a Uniswap V3 tick.

  • upperTick Initial upper price bound for the position, represented as a Uniswap V3 tick.

The lowerTick and upperTick: 1. MUST be set to integers between -887272 and 887272 where upperTick > lowerTick 2. MUST be integers divisible by the tickSpacing of the Uniswap pair.

Returns:

  • vault address of newly deployed Arrakis Vault ERC20 contract (proxied).

To have full verification and functionality on etherscan (read/write methods) verify the proxy contract. Etherscan will recognize the contract address as an ERC20 token and generate the token page after minting of the first Arrakis tokens.

In order for a new vault to be searchable on the arrakis beta ui list of vaults, the new vault needs to be added to the Arrakis community-data (make a pr here). (If any underlying tokens of the vault aren't on are token list yet, you can add those to this repo as well.)

ArrakisRouter (V1)

The ArrakisRouter smart contract is responsible for handling user interactions, namely:

  • adding liquidity to an Arrakis vault and minting Arrakis tokens

  • removing liquidity from an Arrakis vault by burning Arrakis tokens (redeeming that share of the vault's underlying balances)

addLiquidity

remember to approve the ArrakisRouter to spend up to amount0Max and amount1Max before calling this method

    function addLiquidity(
        address arrakisVault,
        uint256 amount0Max,
        uint256 amount1Max,
        uint256 amount0Min,
        uint256 amount1Min,
        uint256 amountSharesMin,
        address receiver
    )
        external
        returns (
            uint256 amount0,
            uint256 amount1,
            uint256 mintAmount
        )

Arguments:

  • arrakisVault : vault address to add liquidity to

  • amount0Max : max amount token0 to deposit

  • amount1Max : max amount token1 to deposit

  • amount0Min : min amount token0 to deposit (slippage param)

  • amount1Min : min amount token1 to deposit (slippage param)

  • amountSharesMin : min amount Arrakis tokens to mint (slippage param)

  • receiver : address receiving Arrakis tokens

use getMintAmount view method of ArrakisVault to get expected values and calculate min thresholds, for example: const { amount0, amount1, mintAmount } =

await vault.getMintAmounts( amount0Max, amount1Max ); const amount0Min = amount0.mul(99).div(100) // 1% slippage const amount1Min = amount1.mul(99).div(100) const amountSharesMin = mintAmount.mul(99).div(100)

Returns:

  • amount0 : amount token0 deposited

  • amount1 : amount token1 deposited

  • mintAmount : amount Arrakis token shares minted

addLiquidityETH

same functionality and arguments as addLiquidity but expects ETH transfers and wraps for WETH token (for vaults with WETH as one of the tokens in the pair)

removeLiquidity

remember to approve Arrakis token to be spent by the router before calling this method.

    function removeLiquidity(
        address arrakisVault,
        uint256 burnAmount,
        uint256 amount0Min,
        uint256 amount1Min,
        address receiver
    )
        external
        returns (
            uint256 amount0,
            uint256 amount1,
            uint128 liquidityBurned
        )

Arguments:

  • arrakisVault : vault address to remove liquidity from

  • burnAmount : vault shares to burn

  • amount0Min : minimum amount of token0 to receive

  • amount1Min : minimum amount of token1 to receive

  • receiver : address receiving tokens

Returns:

  • amount0 : amount token0 deposited

  • amount1 : amount token1 deposited

  • liquidityBurned : amount liquidity burned from Uniswap

removeLiquidityETH

same functionality and arguments as removeLiquidity but unwraps WETH to ETH before remitting underlying to receiver.

ArrakisVault (V1)

State Changing Methods on the ArrakisVaultcontract are low level and are NOT RECOMMENDED for direct interaction by end users unless they know why and what they are doing. For standard addLiquidity / removeLiquidity interaction with Arrakis see ArrakisRouter

The ArrakisVault smart contract is the core implementation that powers the Arrakis protocol (all Arrakis token proxy contracts point to this implementation). It is an extension of the ERC20 interface so all normal ERC20 token functions apply, as well as a number of custom methods particular to Arrakis which will be outlined below:

mint

    function mint(uint256 mintAmount, address receiver)
        external
        nonReentrant
        returns (
            uint256 amount0,
            uint256 amount1,
            uint128 liquidityMinted
        )

NOT SAFE to approve vault and call mint naively from your EOA, since providing DEX liquidity requires sandwich attack protection (slippage tolerance on market price passed from off-chain), see addLiquidity for safe entry method.

burn

    function burn(uint256 burnAmount, address receiver)
        external
        nonReentrant
        returns (
            uint256 amount0,
            uint256 amount1,
            uint128 liquidityBurned
        )

getMintAmounts

View method to compute the amount of Arrakis tokens minted (and exact amounts of token0 and token1 forwarded) from and amount0Max and amount1Max

    function getMintAmounts(uint256 amount0Max, uint256 amount1Max)
        external
        view
        returns (
            uint256 amount0,
            uint256 amount1,
            uint256 mintAmount
        )

getUnderlyingBalances

get the current underlying balances of the entire Arrakis Vault

    function getUnderlyingBalances()
        public
        view
        returns (
            uint256 amount0Current,
            uint256 amount1Current
        )
    

getUnderlyingBalancesAtPrice

Get the current underlying balances given a custom price (not simply taking the current Uniswap price which can be manipulated).

    function getUnderlyingBalancesAtPrice(
        uint160 sqrtRatioX96
    )
        external
        view
        returns (
            uint256 amount0Current,
            uint256 amount1Current
        )

This function is useful for getting a fair unmanipulatable price of a Arrakis token for things like lending protocols (simply pass a time weighted average sqrtPrice to this function to get the unmanipulated underlying balances).

getPositionId

function getPositionID() 
    external view returns (bytes32 positionID) 

get the Identifier of the Arrakis position on the Uniswap V3 pair (useful for fetching data about the Position with the positions method on UniswapV3Pool.sol)

Vault Manager Functions

Vaults created with a manager account who can configure the Gelato Executor meta-parameters and also can control and alter the range of the underlying Uniswap V3 position.

The manager is the most important and centrally trusted role in the Vault. It is the only role that has the power to potentially extract value from the principal invested or potentially grief the vault in a number of ways. One should only put funds into "managed" vaults if they have some information about the manager account: manager could be fully controlled by a DAO (token voting), or could simply be escrowed in a project's multi-sig, or be locked in a smart contract that automates rebalances under a certain codified strategy, or be trusted by the user for some other reason.

executiveRebalance

By far the most important manager function is the executiveRebalance method on the ArrakisVault. This permissioned method is the only way to change the price range of the underlying Uniswap V3 Position. Manager accounts who control this function are the means by which custom rebalancing strategies can be built on top of Arrakis. These strategies can be implemented by governance (slow, but decentralized) or by some central managerial party (more responsive but requiring much more trust) and in the future the manager role can be granted to a Keeper automated smart contract, where some LP strategy is fully codified on-chain!

    function executiveRebalance(
        int24 newLowerTick,
        int24 newUpperTick,
        uint160 swapThresholdPrice,
        uint256 swapAmountBPS,
        bool zeroForOne
    ) external onlyManager {

Arguments:

  • newLowerTick The new lower price bound of the position represented as a Uniswap V3 tick.

  • newUpperTickThe new upper price bound of the position represented as a Uniswap V3 tick.

  • swapThresholdPrice A sqrtPriceX96 that acts as a slippage parameter for the swap that rebalances the inventory (to deposit maximal liquidity around the new price bounds).

  • swapAmountBPS Amount of inventory to swap represented as Basis Points of the remaining amount of the token to swap.

  • zeroForOne The direction of the rebalancing swap.

In order to generate the parameters for an executive rebalance one has to understand the flow of this operation. First, the Arrakis vault removes all the liquidity and fees earned. Then, it tries to deposit as much liquidity as possible around the new price range. Next, whatever is leftover is then swapped based on the swap parameters. Finally, another deposit of maximal liquidity to the position is attempted and any leftover sits in the contract balance waiting to be reinvested.

To generate the swap parameters for an executive rebalance tx, simulate the entire operation. It works like this:

1. Call getUnderlyingBalances on the ArrakisVault to obtain amount0Current and amount1Current 2. Compute amount0Liquidity and amount1Liquidity using LiquidityAmounts.sol library, the new position bounds, the current price and the current amounts from step 1.

3. Compute amount0Leftover and amount1Leftover with formula amount0Leftover = amount0Current - amount0Liquidity. In most cases one of these values will be 0 (or very close to 0). 4. Use amount0Liquidity and amount1Liquidity to compute the current proportion of each asset needed. 5. Use the amount0Leftover and amount1Leftover and the proportion from previous step to compute which token to swap and the swapAmount.

6. Convert swapAmount to swapAmountBPS by doing swapAmount * 10000 / amountLeftover for the token being swapped.

A complete example of this calculation can be found in this test file

updateManagerParams

Another important role of the manager is to configure the manager parameters including those that restrict the functionality of Gelato Executors. These parameters include how often Gelato bots can reinvest fees and withdraw manager fees, as well as other safety params like the slippage check. Only the manager can call updateManagerParams .

    function updateManagerParams(
        int16 newManagerFeeBPS,
        address newManagerTreasury,
        int16 newRebalanceBPS,
        int16 newSlippageBPS,
        int32 newSlippageInterval
    ) external onlyManager {

Arguments:

  • newManagerFeeBPS Change the cut of fees earned that accrue to manager in Basis Points (9750 max since 2.5% of fees go to Arrakis)

  • newManagerTreasury The treasury address where manager fees are auto withdrawn.

  • newRebalanceBPS The maximum percentage the auto fee reinvestment transaction cost can be compared to the fees earned in that feeToken. The percentage is given in Basis Points (where 10000 mean 100%). Example: if rebalanceBPS is 200 and the transaction fee is 10 USDC then the fees earned in USDC must be 500 UDSC or the transaction will revert.

  • newSlippageBPS The maximum percentage that the rebalance slippage can be from the TWAP.

  • newSlippageInterval length of time in seconds for to compute the TWAP (time weighted average price). I.e. 300 means a 5 minute average price for the vault.

transferOwnership

Standard transfer of the manager role to a new account.

    function transferOwnership(address newOwner)
        public onlyManager

Arguments: newOwner the new account to act as manager.

toggleRestrictedMint

function toggleRestrictMint() external onlyManager

Make it so only the manager role can mint LP tokens. Useful for creating a "Wrapped" vault where LP token minting is restricted to the wrapper.

renounceOwnership

Burn the manager role and set all manager fees to 0.

function renounceOwnership() public onlyManager

Arrakis Subgraph

The Arrakis subgraph tracks relevant data for all Arrakis Positions created through the ArrakisFactory createVault method. Most relevant information about about a Arrakis position is indexed and queryable via the subgraph. Query URL is: https://api.thegraph.com/subgraphs/name/arrakisfinance/vault-v1-mainnet Here is an example query which fetches all information about all Arrakis Positions:

      query {
        vaults {
          id
          blockCreated
          manager
          address
          uniswapPool
          token0 {
            address
            name
            symbol
          }
          token1 { 
            address
            name
            symbol
          }
          feeTier
          liquidity
          lowerTick
          upperTick
          totalSupply
          positionId
          managerFee
          name
          reranges {
            lowerTick
            upperTick
            timestamp
          }
          snapshots {
            apr
            startTimestamp
            endTimestamp
          }
          numSnapshots
          apr {
            averageApr
            timestamp
          }
        }
      }

id: subgraph identifier for the vault (contract address) blockCreated: block Arrakis position was deployed address: contract address of Arrakis positions uniswapPool: contract address of Uniswap V3 pair token0: token0 info token1: token1 info feeTier: Uniswap V3 Pair fee tier (500, 3000, 10000) liquidity: amount of liquidity currently in Arrakis position lowerTick: current lower tick of Arrakis Position upperTick: current upper tick of Arrakis Position totalSupply: current total supply of Arrakis token positionId: Uniswap V3 ID of the Arrakis position managerFee : managerFee in BPS (cut of trading fees taken by manager) name : Arrakis token name reranges : info about historical V3 position bounds (past executiveRebalances) snapshots : a fee APR snapshot taken whenever mint/burn (whenever arrakis vault liquidity amount in uniswap changes). Multiply BigDecimal apr by 100 to get as a percentage. numSnapshots : number of snapshots in the list apr : weighted average fee APR since inception of vault. Multiply BigDecimal averageApr by 100 to get a percentage.

Contract Addresses

Ethereum Mainnet

ContractAddress

Arrakis Factory

0xEA1aFf9dbFfD1580F6b81A3ad3589E66652dB7D9

Arrakis Resolver

0x0317650Af6f184344D7368AC8bB0bEbA5EDB214a

Arrakis Router

0xdD92062aDF9F6EDf528babe7F04804fe86424A74

Polygon

ContractAddress

Arrakis Factory

0x37265A834e95D11c36527451c7844eF346dC342a

Arrakis Resolver

0x3638fc820c22b9ecd631943Bc7d5591C0004C7b2

Arrakis Router

0xc73fb100a995b33f9fA181d420f4C8D74506dF66

Optimism

ContractAddress

Arrakis Factory

0x2845c6929d621e32B7596520C8a1E5a37e616F09

Arrakis Resolver

0xd2Bb190dD88e7Af5DF176064Ec42f6dfA8672F40

Arrakis Router

0x9ce88a56d120300061593eF7AD074A1B710094d5

Arbitrum

ContractAddress

Arrakis Factory

0xd68b055fb444D136e3aC4df023f4C42334F06395

Arrakis Resolver

0xe03311D30bdeb60511BAe8de135C6524B9576B2e

Arrakis Router

0x2845c6929d621e32B7596520C8a1E5a37e616F09

Last updated