Skip to main content
Portal’s Android 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.
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.

Checking Yield Balances

Retrieve current yield positions and balances. For complete API documentation, see the Yield.xyz get balances reference.
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.

Exiting Yield Positions

Use the exit method to withdraw from yield positions. For complete API documentation, see the Yield.xyz exit yield reference.

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 for confirmation between steps, and report each hash back to Yield.xyz. Both are suspend and return Result<T>. YieldWithdrawParams and YieldWithdrawResult are type aliases for the deposit types, so the two methods take an identical shape.

Signatures

options is defaulted, so deposit(params) alone is valid. Both run on Dispatchers.IO.

Essential parameters

YieldDepositParams: There is no address parameter. The wallet address is resolved from your Portal instance for whichever chain the yield resolves to. If no wallet exists for that chain, the call fails with YieldXyzActionException.AddressUnavailable. YieldActionTarget is a sealed class with two subtypes, so you cannot supply both forms or neither:
ByChainAndToken requires full CAIP-2. A bare "1" fails with YieldXyzActionException.InvalidChainId. This differs from Li.Fi’s tradeAsset, which forwards whatever chain format you give it — the two APIs shipped in the same release but do not accept the same values.
YieldSubmitOptions:
onProgress is invoked on Dispatchers.IO, not the main thread. Switch dispatchers before touching any UI from inside the callback.
These are milliseconds. The iOS SDK uses pollIntervalSeconds and timeoutSeconds for the same two concepts, so a value copied across platforms will be wrong by a factor of 1000.
Note the enum member casing: YieldSubmitStep members are lowercase (signing, submitted, confirming, confirmed), while YieldSubmitResultStatus members are uppercase (SUCCESS, PARTIAL_SUCCESS, FAILED). Both are written exactly as the SDK declares them. There is no per-call signer or confirmation override — signing always goes through the Portal MPC signer.

Return value

YieldDepositResult:

Handling Results

The when is exhaustive over the three states with no else.
A non-empty hashes list does not mean success. Hashes are recorded as transactions are submitted, before their outcome is known. Always branch on status.

Example (deposit with progress)

Example (targeting by chain and token)

chain and token on the result are populated only when you target this way.

Example (withdraw)

withdraw takes identical parameter shapes:

Errors

Failures arrive inside the returned Result as a YieldXyzActionException: PortalNotInitialized, EmptyYieldId, NoTransactions, InvalidSignResponse, and InvalidUnsignedTransaction are object singletons — match them with is. The rest are data classes carrying detail.

Get Validators

Fetches the validator addresses for a native-staking yield. These are used for approval flows and for populating arguments.validatorAddress.
Available on both the namespace and the provider — portal.yield.getValidators(yieldId) is a passthrough to portal.yield.yieldxyz.getValidators(yieldId).
Only address is non-nullable on YieldXyzValidator. Everything else — name, logoURI, website, rewardRate, provider, commission, tvlUsd, votingPower, preferred, minimumStake, status, and the rest — depends on the protocol and may be absent. Fails with YieldXyzActionException.NoValidators when the response contains no validators, and YieldXyzActionException.ApiError when the backend returns an error payload.

Low-level additions

Two API methods back the high-level flow and are available directly on portal.api.yieldxyz:
getYieldDefaults returns data as a map keyed "{caip2}:{TOKEN}" — for example "eip155:1:USDC". That key is exactly what ByChainAndToken resolves against, so this is how you discover valid chain and token pairs. Pass includeOpportunities = true to populate each entry’s opportunity field.
Unlike the other Yield.xyz responses, the defaults payload is not wrapped in rawResponse — read it from data directly.

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.

Getting Historical Actions

Retrieve the history of yield actions for an address. For complete API documentation, see the Yield.xyz get actions reference.

Transaction Processing (Low-Level Enter / Exit / Manage)

If you use deposit or withdraw, skip this section — the SDK already sequences transactions, waits for confirmation between steps, and reports hashes to Yield.xyz. This section applies to manual flows built on enter, exit, or manage.
When deposit and withdraw sign an EVM transaction they rebuild it from the yield action’s unsignedTransaction, copying to, from, value, data and the fee fields (gasLimit/gas, maxFeePerGas, maxPriorityFeePerGas, gasPrice) but deliberately omitting nonce, so the MPC signer fetches the pending nonce at signing time. If you sign manually, drop the planning nonce the same way — reusing it across a multi-transaction action causes nonce collisions.
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 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.

Enum handling

