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 instance
  • An active wallet with the required token(s) on the target network

Discovering Yield Opportunities

Use the discover method to find available yield opportunities. For complete API documentation, see the Yield.xyz API reference.
val request = YieldXyzGetYieldsRequest(
    offset = 0,
    limit = 10,
    network = "eip155:11155111", // Sepolia network
    // ... other parameters
)

val result = portal.yield.yieldxyz.discover(request)

if (result.isSuccess) {
    val response = result.getOrThrow()
    val rawResponse = response.data?.rawResponse
    val yieldOpportunities = rawResponse?.items

    // Process and display yield opportunities
}

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.
val userAddress = portal.getAddress(PortalNamespace.EIP155) ?: return

val enterRequest = YieldXyzEnterRequest(
    yieldId = "ethereum-sepolia-link-aave-v3-lending",
    address = userAddress,
    arguments = EnterYieldArguments(
        amount = "1", // 1 LINK token
    )
)

val enterResult = portal.yield.yieldxyz.enter(enterRequest)
if (enterResult.isSuccess) {
    val enterResponse = enterResult.getOrThrow()
    val rawResponse = enterResponse.data?.rawResponse
    val transactions = rawResponse?.transactions

    // Process transactions, this is described in the "Transaction Processing" section below
    processTransactions(transactions)
}

Checking Yield Balances

Retrieve current yield positions and balances. For complete API documentation, see the Yield.xyz get balances reference.
val userAddress = portal.getAddress(PortalNamespace.EIP155) ?: return

val balanceRequest = YieldXyzGetBalancesRequest(
    queries = listOf(
        YieldBalanceQuery(
            address = userAddress,
            network = "eip155:11155111" // Sepolia testnet
        )
    )
)

val balanceResult = portal.yield.yieldxyz.getBalances(balanceRequest)
if (balanceResult.isSuccess) {
    val response = balanceResult.getOrThrow()
    val rawResponse = response.data?.rawResponse
    val yieldPositions = rawResponse?.items

    // Process and display yield positions information
}

Exiting Yield Positions

Use the exit method to withdraw from yield positions. For complete API documentation, see the Yield.xyz exit yield reference.
val userAddress = portal.getAddress(PortalNamespace.EIP155) ?: return

val exitRequest = YieldXyzExitRequest(
    yieldId = "ethereum-sepolia-link-aave-v3-lending",
    address = userAddress,
    arguments = YieldXyzEnterArguments(amount = "0.001")
)

val exitResult = portal.yield.yieldxyz.exit(exitRequest)
if (exitResult.isSuccess) {
    val exitResponse = exitResult.getOrThrow()
    val rawResponse = exitResponse.data?.rawResponse
    val transactions = rawResponse?.transactions

    // Process transactions, this is described in the "Transaction Processing" section below
    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.
val userAddress = portal.getAddress(PortalNamespace.EIP155) ?: return

val manageRequest = YieldXyzManageYieldRequest(
    yieldId = "ethereum-sepolia-link-aave-v3-lending",
    address = userAddress,
    action = YieldActionType.WITHDRAW, // Replace with the balance's `pendingAction` item's `type` value.
    passthrough = "eyJhZGRyZXNzZXMiOnsiYWRkcmVzcyI6ImNvc21vczF5ZXk..." // Replace with the balance's `pendingAction` item's `passthrough` value.
)

val manageResult = portal.yield.yieldxyz.manage(manageRequest)
if (manageResult.isSuccess) {
    val manageResponse = manageResult.getOrThrow()
    val rawResponse = manageResponse.data?.rawResponse
    val transactions = rawResponse?.transactions

    // Process transactions, this is described in the "Transaction Processing" section below
    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.
val userAddress = portal.getAddress(PortalNamespace.EIP155) ?: return

val request = YieldXyzGetHistoricalActionsRequest(address = userAddress)
val result = portal.yield.yieldxyz.getHistoricalActions(request)

if (result.isSuccess) {
    val response = result.getOrThrow()
    val rawResponse = response.data?.rawResponse
    val pastActions = rawResponse?.items

    // Process and display past yield actions
}

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.
fun processTransactions(transactions: List<YieldActionTransaction>?) {
    val sorted = transactions?.sortedBy { it.stepIndex } ?: emptyList()
    for (tx in sorted) {
        if (tx.unsignedTransaction != null && tx.status == YieldActionTransactionStatus.CREATED) {
            val ok = runBlocking { signAndSubmitAndConfirm(tx) }
            if (!ok) break
        }
    }
}

suspend fun signAndSubmitAndConfirm(transaction: YieldActionTransaction): Boolean {
    val unsignedTxJson = transaction.unsignedTransaction as? String ?: return false

    val gson = Gson()
    val type = object : TypeToken<Map<String, Any>>() {}.type
    val txParams = gson.fromJson<Map<String, Any>>(unsignedTxJson, type)

    val ethTransaction = EthTransactionParam(
        from = txParams["from"] as? String ?: "",
        to = txParams["to"] as? String ?: "",
        value = txParams["value"] as? String ?: "0x0",
        data = txParams["data"] as? String ?: "0x",
        gas = null, // Let Portal handle gas estimation`
        gasPrice = null,
        maxFeePerGas = null,
        maxPriorityFeePerGas = null
    )

    val send = portal.request(
        chainId = transaction.network,
        method = PortalRequestMethod.eth_sendTransaction,
        params = listOf(ethTransaction)
    )

    val txHash = send.result as? String ?: return false

    portal.yield.yieldxyz.track(transaction.id, txHash)

    return waitForReceipt(txHash, transaction.network)
}

suspend fun waitForReceipt(txHash: String, chainId: String, maxAttempts: Int = 30, delayMs: Long = 2000): Boolean {
    repeat(maxAttempts) {
        kotlinx.coroutines.delay(delayMs)

        val receiptResp = portal.request(
            chainId = chainId,
            method = PortalRequestMethod.eth_getTransactionReceipt,
            params = listOf(txHash)
        )

        val receipt = receiptResp.result as? Map<String, Any>
        val status = receipt?.get("status") as? String
        if (status == "0x1") return true // Was mined
        if (status == "0x0") return false // Was not mined yet
    }
    return false
}

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 Testnet (eip155:10143)
  • Ethereum Mainnet (eip155:1)
  • Ethereum Sepolia (eip155:11155111)
  • Polygon (eip155:137)
  • Polygon Amoy (eip155:80002)
  • Base (eip155:8453)
  • Base Sepolia (eip155:84532)
  • Arbitrum (eip155:42161)
  • Optimism (eip155:10)
  • Optimism Sepolia (eip155:11155420)
  • Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
  • Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)

Next Steps