> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Earn

> Deposit into yield vaults, track positions and rewards, and withdraw with the Dynamic Kotlin SDK.

## Overview

Earn lets a user's wallet deposit into a yield vault, track what the position is worth, claim reward campaigns, and withdraw. `sdk.earn` wraps the Earn API and signs and broadcasts the transactions it returns.

For how vaults, shares, and yield work, see the [Earn overview](/docs/earn/overview.md).

Every Earn action returns an unsigned payload. You sign and broadcast it with `execute`, which sends the ERC-20 approval first and waits for it to confirm whenever the payload carries one.

## Prerequisites

* Dynamic SDK initialized (see [Quickstart](/docs/kotlin/quickstart.md))
* User authenticated (see [Authentication](/docs/kotlin/authentication.md))
* An EVM wallet (see [Wallet Creation](/docs/kotlin/wallet-creation.md)). EVM is the only chain with live vaults today.
* Earn enabled for your environment in the Dynamic dashboard. Until it is, every call throws an `EarnError` with `statusCode` 403.

Earn is available on the SDK instance as `sdk.earn`:

```kotlin theme={"system"}
val earn = sdk.earn
```

## Browse vaults

`getVaults` returns the vaults your environment can access. `getVaultDetails` adds the on-chain fields you need to display and transact against one, including `assetDecimals`.

```kotlin theme={"system"}
val vaults = sdk.earn.getVaults()

vaults.forEach { vault ->
    println("${vault.assetSymbol} by ${vault.curator} on chain ${vault.chainId}")
    println("Net APY: ${vault.netApy}, TVL: ${vault.tvlUsd}")
}

val vault = sdk.earn.getVaultDetails(vaults.first().vaultId)
```

`netApy` and `tvlUsd` come from live market data on a best-effort basis, so both are null when that data is unavailable.

## Read a position

A position holds `shares` and `assets`, both in raw token units. Show `assets`, because it grows as yield accrues. Depending on the vault's provider, `shares` either stays flat or always equals `assets`, so it is not meaningful to show a user.

```kotlin theme={"system"}
val position = sdk.earn.getVaultPosition(
    vaultId = vault.vaultId,
    ownerAddress = wallet.address
)

val assets = position.assets.toBigInteger()
val balance = assets.toBigDecimal(vault.assetDecimals)

println("Balance: $balance ${vault.assetSymbol}")
```

## Read rewards

Rewards are a separate bonus some vaults pay in another token. They sit unclaimed until you claim them, unlike base yield, which shows up as `assets` growing.

```kotlin theme={"system"}
val rewards = sdk.earn.getVaultRewards(
    vaultId = vault.vaultId,
    ownerAddress = wallet.address
)

rewards.forEach { reward ->
    println("${reward.accrued} raw units of ${reward.tokenSymbol}")
}
```

The list is empty when the wallet has accrued nothing in the vault. Rewards are supported only for vaults from some providers. For a vault from any other provider, the API rejects the rewards and claim requests with an HTTP 400 error, which surfaces as an `EarnError`. See [Provider support](/docs/earn/overview.md#provider-support).

## Deposit

`deposit` builds the payload, `execute` sends it. `amount` is human-readable, so `"12.5"` deposits 12.5 USDC.

```kotlin theme={"system"}
val deposit = sdk.earn.deposit(
    vaultId = vault.vaultId,
    amount = "12.5",
    ownerAddress = wallet.address
)

val transactionHash = sdk.earn.execute(
    payload = deposit.signingPayload,
    wallet = wallet,
    onStepChange = { step ->
        when (step) {
            EarnExecutionStep.APPROVAL -> println("Approving the vault to spend the deposit")
            EarnExecutionStep.TRANSACTION -> println("Sending the vault transaction")
        }
    }
)
```

`execute` returns the vault transaction hash. When the payload carries an `evmApproval`, it sends that approval first and waits for its receipt, so `onStepChange` reports `APPROVAL` before `TRANSACTION`.

## Withdraw

Withdrawals redeem shares the wallet already holds, so they never need an approval.

```kotlin theme={"system"}
val withdrawal = sdk.earn.withdraw(
    vaultId = vault.vaultId,
    amount = "12.5",
    ownerAddress = wallet.address
)

val transactionHash = sdk.earn.execute(
    payload = withdrawal.signingPayload,
    wallet = wallet
)
```

## Claim rewards

```kotlin theme={"system"}
try {
    val claim = sdk.earn.claim(
        vaultId = vault.vaultId,
        ownerAddress = wallet.address
    )

    val transactionHash = sdk.earn.execute(
        payload = claim.signingPayload,
        wallet = wallet
    )
} catch (e: EarnError) {
    if (e.nothingToClaim) {
        println("Nothing to claim in this vault")
    }
}
```

## Amount units

Amounts cross the wire as strings in two different units:

* The `amount` you pass to `deposit` and `withdraw` is human-readable: `"12.5"` means 12.5 USDC.
* `EarnVaultPosition`, `EarnVaultReward`, and the amounts inside a signing payload are raw token units. They can exceed an `Int`, so parse them with `BigInteger`.

> [!WARNING]
>
> Mixing the two units up silently transacts the wrong value. Human-readable amounts also need a
> leading zero: `"0.01"` works, `".01"` is rejected by the API.

## Handle errors

Every failed Earn call throws an `EarnError` carrying the HTTP `statusCode`:

| Condition | What it means |
| - | - |
| `statusCode` 403 | Earn is not enabled for your environment |
| `nothingToClaim` (422 on claim) | The wallet has no claimable rewards in the vault |
| `statusCode` 400 | The vault's provider does not support the request, such as rewards |
| Other status codes | The API rejected the request, for example an invalid amount or address |

```kotlin theme={"system"}
try {
    val vaults = sdk.earn.getVaults()
} catch (e: EarnError) {
    println("Earn failed (${e.statusCode}): ${e.message}")
}
```

`execute` also throws an `EarnError` when the wallet's chain does not match the payload, when the payload carries no transaction, or when the approval reverts or is not confirmed in time.

## Next steps

* [ERC-20 Token Transfers](/docs/kotlin/wallets/evm/erc20-transfers.md) to move the vault asset in and out of the wallet
* [Earn overview](/docs/earn/overview.md) for vault, share, and yield concepts


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.