Unknown values

Yield.xyz aggregates many protocols and onboards new ones regularly. Before 9.1.0, a response containing an enum value the SDK did not recognize decoded to null rather than failing — Gson’s default enum adapter returns null for an unrecognized name, and that null was written into a non-null Kotlin field. The call still returned a successful Result, so there was nothing to catch in onFailure; the failure surfaced later, as a NullPointerException at the point where your code read the field. A single new value from an upstream provider could crash a screen rendering getBalances output, far from the call that fetched it. As of 9.1.0, unrecognized values deserialize to UNKNOWN instead. Your app keeps working when Yield.xyz onboards a new protocol, action type, or reward schedule, without waiting for an SDK upgrade — and the value you have to handle now shows up at the call site, in the type system, rather than as a late crash.
Adding UNKNOWN is a source-breaking change. Any exhaustive when over one of the affected enums below will fail to compile until you add an UNKNOWN branch or an else branch.
If you arrived here from a compiler error, this is the fix:
Note the enum member casing: YieldXyzSource members are lowercase (staking, lending, protocol_incentive, …) while UNKNOWN is uppercase. That is not a typo in these docs — it is how the SDK declares them, and the same mix appears in most of the enums listed below. Write UNKNOWN in uppercase even when every other member of the same enum is lowercase.
Prefer an explicit UNKNOWN branch over else. With else, the next value Portal adds falls into it silently; with an explicit UNKNOWN, you get a compiler error and a chance to handle the new value deliberately.

Affected enums

Thirteen public enums gained UNKNOWN in 9.1.0, grouped here by the file that declares them so you can find the one your compiler flagged: From YieldXyzGetYieldsResponse.kt: YieldXyzRateType, YieldXyzSource, YieldXyzRewardSchedule, YieldXyzRewardClaiming, YieldXyzArgumentFieldName, YieldXyzArgumentFieldType From YieldXyzEnterYieldResponse.kt: YieldXyzActionIntent, YieldXyzActionType, YieldXyzActionExecutionPattern, YieldXyzActionStatus, YieldXyzActionTransactionStatus, YieldXyzActionTransactionType From YieldXyzGetYieldsRequest.kt: YieldXyzMechanicsType All thirteen live in io.portalhq.android.api.data.yieldxyz. The reach extends past discover. YieldXyzActionIntent, YieldXyzActionType, YieldXyzActionExecutionPattern, and YieldXyzActionStatus are all fields on YieldOpportunityDetails, which is returned by the high-level deposit and withdraw methods. Code reading result.yieldOpportunityDetails.status is affected — note that these four fields are nullable, so an exhaustive when needs a null branch as well as an UNKNOWN branch:

Handling UNKNOWN in your code

  1. Treat UNKNOWN as “display it, don’t act on it”. Render a neutral label rather than hiding the row, so users still see their position.
  2. Don’t branch business logic on UNKNOWN. If a decision depends on knowing the exact source or status, read the raw response value rather than inferring from the enum.
  3. Log occurrences along with the yieldId. A rising UNKNOWN rate is a signal to upgrade the SDK.

Custom Gson instances

Tolerance is implemented as a public Gson factory:
YieldXyzApi registers it on its own Gson instance, so every SDK call is tolerant out of the box and you do not need to do anything. It matters only when you build your own Gson to deserialize Yield.xyz payloads — decoding a webhook body or a cached response, for example:
Without the factory registered, your own Gson maps an unrecognized value to null, which then violates the non-null Kotlin field it is assigned to and surfaces later as a NullPointerException — even though the SDK’s own calls decode the same payload fine. Register the factory on any Gson instance you point at a Yield.xyz payload.
The factory applies only to enums that declare an UNKNOWN constant; every other enum falls through to Gson’s default adapter, so registering it is safe on a shared Gson instance. Alongside unrecognized strings, it also maps JSON null and non-string tokens (numbers, booleans, objects, arrays) to UNKNOWN rather than leaving a non-null field null.

New lending source

YieldXyzSource gained a lending member in 9.1.0. You encounter this enum on each entry of a discovered opportunity’s rewardRate.components list, as component.yieldSource. The full member list as of 9.1.0:
No member carries a @SerializedName annotation or a constructor value, so the wire strings are the member names exactly as written above — protocol_incentive on the wire is YieldXyzSource.protocol_incentive in Kotlin.
lending and lending_interest are separate members and Yield.xyz uses both. Treat them as distinct values rather than assuming one supersedes the other.

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

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