# Welcome

⏱️ 2 min read

**We are DeFindex** 🔁, a decentralized protocol that makes yield simple and accessible. DeFindex empowers wallet providers, developers, and crypto users to integrate and access a wide range of strategies—bridging DeFi complexity into a plug-and-play solution for yield generation.

### ✨ Why DeFindex?

* **Plug-and-play yield** → no need for deep DeFi expertise.
* **Composable strategies** → support multiple assets and strategies per asset.
* **Secure architecture** → audited, with built-in safeguards like **rebalance** and **rescue** functions.
* **Aligned incentives** → wallets earn whenever their users earn.
* **Future-proof** → easily extend strategies, integrate real-world assets, and unlock new income streams.

<figure><img src="/files/fSTPHT3vRY4lLx4Y45wq" alt=""><figcaption></figcaption></figure>

#### What will you find here?

* [**API Integration Guide**](/api-integration-guide/api): Start here! Learn how to integrate DeFindex into your wallet or application. Access our APIs, SDKs, and quickstart guides to enable yield for your users.
* [**Understanding DeFindex**](/getting-started/getting-started): Core concepts to understand how DeFindex works:
  * [Understanding APY](/getting-started/getting-started/understanding-apy) — How yields are calculated
  * [Vault Roles](/getting-started/getting-started/vault-roles) — Manager, rebalancer, and fee receiver responsibilities
  * [Partner Fees](/getting-started/getting-started/partner-fees) — How the fee model works for partners
