> For the complete documentation index, see [llms.txt](https://docs.markovlabs.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.markovlabs.xyz/alternative-vaults/funding-rate-arbitrage-vault.md).

# Funding Rate Arbitrage Vault

## Overview

A step-by-step guide to setting up, configuring, and operating the **Markov Funding Rate Arbitrage (mkFRA)** vault on Enzyme, including deposit processing and delta-neutral trade execution.

The strategy runs a delta-neutral position: a leveraged long leg on **Aave (Ethereum mainnet)** hedged by an equal short leg on **Hyperliquid**, capturing funding-rate spread while keeping net ETH exposure close to zero.

Users can deposit using the following link: [Funding Rate Arbitrage Vault](https://vaults-markovlabs.user.onyx.enzyme.finance/).

***

### 1. Address Setup & Management

To initiate the vault's operations, three distinct address groups must be set up: a Safe multisig on **Ethereum mainnet**, a Safe multisig on **HyperEVM**, and a **Hyperliquid Perps executor**.

#### Address requirements

* **Vault Owner Multisig (Ethereum):** the address that owns the vault.
* **Vault Administrator Multisig (Ethereum):** performs administration operations such as updating NAV and accepting redemptions/withdrawals.
* **Trading Multisig (Ethereum and HyperEVM mainnet):** the address where the long leg of the Aave loop is executed (on Ethereum mainnet) and from which bridging to HyperEVM mainnet happens.
* **Hyperliquid Perps Executor:** an address dedicated to executing trades on Hyperliquid.

#### Multisig architecture

```mermaid
flowchart TB
    subgraph MO["Management / Operations"]
        direction TB
        S1a["Signer 1"] --> OWN["Vault Owner Multisig"]
        S1b["Signer 2"] --> OWN
        S1c["Signer 3"] --> OWN
        S2a["Signer 1"] --> ADM["Vault Administrator Multisig"]
        S2b["Signer 2"] --> ADM
        S2c["Signer 3"] --> ADM
    end
    subgraph TR["Trading"]
        direction TB
        S3a["Signer 1"] --> TRD["Ethereum & HyperEVM Multisig"]
        S3b["Signer 2"] --> TRD
        S3c["Signer 3"] --> TRD
        S4a["Signer 1"] --> HC["HyperCore Multisig"]
        S4b["Signer 2"] --> HC
        S4c["Signer 3"] --> HC
    end
```

#### Initialization & security

* **Deployment:** deploy both Safe instances with owner permissions assigned to the three designated signers.
* **Permissions:** **3/3** for the Owner Multisig; **2/3** for the others.

#### Gas funding & maintenance

Operations require gas funding:

* **Ethereum / HyperEVM:** native tokens (`$ETH` and `$HYPE`) are required for transaction fees related to deposits, withdrawals, and bridging.
* **Executor address:** the Hyperliquid executor requires native gas (`$HYPE`) for trade execution and management.

#### Official addresses

To adopt a naming standard that facilitates signing, the setup references the following canonical addresses:

| Reference | Role                   | Address                                      |
| --------- | ---------------------- | -------------------------------------------- |
| Address 1 | Owner Multisig         | `0xaf76Ea156FC9a2290ef6D1E1C784CCB2b4bdf9EC` |
| Address 2 | Administrator Multisig | `0x1cF751685d24C096FF99855797FE3E1f13B87A8c` |
| Address 3 | Trading Multisig       | `0xbCA3078239Ec3b8553A424d6531792c09B9102F7` |
| Address 4 | HyperCore Executor     | `0x192Cd74D7E740bbE35103BdbF3A5DC5f8A864489` |

Each multisig is controlled by its three designated signers. Individual signer wallets are not published.

***

### 2. Configuration & Initial Deposit

After the Enzyme team creates the vault and transfers ownership to the owner multisig, the vault must be finished being configured.

This involves:

1. Configuring the Owner multisig also as vault administrator.
2. Setting up the allowlist of authorized investors using the `addDepositControllerToInternalAllowlist(address)` method.

Once the first whitelisted address is configured, the initial deposit is made through the Enzyme Vault UI, similar to any other DeFi protocol interaction.

#### Processing the first deposit

From the administrative side, the vault admin must process every pending request. Before accepting, the Net Asset Value (NAV) of the vault should always be updated. Afterwards, the deposit is finalized by calling `executeDepositRequests([pending_deposit_index])`.

#### Confirmation of deployment

Once the vault is deployed and the first deposit is accepted, verify that the shares received by the depositor address match the number of outstanding shares in the vault, and that the subscription price also matches the vault's NAV.

***

### 3. Execute a Trade

#### Pre-trade setup

With funds already in the vault, the following steps set up a trade:

1. Transfer USDC to the trading Safe (an address outside of the vault) using `withdrawAssetTo(tradingSafe, usdc_amount)`.
2. From the trading Safe, take half of the received USDC and bridge it to HyperEVM through **Circle CCTP**. To get the transaction batch, make a request to the proposer service.
3. After \~15 minutes, once the USDC lands in the HyperEVM multisig address, transfer the USDC to the HyperCore executor address on HyperEVM. Once it lands, bridge that USDC to HyperCore using the Hyperliquid UI.
4. To confirm success, verify that the same USDC amount sits idle on the mainnet multisig and as margin in the HyperCore unified account.

#### Execution

To execute the trade as planned:

1. Make a request to the proposer for opening a trade with the intended amount.
2. Take the JSON batch and drop it in the SAFE UI for the transaction to be proposed.
3. Confirm the orchestrator is listening for transactions and that the price spread is positive.
4. Sign the transaction and execute the Ethereum mainnet leg on Aave.
5. Confirm that the short leg was also opened on Hyperliquid.
6. Confirm that the position is delta-neutral and the fees paid were correctly accounted for.

The end result should be a position very close to delta-neutral (minus the trading fees paid).

***

### 4. Vault Operations

This section details further vault flows: serving a redemption request, updating the NAV, accepting multiple deposits, and rebalancing margin accounts between the Aave and Hyperliquid legs.

#### Update the NAV

Before any deposit or redemption request is processed, or after any trade is taken, the AuM of the vault needs to be updated (along with daily updates). The indexer services provide the AuM of the strategy outside of the vault. Once that value is known, a transaction batch is built on the Safe UI to submit the new AuM value.

**Tx details**

* **Contract:** ValuationHandler `0x7a5b5fde46ff2fd8dec412a38dafdce4822ccb25`
* **Method:** `updateShareValue(int256)`
* Off-vault AuM in 18 decimals (`$100 → 100e18`)

#### Accept deposit requests

Once the NAV is updated, the admin can accept one or multiple pending requests at once. As long as there is at least one pending request, the method can be extended to multiple indexes: `executeDepositRequests([id1, id2])`.

**Tx details**

* **Contract:** DepositHandler `0xd1e6d37d90ef493dc29937626e88050a3654a950`
* **Method:** `executeDepositRequests(uint256[] ids)`

{% hint style="info" %} Finding IDs is possible by scanning `getDepositRequest(id)` up to `getDepositLastId()`. {% endhint %}

#### Serve redemption requests

Before serving redemption requests, besides updating the NAV, there should be enough USDC on the vault address to serve the redemptions being approved. Anytime before a redemption request is approved and executed, the vault NAV should always be updated using the method described above.

**Tx details**

* **Contract:** RedeemHandler `0x2c7b9f6f77de567eb3ab837b894fa599e6a7daae`
* **Method:** `executeRedeemRequests(uint256[] ids)`

***

### 5. Open a Trade

Opening a trade should always follow the pre-trade setup and execution steps described above. Never open trades below **25 USDC** (Hyperliquid's minimum trade amount is 10 USDC), and always update the NAV afterwards.

Opening a trade has two transactions, but only one needs to be signed at the multisig level (the Ethereum mainnet leg), as the HyperCore one is executed automatically using the API Wallet.

**Contracts involved (Ethereum mainnet)**

<table><thead><tr><th>Contract</th><th width="417.7335205078125">Address</th></tr></thead><tbody><tr><td>Aave V3 Pool</td><td><code>0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2</code></td></tr><tr><td>aEthWETH (collateral receipt)</td><td><code>0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8</code></td></tr><tr><td>Uniswap SwapRouter02</td><td><code>0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45</code></td></tr><tr><td>USDC</td><td><code>0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48</code></td></tr><tr><td>WETH</td><td><code>0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</code></td></tr><tr><td>Safe MultiSend (delegatecall)</td><td><code>0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761</code></td></tr><tr><td>Chainlink ETH/USD feed</td><td><code>0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419</code></td></tr></tbody></table>

Swaps use the USDC/WETH 0.05% pool (fee = 500).

**Open tx flow** (iterative loop, no flashloan)

1. `USDC.approve(router, total_usdc)` — initial amount plus all planned borrows.
2. `WETH.approve(pool, total_weth)` — sum of all supply amounts.
3. Seed step: `SwapRouter02.exactInputSingle(USDC → WETH, fee 500)`, then `Pool.supply(WETH, amount, onBehalfOf = Safe, referralCode 0)`.
4. Per loop step until \~2x leverage:
   * `Pool.borrow(USDC, amount, rateMode 2, 0, Safe)`
   * `SwapRouter02.exactInputSingle(USDC → WETH)`
   * `Pool.supply(WETH, amount, Safe, 0)`

Each supply amount is set slightly below the swap's expected output, so a small WETH dust remainder is left in the Safe.

***

### 6. Close a Trade

There are two situations when closing: closing a portion of the position, or closing the entire position.

For a **partial close**, if the trade is small enough, request a close trade from the proposer. It provides the JSON transaction batch to withdraw wETH from Aave and swap to USDC, while the orchestrator reduces the short position by an equal amount. If the balance is small enough, a rebalance can be produced to balance the margin levels after the trade. If the amount is too big, this needs to be done in several steps while rebalancing the liquidity.

For a **full close**, perform the same multiple-batch approach until the remainder of the position reaches a size small enough to execute the inverse flow of opening a position. When this happens, the orchestrator engine also closes the entire short position so the delta exposure to ETH becomes 0.

**Close tx flow** (iterative deleverage)

1. `WETH.approve(router, total)` and `USDC.approve(pool, total)`.
2. Per deleverage step:
   * `Pool.withdraw(WETH, amount, to = Safe)`
   * `SwapRouter02.exactInputSingle(WETH → USDC)`
   * `Pool.repay(USDC, amount, rateMode 2, Safe)`
3. Final step: `Pool.withdraw(WETH, MAX_UINT256)` sweeps all remaining aWETH, then a conservative swap WETH → USDC.

Result should be roughly (collateral − debt) USDC in the Safe, and the position fully closed.

***

### 7. Rebalance Position

There are two rebalancing behaviours: from Aave to Hyperliquid, and from Hyperliquid to Aave.

**Aave → Hyperliquid:** borrow more USDC in the intended amount, bridge it to HyperEVM using Circle CCTP, and from the HyperEVM multisig address make the path to HyperCore to deposit this USDC as margin.

**Hyperliquid → Aave:** do the opposite. There is an important difference: the Circle CCTP relay service only automatically mints the USDC when the bridge happens from Ethereum mainnet to HyperEVM. When bridging the opposite path, once the Iris API from Circle gives permission, the USDC must be minted to the multisig on Ethereum mainnet.

For both rebalance behaviours, the request can be made to the proposer service, which provides the JSON files for the required transactions.

***

### 8. Vault Example Trade

A worked example of a full delta-neutral open.

#### 1. NAV update + remaining deposits + forward

Admin Safe nonce 7 (mainnet) — Tx Hash: `0x23ea28b93c6edacc2dcdb6092c81c3fee49848847a91a8b693eddc3952e49a3c`

One MultiSend:

1. `updateShareValue(1000.738599…e18)` — refreshed untracked NAV ≈ $1,000.74.
2. `executeDepositRequests([4, 5])`.
3. `vault.withdrawAssetTo(USDC, tradingSafe, 50_000_000000)` — 50,000 USDC forwarded from the vault to the trading Safe.

#### 2. Bridge 25k to HyperEVM (CCTP)

Trading Safe nonce 2 (mainnet) — Tx Hash: `0xafcb9934dfd49c3902648e985cfd5eddfce1b50d72186ca6087fb25f902fe8c3`

1. `USDC.approve(TokenMessengerV2 0x28b5…cf5d, 25_000_000000)`
2. `depositForBurn(25_000_000000, domain 19, mintRecipient = trading Safe, minFinality 2000)` — standard-finality burn.

{% hint style="info" %} The mint on HyperEVM is auto-relayed (no manual `receiveMessage`). {% endhint %}

#### 3. Fund the HL executor (two-phase, HyperEVM)

Trading Safe nonce 3 (HyperEVM) — Tx Hash: `0xa456386cdcf787577166950474fc60821945f2fce370bc21d7b7052c48de8b35`

1. `USDC.approve(CoreDepositWallet 0x6B9E…0A24, 25_000_000000)`
2. `CoreDepositWallet.deposit(25_000_000000, dex = spot)` — EVM → Core spot, lands on the Safe's Core spot balance.

Trading Safe nonce 4 (HyperEVM) — Tx Hash: `0x3e1f5e07f0f43107790a15f3635d8d9d01582383b9aa9a4f24f3ffb312ef5853`

1. `CoreWriter.sendRawAction` — spotSend (action 6): 25,000 USDC (2.5e12 Core wei, 8-dec) from the Safe's Core spot to the executor `0x192Cd…4489`. Spot USDC collateralizes the perp (unified account).

#### 4. Aave 2x open long

Trading Safe nonce 3 (mainnet) — Tx Hash: `0x517c61b61a846da7d57578e42d91224882d8187f4632dfb7071757643d6cd0b8`

One MultiSend on Aave Pool `0x8787…A4E2` / SwapRouter02 `0x68b3…Fc45`:

1. `USDC.approve(router)`: 50,000 USDC
2. `WETH.approve(pool)`: 26.953219 WETH
3. `exactInputSingle(USDC → WETH)` seed (in 25,000, minOut 13.271382)
4. `Pool.supply(WETH)`: 13.476610
5. `Pool.borrow(USDC, variable)`: 17,500
6. `exactInputSingle(USDC → WETH)`: in 17,500, minOut 9.289967
7. `Pool.supply(WETH)`: 9.433627
8. `Pool.borrow(USDC, variable)`: 7,500
9. `exactInputSingle(USDC → WETH)`: in 7,500, minOut 3.981415
10. `Pool.supply(WETH)`: 4.042983

**Result:** 26.953219 WETH supplied, 25,000 USDC debt — 2x long on 25k equity. Swap output above the conservative supply amounts (\~0.71 WETH) stays in the Safe as idle WETH.

#### 5. HL short — the mirror trade

4s after the Aave batch — orchestrator-armed hedge, executor `0x192Cd…4489`:

IOC sell, oid 497774304091: 7 partial fills, 26.9428 ETH @ 48,690 USDC.

Fill hash: `0xfbb7cfe1953f05d5fd3104402293a002058300c7303224a89f807b345432dfc0`

**Short after:** 27.5119 ETH = exactly the Aave principal (0.5691 pre-existing pilot + 26.9428).

#### 6. Netting the small delta

{% hint style="info" %} Whenever execution is not perfect, a mechanism is in place to always check the delta not hedged (or over-hedged) and adjust it. {% endhint %}

One-shot IOC to net a small idle-WETH residual (the \~0.71 WETH swap surplus stranded in the Safe at step 5, unhedged by the principal-only orchestrator target):

IOC sell, oid 497780587330: 0.7097 ETH @ 1,285 USDC.

Fill hash: `0xd19daae75ff02c06d317044022a58702054c00ccfaf34ad87566563a1ef405f1`

**Final short:** 28.2216 ETH = principal 27.512 + residual 0.710.

#### End state

* **Aave leg:** 26.953 aWETH collateral / 25,000 USDC variable debt (2x).
* **HL leg:** 28.2216 ETH short, \~25k USDC margin (\~2x at entry).
* **Safe:** \~0.71 idle WETH (hedged by the extra short), USDC dust.
