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 userAddress = await portal.getAddress('eip155')
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 userAddress = await portal.getAddress('eip155')
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
})
})
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 userAddress = await portal.getAddress('eip155')
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)
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 userAddress = await portal.getAddress('eip155')
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 userAddress = await portal.getAddress('eip155')
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
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 and get transaction details reference.
For account abstraction enabled Portal clients, use eth_getUserOperationReceipt instead of eth_getTransactionReceipt since signing returns a user operation hash, not a transaction hash.Extract the transaction hash from response.result.receipt.transactionHash and use that transaction hash when calling the portal.yield.yieldxyz.track() method.
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)
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({
chainId: transaction.network,
method: 'eth_sendTransaction',
params: [
{
from: txParams.from || '',
to: txParams.to || '',
value: txParams.value || '0x0',
data: txParams.data || '0x',
},
],
})
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({
chainId,
method: 'eth_getTransactionReceipt',
params: [txHash],
})
const receipt = receiptResponse.result as Record<string, any> | null
if (receipt?.status === '0x1') return true
if (receipt?.status === '0x0') return false
}
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 yield availability before attempting to enter positions
- Process transactions sequentially as yield operations often require multiple steps and are dependent on previous transactions being mined successfully
- Handle network errors gracefully and provide user feedback
- Monitor transaction status and provide progress updates to users
- 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