* [**SDKs**](/advanced-documentation/sdks): TypeScript and Flutter SDKs for seamless integration.
* [**Postman Collection**](https://drive.google.com/drive/folders/1hp02ySFWFeunRCwiZ6oLCjHzcJXpWhX8?usp=drive_link): Ready-to-use API collection to test and explore DeFindex endpoints.
* [**Smart Contract Development**](/advanced-documentation/developer-introduction): For protocol developers creating new strategies.

If you're new, start with the [**API Integration Guide**](/api-integration-guide/api) to integrate DeFindex, or explore [**What is DeFindex**](/getting-started/getting-started/what-is-defindex) for a detailed overview of the protocol.

With **DeFindex** 🔁, we are offering a secure, efficient, and user-friendly solution to optimize your asset returns.

**DeFi made Easy!**


# Understanding DeFindex

⏱️ 1 min read

DeFindex is a comprehensive yield optimization platform that enables users to create and manage yield-generating vaults. By connecting these vaults to various DeFi strategies, users can maximize their returns while maintaining control over their investment approach.

The platform offers a seamless experience from vault creation to strategy management, with features including:

* Automated yield generation
* Dynamic strategy allocation
* Professional-grade security
* Emergency protection mechanisms

Whether you're a DeFi enthusiast or a professional investor, DeFindex provides the tools and infrastructure needed to optimize your yield farming strategies in a secure and efficient manner.


# What is DeFindex

⏱️ 3 min read

**DeFindex** is a decentralized yield protocol designed to make DeFi simple. It enables **wallet providers** to launch **yield-generating accounts** for their users—fast, secure, and without needing deep DeFi expertise. By connecting to a broad range of decentralized strategies, DeFindex makes it effortless to earn **passive income on crypto assets**.

### ⚙️ How DeFindex Works

* **Tokenized Vaults** → Users interact through DeFindex Vault contracts that gives them access to multiple DeFi opportunities.
* **Diverse Strategies** → Vaults can support one or more assets, and each asset can be allocated across multiple strategies—lending, liquidity pools, tokenized bonds, and more. Anyone can design strategies that plug into the DeFindex ecosystem.
* **Secure Management** → Vault managers can rebalance funds across strategies in a safe and descentralied way, always chasing the best available yield.
* **Optimized Returns** → Earnings are auto-compounded to maximize long-term returns, while **vault managers set fees** to monetize on top of user earnings.

#### Vault Creation and Integration

* Create custom vaults through our [user-friendly frontend](https://app.defindex.io)
* Seamlessly integrate vaults into your applications using our SDKs
* Connect vaults to multiple DeFi strategies for optimal yield generation

#### User Operations

* **Deposits**: Funds are automatically deployed to connected strategies (configurable by vault manager)
* **Withdrawals**: Seamless fund retrieval from strategies and vault reserves
* **Emergency Protocol**: Built-in functionality to rescue funds from strategies if needed

#### Revenue Model

* Vault managers decide how much to charge
* Fees are calculated based on the yield generated by underlying strategies
* For example, if fees are 50%, and a strategy generates 10 USDC of profit, 5 USDC will go to the user, and 5 USDC will be shared between the distributor partner (Vault Manager) and DeFindex.

### Key Benefits

* Immediate yield generation upon deposit
* Flexible strategy management
* Professional-grade security features
* Customizable fee
* Emergency fund protection mechanisms

This architecture allows users to maximize their DeFi yields while maintaining control over their investment strategy and risk exposure.

### 🧩 Solutions for Wallet Builders

* **Dedicated User Vaults** → Launch a vault for your users, decide which assets and strategies to support, and manage fees.
* **Plug-and-Play Yield Accounts** → Offer savings and investment products directly inside your wallet.
* **Composable Portfolios** → Mix assets and strategies to tailor experiences and manage risk.
* **Developer-Friendly Tools** → APIs and SDKs make it easy to integrate yield with just a few lines of code.
* **Aligned Monetization** → Wallets earn a share of the yield whenever their users earn.

### 🛠️ Solutions for DeFi Developers

* **Open Strategy Framework** → Build, test, and deploy strategies directly into the DeFindex ecosystem.
* **Plug-and-Play Exposure** → Once listed, your strategy can be adopted by multiple vaults and wallets instantly—scaling your impact and usage.
* **Composable Ecosystem** → Strategies are modular, allowing them to interact with other DeFi protocols and expand the range of opportunities available to users.

#### Strategy Management

* Dynamically allocate funds across different strategies
* Optimize returns by moving capital between strategies based on performance and risk metrics
* Maintain full control over strategy selection and fund allocation

### 🔒 Security and Transparency

* **Built-in Safeguards** → Features like **Rebalance** and **Rescue** functions protect user funds against volatility and strategy risks.


# Vault Roles

⏱️ 2 min read

**Roles** are unique identifiers that assign specific responsibilities within the vault and are the only entities with privileges to perform critical actions. Each role is associated with an `Address` that represents the entity responsible for that function. None of these roles can withdraw funds from the users.

Since each role is just an `Address`, any role can be assigned to a **smart contract** instead of a regular wallet. This enables policy-based or role-access control patterns — for example, a contract acting as the Manager could define its own internal rules, conditions, or sub-roles to govern who is allowed to trigger actions on behalf of that role.

Also, when deploying a vault, the deploying address can be any address — it doesn't need to be tied to the Manager or any other role. In other words, a vault can be set up on behalf of someone else.

The roles are:

* **Vault Manager** (`Manager`)
  * Primary owner of the vault
  * Controls vault settings, role assignments, and contract upgrades
  * Is included in the authorization check for every role-restricted function — can perform any action that Emergency Manager, Rebalance Manager, or Fee Receiver can perform, without needing to hold those roles
  * The only role that can manually lock fees (`lock_fees`) or release fees (`release_fees`). Note that fee locking also happens automatically on every deposit and withdraw — `lock_fees` is for triggering it manually
  * The only role that can upgrade the contract code (only if the vault was deployed as upgradable)
  * The only role that can change the vault's performance fee (the new rate is supplied as the optional `new_fee_bps` argument when calling `lock_fees`)
  * Can update any role address, including its own
  * *Recommendation*: Use a multisig wallet or a policy-based smart contract.
* **Rebalance Manager** (`RebalanceManager`)
  * Executes rebalancing instructions that move funds across strategies
  * *Recommendation*: Use a multisig wallet or a policy-based smart contract.
* **Fee Receiver** (`VaultFeeReceiver`)
  * Receives fees collected by the vault
  * Triggers distribution of already-locked fees to vault and protocol receivers by calling `distribute_fees`
  * Can also update the fee receiver address (shared with Manager)
  * Cannot lock or release fees, nor change the performance fee — those are Manager-only
  * *Recommendation*: Use a dedicated wallet, and make sure it has trustlines set up for the vault's underlying assets so fee distributions don't fail
* **Emergency Manager** (`EmergencyManager`)
  * Can unwind all funds from a specific Strategy and store them as idle funds in the Vault,\
    automatically pausing that Strategy (`rescue`).
  * Can pause a specific strategy, blocking deposits to it
  * Can unpause a specific strategy
  * Cannot access the vault balance or withdraw user funds directly
  * *Recommendation*: Implement as an automated bot or delegate it

## Role Permissions

| Action                                                      | Manager | Emergency Manager | Rebalance Manager | Fee Receiver |
| ----------------------------------------------------------- | :-----: | :---------------: | :---------------: | :----------: |
| Rescue assets from strategy                                 |    ✅    |         ✅         |         —         |       —      |
| Pause strategy                                              |    ✅    |         ✅         |         —         |       —      |
| Unpause strategy                                            |    ✅    |         ✅         |         —         |       —      |
| Rebalance across strategies                                 |    ✅    |         —         |         ✅         |       —      |
| Receive fees                                                |    —    |         —         |         —         |       ✅      |
| Distribute fees                                             |    ✅    |         —         |         —         |       ✅      |
| Manually lock / release fees (`lock_fees` / `release_fees`) |    ✅    |         —         |         —         |       —      |
| Change performance fee (via `lock_fees`)                    |    ✅    |         —         |         —         |       —      |
| Upgrade contract code (if the vault is upgradable)          |    ✅    |         —         |         —         |       —      |
| Change Manager                                              |    ✅    |         —         |         —         |       —      |
| Change Emergency Manager                                    |    ✅    |         —         |         —         |       —      |
| Change Rebalance Manager                                    |    ✅    |         —         |         —         |       —      |
| Change Fee Receiver                                         |    ✅    |         —         |         —         |       ✅      |

## Protocol Fee Receiver

In addition to the vault roles above, each vault also stores a **DeFindex Protocol Fee Receiver** (`DeFindexProtocolFeeReceiver`). This is not a vault role — it cannot call any function on the vault. It is a passive recipient address set at vault initialization that automatically receives a portion of the fees whenever `distribute_fees` is called. The split between the Protocol Fee Receiver and the Vault Fee Receiver is determined by the `DeFindexProtocolFeeRate` (in basis points), also set at initialization and fixed thereafter.

## Role Assignment and Updates

Roles are set at deployment and can be updated afterward by calling the corresponding setter function. Only the Manager can change most roles — the Fee Receiver address is the only one that either the Manager or the current Fee Receiver can update.

| Role              | Who can change it       |
| ----------------- | ----------------------- |
| Manager           | Manager                 |
| Emergency Manager | Manager                 |
| Rebalance Manager | Manager                 |
| Fee Receiver      | Manager or Fee Receiver |


# Understanding APY

⏱️ 4 min read

## What is APY in DeFindex?

**APY (Annual Percentage Yield)** represents the estimated annual return on your investment, expressed as a percentage. In DeFindex, APY reflects how much your deposited assets could grow over a year if current performance continues.

### APY vs APR: The Key Difference

* **APR (Annual Percentage Rate)**: Simple interest without compounding. If you earn 10% APR, you get exactly 10% on your initial deposit after one year.
* **APY (Annual Percentage Yield)**: Includes compound interest. Your earnings are reinvested, so you earn returns on your returns.

DeFindex uses APY because it better reflects actual returns when strategies auto-compound rewards. When a vault harvests and reinvests gains, your shares become worth more over time. APY captures this growth more accurately.

### Strategy APY vs Vault APY

* **Strategy APY**: The raw yield generated by an individual strategy (e.g., lending on Blend Capital).
* **Vault APY**: The net yield users receive after vault fees are deducted. This is what depositors actually earn.

A vault with a 10% strategy APY and 20% performance fee would have approximately 8% vault APY. This is because the performance fee is applied to the yield, not the principal:

**Example:**

* You deposit $100
* The strategy generates $20 in yield (20% APY)
* The manager takes 50% of those gains: $20 × 0.50 = $10
* You receive $10 in net yield
* Your effective return: $10 / $100 = **10% Vault APY**

The formula is: `Net Vault APY = Strategy APY × (1 - Performance Fee)`

### Price Per Share (PPS)

Instead of tracking individual profits, DeFindex uses **Price Per Share (PPS)** to measure vault performance. When you deposit, you receive shares. As the vault earns yield, each share becomes worth more.

APY is calculated by comparing the PPS now versus the PPS in the past. For technical details and formulas, see [Strategies APY](/advanced-documentation/developer-introduction/strategies-apy).

***

## Why Does APY Vary Between Vaults?

Even vaults using similar strategies can show different APYs. Here's why:

### 1. Vault Fees

The **Vault Manager** sets performance fees that reduce net APY. Lower fees mean more returns for depositors.

| Strategy APY | Performance Fee | Net Vault APY |
| ------------ | --------------- | ------------- |
| 15%          | 50%             | \~7.5%        |
| 15%          | 30%             | \~10.5%       |
| 15%          | 15%             | \~12.75%      |

See [Vault Roles](/getting-started/getting-started/vault-roles) for more on how managers configure fees.

### 2. Rebalancer Decisions

The **Rebalance Manager** allocates funds across strategies. Smart rebalancing can optimize returns:

* Moving funds to higher-yielding strategies
* Responding to market conditions
* Balancing risk and reward

Poor rebalancing decisions can reduce overall vault performance.

### 3. Entry Timing

APY reflects **historical performance**, not future guarantees. DeFindex calculates APY based on Price Per Share (PPS) changes over a recent time window (typically the last 7 days).

This means two vaults with identical configurations created at different times will show different APYs.

***

## How to Interpret APY

### What APY Tells You

* Recent vault performance based on PPS growth
* Net returns after fees
* Compounding effect included

### What APY Doesn't Tell You

* Future guaranteed returns
* Risk level of underlying strategies
* Liquidity conditions

***

## Common Misconceptions

### "APY is a guarantee"

APY is an estimate based on past performance. Market conditions, strategy yields, and other factors can change at any time.

### "APY stays constant"

APY changes continuously as market conditions and strategy performance evolve. What you see today may be different tomorrow.

### "I can directly compare APYs across platforms"

Different platforms calculate APY differently. Some include fees, some don't. Some use 7-day averages, others use 30-day. Compare within DeFindex for accuracy.

***

## Quick Reference

| Term             | Definition                                                                 |
| ---------------- | -------------------------------------------------------------------------- |
| **APY**          | Annual Percentage Yield: the estimated yearly return including compounding |
| **APR**          | Annual Percentage Rate: simple interest without compounding                |
| **Strategy APY** | Raw yield from a strategy before vault fees                                |
| **Vault APY**    | Net yield after fees; what depositors actually receive                     |
| **PPS**          | Price Per Share: measures how much one vault share is worth                |
| **Harvest**      | Process of claiming and reinvesting strategy rewards                       |

***

## Learn More

* [Strategies APY](/advanced-documentation/developer-introduction/strategies-apy) — Technical formulas and PPS calculations
* [Get APY](/api-integration-guide/smart-contracts/get-apy) — How to fetch APY programmatically
* [Vault Roles](/getting-started/getting-started/vault-roles) — Understanding manager, rebalancer, and fee receiver roles


# Partner Fees

⏱️ 4 min read

## The DeFindex Ecosystem

DeFindex operates with three key participants working together:

* **DeFindex**: The protocol that provides the infrastructure, smart contracts, and yield-generating strategies.
* **Partners**: Assets managers, Wallets, fintechs, or applications that integrate DeFindex to offer yield products to their users. Partners configure fees for their specific integration.
* **End Users**: People who deposit funds through a partner's application and earn yield on their assets.

This model allows partners to monetize their user base while offering competitive yield products, and users benefit from easy access to DeFi opportunities through trusted applications.

***

## Performance-Based Fee Model

Partner fees in DeFindex follow a simple principle: **fees are only charged on the yield generated, never on the deposited capital**.

### How It Works

* If your vault generates yield, a percentage goes to the partner and DeFindex
* If there's no yield, there are no fees
* Your principal investment is never touched by fees

### Fee Limits

* **Maximum fee**: 90% of generated yield
* **Typical range**: 50%-30% of generated yield

This performance-based model ensures that partners only earn when users earn. There's no incentive to charge fees on idle capital.

***

## Transparency for Users

One of DeFindex's core principles is transparency. When a user sees an APY displayed in their partner's application:

* The APY shown is **already net of all fees**
* Users see exactly what they will receive
* No hidden deductions or surprise charges

### What Users See vs What Happens

| Displayed         | Meaning                                                                            |
| ----------------- | ---------------------------------------------------------------------------------- |
| 15% APY           | User will earn 15% annually on their deposit if the market conditions stays stable |
| Vault performance | Already accounts for partner fees                                                  |
| Balance growth    | Reflects actual returns after all fees                                             |

This approach eliminates confusion. The number users see is the number they get.

***

## Aligned Incentives

The performance-based fee model creates natural alignment between all parties:

### For Partners

* Earn revenue only when users profit
* Incentive to promote well-performing vaults
* No temptation to charge fees on underperforming products

### For Users

* Capital is protected from fees
* Only pay when earning
* Confidence that partners want the same outcome: good returns

### For DeFindex

* Protocol grows when users and partners succeed
* Focus on building better yield strategies
* Sustainable ecosystem development

This alignment means everyone benefits from the same goal: generating real yield for depositors.

***

## Fee Distribution

When yield is generated, fees are distributed completely on-chain:

1. **Yield is generated** by the vault's strategies
2. **Partner fee is calculated** based on their configured percentage
3. **Distribution occurs** when the partner triggers it
4. **Fees are split** between the partner and DeFindex

The split between partner and DeFindex is handled internally by the protocol.

***

<figure><img src="/files/TlEbMpg71vGxvWj34ZL0" alt=""><figcaption></figcaption></figure>

***

## Practical Example

Let's walk through a concrete scenario:

### Setup

* User deposits **$10,000 USDC** through a partner's app
* The vault's strategy generates **15% APY**
* Partner has configured a **50% performance fee**

### After One Year

| Item                                        | Amount |
| ------------------------------------------- | ------ |
| Gross yield generated before fees (15% APY) | $1,500 |
| Partner + Defindex fee (50% of yield)       | $750   |
| Net yield to user                           | $750   |

### Result

* **User receives**: $750 in yield (**7.5% net APY**) — passive income with zero effort
* **User's capital**: $10,000 remains fully protected
* **Partner revenue before Defindex fee**: $750 annually per user — recurring revenue stream

For a partner with 1,000 active users, this represents **$750,000 in annual revenue** while providing real value to their users.

***

## Key Takeaways

| Principle             | What It Means                        |
| --------------------- | ------------------------------------ |
| Performance-based     | Fees only on yield, never on capital |
| Net APY display       | Users see what they actually earn    |
| Aligned incentives    | Partners profit when users profit    |
| On-chain distribution | Transparent, on-chain fee handling   |
| Protected principal   | Deposits are never reduced by fees   |

***

## Learn More

* [Understanding APY](/getting-started/getting-started/understanding-apy) — How APY is calculated and what it means
* [Vault Roles](/getting-started/getting-started/vault-roles) — Understanding the different roles in vault management
* [Get APY](/api-integration-guide/smart-contracts/get-apy) — How to fetch APY programmatically


# General/FAQ

⏱️ 1 min read

DeFindex makes it easy for wallet providers to offer yield-generating accounts to their users through diverse DeFi strategies. It provides seamless integration, security, and transparency, ensuring both developers and users can benefit from passive income and innovative financial tools.

### What is DeFindex?

DeFindex allows wallet providers to integrate automated, secure, and diversified DeFi strategies into their applications, enabling users to earn passive income from cryptocurrencies. It operates through smart contracts, which handle everything from reinvestment to securing funds.

### How can DeFindex benefit wallet builders?

Wallet builders can customize portfolios using a variety of DeFi strategies, enhancing user engagement with yield-generating accounts. DeFindex provides easy integration tools that allow users to start earning with just one click.

### Is DeFindex secure and decentralized?

Yes, DeFindex operates through secure and transparent smart contracts, ensuring that all transactions are decentralized and under the full control of the user, providing peace of mind to partners and users alike.

### How DeFindex works?

<figure><img src="/files/6OzzYLkKAFDjaDpTq67g" alt=""><figcaption></figcaption></figure>


# Contract Deployments

⏱️ 1 min read


# Mainnet Deployment

⏱️ 2 min read

This page contains the current mainnet contract addresses for the DeFindex protocol.

For new and updated deployments, check\
<https://github.com/defindex-io/stellar-contracts/blob/main/public/mainnet.contracts.json>\\

## Core Contracts

### Factory Contract

* **Contract ID**: `CDKFHFJIET3A73A2YN4KV7NSV32S6YGQMUFH3DNJXLBWL4SKEGVRNFKI`
* **Hash**: `b0fe36b2b294d0af86846ccc4036279418907b60f6f74dae752847ae9d3bca0e`

### Vault Contract

* **Hash**: `ae3409a4090bc087b86b4e9b444d2b8017ccd97b90b069d44d005ab9f8e1468b`

## Strategy Contracts

### Fixed Pool Strategies (with Autocompound)

#### USDC Strategy

* **Contract ID**: `CDB2WMKQQNVZMEBY7Q7GZ5C7E7IAFSNMZ7GGVD6WKTCEWK7XOIAVZSAP`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

#### EURC Strategy

* **Contract ID**: `CC5CE6MWISDXT3MLNQ7R3FVILFVFEIH3COWGH45GJKL6BD2ZHF7F7JVI`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

#### XLM Strategy

* **Contract ID**: `CDPWNUW7UMCSVO36VAJSQHQECISPJLCVPDASKHRC5SEROAAZDUQ5DG2Z`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

### Etherfuse Pool v2 Strategies (with Autocompound)

#### USDC Strategy

* **Contract ID**: `CCBTSHPUVNKCT5V675AAVYNANHXBU26PTZK2QLS7ZLFNYRJZT5HW3VL6`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

#### CETES Strategy

* **Contract ID**: `CAZ3LLLKPWEOVK6K4G5NCQ2VXWABLFIPKKNMN5GLKMZKEN7JSKTEMIKN`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

#### USTRY Strategy

* **Contract ID**: `CA3SO5RRKOONAPWVR5XY6CMOYZGN4M4QKVIGX5DFRIIJUJW2SFSELBXL`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

#### TESOURO Strategy

* **Contract ID**: `CDSCVJHJWUZQMR64FVK3XMND5NKSN7Z23KPRCHKFHVGOEJBWPVH5B5XA`
* **Hash**: `11329c2469455f5a3815af1383c0cdddb69215b1668a17ef097516cde85da988`

## Important Notes

* All strategy contracts share the same hash as they are instances of the same contract template
* The factory contract is used to deploy new vaults
* The vault contract hash is used to verify vault deployments
* Always verify contract addresses before interacting with them


# Testnet Deployment

⏱️ 1 min read

For latest testnet deployment check\
<https://github.com/defindex-io/stellar-contracts/blob/main/public/testnet.contracts.json>


# Introduction

⏱️ 2 min read

## 🎬 Video Tutorial

Prefer learning by watching? Check out our integration walkthrough:

[Watch the integration walkthrough on YouTube](https://www.youtube.com/watch?v=gz6GU5kAUXY\&t=145s):

{% embed url="<https://www.youtube.com/embed/gz6GU5kAUXY?si=54lqva3t6lzKjvdH&start=145>" %}

***

## Generate your API Key

Follow these steps to get your API key:

1. **Register** → <https://api.defindex.io/register>
2. **Login** → <https://api.defindex.io/login>
3. **Create API Key** — from your dashboard, generate your `api_key` and `refresh_token`.

For a detailed walkthrough with examples, see the [**Getting Your API Key guide**](/api-integration-guide/guides-and-tutorials/getting-api-key).

For the full API reference, see the [DeFindex API documentation](https://api.defindex.io/docs).

Postman collection json [here](https://github.com/defindex-io/docs/blob/main/api-integration-guide/postman_collection.json)

This guide will walk you through integrating DeFindex into your app using the provided API. We'll use TypeScript for the examples, but the concepts apply to any language.

## 🚀 TypeScript SDK Available!

If you're developing in TypeScript, we highly recommend using our official SDK instead of direct API integration. The SDK provides:

* Type safety and comprehensive TypeScript definitions
* Simplified authentication with API keys
* Built-in error handling and validation
* Complete coverage of all API endpoints
* Working examples and detailed documentation

[**Check out the DeFindex TypeScript SDK documentation**](/advanced-documentation/sdks/02-defindex-sdk) **for the easiest integration experience.**

For non-TypeScript projects or custom integrations, continue with this direct API guide below.

***

Complete reference: [API Reference](https://api.defindex.io/docs)

## Prerequisites

* Basic knowledge of TypeScript or JavaScript
* Node.js environment
* [Stellar SDK](https://www.stellar.org/developers/reference/) installed (`npm install stellar-sdk`)
* DeFindex API key — see [Getting Your API Key](/api-integration-guide/guides-and-tutorials/getting-api-key)

***

## 1. Setting Up the API Client

First, create an `ApiClient` class to handle authentication and API requests.

```typescript
import StellarSdk from 'stellar-sdk';

class ApiClient {
    private readonly apiUrl = "api.defindex.io";
    private readonly apiKey: string;

    constructor(apiKey: string) {
        this.apiKey = apiKey;
    }

    // Helper for POST requests
    async postData(endpoint: string, vaultAddress: string, params: Record<string, any>): Promise<any> {
        const response = await fetch(`https://${this.apiUrl}/vault/${vaultAddress}/${endpoint}`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${this.apiKey}`
            },
            body: JSON.stringify(params)
        });
        return await response.json();
    }

    // Helper for GET requests
    async getData(endpoint: string, vaultAddress: string, params?: Record<string, any>): Promise<any> {
        const url = params
            ? `https://${this.apiUrl}/vault/${vaultAddress}/${endpoint}?${new URLSearchParams(params).toString()}`
            : `https://${this.apiUrl}/vault/${vaultAddress}/${endpoint}`;

        const response = await fetch(url, {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${this.apiKey}`
            }
        });
        return await response.json();
    }
}
```

Go to[ interact with vault](/api-integration-guide/smart-contracts), see the implementations of the functions:

* Deposit
* Withdraw
* Balance
* APY

***

## Request Parameters Reference

### Deposit Request

```javascript
{
    amounts: [10000000],     // Array of amounts for each vault asset (7 decimals for XLM)
    caller: userAddress,     // User's wallet address
}
```

### Withdraw Request

```javascript
{
    amounts: [5000000],      // Array of amounts to withdraw from each asset
    caller: userAddress,     // User's wallet address
}
```

### Send Request

```javascript
{
    xdr: signedXdr          // Signed transaction XDR
}
```


# Interact with your Vault

⏱️ 4 min read

To integrate DeFindex into your wallet, you can choose between two approaches:

1. **SDKs**: Utilize the SDKs provided by DeFindex, which facilitate interaction with the protocol and are faster to implement.
2. **Smart Contracts**: Interact directly with DeFindex's smart contracts, giving you greater control over transactions but requiring a deeper understanding of the protocol's structure.

DeFindex is a protocol that allows users to interact with various investment strategies and liquidity pools. To integrate it into your wallet, you need to understand how transactions and the smart contracts that make up the protocol are structured.

You can review the contract addresses in the [`~/public/`](https://github.com/defindex-io/stellar-contracts/tree/main/public) folder, where you'll find information about the contract addresses, or deploy your own custom vault and strategies using our Factory contract.

The first thing you need to do is to deploy a vault instance.

***

***

### Interacting with the Vault

Within vault interactions, there are several methods you can use to manage and query the vault's state. Here are the most relevant ones:

**User-facing relevant methods:**

* **Deposit**: Allows users to deposit assets into the vault.
* **Withdraw**: Allows users to withdraw assets from the vault.
* **Balance**: Allows users to query their vault balance.

**Management methods:**

* **rebalance**: Allows adjusting asset allocation within the vault.
* **rescue**: Allows recovering assets in critical situations.
* **set\_fees**: Allows managing the fees associated with the vault.
* **pause / unpause**: Allows pausing or resuming vault operations.

**Methods available only to the `Manager` role:**

* **set\_fee\_receiver**: Allows changing the fee receiver of the vault.
* **set\_manager**: Allows changing the manager of the vault.
* **set\_emergency\_manager**: Allows changing the emergency manager of the vault.
* **set\_rebalance\_manager**: Allows changing the rebalance manager of the vault.
* **upgrade**: Allows changing the WASM code without requiring users signatures.
* **lock\_fees**: Allows locking the fees in the vault, preventing them from being withdrawn until the lock is released.
* **release\_fees**: Allows releasing the locked fees in the vault, making them available for withdrawal.

You can find the complete list of methods and their parameters in the [Vault contract](https://github.com/defindex-io/docs/blob/main/contracts/vault/src/interface.rs)

### Creating Transactions to Interact with the Vault

}

## Using the Example Script (`vault_usage_example.ts`)

You can interact with your DeFindex Vault directly from the command line using the provided example script: `Contracts/src/vault_usage_example.ts`. This script demonstrates how to perform key vault operations such as deposit, withdraw, invest, unwind, and harvest.

### Prerequisites

* Node.js and yarn installed
* All dependencies installed (`yarn install` in the project root)
* Properly configured environment (setup a `.env` file, see `Contracts/src/utils/env_config.js` for user/secret setup)
* The vault and strategy contracts deployed and addresses set in your address book

### How to Use

1. **Navigate to the Contracts directory:**

   ```bash
   cd Contracts
   ```
2. **Edit the script if needed:** Uncomment the function call(s) you want to run at the bottom of `src/vault_usage_example.ts` (e.g., `await deposit();`).
3. **Run the script:**

   ```bash
   yarn vault-example <network>
   ```

Replace `<network>` with your target network (e.g., `testnet`, `mainnet`, or your custom config).

### Available Operations

* **Deposit:**

  Deposits assets into the vault for the configured user.

  ```typescript
  await deposit();
  ```
* **Withdraw:** Withdraws assets from the vault for the configured user.

  ```typescript
  await withdraw();
  ```
* **Invest:**

  Allocates vault funds into a strategy (admin only).

  ```typescript
  await invest();
  ```
* **Unwind:**

  Withdraws funds from a strategy back to the vault (admin only).

  ```typescript
  await unwind();
  ```
* **Harvest:** Triggers a strategy harvest (keeper only).

  ```typescript
  await harvest();
  ```

**Note:** Only uncomment and run one operation at a time to avoid transaction conflicts. Make sure your environment variables and address book are set up for the network you are targeting.

For more details, review the comments and code in `vault_usage_example.ts`.

***

***

If you need a solution out of the box, you can use the DeFindex SDKs, which provide a set of functions to interact with the vault and strategies without having to manually create transactions. The SDKs handle the underlying complexities and allow you to focus on building your wallet's user interface and experience.


# Deposit

⏱️ 2 min read

This section explains how to deposit funds into a DeFindex vault. You can choose from two different approaches depending on your use case and technical requirements.

## Overview

Deposits allow you to add assets to a vault, with the option to automatically invest them into the vault's strategies or keep them as idle funds. All deposit operations require specifying amounts, user addresses, and slippage parameters.

## Method 1: Using the API

**Best for:** Applications that don't need direct smart contract interaction and want language/framework flexibility.

The API approach abstracts away smart contract complexity and handles transaction building for you.

### Implementation

```typescript
const vaultAddress = 'CAQ6PAG4X6L7LJVGOKSQ6RU2LADWK4EQXRJGMUWL7SECS7LXUEQLM5U7';

async function deposit(
  amount: number,
  user: string,
  apiClient: ApiClient,
  signerFunction: (tx: string) => string
) {
  // Step 1: Request an unsigned transaction from the API
  const { xdr: unsignedTx } = await apiClient.postData("deposit", vaultAddress, {
    amounts: [amount],
    from: user
  });

  // Step 2: Sign the transaction (implement your own signer)
  const signedTx = signerFunction(unsignedTx);

  // Step 3: Send the signed transaction back to the API
  const response = await apiClient.postData("send", vaultAddress, {
    xdr: signedTx
  });

  return response;
}
```

### API Request Parameters

```javascript
{
  amounts: [10000000],     // Array of amounts for each vault asset (7 decimals for XLM)
  caller: userAddress,     // User's wallet address
  invest: true,           // Auto-invest into strategies (recommended: true)
  slippageBps: 50        // 0.5% slippage tolerance (optional, default: 0)
}
```

## Method 2: Direct Smart Contract Interaction

**Best for:** dApps that need direct blockchain interaction without backend dependencies, or applications requiring maximum control over contract calls.

### Rust Contract Function

```rust
fn deposit(
    e: Env,
    amounts_desired: Vec<i128>,
    amounts_min: Vec<i128>,
    from: Address,
    invest: bool,
) -> Result<(Vec<i128>, i128, Option<Vec<Option<AssetInvestmentAllocation>>>), ContractError>
```

### Parameters

* **`amounts_desired`**: Vector specifying the desired quantities of each asset you wish to deposit
* **`amounts_min`**: Vector specifying the minimum quantities of each asset to be transferred (slippage protection)
* **`from`**: Soroban address of the user making the deposit
* **`invest`**: Boolean indicating whether deposited funds should be automatically invested in vault strategies (`true`) or remain as idle funds (`false`)

### Implementation Example

```rust
let deposit_args = vec![
            e,
            &amounts_desired,
            &amounts_min,
            &user_address,
            &auto_invest,
        ]
let result = e.try_invoke_contract::(
            &vault_address,
            &Symbol::new(&e, "deposit"),
            deposit_args.into_val(e),
    ).unwrap_or_else(|_| {
        panic_with_error!(e, SomeError::SomeError);
    }).unwrap();
```

## Return Values

All deposit methods return information about the completed transaction:

* **Deposited amounts**: The actual amounts deposited for each asset
* **Vault shares minted**: Number of vault shares issued to the depositor
* **Investment allocations** (if `invest = true`): Details of how funds were allocated across strategies


# Withdraw

⏱️ 2 min read — This section covers the withdraw functions on a Vault. You can do this in 3 ways: through Smart Contract, Through API or through SDK.

### Method 1: Smart Contract call

#### Withdraw

To withdraw assets from the vault, use the `withdraw` method. Here are the steps to create the transaction:

1. **Prepare parameters**:
   * `withdraw_shares`: The amount of vault shares you wish to withdraw.
   * `min_amounts_out`: A vector specifying the minimum amounts required to receive before the transaction fails (tolerance). This amount is represented in underlying assets.
   * `from`: The address of the user performing the withdrawal, who will receive the funds. Represents a Soroban address.
2. **Example arguments transaction**:

   ```json
   {
     "method": "withdraw",
     "params": {
       "withdraw_shares": 500,
       "min_amounts_out": [450],
       "from": "GCINP..."
     }
   }
   ```

In code it should look like something like this

```rust
let withdraw_args = vec![
            e,
            &withdraw_shares,
            &min_amounts_out,
            &from
        ]
let result = e.try_invoke_contract::(
            &vault_address,
            &Symbol::new(&e, "withdraw"),
            withdraw_args.into_val(e),
    ).unwrap_or_else(|_| {
        panic_with_error!(e, SomeError::SomeError);
    }).unwrap();
```

If you want to withdraw specifying the underlying asset, you need to do a "simple rule of three".

So first you call `total_supply` to get the total amount of shares of the vault, then you need to call `fetch_total_managed_funds` to get the `total_amount` of the asset

*(Note that DeFindex support multiple assets, so if you are using a vault with only one asset, you should take the first element and get the `total_amount`)*

Then, the needed shares to withdraw will be `shares_to_withdraw=total_supply*amount_to_withdraw/total_amount`

***

### Method 2: Withdraw using API

Withdraws funds from the DeFindex vault.

```typescript
const vault = 'CAQ6PAG4X6L7LJVGOKSQ6RU2LADWK4EQXRJGMUWL7SECS7LXUEQLM5U7';

async function withdraw(amount: number, user: string, apiClient: ApiClient, signerFunction: (tx: string) => string) {
    const { xdr: unsignedTx } = await apiClient.postData("withdraw", vault, {
        amounts: [amount],
        from: user
    });

    // This should be done by implementer
    const signedTx = signerFunction(unsignedTx);

    const response = await apiClient.postData("send", vault, {
        xdr: signedTx
    });

    return response;
}
```

### Method 3: Using SDK

#### Withdraw from Vault

Remove funds by specifying amounts:

```typescript
const withdrawData: WithdrawFromVaultParams = {
  amounts: [500000], // Specific amounts to withdraw
  caller: userAddress,
  slippageBps: 100 // 1% slippage tolerance
};

const response = await sdk.withdrawFromVault(vaultAddress, withdrawData, SupportedNetworks.TESTNET);
// Sign response.xdr with the caller account and submit transaction

```

#### Withdraw by Shares

Remove funds by burning vault shares:

```typescript
const shareData: WithdrawSharesParams = {
  shares: 1000000, // Number of vault shares to burn
  caller: userAddress,
  slippageBps: 100
};

const response = await sdk.withdrawShares(vaultAddress, shareData, SupportedNetworks.TESTNET);
// Sign response.xdr with the caller account and submit transaction
```

####


# Get Balance

⏱️ 1 min read

#### Balance

To query the vault's balance, use the `balance` method. Here are the steps to create the transaction:

1. **Prepare parameters**:
   * `from`: The address of the user who wants to query the balance. Represents a Soroban address.
2. **Example transaction**:

   ```json
   {
     "method": "balance",
     "params": {
       "from": "GCINP..."
     }
   }
   ```

### Balance

Fetches the balance for a user.

```typescript
const vault = 'CAQ6PAG4X6L7LJVGOKSQ6RU2LADWK4EQXRJGMUWL7SECS7LXUEQLM5U7';

async function balance(user: string, apiClient: ApiClient): bigint {
    const {underlyingBalance: balance} = await apiClient.getData("balance", vault, {
        from: user
    });
    return BigInt(balance[0]);
}
```


# Get APY

⏱️ 1 min read

Fetches the current APY for the vault. It considers the fee charged by the vault.

```typescript
const vault = 'CAQ6PAG4X6L7LJVGOKSQ6RU2LADWK4EQXRJGMUWL7SECS7LXUEQLM5U7';

async function apy(apiClient: ApiClient): number {
    const {apy} = await apiClient.getData("apy", vault);
    return apy;
}
```


# Manage your Vault

⏱️ 2 min read

#### Rebalance

To adjust asset allocation within the vault, use the `rebalance` method. Here are the steps to create the transaction:

1. **Prepare parameters**:
   * `caller`: The address of the user performing the rebalance.
2. **Example transaction**:

   ```json
   {
     "method": "rebalance",
     "params": {
       "caller": "GCINP..."
     }
   }
   ```

***

#### Rescue

To recover assets in critical situations, use the `rescue` method. Here are the steps to create the transaction:

1. **Prepare parameters**:
   * `strategy_address`: The address of the strategy from which you want to recover assets. This must be a valid address of a strategy linked to the vault.
   * `caller`: The address of the user performing the rescue operation.
2. **Example transaction**:

   ```json
   {
     "method": "rescue",
     "params": {
       "strategy_address": "GCINP...",
       "caller": "GCINP..."
     }
   }
   ```

***

#### Pause / Unpause

To pause or unpause a strategy, use the `pause_strategy` and `unpause_strategy` methods. Here are the steps to create the transactions:

1. **Prepare parameters**:
   * `strategy_address`: The address of the strategy you want to pause or unpause.
   * `caller`: The address of the user performing the operation.
2. **Example transaction for pausing**:

   ```json
   {
     "method": "pause_strategy",
     "params": {
       "strategy_address": "GCINP...",
       "caller": "GCINP..."
     }
   }
   ```
3. **Example transaction for unpausing**:

   ```json
   {
     "method": "unpause_strategy",
     "params": {
       "strategy_address": "GCINP...",
       "caller": "GCINP..."
     }
   }
   ```

***

#### Upgrade

To update the vault's WASM code, use the `upgrade` method. Here are the steps to create the transaction:

1. **Prepare parameters**:
   * `new_wasm_hash`: The hash of the new WASM code.
   * `caller`: The address of the user performing the upgrade.
2. **Example transaction**:

   ```json
   {
     "method": "upgrade",
     "params": {
       "new_wasm_hash": "HASH...",
       "caller": "GCINP..."
     }
   }
   ```


# Create a Vault

⏱️ 3 min read

DeFindex Vaults let **wallet builders** design yield products tailored to their users.\
With a vault, you choose the **assets**, **strategies**, **allocation**, and **fees**—and you can rebalance positions or rescue funds at any time. From the end-user's perspective, it's just **deposit and withdraw**.

***

## Vault Creation Requirements

Before you deploy a vault, make sure you meet the following requirements. These details are often the source of confusion for new builders.

### Network

DeFindex operates on **Stellar Mainnet** and **Stellar Testnet**. The two networks are completely independent and use different contract addresses and token types.

| Requirement        | Testnet                                                            | Mainnet                                                            |
| ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ |
| Network Passphrase | `Test SDF Network ; September 2015`                                | `Public Global Stellar Network ; September 2015`                   |
| Factory contract   | See [Testnet Deployment](/contract-deployments/testnet-deployment) | See [Mainnet Deployment](/contract-deployments/mainnet-deployment) |
| RPC URL            | `https://soroban-testnet.stellar.org`                              | `https://soroban.stellar.org` or another provider                  |
| Horizon URL        | `https://horizon-testnet.stellar.org`                              | `https://horizon.stellar.org`                                      |

### Tokens

**On Testnet**, DeFindex strategies use a test USDC issued by the Blend Capital testnet deployment — referred to here as **BlendUSDC**. This is **not** Soroswap or regular USDC; it is a separate test token you must obtain from [testnet.blend.capital](https://testnet.blend.capital).

**On Mainnet**, strategies use real USDC (Circle) and other well-known tokens.

#### Token addresses

| Token            | Network           | Contract Address                                           |
| ---------------- | ----------------- | ---------------------------------------------------------- |
| XLM (native SAC) | Testnet & Mainnet | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` |
| BlendUSDC        | **Testnet only**  | `CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU` |
| USDC (Circle)    | **Mainnet only**  | `CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75` |
| CETES            | Testnet           | `CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC` |

For a full list of deployed strategy contract addresses, see the [Contract Deployments](/contract-deployments) section.

### Getting Testnet Tokens

You need testnet tokens to deploy and interact with a vault on testnet:

1. **XLM (for fees)**: Use [Friendbot](https://friendbot.stellar.org/) to fund your testnet account.

   ```
   https://friendbot.stellar.org/?addr=YOUR_STELLAR_ADDRESS
   ```
2. **BlendUSDC** (required for USDC vaults on testnet):
   * Go to [testnet.blend.capital](https://testnet.blend.capital).
   * Connect your Freighter wallet (switch to Testnet in wallet settings first).
   * Click **"Faucet"** or use the asset menu to add a BlendUSDC trustline and receive test tokens.
3. **CETES** (required for CETES vaults on testnet):
   * Contact Etherfuse team to get the official test CETES

> **Why BlendUSDC?** On testnet, DeFindex strategies are deployed against the Blend Capital testnet pools, which use their own test USDC. Real Circle USDC does not support large amounts minting on Stellar Testnet.

### Funding Requirements

A **minimum first deposit of 1001 units** (in the asset's smallest denomination — stroops) of your vault's underlying asset is required immediately after deployment.

* On the **first deposit**, the vault locks **1000 shares** as an inflation-attack defense mechanism.
* **Example**: For a USDC vault (7 decimals), 1001 stroops = **0.0001001 USDC** — practically free.
* **Example**: For an XLM vault (7 decimals), 1001 stroops = **0.0001001 XLM** — also negligible.

This is required to prevent the [ERC-4626 inflation attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks) (the same class of attack applies to Soroban vaults).

> Funds are **not** automatically invested on deposit. After the first deposit you must perform a [**first rebalance**](#step-5-first-rebalance) to allocate funds across strategies.

{% hint style="info" %}
A first deposit of 20 USDC prevents historical APY endpoint from having approximation errors
{% endhint %}

***

### Step 1: Assign Vault Roles, Fees, and Upgradability

Before deployment, you must configure the following **roles** (each tied to an address):

* **Manager** – primary owner, manages settings, upgrades, and other roles (use a secure wallet, ex. multisig, cold or MPC)
* **Emergency Manager** – rescues funds and pauses risky strategies (use a hot wallet with fast access or automate it for faster response)
* **Rebalance Manager** – allocates funds across strategies (use a hot wallet or it can be managed by a third party)
* **Fee Receiver** – collects performance fees (*use a secure, dedicated wallet*)

> All four roles must be assigned. You may use the same address for multiple roles, but this is not recommended for production.

***

**Fees**

* Fees are expressed in **basis points** (1 bp = 0.01%).
* The **Vault Fee** is applied to strategy earnings and assigned to the Fee Receiver.
* Maximum allowed fee: **10,000 bps (100%)** — in practice, keep this well below 100%.

***

**Upgradability**

* Choose whether this vault can be upgraded after deployment.
* If enabled, you can migrate to a new vault version in the future.
* Your users' funds and positions remain unaffected — no action required on their end.

***

### Step 2: Select Assets and Strategies

DeFindex offers **curated and audited strategies**, currently live for:

* **Blend Autocompound – Fixed Pool**: USDC, EURC, XLM
* **Blend Autocompound – Etherfuse Pool**: USDC, XLM, CETES, USTRY, TESOURO

Each vault supports one or more assets, and each asset can be backed by one or more strategies.

> Need support for additional pools? Just ping us on [Discord](https://discord.gg/e2qAhJCBmx).

***

### Step 3: Deploy the Vault

You can deploy your Vault in two ways:

* [**Using GUI (Basic)**](/api-integration-guide/creating-a-defindex-vault/using-gui-basic) **Skip next steps if using the GUI**
* [**Using the Factory Contract or API (Advanced)**](/api-integration-guide/creating-a-defindex-vault/using-the-factory-advanced)

### Step 4: Do a First Deposit (Skip if using the GUI)

A **minimum first deposit of 1001 units** of your supported asset is required.

* **Why?**
  * **1001 units** will be permanently locked in the Vault for security.
* **Example:**\
  If you are depositing **USDC**, this equals just **0.0001001 USDC** — practically nothing!

This is because these vaults are protected from something called "inflation attacks" you can read more about this kind of attacks on [OpenZeppelin blog](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks)

### Step 5: First Rebalance (Skip if using the GUI)

After deployment, perform the **first rebalance** to define allocations across chosen strategies. This may be a bit confusing right? but How the vault is going to know how to distribute the funds across the different strategies? This step only need to be done once.

You have 3 ways to make the first rebalance: using API, using a script ([discussed here](https://docs.defindex.io/api-integration-guide/pages/7jlfzLvmVANYFrleC8x5#using-the-example-script-vault_usage_example.ts)) or using [stellar-cli](https://developers.stellar.org/docs/build/guides/cli)

#### Using API

Make sure you have an `API_KEY` to call the API. And then, call the rebalance function

```
curl --location 'https://api.defindex.io//vault/${VAULT_ADDRESS}/rebalance?network=mainnet' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ${JWT_TOKEN}' \
--data '{
    "caller": "${MANAGER_OR_REBALANCE_MANAGER}$",
    "instructions": [
        {
            "type": "Invest",
            "strategy_address": "${STRATEGY_ADDRESS}$",
            "amount": 1000000
        }
    ]
}'

```

Where `VAULT_ADDRESS` is the address of the recently deployed vault, `JWT_TOKEN` is the API key, the `MANAGER_OR_REBALANCE_MANAGER` is what you defined when creating the vault. The strategy address is the one you want to invest on. For a list of all the addresses you can check -> [here](https://github.com/defindex-io/docs/blob/main/public/mainnet.contracts.json).

This will return you an unsigned XDR, that can be signed using your preferred method of signing. One simple method could be using [Stellar Laboratory](https://lab.stellar.org/transaction/sign). Simply copy and paste the unsigned XDR and sign it.

#### Using stellar-cli

First, you need to setup your keys, make sure the rebalancer manager role defined previously is the one you are going to setup. For example, you can set it up using secret key by:

```bash
stellar keys add --secret-key rebalancer
```

then, you will be prompted to write your secret key.

Next, let's make the rebalance using testnet as example. You can do that by:

```bash
stellar contract invoke \
  --rpc-url https://soroban-testnet.stellar.org/ \
  --network-passphrase 'Test SDF Network ; September 2015' \
  --id <CONTRACT_ID> \
  --source-account rebalancer \
  -- \
  rebalance \
  --caller <REBALANCER_ADDRESS> \
  --instructions '[{"Invest":["<STRATEGY_ADDRESS>", "<AMOUNT_IN_STROOPS>"]}]'
```

you can find the strategy addresses on [`~/public/<network>.contracts.json`](https://github.com/defindex-io/docs/blob/main/public/mainnet.contracts.json).

And that's all!


# Using GUI (Basic)

⏱️ 2 min read

If you want to create a vault using the strategies available in DeFindex without writing any code, you can use the DeFindex user interface. Here's how:

1. **Visit the DeFindex page**: Go to [app.defindex.io](https://app.defindex.io) and navigate to the vault creation section in the DeFindex user interface.

<figure><img src="/files/NxqIZOSt83YWXUQx1afk" alt=""><figcaption></figcaption></figure>

2. **Connect your wallet**: Make sure your wallet is connected to the correct network and that you have the necessary funds to pay for transaction fees.

<figure><img src="/files/RPJsHmpPc4e1PnP3N9en" alt=""><figcaption></figcaption></figure>

3. **Complete the vault creation form**: Provide the required information, such as the vault's name, symbol, select the asset you wish to use, and use the switch to choose if you want your vault to be **upgradable**.

<figure><img src="/files/fZ5bfIoi7cDaWQW48npW" alt=""><figcaption></figcaption></figure>

1. **Select strategies and first deposit amount**: Choose the strategies you want to include in your vault. You can select multiple strategies based on your preferences. 20 USDC for first deposit will help to avoid rounding errors when calculating APY

> \[!NOTE]\
> The available strategies you can select will depend on the asset you have chosen for your vault. Only compatible strategies for the selected asset will be displayed.

<figure><img src="/files/GLuCw5FMvr5JJC7PlabE" alt=""><figcaption></figcaption></figure>

1. **Configure the remaining fields**: Finish completing the form with the required information, such as manager addresses, fees, and other relevant parameters.

<figure><img src="/files/BlWqI5VI3cczKIZhvU2H" alt=""><figcaption></figcaption></figure>

1. **Review and confirm**: Before submitting the transaction, review all the details to ensure everything is correct.

<figure><img src="/files/TGX97qespDUS1VS5m8Xz" alt=""><figcaption></figcaption></figure>

1. **Submit the transaction**: Once you are sure all the information is correct, sign and submit the transaction to create your vault with your wallet.
2. **Wait for confirmation**: After submitting the transaction, wait for it to be confirmed on the blockchain. Once confirmed, your vault will be active, and you can start interacting with it.

<figure><img src="/files/h8JI89rVfDvYRD3rRWk0" alt=""><figcaption></figcaption></figure>


# Using the Factory (Advanced)

⏱️ 5 min read

This guide documents every argument involved in deploying a DeFindex vault — whether you call the factory smart contract directly or use the DeFindex REST API. Read the [Vault Creation Requirements](/api-integration-guide/creating-a-defindex-vault#vault-creation-requirements) section first before proceeding.

***

## Quick Reference: Contract Addresses

### Testnet

Testnet addresses may be not valid after the June 17 or December 16, 2026, testnet resets. If that's the case let us know via Discord so we can update the docs.

| Contract             | Address                                                    |
| -------------------- | ---------------------------------------------------------- |
| Factory              | `CDSCWE4GLNBYYTES2OCYDFQA2LLY4RBIAX6ZI32VSUXD7GO6HRPO4A32` |
| Soroswap Router      | `CCJUD55AG6W5HAI5LRVNKAE5WDP5XGZBUDS5WNTIVDU7O264UZZE7BRD` |
| XLM (native SAC)     | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` |
| BlendUSDC            | `CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU` |
| CETES token          | `CC72F57YTPX76HAA64JQOEGHQAPSADQWSY5DWVBR66JINPFDLNCQYHIC` |
| USDC Blend Strategy  | `CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY` |
| XLM Blend Strategy   | `CDVLOSPJPQOTB6ZCWO5VSGTOLGMKTXSFWYTUP572GTPNOWX4F76X3HPM` |
| CETES Blend Strategy | `CCP4RBDWPRNO2LWO23XFU4BBLGA73J5N3BK7EHRJUHVN33YEMMFB2MBE` |

### Mainnet

| Contract                 | Address                                                    |
| ------------------------ | ---------------------------------------------------------- |
| Factory                  | `CDKFHFJIET3A73A2YN4KV7NSV32S6YGQMUFH3DNJXLBWL4SKEGVRNFKI` |
| Soroswap Router          | `CAG5LRYQ5JVEUI5TEID72EYOVX44TTUJT5BQR2J6J77FH65PCCFAJDDH` |
| XLM (native SAC)         | `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` |
| USDC (Circle)            | `CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75` |
| USDC Fixed Pool Strategy | `CDB2WMKQQNVZMEBY7Q7GZ5C7E7IAFSNMZ7GGVD6WKTCEWK7XOIAVZSAP` |
| EURC Fixed Pool Strategy | `CC5CE6MWISDXT3MLNQ7R3FVILFVFEIH3COWGH45GJKL6BD2ZHF7F7JVI` |
| XLM Fixed Pool Strategy  | `CDPWNUW7UMCSVO36VAJSQHQECISPJLCVPDASKHRC5SEROAAZDUQ5DG2Z` |
| USDC YieldBlox Strategy  | `CCSRX5E4337QMCMC3KO3RDFYI57T5NZV5XB3W3TWE4USCASKGL5URKJL` |
| XLM YieldBlox Strategy   | `CBDOIGFO2QOOZTWQZ7AFPH5JOUS2SBN5CTTXR665NHV6GOCM6OUGI5KP` |
| CETES YieldBlox Strategy | `CBTSRJLN5CVVOWLTH2FY5KNQ47KW5KKU3VWGASDN72STGMXLRRNHPRIL` |

For the complete list of mainnet strategies, see [Mainnet Deployment](/contract-deployments/mainnet-deployment).

***

## Vault Role IDs

When calling the factory contract directly, roles are passed as a `Map<u32, Address>`. The role IDs are:

| ID  | Role              | Description                                          |
| --- | ----------------- | ---------------------------------------------------- |
| `0` | Emergency Manager | Can pause strategies and rescue funds in emergencies |
| `1` | Fee Receiver      | Receives vault performance fees                      |
| `2` | Manager           | Full administrative control over the vault           |
| `3` | Rebalance Manager | Can rebalance asset allocations across strategies    |

All four roles must be assigned. The same address can be used for multiple roles.

***

## Method 1: Direct Factory Contract Call

Use this method when integrating at the smart-contract level (e.g., building your own deployment script or using `stellar-cli`).

### Function: `create_defindex_vault`

```rust
fn create_defindex_vault(
    e: Env,
    roles: Map<u32, Address>,
    vault_fee: u32,
    assets: Vec<AssetStrategySet>,
    soroswap_router: Address,
    name_symbol: Map<String, String>,
    upgradable: bool,
) -> Result<Address, FactoryError>
```

#### Parameter Reference

| Parameter         | Type                    | Description                                                                  |
| ----------------- | ----------------------- | ---------------------------------------------------------------------------- |
| `roles`           | `Map<u32, Address>`     | Maps role IDs (0–3) to Stellar addresses. All four roles are required.       |
| `vault_fee`       | `u32`                   | Vault fee in basis points (1 bps = 0.01%). Max: 10,000 (100%).               |
| `assets`          | `Vec<AssetStrategySet>` | The assets the vault manages and their associated strategies.                |
| `soroswap_router` | `Address`               | Address of the Soroswap router used for internal swaps.                      |
| `name_symbol`     | `Map<String, String>`   | Metadata: must contain keys `"name"` and `"symbol"`.                         |
| `upgradable`      | `bool`                  | If `true`, the Manager can upgrade the vault's WASM without user signatures. |

#### `AssetStrategySet` Structure

```rust
struct AssetStrategySet {
    address: Address,          // The token contract address (e.g., USDC or XLM SAC)
    strategies: Vec<Strategy>, // Strategies that manage this asset
}

struct Strategy {
    address: Address, // Strategy contract address
    name: String,     // Human-readable name (stored on-chain)
    paused: bool,     // Set to false on creation; strategies start active
}
```

#### Example: `stellar-cli` (Testnet, USDC vault)

```bash
stellar contract invoke \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase 'Test SDF Network ; September 2015' \
  --id CDSCWE4GLNBYYTES2OCYDFQA2LLY4RBIAX6ZI32VSUXD7GO6HRPO4A32 \
  --source-account deployer \
  -- \
  create_defindex_vault \
  --roles '{"0":"GCKFBEIY...","1":"GCKFBEIY...","2":"GCKFBEIY...","3":"GCKFBEIY..."}' \
  --vault_fee 100 \
  --assets '[{"address":"CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU","strategies":[{"address":"CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY","name":"BlendUSDC Strategy","paused":false}]}]' \
  --soroswap_router CCJUD55AG6W5HAI5LRVNKAE5WDP5XGZBUDS5WNTIVDU7O264UZZE7BRD \
  --name_symbol '{"name":"My USDC Vault","symbol":"MUSDC"}' \
  --upgradable true
```

> Replace `GCKFBEIY...` with your actual Stellar addresses for each role.

### Function: `create_defindex_vault_deposit`

Creates a vault **and** makes the initial deposit in one transaction.

```rust
fn create_defindex_vault_deposit(
    e: Env,
    caller: Address,
    roles: Map<u32, Address>,
    vault_fee: u32,
    assets: Vec<AssetStrategySet>,
    soroswap_router: Address,
    name_symbol: Map<String, String>,
    upgradable: bool,
    amounts: Vec<i128>,
) -> Result<Address, FactoryError>
```

Additional parameter over `create_defindex_vault`:

| Parameter | Type        | Description                                                                                              |
| --------- | ----------- | -------------------------------------------------------------------------------------------------------- |
| `caller`  | `Address`   | The address that signs the transaction and makes the deposit.                                            |
| `amounts` | `Vec<i128>` | Initial deposit amounts in stroops, one per asset in the same order as `assets`. Minimum 1001 per asset. |

***

## Method 2: API — `POST /factory/create-vault`

Builds an unsigned XDR to deploy a vault. You must sign the XDR and submit it via `POST /send`.

```http
POST https://api.defindex.io/factory/create-vault?network=testnet
Authorization: Bearer <API_KEY>
Content-Type: application/json
```

### Request Body

```json
{
  "roles": {
    "manager": "GACKTN5D...",
    "emergencyManager": "GACKTN5D...",
    "rebalanceManager": "GACKTN5D...",
    "feeReceiver": "GACKTN5D..."
  },
  "vaultFeeBps": 100,
  "assets": [
    {
      "address": "CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU",
      "strategies": [
        {
          "address": "CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY",
          "name": "BlendUSDC Strategy",
          "paused": false
        }
      ]
    }
  ],
  "name": "My USDC Vault",
  "symbol": "MUSDC",
  "upgradable": true,
  "caller": "GACKTN5D..."
}
```

### Field Reference

| Field                           | Type         | Required | Description                                               |
| ------------------------------- | ------------ | -------- | --------------------------------------------------------- |
| `roles.manager`                 | `string`     | Yes      | Stellar address for the Manager role                      |
| `roles.emergencyManager`        | `string`     | Yes      | Stellar address for the Emergency Manager role            |
| `roles.rebalanceManager`        | `string`     | Yes      | Stellar address for the Rebalance Manager role            |
| `roles.feeReceiver`             | `string`     | Yes      | Stellar address for the Fee Receiver role                 |
| `vaultFeeBps`                   | `number`     | Yes      | Vault fee in basis points (0–10000). `100` = 1%           |
| `assets`                        | `Asset[]`    | Yes      | Array of assets the vault will manage                     |
| `assets[].address`              | `string`     | Yes      | Token contract address for this asset                     |
| `assets[].strategies`           | `Strategy[]` | Yes      | Strategies that manage this asset                         |
| `assets[].strategies[].address` | `string`     | Yes      | Strategy contract address                                 |
| `assets[].strategies[].name`    | `string`     | Yes      | Human-readable name stored on-chain                       |
| `assets[].strategies[].paused`  | `boolean`    | Yes      | Whether the strategy starts paused. Use `false`           |
| `name`                          | `string`     | Yes      | Vault display name (stored on-chain)                      |
| `symbol`                        | `string`     | Yes      | Vault token symbol for dfTokens (e.g., `MUSDC`)           |
| `upgradable`                    | `boolean`    | Yes      | Whether the vault contract can be upgraded by the Manager |
| `caller`                        | `string`     | Yes      | Stellar address of the deployer (signs the transaction)   |

### Response

```json
{
  "xdr": "AAAAAgAAAAB...",
  "simulationResponse": { ... },
  "error": null
}
```

Sign the returned `xdr` with the `caller`'s key and submit via `POST /send?network=testnet`.

***

## Method 3: API — `POST /factory/create-vault-deposit`

Creates a vault **and** performs the initial deposit atomically. This is the recommended approach since it handles Steps 3 and 4 in a single transaction.

```http
POST https://api.defindex.io/factory/create-vault-deposit?network=testnet
Authorization: Bearer <API_KEY>
Content-Type: application/json
```

### Request Body

```json
{
  "roles": {
    "manager": "GACKTN5D...",
    "emergencyManager": "GACKTN5D...",
    "rebalanceManager": "GACKTN5D...",
    "feeReceiver": "GACKTN5D..."
  },
  "vaultFeeBps": 100,
  "assets": [
    {
      "address": "CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU",
      "strategies": [
        {
          "address": "CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY",
          "name": "BlendUSDC Strategy",
          "paused": false
        }
      ]
    }
  ],
  "name": "My USDC Vault",
  "symbol": "MUSDC",
  "upgradable": true,
  "caller": "GACKTN5D...",
  "depositAmounts": [10000000]
}
```

### Additional Field

| Field            | Type       | Required | Description                                                                                                    |
| ---------------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `depositAmounts` | `number[]` | Yes      | Initial deposit amounts in **stroops**, one per asset in the same order as `assets`. Must be ≥ 1001 per asset. |

All other fields are identical to `create-vault` above.

> **Decimal reference:** 1 USDC = `10_000_000` stroops (7 decimals). 1 XLM = `10_000_000` stroops. The minimum `1001` stroops equals **0.0001001 units**.

### Response

Same shape as `create-vault`:

```json
{
  "xdr": "AAAAAgAAAAB...",
  "simulationResponse": { ... },
  "error": null
}
```

***

## Method 4: API — `POST /factory/create-vault-auto-invest`

Creates a vault, makes the initial deposit, **and** immediately rebalances (invests) into strategies — all in one batched transaction. At the end it also transfers the Manager role to the final address. This is the most convenient option for fully automated deployments.

```http
POST https://api.defindex.io/factory/create-vault-auto-invest?network=testnet
Authorization: Bearer <API_KEY>
Content-Type: application/json
```

### Request Body

```json
{
  "caller": "GBZXUKUY...",
  "roles": {
    "manager": "GBAJGSZQ...",
    "emergencyManager": "GBAJGSZQ...",
    "rebalanceManager": "GBAJGSZQ...",
    "feeReceiver": "GBAJGSZQ..."
  },
  "name": "Auto-Invest USDC Vault",
  "symbol": "AIUSDC",
  "vaultFee": 100,
  "upgradable": true,
  "assets": [
    {
      "address": "CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU",
      "symbol": "USDC",
      "amount": 10000000,
      "strategies": [
        {
          "address": "CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY",
          "name": "BlendUSDC Strategy",
          "amount": 10000000
        }
      ]
    }
  ]
}
```

### Field Reference

| Field                           | Type         | Required | Description                                                                                                                         |
| ------------------------------- | ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `caller`                        | `string`     | Yes      | Deployer's Stellar address. Signs the transaction. The manager role is transferred from this address to `roles.manager` at the end. |
| `roles.manager`                 | `string`     | Yes      | Final Manager address after deployment                                                                                              |
| `roles.emergencyManager`        | `string`     | Yes      | Emergency Manager address                                                                                                           |
| `roles.rebalanceManager`        | `string`     | Yes      | Rebalance Manager address                                                                                                           |
| `roles.feeReceiver`             | `string`     | Yes      | Fee Receiver address                                                                                                                |
| `name`                          | `string`     | Yes      | Vault display name                                                                                                                  |
| `symbol`                        | `string`     | Yes      | dfToken symbol (e.g., `AIUSDC`)                                                                                                     |
| `vaultFee`                      | `number`     | Yes      | Vault fee in basis points. Note: this field is named `vaultFee` (not `vaultFeeBps`) for this endpoint.                              |
| `upgradable`                    | `boolean`    | Yes      | Whether the vault contract can be upgraded                                                                                          |
| `assets`                        | `Asset[]`    | Yes      | Assets to manage                                                                                                                    |
| `assets[].address`              | `string`     | Yes      | Token contract address                                                                                                              |
| `assets[].symbol`               | `string`     | Yes      | Human-readable token symbol                                                                                                         |
| `assets[].amount`               | `number`     | Yes      | Total deposit amount for this asset in stroops. Must be ≥ 1001 and equal to the sum of all strategy amounts.                        |
| `assets[].strategies`           | `Strategy[]` | Yes      | Strategies for this asset                                                                                                           |
| `assets[].strategies[].address` | `string`     | Yes      | Strategy contract address                                                                                                           |
| `assets[].strategies[].name`    | `string`     | Yes      | Strategy name                                                                                                                       |
| `assets[].strategies[].amount`  | `number`     | Yes      | Amount (in stroops) to invest into this strategy. The sum of all strategy amounts must equal `assets[].amount`.                     |

> **Strategy amounts must sum to the asset amount.** If `assets[0].amount = 10000000`, then all `strategies[].amount` values for that asset must add up to `10000000`.

### Response

```json
{
  "xdr": "AAAAAgAAAAA...",
  "predictedVaultAddress": "CCQ2BCKKDX7HSF5TULLRFRKS4RYIC5ZZGYYTBR3XFDLZ6MMZFRJNXIEA",
  "warning": "The vault address is predicted from simulation. Actual address may differ if network state changes."
}
```

The `predictedVaultAddress` is derived from simulation and is accurate in most cases, but treat it as advisory until the transaction confirms on-chain.

***

## Complete Testnet Example (TypeScript)

This example deploys a BlendUSDC vault on testnet using the `create-vault-deposit` endpoint.

> **Before running**: Make sure you have BlendUSDC in your wallet. Go to [testnet.blend.capital](https://testnet.blend.capital), connect your Freighter wallet (on Testnet), and use the faucet to receive BlendUSDC.

```typescript
const API_KEY = process.env.DEFINDEX_API_KEY!;
const DEPLOYER_ADDRESS = "GACKTN5D..."; // Your Stellar testnet address

const body = {
  roles: {
    manager: DEPLOYER_ADDRESS,
    emergencyManager: DEPLOYER_ADDRESS,
    rebalanceManager: DEPLOYER_ADDRESS,
    feeReceiver: DEPLOYER_ADDRESS,
  },
  vaultFeeBps: 100, // 1%
  assets: [
    {
      // BlendUSDC on testnet
      address: "CAQCFVLOBK5GIULPNZRGATJJMIZL5BSP7X5YJVMGCPTUEPFM4AVSRCJU",
      strategies: [
        {
          address: "CALLOM5I7XLQPPOPQMYAHUWW4N7O3JKT42KQ4ASEEVBXDJQNJOALFSUY",
          name: "Blend USDC Strategy",
          paused: false,
        },
      ],
    },
  ],
  name: "My USDC Vault",
  symbol: "MUSDC",
  upgradable: true,
  caller: DEPLOYER_ADDRESS,
  depositAmounts: [1001], // Minimum first deposit (1001 stroops)
};

// Step 1: Build the transaction
const res = await fetch(
  "https://api.defindex.io/factory/create-vault-deposit?network=testnet",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
    body: JSON.stringify(body),
  }
);
const { xdr } = await res.json();

// Step 2: Sign with your wallet (Freighter / Privy / Crossmint)
const signedXdr = await signTransaction(xdr, { network: "TESTNET" });

// Step 3: Submit
const sendRes = await fetch(
  "https://api.defindex.io/send?network=testnet",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
    body: JSON.stringify({ xdr: signedXdr }),
  }
);
const result = await sendRes.json();
console.log("Vault deployed:", result.txHash);
```

***

## Complete Mainnet Example (TypeScript)

```typescript
const body = {
  roles: {
    manager: "GMANAGER...",
    emergencyManager: "GEMERGENCY...",
    rebalanceManager: "GREBALANCE...",
    feeReceiver: "GFEERECEIVER...",
  },
  vaultFeeBps: 50, // 0.5%
  assets: [
    {
      // Real USDC on mainnet (Circle / Stellar SAC)
      address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
      strategies: [
        {
          address: "CDB2WMKQQNVZMEBY7Q7GZ5C7E7IAFSNMZ7GGVD6WKTCEWK7XOIAVZSAP",
          name: "Blend USDC Fixed Strategy",
          paused: false,
        },
      ],
    },
  ],
  name: "My USDC Vault",
  symbol: "MUSDC",
  upgradable: true,
  caller: "GDEPLOYER...",
  depositAmounts: [1001], // 0.0001001 USDC — the required minimum
};

const res = await fetch(
  "https://api.defindex.io/factory/create-vault-deposit?network=mainnet",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
    body: JSON.stringify(body),
  }
);
const { xdr } = await res.json();
// ... sign and send as above
```

***

## After Deployment: First Rebalance

After the vault is deployed and the first deposit is made, call the `rebalance` function to invest funds into your chosen strategies. Without this step, all deposited funds remain **idle** (uninvested) in the vault.

See the [Rebalance section in the main Create a Vault guide](/api-integration-guide/creating-a-defindex-vault#step-5-first-rebalance) for how to do this via the API or `stellar-cli`.

> If you used `create-vault-auto-invest`, the initial rebalance is already done for you as part of the deployment transaction.

***

## Testnet vs Mainnet Differences at a Glance

| Aspect              | Testnet                                                      | Mainnet                                          |
| ------------------- | ------------------------------------------------------------ | ------------------------------------------------ |
| USDC                | BlendUSDC (`CAQCFV...`) — get from testnet.blend.capital     | Circle USDC (`CCW67T...`)                        |
| CETES               | Testnet CETES (`CC72F5...`) — get from testnet.blend.capital | Real CETES token                                 |
| XLM                 | Testnet XLM — get from Friendbot                             | Real XLM                                         |
| Factory             | `CDSCWE4...`                                                 | `CDKFHFJ...`                                     |
| API `network` param | `testnet`                                                    | `mainnet`                                        |
| Network passphrase  | `Test SDF Network ; September 2015`                          | `Public Global Stellar Network ; September 2015` |
| RPC URL             | `https://soroban-testnet.stellar.org`                        | Provider-dependent                               |

***

## Troubleshooting

**`StrategyDoesNotSupportAsset` (error 102)**\
The strategy address you provided does not support the asset token you specified. Double-check that the strategy address matches the asset and the strategy/asset/network relation is correct. For example, the USDC strategy cannot be used with the XLM token address or, the USDC strategy on testnet cannot be used with the USDC token address from mainnet.

**Transaction fails with simulation error on testnet**\
You may not have a BlendUSDC trustline. Visit [testnet.blend.capital](https://testnet.blend.capital), connect your wallet, and add the BlendUSDC asset before trying again.

**`RolesIncomplete` (error 104)**\
All four roles (0–3) must be provided. If you leave any out, the factory will reject the transaction.

**`FeeTooHigh` (factory error 406)**\
The `vaultFeeBps` value exceeds the maximum allowed. Keep it at or below 10,000 (100%).

**`AmountNotAllowed` (error 110)**\
The initial deposit amount is zero or negative. Provide at least 1001 stroops per asset.

For additional errors, see the [Troubleshooting Guide](/api-integration-guide/troubleshooting).


# Guides and Tutorials

⏱️ 1 min read


# Getting Your API Key

⏱️ 2 min read

Before integrating with the DeFindex API, you need an API key. This guide walks you through the self-service registration and key creation process.

***

## Step 1: Register an Account

Go to the registration page and create your account:

👉 <https://api.defindex.io/register>

Fill in your email and password, then submit the form. Then refresh your browser tab.

***

## Step 2: Log In

Once registered, log in to your account:

👉 <https://api.defindex.io/login>

Enter your credentials and submit. You will be redirected to your dashboard.

***

## Step 3: Create an API Key

From your dashboard, navigate to the **API Keys** section and click **Create API Key**.

You will receive:

* **`api_key`** — the key you include in the `Authorization` header of every request.
* **`refresh_token`** — used to obtain a new `api_key` when the current one expires.

> **Keep these values secure.** Do not commit them to public repositories or expose them in client-side code.

***

## Step 4: Use the API Key in Requests

Include the `api_key` as a Bearer token in the `Authorization` header:

```http
Authorization: Bearer <your_api_key>
```

TypeScript example:

```typescript
const response = await fetch(`https://api.defindex.io/vault/${vaultAddress}/deposit`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.DEFINDEX_API_KEY}`
    },
    body: JSON.stringify(params)
});
```

***

## Refreshing an Expired Key

When your `api_key` expires, use the `refresh_token` to get a new one by calling the `/refresh` endpoint with your `refresh_token`. Check the [API Reference](https://api.defindex.io/docs) for the exact request format.

***

## Next Steps

* [Getting Started with the API](/api-integration-guide/api)
* [Beginner Guide — Vault Deposit Example](/api-integration-guide/guides-and-tutorials/beginner-guide)
* [Full API Reference](https://api.defindex.io/docs)

***

## Need Help?

If you run into issues, join our [Discord](https://discord.gg/ftPKMPm38f) and ask in the developer channel.


# Beginner Guide

⏱️ 5 min read

## Interactive Tutorial

Want to see a working example? Try our hands-on HTML tutorial that demonstrates a complete vault deposit flow: [beginner-example.html](https://github.com/defindex-io/docs/blob/main/api-integration-guide/guides-and-tutorials/beginner-example.html)

***

## 📖 What You'll Learn

This guide teaches you how to integrate DeFindex API into your application using:

* **Freighter Wallet** for secure wallet connection and transaction signing
* **DeFindex API** for creating yield-generating vault deposits and withdrawals
* **Vault concepts** like dfTokens (vault shares) and investment strategies

## 🎯 Prerequisites

Before starting, make sure you have:

* Basic knowledge of **HTML, CSS, and JavaScript**
* **Freighter Wallet** extension installed
* An **API key** from DeFindex — follow the [Getting Your API Key guide](/api-integration-guide/guides-and-tutorials/getting-api-key) to register and create one
* A **web browser** with developer tools
* **5-10 minutes** of focused time

## 🏗️ Project Structure Overview

Our vault deposit application consists of **6 main parts**:

```
1. HTML Structure     → User interface elements with professional styling
2. External Libraries → Freighter API for wallet integration
3. Configuration     → Centralized CONFIG object with vault and API settings
4. Application State  → Tracks wallet connection and vault transaction status
5. Utility Functions  → Helper functions for UI updates and API calls
6. Core Functions    → Connect, Deposit, Sign, and Send (4 separate steps)
```

Since this guide is to use the DeFindex API, we'll focus on the **core functions** that make vault deposits happen.

## 🔧 Core Functions Explained

### Function 1: Connect to Wallet 🔗

```javascript
async function connectStellarWallet() {
    // Step 1: Check if Freighter wallet is installed
    const hasFreighter = await freighter.isConnected();
    console.log('Freighter connected:', hasFreighter);

    // Step 2: If not installed, show error message
    if (!hasFreighter.isConnected) {
        alert('Please install the Freighter wallet extension first.');
        return; // Stop function execution
    }

    // Step 3: Request permission to connect
    console.log('Connecting to Freighter...');
    account = await freighter.requestAccess();
    account = account.address; // Extract just the address

    // Step 4: Update the user interface
    connectButton.disabled = true; // Disable connect button
    document.getElementById('account').innerText = `Connected account: ${account}`;
}
```

**🔍 What this function does:**

1. **Checks** if Freighter wallet is available
2. **Requests** permission to access the wallet
3. **Stores** the wallet address for later use
4. **Updates** the UI to show connection status

**🚨 Common issues:**

* User doesn't have Freighter installed → Show installation instructions
* User denies permission → Ask them to try again

### Function 2: Get Vault Info and Build Deposit Transaction 💰

```javascript
async function getVaultInfoAndDeposit() {
    try {
        // Make sure wallet is connected
        if (!appState.connected) {
            updateStatus('❌ Please connect your wallet first!', 'error');
            return;
        }

        // PHASE 1: Get Vault Information
        updateStatus('🔄 Getting vault information...', 'info');

        appState.vaultInfo = await makeAPIRequest(`/vault/${CONFIG.VAULT_ADDRESS}`, null, 'GET');

        updateStatus(`✅ Vault loaded: ${appState.vaultInfo.name}<br>🔄 Building deposit transaction...`, 'info');

        // PHASE 2: Build Deposit Transaction
        const depositRequest = {
            amounts: [CONFIG.DEPOSIT.AMOUNT],    // Amount to deposit (1 XLM)
            caller: appState.walletAddress,      // Who's depositing
            invest: CONFIG.DEPOSIT.INVEST,       // Auto-invest into strategies
            slippageBps: CONFIG.DEPOSIT.SLIPPAGE // Slippage tolerance
        };

        const buildResult = await makeAPIRequest(`/vault/${CONFIG.VAULT_ADDRESS}/deposit`, depositRequest);
        appState.unsignedXdr = buildResult.xdr;

        // Show the unsigned transaction for educational purposes
        ELEMENTS.unsignedXdr.value = buildResult.xdr;
        ELEMENTS.technicalDetails.classList.remove('hidden');

        updateStatus(`✅ Deposit transaction built!<br>⏳ Ready for signing...`, 'info');
        updateButtonStates();
    } catch (error) {
        console.error('Vault deposit process failed:', error);
        updateStatus(`❌ Process failed: ${error.message}`, 'error');
    }
}
```

**🔍 What this function does:**

1. **Validates** wallet connection first
2. **Fetches** vault information (name, assets, strategies)
3. **Builds** a deposit transaction for the specified amount
4. **Prepares** the transaction for signing (stores in app state)
5. **Updates** the UI to enable the next step

**🚨 Common issues:**

* Wallet not connected → Connect wallet first
* API key expired → Refresh it using your `refresh_token` (see [Getting Your API Key](/api-integration-guide/guides-and-tutorials/getting-api-key#refreshing-an-expired-key))
* Insufficient balance → Make sure wallet has enough XLM
* Vault not found → Check vault address and network

### Function 3: Sign the Transaction ✍️

```javascript
async function signTransaction() {
    try {
        // Make sure wallet is connected and we have a transaction to sign
        if (!appState.connected) {
            updateStatus('❌ Please connect your wallet first!', 'error');
            return;
        }

        if (!appState.unsignedXdr) {
            updateStatus('❌ No transaction to sign. Please get a quote first!', 'error');
            return;
        }

        updateStatus('📝 Please approve the transaction in Freighter...', 'info');

        // Sign the transaction using Freighter
        const signResult = await freighterAPI.signTransaction(appState.unsignedXdr, {
            network: CONFIG.NETWORK,
            networkPassphrase: 'Test SDF Network ; September 2015',
            address: appState.walletAddress
        });

        appState.signedTransaction = signResult.signedTxXdr;

        // Show the signed transaction for educational purposes
        ELEMENTS.signedXdr.value = appState.signedTransaction;

        const depositAmount = formatAmount(CONFIG.DEPOSIT.AMOUNT, 7);
        updateStatus(`✅ Transaction signed successfully!<br>📋 Ready to deposit ${depositAmount} XLM into vault`, 'success');
        updateButtonStates();
    } catch (error) {
        console.error('Transaction signing failed:', error);
        updateStatus(`❌ Transaction signing failed: ${error.message}`, 'error');
    }
}
```

**🔍 What this function does:**

1. **Validates** wallet connection and transaction availability
2. **Calls** Freighter to sign the transaction
3. **Stores** the signed transaction in app state
4. **Updates** UI to enable final step

**🚨 Common issues:**

* User rejects signing → Ask them to try again
* Freighter not connected → Check wallet connection
* Wrong network → Ensure Freighter is on testnet

### Function 4: Send Transaction to Network 🚀

```javascript
async function sendTransaction() {
    try {
        // Make sure we have a signed transaction
        if (!appState.signedTransaction) {
            updateStatus('❌ No signed transaction available. Please get a quote and sign it first.', 'error');
            return;
        }

        updateStatus('🚀 Broadcasting transaction to Stellar network...', 'info');

        // Send the signed transaction
        const sendRequest = {
            xdr: appState.signedTransaction     // The signed transaction
        };

        const sendResult = await makeAPIRequest('/send', sendRequest);

        // Create link to view transaction on Stellar Expert
        const explorerUrl = `https://stellar.expert/explorer/${CONFIG.NETWORK}/tx/${sendResult.txHash}`;

        // Show success message
        ELEMENTS.transactionLink.innerHTML = `
            <strong>Transaction Hash:</strong> <code>${sendResult.txHash}</code><br>
            <a href="${explorerUrl}" target="_blank" rel="noopener">🔗 View on Stellar Expert</a>
        `;
        ELEMENTS.finalResults.classList.remove('hidden');

        updateStatus('🎉 Vault deposit completed successfully! You now own vault shares (dfTokens). Check the transaction link above.', 'success');

        // Reset state for potential next transaction
        appState.unsignedXdr = null;
        appState.signedTransaction = null;
        appState.currentQuote = null;
        updateButtonStates();

    } catch (error) {
        console.error('Transaction send failed:', error);
        updateStatus(`❌ Transaction failed: ${error.message}`, 'error');
    }
}
```

**🔍 What this function does:**

1. **Validates** we have a signed transaction ready
2. **Submits** the transaction to the Stellar network via Soroswap API
3. **Shows** success message with transaction hash and explorer link
4. **Resets** state for potential next transaction

## 🔄 Complete Workflow Summary

**4-Step Process:**

```
🔗 STEP 1: Connect Wallet
User clicks "Connect Freighter Wallet"
   ↓ connectWallet() function
App connects to Freighter wallet ✅
   ↓

💰 STEP 2: Get Vault Info & Build Deposit
User clicks "Deposit into Vault (1 XLM)"
   ↓ getVaultInfoAndDeposit() function
App gets vault info + builds unsigned deposit transaction ✅
   ↓

✍️ STEP 3: Sign Transaction
User clicks "Sign Transaction"
   ↓ signTransaction() function
User approves transaction in Freighter wallet ✅
   ↓

🚀 STEP 4: Send to Network
User clicks "Send Transaction"
   ↓ sendTransaction() function
App broadcasts to Stellar network ✅
   ↓
🎉 VAULT DEPOSIT COMPLETE!
```

## 🛠️ Key Concepts for Beginners

### What is XDR?

**XDR** (External Data Representation) is how Stellar transactions are encoded:

* **Unsigned XDR**: Transaction ready to be signed
* **Signed XDR**: Transaction with digital signature, ready to submit

### What is a Vault?

A **vault** is a smart contract that:

* Holds multiple users' assets
* Automatically invests them in yield-generating strategies
* Issues **dfTokens** (vault shares) representing ownership

### What are dfTokens?

**dfTokens** are like receipts for your vault deposit:

* Represent your proportional share of the vault
* Increase in value as the vault earns yield
* Can be redeemed for underlying assets + profits

### What are Strategies?

**Strategies** are investment protocols that:

* Generate yield on deposited assets
* Examples: Blend Capital lending, liquidity providing
* Vaults can use multiple strategies to maximize returns

### What is Signing?

**Signing** a transaction means:

* Proving you own the wallet
* Authorizing the vault deposit
* Making it ready for the network

## 🚨 Security Best Practices

### ✅ DO:

* Use testnet for learning and testing
* Keep your API keys secure
* Verify transaction details before signing
* Start with small amounts

### ❌ DON'T:

* Put API keys in public code repositories
* Sign transactions you don't understand
* Use mainnet while learning
* Hardcode private keys (never!)

## 🔧 Common Troubleshooting

### Problem: "Freighter not found"

**Solution**: Install Freighter wallet extension

### Problem: "403 Forbidden" error

**Solution**: Check your API key is correct and not expired

### Problem: "Insufficient balance"

**Solution**: Make sure you have enough XLM in your wallet for the deposit

### Problem: Transaction fails

**Solution**: Check you have enough XLM for fees and minimum deposit requirements

## 🎓 Next Steps

Once you understand this basic example:

1. **Customize the UI** with better styling
2. **Add error handling** for better user experience
3. **Support multiple vaults** with different asset combinations
4. **Add APY tracking** to show vault performance
5. **Try withdrawal functionality** to redeem your dfTokens
6. **Explore the SDKs** from [DeFindex](/advanced-documentation/sdks)
7. **Learn about strategies** and how they generate yield

## 📚 Additional Resources

* **DeFindex API Docs**: <https://api.defindex.io/docs>
* **DeFindex Discord**: <https://discord.gg/ftPKMPm38f>
* **Freighter Docs**: <https://freighter.app/docs>
* **Stellar Docs**: <https://developers.stellar.org>
* **Stellar Expert**: <https://stellar.expert> (blockchain explorer)
* **DeFindex**: <https://defindex.io>

## 💡 Pro Tips

1. **Use browser dev tools** to debug API calls
2. **Check the console** for error messages
3. **Read API responses** to understand what's happening


# Setting Partner Fees

⏱️ 7 min read

## 📖 What You'll Learn

This guide walks you through the complete fee management lifecycle for a deployed DeFindex vault:

* **Check** the current fee configuration of your vault
* **Update** the vault fee rate (in basis points)
* **Change** the fee receiver address
* **Distribute** accumulated fees to the appropriate parties

***

## 🎯 Prerequisites

Before starting, make sure you have:

* **Manager role** on the vault (or Fee Receiver role for specific operations)
* An **API key** from DeFindex (see [Getting Started with API](/api-integration-guide/api))
* Your **vault address** on mainnet or testnet
* A way to **sign transactions** (Freighter Wallet, Stellar Laboratory, etc.)

***

## 📐 Understanding BPS (Basis Points)

DeFindex uses **basis points (BPS)** to express fee percentages. One basis point equals 0.01%.

| BPS Value | Percentage | Description          |
| --------- | ---------- | -------------------- |
| 100       | 1%         | Low fee              |
| 500       | 5%         | Moderate fee         |
| 1000      | 10%        | Common fee           |
| 3000      | 30%        | Standard partner fee |
| 5000      | 50%        | High fee             |
| 9000      | 90%        | Maximum allowed      |

**Key constants:**

* `SCALAR_BPS = 10,000` → 10,000 BPS = 100%
* **Maximum vault fee**: 9,000 BPS (90%)
* Fees are only charged on **yield generated**, never on deposited capital

***

## Step 1: Check Current Fee Configuration 🔍

Before making changes, verify the current fee settings on your vault.

### Get Vault Info (includes fee rates)

```bash
curl -X GET "https://api.defindex.io/vault/YOUR_VAULT_ADDRESS?network=mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

The response includes fee configuration:

```json
{
  "name": "My Yield Vault",
  "address": "CABC...XYZ",
  "feesBps": {
    "vaultFee": 3000,
    "defindexFee": 500
  }
}
```

**🔍 What this tells you:**

* `vaultFee`: The partner's fee rate in BPS (3000 = 30%)
* `defindexFee`: The DeFindex protocol fee rate in BPS (500 = 5%)

### Get Current Fee Receiver

```bash
curl -X GET "https://api.defindex.io/vault/YOUR_VAULT_ADDRESS/get/fee-receiver?network=mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Response:

```json
{
  "address": "GFEE_RECEIVER_ADDRESS..."
}
```

***

## Step 2: Update the Vault Fee (BPS) 💰

To change the fee rate on your vault, use the `lock-fees` endpoint. This endpoint serves a **dual purpose**: it locks any accrued fees AND updates the fee rate.

### Build the Transaction

```bash
curl -X POST "https://api.defindex.io/vault/YOUR_VAULT_ADDRESS/lock-fees?network=mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "new_fee_bps": 3000,
    "caller": "GMANAGER_ADDRESS..."
  }'
```

**Parameters:**

| Parameter     | Type   | Description                  |
| ------------- | ------ | ---------------------------- |
| `new_fee_bps` | number | New fee rate in BPS (0–9000) |
| `caller`      | string | Manager wallet address       |

**🔍 What this does:**

1. **Locks** any currently accrued fees at the previous rate
2. **Updates** the vault fee rate to the new value
3. Returns an **unsigned XDR** transaction

### Sign and Submit

The response contains an unsigned XDR transaction:

```json
{
  "xdr": "AAAAAgAAAA..."
}
```

**Sign the transaction** using Freighter, Stellar Laboratory, or your preferred method, then submit:

```bash
curl -X POST "https://api.defindex.io/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "xdr": "SIGNED_XDR_HERE..."
  }'
```

**🚨 Important notes:**

* Only the **Manager** role can update fees
* Maximum allowed value is **9000 BPS** (90%)
* Setting `new_fee_bps: 0` effectively disables partner fees

***

## Step 3: Change the Fee Receiver Address 🔄

To redirect fee payments to a different address, use the `set/fee-receiver` endpoint.

### Build the Transaction

```bash
curl -X POST "https://api.defindex.io/vault/YOUR_VAULT_ADDRESS/set/fee-receiver?network=mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "new_address": "GNEW_RECEIVER_ADDRESS...",
    "caller": "GMANAGER_ADDRESS..."
  }'
```

**Parameters:**

| Parameter     | Type   | Description                             |
| ------------- | ------ | --------------------------------------- |
| `new_address` | string | New fee receiver Stellar address        |
| `caller`      | string | Manager OR current Fee Receiver address |

**🔍 What this does:**

1. Updates the vault's fee receiver to the new address
2. Returns an **unsigned XDR** transaction

### Sign and Submit

Sign the returned XDR and submit it using the `/send` endpoint (same flow as Step 2).

**🚨 Important notes:**

* Can be called by **Manager** OR the **current Fee Receiver**
* The new address must be a valid Stellar address
* Always verify the new address before submitting — this action is irreversible without another update

***

## Step 4: Distribute Accumulated Fees 📤

Fees accumulate in the vault as yield is generated. To distribute them, use the `distribute-fees` endpoint.

### Build the Transaction

```bash
curl -X POST "https://api.defindex.io/vault/YOUR_VAULT_ADDRESS/distribute-fees?network=mainnet" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "caller": "GMANAGER_ADDRESS..."
  }'
```

**Parameters:**

| Parameter | Type   | Description                     |
| --------- | ------ | ------------------------------- |
| `caller`  | string | Manager OR Fee Receiver address |

**🔍 What this does:**

1. Calculates the locked fee amount
2. **Splits** the fees between the vault fee receiver and the DeFindex protocol receiver based on the `defindexFee` rate
3. Sends each party their share
4. Returns an **unsigned XDR** transaction

### Sign and Submit

Sign the returned XDR and submit it using the `/send` endpoint (same flow as Step 2).

**🚨 Important notes:**

* Can be called by **Manager** OR **Fee Receiver**
* Fees must be locked before they can be distributed (Step 2 locks fees automatically)
* Distribution sends actual tokens to the receiver addresses

***

## 🔄 Complete Fee Management Workflow

```md
📐 CHECK CURRENT FEES
GET /vault/{address}?network=mainnet
GET /vault/{address}/get/fee-receiver?network=mainnet
            ↓

💰 UPDATE FEE RATE (Optional)
POST /vault/{address}/lock-fees
   → Locks accrued fees + sets new rate
   → Sign & submit unsigned XDR
            ↓

🔄 CHANGE FEE RECEIVER (Optional)
POST /vault/{address}/set/fee-receiver
   → Updates receiver address
   → Sign & submit unsigned XDR
            ↓

📤 DISTRIBUTE FEES
POST /vault/{address}/distribute-fees
   → Splits fees: partner share + DeFindex share
   → Sign & submit unsigned XDR
            ↓

✅ FEES DISTRIBUTED!
   → Fee Receiver gets vault fee share
   → DeFindex gets protocol fee share
```

***

## 📊 BPS Quick Reference Table

| BPS  | Percentage | Annual fee on $10,000 yield |
| ---- | ---------- | --------------------------- |
| 100  | 1%         | $100                        |
| 250  | 2.5%       | $250                        |
| 500  | 5%         | $500                        |
| 1000 | 10%        | $1,000                      |
| 2000 | 20%        | $2,000                      |
| 3000 | 30%        | $3,000                      |
| 5000 | 50%        | $5,000                      |
| 9000 | 90%        | $9,000                      |

Remember: fees are charged on **yield only**, not on deposited capital.

***

## 🔒 Security Best Practices

### ✅ DO

* Use a **multisig wallet** for the Manager role
* Use a **dedicated, secure wallet** for the Fee Receiver
* **Verify fee values** before signing transactions (double-check BPS math)
* **Test on testnet** before making mainnet changes
* Distribute fees **regularly** to avoid large accumulated amounts

### ❌ DON'T

* Set fees above **9000 BPS** (the transaction will fail)
* Share your **API key** or expose it in client-side code
* Change the fee receiver to an **uncontrolled address**
* Skip **transaction verification** before signing

***

## 🔧 Common Troubleshooting

### Problem: "Permission denied" or "Unauthorized"

**Solution**: Only the Manager role can update fees. Verify you're using the correct caller address. For `set/fee-receiver` and `distribute-fees`, the current Fee Receiver can also call these.

### Problem: "Fee exceeds maximum"

**Solution**: The maximum allowed vault fee is 9000 BPS (90%). Reduce the `new_fee_bps` value.

### Problem: "No fees to distribute"

**Solution**: Fees accumulate as the vault generates yield. If the vault hasn't generated yield since the last distribution, there may be nothing to distribute. Also ensure fees have been locked first.

### Problem: "403 Forbidden"

**Solution**: Check your API key is correct and not expired. See [Getting Started with API](/api-integration-guide/api) for key generation.

### Problem: Transaction fails after signing

**Solution**: The unsigned XDR may have expired. Rebuild the transaction and sign again promptly. Also ensure your wallet has enough XLM for network fees.

***

## 📚 Related Resources

* [Partner Fees](/getting-started/getting-started/partner-fees) — Conceptual overview of the fee model
* [Vault Roles](/getting-started/getting-started/vault-roles) — Understanding Manager and Fee Receiver roles
* [Beginner Guide](/api-integration-guide/guides-and-tutorials/beginner-guide) — Full walkthrough of the build → sign → submit flow
* [API Documentation](https://api.defindex.io/docs) — Complete API reference
* [Getting Started with API](/api-integration-guide/api) — API key setup and client configuration


# Deposit & Transfer dfTokens

⏱️ 7 min read

## 📖 What You'll Learn

In this tutorial you'll build a TypeScript script that:

* **Deposits** assets into a DeFindex vault and receives dfTokens in return
* **Transfers** those dfTokens to another Stellar wallet

By the end, you'll have a working script that automates this entire flow — useful for vault managers who want to run **raffles**, **airdrops**, or distribute **incentives** to their users.

## 💡 Why Would I Want to Transfer dfTokens?

When you deposit into a DeFindex vault, the vault mints **dfTokens** — tokens that represent your proportional share of the vault's assets. These tokens:

* Increase in value as the vault earns yield
* Can be redeemed later for the underlying assets + profits
* **Are transferable**, just like any other Stellar token

This means a vault manager can deposit funds, receive dfTokens, and then **distribute them to other wallets**. The recipients now hold vault shares without needing to deposit themselves. Some practical use cases:

| Use Case               | Description                                   |
| ---------------------- | --------------------------------------------- |
| 🎁 Raffles & Giveaways | Deposit and distribute dfTokens as prizes     |
| 🏆 User Incentives     | Reward active users with yield-bearing tokens |
| 💼 Team Distribution   | Split vault ownership across team members     |
| 🔄 OTC Transfers       | Move vault positions between wallets          |

## 🎯 Prerequisites

Before starting, make sure you have:

* **Node.js** (v18+) and a package manager (`npm`, `yarn`, or similar)
* A **Stellar wallet** with enough balance for the deposit + transaction fees
* A **DeFindex API key** (register at [api.defindex.io](https://api.defindex.io/register) or contact us on [Discord](https://discord.gg/ftPKMPm38f))
* Basic knowledge of **TypeScript** and the **Stellar network**

## 🧠 Key Concepts

Before diving into code, let's understand the two core operations we'll perform.

### Deposit → Mint dfTokens

When you call `deposit` on a DeFindex vault, the contract:

1. Transfers your assets (e.g., XLM) from your wallet into the vault
2. Calculates how many shares you should receive, proportional to your contribution
3. **Mints dfTokens** to your wallet representing those shares
4. Optionally invests the deposited funds into the vault's strategies

The vault contract's deposit function signature looks like this:

```rust
fn deposit(
    e: Env,
    amounts_desired: Vec<i128>,
    amounts_min: Vec<i128>,
    from: Address,
    invest: bool,
) -> Result<(Vec<i128>, i128, Option<Vec<Option<AssetInvestmentAllocation>>>), ContractError>
```

It returns a tuple with: the actual deposited amounts, the **number of dfTokens minted**, and the investment allocations.

### Transfer dfTokens

Here's something important: **the vault contract itself is the dfToken contract**. The vault implements the standard Stellar token interface, which means you can call `transfer` directly on the vault contract to move dfTokens between wallets:

```rust
fn transfer(e: Env, from: Address, to: Address, amount: i128)
```

No separate token contract needed — just call `transfer` on the vault address with the sender, receiver, and amount.

## 🏗️ Step-by-Step Implementation

### Step 1: Project Setup

Create a new project and install the required dependencies:

```bash
mkdir defindex-deposit-transfer
cd defindex-deposit-transfer
npm init -y
npm install @defindex/sdk @stellar/stellar-sdk
npm install -D typescript tsx @types/node dotenv
```

Add a run script to your `package.json`:

```json
{
  "scripts": {
    "start": "tsx src/index.ts"
  }
}
```

Create a `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "rootDir": "src",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}
```

Create a `.env` file with your configuration:

```bash
DEFINDEX_API_KEY=sk_your_api_key_here
STELLAR_SECRET_KEY=S_your_wallet_secret_key
RECEIVER_ADDRESS=G_receiver_wallet_public_key
SOROBAN_RPC=your testnet soroban rpc url (e.g., https://soroban-testnet.stellar.org)
```

> ⚠️ **Never commit your `.env` file to version control.** Add it to your `.gitignore`.
>
> **Note:** This tutorial uses **testnet** for learning purposes. For production, change `SOROBAN_RPC` to a mainnet endpoint (e.g., `https://soroban-rpc.mainnet.stellar.gateway.fm`) and update the network references in the code to `Networks.PUBLIC` / `SupportedNetworks.MAINNET`.

### Step 2: Load Configuration

Create `src/index.ts` and start by importing dependencies and loading environment variables:

```typescript
import { DefindexSDK, SupportedNetworks } from '@defindex/sdk';
import {
  rpc,
  Keypair,
  Networks,
  TransactionBuilder,
  Transaction,
  Contract,
  Address,
  BASE_FEE,
  xdr,
  scValToNative,
  nativeToScVal,
} from '@stellar/stellar-sdk';
import { config } from 'dotenv';

config();
```

Define your constants. You'll need to update `VAULT_ADDRESS` to match the vault you want to deposit into:

```typescript
// ─── Constants ───────────────────────────────────────────────
const VAULT_ADDRESS = 'YOUR_VAULT_ADDRESS_HERE'; // ← Replace with your vault
const DECIMALS = 7;
const DEPOSIT_AMOUNT = 10; // Human-readable amount
const DEPOSIT_AMOUNT_RAW = BigInt(Math.round(DEPOSIT_AMOUNT * 10 ** DECIMALS));
```

> **About decimals:** Stellar uses 7 decimal places for most assets. So `10 XLM` = `100,000,000` stroops (base units). Always convert to raw amounts before calling contract functions. We use `BigInt(Math.round(...))` to avoid floating-point precision issues with non-integer amounts like `0.5`.

Now load and validate the environment variables:

```typescript
// ─── Environment ─────────────────────────────────────────────
interface EnvConfig {
  apiKey: string;
  secretKey: string;
  receiverAddress: string;
  sorobanRpc: string;
}

function loadEnv(): EnvConfig {
  const apiKey = process.env.DEFINDEX_API_KEY;
  const secretKey = process.env.STELLAR_SECRET_KEY;
  const receiverAddress = process.env.RECEIVER_ADDRESS;
  const sorobanRpc = process.env.SOROBAN_RPC;

  const missing: string[] = [];
  if (!apiKey) missing.push('DEFINDEX_API_KEY');
  if (!secretKey) missing.push('STELLAR_SECRET_KEY');
  if (!receiverAddress) missing.push('RECEIVER_ADDRESS');
  if (!sorobanRpc) missing.push('SOROBAN_RPC');

  if (missing.length > 0) {
    console.error(`Missing environment variables: ${missing.join(', ')}`);
    process.exit(1);
  }

  return {
    apiKey: apiKey!,
    secretKey: secretKey!,
    receiverAddress: receiverAddress!,
    sorobanRpc: sorobanRpc!,
  };
}
```

### Step 3: Transaction Helper

Both deposit and transfer need to submit a transaction and wait for on-chain confirmation. Let's create a reusable helper:

```typescript
// ─── Transaction Helpers ─────────────────────────────────────
interface TxResult {
  txHash: string;
  returnValue?: xdr.ScVal;
}

const MAX_POLLS = 30; // 30 polls × 2s = 60s max wait

async function sendAndConfirm(
  rpcServer: rpc.Server,
  transaction: Transaction
): Promise<TxResult> {
  const response = await rpcServer.sendTransaction(transaction);

  if (response.status !== 'PENDING') {
    const errorXdr = response.errorResult?.toXDR('base64');
    if (errorXdr) {
      const errorName = xdr.TransactionResult.fromXDR(errorXdr, 'base64')
        .result()
        .switch().name;
      throw new Error(`Transaction rejected: ${errorName}`);
    }
    throw new Error(`Transaction rejected with status: ${response.status}`);
  }

  const txHash = response.hash;
  console.log(`  Submitted: ${txHash}`);
  console.log('  Waiting for confirmation...');

  // Poll until confirmed, failed, or timeout
  for (let i = 0; i < MAX_POLLS; i++) {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    const txResponse = await rpcServer.getTransaction(txHash);

    if (txResponse.status === 'SUCCESS') {
      const successResponse =
        txResponse as rpc.Api.GetSuccessfulTransactionResponse;
      return { txHash, returnValue: successResponse.returnValue };
    }

    if (txResponse.status === 'FAILED') {
      throw new Error(`Transaction failed on-chain: ${txHash}`);
    }
  }

  throw new Error(
    `Transaction not confirmed after ${MAX_POLLS * 2}s: ${txHash}`
  );
}
```

**🔍 What this function does:**

1. **Submits** the signed transaction to the Soroban RPC
2. **Validates** the transaction was accepted (status `PENDING`)
3. **Polls** every 2 seconds (up to 60s) until the transaction is confirmed or fails
4. **Returns** the transaction hash and the on-chain return value

### Step 4: The Deposit Function 💰

This function uses the DeFindex SDK to build a deposit transaction, signs it locally, and submits it. The key part is **extracting the minted dfTokens** from the return value:

```typescript
// ─── Deposit ─────────────────────────────────────────────────
async function deposit(
  sdk: DefindexSDK,
  rpcServer: rpc.Server,
  keypair: Keypair
): Promise<{ txHash: string; dfTokensMinted: bigint }> {
  const caller = keypair.publicKey();

  console.log('  Building deposit via SDK...');
  const depositResponse = await sdk.depositToVault(
    VAULT_ADDRESS,
    { amounts: [DEPOSIT_AMOUNT_RAW], caller, invest: true },
    SupportedNetworks.TESTNET
  );

  console.log('  Signing...');
  const tx = TransactionBuilder.fromXDR(
    depositResponse.xdr,
    Networks.TESTNET
  ) as Transaction;
  tx.sign(keypair);

  console.log('  Sending deposit...');
  const { txHash, returnValue } = await sendAndConfirm(rpcServer, tx);

  if (!returnValue) {
    throw new Error('Deposit transaction returned no value');
  }

  // The deposit function returns a tuple:
  //   Index 0: Vec<i128> → actual amounts deposited
  //   Index 1: i128      → dfTokens minted  ← this is what we need
  //   Index 2: Option     → investment allocations
  const nativeResult = scValToNative(returnValue);

  if (!Array.isArray(nativeResult) || nativeResult[1] == null) {
    throw new Error('Unexpected deposit return shape');
  }

  const dfTokensMinted = BigInt(nativeResult[1] as string | number | bigint);

  return { txHash, dfTokensMinted };
}
```

**🔍 What this function does:**

1. **Builds** an unsigned deposit transaction using the DeFindex SDK (`depositToVault`)
2. **Signs** the transaction with your keypair
3. **Submits** it to the network and waits for confirmation
4. **Parses** the return value to extract how many dfTokens were minted

> **Why `invest: true`?** When set to `true`, the vault automatically allocates your deposited funds into its yield-generating strategies. Set to `false` if you want the funds to remain idle in the vault.
>
> **Tip:** You can also pass `slippageBps` in the deposit params (e.g., `slippageBps: 100` for 1% tolerance) to control slippage during deposit.
>
> **Note:** The number of dfTokens minted is **not** 1:1 with the deposited amount. dfTokens represent your proportional share of the vault — their quantity depends on the vault's current share price, which changes as the vault earns yield.

### Step 5: The Transfer Function 🔄

Now the interesting part — transferring dfTokens to another wallet. Since the vault contract implements the token interface, we call `transfer` directly on the vault contract address:

```typescript
// ─── Transfer dfTokens ──────────────────────────────────────
async function transferDfTokens(
  rpcServer: rpc.Server,
  keypair: Keypair,
  toAddress: string,
  amount: bigint
): Promise<string> {
  const from = keypair.publicKey();
  const contract = new Contract(VAULT_ADDRESS);

  // Build the transfer operation on the vault contract
  const operation = contract.call(
    'transfer',
    new Address(from).toScVal(),
    new Address(toAddress).toScVal(),
    nativeToScVal(amount, { type: 'i128' })
  );

  // Create the transaction
  const account = await rpcServer.getAccount(from);
  const tx = new TransactionBuilder(account, {
    fee: BASE_FEE,
    networkPassphrase: Networks.TESTNET,
  })
    .addOperation(operation)
    .setTimeout(30)
    .build();

  // Simulate to estimate resources
  console.log('  Simulating transfer...');
  const simulation = await rpcServer.simulateTransaction(tx);

  if (rpc.Api.isSimulationError(simulation)) {
    throw new Error(`Transfer simulation failed: ${simulation.error}`);
  }

  // Assemble with simulation results, sign, and send
  const preparedTx = rpc.assembleTransaction(tx, simulation).build();
  preparedTx.sign(keypair);

  console.log('  Sending transfer...');
  const { txHash } = await sendAndConfirm(rpcServer, preparedTx);

  return txHash;
}
```

**🔍 What this function does:**

1. **Creates** a `Contract` instance pointing to the vault address
2. **Builds** a `transfer(from, to, amount)` call — this is the standard Soroban token transfer
3. **Simulates** the transaction to estimate the required resources (CPU, memory, ledger I/O)
4. **Assembles** the final transaction with the simulation results
5. **Signs** and **submits** it to the network

> **Why simulate first?** Soroban smart contract calls require accurate resource estimation. The simulation step calculates CPU instructions, memory bytes, and ledger operations needed, so the transaction has the right resource limits to succeed on-chain.
>
> **About fees:** `BASE_FEE` is the minimum fee (100 stroops). On a congested network, transactions with the minimum fee may be dropped. For production, consider using a higher fee (e.g., `String(100 * 10)`) or implementing a fee-bumping strategy.

### Step 6: Put It All Together 🚀

Finally, wire everything up in a `main` function:

```typescript
// ─── Main ────────────────────────────────────────────────────
async function main(): Promise<void> {
  const { apiKey, secretKey, receiverAddress, sorobanRpc } = loadEnv();

  const keypair = Keypair.fromSecret(secretKey);
  const walletA = keypair.publicKey();
  const walletB = receiverAddress;
  const rpcServer = new rpc.Server(sorobanRpc);
  const sdk = new DefindexSDK({ apiKey });

  console.log('='.repeat(60));
  console.log('DeFindex: Deposit & Transfer dfTokens');
  console.log('='.repeat(60));
  console.log(`  Wallet A (depositor): ${walletA}`);
  console.log(`  Wallet B (receiver):  ${walletB}`);
  console.log(`  Vault:                ${VAULT_ADDRESS}`);
  console.log(`  Deposit:              ${DEPOSIT_AMOUNT} XLM`);
  console.log('='.repeat(60));
  console.log('');

  // Step 1: Deposit into vault → receive dfTokens
  console.log('[Step 1] Depositing into vault...');
  const { txHash: depositTxHash, dfTokensMinted } = await deposit(
    sdk,
    rpcServer,
    keypair
  );
  console.log(`  ✅ Deposit confirmed: ${depositTxHash}`);
  console.log(`  dfTokens minted: ${dfTokensMinted}`);
  console.log('');

  // Step 2: Transfer dfTokens to Wallet B
  console.log(`[Step 2] Transferring ${dfTokensMinted} dfTokens to Wallet B...`);
  const transferTxHash = await transferDfTokens(
    rpcServer,
    keypair,
    walletB,
    dfTokensMinted
  );
  console.log(`  ✅ Transfer confirmed: ${transferTxHash}`);
  console.log('');

  // Summary
  console.log('='.repeat(60));
  console.log('DONE');
  console.log('='.repeat(60));
  console.log(`  Deposited:      ${DEPOSIT_AMOUNT} XLM`);
  console.log(`  dfTokens:       ${dfTokensMinted}`);
  console.log(`  Transferred to: ${walletB}`);
  console.log(`  Deposit TX:     ${depositTxHash}`);
  console.log(`  Transfer TX:    ${transferTxHash}`);
  console.log('='.repeat(60));
}

main().catch((error: unknown) => {
  console.error('Fatal error:', error);
  process.exit(1);
});
```

### Run It

```bash
npm start
```

You should see output similar to:

```
============================================================
DeFindex: Deposit & Transfer dfTokens
============================================================
  Wallet A (depositor): GABC...
  Wallet B (receiver):  GXYZ...
  Vault:                CVAULT...
  Deposit:              10 XLM
============================================================

[Step 1] Depositing into vault...
  Building deposit via SDK...
  Signing...
  Sending deposit...
  Submitted: abc123...
  Waiting for confirmation...
  ✅ Deposit confirmed: abc123...
  dfTokens minted: 19850000

[Step 2] Transferring 19850000 dfTokens to Wallet B...
  Simulating transfer...
  Sending transfer...
  Submitted: def456...
  ✅ Transfer confirmed: def456...

============================================================
DONE
============================================================
```

## 🚨 Troubleshooting

| Problem                               | Solution                                                                                          |
| ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `Missing environment variables`       | Check that your `.env` file has all four variables filled in                                      |
| `Transaction rejected`                | Verify Wallet A has enough asset balance and XLM for fees                                         |
| `Transfer simulation failed`          | Confirm the vault address is correct and Wallet A holds dfTokens                                  |
| `Deposit returned no value`           | May be a network issue — check your Soroban RPC endpoint                                          |
| `Transaction failed on-chain`         | Check the transaction on [Stellar Expert](https://stellar.expert) for details                     |
| `Transaction not confirmed after 60s` | The RPC node may have dropped the tx — check your Soroban RPC endpoint or retry with a higher fee |

## 🎓 Next Steps

* **Withdraw** — Redeem dfTokens back for the underlying assets: [Withdraw Guide](/api-integration-guide/smart-contracts/withdraw)
* **Check Balances** — Query dfToken holdings: [Get Balance](/api-integration-guide/smart-contracts/get-balance)
* **Monitor APY** — Track vault performance: [Get APY](/api-integration-guide/smart-contracts/get-apy)
* **Explore the SDK** — More features available in the [TypeScript SDK](/advanced-documentation/sdks/02-defindex-sdk)

***

## 🤖 Build It with AI

Want to build this script quickly? Copy the prompt below and paste it into a new Claude Code session. It contains all the technical details needed to generate the complete implementation:

````
Build a TypeScript script that deposits assets into a DeFindex vault and transfers the minted
dfTokens to another wallet on the Stellar testnet.

## Technical Context

- DeFindex vaults are Soroban smart contracts on Stellar that accept asset deposits and mint
  dfTokens (vault share tokens) in return
- The vault contract itself implements the Soroban token interface, so dfTokens can be
  transferred by calling `transfer` directly on the vault contract address
- The deposit function returns a tuple where index [1] is the number of dfTokens minted (i128)

## Required Dependencies

```bash
npm install @defindex/sdk @stellar/stellar-sdk
npm install -D typescript tsx @types/node dotenv
```

Use your preferred package manager (`npm`, `yarn`, etc.). Install the latest versions.

## Environment Variables (.env)

```
DEFINDEX_API_KEY=     # DeFindex API key (sk_...)
STELLAR_SECRET_KEY=   # Wallet A secret key (depositor/signer)
RECEIVER_ADDRESS=     # Wallet B public key (receives dfTokens)
SOROBAN_RPC=your testnet soroban rpc url
```

## Implementation Requirements

Create `src/index.ts` with the following structure:

### 1. Constants
- `VAULT_ADDRESS`: The target vault contract address (user must configure)
- `DECIMALS = 7` (Stellar standard)
- `DEPOSIT_AMOUNT`: Human-readable amount, converted to raw (amount * 10^7)

### 2. `loadEnv()` function
- Load and validate all four environment variables
- Exit with error if any are missing
- Return typed config object

### 3. `sendAndConfirm(rpcServer, transaction)` helper
- Send transaction to Soroban RPC via `rpcServer.sendTransaction()`
- Check status is 'PENDING', otherwise parse error from `response.errorResult`
- Poll `rpcServer.getTransaction(hash)` every 2 seconds until SUCCESS or FAILED (max 30 polls / 60s timeout)
- On SUCCESS, return `{ txHash, returnValue }` from `GetSuccessfulTransactionResponse`

### 4. `deposit(sdk, rpcServer, keypair)` function
- Use `sdk.depositToVault(VAULT_ADDRESS, { amounts: [RAW_AMOUNT], caller, invest: true },
  SupportedNetworks.TESTNET)` to build unsigned XDR
- Parse XDR with `TransactionBuilder.fromXDR(xdr, Networks.TESTNET)`
- Sign with keypair, send via `sendAndConfirm()`
- Parse return value: `StellarSdk.scValToNative(returnValue)` returns an array,
  index [1] is dfTokens minted as bigint
- Return `{ txHash, dfTokensMinted }`

### 5. `transferDfTokens(rpcServer, keypair, toAddress, amount)` function
- Create `new Contract(VAULT_ADDRESS)` instance
- Build operation: `contract.call('transfer', Address(from).toScVal(),
  Address(to).toScVal(), nativeToScVal(amount, { type: 'i128' }))`
- Get account via `rpcServer.getAccount(from)`
- Build transaction with `TransactionBuilder`, fee `BASE_FEE`,
  network `Networks.TESTNET`, timeout 30s
- Simulate with `rpcServer.simulateTransaction(tx)`, check for errors
  with `rpc.Api.isSimulationError()`
- Assemble with `rpc.assembleTransaction(tx, simulation).build()`
- Sign and send via `sendAndConfirm()`

### 6. `main()` function
- Load env, create Keypair, rpc.Server, and DefindexSDK instances
- Step 1: Call deposit, log the deposit tx hash and dfTokens minted
- Step 2: Call transferDfTokens with the minted amount, log the transfer tx hash
- Print summary with all transaction details

## Run Script
Add `"start": "tsx src/index.ts"` to package.json scripts.

## Important Notes
- Use `Networks.TESTNET` and `SupportedNetworks.TESTNET` for testnet
- For production, switch to `Networks.PUBLIC` / `SupportedNetworks.MAINNET`
  and a mainnet RPC endpoint
- All amounts use 7 decimals (stroops)
- The vault contract address is used both for deposit (via SDK) and for the
  transfer call (as a Contract instance)
- Handle errors with try/catch — re-throw or call `process.exit(1)` on fatal errors
- Validate the deposit return shape before accessing positional indices
````


# Sponsored Transactions (Fee Bump Tx)

⏱️ 4 min read

## Introduction

Sponsored transactions allow a **sponsor account** to pay Stellar transaction fees on behalf of another user. This is essential when working with **smart accounts** (Soroban contract-based accounts) that cannot pay fees themselves. Using the fee-bump pattern, you can build seamless user experiences where end users never need to hold XLM for gas.

This guide walks you through implementing fee-bump deposits and withdrawals with the DeFindex API.

## Why Are Sponsored Transactions Needed?

### Smart Accounts and Transaction Fees

On Stellar, there are two types of accounts:

* **Native accounts** (`G...` addresses) — Standard Stellar accounts that hold XLM and can pay transaction fees. They also serve as the **source account** (sequence number provider) for transactions.
* **Smart accounts** (`C...` addresses) — Soroban contract-based accounts. These accounts **cannot be the source account** of a transaction and **cannot pay transaction fees** because Stellar requires fees and sequence numbers from a native `G...` account.

> - **Source** = always a native `G...` account (provides the sequence number). In a fee-bump transaction, the **sponsor** pays the fee, not the source account.
> - **Caller (from)** = can be `G...` or `C...` (the account that authorizes the vault operation)

When a DeFindex vault is operated by a smart account, the transaction will fail if no one covers the fee.

### The Fee-Bump Solution

Stellar's **fee-bump transaction** wraps an existing (inner) transaction with an outer envelope that specifies a different fee-paying account:

```
┌──────────────────────────────────────┐
│  Fee-Bump Transaction (outer)        │
│  Fee Source: Sponsor (G...)          │
│                                      │
│  ┌──────────────────────────────┐    │
│  │  Inner Transaction           │    │
│  │  Source: Native account (G..)│    │
│  │  Caller/From: C... or G...   │    │
│  │  Operations: deposit/        │    │
│  │    withdraw, etc.            │    │
│  │  Signed by: Caller           │    │
│  └──────────────────────────────┘    │
│                                      │
│  Signed by: Sponsor                  │
└──────────────────────────────────────┘
```

The inner transaction's **source account** is always a native `G...` account (which provides the sequence number). The **caller** signs the inner transaction to authorize the vault operation. The **sponsor** wraps it in a fee-bump and signs the outer transaction to pay the fee. The network processes both as a single unit.

## Prerequisites

* **`@stellar/stellar-sdk`** `^14.3.0`
* **Two Stellar keypairs:**
  * **Sponsor** — A native `G...` account funded with XLM to pay fees
  * **Caller** — The account executing vault operations (can be `G...` or `C...`)
* **DeFindex API key** (get it at the [API Dashboard](https://api.defindex.io/login))

### Environment Configuration

Create a `.env` file based on the following template:

```bash
# Network: testnet or mainnet
NETWORK=testnet

# Sponsor keypair secret (pays transaction fees)
SPONSOR_SECRET=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Caller secret (signs the inner transaction; see note below)
CALLER_SECRET=SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Defindex API credentials
DEFINDEX_API_KEY=your_api_key_here
DEFINDEX_API_URL=https://api.defindex.io

# Vault contract address on Stellar
VAULT_ADDRESS=CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

> **Note on `CALLER_SECRET` and smart accounts:** `CALLER_SECRET` expects a Stellar secret key (`S...`) for native `G...` accounts. If the caller is a **smart account** (`C...`), authorization comes from wallet interactions (Freighter, xBull, etc.), not a raw private key. In that scenario, present the unsigned XDR to the user's wallet for signing instead of using `Keypair.fromSecret(...)`.

## Deposit with Fee Bump

### Step 1: Initialize API Client and Keypairs

```typescript
import {
  Keypair,
  Networks,
  Transaction,
  TransactionBuilder,
} from "@stellar/stellar-sdk";
import { config } from "dotenv";

config();

const network = process.env.NETWORK?.toLowerCase() || "testnet";
const isMainnet = network === "mainnet";
const stellarNetwork = isMainnet ? Networks.PUBLIC : Networks.TESTNET;

const API_BASE = process.env.DEFINDEX_API_URL as string;
const API_KEY = process.env.DEFINDEX_API_KEY as string;

const sponsorKeypair = Keypair.fromSecret(process.env.SPONSOR_SECRET as string);
// Assumes caller is a native G... account. For smart accounts (C...), use wallet signing instead.
const callerKeypair = Keypair.fromSecret(process.env.CALLER_SECRET as string);

// Lightweight helper for DeFindex API calls. Alternatively, you can use the @defindex/sdk package.
async function api<T>(path: string, options?: { method?: string; body?: unknown }): Promise<T> {
  const separator = path.includes("?") ? "&" : "?";
  const url = `${API_BASE}${path}${separator}network=${network}`;

  const res = await fetch(url, {
    method: options?.method ?? "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
    },
    ...(options?.body ? { body: JSON.stringify(options.body) } : {}),
  });

  if (!res.ok) {
    const errorBody = await res.text();
    throw new Error(`API ${res.status}: ${errorBody}`);
  }

  return res.json() as Promise<T>;
}
```

### Step 2: Get Unsigned Deposit Transaction

> **API Reference:** [`POST /vault/{address}/deposit`](https://api.defindex.io/docs#tag/Vault/operation/VaultController_deposit) — `DepositDto`

```typescript
const vaultAddress = process.env.VAULT_ADDRESS as string;

const depositResponse = await api<{ xdr: string }>(
  `/vault/${vaultAddress}/deposit`,
  {
    method: "POST",
    body: {
      amounts: [10000000],               // Amount in stroops
      invest: true,                      // Auto-invest into strategies
      caller: callerKeypair.publicKey(),
    },
  }
);
```

### Step 3: Sign Inner Transaction with Caller

```typescript
const transaction = TransactionBuilder.fromXDR(
  depositResponse.xdr,
  stellarNetwork
) as Transaction;

transaction.sign(callerKeypair);
```

### Step 4: Create and Sign Fee-Bump with Sponsor

```typescript
const innerTxFee = parseInt(transaction.fee);

const feeBumpTx = TransactionBuilder.buildFeeBumpTransaction(
  sponsorKeypair,
  innerTxFee.toString(),
  transaction,
  stellarNetwork
);

feeBumpTx.sign(sponsorKeypair);
```

### Step 5: Submit the Transaction

> **API Reference:** [`POST /send`](https://api.defindex.io/docs) — `SendXdrDto`

```typescript
const feeBumpXdr = feeBumpTx.toXDR();
const response = await api<{ txHash: string }>(
  "/send",
  { method: "POST", body: { xdr: feeBumpXdr } }
);

console.log("Transaction hash:", response.txHash);
```

## Withdraw with Fee Bump

The withdrawal flow is the same pattern, but first queries the user's vault balance to determine how much of the underlying assets to withdraw.

### Step 1: Get Vault Balance

> **API Reference:** [`GET /vault/{address}/balance`](https://api.defindex.io/docs#tag/Vault/operation/VaultController_getVaultBalance)

```typescript
const balanceResponse = await api<{ balances: number[] }>(
  `/vault/${vaultAddress}/balance?from=${callerKeypair.publicKey()}`
);

const amountsToWithdraw = balanceResponse.balances;
```

### Step 2: Get Unsigned Withdrawal Transaction

> **API Reference:** [`POST /vault/{address}/withdraw`](https://api.defindex.io/docs#tag/Vault/operation/VaultController_withdraw) — `WithdrawDto`

```typescript
const withdrawResponse = await api<{ xdr: string }>(
  `/vault/${vaultAddress}/withdraw`,
  {
    method: "POST",
    body: {
      amounts: amountsToWithdraw,
      caller: callerKeypair.publicKey(),
      slippageBps: 100,                  // Optional: max slippage in basis points (100 = 1%)
    },
  }
);
```

### Step 3: Sign, Wrap, and Submit

The signing and fee-bump steps are identical to the deposit flow:

```typescript
// Sign inner transaction with caller
const transaction = TransactionBuilder.fromXDR(
  withdrawResponse.xdr,
  stellarNetwork
) as Transaction;
transaction.sign(callerKeypair);

// Create fee-bump with sponsor
const innerTxFee = parseInt(transaction.fee);
const feeBumpTx = TransactionBuilder.buildFeeBumpTransaction(
  sponsorKeypair,
  innerTxFee.toString(),
  transaction,
  stellarNetwork
);
feeBumpTx.sign(sponsorKeypair);

// Submit
const response = await api<{ txHash: string }>(
  "/send",
  { method: "POST", body: { xdr: feeBumpTx.toXDR() } }
);
console.log("Transaction hash:", response.txHash);
```

## Fee Considerations

* `buildFeeBumpTransaction` takes a **per-operation base fee**, not a total fee. The SDK internally multiplies by `(numOperations + 1)` to compute the total fee-bump fee ([CAP-0015 rule](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md)).
* For a typical Soroban transaction with 1 operation, passing `parseInt(transaction.fee)` as the base fee produces a **total** fee-bump fee of `2 × innerFee` (since `1 op + 1 = 2`). This satisfies the protocol's fee-rate check because the per-operation rate equals the inner transaction's rate.
* The DeFindex API builds the inner transaction with a simulated resource fee and a correct inclusion fee. Passing `parseInt(transaction.fee)` as the base fee ensures you meet the required minimum.
* To **increase priority** during network congestion, pass a higher base fee:

```typescript
const feeBumpTx = TransactionBuilder.buildFeeBumpTransaction(
  sponsorKeypair,
  (innerTxFee * 2).toString(), // 2× base fee → 4× total fee (for a 1-op tx) for higher priority
  transaction,
  stellarNetwork
);
```

## Common Issues

| Issue                   | Cause                                                                      | Solution                                                                                                                                                                                                                                                            |
| ----------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tx_insufficient_fee`   | Fee-bump per-operation fee rate is lower than the inner transaction's rate | Ensure the base fee passed to `buildFeeBumpTransaction` is ≥ `parseInt(transaction.fee)`. The SDK handles the `(numOps + 1)` multiplication internally                                                                                                              |
| `tx_bad_auth`           | Inner transaction not signed by caller                                     | Ensure `transaction.sign(callerKeypair)` is called before building the fee-bump                                                                                                                                                                                     |
| Wrong signing order     | Fee-bump created before signing inner tx                                   | Always sign the inner transaction first, then build the fee-bump                                                                                                                                                                                                    |
| `429 Too Many Requests` | API rate limit exceeded                                                    | Implement exponential backoff (see [Troubleshooting](/api-integration-guide/troubleshooting#rate-limiting))                                                                                                                                                         |
| `tx_bad_seq`            | Stale sequence number                                                      | Re-fetch the unsigned transaction from the API and retry. Note that Soroban resource estimates (footprint, CPU/memory limits) are also ledger-specific, so simply adjusting the sequence number on a stale transaction is not enough — you must request a fresh XDR |
| `tx_too_late`           | Transaction timebounds expired before submission                           | The XDR was held too long. Re-fetch a fresh unsigned transaction from the API and sign/submit promptly                                                                                                                                                              |

## Production Notes

* **Channel accounts for concurrent sponsors:** If the sponsor account submits multiple fee-bump transactions in parallel, sequence-number collisions will cause failures. Use [channel accounts](https://developers.stellar.org/docs/encyclopedia/channel-accounts). a pool of funded `G...` accounts — so each concurrent submission uses its own sequence number.
* **XDR verification before signing:** In production, the sponsor should decode and inspect the inner transaction XDR before signing. Verify that the operations, amounts, and destination contracts match expected values. Never blindly sign arbitrary XDRs.

## Complete Example Repository

A fully working example with deposit and withdraw scripts is available at:

[**paltalabs/examples/defindex-sdk-deposit-withdraw-fee-bump**](https://github.com/paltalabs/examples/tree/main/defindex-sdk-deposit-withdraw-fee-bump)

To run it:

```bash
git clone https://github.com/paltalabs/examples.git
cd examples/defindex-sdk-deposit-withdraw-fee-bump
npm install
cp .env.example .env
# Edit .env with your keys and vault address

npm run deposit    # Run deposit example
npm run withdraw   # Run withdraw example
```

## Additional Resources

* [Stellar Fee-Bump Transactions](https://developers.stellar.org/docs/build/guides/transactions/fee-bump-transactions)
* [Deposit](/api-integration-guide/smart-contracts/deposit)
* [Withdraw](/api-integration-guide/smart-contracts/withdraw)
* [DeFindex API Documentation](https://api.defindex.io/docs)


# Privy Server Wallets

⏱️ 3 min read

## Overview

This guide points to a reference repository that shows you how to integrate DeFindex vaults using **Privy server wallets** enabling fully automated, server-side deposits, withdrawals, and cross-chain bridging with **zero user interaction**.

The pattern relies on Privy's **Authorization Key** (TEE-backed) to sign Stellar transactions from your backend, making it ideal for custodial products, bots, and programmatic yield strategies.

## Repository

[**defindex-io/privy-defindex-guide**](https://github.com/defindex-io/privy-defindex-guide)

## What the Repository Covers

| Topic          | Description                                           |
| -------------- | ----------------------------------------------------- |
| Privy setup    | App ID, TEE activation, Authorization Key generation  |
| Stellar wallet | Creation, XLM funding via Friendbot, USDC trustline   |
| EVM wallet     | Base EVM wallet with `sendTransaction`                |
| Deposit        | Full signing flow: XDR → hash → `rawSign` → broadcast |
| Withdraw       | Withdraw by amount or by shares (% redemption)        |
| Bridge         | Base USDC → Stellar → Defindex vault via Sodax        |
| Gotchas        | 9 documented edge cases with root causes and fixes    |

## Architecture at a Glance

```md
Your Server (P-256 Authorization Key)
       │  signs every request
       ▼
Privy TEE
  ├── Stellar wallet (Tier 2) — rawSign only
  └── EVM wallet    (Tier 3) — full sendTransaction

Defindex API (api.defindex.io)
  ├── POST /vault/{addr}/deposit         → unsigned Soroban XDR
  ├── POST /vault/{addr}/withdraw        → unsigned Soroban XDR
  ├── POST /vault/{addr}/withdraw_shares → unsigned Soroban XDR
  └── POST /send                         → { txHash }
```

All vault operations follow the same signing loop:

1. Authenticated `POST` to Defindex API → receive unsigned XDR
2. Parse XDR → hash it
3. `privy.rawSign(walletId, { hash })` → Ed25519 signature
4. Attach `DecoratedSignature` to the envelope
5. `POST` signed XDR to `/send`

## Quick Start

```bash
git clone https://github.com/paltalabs/privy-defindex-guide
cd privy-defindex-guide
pnpm install
cp .env.example .env
# Fill in: PRIVY_APP_ID, PRIVY_APP_SECRET, PRIVY_AUTHORIZATION_PRIVATE_KEY, DEFINDEX_API_KEY
```

```bash
pnpm example:deposit          # Deposit into Defindex XLM vault (testnet)
pnpm example:withdraw         # Withdraw by amount (testnet)
pnpm example:withdraw-shares  # Withdraw by shares / percentage (testnet)
pnpm example:bridge           # Base USDC → Stellar → Defindex vault (mainnet)
```

## Prerequisites

* [Privy](https://privy.io) app with TEE enabled and an Authorization Key configured
* Defindex API key — request access on [Discord](https://discord.gg/e2qAhJCBmx)

## Additional Resources

* [Privy Documentation](https://docs.privy.io)
* [Defindex API Reference](https://api.defindex.io/docs)
* [Sodax Bridge Documentation](https://docs.sodax.io)


# Crossmint Smart Wallets

⏱️ 3 min read

## Overview

This guide points to a reference repository that shows you how to integrate DeFindex vaults using **Crossmint smart wallets** — enabling fully automated, server-side deposits, withdrawals, and cross-chain bridging with **zero user interaction**.

The pattern uses an **EVM private key registered as `adminSigner`** to control both an ERC-4337 smart wallet on Base and a Stellar smart wallet. All Defindex vault interactions go through Crossmint's REST API as Soroban `contract-call` transactions — no manual XDR construction required.

## Repository

[**defindex-io/crossmint-defindex-guide**](https://github.com/defindex-io/crossmint-defindex-guide)

## What the Repository Covers

| Topic           | Description                                                               |
| --------------- | ------------------------------------------------------------------------- |
| Crossmint setup | Server API key (`sk_`), wallet email, staging vs production               |
| EVM wallet      | ERC-4337 smart wallet on Base with `external-wallet` adminSigner          |
| Stellar wallet  | Stellar smart wallet with auto-XLM funding, Soroban contract-call signing |
| Deposit         | `contract-call` via Crossmint REST → base64 XDR approval → poll           |
| Withdraw        | Withdraw by amount or by shares (% redemption)                            |
| Bridge          | Base USDC → Stellar → Defindex vault via Sodax                            |
| Gotchas         | 9 documented edge cases with root causes and fixes                        |

## Architecture at a Glance

```
Your Server (EVM Private Key as adminSigner)
       │
       ▼
Crossmint REST API (api/2025-06-09)
  ├── EVM Smart Wallet (ERC-4337, Base)
  │     POST /transactions → sign hex bytes → POST /approvals → onChain.txId
  └── Stellar Smart Wallet
        POST /transactions (contract-call) → sign base64 XDR → POST /approvals → onChain.txId

Sodax Bridge
  └── Base USDC → Stellar USDC (via Sonic hub, no Horizon polling needed)

Defindex Vault (Soroban)
  ├── method: deposit         → amounts_desired, amounts_min, from, invest
  ├── method: withdraw        → amounts_to_withdraw, from
  └── method: withdraw_shares → shares_amount, from
```

All vault operations follow the same pattern:

1. `POST` to Crossmint REST → create `contract-call` transaction
2. Response is `awaiting-approval` with a base64-encoded XDR message
3. Sign with `keypair.sign(Buffer.from(message, "base64"))` using `STELLAR_SERVER_KEY`
4. `POST` signature to `/approvals`
5. Poll until `onChain.txId` is returned

## Quick Start

```bash
git clone https://github.com/defindex-io/crossmint-defindex-guide
cd crossmint-defindex-guide
pnpm install
cp .env.example .env
# Fill in: CROSSMINT_SERVER_API_KEY (sk_...), CROSSMINT_WALLET_EMAIL,
#          EVM_PRIVATE_KEY, STELLAR_SERVER_KEY
```

```bash
CROSSMINT_ENV=staging pnpm example:deposit          # Deposit into testnet vault
CROSSMINT_ENV=staging pnpm example:withdraw         # Withdraw by amount (testnet)
CROSSMINT_ENV=staging pnpm example:withdraw-shares  # Withdraw by shares (testnet)
CROSSMINT_ENV=production pnpm example:bridge        # Base USDC → Stellar → Defindex vault (mainnet)
```

## Prerequisites

* [Crossmint](https://crossmint.com) account with a **server API key** (must start with `sk_`, not `ck_`)
* `EVM_PRIVATE_KEY` — becomes the `adminSigner` of the EVM smart wallet on Base
* `STELLAR_SERVER_KEY` — Stellar ed25519 secret key, becomes the `adminSigner` of the Stellar wallet
* Defindex API key — request access on [Discord](https://discord.gg/e2qAhJCBmx)

## Additional Resources

* [Crossmint Documentation](https://docs.crossmint.com)
* [Defindex API Reference](https://api.defindex.io/docs)
* [Sodax Bridge Documentation](https://docs.sodax.io)


# AI Tools (MCP & Skill)

⏱️ 3 min read

DeFindex provides two complementary AI integrations that let you query documentation and build integrations using natural language.

***

## 1. DeFindex MCP Server

The **Model Context Protocol (MCP)** server exposes the full DeFindex documentation to any compatible AI assistant, including Claude, so it can answer questions about vaults, strategies, API endpoints, and SDK usage without you having to paste docs manually.

### What is it?

The DeFindex MCP server (`https://docs.defindex.io/~gitbook/mcp`) gives AI tools direct, searchable access to these docs. Ask your AI assistant about deposit flows, vault roles, APY calculations, or any other DeFindex concept and it will answer from the latest documentation.

### Add to Claude Code (CLI)

Add the following to your project's `.claude/settings.json` or to your global `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "Defindex": {
      "type": "http",
      "url": "https://docs.defindex.io/~gitbook/mcp"
    }
  }
}
```

After saving, restart Claude Code. The server will be available as `mcp__Defindex__*` tools.

### Add to Claude.ai (Web)

1. Open **Claude.ai → Settings → Integrations → Add MCP Server**
2. Enter the server URL: `https://docs.defindex.io/~gitbook/mcp`
3. Give it a name (e.g. `DeFindex Docs`) and save

### Add to Claude Desktop

In your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "Defindex": {
      "type": "http",
      "url": "https://docs.defindex.io/~gitbook/mcp"
    }
  }
}
```

### Example Queries

Once configured, you can ask your AI assistant:

* *"How do I deposit into a DeFindex vault?"*
* *"What roles does a DeFindex vault have?"*
* *"Show me the withdraw-shares endpoint"*
* *"How is APY calculated in DeFindex?"*
* *"What is a dfToken?"*

***

## 2. Claude Code Skill — `defindex-api`

The **`defindex-api` skill** is a Claude Code playbook that gives the model a complete, structured reference for the DeFindex REST API. It covers authentication, every endpoint, request/response shapes, error handling, and code examples.

### What it does

When invoked, the skill provides Claude Code with:

* **Auth flow** — register → login → generate API key → use Bearer token
* **User operations** — deposit, withdraw, withdraw-shares, balance, APY, discover vaults
* **Vault administration** — roles (get/set), rebalance, lock/release/distribute fees, rescue, pause/unpause strategies, upgrade WASM
* **Factory** — create-vault, create-vault-deposit, create-vault-auto-invest
* **Submit transactions** — POST `/send` for signed XDRs
* **Rate limits** — tier configs and retry patterns

### Installation

Clone the skill repository into your Claude Code skills directory:

```bash
git clone https://github.com/defindex-io/defindex-skill ~/.claude/skills/defindex-api
```

Or install file-by-file:

```bash
mkdir -p ~/.claude/skills/defindex-api
curl -sL https://raw.githubusercontent.com/defindex-io/defindex-skill/main/SKILL.md -o ~/.claude/skills/defindex-api/SKILL.md
curl -sL https://raw.githubusercontent.com/defindex-io/defindex-skill/main/auth.md -o ~/.claude/skills/defindex-api/auth.md
curl -sL https://raw.githubusercontent.com/defindex-io/defindex-skill/main/endpoints.md -o ~/.claude/skills/defindex-api/endpoints.md
```

### How to use it

In Claude Code, type:

```
/defindex-api
```

Or reference it in your prompt:

```
Use the defindex-api skill to help me deposit 100 USDC into the mainnet vault
```

Argument hints:

| Argument      | What you get                                    |
| ------------- | ----------------------------------------------- |
| `auth`        | Registration, login, API key generation         |
| `vault`       | Vault info, balance, APY                        |
| `deposit`     | Deposit flow with code example                  |
| `withdraw`    | Withdraw and withdraw-shares                    |
| `admin`       | Roles, rebalance, fees, rescue, pause, upgrade  |
| `factory`     | Create vault, create-vault-deposit, auto-invest |
| `send`        | Submit signed XDR                               |
| `rate-limits` | Tier configs, 429 handling                      |

### Related Skills

* **`stellar-dev`** — general Stellar and Soroban development playbook.

***

## Getting Your API Key

Before you can call protected endpoints, you need an API key:

1. **Register** → <https://api.defindex.io/register>
2. **Login** → <https://api.defindex.io/login>
3. **Generate key** — from the dashboard, create your API key

See the full walkthrough: [Getting Your API Key](/api-integration-guide/guides-and-tutorials/getting-api-key)

***

## Additional Resources

* [DeFindex API Claude Code Skill](https://github.com/defindex-io/defindex-skill)
* [Full API Reference](https://api.defindex.io/docs)
* [Postman Collection](https://drive.google.com/drive/folders/1hp02ySFWFeunRCwiZ6oLCjHzcJXpWhX8?usp=drive_link)
* [Discord — developer channel](https://discord.gg/e2qAhJCBmx)


# Additional Resources

⏱️ 1 min read

### :globe\_with\_meridians: **Official Channels**

* [Defindex Website](https://www.defindex.io)
* [Discord](https://discord.gg/p6FuuSpmKG)
* [Defindex Documentation](https://docs.defindex.io/)
* [Defindex GitHub](https://github.com/paltalabs/defindex)
* [Twitter](https://x.com/paltalabs)
* [LinkedIn](https://www.linkedin.com/company/paltalabs/)
* [Blog Dev](https://dev.to/paltalabs)
* [Medium](https://medium.com/paltalabs)


# Troubleshooting

⏱️ 15 min read

This guide provides solutions and explanations for common issues encountered when using the DeFindex protocol. It covers API errors, environment setup, contract error codes, transaction failures, and frequently asked questions.

## Table of Contents

* [API](#api)
  * [API Error Reference](#api-error-reference)
  * [API Rate Limits](#rate-limiting)
* [Environment & Configuration](#environment--configuration)
  * [Step-by-Step Debugging Guide](#step-by-step-debugging-guide)
* [Smart Contracts](#smart-contracts)
  * [Error Log Debugging Example](#error-log-debbugging-example)
  * [Common Contract Errors](#common-contract-errors)
  * [Withdrawing All — Dust Left Behind](#withdrawing-all--dust-left-behind)
* [Soroban Transaction Errors](#soroban-transaction-errors)
* [Frequently Asked Questions (FAQ)](#frequently-asked-questions-faq)
* [Additional Resources](#additional-resources)

## API

### API Error Reference

HTTP status codes returned by the DeFindex API:

| Status | Error               | Common Causes                                                                                                        | Solution                                                                                                               |
| ------ | ------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| 400    | Bad Request         | Invalid address format, wrong amounts length, missing parameters, slippage out of range, contract simulation failure | Check request body against API docs; for simulation failures inspect the `errorCode` field for the contract error code |
| 401    | Unauthorized        | Missing or expired JWT / API key                                                                                     | Refresh the token or provide a valid API key                                                                           |
| 403    | Forbidden           | Insufficient vault role permissions                                                                                  | Verify API key has role                                                                                                |
| 404    | Not Found           | Vault address not found, account has no transactions                                                                 | Verify the address and network (testnet or mainnet)                                                                    |
| 409    | Conflict            | Duplicate email on registration                                                                                      | Use a different email address                                                                                          |
| 429    | Too Many Requests   | Rate limit exceeded                                                                                                  | Implement backoff (see [API Rate Limits](#rate-limiting)); check `retryAfter` in response                              |
| 503    | Service Unavailable | Stellar network unreachable, external service failure                                                                | Retry after a delay; check Stellar network status                                                                      |

### API Rate Limits

The DeFindex API uses a rate limiter with a 5 minute window. Limits are applied per API key (authenticated requests) or per IP address (unauthenticated requests).

#### Rate Limit Tiers

| Tier         | Burst Capacity | Sustained Rate |
| ------------ | -------------- | -------------- |
| Free         | 5 requests     | 1 req/s        |
| Starter      | 20 requests    | 10 req/s       |
| Professional | 100 requests   | 50 req/s       |
| Business     | 200 requests   | 100 req/s      |

Contact PaltaLabs🥑 team on [discord](https://discord.gg/MABd5JXmPN) to upgrade tier.

#### Response Headers

Every API response includes rate limit headers:

| Header                  | Description                                    |
| ----------------------- | ---------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests remaining in the current window       |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets          |

> **Tip:** Use the `GET /rate-limits/tiers` endpoint to retrieve the current rate limit configuration for your API key.

#### Handling 429 Responses

When you exceed the rate limit, the API returns **429 (Too Many Requests)**. Use exponential backoff to handle this:

```typescript
async function withRateLimit<T>(
  fn: () => Promise<T>,
  maxRetries: number = 5,
  initialDelay: number = 1000
): Promise<T> {
  let lastError: any;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error: any) {
      lastError = error;

      if (error?.statusCode === 429 || error?.error === "Too Many Requests") {
        const retryAfter = error?.retryAfter || 1;
        const delayMs = Math.max(
          retryAfter * 1000,
          initialDelay * Math.pow(2, attempt)
        );

        if (attempt < maxRetries) {
          await new Promise((resolve) => setTimeout(resolve, delayMs));
          continue;
        }
      }

      throw error;
    }
  }

  throw lastError;
}
```

Usage example:

```typescript
const depositResponse = await withRateLimit(() =>
  defindexSdk.depositToVault(vaultAddress, depositData, supportedNetwork)
);
```

## Environment & Configuration

### Step-by-Step Debugging Guide

#### 1. Check Environment Variables

* Ensure all required environment variables (e.g., `MAINNET_RPC_URL`) are set correctly.
* Example (.env):

  ```dotenv
  MAINNET_RPC_URL=your_rpc_url
  ```

#### 2. Validate Network and Contract Deployment

* Confirm you are connected to the correct network (testnet/mainnet).
* Verify the contract address is correct and the contract is deployed.

#### 3. Simulate Transactions Before Sending

* Use the SDK's simulation methods to check for errors before submitting transactions.
* Review simulation results for error codes or failed preconditions.

#### 4. Handle Transaction Failures

* If a transaction fails, inspect the error code returned.
* Refer to the tables in this page to interpret the error and apply the suggested fix.

#### 5. Check Parameter Types and Lengths

* Ensure all parameters (amounts, addresses, etc.) are of the correct type and length.
* For multi-asset vaults, input arrays must match the number of assets.

#### 6. Review Contract and SDK Versions

* Make sure you are using compatible versions of the SDK and smart contracts.

#### 7. Debug XDR manually

* Debug XDR transaction in [Stellar Lab](https://lab.stellar.org)

## Smart Contracts

### Error Log Debbugging Example

Consider the following error log from a `withdraw` transaction:

```
Event log (newest first):
  0: [Diagnostic Event] contract:CBDZYJVQJQT7QJ7ZTMGNGZ7RR3DF32LERLZ26A2HLW5FNJ4OOZCLI3OG, topics:[error, Error(Contract, #160)], data:"escalating error to VM trap from failed host function call: fail_with_error"
  1: [Diagnostic Event] contract:CBDZYJVQJQT7QJ7ZTMGNGZ7RR3DF32LERLZ26A2HLW5FNJ4OOZCLI3OG, topics:[error, Error(Contract, #160)], data:["failing with contract error", 160]
  2: [Contract Event] contract:CBDZYJVQJQT7QJ7ZTMGNGZ7RR3DF32LERLZ26A2HLW5FNJ4OOZCLI3OG, topics:[burn, GBI6SIGPSKXTBLXGSAFT2TN5DYFBHIJXKO7IGGQTR7DKO2ANWILGXIDA], data:9999563
  11: [Diagnostic Event] topics:[fn_call, CBDZYJVQJQT7QJ7ZTMGNGZ7RR3DF32LERLZ26A2HLW5FNJ4OOZCLI3OG, withdraw], data:[9999563, [10000065], GBI6SIGPSKXTBLXGSAFT2TN5DYFBHIJXKO7IGGQTR7DKO2ANWILGXIDA]

```

**Breakdown:**

1. **Identify the Error:**
   * Events `0` and `1` indicate the error: `Error(Contract, #160)`.
   * Error code: `160` (InsufficientOutputAmount)
2. **Understand the Context:**
   * Event `0`: The contract explicitly triggered an error.
   * Event `1`: Confirms the contract error code is 160.
3. **Determine Function Arguments:**
   * Event `11` shows the `withdraw` function call and its arguments:
     * `9999563`: `withdraw_shares` (number of shares to burn)
     * `[10000065]`: `min_amounts_out` (minimum expected output amount)
     * `GBI6SIGPSKXTBLXGSAFT2TN5DYFBHIJXKO7IGGQTR7DKO2ANWILGXIDA`: `to` (recipient address)
4. **Interpret the Error in Context:**
   * The `withdraw` transaction failed because the vault could not provide at least 10000065 stroops of the underlying asset when burning 9999563 shares.

### Common Contract Errors

The DeFindex contracts return specific error codes when a transaction fails. Below is a comprehensive reference grouped by contract and category.

### Vault Errors

#### Initialization Errors (100–108)

| Code | Name                        | Cause                                                                   | Solution/Tip                                                      |
| ---- | --------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------- |
| 100  | NotInitialized              | Vault has not been initialized                                          | Call the vault's `initialize` function before any other operation |
| 101  | InvalidRatio                | Asset allocation ratios are invalid (e.g., don't sum to expected total) | Ensure allocation ratios are valid and sum correctly              |
| 102  | StrategyDoesNotSupportAsset | A strategy was assigned an asset it cannot handle                       | Verify the strategy supports the asset before assigning           |
| 103  | NoAssetAllocation           | No asset allocation was provided during initialization                  | Provide at least one asset allocation                             |
| 104  | RolesIncomplete             | Required roles were not fully assigned                                  | Assign all required roles (manager, emergency manager, etc.)      |
| 105  | MetadataIncomplete          | Vault metadata (name, symbol, etc.) is missing or incomplete            | Provide all required metadata fields                              |
| 106  | MaximumFeeExceeded          | The fee set exceeds the maximum allowed                                 | Lower the fee to within the allowed range                         |
| 107  | DuplicatedAsset             | The same asset was provided more than once                              | Remove duplicate assets from the allocation                       |
| 108  | DuplicatedStrategy          | The same strategy was provided more than once                           | Remove duplicate strategies from the allocation                   |

#### Validation Errors (110–129)

| Code | Name                      | Cause                                                       | Solution/Tip                                                            |
| ---- | ------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------- |
| 110  | AmountNotAllowed          | The provided amount is not allowed (e.g., zero or negative) | Check that all amounts are positive and within allowed limits           |
| 111  | InsufficientBalance       | User does not have enough balance for the operation         | Verify the user's token balance before submitting                       |
| 112  | WrongAmountsLength        | The number of amounts does not match the number of assets   | Ensure your input arrays match the vault's asset count                  |
| 113  | WrongLockedFees           | Locked fees value is incorrect                              | Verify the locked fees parameter                                        |
| 114  | InsufficientManagedFunds  | The vault does not have enough managed funds                | Check vault TVL before attempting the operation                         |
| 115  | MissingInstructionData    | Required instruction data is missing                        | Verify all required parameters are provided for the operation           |
| 116  | UnsupportedAsset          | The provided asset is not supported by the vault            | Use only assets that the vault was configured with                      |
| 117  | InsufficientAmount        | The amount provided is too small                            | Increase the amount to meet the minimum requirement                     |
| 118  | NoOptimalAmounts          | Could not calculate optimal deposit amounts                 | This is an internal error; check vault state and asset ratios           |
| 119  | WrongInvestmentLength     | The investment allocation array length is incorrect         | Check that investment allocations match the number of strategies/assets |
| 122  | WrongAssetAddress         | The provided asset address does not match expected          | Verify the asset contract address is correct                            |
| 123  | WrongStrategiesLength     | The strategies array length does not match expected         | Ensure the strategies array length matches the vault configuration      |
| 124  | AmountOverTotalSupply     | The requested amount exceeds the total supply of shares     | Reduce the amount to at most the total supply                           |
| 125  | NoInstructions            | No instructions were provided for the operation             | Provide the required instructions for the operation                     |
| 126  | NotUpgradable             | The vault contract is not upgradable                        | This vault was deployed as non-upgradable; deploy a new vault if needed |
| 128  | UnwindMoreThanAvailable   | Attempting to unwind more than is available in the strategy | Reduce the unwind amount or check available strategy balance            |
| 129  | InsufficientFeesToRelease | Not enough accrued fees to release                          | Wait for more fees to accumulate before releasing                       |

#### Arithmetic Errors (120–127)

| Code | Name            | Cause                               | Solution/Tip                                                                |
| ---- | --------------- | ----------------------------------- | --------------------------------------------------------------------------- |
| 120  | ArithmeticError | A general arithmetic error occurred | Check for very large or very small values that may cause overflow/underflow |
| 121  | Overflow        | An arithmetic overflow occurred     | Reduce input values to prevent overflow                                     |
| 127  | Underflow       | An arithmetic underflow occurred    | Ensure values are large enough to avoid underflow                           |

#### Authorization Errors (130–134)

| Code | Name                 | Cause                                                | Solution/Tip                                                               |
| ---- | -------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------- |
| 130  | Unauthorized         | The caller is not authorized to perform this action  | Ensure the caller has the required role (manager, emergency manager, etc.) |
| 131  | RoleNotFound         | The specified role does not exist                    | Check available roles in the vault configuration                           |
| 132  | ManagerNotInQueue    | The manager address is not in the pending queue      | Add the manager to the queue first using the appropriate function          |
| 133  | SetManagerBeforeTime | Attempted to set manager before the timelock expires | Wait for the timelock period to pass before confirming the manager change  |
| 134  | QueueEmpty           | The manager queue is empty                           | Add a manager to the queue before attempting to confirm                    |

#### Strategy Operation Errors (140–144)

| Code | Name                     | Cause                                               | Solution/Tip                                                      |
| ---- | ------------------------ | --------------------------------------------------- | ----------------------------------------------------------------- |
| 140  | StrategyNotFound         | The specified strategy was not found in the vault   | Verify the strategy address is registered with the vault          |
| 141  | StrategyPausedOrNotFound | The strategy is paused or does not exist            | Check strategy status; if paused, contact the vault manager       |
| 142  | StrategyWithdrawError    | An error occurred while withdrawing from a strategy | Check the strategy's state and available balance                  |
| 143  | StrategyInvestError      | An error occurred while investing into a strategy   | Verify the investment amount and strategy availability            |
| 144  | StrategyPaused           | The strategy is currently paused                    | Wait for the strategy to be unpaused or contact the vault manager |

#### Asset Errors (150–151)

| Code | Name             | Cause                                          | Solution/Tip                                                      |
| ---- | ---------------- | ---------------------------------------------- | ----------------------------------------------------------------- |
| 150  | AssetNotFound    | The specified asset was not found in the vault | Verify the asset address is correct and registered with the vault |
| 151  | NoAssetsProvided | No assets were provided for the operation      | Provide at least one asset                                        |

#### Input Errors (160–162)

| Code | Name                     | Cause                                               | Solution/Tip                                                 |
| ---- | ------------------------ | --------------------------------------------------- | ------------------------------------------------------------ |
| 160  | InsufficientOutputAmount | The output amount is less than the required minimum | Lower your minimums or check vault liquidity before retrying |
| 161  | ExcessiveInputAmount     | The input amount exceeds allowed limits             | Reduce the input amount to within allowed limits             |
| 162  | InvalidFeeBps            | The fee basis points value is invalid               | Provide a valid fee in basis points (0–10000)                |

#### External / Swap Errors (190–202)

| Code | Name                       | Cause                                                   | Solution/Tip                                                |
| ---- | -------------------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
| 190  | LibrarySortIdenticalTokens | Soroswap library received two identical token addresses | Ensure the two tokens in the swap are different             |
| 200  | SoroswapRouterError        | An error occurred in the Soroswap router                | Check the Soroswap router status and input parameters       |
| 201  | SwapExactInError           | The exact-input swap failed                             | Verify swap parameters (token addresses, amounts, deadline) |
| 202  | SwapExactOutError          | The exact-output swap failed                            | Verify swap parameters (token addresses, amounts, deadline) |

### Factory Errors

| Code | Name                | Cause                                                        | Solution/Tip                                                     |
| ---- | ------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------- |
| 401  | NotInitialized      | The factory contract has not been initialized                | Call `initialize` on the factory before creating vaults          |
| 404  | AssetLengthMismatch | The number of assets does not match the expected length      | Ensure all asset arrays have consistent lengths                  |
| 405  | IndexDoesNotExist   | The requested vault index does not exist                     | Verify the vault index; use the factory to list available vaults |
| 406  | FeeTooHigh          | The specified fee exceeds the maximum allowed by the factory | Reduce the fee to within the factory's allowed range             |

### Strategy Errors

#### Validation Errors (401–418)

| Code | Name                      | Cause                                                        | Solution/Tip                                                          |
| ---- | ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------- |
| 401  | NotInitialized            | The strategy contract has not been initialized               | Call `initialize` on the strategy before use                          |
| 410  | NegativeNotAllowed        | A negative value was provided where only positive is allowed | Ensure all amounts are non-negative                                   |
| 411  | InvalidArgument           | An invalid argument was provided                             | Check all function arguments against expected types and ranges        |
| 412  | InsufficientBalance       | The strategy does not have enough balance                    | Verify the strategy's balance before withdrawing                      |
| 413  | UnderflowOverflow         | An arithmetic underflow or overflow occurred                 | Reduce input values to prevent arithmetic errors                      |
| 414  | ArithmeticError           | A general arithmetic error occurred                          | Check for edge cases in input values                                  |
| 415  | DivisionByZero            | A division by zero was attempted                             | Ensure divisor values are non-zero                                    |
| 416  | InvalidSharesMinted       | The calculated shares to mint are invalid (zero or negative) | Check that the deposit amount is large enough to produce valid shares |
| 417  | OnlyPositiveAmountAllowed | The amount must be strictly positive                         | Provide a positive (non-zero) amount                                  |
| 418  | NotAuthorized             | The caller is not authorized                                 | Ensure the caller has permission (typically the vault contract)       |

#### Protocol Errors (420–423)

| Code | Name                    | Cause                                            | Solution/Tip                                                        |
| ---- | ----------------------- | ------------------------------------------------ | ------------------------------------------------------------------- |
| 420  | ProtocolAddressNotFound | The external protocol address was not found      | Verify the protocol address is correctly configured in the strategy |
| 421  | DeadlineExpired         | The transaction deadline has passed              | Resubmit the transaction with a fresh deadline                      |
| 422  | ExternalError           | An error occurred in an external protocol call   | Check the external protocol's status and parameters                 |
| 423  | SoroswapPairError       | An error occurred with a Soroswap liquidity pair | Verify the pair exists and has sufficient liquidity                 |

#### Blend Strategy Errors (451–455)

| Code | Name                     | Cause                                                           | Solution/Tip                                             |
| ---- | ------------------------ | --------------------------------------------------------------- | -------------------------------------------------------- |
| 451  | AmountBelowMinDust       | The amount is below the Blend protocol's minimum dust threshold | Increase the amount above the minimum dust requirement   |
| 452  | UnderlyingAmountBelowMin | The underlying token amount is below the minimum                | Increase the underlying amount                           |
| 453  | BTokensAmountBelowMin    | The bToken amount is below the minimum                          | Increase the deposit to receive more bTokens             |
| 454  | InternalSwapError        | An error occurred during an internal swap within the strategy   | Check the swap route and liquidity availability          |
| 455  | SupplyNotFound           | The Blend supply pool was not found                             | Verify the Blend pool is correctly configured and active |

### Withdrawing All — Dust Left Behind

#### Why Does This Happen?

When withdrawing all shares, a tiny residual balance (typically 1–3 stroops per asset) may remain. This is by design, not a bug.

The vault calculates each asset's withdrawal amount using **integer floor division**:

```
withdrawal_amount = (total_asset × user_shares) / total_shares
```

Since Soroban uses integer arithmetic (no decimals), the division **truncates** any fractional stroop. In multi-asset vaults, this truncation can happen once per asset per division step, compounding to a few stroops total.

This behavior is intentional: it prevents the vault from ever paying out more than it holds.

#### How Much Dust?

Typically **1–3 stroops per asset** (1 stroop = 0.0000001 XLM or the smallest unit of a Soroban token). This is economically negligible.

#### Workaround: Two-Step Withdraw

If you need to recover the dust, use a two-step approach:

```typescript
// Step 1: Withdraw all shares
const userShares = await defindexSdk.getUserShares(vaultAddress, userAddress);
const minAmountsOut = vaultAssets.map(() => 0n); // accept any amount
await defindexSdk.withdraw(vaultAddress, {
  withdrawShares: userShares,
  minAmountsOut,
  from: userAddress,
});

// Step 2: Check for remaining dust
const remainingShares = await defindexSdk.getUserShares(vaultAddress, userAddress);
if (remainingShares > 0n) {
  // Withdraw the remaining dust
  const dustMinAmounts = vaultAssets.map(() => 0n);
  await defindexSdk.withdraw(vaultAddress, {
    withdrawShares: remainingShares,
    minAmountsOut: dustMinAmounts,
    from: userAddress,
  });
}
```

## Soroban Transaction Errors

These are Stellar/Soroban transaction-level errors that occur before or outside of contract execution. They appear in the transaction result rather than in contract event logs.

| Error                     | Cause                                 | Solution                                                                                                                                               |
| ------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `tx_failed`               | One or more operations failed         | Check operation-level result codes and contract error codes (contract error tables above)                                                              |
| `tx_bad_seq`              | Wrong sequence number                 | Re-fetch the transaction from the API; Soroban resource estimates are ledger-specific                                                                  |
| `tx_insufficient_fee`     | Fee-bump per-op rate too low          | Increase base fee ≥ inner tx fee (see [Sponsored Transactions](/api-integration-guide/guides-and-tutorials/sponsored-transactions#fee-considerations)) |
| `tx_too_late`             | Timebounds expired before submission  | Re-fetch fresh XDR and submit promptly                                                                                                                 |
| `tx_too_early`            | Timebounds not yet valid              | Wait for the valid time window or re-fetch the transaction                                                                                             |
| `tx_bad_auth`             | Missing or invalid signature          | Ensure all required signers have signed the transaction                                                                                                |
| `tx_insufficient_balance` | Source account lacks XLM for fee      | Fund the fee-paying account with enough XLM                                                                                                            |
| `tx_soroban_invalid`      | Invalid Soroban transaction structure | Check that contract invocation arguments match the expected types                                                                                      |

## Frequently Asked Questions (FAQ)

**Q: My transaction fails with `WrongAmountsLength` (112). What does this mean?**

* A: The number of amounts you provided does not match the number of assets in the vault. Double-check your input arrays.

**Q: What should I do if I get `InsufficientOutputAmount` (160)?**

* A: The vault could not provide the minimum output you requested. Try lowering your minimums or check if the vault has enough liquidity.

**Q: How do I know which error code was returned?**

* A: Inspect the transaction response object. The error code will be included in the failure reason or logs.

**Q: How can I debug contract errors further?**

* A: Review the contract's `error.rs` file for detailed error definitions. Use simulation and logging to narrow down the cause.

**Q: Are there any environment setup issues I should be aware of?**

* A: Yes. Ensure all required environment variables are set, dependencies are installed, and you are using the correct network and contract addresses.

**Q: When I withdraw a specific amount, why might I receive slightly more than requested?**

* A: Due to the fluctuating ratio between the underlying asset and vault shares (caused by Blend strategy gains) and the Soroban contract's handling of the smallest asset unit ("stroop"), the contract uses ceiling division to calculate the shares to burn. This ensures you receive *at least* the requested amount, but it can sometimes result in a slightly higher output. The contract prioritizes fulfilling the minimum withdrawal amount.

**Q: When I deposit a specific amount, will I always receive the same number of shares?**

* A: No. Similar to withdrawals, the number of shares you receive when depositing a fixed amount of the underlying asset can vary. This is because the ratio between the asset and shares changes constantly. A deposit made moments apart can yield slightly different share amounts.

**Q: I withdrew all my shares but still have a tiny balance (1–3 stroops). Is this a bug?**

* A: No. This is expected behavior due to integer floor division in the contract. See [Withdrawing All — Dust Left Behind](#withdrawing-all--dust-left-behind) for a full explanation and workaround.

## Additional Resources

* [DeFindex Protocol Documentation](https://github.com/paltalabs)
* [Smart Contract Error Codes](https://github.com/defindex-io/docs/blob/main/contracts/vault/src/error.rs)

If you encounter an issue not covered here, please open an issue on the project's GitHub repository.


# Smart Contracts Development

⏱️ 1 min read

Welcome to the DeFindex Smart Contract Development Guide! This guide will help you understand how to create a strategy that uses your protocol.

## What is a DeFindex Vault?

DeFindex vaults let users deposit tokens into a pooled account that automatically executes diversified yield strategies across DeFi protocols. The vault issues shares representing each depositor’s stake, rebalances holdings to capture returns and reduce risk, and provides simple actions like deposit, withdraw, and view balance — all without users having to manage multiple protocols themselves.

## What is a DeFindex Strategy?

A DeFindex Strategy is a smart contract that implements a specific investment logic for a DeFi protocol. In other words, it is the connection to a DeFi protocol. Also, It allows users to automate complex DeFi operations and optimize their yield through a single interface.

It only needs to comply with the strategy interface, which can be found in [github](https://github.com/defindex-io/docs/blob/main/contracts/strategies/core/src/lib.rs). Once it complies with this interface, it can be used by vaults.

## Resources

1. Review the [Strategy Contract](/advanced-documentation/10-whitepaper/04-contracts/02-strategy-contract) on whitepaper docs
2. Check out our [Strategies](https://github.com/defindex-io/docs/blob/main/public/mainnet.contracts.json)
3. Join our [developer community](https://discord.gg/ftPKMPm38f) for support


# What is a strategy?

⏱️ 1 min read

A strategy is a set of **steps** to be followed to execute an investment in one or several protocols. This could be as simple as just holding assets, or as complex as farming and auto-compound rewards automatically, leverage lending or leveraged farming strategies for borrowing and lending markets like Blend.Capital.\
\
Example of an Autocompound Strategy on Blend Capital:

> When investing USDC in Blend, Blend gives interest by lending those USDC, plus a reward in BLND token, so the strategy will follow these steps:
>
> 1. Lend USDC on Blend
> 2. Wait a little.
> 3. Claim BLND rewards.
> 4. Swap BLND for USDC.
> 5. Lend those USDC after the swap.
>
> This strategy converts the regular APR given by Blend into APY.


# What is Blend Capital?

⏱️ 1 min read

## What is Blend? <a href="#what-is-blend" id="what-is-blend"></a>

Blend is a DeFi (decentralized finance) protocol that allows any entity to create or utilize an immutable lending market that fits its needs.

<figure><img src="/files/CR3TxcOf1cOL0nByiMcr" alt=""><figcaption></figcaption></figure>

#### What's a BLND token? <a href="#whats-a-blnd-token" id="whats-a-blnd-token"></a>

BLND is Blend's platform token. It is issued to protocol users and can be deposited in the backstop module to insure lending pools.


# Strategies APY

⏱️ 2 min read

Each **strategy** will give a different **APY (Annual Percentage Yield)** depending on what the strategy does — for example lending, swapping, farming or even leverage lending, etc...

To calculate APY, you might need some extra data, like:

* The value of the harvested token (the token the strategy earns),
* The APY of other protocols the strategy interacts with,
* The emission rate of the harvested token (how fast it’s being distributed),
* And any other rewards or fees involved.

But instead of tracking all of that manually, the [**Strategy Crate**](https://github.com/defindex-io/docs/blob/main/contracts/strategies/core/src/event.rs#L32) makes things easier. It emits a `HarvestEvent` every time the strategy runs its logic (in the `harvest()` function). This event includes a very important value: the **Price Per Share** (`price_per_share` or **PPS**).

```rust
pub struct HarvestEvent {
    pub amount: i128,
    pub from: Address,
    pub price_per_share: i128,
}
```

#### 🪙 What is Price Per Share (PPS)?

Every time someone deposits into a strategy, they receive **shares**. As the strategy earns yield, the value of each share increases.

The **Price Per Share (PPS)** tells you how much one share is worth. You don’t need to track individual profits — just track the PPS over time.

#### 📅 How to Calculate APY

To calculate the APY, we compare the **PPS now** with the **PPS in the past** (e.g., 1 day, 7 days, or 30 days ago).

Let:

* PPS now​: the latest price per share
* PPS then: the price per share at a past time
* Δt: number of days between the two points

#### 🧮 Step 1: Calculate ROI (Return on Investment)

$$
\text{ROI} = \frac{\text{PPS}*\text{now}}{\text{PPS}*\text{then}} - 1
$$

This gives the percentage growth over that time period.

#### 📈 Step 2: Annualize It to Get the APY

$$
\text{APY} = \left(1 + \text{ROI} \right)^{\left(\frac{365.2425}{\Delta t}\right)} - 1
$$

Here, `365.2425` is the average number of days in a year (to account for leap years).

#### ✅ Example

* PPS now = `1.10`
* PPS 30 days ago = `1.00`
* Days = 30

$$
\text{ROI} = \frac{1.10}{1.00} - 1 = 0.10
$$

$$
\text{APY} = (1 + 0.10)^{\left(\frac{365.2425}{30}\right)} - 1 \approx 2.138 - 1 = \mathbf{113.8%}
$$

This means if the strategy keeps performing the same way, the estimated yearly return is **113.8%**.


# Building the Blend Strategy

⏱️ 5 min read

## **Introduction**

Welcome to this guide on implementing the **Blend Strategy** for DeFindex. This tutorial is designed to provide a comprehensive walkthrough of the Blend Strategy smart contract, which integrates with the **Blend Protocol**, a lending and borrowing platform.

*Note: This guide is for understanding how to create a strategy. The actual strategy may differ from this example. Do not use this code as is, but rather use it as a reference to create your own strategy.*

The Blend Strategy implements the `DeFindexStrategyTrait` from the core strategy module, providing a standardized interface for interacting with the DeFindex vault while managing positions in the Blend Protocol.

***

## **Why a Strategy?**

A strategy in DeFindex acts as a **proxy** between the Vault and an external protocol. This design is essential because:

1. **Protocol-specific Authorization**: The Vault cannot directly authorize interactions with external protocols like Blend.
2. **Position Management**: The Strategy holds positions for each interacting vault and tracks them using shares.
3. **Standardized Outputs**: The Strategy always converts internal shares to **underlying asset balances** for the Vault to ensure consistency.

***

### **Getting Started**

To implement the Blend Strategy, you need to be familiar with **Soroban smart contract development** and **Rust**. If you're new to Soroban, start with the official [Soroban Getting Started Guide](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup).

#### **Cargo.toml**

Here's the `Cargo.toml` for the Blend Strategy:

```toml
[package]
name = "blend_strategy"
version = "0.1.0"
authors = ["coderipper <joaquin@paltalabs.io>"]
license = "GPL-3.0"
edition = "2021"
publish = false
repository = "https://github.com/paltalabs/defindex"

[lib]
crate-type = ["cdylib"]

[dependencies]
soroban-sdk = "22.0.0-rc.2.1"
defindex-strategy-core = "0.2.0"
soroban-fixed-point-math = "1.3.0"

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
sep-40-oracle = { version = "1.2.0", features = ["testutils"] }
sep-41-token = { version = "1.2.0", features = ["testutils"] }
```

***

### **Project Setup**

Below, we'll break the Blend Strategy into its components, explaining each part with the corresponding code.

### **1. lib.rs: Core Logic**

The **Blend Strategy** implements the `DeFindexStrategyTrait` and provides all core functionality, including deposits, withdrawals, rewards harvesting, and balance tracking.

**Code:**

```rust
#![no_std]
use blend_pool::perform_reinvest;
use constants::{MIN_DUST, SCALAR_9};
use reserves::StrategyReserves;
use soroban_sdk::{contract, contractimpl, token::TokenClient, Address, Env, IntoVal, String, Val, Vec};

mod blend_pool;
mod constants;
mod reserves;
mod soroswap;
mod storage;

use storage::{extend_instance_ttl, has_config, Config};

pub use defindex_strategy_core::{
	DeFindexStrategyTrait,
	StrategyError,
	event
};

pub fn check_nonnegative_amount(amount: i128) -> Result<(), StrategyError> {
	if amount < 0 {
		Err(StrategyError::NegativeNotAllowed)
	} else {
		Ok(())
	}
}

fn check_initialized(e: &Env) -> Result<(), StrategyError> {
	if has_config(e) {
		Ok(())
	} else {
		Err(StrategyError::NotInitialized)
	}
}

const STRATEGY_NAME: &str = "BlendStrategy";

#[contract]
struct BlendStrategy;

#[contractimpl]
impl DeFindexStrategyTrait for BlendStrategy {
	fn __constructor(
		e: Env,
		asset: Address,
		init_args: Vec<Val>,
	) {
		// Getting init args from the Vec<Val>
		let blend_pool_address: Address = init_args.get(0).ok_or(StrategyError::InvalidArgument).unwrap().into_val(&e);
		let reserve_id: u32 = init_args.get(1).ok_or(StrategyError::InvalidArgument).unwrap().into_val(&e);
		let blend_token: Address = init_args.get(2).ok_or(StrategyError::InvalidArgument).unwrap().into_val(&e);
		let soroswap_router: Address = init_args.get(3).ok_or(StrategyError::InvalidArgument).unwrap().into_val(&e);

		let config = Config {
			asset: asset.clone(),
			pool: blend_pool_address,
			reserve_id,
			blend_token,
			router: soroswap_router,
		};

		// Storing the configuration in Config
		storage::set_config(&e, config);
	}

	// It returns the underlying asset
	fn asset(e: Env) -> Result<Address, StrategyError> {
		check_initialized(&e)?;
		extend_instance_ttl(&e);

		Ok(storage::get_config(&e).asset)
	}

	fn deposit(
		e: Env,
		amount: i128,
		from: Address,
	) -> Result<i128, StrategyError> {
		check_initialized(&e)?;
		check_nonnegative_amount(amount)?;
		extend_instance_ttl(&e);
		from.require_auth();

		if amount < MIN_DUST {
			return Err(StrategyError::AmountBelowMinDust);
		}

		let config = storage::get_config(&e);
		// It claims any available BLND tokens and if its greater than the threshold it swaps them to the underlying asset and reinvest into the pool
		blend_pool::claim(&e, &e.current_contract_address(), &config);
		perform_reinvest(&e, &config)?;

		let reserves = storage::get_strategy_reserves(&e);

		// transfer tokens from the vault to the strategy contract
		TokenClient::new(&e, &config.asset).transfer(&from, &e.current_contract_address(), &amount);

		let b_tokens_minted = blend_pool::supply(&e, &from, &amount, &config);

		// Keeping track of the total deposited amount and the total bTokens owned by the strategy depositors
		let vault_shares = reserves::deposit(&e, reserves.clone(), &from, amount, b_tokens_minted);

		// Getting the underlying asset balance from the shares holded by the "from" address
		let underlying_balance = shares_to_underlying(vault_shares, reserves);

		event::emit_deposit(&e, String::from_str(&e, STRATEGY_NAME), amount, from);
		// It is required by the vault that the strategy returns the balance of the "from" address to keep track of the status and health of the strategy
		Ok(underlying_balance)
	}

	fn harvest(e: Env, from: Address, data: Option<Bytes>) -> Result<(), StrategyError> {
		check_initialized(&e)?;
		extend_instance_ttl(&e);

		let config = storage::get_config(&e);

		// Claims BLND tokens
		let harvested_blend = blend_pool::claim(&e, &e.current_contract_address(), &config);
		// If the threshold is greater than X it will swap and reinvest the claimed BLND tokens
		perform_reinvest(&e, &config)?;

		event::emit_harvest(&e, String::from_str(&e, STRATEGY_NAME), harvested_blend, from);

		Ok(())
	}

	fn withdraw(
		e: Env,
		amount: i128,
		from: Address,
		to: Address,
	) -> Result<i128, StrategyError> {
		check_initialized(&e)?;
		check_nonnegative_amount(amount)?;
		extend_instance_ttl(&e);
		from.require_auth();

		// protect against rouding of reserve_vault::update_rate, as small amounts
		// can cause incorrect b_rate calculations due to the pool rounding
		if amount < MIN_DUST {
			return Err(StrategyError::AmountBelowMinDust)
		}

		let reserves = storage::get_strategy_reserves(&e);

		let config = storage::get_config(&e);

		// It withdraws the underlying asset from the blend pool
		let (tokens_withdrawn, b_tokens_burnt) = blend_pool::withdraw(&e, &to, &amount, &config);

		// It updates the vault shares and withdrawed amounts
		let vault_shares = reserves::withdraw(&e, reserves.clone(), &from, tokens_withdrawn, b_tokens_burnt);

		// Getting the underlying asset balance from the shares holded by the "from" address
		let underlying_balance = shares_to_underlying(vault_shares, reserves);

		event::emit_withdraw(&e, String::from_str(&e, STRATEGY_NAME), amount, from);

		Ok(underlying_balance)
	}

	fn balance(
		e: Env,
		from: Address,
	) -> Result<i128, StrategyError> {
		check_initialized(&e)?;
		extend_instance_ttl(&e);

		// Get the vault's shares
		let vault_shares = storage::get_vault_shares(&e, &from);

		// Get the strategy's total shares and bTokens
		let reserves = storage::get_strategy_reserves(&e);
		let underlying_balance = shares_to_underlying(vault_shares, reserves);

		Ok(underlying_balance)
	}
}

fn shares_to_underlying(shares: i128, reserves: StrategyReserves) -> i128 {
	let total_shares = reserves.total_shares;
	let total_b_tokens = reserves.total_b_tokens;

	if total_shares == 0 || total_b_tokens == 0 {
		// No shares or bTokens in the strategy
		return 0i128;
	}

	// Calculate the bTokens corresponding to the vault's shares
	let vault_b_tokens = (shares * total_b_tokens) / total_shares;

	// Use the b_rate to convert bTokens to underlying assets
	(vault_b_tokens * reserves.b_rate) / SCALAR_9
}
```

***

### **2. Storage Module**

The `storage.rs` file is fundamental to the Blend Strategy as it handles the configuration, reserves, and vault position data. This module is the first introduced into the contract, as it's initialized by the constructor to store the strategy's configuration.

#### **Purpose**

1. **Configuration Management**:
   * Stores essential information like the underlying asset, Blend Pool address, and reserve ID.
   * Used to retrieve the configuration during operations like deposits and withdrawals.
2. **Vault Position Tracking**:
   * Tracks the number of shares each vault or user owns.
   * Shares represent a user's proportionate stake in the strategy's reserves.
3. **Reserves Management**:
   * Maintains the total shares, bTokens, and bRate (exchange rate) for the strategy.

**Code Walkthrough**

Here's the complete `storage.rs` file with detailed explanations:

```rust
use soroban_sdk::{contracttype, Address, Env};
use crate::reserves::StrategyReserves;

#[contracttype]
pub struct Config {
	pub asset: Address,         // The underlying asset managed by the strategy
	pub pool: Address,          // Blend Pool address
	pub reserve_id: u32,        // Reserve ID for the Blend Pool
	pub blend_token: Address,   // Blend token address for rewards
	pub router: Address,        // Soroswap Router address for swaps
}

#[derive(Clone)]
#[contracttype]
pub enum DataKey {
	Config,             // Key for storing the strategy configuration
	Reserves,           // Key for storing strategy reserves
	VaultPos(Address),  // Key for storing vault positions (per user or vault)
}

pub const DAY_IN_LEDGERS: u32 = 17280; // Number of ledgers in a day
pub const INSTANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; // TTL extension for 30 days
pub const INSTANCE_LIFETIME_THRESHOLD: u32 = INSTANCE_BUMP_AMOUNT - DAY_IN_LEDGERS;

const LEDGER_BUMP: u32 = 120 * DAY_IN_LEDGERS; // TTL bump for persistent storage
const LEDGER_THRESHOLD: u32 = LEDGER_BUMP - 20 * DAY_IN_LEDGERS;

pub fn extend_instance_ttl(e: &Env) {
	e.storage()
		.instance()
		.extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT);
}

// Config Management
pub fn set_config(e: &Env, config: Config) {
	e.storage().instance().set(&DataKey::Config, &config);
}

pub fn get_config(e: &Env) -> Config {
	e.storage().instance().get(&DataKey::Config).unwrap()
}

pub fn has_config(e: &Env) -> bool {
	e.storage().instance().has(&DataKey::Config)
}

// Vault Position Management
/// Set the number of shares a user or vault owns.
pub fn set_vault_shares(e: &Env, address: &Address, shares: i128) {
	let key = DataKey::VaultPos(address.clone());
	e.storage().persistent().set::<DataKey, i128>(&key, &shares);
	e.storage()
		.persistent()
		.extend_ttl(&key, LEDGER_THRESHOLD, LEDGER_BUMP);
}

/// Get the number of shares a user or vault owns.
pub fn get_vault_shares(e: &Env, address: &Address) -> i128 {
	let result = e.storage().persistent().get::<DataKey, i128>(&DataKey::VaultPos(address.clone()));
	match result {
		Some(shares) => {
			e.storage()
				.persistent()
				.extend_ttl(&DataKey::VaultPos(address.clone()), LEDGER_THRESHOLD, LEDGER_BUMP);
			shares
		}
		None => 0,
	}
}

// Reserves Management
pub fn set_strategy_reserves(e: &Env, new_reserves: StrategyReserves) {
	e.storage().instance().set(&DataKey::Reserves, &new_reserves);
}

pub fn get_strategy_reserves(e: &Env) -> StrategyReserves {
	e.storage().instance().get(&DataKey::Reserves).unwrap_or(
		StrategyReserves {
			total_shares: 0,
			total_b_tokens: 0,
			b_rate: 0,
		}
	)
}
```

**Key Points**

1. **Configuration**:
   * The Config struct holds all necessary parameters for the strategy.
   * The constructor uses set\_config to initialize these values.
2. **Vault Positions**:
   * Shares are stored with the VaultPos key and are specific to each vault or user.
   * Precision is managed with 7 decimal places to ensure accuracy.
3. **Reserves**:
   * Reserves track the strategy's overall state, including total shares, bTokens, and the current exchange rate (bRate).
   * If reserves are missing, default values are used.

***

### **3. Blend Pool Interactions**

The `blend_pool.rs` file is responsible for managing all interactions with the Blend Pool smart contract. This includes supplying and withdrawing assets, claiming rewards, and reinvesting harvested tokens.

#### **Purpose**

1. **Supply and Withdraw Assets**:
   * Handles depositing and withdrawing the underlying asset to/from the Blend Pool.
   * Tracks `bTokens` received or burned during these operations.
2. **Claim Rewards**:
   * Retrieves rewards (e.g., BLND tokens) accrued in the Blend Pool.
3. **Reinvest Rewards**:
   * Converts rewards into the underlying asset and reinvests them into the Blend Pool.

#### **Code Walkthrough**

```rust
use defindex_strategy_core::StrategyError;
use soroban_sdk::{
	auth::{ContractContext, InvokerContractAuthEntry, SubContractInvocation},
	panic_with_error, token::TokenClient, vec, Address, Env, IntoVal, Symbol, Vec,
};

use crate::{
	constants::REWARD_THRESHOLD,
	reserves,
	soroswap::internal_swap_exact_tokens_for_tokens,
	storage::{self, Config},
};

// Importing the Contract WASM file from Blend Pool
soroban_sdk::contractimport!(
	file = "../external_wasms/blend/blend_pool.wasm"
);
pub type BlendPoolClient<'a> = Client<'a>;

// Define the RequestType enum with explicit u32 values
#[derive(Clone, PartialEq)]
#[repr(u32)]
pub enum RequestType {
	Supply = 0,
	Withdraw = 1,
}

impl RequestType {
	fn to_u32(self) -> u32 {
		self as u32
	}
}

// Deposits the underlying asset into the Blend Pool and returns the number of bTokens minted.
pub fn supply(e: &Env, from: &Address, amount: &i128, config: &Config) -> i128 {
	let pool_client = BlendPoolClient::new(e, &config.pool);

	// Get deposit amount pre-supply used to then calculate the bTokens received
	let pre_supply = pool_client
		.get_positions(&e.current_contract_address())
		.supply
		.get(config.reserve_id)
		.unwrap_or(0);

	//  Creating the request for the Blend Pool (this can be checked in Blend Documentation)
	let requests: Vec<Request> = vec![&e, Request {
		address: config.asset.clone(),
		amount: amount.clone(),
		request_type: RequestType::Supply.to_u32(),
	}];

	e.authorize_as_current_contract(vec![
		&e,
		InvokerContractAuthEntry::Contract(SubContractInvocation {
			context: ContractContext {
				contract: config.asset.clone(),
				fn_name: Symbol::new(&e, "transfer"),
				args: (
					e.current_contract_address(),
					config.pool.clone(),
					amount.clone()).into_val(e),
			},
			sub_invocations: vec![&e],
		}),
	]);

	let new_positions = pool_client.submit(
		&e.current_contract_address(),
		&e.current_contract_address(),
		&from,
		&requests
	);

	// Calculate the amount of bTokens received
	let b_tokens_amount = new_positions.supply.get_unchecked(config.reserve_id) - pre_supply;

	b_tokens_amount
}

// Withdraws the underlying asset from the Blend Pool and calculates the actual amount received.
pub fn withdraw(e: &Env, to: &Address, amount: &i128, config: &Config) -> (i128, i128) {
	let pool_client = BlendPoolClient::new(e, &config.pool);

	// Get withdraw amount pre-withdraw used to then calculate the bTokens burned
	let pre_withdraw_btokens = pool_client
		.get_positions(&e.current_contract_address())
		.supply
		.get(config.reserve_id)
		.unwrap_or_else(|| panic_with_error!(e, StrategyError::InsufficientBalance));

	// Get balance pre-withdraw, as the pool can modify the withdrawal amount
	let pre_withdrawal_balance = TokenClient::new(&e, &config.asset).balance(&to);

	let requests: Vec<Request> = vec![&e, Request {
		address: config.asset.clone(),
		amount: amount.clone(),
		request_type: RequestType::Withdraw.to_u32(),
	}];

	let new_positions = pool_client.submit(
		&e.current_contract_address(),
		&e.current_contract_address(),
		&to,
		&requests
	);

	// Calculate the amount of tokens withdrawn and bTokens burnt
	let post_withdrawal_balance = TokenClient::new(&e, &config.asset).balance(&to);

	let real_amount = post_withdrawal_balance - pre_withdrawal_balance;

	// Calculates the amount of bToken burned
	let b_tokens_amount = pre_withdraw_btokens - new_positions.supply.get(config.reserve_id).unwrap_or(0);

	(real_amount, b_tokens_amount)
}

// Claims rewards (e.g., BLND tokens) from the Blend Pool.
pub fn claim(e: &Env, from: &Address, config: &Config) -> i128 {
	let pool_client = BlendPoolClient::new(e, &config.pool);
	pool_client.claim(from, &vec![&e, 0u32, 1u32, 2u32, 3u32], from)
}

// Converts rewards into the underlying asset and reinvests them into the Blend Pool.
pub fn perform_reinvest(e: &Env, config: &Config) -> Result<bool, StrategyError> {
	// Getting the BLND Token balance to check if it needs to reinvest
	let blnd_balance = TokenClient::new(e, &config.blend_token).balance(&e.current_contract_address());

	// If balance does not exceed threshold, skip reinvest
	if blnd_balance < REWARD_THRESHOLD {
		return Ok(false);
	}

	// Swap BLND to the underlying asset
	let mut swap_path: Vec<Address> = vec![&e];
	swap_path.push_back(config.blend_token.clone());
	swap_path.push_back(config.asset.clone());

	let deadline = e.ledger().timestamp() + 600;

	// Swaps the BLND token into the underlying asset eg. USDC
	let swapped_amounts = internal_swap_exact_tokens_for_tokens(
		e,
		&blnd_balance,
		&0i128,
		swap_path,
		&e.current_contract_address(),
		&deadline,
		config,
	)?;

	let amount_out: i128 = swapped_amounts
		.get(1)
		.ok_or(StrategyError::InvalidArgument)?
		.into_val(e);

	// Supplying underlying asset into blend pool
	let b_tokens_minted = supply(&e, &e.current_contract_address(), &amount_out, &config);

	let reserves = storage::get_strategy_reserves(&e);
	reserves::harvest(&e, reserves, amount_out, b_tokens_minted);

	Ok(true)
}
```

**Key Points**

1. **Supply and Withdraw**:
   * Use RequestType to define the operation.
   * Ensure accurate tracking of bTokens for precise position management.
2. **Claim**:
   * Hardcoded reserve token IDs are used as placeholders for now
3. **Reinvest**:
   * Converts rewards to maximize returns.
   * Leverages Soroswap to swap BLND for the underlying asset.

***

### **4. Token Swapping with Soroswap**

This module handles token swaps, converting rewards (e.g., BLND tokens) into the underlying asset during the **harvest** process to reinvest them into the Blend Pool.

#### **Code Walkthrough**

```rust
use defindex_strategy_core::StrategyError;
use soroban_sdk::{
	auth::{ContractContext, InvokerContractAuthEntry, SubContractInvocation},
	vec, Address, Env, IntoVal, Symbol, Val, Vec,
};

use crate::storage::Config;

// Handles swaps using the Soroswap Router
pub fn internal_swap_exact_tokens_for_tokens(
	e: &Env,
	amount_in: &i128,
	amount_out_min: &i128,
	path: Vec<Address>,
	to: &Address,
	deadline: &u64,
	config: &Config,
) -> Result<Vec<i128>, StrategyError> {

	let mut swap_args: Vec<Val> = vec![&e];
	swap_args.push_back(amount_in.into_val(e));
	swap_args.push_back(amount_out_min.into_val(e));
	swap_args.push_back(path.into_val(e));
	swap_args.push_back(to.to_val());
	swap_args.push_back(deadline.into_val(e));

	let pair_address: Address = e.invoke_contract(
		&config.router,
		&Symbol::new(&e, "router_pair_for"),
		vec![&e, path.get(0).unwrap().into_val(e), path.get(1).unwrap().into_val(e)],
	);

	e.authorize_as_current_contract(vec![
		&e,
		InvokerContractAuthEntry::Contract(SubContractInvocation {
			context: ContractContext {
				contract: path.get(0).unwrap().clone(),
				fn_name: Symbol::new(&e, "transfer"),
				args: (
					e.current_contract_address(),
					pair_address,
					amount_in.clone(),
				).into_val(e),
			},
			sub_invocations: vec![&e],
		}),
	]);

	e.invoke_contract(
		&config.router,
		&Symbol::new(&e, "swap_exact_tokens_for_tokens"),
		swap_args,
	)
}
```

**Key Points**

* Swaps BLND tokens into the underlying asset during harvest.
* Uses the Soroswap Router contract.

**References**

* [DeFindex GitHub Repository](https://github.com/paltalabs/defindex/)
* [Script3 "Fee Vault" Contract](https://github.com/script3/fee-vault/)
* [DeFindex Whitepaper - Strategy Section](https://docs.defindex.io/10-whitepaper/02-contracts/02-strategy-contract.md)

The Blend Strategy for DeFindex showcases the power of modular architecture in decentralized finance. By acting as a proxy between the Vault and external protocols, the strategy ensures seamless integration while maintaining flexibility and security.

This guide provides a complete walkthrough for implementing the Blend Strategy, covering:

* Initialization and storage management
* Interactions with the Blend Pool
* Reinvestment logic using Soroswap

With this foundation, you can build custom strategies tailored to specific protocols and assets, expanding DeFindex's utility. Remember to follow best practices, rigorously test your strategies, and monitor deployments to ensure optimal performance.

If you have questions or need help, join the conversation on our [DeFindex Discord](https://discord.gg/CUC26qUTw7) or connect with us on the [PaltaLabs Discord](https://discord.com/invite/4F4pWFkkyZ). We're here to help you build and innovate. Happy coding! 🚀


# WhitePaper

⏱️ 2 min read

This protocol has been designed and developed by PaltaLabs

Francisco Catrileo | Joaquin Soza | Esteban Iglesias

### Abstract

DeFindex is a suite of smart contracts designed to facilitate interaction with various Decentralized Finance (DeFi) protocols on the Stellar/Soroban Blockchain. It enables users to create custom strategies, allowing investments to be distributed across multiple DeFi protocols in a streamlined manner. The protocol serves two primary audiences:

1. **Wallet Users (including Web2 users):** DeFindex provides a simplified interface that wallet developers can integrate into their platforms, enabling users to access DeFi investment services effortlessly.
2. **Expert Users:** For experienced investors, DeFindex offers an efficient way to diversify investments without the complexity of building and managing their own strategies.

Inspired by projects such as Yearn, Set Protocol, Compound, and YieldYak, DeFindex adapts their core principles to the Stellar ecosystem.

The protocol comprises three main components:

1. **Factory:** A smart contract responsible for creating new Vaults.
2. **Vaults:** The primary contracts through which users interact, enabling deposits, withdrawals, and position adjustments.
3. **Strategies:** Contracts that allocate Vault assets across various DeFi protocols.

To ensure robust functionality and security, DeFindex implements a role-based management system:

* **Manager:** Oversees strategies and the assets within Vaults.
* **Emergency Manager:** Handles rescues.
* **Fee Receiver:** Collects and manages strategy-related fees.

By combining simplicity for newcomers with advanced features for seasoned users, DeFindex aims to make DeFi more accessible and efficient on the Stellar Blockchain.

### Introduction

* [Introduction](/advanced-documentation/10-whitepaper/01-introduction)
* [Core Concepts](/advanced-documentation/10-whitepaper/02-core-concepts)

### The DeFindex Approach

* [Overview](/advanced-documentation/10-whitepaper/03-the-defindex-approach)
* [Design Decisions](/advanced-documentation/10-whitepaper/03-the-defindex-approach)

### Contracts

* [Vault Contract](/advanced-documentation/10-whitepaper/04-contracts/01-vault-contract)
* [Strategy Contract](/advanced-documentation/10-whitepaper/04-contracts/02-strategy-contract)
* [Zapper Contract](/advanced-documentation/10-whitepaper/04-contracts/02-zapper-contract)

### State of the Art

* [State of the Art](/advanced-documentation/10-whitepaper/07-state-of-the-art)

### Appendix

* [Appendix](/advanced-documentation/10-whitepaper/08-appendix)


# Introduction

⏱️ 2 min read

With the introduction of Protocol 20 of Stellar, new Smart Contract based Descentralized Protocols have arised in the Stellar Blockchain. Automated Market Makers like **Soroswap.Finance**, or Lending and Borrowing protocols like **Blend Capital** are just the beggining of a new set of DeFi Protocols.

These protocols allow from simple to complex investment stragtegies. The most simple strategy can be holding assets, other simple strategies can be investing in a Soroswap.Finance AMM Constant Product Liquidity Pool. Also, a more complex strategy can be lending USDC in a Blend Market, harvest the BLND reward to later swap those BLND harvested to USDC and reinvest them in the lending pool.

However, this is very time consuming and requires a lot of knowledge about the protocols and strategies. From one side, a crypto investor would need to spend a lot of time learning about the protocols and strategies, and then, would need to manually manage their investments. From another side, it's just too complex for non-expert users, or even for wallets users that prefer a very simple interface.

That's why DeFindex was created. DeFindex is a protocol where users can define how their **investments are distributed** among **multiple DeFi protocols and strategies**. The definition of this distribution and its rules involves the creation of a vault, which works like an index fund or an ETF, where the underlying assets are invested in DeFi protocols.

This is particularly useful for wallets users (even web2 users) that prefer a very simple interface, where wallet builders can integrate DeFindex in their wallets, to offer a DeFi investing service to their users. Also, for expert users that prefer to have a very easy way to diversify their investments among multiple protocols and strategies.

We want to make DeFi investing easy, simple and accessible for everyone!

In this whitepaper we will explain core concepts to understand DeFindex. Then, design decisions, how it works, which contracts are involved, the current state of the art.


# Core Concepts

⏱️ 3 min read

These are some concepts to understand the DeFindex protocol:

* **Vault:** A **DeFindex Vault** is a smart contract that **defines a distribution** of an investment into **one or more strategies**. It works like an index fund or an ETF, where the underlying assets are invested in DeFi protocols. In order to be exposed to DeFi strategies, a user just needs to deposit assets into the Vault. Then, the Vault will take care of automatically investing those assets in the defined strategies.
* **Strategy:** A strategy is a set of **steps** to be followed to execute an investment in one or several protocols. This could be as simple as just holding assets, or as complex as farming and auto-compound rewards automatically, leverage lending or leveraged farming strategies for borrowing and lending markets like Blend.Capital.

  Example of Leverage Lending:

  ```
  ```

1. Deposit 100% in Blend, 2) take a 50% loan in XLM, 3) Swap XLM for USDC, 4) Deposit more USDC. Then harvest BLND rewards. \`\`\`

* **Rebalancing:** Rebalancing involves changing the allocation of funds between strategies of a DeFindex Vault. For example, a vault with 50% in two strategies could change to 80% and 20%, respectively.
* **Shares**, **dfTokens**, or **DeFindex Vault Tokens**: Shares are fungible tokens issued to users upon depositing assets into a specific DeFindex Vault. They represent a proportional share of the total assets managed by the DeFindex Vault. Users can burn shares to withdraw their underlying assets, which would require to unwind postions in current strategies.
* **Automated Market Makers (AMM):** AMMs are decentralized exchanges that use algorithms to set prices and facilitate trading. In DeFindex, AMM LP tokens represent liquidity provision in various trading pairs. Users can earn yields from trading fees and token incentives by holding or staking these LP tokens.

  Example: [Soroswap.Finance](https://soroswap.finance).
* **Lending Platforms:** Lending platforms allow users to deposit assets in exchange for earning interest. DeFindex incorporates lending strategies to diversify asset allocation and maximize returns. Assets deposited in DeFindex can be lent out to earn additional yield.

  Example: [Blend Capital](https://blend.capital).
* **IDLE Assets:** DeFindex maintains its balance between invested and idle assets. Idle assets are kept liquid to ensure users can easily withdraw funds without disrupting ongoing investments. Also, if a strategy is unhealthy, Emergency Managers can unwind risky positions and move all funds into idle assets in order to protect investors from unhealthy or risky strategies.
* **Rescue funds:** Emergency Managers can unwind risky positions and move all funds into idle assets in order to protect investors from unhealthy or risky strategies.
* **Price Per Share (PPS):** Price Per Share (PPS) is a key metric that determines the value of one share (dfTokens) relative to the total assets managed by a DeFindex Vault.
* **Autocompounding:** Autocompounding is the process of reinvesting rewards automatically into the same strategy. This allows for changing from APR to APY! This allows for continuous growth of the investment without the need for manual intervention. Let's see an example:

  If a user deposits 100 USDC in a strategy with 30% APR, after one year the user will have 130 USDC. However, if the user reinvests the rewards every day, she will get more! Let's see how this works:

  1. A 30% APR is 0.082191781% per day. Because daily return is APR/365 = 0.082191781%
  2. If the user reinvests the rewards every day, after one year the user will have \~135 USDC. Because $(1 + 0.00082191781)^{365} = 1.349692488$ Meaning that instead of 30% APR, the user will have 34.96% APY.

  This shows how powerful the autocompounding is!


# The DeFindex Approach

⏱️ 1 min read

In this section we will describe the approach we took to build DeFindex. Why we chose the design decisions we made and how we tried to improve the current state of the art, leveraging the Stellar ecosystem.

## Design Decisions

We have decided to do:

### Multi Assets Vaults.

We think is important to offer diversified Vaults to our users, not only in the platforms or strategies they will be interacting, but also in the assets they will be exposed to.

### AMM Liquidity Pool Support

When supporting a AMM Liquity Pool, the underlying asset will be considered as the **AMM LP token**, for example, for a Soroswap USDC-XLM liquidity pool, the underlying asset will be the Soroswap-USDC-XLM-LP token and not the USDC or XLM tokens.

### User should provide the exact underlying assets

Even if we would provide the best user experience, every Vault only accepts the corresponding assets it will be using for its strategies. We can help the user to get these assets before investing in the Vault(See Zapper contract). However it is a decision that the Vault will only accept the desired assets in the correct ratio.

To understand better why we decide this please check the [Why we can\`t swap on deposit](/advanced-documentation/10-whitepaper/08-appendix/01-why-we-cant-swap-on-deposit-or-withdraw) section.

### IDLE funds.

IDLE funds are funds that are not being used for any strategy. But they are protected by being held inside the DeFindex Smart Contracts.

* Security: Enables `rescue`. This means that if a DeFi protocol gets too risky, the users won't lose their funds because they can be withdrawn from the DeFi protocol to the DeFindex Smart Contracts.
* Performance: Enable multi transaction movements.
* Transaction Cost: Enable small transactions that wont be affected by costly txs.

### Rescue

* It allows the Emergency Manager to rescue funds in case of an emergency. These are held in the DeFindex Smart Contracts. Thus, the users won't lose their funds and they will be able to withdraw them anytime.

### Roles

* Manager: Can change the Emergency Manager and the Fee Receiver. Rebalance between strategies to optimize the performance and minimize the risk.
* Emergency Manager: Can rescue funds in case of an emergency.
* Fee Receiver: Receives the fees that the protocol pays to incentivize good management.


# Smart Contracts

⏱️ 1 min read

There are 3 main contracts

* DeFindex Valut
* DeFindex Strategy
* DeFindex Zapper


# Vault Contract

⏱️ 13 min read

This contract serves as the core of the DeFindex platform, responsible for managing assets, executing strategies, and ensuring proper asset rebalancing. It operates with four primary roles: **Deployer**, **Fee Receiver**, **Manager**, **rebalancer** and **Emergency Manager**. Additionally, the contract functions as a token referred to as the *dfToken* that represents the shares of the vault.

While anyone can invest in a DeFindex, only the Manager and Emergency Manager have the authority to move funds between strategies or even outside strategies and into the Vault itself (see idle assets and rescue).

The contract also holds funds not currently invested in any strategy, known as **IDLE funds**. These funds act as a safety buffer, allowing the Emergency Manager to withdraw assets from underperforming or unhealthy strategies and store them as IDLE funds. (also to enable fast small withdrawals)

## Underlying Assets

Each DeFindex Vault will use a defined set of underlying assets to be invested in one or more strategies.

Because Strategies are the only one that know exactly the current balance of the asset, the Vault relies on the strategies in order to know the exact total balance for each underlying asset.??

Or if the Vault executes Strategies at its own name (auth), it should execute a speficic `get_assets_balance` function in the strategy contract to know exactely how many assets it has at a specific moment.

## Initialization

The DeFindex Vault contract is structured with specific roles and strategies for managing assets effectively. The key roles include the **Fee Receiver**, **Manager**, **rebalancer** and **Emergency Manager**, each responsible for different tasks in managing the Vault. Additionally, a predefined set of strategies determines how assets will be allocated within the Vault. A performance fee is also established at the time of initialization, which can later be adjusted by the Fee Receiver or the Manager. Further details on fee handling are explained later in the document.

The allocation ratios for these strategies are not set during the deployment but are defined during the first deposit made into the Vault. For example, imagine a scenario where the Vault is set up to allocate 20% of its assets to a USDC lending pool (like Blend), 30% to another USDC lending pool (such as YieldBlox), and 50% to a USDC-XLM liquidity pool on an Automated Market Maker (AMM) platform (like Soroswap).

To establish this allocation, the deployer must make a first deposit into the Vault, even if the amount is small. This initial deposit sets the ratio for all future deposits. The deployer is required to hold USDC and the liquidity pool tokens, such as LP-USDC-XLM, to start this process. However, a **zapper contract** simplifies this by automating asset conversion and liquidity pooling. The zapper takes the deployer’s USDC, swaps 25% of it into XLM, and then uses both USDC and XLM to add liquidity to the Soroswap pool. This process generates LP tokens, which is required to complete the first deposit, ensuring the allocation ratios are correctly set. It's worth noting that the first deposit is made within the same transaction that creates and initializes the vault, so the deployer must have at least a minimal amount of assets ready when creating a vault.

Once the contract is initialized and the first deposit is made, the **Manager** has the authority to adjust the allocation ratios over time. For example, if market conditions change or certain strategies perform better, the Manager can rebalance the allocations between the strategies to optimize performance. However, the Manager is limited to reallocating funds only between the existing strategies. They cannot introduce new strategies, which ensures the safety of user funds by minimizing potential security risks.

The **rebalancer** can only move funds between strategies and nothing else. This allow to have a keeper or bot constantly checking for the best yields and less risk. One does not want a bot to have the authority to change other roles.

This restriction on adding new strategies is a deliberate security feature. Allowing new strategies could increase the attack surface, potentially exposing the Vault to more vulnerabilities. By keeping the strategies fixed, the contract provides a stable and secure environment for users’ assets while still allowing flexibility in reallocating funds between existing strategies.

In summary:

1. **Roles and strategies are predefined** in the contract.
2. **Allocation ratios** for these strategies are set during the **first deposit**.
3. A **zapper contract** helps convert assets and establish the correct ratios.
4. The **Manager** can adjust allocations but cannot add new strategies, ensuring security and stability.

## Investing: Deposit

When a user deposits assets into the DeFindex Vault, they receive dfTokens, representing their proportional share of the Vault’s total assets. These dfTokens can later be burned to redeem the user’s share of assets.

Upon calling the `deposit()` function, assets are transferred to the DeFindex Vault and allocated based on the current asset ratios. For example, if the Vault maintains a 1:2:3 ratio for assets A, B, and C per dfToken, this ratio will be applied to new deposits. The user receives dfTokens reflecting their share of the Vault’s total assets.

To withdraw assets, users call the `withdraw` function to burn their dfTokens, releasing assets according to the current asset ratio.

Thus, the value per dfToken reflects a multi-asset backing. Using the above example, to mint 1 dfToken, a user would need to deposit 1 unit of asset A, 2 units of asset B, and 3 units of asset C. Therefore, the value of 1 dfToken can be represented as:

$$
p(\text{dfToken}) = (1 \text{A}, 2 \text{B}, 3 \text{C})
$$

### Depositing When Total Assets = 1

When the Vault only holds one asset, the deposit process is straightforward: the amount deposited by the user will be directly used to mint shares proportional to the total funds in the Vault.

1. **First Deposit**:\
   For the initial deposit, `shares_to_deposit` is set equal to the `amount` sent by the user, simplifying the initial setup.
2. **When There Are Existing Funds**:\
   If the Vault already holds funds, `shares_to_deposit` are calculated based on the current `total_managed_funds` and `total_supply` (i.e., the current number of shares), according to the following formula:

Let's denote the total supply at time 0 as s<sub>0</sub> and the total managed funds as v<sub>0</sub>. At time 1, a user wants to deposit an additional amount v', and new shares s' are minted. The value of any share val(s) at time t is calculated as:

$$
val(s)\_t = \frac{v\_t}{s\_t} \cdot s
$$

At time t<sub>1</sub>, this must hold:

$$
val(s') = \frac{v\_1}{s\_1} \cdot s'
$$

Given that v<sub>1</sub> = v<sub>0</sub> + v' and s<sub>1</sub> = s<sub>0</sub> + s', we can rearrange terms to find the new shares:

$$
s' = \frac{v'}{v\_0} \cdot s\_0
$$

## Withdrawals

When a user wishes to withdraw funds, they must burn a corresponding amount of dfTokens (shares) to receive their **assets at the ratio of the time of withdrawal**.

If there are sufficient **IDLE funds** available, the withdrawal is fulfilled directly from these IDLE funds. If additional assets are needed beyond what is available in the IDLE funds, a liquidation process is triggered to release the required assets.

To calculate the amount of each asset a<sub>i</sub> to be withdrawn, use the following formula:

$$
a\_i = \frac{m\_s}{M\_s} \cdot A\_i \quad \forall i \in \text{Underlying Asset}
$$

where:

* a<sub>i</sub>: Amount of asset i to receive
* m<sub>s</sub>: Amount of shares to burn
* M<sub>s</sub>: Total supply of dfTokens (shares)
* A<sub>i</sub>: Total amount of asset i held by the **DeFindex**

As discussed in the [Underlying Assets](#underlying-assets) section, A<sub>i</sub> is the sum of balances held by every strategy that works with asset i, plus total amount of idle assets i.

$$
A\_i = a\_{i, \text{IDLE}} + \sum^{j \in S\_i} a\_{i,s^i\_j}
$$

Here a<sub>i,s^i\_j</sub> represents the amount of assets i held by any strategy s<sup>i</sup><sub>j</sub>, and S<sub>i</sub> is the set of strategies that works with asset i that are supported by the Vault.

#### Liquidation on Withdrawal

For every time that the amount to assets to withdraw a<sub>i</sub> is greater than IDLE assets, the `withdraw()` function will liquidate the positions in the strategies to get the remaining assets, always maintaining the following relationship:

$$
a\_i = a\_{i, \text{IDLE}} + a\_{i, \text{Strategy}} \quad \forall a\_i>a\_{i, \text{IDLE}}
$$

Where:

* a<sub>i</sub>: Amount of asset i withdraw.
* a<sub>i, IDLE</sub>: Amount of asset i to get from the IDLE funds
* a<sub>i, Strategy</sub>: Amount of asset i to get from the strategies

## Rebalancing

Rebalancing is overseen by the **Manager** and/or **rebalancer**, who adjusts the allocation of funds between different strategies to maintain or change the ratio of underlying assets. For example, a DeFindex might start with a ratio of 2 USDC to 1 XLM, as initially set by the Deployer. However, this ratio can be modified by the Manager based on strategy performance or market conditions.

After the deployment and first deposit, the Manager or Rebalancer has to invest in a strategy. After that step, the deposits to the vault can be invested inmediately according to the current ratios of allocations. The Manager and/or rebalancer has the authority to adjust these ratios as needed to respond to evolving conditions or to optimize performance. The idea of the rebalancer role is to automate this process, while limiting the actions that rebalancer can do.

To ensure accurate representation of asset proportions, strategies are required to **answer** the amount of each underlying asset they hold. This communication ensures that when shares are minted or redeemed, the DeFindex maintains the correct asset ratios in line with the current balance and strategy allocations.

When dealing with multi-asset vaults, the rebalancing process may require trading assets to invest in various strategies. This is achieved through Soroswap using direct path movements, which are preferred for security reasons. Multi-hop or multi-DEX swaps require off-chain calculations to determine the optimal paths, which are then sent to the contracts. This process increases the protocol's attack surface. In contrast, direct paths avoid introducing additional assets beyond those already used by the vault, minimizing risk.

### Functions

* `assets()`: Returns the assets addresses and amount of each of them in the DeFindex (and hence its current ratio). `[[adress0, amount0], [address1, amount1]]`. TODO: Separate in 2 functions.
* `withdraw_from_strategies`: Allows the Manager to withdraw assets from one or more strategies, letting them as IDLE funds.
* `invest_in_strategies`: Allows the Manager to invest IDLE fund assets in one or more strategies.
* `internal_swap`: Allows the Manager to swap one IDLE asset into another IDLE asset supported by the Vault. As arguments, it receives an array of Soroswap's Aggregator Swap arguments.
* `rebalance`: Allows the Manager to rebalance the DeFindex. It executes `withdraw_from_strategies`, `internal_swap`, and `invest_in_strategies` functions.

Then, a rebalance execution will withdraw assets from the strategies, swap them, and invest them back in the strategies.

* `rescue`: Allows the Emergency Manager to withdraw all assets from a specific Strategy. As arguments, it receives the the address of a Strategy. It also turns off the strategy.

## Emergency Management

The Emergency Manager has the authority to withdraw assets from the DeFindex in case of an emergency. This role is designed to protect users' assets in the event of a critical situation, such as a hack of a underlying protocol or a if a strategy gets unhealthy. The Emergency Manager can withdraw assets from the Strategy and store them as IDLE funds inside the Vault until the situation is resolved.

## Management

Every DeFindex has a manager, who is responsible for managing the DeFindex. The Manager can ebalance the Vault, and invest IDLE funds in strategies.

## Fee Collection

### Fee Receivers

The DeFindex protocol defines two distinct fee receivers to reward both the creators of the DeFindex Protocol and the deployers of individual Vaults:

1. **DeFindex Protocol Fee Receiver**
2. **Vault Fee Receiver**

The fees collected are from the gains of the strategies. Thus, it is a performance-based fee.

### Fee Collection Methodology

The DeFindex fee collection process is designed to track fees in the vault until distribution, with fees originating from the strategy gains. This ensures an organized and accountable fee handling system.

#### General Overview

Fees are charged on a per-strategy basis, meaning each strategy independently calculates its gains and the corresponding fees. These fees are then collected and distributed to the protocol and manager. The fee percentages are fixed per vault and they are decided when creating it. However, the manager can change the ratio of the Vault fees at any time.

#### Detailed Workflow

1. **Fee Structure Example**:
   * Protocol Fee Receiver: 25%
   * Vault Fee Receiver: 20%
2. **Execution Example**:
   * A user deposits 100 USDC into a vault with one strategy.
   * The strategy earns 10 USDC in gains.
   * The vault collects 20% of the gains as fees (2 USDC).
   * From the fees collected, 25% is going to the Protocol (0.5 USDC), and the rest is going to the Vault Fee Receiver.
   * The total assets of the vault become (100 + 10 - 2 = 108) USDC.

#### Strategy Gains Tracking

Since fees depend on strategy performance, gains and losses must be tracked meticulously. To achieve this, a `report()` function is implemented (in the vault contract) to log the gains or losses since the last update.

**Pseudocode for Tracking Gains and Losses**:

```rust
fn report(strategy: Address) -> (u256, u256) {
    let current_balance = get_current_balance(strategy);
    let prev_balance = get_prev_balance(strategy);
    let previous_gains_or_losses = get_gains_or_losses(strategy);

    let gains_or_losses = current_balance - prev_balance;
    let current_gains_or_losses = previous_gains_or_losses + gains_or_losses;

    store_gains_or_losses(strategy, current_gains_or_losses);
    store_prev_balance(strategy, current_balance);
}

fn report_all_strategies() {
    for strategy in strategies {
        report(strategy);
    }
}
```

* **Note**: similar functions are called when someone executes a deposit, rebalance or withdraw. The whole idea is to keep the gain or losses and previous balance updated, without mixing information. For example, a deposit updates previous balance, but not gains.

#### Fee Locking and Distribution

Once gains are tracked, fees can be inspected and/or locked for future distribution. So, the manager can see the current gains and losses, and decide if he wants to change the ratio of the fees, before locking them.

The locking process is done by the manager calling the `lock_fees()` function.

**pseudocode for locking fees**

```rust
fn lock_fees(new_fee_bps: Option<i128>) {
    for strategy in strategies {
        if gains_or_losses > 0 {
            let fee = gains_or_losses * new_fee_bps.unwrap_or(vault_fee_bps) / MAX_BPS;
            let previous_locked_fee = get_locked_fee(strategy.asset)
            lock_fee(strategy.asset, fee + previous_locked_fee );
            reset_gains_or_losses(strategy);
        }
    }
}
```

When locking the fees, it is applied the current ratio to all the gains, and then they are reset to 0. If there is not gains, there is no fee to lock, and gains\_or\_losses can't be reset to 0. Also, this is run everytime a `withdraw` call occurs.

The locked fees are not considered when calculating ratios to be invested or price per share.

If, for some reason the yield generated by a strategy is too little, we can call the function `release_fee()` to make some of the fees go to the gain\_or\_losses. **Pseudocode for release\_fees**

```rust
fn release_fees(strategy: Address, amount: i128) {
    release_fee(strategy.asset, amount)
    let previous_gains_or_losses = get_gains_or_losses(strategy);
    store_gains_or_losses(strategy, current_gains_or_losses + amount);
}
```

Then, the fees are distributed to the protocol and manager, whenever a person calls the `distribute_fees()` function.

**Pseudocode for Fee Distribution**:

```rust
fn distribute_fees() {
    for strategy in strategies {
        let locked_fees = get_locked_fees(strategy);
        if locked_fees > 0 {
            transfer_from_strategy(strategy.asset, protocol_fee_receiver, locked_fees * protocol_fee_bps / MAX_BPS);
            transfer_from_strategy(strategy.asset, vault_fee_receiver, locked_fees * (MAX_BPS - protocol_fee_bps) / MAX_BPS);
            reset_locked_fees(strategy);
        }
    }
}
```

#### Displaying User Balances

To provide users with an accurate view of their balances, any outstanding fees should be deducted offchain from the total assets when showing the current balances.

By following this structured methodology, DeFindex ensures transparent and fair fee collection, tracking, and distribution processes.

It is expected that the Fee Receiver is associated with the manager, allowing the entity managing the Vault to be compensated through the Fee Receiver. In other words, the Fee Receiver could be the manager using the same address, or it could be a different entity such as a streaming contract, a DAO, or another party.

## Storage Management

Strategies are stored in instance storage, as the DeFindex is expected to work with a limited number of strategies.


# Strategy Contract

⏱️ 3 min read

The Strategy contract is the backbone of the DeFindex Protocol, responsible for generating yields for each DeFindex Vault that integrates it. By adhering to the standardized [DeFindexStrategyTrait](https://crates.io/crates/defindex-strategy-core), these contracts enable seamless interaction between the Vaults and external DeFi protocols.

***

**Key Features of a Strategy**

1. **Protocol-Specific Integration**:\
   Strategies act as a **proxy**, managing specific external protocols such as lending pools or liquidity providers. This design ensures:
   * DeFindex Vaults are decoupled from protocol-specific complexities.
   * Flexibility in introducing new strategies without modifying the core Vault contracts.
2. **Single Underlying Asset**:\
   Each Strategy manages one underlying asset, such as USDC or XLM. For instance:
   * A Strategy can manage a single token like USDC.
   * In liquidity pools, the Strategy manages the liquidity pool token (e.g., USDC/XLM LP tokens).
3. **Position and Balance Tracking**:
   * **Shares Management**: In some cases, like the Blend Strategy, the Strategy must issue **shares** internally to track positions and investments. This happens when the protocol requires specific authorizations or lacks direct support for managing positions at the Vault level.
   * **Proxy Use Cases**: In other cases, where the protocol supports directly adding positions on behalf of the Vault, the Strategy can act purely as a proxy, and shares tracking inside the Strategy might not be necessary.
   * **Consistency with Vaults**: Regardless of the internal tracking mechanism, the `deposit()`, `withdrawal()`, and `balance()` functions **must always return the depositor's balance in the underlying asset**. This ensures that the Vault can accurately track the status and health of its associated Strategy.
4. **Modularity and Extensibility**:\
   Developers can create custom Strategies tailored to specific use cases, DeFi protocols, or yield-generation techniques. This opens doors for innovation while maintaining compatibility with the DeFindex ecosystem.

***

**Core Functions of a Strategy**

Every Strategy implements the [DeFindexStrategyTrait](https://crates.io/crates/defindex-strategy-core), which defines the following core functions:

1. **Initialization** (`__constructor`):
   * Configures the Strategy with parameters such as the underlying asset, external protocol addresses, and custom settings.
2. **Asset Retrieval** (`asset`):
   * Returns the address of the underlying asset managed by the Strategy.
3. **Deposits** (`deposit`):
   * Allows the Vault to deposit assets into the Strategy for yield generation.
   * **Requirement**: Must return the depositor’s balance in the underlying asset.
4. **Harvesting Yields** (`harvest`):
   * Executes protocol-specific actions to claim or generate rewards.
   * Can trigger reinvestments for compounding yields.
5. **Withdrawals** (`withdraw`):
   * Enables the Vault to withdraw assets from the Strategy.
   * **Requirement**: Must return the depositor’s balance in the underlying asset.
6. **Balance Tracking** (`balance`):
   * Provides the current balance of the underlying asset held by the Strategy for a specific depositor.
   * **Requirement**: Must return the balance in the underlying asset, not shares or derivatives.

***

**Advantages of the DeFindexStrategyTrait**

1. **Standardization**:
   * Unified interface for interacting with any Strategy, reducing complexity for Vaults.
   * Facilitates third-party Strategy development while maintaining compatibility.
2. **Transparency**:
   * Vaults can track their balances and yields in terms of the underlying asset, avoiding ambiguity with derivatives or shares.
3. **Flexibility**:
   * Support for diverse protocols and yield-generation mechanisms.
   * Easy to introduce new Strategies or upgrade existing ones without disrupting the Vaults.
4. **Security**:
   * Decoupling Vaults from external protocols minimizes risk by limiting direct interactions.


# Zapper Contract

⏱️ 2 min read

This contract enables users to invest in and withdraw from a DeFindex Vault withouyt needing to hold the exact required set of assets of the vault.

For instance, if a DeFindex Vault requires both USDC and XLM in a defined ratio, the Zapper contract allows users to inpu USDC, automatically swapping the USDC for XLM and depositing both assets into the DeFindex Vault according to a predefined ratio. Similarly, the Zapper contract facilitates withdrawals by swapping the XLM back to USDC before returning the funds to the user.

The specific paths used for asset swaps, as well as the proportion of the output assets, are determined off-chain.

### Functions

* `deposit`: Allows users to deposit assets into the DeFindex Vault.
* `zap`: Allows users to deposit assets into the DeFindex using a single asset. This function receives the amount of one asset and an array of Soroswap's Aggregator Swap transactions. This array is computed offchain using the best path and the proportion of the output assets.
* `zap_deposit`: It executes a zap and a deposit in a single transaction.
* `withdraw`: Allows users to withdraw assets from the DeFindex.
* `zap_withdraw`: Allows users to withdraw assets from the DeFindex and receive a single asset. This function receives the amount of one asset and an array of Soroswap's Aggregator Swap transactions. This array is computed offchain using the best path and the proportion of the output assets.


# Rates and APY

⏱️ 3 min read

The **Vault APY** shows how much the value of a vault shares grows over time — similar to earning interest on savings.

This value depends on:

* The **assets** supported in the vault,
* The **strategies** the vault uses,
* The **rebalancing actions** taken by the Vault Manager.

Even though there are many moving parts, **DeFindex Vaults make it easy** to track this performance through the **Vault Price Per Share (VPPS)**.

***

💡 What is Vault Price Per Share?

Just like strategies have a **price per share**, the **vault itself** has a **price per share** that shows how much 1 share of the vault is worth.

This includes:

* All the strategies the vault uses,
* How much is allocated to each strategy,
* And how well each strategy is performing.

So, when the vault earns yield or its strategies grow, the **vault price per share increases**.

***

### 🧮 How to Get the Vault Price Per Share

There are two main ways to calculate it. Both give the same result.

#### ✅ Method 1: Use the Contract Function `get_asset_amounts_per_shares`

```rust
fn get_asset_amounts_per_shares(
        e: Env,
        vault_shares: i128,
    ) -> Result<Vec<i128>, ContractError>;
```

You can get the real-time vault PPS by calling

```rust
get_asset_amounts_per_shares(1_000_000_000_000) // 1 Vault Share is SCALAR_12
```

This function returns a `Vec`of asset amounts per share. Each amount matches the asset at the same index in the vault's asset list.

To calculate the vault share price in a specific pricing currency (e.g. USD):

$$
\text{Vault PPS} = \sum\_{i} \left( \text{Asset Price}\_i \times \text{VAmount}\_i \right)
$$

Where:

* `VAmount_i` = amount of asset `i` per share fasdf
* `Asset Price_i` = price of asset `i` (from an oracle or external source)

**If the vault has only one asset** and you're pricing in that same asset, just use the first value from `get_asset_amounts_per_shares`.

***

#### ✅ Method 2: Use Vault Events

Each time someone deposits or withdraws from the vault, deposit and withdraw events are emitted:

```rust

pub struct VaultWithdrawEvent {
    pub withdrawer: Address,
    pub df_tokens_burned: i128,
    pub amounts_withdrawn: Vec<i128>,
    pub total_supply_before: i128,
    pub total_managed_funds_before: Vec<CurrentAssetInvestmentAllocation>,
}
pub struct VaultDepositEvent {
    pub depositor: Address,
    pub amounts: Vec<i128>,
    pub df_tokens_minted: i128,
    pub total_supply_before: i128,
    pub total_managed_funds_before: Vec<CurrentAssetInvestmentAllocation>,
}
```

Each event includes:

* `total_supply_before` — the number of vault shares before the action
* `total_managed_funds_before` — a list of all asset allocations

Each asset allocation looks like this:

```rust
pub struct CurrentAssetInvestmentAllocation {
    pub asset: Address,
    pub total_amount: i128,
    pub idle_amount: i128,
    pub invested_amount: i128,
    pub strategy_allocations: Vec<StrategyAllocation>,
}
```

To calculate the vault price per share:

$$
\text{Vault PPS} = \frac{\sum \left( \text{Asset Price}\_i \times \text{Total Asset Amount}\_i \right)}{\text{Total Vault Shares}}
$$

If the vault only holds **one asset**, then:

$$
\text{Vault PPS} = \frac{\text{Total Asset Amount}}{\text{Total Supply}}
$$

Where:

* **`Total Asset Amount`** = sum of all units of that one asset held by the vault (from `total_managed_funds_before[0].total_amount`)
* **`Total Supply`** = number of shares before the action (from `total_supply_before`)

### 📈 How to Calculate Vault APY

Once you have the Vault PPS at two different points in time, you can calculate **APY** using the same method as when is calculated for strategies:

$$
\text{pps\_delta} = \frac{\text{PPS}*{\text{now}}}{\text{PPS}*{\text{then}}} - 1
$$

Then annualize it:

$$
\text{Vault APY} = \left(1 + \text{pps\_delta} \right)^{\left( \frac{365.2425}{\text{days}} \right)} - 1
$$

Where `days` is the number of days between the two PPS values.


# Strategy Examples

⏱️ 1 min read

## Strategy Examples

### Most Probable in Short Term

A. USDC Blend in USDC/XLM Blend This strategy will deposit USDC in the USDC/XLM Blend Market. It aquires USDC yield, it does not harvest BLND rewards

B. Autocompound USDC Blend in USDC/XLM Blend This strategy will deposit USDC in the USDC/XLM Blend Market. It aquires USDC yield, and it DOES harvest and reinvest BLND rewards.

C. Autocompound USDC Blend in USDC/XLM/EUR/AQUA Blend This strategy will deposit USDC in the USDC/XLM/EUR/AQUA Blend Market. It aquires USDC yield, and it DOES harvest and reinvest BLND rewards.

D. Autocompund AMM Staking.

## Vault Example with Beans App

1.- 50% Strategy B and 50% Strategy C.

Manager Example: Beans App Team Manager decides to diversify investment in Fixed USDC/XLM (Strategy B) and YieldBox USDC/XLM/EUR/AQUA (Strategy C) Because the best yield comes from Strategy B, the initial ratio will be 1:0

If in the future, YieldBox USDC Supply APY is better than the Strategy A USDC supply apy, the Manager can decide to change the ratio to be 0:1


# State of the Art

⏱️ 1 min read

From all types of DeFi protocols that achieve similar goals currently available on different ecosystem, we have selected 2 to define the basic concepts for DeFidex. These are **Yearn.Finance** and **SetProtocol**.

* [Yearn.Finance](/advanced-documentation/10-whitepaper/07-state-of-the-art/01-yearn-finance)
* [SetProtocol](/advanced-documentation/10-whitepaper/07-state-of-the-art/02-set-protocol)


# Yearn Finance

⏱️ 9 min read

## Yearn Finance V3

[Yearn Finance V3](https://yearn.fi/v3) represents the latest evolution of the Yearn Finance protocol, a decentralized platform focused on optimizing yield farming strategies. Leveraging advanced automation and smart contract technology, Yearn V3 introduces enhanced modularity, allowing for more flexible and efficient strategy deployments across various DeFi protocols. This iteration aims to provide users with higher returns, reduced risks, and greater customization in managing their assets. By continuously **aggregating yields from different lending protocols**, Yearn V3 ensures that users' assets are **dynamically allocated** to the most profitable opportunities, streamlining the complex process of yield farming.

### The Yearn Finance Concepts

* **Yearn Vaults (yVaults)**: A Vault is a Smart Contract that manages users funds and allocates them in different strategies. Funds can be allocated in a **single strategy or in a collection of multiple strategies**. From the [documentation page](https://docs.yearn.fi/getting-started/products/yvaults/overview), yVaults are like savings accounts for your crypto assets. They accept your deposit, then route it through strategies which seek out the highest yield available in DeFi. With YearnV3, yVaults are ERC-4626 compatible. See more [here](https://docs.yearn.fi/getting-started/products/yvaults/v3).

A vault or "Allocator Vault" in V3 refers to an [ERC-4626 compliant](https://github.com/yearn/yearn-vaults-v3/blob/master/contracts/VaultV3.vy#L40) contract that takes in user deposits, mints shares corresponding to the user's share of the underlying assets held in that vault, and then allocates the underlying asset to an array of different "strategies" that earn yield on that asset.

* **Stategy**: A strategy or Opportunioty in V3 refers to a yield-generating contract added to a vault that has the needed ERC-4626 interface. The strategy takes the underlying asset and deploys it to a single source, generating yield on that asset.
* **Shares or Vault Tokens**: Tokens that will represents an user participation in a specific Yearn Vault. Depositors receive shares proportional to their deposit amount

## Yearn V3 Main Smart Contracts

#### Factory Contract

The factory contract is designed to deploy new vaults using a specific `VAULT_ORIGINAL` as a blueprint. The deployment process ensures that each vault has unique parameters and cannot be duplicated.

#### Key Function

```jsx
def deploy_new_vault(
    asset: address,
    name: String[64],
    symbol: String[32],
    role_manager: address,
    profit_max_unlock_time: uint256
) -> address
```

This function creates a new vault with the following parameters:

* **asset**: The underlying token the vault will use (e.g., USDC).
* **name**: The name of the vault token (e.g., DeFindex Blend USDC Pool) that will be issued to investors.
* **symbol**: The symbol of the vault token (e.g., dfBlUSDC) that will be issued to investors.
* **role\_manager**: The admin responsible for managing the vault's roles.
* **profit\_max\_unlock\_time**: The time over which the profits will unlock (in seconds).

#### Events

* **NewVault**: Emitted when a new vault is deployed. Provides the vault address
* **UpdateProtocolFeeBps**: Emitted when the protocol fee basis points are updated.
* **UpdateProtocolFeeRecipient**: Emitted when the protocol fee recipient is updated.

NOTE: The vault factory utilizes create2 opcode to deploy vaults to deterministic addresses. This means the same address can not deploy two vaults with the same default parameters for 'asset', 'name' and 'symbol'.

#### Vault Contract

The vault contract manages user deposits, handles idle assets, and interacts with various strategies to generate yield. It issues vault tokens to users based on their share of the vault's total assets.

#### Key Concepts

* **vault** (allocator): ERC-4626 compliant contract that accepts deposits, issues shares, and allocates funds to different strategies to earn yield.
* **vault shares**: A tokenized representation of a depositor's share of the underlying balance of a vault. strategy: Any ERC-4626 compliant contract that can be added to an allocator vault that earns yield on an underlying asset.
* **debt**: The amount of the underlying asset that an allocator vault has sent to a strategy to earn yield.
* **report**: The function where a vault accounts for any profits or losses a strategy has accrued, charges applicable fees, and locks profit to be distributed to depositors.
* **Idle Amount**: The portion of underlying assets kept liquid within the vault for quick withdrawals.
* **Min Idle Amount**: Minimum liquid amount of underlying assets the vault must maintain.
* **Update Debt**: A function executed by administrators to allocate funds to different strategies, setting target debt levels.
* **Debt Manager**: A role responsible for managing the vault's debt (strategy allocations).
* **Default Queue**: A queue of strategies to take funds from when the vault needs to free up assets. It defines the priority order for liquidating strategy positions.

#### Important Functions

* **Deposit**: Users deposit funds into the vault, receiving vault shares in return.
* **Withdraw**: Users burn their vault shares and withdraw their funds, which may involve liquidating strategy positions if the idle amount is insufficient.

#### Issuing Shares

[From the Github Repo:](https://github.com/yearn/yearn-vaults-v3/blob/9fbc614bbce9d7cbad42e284a15f0f43cf1a673f/contracts/VaultV3.vy#L503)

```jsx
def _total_assets() -> uint256:
    return self.total_idle + self.total_debt

def _deposit(sender: address, recipient: address, assets: uint256) -> uint256:
    ...
    self.total_idle += assets
    shares: uint256 = self._issue_shares_for_amount(assets, recipient)
    ...

def _issue_shares_for_amount(amount: uint256, recipient: address) -> uint256:
    total_supply: uint256 = self._total_supply()
    total_assets: uint256 = self._total_assets()
    new_shares: uint256 = 0

    if total_supply == 0:
        new_shares = amount
    elif total_assets > amount:
        new_shares = amount * total_supply / (total_assets - amount)

    if new_shares == 0:
       return 0

    self._issue_shares(new_shares, recipient)
    return new_shares

```

This function calculates and issues new shares based on the amount of assets deposited. What these functions do is to maintain the relation between shares and assets invested. In fact, if S<sub>t</sub> is the Total Share Supply at time t, A<sub>t</sub> is the total amount of Assets at time t, s is the new amount of shares to be minted and a is the amount of assets being invested, what this code is doing is to maintain the following relationship

$$
\frac{S\_t}{A\_t} = \frac{s}{a}
$$

Because, when `_issue_shares_for_amount` is being called, `total_assets` is already A<sub>0</sub> + a = A<sub>1</sub>, but `total_supply` is still S<sub>0</sub> then the relationship will be

$$
\frac{S\_1}{A\_1} = \frac{S\_0 + s}{A\_1} = \frac{s}{a}
$$

$$
(S\_0 + s) \cdot a = S\_0 \cdot a + s \cdot a = s \cdot A\_1
$$

$$
s = \frac{a \cdot S\_0 }{A\_1 - a}
$$

Links:

* [Tech Specs for YearnV3 Vaults](https://github.com/yearn/yearn-vaults-v3/blob/master/TECH_SPEC.md)
* [Vault Management](https://docs.yearn.fi/developers/v3/vault_management)

#### Strategy Contract

The strategy contract in Yearn V3 focuses on specific yield-generating tasks, delegating standardized ERC-4626 and vault logic to a central `TokenizedStrategy` contract.

#### Key Components

* **BaseStrategy**: Inherited by strategies to handle communication with the `TokenizedStrategy`.
* **TokenizedStrategy**: Implements all ERC-4626 and vault-specific logic.
* **Modifiers**: Ensure only authorized addresses can call certain functions, enhancing security.

#### Functions

* **\_deployFunds**: Deploys assets into yield sources.
* **\_freeFunds**: Frees assets when needed.
* **\_harvestAndReport**: Harvests rewards, redeploys idle funds, and reports the strategy's total assets.

Reference:

* [BaseStrategy](https://docs.yearn.fi/developers/smart-contracts/V3/Current-v3.0.2/BaseStrategy)
* [TokenizedStrategy](https://docs.yearn.fi/developers/smart-contracts/V3/Current-v3.0.2/TokenizedStrategy)

#### Fee and Price Per Share (PPS) Management

#### Fee Management

Fees are a percentage charged each time a V3 vault or strategy "reports". In Yearn V3 there are also **Protocol Fees**, which are a percentage of the total performance fees, that will go to Yearn for providing the infrastucture. Yearn Governande is responsible to set this percentage. It can be set for all Vaults or for individual vaults and strategies. Allowing full customization of the system.

* **Default and Custom Protocol Fees**: The factory contract allows setting default and custom protocol fees for vaults and strategies.
* **Fee Recipient**: Protocol fees are sent to the designated fee recipient, with the remaining fees going to the vault or strategy-specific recipient (vaults managers).

#### Price Per Share (PPS) Calculation

The PPS is calculated based on the total assets and total supply of shares within the vault.

```jsx
@view
@internal
def _convert_to_assets(shares: uint256, rounding: Rounding) -> uint256:
    """
    assets = shares * (total_assets / total_supply) --- (== price_per_share * shares)
    """
    if shares == max_value(uint256) or shares == 0:
        return shares

    total_supply: uint256 = self._total_supply()
    # if total_supply is 0, price_per_share is 1
    if total_supply == 0:
        return shares

    numerator: uint256 = shares * self._total_assets()
    amount: uint256 = numerator / total_supply
    if rounding == Rounding.ROUND_UP and numerator % total_supply != 0:
        amount += 1

    return amount

@view
@external
def pricePerShare() -> uint256:
    return self._convert_to_assets(10 ** convert(self.decimals, uint256), Rounding.ROUND_DOWN)

```

This function provides the PPS, ensuring precise share-to-asset confeversion.

In pricePerShare, we are converting 10\*\*decimals units of shares into asset, meaning, an exact unit of share. Meaning `pricePerShare() === convert_to_assets(1)`

**Calculating Price Per Share (PPS)**:

The PPS is a crucial metric for ensuring users receive the correct value for their dfTokens. It is calculated as follows:

$$
\text{PPS} = \frac{\text{Total Assets}}{\text{Total Supply of dfTokens}}
$$

### Limitations of Yearn Finance V3

* **Does not support multi-asset** strategies: For example you can't invest on a vault composed by a strategy of USDC on Aave and another strategy of USDC-WETH on Uniswap.
* Price Per Shares (PPS), ConvertToShares and ConvertToAssets functions need to be described in a single asset.
* **Fees and Revenues:** Every DeFindex Vault generates revenue through protocol fees, which include:

  * **Streaming Fees:** Charged on assets under management, collected over time.
  * **Performance Fees:** Based on the returns generated by the investment strategies.
  * **Protocol Fees:** For transactions such as trading and borrowing.

  These fees are designed to incentivize protocol development and cover operational costs.

TODO: CHeck who decides the amounts of these fees

### Fees Collection

All info is in the this website; <https://docs.yearn.fi/developers/v3/protocol\\_fees>

Yearn collects fees through a performance-based system defined by governance, which controls the percentage of protocol fees and allows customization for each vault and strategy. This ensures flexibility and precise tuning of the fee structure. Yearn Governance dictates the amount of the Protocol fee and can be set anywhere between 0 - 50%. Yearn governance also holds the ability to set custom protocol fees for individual vaults and strategies. Allowing full customization of the system.

Example

```
profit = 100
performance_fee = 20%
protocol_fee = 10%

total_fees = profit * performance_fee = 20
protocol_fees = total_fees * protocol_fee = 2
performance_fees = total_fees - protocol_fees = 18

18 would get paid to the vault managers performance_fee_recipient.
2 would get paid to the Yearn Treasury.
```

#### When Fees Are Collected

Fees are collected when a strategy reports gains or losses via the report() function. During the report, the strategy will calculate the gains since the last report and then calculate the fees based on the gains. This fees are then distributed as shares of the vault. Then, fees are collected per strategy.

Accountant reports the fees or refunds to the vault, from the gains or losses of the strategy. Then, the vault will calculate the fees and the protocol fees and then distribute the fees to the vault manager and the protocol fee recipient. This accountant is an interface and apparently it depends on the vault.

Yearn burns shares when there is fees or losses. When there is a loss and there is still fees not paid, the vault will burn shares to pay the fees.

The Vaults utilizes several mechanisms to mitigate price per share (pps) fluctuations and manipulation:

1. Internal accounting is used instead of balanceOf() to keep track of the vault's debt and idle.
2. A profit locking machenism designed by V3 Vaults locks profits or accountant's refunds by issuing new shares to the vault itself that are slowly burnt over the unlock perior.
3. In the event of losses or fees, the vault will always try to offset them by butning locked shares it owns. the price per share is expected to decrease only when excess losses or fees occur upon processing a report, or a loss occurs upon force revoking a strategy. [reference](https://github.com/yearn/yearn-security/blob/master/audits/20240504_ChainSecurity_Yearn_V3/Yearn-Smart-Contract-Audit_V3_Vaults_-ChainSecurity.pdf)


# Set Protocol

⏱️ 4 min read

## Set Protocol

<https://www.tokensets.com/#/> <https://docs.tokensets.com/protocol/litepaper> <https://docs.tokensets.com/>

Set Protocol is a decentralized platform that enables the creation, management, and trading of tokenized investment portfolios, known as Sets. By leveraging Ethereum smart contracts, Set Protocol allows users to automate and rebalance their portfolios based on predefined strategies, making complex financial maneuvers accessible to both novice and experienced investors. These Sets can include a diverse range of assets, from cryptocurrencies to tokenized traditional assets, providing broad exposure and diversification. Set Protocol's intuitive interface and advanced features empower users to maximize returns while minimizing risks, making it a powerful tool for modern digital asset management.

### Multi-asset:

It supports multi-asset strategies, allowing users to create Sets composed of various tokens and assets. However, a user needs to have the underlying assets to mint a Set token, which can be a barrier for some investors.

### How the LPToken are minted when new underlying asset are added? ¿What is the formula?

That’s defined at the beginning as an arbitrary parameter. For example, I can define 1 SetToken to have 1WBTC, 2WETH and 3USDC. Then, if I want to mint 10 SetTokens I need to have 10WBTC, 20WETH, 30USDC.

It can be added a module to mint SetTokens with only one Asset, which is the [NAV (Net Asset Value) Issuance](https://docs.tokensets.com/developers/guides-and-tutorials/protocol/nav-issuance) . It uses oracles to identify how much you can mint. “The issuer receives a proportional amount of SetTokens on issuance based on the calculated net asset value of the Set using **oracle prices**.”

## TokenSets

Web: <https://www.tokensets.com/#/>

Litepaper: <https://docs.tokensets.com/protocol/litepaper>

Docs: <https://docs.tokensets.com/>

### Are there some examples of mixing AMM Liquidity Pool tokens with Lending Platform?

Multi-Asset: Set V2 enables the creation and implementation of strategies employing single asset, pairs, and 3+ assets. Apparently you need to have the assets beforehand Does it include swaps when investing?

### How the LPToken are minted when new underlying asset are added? ¿What is the formula?

That’s defined at the beginning as an arbitrary parameter. For example, I can define 1 SetToken to have 1WBTC, 2WETH and 3USDC. Then, if I want to mint 10 SetTokens I need to have 10WBTC, 20WETH, 30USDC.

It can be added a module to mint SetTokens with only one Asset <https://docs.tokensets.com/developers/guides-and-tutorials/protocol/nav-issuance> . It uses oracles to identify how much you can mint. “The issuer receives a proportional amount of SetTokens on issuance based on the calculated net asset value of the Set using **oracle prices**.”

### What is the concept of rebalancing / reinvesting in this protocol?

Rebalancing can be done using the Trade Module. The Trade Module enables managers of SetTokens to perform atomic trades using aggregators such as 0x and 1inch, and decentralized exchanges such as Sushiswap and Uniswap. This rebalances the Set for all Set holders.

### How do the contracts handle user funds?

Funds are held by the SetToken Contract.

### How does the protocol generate revenue?

This is done through the Streaming Fee Module

The Streaming Fee Module is a module that accrues streaming fees for Set managers. Streaming fees are denominated as percent per year and realized as Set inflation rewarded to the manager.

The formula to solve for fee is:

* (feeQuantity / feeQuantity) + totalSupply = fee / scaleFactor

The simplified formula utilized below is:

* feeQuantity = fee \* totalSupply / (scaleFactor - fee) The streaming fees are fees that are paid out to Set managers over time are based on the entire market cap of the Set (e.g. 2% of market cap over 1 year). This incentivizes managers to increase the value of their Sets over time for their users.

The streaming fee is calculated linearly over the lifespan of the Set. For example, if a Set has a 2% streaming fee and 6 months has passed, 1% of streaming fees can be collected.

Protocol Fees: To allow for protocol sustainability, the Protocol will charge fees for protocol-native transactions such as trading via dutch auctions, borrowing using the protocol’s lending pool, and subscription/profit fee sharing.

Manager Admin: Set V2 gives managers greater control over how and when Sets can be minted and by whom.

Trader Subscription and Performance Fees: Traders can implement time-based (streaming) and performance-based (profit) fees


# Appendix

⏱️ 1 min read

* [Why we can't swap on deposit or withdraw](/advanced-documentation/10-whitepaper/08-appendix/01-why-we-cant-swap-on-deposit-or-withdraw)


# Why We Can't Swap on Deposit or Withdraw

⏱️ 4 min read

When depositing (or investing) in DeFindex, a user receives dfTokens, which represent their share of the DeFindex portfolio. These tokens are minted based on the amount of assets deposited and the current price per share (denoted as p<sub>ps</sub>). Later, the user can burn these tokens to withdraw their assets.

The challenge arises when calculating this price per share.

### Example Scenario

Consider a DeFindex with a single strategy: 100% allocation to XLM on Xycloans, while the DeFindex receives deposits in USDC. The price per share p<sub>ps</sub> could be calculated as the amount of USDC one receives after withdrawing from Xycloans and swapping the XLM to USDC, divided by the total supply of dfTokens:

$$
p\_{ps}(m) = \frac{p\_{XLM}(m) \cdot M\_{XLM}}{T\_{dfTokens}}
$$

Where:

* p<sub>XLM</sub>(m) is the price of XLM in terms of USDC after liquidating m XLM.
* M<sub>XLM</sub> is the total amount of XLM held by the DeFindex.
* T<sub>dfTokens</sub> is the total supply of dfTokens.

The problem is that the price of XLM p<sub>XLM</sub>(m) will depend on the amount of XLM we need to withdraw. This price can be manipulated by a large swap. For instance, someone could swap a large amount of USDC for XLM, artificially inflating the price of XLM. As a result, the price per share would increase, allowing the user to receive more USDC when burning their dfTokens.

### Fixed Price Per Share Approach

Given the manipulation risk with a variable price per share, let's consider using a fixed price per share.

Assume p<sub>0</sub> is the nominal (initial or fixed) price of XLM in USDC. The amount of USDC received by a user who swaps m<sub>XLM</sub> XLM for USDC will be:

$$
m\_{USDC} = p\_{XLM}(m\_{XLM}) \cdot m\_{XLM}
$$

The price per share would then be:

$$
p\_{ps} = \frac{p\_0 \cdot M\_{XLM}}{T\_{dfTokens}}
$$

After burning $m\_{dfTokens}$ dfTokens, the user should receive:

$$
m\_{USDC} = p\_{ps} \cdot m\_{dfTokens} = p\_0 \cdot M\_{XLM} \cdot \frac{m\_{dfTokens}}{T\_{dfTokens}}
$$

Where:

* m<sub>XLM</sub> is the amount of XLM the DeFindex needs to liquidate to pay the user.
* m<sub>dfTokens</sub> is the amount of dfTokens the user is burning to withdraw their share.

The amount of XLM to be liquidated, m<sub>XLM</sub>, is given by:

$$
m\_{XLM} = M\_{XLM} \cdot \frac{m\_{dfTokens}}{T\_{dfTokens}}
$$

The USDC received after the swap m<sub>USDCout</sub> would then be:

$$
m\_{USDCout} = p\_{XLM}(m\_{XLM}) \cdot m\_{XLM} = p\_{XLM}(m\_{XLM}) \cdot M\_{XLM} \cdot \frac{m\_{dfTokens}}{T\_{dfTokens}}
$$

Since p<sub>0</sub> is the nominal price and p<sub>XLM</sub>(m<sub>XLM</sub>) is the actual price after liquidation:

$$
p\_{XLM}(m\_{XLM}) < p\_0 \quad \forall m\_{XLM} > 0
$$

Thus, we have:

$$
p\_{XLM}(m\_{XLM}) \cdot M\_{XLM} \cdot \frac{m\_{dfTokens}}{T\_{dfTokens}} < p\_0 \cdot M\_{XLM} \cdot \frac{m\_{dfTokens}}{T\_{dfTokens}}
$$

In summary:

$$
m\_{USDCout} < m\_{USDC}
$$

This inequality shows that the user would request more USDC than what they can actually receive after the swap. This discrepancy leads to a potential loss of funds for DeFindex, highlighting why we can't rely on swapping assets during the deposit process.

We can argue that when using any fixed price per share, the amount of USDC received after a swap, denoted as m<sub>USDCout</sub>, will differ from the expected amount m<sub>USDC</sub>. This discrepancy introduces a vulnerability, making the protocol susceptible to manipulation.

**Calculating Price Per Share (PPS)**:

The PPS is a crucial metric for ensuring users receive the correct value for their dfTokens. It is calculated as follows:

$$
\text{PPS} = \frac{\text{Total Assets}}{\text{Total Supply of dfTokens}}
$$

Where:

* **Total Assets**: The sum of the value of assets managed by all adapters plus any idle assets held directly by the DeFindex contract.
* **Total Supply of dfTokens**: The total number of dfTokens issued to users.

To illustrate, consider the following scenario:

* DeFindex has three adapters managing different investments:
  * Adapter A manages $50,000 in a liquidity pool.
  * Adapter B manages $30,000 in a lending pool.
  * Adapter C manages $20,000 in a staking protocol.
* The DeFindex contract holds an additional $10,000 in idle assets.

The Total Assets would be:

$$
50,000 + 30,000 + 20,000 + 10,000 = 110,000 \text{ USDC}
$$

If the Total Supply of dfTokens is 100,000, the PPS would be:

$$
\text{PPS} = \frac{110,000 \text{ USDC}}{100,000 \text{ dfTokens}} = 1.1 \text{ USDC per dfToken}
$$

This calculation ensures users can accurately determine the value of their holdings in DeFindex, promoting transparency and trust.


# Risks and Audits

⏱️ 1 min read

## **Smart Contracts**

DeFindex is built on smart contracts. In the event of a hack, funds could be at risk.

## Underlying Protocols

Because DeFindex integrates with external DeFi protocols, vulnerabilities in those protocols may affect vault performance.

## Risk Mitigation

DeFindex minimizes risk through safeguards enforced at the smart contract level:

* **Whitelisted strategies only.** Each vault's strategies are defined when the vault is created. Deposits can only be invested into these whitelisted strategies — there is no contract function to add or swap strategies afterwards.
* **Funds never leave the vault's ecosystem.** Rebalancing can only move funds between the vault and its whitelisted strategies: unwound funds are hard-coded to return to the vault, and no role can make the vault transfer assets to an arbitrary address.
* **Rescue function.** Vault Managers and Emergency Managers can trigger a **rescue function**, which safely unwinds all funds from an underlying strategy back to the vault — where only users can withdraw. This enables proactive risk management in collaboration with chain analysis tools.
* **Strategy pausing.** Managers and Emergency Managers can pause a strategy, blocking new investments into it while user withdrawals remain fully available.
* **Withdrawals are always open.** No role can freeze user deposits or withdrawals — users can always exit with their share of the vault.
* **Fees only on yield.** Fees accrue exclusively on positive gains; user principal is never charged.
* **First-deposit protection.** Vaults enforce a minimum liquidity of locked "dead shares" on the first deposit, protecting depositors against share-inflation attacks.

You can verify these mechanisms directly in the open-source contracts: [defindex-io/stellar-contracts](https://github.com/defindex-io/stellar-contracts).

## Audits

**Audit Report by OtterSec:** [View the report](https://github.com/defindex-io/docs/blob/main/audits/2025_03_18_ottersec_defindex_audit.pdf)

For a detailed evaluation of DeFindex’s security and compliance, refer to the audit report conducted by [**OtterSec**](https://osec.io). You can also browse all OtterSec audits at [osec.io/audits](https://osec.io/audits).

### Underlying Protocols

DeFindex uses underlying protocols. These protocols have been independently audited:

| Protocol | Number of Audits | Audits                                                                      |
| -------- | ---------------- | --------------------------------------------------------------------------- |
| Blend V2 | 3                | [View audits](https://docs.blend.capital/audits-and-bug-bounties#v2-audits) |


# Available SDKs

⏱️ 1 min read

Welcome to the DeFindex SDK documentation! Choose the SDK that best fits your development environment and integration requirements.

### Direct API Integration (Recommended)

**Perfect for:** Custom integrations and any programming language

* **Platform:** Language-agnostic REST API
* **Use Case:** Any app
* **Features:** Complete API access, maximum flexibility, Bearer token authentication

[**View API Integration Guide**](/api-integration-guide/api)

## Getting Started

1. **Choose your platform** based on your development environment
2. **Follow the integration guide** for your selected SDK or API
3. **Deploy a vault** using the [DeFindex DApp](https://app.defindex.io/) or TypeScript SDK
4. **Start integrating** vault operations into your application

## Available SDKs

### DotNet SDK

**Perfect for:** server-side development

* **Platform:** dotnet
* **Use Case:** Web backends, server-side applications
* **Features:** Only deposit, withdraw, balance and APY

### TypeScript SDK

**Perfect for:** Web applications and server-side development

* **Platform:** TypeScript/JavaScript (Node.js)
* **Use Case:** Web backends, API integrations, server-side applications
* **Features:** Complete vault management, factory operations, admin functions, full type safety

[**View TypeScript SDK Documentation**](#typescript-sdk)

### Flutter SDK (Deprecated)

## Need Help?

* **Discord Community / Developer Support:** [Join our Discord](https://discord.gg/ftPKMPm38f)
* **API Documentation:** <https://api.defindex.io/docs>


# Typescript SDK

⏱️ 3 min read

Welcome to the DeFindex TypeScript SDK documentation! This SDK provides server-side access to DeFindex's vault management system through a comprehensive TypeScript interface. With this SDK, you can:

1. Create and manage decentralized vaults
2. Perform vault operations (deposit, withdraw, balance queries)
3. Access real-time APY data
4. Execute administrative operations
5. Integrate secure API key authentication

## Prerequisites

Before integrating the SDK, ensure you have:

* Node.js environment (version 16 or higher)
* TypeScript knowledge for optimal development experience
* [API key from DeFindex](/api-integration-guide/guides-and-tutorials/getting-api-key)
* Understanding of Stellar/Soroban blockchain concepts

## Integration Guide

### 1. Install the SDK

Add the SDK to your project using your preferred package manager:

```bash
npm install @defindex/sdk
# or
pnpm install @defindex/sdk
# or
yarn add @defindex/sdk
```

### 2. Import and Initialize

Import the SDK and configure it with your API key:

```typescript
import { DefindexSDK, SupportedNetworks } from '@defindex/sdk';

// Initialize with API key (recommended for server-side use)
const sdk = new DefindexSDK({
  apiKey: process.env.DEFINDEX_API_KEY, // Store securely in environment variables
  baseUrl: 'https://api.defindex.io',   // Optional: defaults to production API
  timeout: 30000                        // Optional: request timeout in milliseconds
});
```

## Quick Start

Here's a minimal example to get you started with vault operations:

```typescript
import { DefindexSDK, SupportedNetworks } from '@defindex/sdk';

// Initialize the SDK
const sdk = new DefindexSDK({
  apiKey: 'sk_your_api_key_here'
});

async function quickStart() {
  try {
    // Check API health
    const health = await sdk.healthCheck();
    console.log('API Status:', health.status.reachable);

    // Get factory address
    const factory = await sdk.getFactoryAddress(SupportedNetworks.TESTNET);
    console.log('Factory Address:', factory.address);

    // Get vault information
    const vaultAddress = 'CVAULT_CONTRACT_ADDRESS...';
    const vaultInfo = await sdk.getVaultInfo(vaultAddress, SupportedNetworks.TESTNET);
    console.log(`Vault: ${vaultInfo.name} (${vaultInfo.symbol})`);

    // Check user balance
    const userAddress = 'GUSER_ADDRESS...';
    const balance = await sdk.getVaultBalance(vaultAddress, userAddress, SupportedNetworks.TESTNET);
    console.log(`Vault Shares: ${balance.dfTokens}`);

  } catch (error) {
    console.error('Operation failed:', error.message);
  }
}

quickStart();
```

## Implementation Example

### Complete Vault Operations Flow

Here's a comprehensive example demonstrating vault creation, deposits, and withdrawals:

```typescript
import {
  DefindexSDK,
  SupportedNetworks,
  CreateVaultParams,
  DepositParams,
  WithdrawFromVaultParams
} from '@defindex/sdk';

const sdk = new DefindexSDK({
  apiKey: process.env.DEFINDEX_API_KEY
});

async function completeVaultFlow() {
  try {
    // 1. Create a new vault
    const vaultConfig: CreateVaultParams = {
      roles: {
        emergencyManager: "GEMERGENCY_MANAGER_ADDRESS...",
        feeReceiver: "GFEE_RECEIVER_ADDRESS...",
        manager: "GVAULT_MANAGER_ADDRESS...",
        rebalanceManager: "GREBALANCE_MANAGER_ADDRESS..."
      },
      vaultFeeBps: 100, // 1% fee (100 basis points)
      assets: [{
        address: "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", // XLM asset
        strategies: [{
          address: "CCEE2VAGPXKVIZXTVIT4O5B7GCUDTZTJ5RIXBPJSZ7JWJCJ2TLK75WVW", // Strategy contract
          name: "XLM HODL Strategy",
          paused: false
        }]
      }],
      name: "My DeFi Vault",  // 1-32 characters
      symbol: "MDV",           // 1-10 characters
      upgradable: true,
      caller: "GCREATOR_ADDRESS..." // Public key of the signer account
    };

    const createResponse = await sdk.createVault(vaultConfig, SupportedNetworks.TESTNET);
    console.log('Vault XDR for signing:', createResponse.xdr);

    // Sign the XDR with your wallet here
    // const signedXDR = await yourWallet.sign(createResponse.xdr);
    // const txResult = await sdk.sendTransaction(signedXDR, SupportedNetworks.TESTNET);

    // 2. Deposit to vault
    const vaultAddress = 'CVAULT_CONTRACT_ADDRESS...';
    const depositData: DepositParams = {
      amounts: [1000000], // 1 XLM (7 decimals)
      caller: 'GUSER_ADDRESS...', // User's public key from which to sign and deposit
      invest: true, // Auto-invest after deposit
      slippageBps: 100 // 1% slippage tolerance
    };

    const depositResponse = await sdk.depositToVault(vaultAddress, depositData, SupportedNetworks.TESTNET);
    console.log('Deposit XDR for signing:', depositResponse.xdr);

    // Sign the deposit XDR with your wallet here
    // const signedDepositXDR = await yourWallet.sign(depositResponse.xdr);
    // const depositResult = await sdk.sendTransaction(signedDepositXDR, SupportedNetworks.TESTNET);

    // 3. Check balance after deposit
    const balance = await sdk.getVaultBalance(
      vaultAddress,
      'GUSER_ADDRESS...',
      SupportedNetworks.TESTNET
    );
    console.log(`New vault shares: ${balance.dfTokens}`);

    // 4. Withdraw from vault
    const withdrawData: WithdrawFromVaultParams = {
      amounts: [500000], // 0.5 XLM
      caller: 'GUSER_ADDRESS...',
      slippageBps: 100
    };

    const withdrawResponse = await sdk.withdrawFromVault(vaultAddress, withdrawData, SupportedNetworks.TESTNET);
    console.log('Withdrawal XDR for signing:', withdrawResponse.xdr);

  } catch (error) {
    console.error('Vault operation failed:', error.message);
  }
}
```

***

## Core Functions

### System Operations

#### Health Check

Monitor API availability and system status:

```typescript
const health = await sdk.healthCheck();
if (health.status.reachable) {
  console.log('API is healthy and operational');
} else {
  console.log('API health issues detected');
}
```

### Factory Operations

#### Get Factory Address

Retrieve the factory contract address for vault creation:

```typescript
const factory = await sdk.getFactoryAddress(SupportedNetworks.TESTNET);
console.log('Factory contract:', factory.address);
```

#### Create Vault

Deploy a new vault with custom configuration:

```typescript
const vaultConfig: CreateVaultParams = {
  roles: {
    emergencyManager: "GEMERGENCY_MANAGER...",
    feeReceiver: "GFEE_RECEIVER...",
    manager: "GVAULT_MANAGER...",
    rebalanceManager: "GREBALANCE_MANAGER..."
  },
  vaultFeeBps: 100,            // 1% vault fee
  assets: [{
    address: "CASSET_ADDRESS...", // Asset contract address
    strategies: [{
      address: "CSTRATEGY_ADDR...", // Strategy contract address
      name: "Strategy Name",
      paused: false
    }]
  }],
  name: "Vault Name",
  symbol: "VLT",
  upgradable: true,
  caller: "GCALLER_ADDRESS..."
};

const response = await sdk.createVault(vaultConfig, SupportedNetworks.TESTNET);
// Sign response.xdr with your wallet and submit via sendTransaction()
```

### Vault Operations

#### Get Vault Information

Query comprehensive vault details:

```typescript
const vaultInfo = await sdk.getVaultInfo(vaultAddress, SupportedNetworks.TESTNET);
console.log(`Vault: ${vaultInfo.name} (${vaultInfo.symbol})`);
console.log(`Total Assets: ${vaultInfo.totalAssets}`);
console.log(`Vault Fee: ${vaultInfo.feesBps.vaultFee / 100}%`);

// Display strategies
vaultInfo.assets.forEach((asset, index) => {
  console.log(`Asset ${index + 1}: ${asset.address}`);
  asset.strategies.forEach(strategy => {
    console.log(`  - ${strategy.name}: ${strategy.paused ? 'PAUSED' : 'ACTIVE'}`);
  });
});
```

#### Get User Balance

Check user's vault position:

```typescript
const balance = await sdk.getVaultBalance(
  vaultAddress,
  userAddress,
  SupportedNetworks.TESTNET
);
console.log(`Vault Shares: ${balance.dfTokens}`);
console.log(`Underlying Value: ${balance.underlyingBalance}`);
```

#### Deposit to Vault

Add funds to a vault:

```typescript
const depositData: DepositParams = {
  amounts: [1000000, 2000000], // Amounts for each vault asset
  caller: userAddress,
  invest: true, // Automatically invest after deposit
  slippageBps: 100 // 1% slippage tolerance
};

const response = await sdk.depositToVault(vaultAddress, depositData, SupportedNetworks.TESTNET);
// Sign response.xdr with the caller account and submit transaction
```

#### Withdraw from Vault

Remove funds by specifying amounts:

```typescript
const withdrawData: WithdrawFromVaultParams = {
  amounts: [500000], // Specific amounts to withdraw
  caller: userAddress,
  slippageBps: 100 // 1% slippage tolerance
};

const response = await sdk.withdrawFromVault(vaultAddress, withdrawData, SupportedNetworks.TESTNET);
// Sign response.xdr with the caller account and submit transaction
```

#### Withdraw by Shares

Remove funds by burning vault shares:

```typescript
const shareData: WithdrawSharesParams = {
  shares: 1000000, // Number of vault shares to burn
  caller: userAddress,
  slippageBps: 100
};

const response = await sdk.withdrawShares(vaultAddress, shareData, SupportedNetworks.TESTNET);
// Sign response.xdr with the caller account and submit transaction
```

#### Get Vault APY

Query current Annual Percentage Yield:

```typescript
const apy = await sdk.getVaultAPY(vaultAddress, SupportedNetworks.TESTNET);
console.log(`Current APY: ${apy.apyPercent}%`);
console.log(`Calculation period: ${apy.period}`);
```

### Administrative Operations

#### Emergency Rescue

Execute emergency asset recovery and pauses strategy (requires Emergency Manager role):

```typescript
const rescueData: RescueFromVaultParams = {
  strategy_address: 'CSTRATEGY_TO_RESCUE...',
  caller: 'GEMERGENCY_MANAGER_ADDRESS...'
};

const response = await sdk.emergencyRescue(vaultAddress, rescueData, SupportedNetworks.TESTNET);
console.log('Emergency rescue XDR:', response.transactionXDR);
// Sign and submit the transaction
```

#### Pause/Unpause Strategy

Control strategy operations (requires appropriate role):

```typescript
// Note: Ensure the caller has the necessary role to perform this operation
// Pause a strategy
await sdk.pauseStrategy(vaultAddress, {
  strategy_address: 'CSTRATEGY_ADDRESS...',
  caller: 'GMANAGER_ADDRESS...'
}, SupportedNetworks.TESTNET);

// Unpause a strategy
await sdk.unpauseStrategy(vaultAddress, {
  strategy_address: 'CSTRATEGY_ADDRESS...',
  caller: 'GMANAGER_ADDRESS...'
}, SupportedNetworks.TESTNET);
```

### Transaction Management

#### Submit Signed Transactions

Send signed XDR to the Stellar network:

```typescript
const response = await sdk.sendTransaction(
  signedXDR,
  SupportedNetworks.TESTNET
);

console.log('Transaction hash:', response.txHash);
console.log('Success:', response.success);
console.log('Result:', response.result);
```

***

## Error Handling

The SDK provides comprehensive error handling with specific error types:

```typescript
import {
  isApiError,
  isAuthError,
  isValidationError,
  isNetworkError
} from '@defindex/sdk';

try {
  const vaultInfo = await sdk.getVaultInfo(vaultAddress, network);
} catch (error) {
  if (isAuthError(error)) {
    console.error('Authentication failed:', error.message);
    // Check API key configuration
  } else if (isValidationError(error)) {
    console.error('Validation error:', error.message);
    // Check input parameters
  } else if (isNetworkError(error)) {
    console.error('Network error:', error.message);
    // Handle blockchain/network issues
  } else {
    console.error('Unknown error:', error.message);
  }
}
```

## Security Best Practices

1. **Environment Variables**: Always store API keys in environment variables

```typescript
const sdk = new DefindexSDK({
  apiKey: process.env.DEFINDEX_API_KEY // Never hardcode credentials
});
```

2. **Error Handling**: Always wrap API calls in try-catch blocks

```typescript
try {
  const result = await sdk.someOperation();
  // Handle success
} catch (error) {
  // Handle error appropriately
  console.error('Operation failed:', error.message);
}
```

3. **Server-Side Only**: This SDK is designed for server-side use only
4. **Role Management**: Understand vault roles and permissions before administrative operations

***

## Running Examples

The SDK includes a comprehensive functional example demonstrating all features:

```bash
# Navigate to SDK directory
cd /path/to/defindex-sdk

# Install dependencies
pnpm install

# Copy environment configuration
cp .env.example .env

# Edit .env with your API key
# DEFINDEX_API_KEY=sk_your_api_key_here

# Run the complete example
pnpm run example
```

The example demonstrates:

* SDK initialization and authentication
* API health checking
* Factory operations and vault creation
* Vault deposits and withdrawals
* Administrative vault management
* Error handling patterns

## TypeScript Support

The SDK provides full TypeScript support with comprehensive type definitions:

```typescript
import {
  DefindexSDK,
  DefindexSDKConfig,
  SupportedNetworks,
  CreateVaultParams,
  DepositParams,
  WithdrawParams,
  VaultInfo,
  VaultBalance,
  VaultAPY
} from '@defindex/sdk';
```

## Support and Resources

* **API Documentation**: <https://api.defindex.io/docs>
* **GitHub Repository / SDK documentation**: <https://github.com/paltalabs/defindex-sdk>
* **Developer Support / Discord Community**: [Join our Discord](https://discord.gg/ftPKMPm38f)

For additional questions or integration support, please reach out to our developer support team.


# Flutter SDK

⏱️ 2 min read

Welcome to the DeFindex Flutter SDK documentation! This SDK enables you to integrate DeFindex's savings account functionality into your Flutter application. With this SDK, your users can:

1. Deposit funds into a DeFindex vault
2. Check their vault balance
3. Withdraw funds from their vault
4. View the current APY (Annual Percentage Yield) of their vault

## Prerequisites

Before integrating the SDK, you'll need to deploy a vault contract for your application. You can do this through our [DeFindex DApp](https://app.defindex.io/). Make sure to thoroughly understand vault management and operations before proceeding.

For detailed instructions on creating, deploying, and managing vaults, please refer to our [Creating a DeFindex Vault](/api-integration-guide/creating-a-defindex-vault) guide.

## Integration Guide

### 1. Add the SDK to Your Project

Add the following dependency to your `pubspec.yaml` file:

```yaml
dependencies:
  defindex_sdk: ^1.0.1
```

### 2. Import the SDK

Import the SDK in your `main.dart` file:

```dart
import 'package:defindex_sdk/defindex_sdk.dart';
```

## Quick Start

The Flutter SDK makes it incredibly simple to integrate DeFindex vault functionality into your app. With just three lines of code, you can set up a vault and enable deposits! Here's what you need to do:

1. **Get Your Vault Contract Address:** Retrieve the contract address for your vault from the DeFindex DApp
2. **Initialize the Vault:** Create a vault instance in your code
3. **Implement Vault Functions:** Use `vault.deposit`, `vault.balance`, or `vault.withdraw` as needed

## Implementation Example

Here's a practical example demonstrating how to create a vault instance and implement a deposit function:

```dart
import 'package:defindex_sdk/defindex_sdk.dart';

// Initialize the vault
var vault = Vault(
  sorobanRPCUrl: 'https://soroban-testnet.stellar.org', // Your RPC URL
  network: SorobanNetwork.TESTNET, // Your network
  contractId: 'CD76H2IVRMRMLE4KZXLAVK3L3CO7PENUB3X4VB2FQVUAFVAJMQYQIFDE', // Your vault contract address
);

// Execute a deposit
String? transactionHash = await vault.deposit(
  'GCW36WQUHJASZVNFIIL7VZQWL6Q72XT6TAU6N3XMFGTLSNE2L7LMJNWT', // User's Stellar address
  100.0, // Deposit amount
  (transaction) async => signerFunction(transaction),
);

print('Transaction hash: $transactionHash');

// Display transaction result to user
ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text('Transaction hash: $transactionHash')),
);
```


# About Us

⏱️ 1 min read

**DeFindex** **🔁** is DeFi infrastructure developed by **PaltaLabs 🥑** an innovative blockchain hub based in Latin America and focused on the Stellar ecosystem. It enables wallet providers to easily integrate automated investment strategies through modular smart contracts including Vaults, Strategies, and Factory that connect DeFi protocols to accessible user interfaces.

Our mission is to **democratize access to global financial products**, allowing any wallet to offer yield opportunities with no technical friction, optimized performance, and a seamless user experience.

Follow PaltaLabs:

<https://x.com/PaltaLabs>


# Media Kit

Official DeFindex brand assets and design guidelines

Official brand assets and design guidelines for DeFindex.

***

## Brand Identity

**Name:** DeFindex (always written with capital "D" and "F")

**Domain:** defindex.io

**Sector:** DeFi (Decentralized Finance) on Stellar/Soroban

**Value Proposition:** Automated savings accounts with diversified DeFi portfolio

**Brand Personality:** Professional, accessible, modern, trustworthy. DeFindex simplifies DeFi for everyone.

### Official Taglines

* "DeFi made easy."
* "Empower your wallet."
* "We grow together."
* "Achieve more with automated DeFi."

### Tone of Voice

* Clear, direct, without unnecessary technical jargon
* Conveys trust and simplicity
* Inclusive: addresses both new and experienced DeFi users
* Never alarmist, never exaggerated hype
* Professional but approachable

***

## Usage Guidelines

* Use assets exactly as provided without modifications
* Maintain safety margin around logos (at least symbol height on each side)
* Minimum logo size: 40px height (digital) / 14mm (print)
* Never rotate, distort, or change opacity
* Never apply logo on backgrounds with insufficient contrast
* For minimal applications (favicons, social avatars), use only the Symbol

***

## DeFindex Symbol

Two intertwined arrows forming a cyclical pattern, representing automated reinvest.

| Light Background                                | Dark Background                              | Monochrome                                   |
| ----------------------------------------------- | -------------------------------------------- | -------------------------------------------- |
| ![Symbol Gradient](/files/YKnfyq3CK50W52XhNjg3) | ![Symbol White](/files/HHu43v9IuJ4IS7FbPP15) | ![Symbol Black](/files/nqDF4ZSFf1SzFeif1qwR) |

***

## DeFindex Logo

Complete signature: Symbol + Wordmark in Familjen Grotesk Bold.

### Horizontal

| Light Background                                         | Dark Background                                       | Monochrome                                            |
| -------------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------- |
| ![Logo Horizontal Gradient](/files/H7scddJZFBzl4S8QI5dB) | ![Logo Horizontal White](/files/RQzcHrxz03q4OiqGPZBt) | ![Logo Horizontal Black](/files/A7CnfJJU4E48pxng9Ra2) |

### Vertical

| Light Background                                       | Dark Background                                     | Monochrome                                          |
| ------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------- |
| ![Logo Vertical Gradient](/files/u0iHowyGpGia7hAHZKYE) | ![Logo Vertical White](/files/hHA4sp6TMrrlhOWbbBPy) | ![Logo Vertical Black](/files/2ekCRsziWiZAseRVwFxa) |

### Logo Color Variants

| Context                    | Symbol             | Wordmark             |
| -------------------------- | ------------------ | -------------------- |
| Light background (primary) | Lavender `#DEC9F4` | Dark Green `#014751` |
| Dark background (primary)  | Lavender `#DEC9F4` | White `#FFFFFF`      |
| Monochrome light           | Dark Green         | Dark Green           |
| Monochrome dark            | White              | White                |

***

## Color Palette

| Color           | HEX       | RGB           | Usage                                 |
| --------------- | --------- | ------------- | ------------------------------------- |
| **White**       | `#FFFFFF` | 255, 255, 255 | Light backgrounds, text on dark       |
| **Dark Green**  | `#014751` | 1, 71, 81     | Primary. Titles, text, backgrounds    |
| **Lavender**    | `#DEC9F4` | 221, 201, 244 | Primary. Logo symbol, accents         |
| **Light Green** | `#D3FFB4` | 211, 255, 180 | Accent. Highlights, soft backgrounds  |
| **Light Cyan**  | `#D3FBFF` | 211, 251, 255 | Accent. Backgrounds, graphic elements |
| **Coral**       | `#FC5B31` | 252, 91, 49   | Strong accent. Emphasis, CTAs         |

### Color Usage Rules

* **White, Dark Green, and Lavender** are frequently used: titles, subtitles, descriptive text
* **Light Green, Light Cyan, and Coral** are accent colors: highlights, boxes, buttons
* **Coral** is used for emphasis and keywords in italics within headlines
* Always ensure contrast and legibility
* **Never** use colors outside this palette

***

## Typography

| Font                 | Usage                            | Weights                         |
| -------------------- | -------------------------------- | ------------------------------- |
| **Familjen Grotesk** | Headlines, titles, featured info | Regular, Medium, SemiBold, Bold |
| **Inter Tight**      | Subtitles, body text             | Regular, Medium, SemiBold       |

Both fonts are available on [Google Fonts](https://fonts.google.com/).

### Typography Rules

* Headlines: **Familjen Grotesk** Bold or SemiBold
* Body text: **Inter Tight** Regular or Medium
* Emphasis keyword in headlines: **italic** + **Coral** color
* Maintain clear hierarchy
* Never mix more than 2 font weights in the same piece
* Fallback: `system-ui, -apple-system, "Segoe UI", sans-serif`

***

## Visual Elements

### Glass Elements (3D)

Translucent 3D shapes with glassmorphism aesthetic, colored in **duotone** using brand palette combinations.

|                                          |                                          |                                          |
| ---------------------------------------- | ---------------------------------------- | ---------------------------------------- |
| ![Glass 01](/files/I6D3wFypjycEK65rSItA) | ![Glass 03](/files/ASXJxd1U1Bd3HJu2dkfr) | ![Glass 05](/files/4FjI6wXEYfEGhTVTIfWq) |

**Rules:**

* Always translucent, never solid or opaque
* Types: interlocking tori/rings, crystalline shapes, fluid blobs
* Use combinations like Dark Green + Lavender, Coral + Light Cyan

### Gradients

Soft, diffused gradients using brand palette colors.

|                                             |                                             |
| ------------------------------------------- | ------------------------------------------- |
| ![Gradient 01](/files/Aenph1kYIC7NJHlNq6DY) | ![Gradient 03](/files/G3FUjELyw5wyWYLCGHbk) |

**Approved combinations:**

* Cyan → Light Green → Lavender
* Coral → Light Cyan
* Lavender → Coral
* Light Cyan → Lavender
* Light Green → Lavender → Coral

Gradients must be **soft and diffused**, never with abrupt transitions.

***

## Icons

| Vault                                 | Strategies                                 | User                                 | Graph                                 | Group                                 |
| ------------------------------------- | ------------------------------------------ | ------------------------------------ | ------------------------------------- | ------------------------------------- |
| ![Vault](/files/317O9YWirsOQzqx3vlH4) | ![Strategies](/files/rZ7lB758B32LFzkK5n3Z) | ![User](/files/y6xC1shYfgfKrOQDsYUp) | ![Graph](/files/2z1lrl6bMFuVLYTqfrrR) | ![Group](/files/jebscLoXedg50jo7Xm7Q) |

***

## Do's and Don'ts

### ✅ Do

* Use generous whitespace - the brand breathes
* Prioritize clean backgrounds (white, Dark Green, or soft gradients)
* Use glass 3D elements as hero visuals
* Write "DeFindex" with capital D and F

### ❌ Don't

* Use colors outside the 6-color palette
* Rotate, distort, or alter the logo
* Apply logo as watermark with reduced opacity
* Use aggressive "crypto bro" aesthetic (rockets, moons, diamonds)
* Create visually noisy or saturated compositions
* Create opaque 3D elements - always translucent glass aesthetic


# Terms of Use

⏱️ 1 min read

Your access to and use of DeFindex — including our websites, web applications, APIs, smart contracts, and all associated services — is governed by the DeFindex **Terms of Use**.

DeFindex develops and operates non-custodial blockchain software infrastructure. It is not a financial institution, custodian, broker, or investment adviser, and never holds or controls your private keys or digital assets. Before interacting with the protocol, please review the full Terms, including the risk disclosures and the arbitration and class-action waiver provisions.

{% hint style="info" %}
Read the complete and legally binding document here: [**DeFindex Terms of Use**](https://defindex.io/terms).
{% endhint %}

For questions regarding the Terms, contact us at <hello@defindex.io>.


