> ## 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 Flutter SDK provides comprehensive yield opportunities capabilities through the `portal.yieldIntegrations.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).

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final response = await portal.yieldIntegrations.yieldxyz.discover(
  request: YieldXyzGetYieldsRequest(
    offset: 0,
    limit: 10,
    network: 'eip155:11155111', // Sepolia network
    // ... other parameters
  ),
);

final yieldOpportunities = response.items;

// Process and display yield opportunities
for (final opp in yieldOpportunities.whereType<YieldOpportunity>()) {
  print('${opp.name}: ${opp.rewardRate}% ${opp.rewardRateType.name}');
}
```

<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.

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final userAddress = await portal.getAddress('eip155:11155111');

final enterResponse = await portal.yieldIntegrations.yieldxyz.enter(
  yieldId: 'ethereum-sepolia-link-aave-v3-lending',
  address: userAddress, // Optional - defaults to current wallet address
  amount: '1', // 1 LINK token
);

final transactions = enterResponse.transactions;

// Process transactions, this is described in the "Transaction Processing" section below
await processTransactions(transactions, 'eip155:11155111');
```

## 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).

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final userAddress = await portal.getAddress('eip155:11155111');

final response = await portal.yieldIntegrations.yieldxyz.getBalances(
  queries: [
    YieldXyzGetBalancesQuery(
      address: userAddress,
      network: 'eip155:11155111', // Sepolia testnet
    ),
  ],
);

final yieldPositions = response.items;

// Process and display yield positions information
for (final item in yieldPositions.whereType<YieldBalance>()) {
  for (final balance in item.balances.whereType<YieldBalanceItem>()) {
    print('${balance.amount} ${balance.token.symbol} (earning: ${balance.isEarning})');
  }
}
```

<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.

  ```dart theme={null}
  final response = await portal.yieldIntegrations.yieldxyz.getBalances(
    queries: [
      YieldXyzGetBalancesQuery(
        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).

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final userAddress = await portal.getAddress('eip155:11155111');

final exitResponse = await portal.yieldIntegrations.yieldxyz.exit(
  yieldId: 'ethereum-sepolia-link-aave-v3-lending',
  address: userAddress, // Optional - defaults to current wallet address
  amount: '0.001',
);

final transactions = exitResponse.transactions;

// Process transactions, this is described in the "Transaction Processing" section below
await processTransactions(transactions, 'eip155:11155111');
```

## 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).

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final userAddress = await portal.getAddress('eip155:11155111');

final manageResponse = await portal.yieldIntegrations.yieldxyz.manage(
  yieldId: 'ethereum-sepolia-link-aave-v3-lending',
  address: userAddress,
  action: 'WITHDRAW', // Replace with the balance's `pendingAction` item's `type` value.
  passthrough: 'eyJhZGRyZXNzZXMiOnsiYWRkcmVzcyI6ImNvc21vczF5ZXk...', // Replace with the balance's `pendingAction` item's `passthrough` value.
);

final transactions = manageResponse.transactions;

// Process transactions, this is described in the "Transaction Processing" section below
await processTransactions(transactions, 'eip155:11155111');
```

<Warning>
  The `passthrough` parameter is required and must come from a balance's pending action. Call `getBalances()` first, find a `YieldBalanceItem` with non-empty `pendingActions`, and use the `passthrough` value from the relevant pending action. The SDK throws a `PortalException` with code `MISSING_PASSTHROUGH` if it is null or empty.
</Warning>

## 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).

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

final userAddress = await portal.getAddress('eip155:11155111');

final response = await portal.yieldIntegrations.yieldxyz.getHistoricalActions(
  address: userAddress,
);

final pastActions = response.items;

// Process and display past yield actions
for (final action in pastActions.whereType<YieldXyzHistoricalActionItem>()) {
  print('${action.intent.name}: ${action.status.name} - ${action.amount} (${action.createdAt})');
}
```

## 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>

```dart theme={null}
import 'dart:convert';
import 'package:portal_flutter/portal_flutter.dart';

final portal = Portal();

Future<void> processTransactions(
  List<YieldXyzActionTransaction?> transactions,
  String chainId,
) async {
  final sorted = transactions
      .whereType<YieldXyzActionTransaction>()
      .toList()
    ..sort((a, b) => a.stepIndex.compareTo(b.stepIndex));

  for (final tx in sorted) {
    if (tx.unsignedTransaction != null &&
        tx.status == YieldXyzActionTransactionStatus.created) {
      final success = await signAndSubmitAndConfirm(tx, chainId);
      if (!success) break;
    }
  }
}

Future<bool> signAndSubmitAndConfirm(
  YieldXyzActionTransaction transaction,
  String chainId,
) async {
  final unsignedTxJson = transaction.unsignedTransaction;
  if (unsignedTxJson == null) return false;

  // Parse the unsigned transaction JSON string
  final txParams = jsonDecode(unsignedTxJson) as Map<String, dynamic>;

  try {
    // Sign and send the transaction
    final txHash = await portal.sendTransaction(
      chainId: chainId,
      to: txParams['to'] as String,
      value: txParams['value'] as String? ?? '0x0',
      data: txParams['data'] as String?,
      // Portal handles gas estimation automatically
    );

    // Track the transaction with the yield system
    await portal.yieldIntegrations.yieldxyz.track(
      transaction.id,
      txHash,
    );

    // Wait for transaction confirmation
    return await waitForReceipt(txHash: txHash, chainId: chainId);
  } catch (e) {
    print('Error signing and submitting transaction: $e');
    return false;
  }
}

Future<bool> waitForReceipt({
  required String txHash,
  required String chainId,
  int maxAttempts = 30,
  Duration delay = const Duration(seconds: 2),
}) async {
  for (var i = 0; i < maxAttempts; i++) {
    await Future.delayed(delay);

    try {
      final response = await portal.request(
        chainId: chainId,
        method: 'eth_getTransactionReceipt',
        params: [txHash],
      );

      if (response.result != null) {
        final receipt = jsonDecode(response.result!) as Map<String, dynamic>?;
        if (receipt != null) {
          final status = receipt['status'] as String?;
          if (status == '0x1') return true;  // Transaction succeeded
          if (status == '0x0') return false; // Transaction reverted
        }
      }
    } catch (e) {
      // 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
6. **Check for pending actions in balances before calling the `manage` method** to avoid conflicting or duplicate operations
7. **Test on testnets first (e.g., Sepolia) before moving to mainnet** to validate flows and configurations in a safe environment

## 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 [signing transactions](./sign-a-transaction)
* Explore [transaction simulation](./evaluate-a-transaction)
* Learn how to [send tokens](./send-tokens)
