> ## 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 Web 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
* **Deposit and withdraw in one call** using the high-level `deposit` and `withdraw` helpers (sign, confirm between steps, and track with Yield.xyz)

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

```typescript theme={null}
try {
  const discoverRequest: YieldXyzGetYieldsRequest = {
    limit: 10,
    offset: 0,
    network: 'eip155:11155111', // Sepolia network
    // ... other parameters
  };

  const result = await portal.yield.yieldXyz.discover(discoverRequest);

  // manage Yield.xyz result
  console.log(result);
} catch (err) {
  // Handle Yield.xyz discover error
  console.error(err);
}
```

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

```typescript theme={null}
try {
  const userAddress = await portal.getEip155Address(); // or getSolanaAddress() if using a Solana yield

  const enterRequest: YieldXyzEnterRequest = {
    yieldId: 'ethereum-sepolia-link-aave-v3-lending',
    address: userAddress,
    arguments: {
      amount: '1', // 1 LINK Token
    },
  };

  const enterResult = await portal.yield.yieldXyz.enter(enterRequest);

  const transactions = enterResult.data?.rawResponse?.transactions;

  // Process transactions, this is described in the "Transaction Processing" section below
  processTransactions(transactions);
} catch (err) {
  // Handle Yield.xyz enter error
  console.error(err);
}
```

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

```typescript theme={null}
try {
  const address = await portal.getEip155Address(); // or getSolanaAddress() if using a Solana yield

  const getBalancesRequest: YieldXyzGetBalancesRequest = {
    queries: [
      {
        address,
        network: 'eip155:11155111',
      },
    ],
  };
  const result = await portal.yield.yieldXyz.getBalances(getBalancesRequest);

  // Process and display yield positions information
  console.log(result);
} catch (err) {
  // Handle Yield.xyz Get balances error
  console.error(err);
}
```

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

  ```typescript theme={null}
  const getBalancesRequest: YieldXyzGetBalancesRequest = {
    queries: [
      {
        address,
        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).

```typescript theme={null}
try {
  const address = await portal.getEip155Address(); // or getSolanaAddress() if using a Solana yield

  const exitRequest: YieldXyzExitRequest = {
    yieldId: exitYieldId,
    address,
    arguments: {
      amount: exitYieldAmount || '0.001',
    },
  };
  const result = await portal.yield.yieldXyz.exit(exitRequest);

  const transactions = result.data.rawResponse.transactions;

  // Process transactions, this is described in the "Transaction Processing" section below
  processTransactions(transactions);
} catch (err) {
  // Handle Yield.xyz Exit error
  console.error(err);
}
```

## High-Level Methods

Use `deposit` and `withdraw` when you want one call for the full flow: resolve the yield, build the action, sign and send each transaction in order, wait between steps when configured, and report hashes to Yield.xyz.

Specify the position with **`yieldId`**, or with **`chain`** (CAIP-2) + **`token`** (must match your Portal yield defaults). Both methods require **`amount` and `address`**. Optionally set **`arguments`**.

### Signatures

```typescript theme={null}
deposit(
  params: YieldDepositParams,
  options?: YieldSubmitOptions
): Promise<YieldDepositResult>

