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 React Native 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)
- 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.
import type { YieldXyzGetYieldsRequest } from '@portal-hq/core'
const request: YieldXyzGetYieldsRequest = {
offset: 0,
limit: 10,
network: 'eip155:11155111',
// Optional: type, token, provider, hasCooldownPeriod, hasWarmupPeriod, search, sort
}
const response = await portal.yield.yieldxyz.discover(request)
const yieldOpportunities = response.data?.rawResponse?.items
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.
import type { YieldXyzEnterRequest } from '@portal-hq/core'
const addresses = await portal.addresses
const userAddress = addresses?.eip155
if (!userAddress) {
throw new Error('No EVM address found')
}
const enterRequest: YieldXyzEnterRequest = {
yieldId: 'ethereum-sepolia-link-aave-v3-lending',
address: userAddress,
amount: '1',
// Optional: arguments (validatorAddress, inputToken, etc.)
}
const enterResponse = await portal.yield.yieldxyz.enter(enterRequest)
const transactions = enterResponse.data?.rawResponse?.transactions
await processTransactions(transactions)
Checking Yield Balances
Retrieve current yield positions and balances.
For complete API documentation, see the Yield.xyz get balances reference.
import type { YieldXyzGetBalancesRequest } from '@portal-hq/core'
const addresses = await portal.addresses
const userAddress = addresses?.eip155
if (!userAddress) {
throw new Error('No EVM address found')
}
const balanceRequest: YieldXyzGetBalancesRequest = {
queries: [
{
address: userAddress,
network: 'eip155:11155111',
// Optional: yieldId
},
],
}
const balanceResponse = await portal.yield.yieldxyz.getBalances(balanceRequest)
const yieldPositions = balanceResponse.data?.rawResponse?.items
yieldPositions?.forEach((position) => {
position.balances.forEach((balance) => {
// Check balance.pendingActions for available management actions
})
})
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 balanceRequest: YieldXyzGetBalancesRequest = {
queries: [
{
address: userAddress,
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.
import type { YieldXyzExitRequest } from '@portal-hq/core'
const addresses = await portal.addresses
const userAddress = addresses?.eip155
if (!userAddress) {
throw new Error('No EVM address found')
}
const exitRequest: YieldXyzExitRequest = {
yieldId: 'ethereum-sepolia-link-aave-v3-lending',
address: userAddress,
amount: '0.001',
// Optional: arguments (validatorAddress, etc.)
}
const exitResponse = await portal.yield.yieldxyz.exit(exitRequest)
const transactions = exitResponse.data?.rawResponse?.transactions
await processTransactions(transactions)
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
| Name | Required | Description |
|---|
yieldId or chain + token | Yes | Non-empty yieldId wins over chain / token. |
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: • true → Transaction confirmed. Execution continues. • false → Transaction failed on-chain (e.g., reverted with receipt status 0x0). Execution stops immediately with status: 'FAILED'. • Throws or times out → Execution stops safely with status: 'PARTIAL_SUCCESS' (or 'FAILED' if no prior confirmations).
When omitted, portal.waitForConfirmation polls eth_getTransactionReceipt via gatewayConfig with the same strict behavior. |
evmPollerOptions | No | Tune the built-in EVM receipt poller (pollIntervalMs, timeoutMs) when the default portal.waitForConfirmation is active. Has no effect when waitForConfirmation is provided. |
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: • 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 / token | Set when you passed chain + token. |
yieldOpportunityDetails | Action 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.
Strict confirmation semantics:
Yield operations use strict confirmation. Only waitForConfirmation(...) === true is considered success. The behavior for each confirmation result is:
true → Transaction confirmed successfully. Execution continues to next step.
false → Transaction failed on-chain (e.g., receipt status: "0x0" indicates revert). Execution stops immediately and returns status: 'FAILED'.
- Throws or times out → Execution stops safely and returns
status: 'PARTIAL_SUCCESS' (if some transactions were confirmed) or status: 'FAILED' (if no transactions completed).
The result always includes:
hashes — Transaction hashes submitted up to the point execution stopped.
status — One of SUCCESS, PARTIAL_SUCCESS, or FAILED.
There is no optimistic continuation after a failure.
How reverted transactions are detected:
When using the default portal.waitForConfirmation (or a custom implementation), the SDK detects on-chain failures by checking the transaction receipt:
// Example: built-in behavior
const receipt = await eth_getTransactionReceipt(txHash)
if (receipt.status === '0x1') {
// Success: waitForConfirmation returns true
return true
}
if (receipt.status === '0x0') {
// Revert: waitForConfirmation returns false
// SDK immediately stops execution and sets status: 'FAILED'
return false
}
This ensures that a reverted transaction (status: "0x0") is never reported as SUCCESS.
Example (deposit with progress and poller tuning)
import { Portal } from '@portal-hq/core'
const portal = new Portal({
apiKey: 'YOUR_PORTAL_CLIENT_API_KEY',
backup: {
/* ... */
},
gatewayConfig: { 'eip155:11155111': 'https://YOUR_RPC_URL' },
})
async function depositWithProgress() {
const addresses = await portal.addresses
const address = addresses?.eip155
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 ?? '',
)
},
// Optional: tune the EVM receipt poller for this call only
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',
address,
},
{
// Optional: provide your own post-broadcast wait for this call only.
// MUST return true for success; false for on-chain failure (e.g. revert).
// Returning false stops execution immediately with status: 'FAILED'.
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',
address,
},
{
// Optional: override the MPC signer for this call only.
// Receives the unsigned transaction payload and network string; return the tx hash.
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[]>
React Native Example
import { Portal } from '@portal-hq/core'
async function getValidators(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.
import type { YieldXyzManageYieldRequest, YieldXyzActionType } from '@portal-hq/core'
const addresses = await portal.addresses
const userAddress = addresses?.eip155
if (!userAddress) {
throw new Error('No EVM address found')
}
const balanceResponse = await portal.yield.yieldxyz.getBalances({
queries: [{ address: userAddress, network: 'eip155:11155111' }],
})
const pendingAction = balanceResponse.data?.rawResponse?.items?.[0]?.balances?.[0]?.pendingActions?.[0]
if (pendingAction) {
const manageRequest: YieldXyzManageYieldRequest = {
yieldId: 'ethereum-sepolia-link-aave-v3-lending',
address: userAddress,
action: pendingAction.type as YieldXyzActionType,
passthrough: pendingAction.passthrough,
// Optional: arguments (amount, etc.)
}
const manageResponse = await portal.yield.yieldxyz.manage(manageRequest)
const transactions = manageResponse.data?.rawResponse?.transactions
await processTransactions(transactions)
}
Getting Historical Actions
Retrieve the history of yield actions for an address.
For complete API documentation, see the Yield.xyz get actions reference.
import type { YieldXyzGetHistoricalActionsRequest } from '@portal-hq/core'
const addresses = await portal.addresses
const userAddress = addresses?.eip155
if (!userAddress) {
throw new Error('No EVM address found')
}
const request: YieldXyzGetHistoricalActionsRequest = {
address: userAddress,
// Optional: offset, limit, status, intent, type, yieldId
}
const response = await portal.yield.yieldxyz.getHistoricalActions(request)
const pastActions = response.data?.rawResponse?.items
Transaction processing (low-level enter / exit / manage)
If you use deposit() or withdraw(), skip this section—the SDK already sequences transactions, waits between steps, 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).
Use portal.waitForConfirmation(txHash, network) instead of reimplementing receipt polling. After the wait step, call portal.yield.yieldxyz.track(transactionId, txHash).
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.
import type { YieldXyzActionTransaction } from '@portal-hq/core'
async function processTransactions(
transactions: YieldXyzActionTransaction[] | undefined
): Promise<void> {
if (!transactions?.length) return
const sorted = [...transactions].sort((a, b) => a.stepIndex - b.stepIndex)
for (const tx of sorted) {
if (tx.unsignedTransaction && tx.status === 'CREATED') {
const success = await signAndSubmitAndConfirm(tx)
// Stop processing if transaction failed (returns false when receipt.status === '0x0')
if (!success) break
}
}
}
async function signAndSubmitAndConfirm(
transaction: YieldXyzActionTransaction
): Promise<boolean> {
const txParams =
typeof transaction.unsignedTransaction === 'string'
? JSON.parse(transaction.unsignedTransaction)
: transaction.unsignedTransaction
const sendResult = await portal.request(
'eth_sendTransaction',
[
{
from: txParams.from || '',
to: txParams.to || '',
value: txParams.value || '0x0',
data: txParams.data || '0x',
},
],
transaction.network
)
const txHash = sendResult.result as string
if (!txHash) return false
await portal.yield.yieldxyz.track(transaction.id, txHash)
return await waitForReceipt(txHash, transaction.network)
}
async function waitForReceipt(
txHash: string,
chainId: string,
maxAttempts: number = 30,
delayMs: number = 2000
): Promise<boolean> {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
await new Promise((resolve) => setTimeout(resolve, delayMs))
const receiptResponse = await portal.request(
'eth_getTransactionReceipt',
[txHash],
chainId
)
const receipt = receiptResponse.result as Record<string, any> | null
// Receipt status 0x1 = success
if (receipt?.status === '0x1') return true
// Receipt status 0x0 = reverted (on-chain failure)
if (receipt?.status === '0x0') return false
}
// Timeout reached without confirmation
return false
}
Getting Transaction Details
You can retrieve details about a specific transaction using the getTransaction method:
const transactionId = 'transaction-id-from-enter-or-exit-response'
const txResponse = await portal.yield.yieldxyz.getTransaction(transactionId)
const transaction = txResponse.data?.rawResponse
Best Practices
- Always check the
status field in deposit/withdraw results to determine operation outcome (SUCCESS, PARTIAL_SUCCESS, or FAILED)
- Always check yield availability before attempting to enter positions
- Prefer
deposit() / withdraw() when you want Portal to handle signing, confirmation between steps, and tracking—avoid copying receipt-poll loops from older examples
- Process low-level transactions sequentially when using
enter / exit / manage directly; later steps depend on earlier transactions being mined
- Handle network errors gracefully and provide user feedback
- Monitor transaction status and provide progress updates to users (
onProgress on high-level methods)
- Validate user balances before initiating yield operations
- Check for pending actions in balances before calling the manage method
- Test on testnets first (e.g., 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