Comment on page
Arrakis V1 Docs (old)
Legacy Arrakis V1 Developer Documentation
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).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()
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 pairtokenB
The other token in the Uniswap V3 pairuniFee
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.)
- 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 toamount0Max
: max amount token0 to depositamount1Max
: max amount token1 to depositamount0Min
: 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 depositedamount1
: amount token1 depositedmintAmount
: amount Arrakis token shares minted
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)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 fromburnAmount
: vault shares to burnamount0Min
: minimum amount of token0 to receiveamount1Min
: minimum amount of token1 to receivereceiver
: address receiving tokens
Returns:
amount0
: amount token0 depositedamount1
: amount token1 depositedliquidityBurned
: amount liquidity burned from Uniswap
same functionality and arguments as
removeLiquidity
but unwraps WETH to ETH before remitting underlying to receiver.State Changing Methods on the
ArrakisVault
contract 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: 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. function burn(uint256 burnAmount, address receiver)
external
nonReentrant
returns (
uint256 amount0,
uint256 amount1,
uint128 liquidityBurned
)
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
)
get the current underlying balances of the entire Arrakis Vault
function getUnderlyingBalances()
public
view
returns (
uint256 amount0Current,
uint256 amount1Current
)
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).
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
)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.newUpperTick
The 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.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
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 | Address |
---|---|
Arrakis Factory | 0xEA1aFf9dbFfD1580F6b81A3ad3589E66652dB7D9 |
Arrakis Resolver | 0x0317650Af6f184344D7368AC8bB0bEbA5EDB214a |
Arrakis Router | 0xdD92062aDF9F6EDf528babe7F04804fe86424A74 |
Contract | Address |
---|---|
Arrakis Factory | 0x37265A834e95D11c36527451c7844eF346dC342a |
Arrakis Resolver | 0x3638fc820c22b9ecd631943Bc7d5591C0004C7b2 |
Arrakis Router | 0xc73fb100a995b33f9fA181d420f4C8D74506dF66 |
Contract | Address |
---|---|
Arrakis Factory | 0x2845c6929d621e32B7596520C8a1E5a37e616F09 |
Arrakis Resolver | 0xd2Bb190dD88e7Af5DF176064Ec42f6dfA8672F40 |
Arrakis Router | 0x9ce88a56d120300061593eF7AD074A1B710094d5 |
Arbitrum
Contract | Address |
---|---|
Arrakis Factory | 0xd68b055fb444D136e3aC4df023f4C42334F06395 |
Arrakis Resolver | 0xe03311D30bdeb60511BAe8de135C6524B9576B2e |
Arrakis Router | 0x2845c6929d621e32B7596520C8a1E5a37e616F09 |
Last modified 10mo ago