withdraw(
  params: YieldWithdrawParams,
  options?: YieldSubmitOptions
): Promise<YieldWithdrawResult>
```

### Essential parameters

| Name                             | Required | Description                                                                                                                |
| -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `yieldId` *or* `chain` + `token` | Yes      | Non-empty `yieldId` wins over `chain` / `token`. On Web, `chain` must be a full CAIP-2 id (for example `eip155:11155111`). |
| `amount`                         | Yes      | Amount to deposit or withdraw.                                                                                             |
| `address`                        | Yes      | Wallet address for the action.                                                                                             |
| `arguments`                      | No       | Protocol-specific Yield.xyz arguments.                                                                                     |

Second argument `YieldSubmitOptions`:

| Name                     | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onProgress`             | No       | Callback fired at each step per transaction: `signing`, `submitted`, `confirming`, `confirmed`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `signAndSendTransaction` | No       | Per-call signer. Receives the unsigned transaction payload and the network string; must return the submitted tx hash. Takes priority over the instance-level setter. When omitted, Portal's built-in MPC signer is used automatically.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `waitForConfirmation`    | No       | Per-call confirmation override. Called after each broadcast with `(txHash, network)`. **MUST return `true` for confirmed success.** The behavior for each return value is:<br />• **`true`** → Transaction confirmed successfully. Execution continues to next step.<br />• **`false`** → Transaction failed on-chain (e.g., reverted with receipt status `0x0`). Execution stops immediately and returns partial results with `status: 'FAILED'`.<br />• **Throws or times out** → Execution stops safely and returns partial results with `status: 'PARTIAL_SUCCESS'` (if some transactions completed) or `status: 'FAILED'` (if none completed).<br /><br />When omitted, `portal.waitForConfirmation` is used for supported EVM networks. |
| `evmRequestFn`           | No       | **Web SDK:** JSON-RPC function used when building the default confirmation waiter for EVM (for example receipt polling via `eth_getTransactionReceipt`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `evmPollerOptions`       | No       | Tune the built-in EVM receipt poller (`pollIntervalMs`, `timeoutMs`) when the default confirmation path uses `evmRequestFn`. Has no effect when `waitForConfirmation` is provided.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

<Note>
  **Strict confirmation semantics:** Yield operations use **strict confirmation**. Only `waitForConfirmation(...) === true` is considered success.

  **Confirmation outcomes:**

  * **Returns `true`:** Transaction confirmed successfully. Execution continues to next step.
  * **Returns `false`:** Transaction failed on-chain (e.g., reverted with `receipt.status === "0x0"`). Execution **stops immediately** and returns partial results with `status: 'FAILED'`.
  * **Throws or times out:** Execution **stops safely** and returns partial results with `status: 'PARTIAL_SUCCESS'` (if some transactions completed) or `status: 'FAILED'` (if none completed).

  **Status determination:**

  * `SUCCESS`: All required confirmations returned `true`, OR no confirmation waiter was configured
  * `PARTIAL_SUCCESS`: At least one confirmation succeeded, but execution stopped before completing all steps (e.g., timeout or later transaction failed)
  * `FAILED`: First transaction confirmation returned `false` or failed (zero confirmations reached)

  **Default timeout behavior:**

  * Timeout: `900_000ms` (15 minutes)
  * Poll interval: `4_000ms`

  This differs from Li.Fi and 0x `tradeAsset`, where any confirmation failure **throws** and aborts the entire operation — Yield's approach allows graceful recovery with partial progress when confirmations time out or are uncertain.
</Note>

### Return value

| Field                     | Description                                                                                                                                                                                                                                                                                                                                                           |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hashes`                  | Submitted transaction hashes, in order. Contains hashes for all transactions that were submitted, regardless of final outcome.                                                                                                                                                                                                                                        |
| `yieldId`                 | Resolved yield id.                                                                                                                                                                                                                                                                                                                                                    |
| `status`                  | **Operation result status:**<br />• `SUCCESS` — All transactions confirmed successfully (or no `waitForConfirmation` configured).<br />• `PARTIAL_SUCCESS` — Some transactions confirmed, but execution stopped before completing all steps (e.g., timeout).<br />• `FAILED` — A transaction failed on-chain (receipt status `0x0`) or confirmation returned `false`. |
| `chain` / `token`         | Set when you passed `chain` + `token`.                                                                                                                                                                                                                                                                                                                                |
| `yieldOpportunityDetails` | Action metadata from Yield.xyz.                                                                                                                                                                                                                                                                                                                                       |

### Handling Results

Always check the `status` field to determine the outcome:

```typescript theme={null}
const result = await portal.yield.yieldXyz.deposit(params, options)

switch (result.status) {
  case 'SUCCESS':
    console.log('All transactions confirmed:', result.hashes)
    // Safe to show success UI
    break
    
  case 'PARTIAL_SUCCESS':
    console.warn('Some transactions completed:', result.hashes)
    // Check yield balance to determine actual position state
    // May need user action to complete or retry remaining steps
    break
    
  case 'FAILED':
    console.error('Transaction failed on-chain:', result.hashes[result.hashes.length - 1])
    // Show error UI, check transaction receipt for revert reason
    break
}
```

**Important:** A non-empty `hashes` array does NOT guarantee success. Always check `status`.

<Note>
  **Reverted transactions:** When an EVM transaction is reverted on-chain (`receipt.status === "0x0"`), the default `waitForConfirmation` implementation returns `false`, which causes the SDK to stop execution immediately and set `status: 'FAILED'`. This is the correct behavior — a reverted transaction is a definitive failure, not an uncertain state.

  If you implement a custom `waitForConfirmation`, ensure reverted transactions either:

  * Return `false` (recommended for graceful degradation in multi-step flows)
  * Throw an error (for explicit failures)

  **Never return `true` for a reverted transaction.**
</Note>

### Example (`deposit` with progress and poller tuning)

```typescript theme={null}
import Portal from '@portal-hq/web'

const portal = new Portal({
  apiKey: 'YOUR_PORTAL_CLIENT_API_KEY',
  rpcConfig: { 'eip155:11155111': 'https://YOUR_RPC_URL' },
})

async function depositWithProgress() {
  const address = await portal.getEip155Address()
  if (!address) throw new Error('No EVM address')

  const result = await portal.yield.yieldXyz.deposit(
    {
      chain: 'eip155:11155111',
      token: 'ETH',
      amount: '0.0000001',
      address,
    },
    {
      onProgress: (e) => {
        console.log(
          `[deposit] ${e.step} step ${e.index + 1}/${e.total}`,
          e.hash ?? '',
        )
      },
      evmPollerOptions: { pollIntervalMs: 4000, timeoutMs: 300_000 },
    },
  )

  console.log('Status:', result.status)
  console.log('Hashes:', result.hashes)
  
  // Always check status to determine outcome
  if (result.status === 'SUCCESS') {
    console.log('Deposit completed successfully!')
  } else if (result.status === 'PARTIAL_SUCCESS') {
    console.warn('Deposit partially completed. Check yield balance.')
  } else {
    console.error('Deposit failed on-chain.')
  }
  
  return result
}
```

Use `withdraw` the same way with identical parameter shapes.

### Example (custom confirmation logic)

```typescript theme={null}
const result = await portal.yield.yieldXyz.deposit(
  {
    chain: 'eip155:11155111',
    token: 'ETH',
    amount: '0.0000001',
  },
  {
    waitForConfirmation: async (txHash, network) => {
      return portal.waitForConfirmation(txHash, network)
    },
  },
)
```

### Example (custom signer)

```typescript theme={null}
const result = await portal.yield.yieldXyz.deposit(
  {
    chain: 'eip155:11155111',
    token: 'ETH',
    amount: '0.0000001',
  },
  {
    signAndSendTransaction: async (transaction, network) => {
      const txHash = await myCustomSigner.signAndSend(transaction, network)
      return txHash
    },
  },
)
```

## Get Validators

Use `getValidators()` to fetch the validator addresses for a specific `yieldId`.\
These addresses are used for token approval flows.

```typescript theme={null}
getValidators(yieldId: string): Promise<YieldXyzValidator[]>
```

### Example

```typescript theme={null}
import Portal from '@portal-hq/web'

async function fetchValidators(portal: Portal) {
  try {
    const yieldId = 'monad-testnet-mon-native-staking'
    const validators = await portal.yield.getValidators(yieldId)

    console.log(`Validators for ${yieldId}:`, validators)
  } catch (error) {
    console.error('getValidators failed:', error)
  }
}
```

The SDK throws an error if the response does not contain a valid validators array.

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

```typescript theme={null}
try {
  const address = await portal.getEip155Address(); // or getSolanaAddress() if using a Solana yield

  const manageRequest: YieldXyzManageYieldRequest = {
    yieldId,
    address,
    action: yieldAction as any,
    passthrough: yieldPassthrough,
  };

  const result = await portal.yield.yieldXyz.manage(manageRequest);

  const transactions = result.data.rawResponse.transactions;

  // Process transactions, this is described in the "Transaction Processing" section below
  processTransactions(transactions);
} catch (err) {
  // Handle Yield.xyz Manage error
  console.error(err);
}
```

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

```typescript theme={null}
try {
  const address = await portal.getEip155Address(); // or getSolanaAddress() if using a Solana yield

  const historicalActionsRequest: YieldXyzGetHistoricalActionsRequest = {
    address,
    limit: 10,
    offset: 0,
  };

  const result = await portal.yield.yieldXyz.getHistoricalActions(historicalActionsRequest);

  // process historical actions result
} catch (err) {
  // Handle Yield.xyz Historical Actions error
  console.error(err);
}
```

## Transaction processing (low-level `enter` / `exit` / `manage`)

If you use **`deposit()` or `withdraw()`**, skip this section—the Web SDK already sequences transactions, waits between steps when configured, and calls Yield.xyz tracking.

For **manual** flows that return raw transactions from `enter`, `exit`, or `manage`: process steps in order, and **wait for inclusion before sending the next transaction** (multi-step actions depend on prior txs mining; the high-level methods use `portal.waitForConfirmation` or your override between steps).

For standard transaction-hash flows, prefer `portal.waitForConfirmation(txHash, network)` rather than writing your own receipt polling. If your signer returns a non-standard identifier — such as a user operation hash for account abstraction — you will need a custom wait loop against the appropriate receipt API (e.g., `eth_getUserOperationReceipt` for bundler-based AA wallets). After the wait step, call `portal.yield.yieldXyz.track({ transactionId, hash })`.

<Tip>
  `deposit()` / `withdraw()` strip stale planning **`nonce`** from JSON EVM `unsignedTransaction` values before signing. If you implement manual signing, refresh or omit `nonce` the same way the signer expects, or you may see nonce collisions across steps.
</Tip>

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>

```typescript theme={null}
async function processTransactions(transactions?: YieldXyzActionTransaction[]): Promise<void> {
  const sorted = transactions?.sort((a, b) => a.stepIndex - b.stepIndex) ?? [];
  for (const tx of sorted) {
    if (tx.unsignedTransaction != null && tx.status === 'CREATED') {
      const ok = await signAndSubmitAndConfirm(tx);
      if (!ok) break;
    }
  }
}

async function signAndSubmitAndConfirm(transaction: YieldXyzActionTransaction): Promise<boolean> {
  const unsignedTxJson = transaction.unsignedTransaction;
  if (typeof unsignedTxJson !== 'string') return false;

  const txParams = JSON.parse(unsignedTxJson) as Record<string, any>;

  const ethTransaction = {
    from: (txParams.from as string) ?? '',
    to: (txParams.to as string) ?? '',
    value: (txParams.value as string) ?? '0x0',
    data: (txParams.data as string) ?? '0x',
    gas: null,
    gasPrice: null,
    maxFeePerGas: null,
    maxPriorityFeePerGas: null,
  };

  const txHash = await portal.request({
    chainId: transaction.network,
    method: 'eth_sendTransaction',
    params: [ethTransaction],
  });

  if (!txHash) return false;

  // IMPORTANT: Check the confirmation result
  const confirmed = await portal.waitForConfirmation(txHash, transaction.network)

  // Track the transaction with Yield.xyz
  await portal.yield.yieldXyz.track({
    transactionId: transaction.id,
    hash: txHash,
  })

  // Return confirmation status - false will stop multi-step flows safely
  return confirmed
}
```

## Best Practices

1. **Always check the `status` field** in deposit/withdraw results to determine operation outcome (`SUCCESS`, `PARTIAL_SUCCESS`, or `FAILED`)
2. **Always check yield availability** before attempting to enter positions
3. **Prefer `deposit()` / `withdraw()`** when you want Portal to handle signing, confirmation between steps, and tracking—avoid copying receipt-poll loops from older examples
4. **Process low-level transactions sequentially** when using `enter` / `exit` / `manage` directly; later steps depend on earlier transactions being mined
5. **Handle network errors gracefully** and provide user feedback
6. **Monitor transaction status** and provide progress updates to users (`onProgress` on high-level methods)
7. **Validate user balances** before initiating yield operations
8. **Check for pending actions** in balances before calling the manage method
9. **Test on testnets first** (for example Sepolia) before moving to mainnet

## 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](./simulate-a-transaction)
* Check out [Portal API methods](./portal-api-methods)
