Skip to main content

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.

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)
  • Yield.xyz integration enabled in your Portal Dashboard (see Yield.xyz Integration)

Discovering Yield Opportunities

Use the discover method to find available yield opportunities. For complete API documentation, see the Yield.xyz API reference.
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);
}
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

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. 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.
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.
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);
}
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.
const getBalancesRequest: YieldXyzGetBalancesRequest = {
  queries: [
    {
      address,
      network: 'eip155:11155111',
      yieldId: 'ethereum-sepolia-link-aave-v3-lending',
    },
  ],
};

Exiting Yield Positions

Use the exit method to withdraw from yield positions. For complete API documentation, see the Yield.xyz exit yield reference.
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

deposit(
  params: YieldDepositParams,
  options?: YieldSubmitOptions
): Promise<YieldDepositResult>

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

Essential parameters

NameRequiredDescription
yieldId or chain + tokenYesNon-empty yieldId wins over chain / token. On Web, chain must be a full CAIP-2 id (for example eip155:11155111).
amountYesAmount to deposit or withdraw.
addressYesWallet address for the action.
argumentsNoProtocol-specific Yield.xyz arguments.
Second argument YieldSubmitOptions:
NameRequiredDescription
onProgressNoCallback fired at each step per transaction: signing, submitted, confirming, confirmed.
signAndSendTransactionNoPer-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.
waitForConfirmationNoPer-call confirmation override. Called after each broadcast with (txHash, network). MUST return true for confirmed success. The behavior for each return value is:
true → Transaction confirmed successfully. Execution continues to next step.
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).

When omitted, portal.waitForConfirmation is used for supported EVM networks.
evmRequestFnNoWeb SDK: JSON-RPC function used when building the default confirmation waiter for EVM (for example receipt polling via eth_getTransactionReceipt).
evmPollerOptionsNoTune the built-in EVM receipt poller (pollIntervalMs, timeoutMs) when the default confirmation path uses evmRequestFn. Has no effect when waitForConfirmation is provided.
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.

Return value

FieldDescription
hashesSubmitted transaction hashes, in order. Contains hashes for all transactions that were submitted, regardless of final outcome.
yieldIdResolved yield id.
statusOperation result status:
SUCCESS — All transactions confirmed successfully (or no waitForConfirmation configured).
PARTIAL_SUCCESS — Some transactions confirmed, but execution stopped before completing all steps (e.g., timeout).
FAILED — A transaction failed on-chain (receipt status 0x0) or confirmation returned false.
chain / tokenSet when you passed chain + token.
yieldOpportunityDetailsAction metadata from Yield.xyz.

Handling Results

Always check the status field to determine the outcome:
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.
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.

Example (deposit with progress and poller tuning)

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)

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)

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.
getValidators(yieldId: string): Promise<YieldXyzValidator[]>

Example

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.
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.
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 }).
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.
For complete API documentation, see the Yield.xyz submit transaction hash reference and get transaction details reference.
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.
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