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

# Earn with Yield.xyz

> Learn how to discover, enter, manage, and exit yield opportunities.

Portal's iOS SDK provides comprehensive yield opportunities capabilities through the `portal.yield.yieldxyz` API. This guide covers discovering yield opportunities, entering positions, managing existing positions, and exiting yield opportunities.

## Overview

The yield functionality allows users to:

* **Discover** available yield opportunities across different protocols and networks
* **Enter** yield positions by depositing tokens into yield opportunities
* **Manage** existing positions (claim rewards, voting, etc.)
* **Exit** yield positions to withdraw aggregated tokens and rewards
* **Track** yield balances and historical yield actions

## Prerequisites

Before using yield operations, ensure you have:

* A properly initialized Portal client
* An active wallet with the required token(s) on the target network (see [Create a wallet](./create-a-wallet))
* Yield.xyz integration enabled in your Portal Dashboard (see [Yield.xyz Integration](../../../resources/integrations/yield-xyz))

## Discovering Yield Opportunities

Use the `discover` method to find available yield opportunities.

For complete API documentation, see the [Yield.xyz API reference](https://docs.yield.xyz/reference/yieldscontroller_getyields).

```swift theme={null}
let request = YieldXyzGetYieldsRequest(
    offset: 0,
    limit: 10,
    network: "eip155:11155111" // Sepolia network
    // ... other parameters
)

do {
    let response = try await portal.yield.yieldxyz.discover(request: request)
    if let rawResponse = response.data?.rawResponse {
        let yieldOpportunities = rawResponse.items

        // Process and display yield opportunities
    }
} catch {
    // Handle error
    print("Error discovering yields: \(error)")
}
```

<Tip>
  Popular, high-quality USDC yield options with no lockups or limits:

  * USDC Aave V3 Lending:
    * `base-usdc-aave-v3-lending`
  * USDC Fluid Vault:
    * `base-usdc-fusdc-0xf42f5795d9ac7e9d757db633d693cd548cfd9169-4626-vault`
  * USDC Spark Savings Vault:
    * `ethereum-usdc-spusdc-0x28b3a8fb53b741a8fd78c0fb9a6b2393d896a43d-4626-vault`
</Tip>

## Entering Yield Positions

To enter a yield position, first discover the specific yield, then use the `enter` method.

For complete API documentation, see the [Yield.xyz enter yield reference](https://docs.yield.xyz/reference/actionscontroller_enteryield).

For the example below, we will use the yield opportunity with the ID `"ethereum-sepolia-link-aave-v3-lending"`. Fund your Portal client with the required `LINK` token to enter the position.

```swift theme={null}
do {
    let userAddress = try await portal.getAddress()

    let enterRequest = YieldXyzEnterRequest(
        yieldId: "ethereum-sepolia-link-aave-v3-lending",
        address: userAddress,
        arguments: YieldXyzEnterArguments(
            amount: "1" // 1 LINK token
        )
    )

    let enterResponse = try await portal.yield.yieldxyz.enter(request: enterRequest)
    if let rawResponse = enterResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error entering yield position: \(error)")
}
```

## Checking Yield Balances

Retrieve current yield positions and balances.

For complete API documentation, see the [Yield.xyz get balances reference](https://docs.yield.xyz/reference/yieldscontroller_getaggregatebalances).

```swift theme={null}
do {
    let userAddress = try await portal.getAddress()

    let balanceRequest = YieldXyzGetBalancesRequest(
        queries: [
            YieldXyzBalanceQuery(
                address: userAddress,
                network: "eip155:11155111" // Sepolia testnet
            )
        ]
    )

    let response = try await portal.yield.yieldxyz.getBalances(request: balanceRequest)
    if let rawResponse = response.data?.rawResponse {
        let yieldPositions = rawResponse.items

        // Process and display yield positions information
    }
} catch {
    // Handle error
    print("Error getting balances: \(error)")
}
```

<Note>
  We recommend always specifying a `yieldId` on each balance query. When `yieldId` is provided, Yield.xyz can resolve balances directly, so you don't need to call the `track` endpoint after entering or exiting positions.

  ```swift theme={null}
  let balanceRequest = YieldXyzGetBalancesRequest(
      queries: [
          YieldXyzBalanceQuery(
              address: userAddress,
              network: "eip155:11155111",
              yieldId: "ethereum-sepolia-link-aave-v3-lending"
          )
      ]
  )
  ```
</Note>

## Exiting Yield Positions

Use the `exit` method to withdraw from yield positions.

For complete API documentation, see the [Yield.xyz exit yield reference](https://docs.yield.xyz/reference/actionscontroller_exityield).

```swift theme={null}
do {
    let userAddress = try await portal.getAddress()

    let exitRequest = YieldXyzExitRequest(
        yieldId: "ethereum-sepolia-link-aave-v3-lending",
        address: userAddress,
        arguments: YieldXyzEnterArguments(amount: "0.001")
    )

    let exitResponse = try await portal.yield.yieldxyz.exit(request: exitRequest)
    if let rawResponse = exitResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error exiting yield position: \(error)")
}
```

## Managing Yield Positions

If your Portal client has entered into a yield balance, they may have a yield balance that has an available `pendingActions`. You can use the `manage` method to perform actions on existing yield positions. For example, if the balance has a `pendingAction` of `WITHDRAW` or `CLAIM_REWARDS`, you can use the `manage` method to withdraw or claim rewards from the yield balance.

For complete API documentation, see the [Yield.xyz manage yield reference](https://docs.yield.xyz/reference/actionscontroller_manageyield).

```swift theme={null}
do {
    let userAddress = try await portal.getAddress()

    let manageRequest = YieldXyzManageYieldRequest(
        yieldId: "ethereum-sepolia-link-aave-v3-lending",
        address: userAddress,
        action: YieldActionType.WITHDRAW, // Replace with the balance's `pendingAction` item's `type` value.
        passthrough: "eyJhZGRyZXNzZXMiOnsiYWRkcmVzcyI6ImNvc21vczF5ZXk..." // Replace with the balance's `pendingAction` item's `passthrough` value.
    )

    let manageResponse = try await portal.yield.yieldxyz.manage(request: manageRequest)
    if let rawResponse = manageResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error managing yield position: \(error)")
}
```

## Getting Historical Actions

Retrieve the history of yield actions for an address.

For complete API documentation, see the [Yield.xyz get actions reference](https://docs.yield.xyz/reference/actionscontroller_getactions).

```swift theme={null}
do {
    let userAddress = try await portal.getAddress()

    let request = YieldXyzGetHistoricalActionsRequest(address: userAddress)
    let response = try await portal.yield.yieldxyz.getHistoricalActions(request: request)

    if let rawResponse = response.data?.rawResponse {
        let pastActions = rawResponse.items

        // Process and display past yield actions
    }
} catch {
    // Handle error
    print("Error getting historical actions: \(error)")
}
```

## Transaction Processing

Yield operations can require multiple transactions. Process them sequentially, submit each, track it, and wait for on-chain confirmation (e.g. using `eth_getTransactionReceipt`) before proceeding to the next.

For complete API documentation, see the [Yield.xyz submit transaction hash reference](https://docs.yield.xyz/reference/transactionscontroller_submittransactionhash) and [get transaction details reference](https://docs.yield.xyz/reference/transactionscontroller_gettransaction).

<Tip>
  For account abstraction enabled Portal clients, use `eth_getUserOperationReceipt` instead of `eth_getTransactionReceipt` to wait for confirmation, since signing returns a *user operation hash*, not a transaction hash.

  If you don't specify a `yieldId` on your balance queries, you'll need to call `track` after each transaction so Yield.xyz can attribute the position. Pass the **transaction hash** (extracted from `response.result.receipt.transactionHash` for AA clients), not the user operation hash.
</Tip>

```swift theme={null}
func processTransactions(_ transactions: [YieldActionTransaction]) async throws {
    let sorted = transactions.sorted { $0.stepIndex < $1.stepIndex }
    for tx in sorted {
        if tx.unsignedTransaction != nil && tx.status == YieldActionTransactionStatus.CREATED {
            let success = await signAndSubmitAndConfirm(transaction: tx)
            if !success { break }
        }
    }
}

func signAndSubmitAndConfirm(transaction: YieldActionTransaction) async -> Bool {
    guard let unsignedTxJson = transaction.unsignedTransaction as? String else {
        return false
    }

    // Parse the unsigned transaction JSON string
    guard let jsonData = unsignedTxJson.data(using: .utf8),
          let txParams = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
    else {
        return false
    }

    // Create ETHTransactionParam from the parsed JSON
    let ethTransaction = ETHTransactionParam(
        from: txParams["from"] as? String ?? "",
        to: txParams["to"] as? String ?? "",
        value: txParams["value"] as? String ?? "0x0",
        data: txParams["data"] as? String ?? "0x"
        // Portal handles gas estimation automatically
    )

    do {
        // Sign and send the transaction
        let sendResponse = try await portal.request(
            transaction.network,
            withMethod: .eth_sendTransaction,
            andParams: [ethTransaction]
        )

        guard let txHash = sendResponse.result as? String else {
            return false
        }

        // Track the transaction with the yield system
        _ = try await portal.yield.yieldxyz.track(
            transactionId: transaction.id,
            txHash: txHash
        )

        // Wait for transaction confirmation
        return await waitForReceipt(txHash: txHash, chainId: transaction.network)
    } catch {
        print("Error signing and submitting transaction: \(error)")
        return false
    }
}

func waitForReceipt(
    txHash: String,
    chainId: String,
    maxAttempts: Int = 30,
    delaySeconds: UInt64 = 2
) async -> Bool {
    for _ in 0..<maxAttempts {
        try? await Task.sleep(nanoseconds: delaySeconds * 1_000_000_000)

        do {
            let response = try await portal.request(
                chainId,
                withMethod: .eth_getTransactionReceipt,
                andParams: [txHash]
            )

            if let innerResponse = response.result as? EthTransactionResponse,
               let status = innerResponse.result?.status {
                if status == "0x1" {
                    return true  // Transaction succeeded
                } else if status == "0x0" {
                    return false // Transaction reverted
                }
            }
        } catch {
            // Continue waiting if request fails
            continue
        }
    }

    return false // Timeout
}
```

## Best Practices

1. **Always check yield availability** before attempting to enter positions
2. **Process transactions sequentially** as yield operations often require multiple steps and are dependent on previous transactions being mined successfully
3. **Handle network errors gracefully** and provide user feedback
4. **Monitor transaction status** and provide progress updates to users
5. **Validate user balances** before initiating yield operations

## Supported Networks

The yield functionality supports various networks including:

* Monad (`eip155:143`)
* Monad Testnet (`eip155:10143`)
* Arbitrum (`eip155:42161`)
* Avalanche C (`eip155:43114`)
* Base (`eip155:8453`)
* Base Sepolia (`eip155:84532`)
* Celo (`eip155:42220`)
* Core (`eip155:1116`)
* Ethereum (`eip155:1`)
* Ethereum Sepolia (`eip155:11155111`)
* Fantom (`eip155:250`)
* Gnosis (`eip155:100`)
* Harmony (`eip155:1666600000`)
* Hyperevm (`eip155:999`)
* Katana (`eip155:747474`)
* Linea (`eip155:59144`)
* Moonriver (`eip155:1285`)
* Optimism (`eip155:10`)
* Optimism Sepolia (`eip155:11155420`)
* Plasma (`eip155:9745`)
* Polygon (`eip155:137`)
* Polygon Amoy (`eip155:80002`)
* Sonic (`eip155:146`)
* Unichain (`eip155:130`)
* Viction (`eip155:88`)
* zkSync (`eip155:324`)
* Solana (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`)
* Solana Devnet (`solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`)
* Stellar (`stellar:pubnet`)
* Stellar Testnet (`stellar:testnet`)
* Tron (`tron:mainnet`)

## Next Steps

* Learn about [managing wallet lifecycle states](./manage-wallet-lifecycle-states)
* Explore [transaction simulation](./evaluate-a-transaction)
* Check out [Portal API methods](./portal-api-methods)
