Skip to main content
Portal’s iOS 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.
let request = YieldXyzGetYieldsRequest(
    offset: 0,
    limit: 10,
    network: "eip155:11155111" // Sepolia network
    // ... other parameters
)

do {
    let response = try await portal.yield.yieldxyz.discover(request: request)
    if let rawResponse = response.data?.rawResponse {
        let yieldOpportunities = rawResponse.items

        // Process and display yield opportunities
    }
} catch {
    // Handle error
    print("Error discovering yields: \(error)")
}
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.
do {
    let userAddress = try await portal.getAddress()

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

    let enterResponse = try await portal.yield.yieldxyz.enter(request: enterRequest)
    if let rawResponse = enterResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error entering yield position: \(error)")
}

Checking Yield Balances

Retrieve current yield positions and balances. For complete API documentation, see the Yield.xyz get balances reference.
do {
    let userAddress = try await portal.getAddress()

    let balanceRequest = YieldXyzGetBalancesRequest(
        queries: [
            YieldXyzBalanceQuery(
                address: userAddress,
                network: "eip155:11155111" // Sepolia testnet
            )
        ]
    )

    let response = try await portal.yield.yieldxyz.getBalances(request: balanceRequest)
    if let rawResponse = response.data?.rawResponse {
        let yieldPositions = rawResponse.items

        // Process and display yield positions information
    }
} catch {
    // Handle error
    print("Error getting balances: \(error)")
}

Exiting Yield Positions

Use the exit method to withdraw from yield positions. For complete API documentation, see the Yield.xyz exit yield reference.
do {
    let userAddress = try await portal.getAddress()

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

    let exitResponse = try await portal.yield.yieldxyz.exit(request: exitRequest)
    if let rawResponse = exitResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error exiting yield position: \(error)")
}

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.
do {
    let userAddress = try await portal.getAddress()

    let 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.
    )

    let manageResponse = try await portal.yield.yieldxyz.manage(request: manageRequest)
    if let rawResponse = manageResponse.data?.rawResponse {
        let transactions = rawResponse.transactions

        // Process transactions, this is described in the "Transaction Processing" section below
        try await processTransactions(transactions)
    }
} catch {
    // Handle error
    print("Error managing yield position: \(error)")
}

Getting Historical Actions

Retrieve the history of yield actions for an address. For complete API documentation, see the Yield.xyz get actions reference.
do {
    let userAddress = try await portal.getAddress()

    let request = YieldXyzGetHistoricalActionsRequest(address: userAddress)
    let response = try await portal.yield.yieldxyz.getHistoricalActions(request: request)

    if let rawResponse = response.data?.rawResponse {
        let pastActions = rawResponse.items

        // Process and display past yield actions
    }
} catch {
    // Handle error
    print("Error getting historical actions: \(error)")
}

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.
func processTransactions(_ transactions: [YieldActionTransaction]) async throws {
    let sorted = transactions.sorted { $0.stepIndex < $1.stepIndex }
    for tx in sorted {
        if tx.unsignedTransaction != nil && tx.status == YieldActionTransactionStatus.CREATED {
            let success = await signAndSubmitAndConfirm(transaction: tx)
            if !success { break }
        }
    }
}

func signAndSubmitAndConfirm(transaction: YieldActionTransaction) async -> Bool {
    guard let unsignedTxJson = transaction.unsignedTransaction as? String else {
        return false
    }

    // Parse the unsigned transaction JSON string
    guard let jsonData = unsignedTxJson.data(using: .utf8),
          let txParams = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
    else {
        return false
    }

    // Create ETHTransactionParam from the parsed JSON
    let ethTransaction = ETHTransactionParam(
        from: txParams["from"] as? String ?? "",
        to: txParams["to"] as? String ?? "",
        value: txParams["value"] as? String ?? "0x0",
        data: txParams["data"] as? String ?? "0x"
        // Portal handles gas estimation automatically
    )

    do {
        // Sign and send the transaction
        let sendResponse = try await portal.request(
            transaction.network,
            withMethod: .eth_sendTransaction,
            andParams: [ethTransaction]
        )

        guard let txHash = sendResponse.result as? String else {
            return false
        }

        // Track the transaction with the yield system
        _ = try await portal.yield.yieldxyz.track(
            transactionId: transaction.id,
            txHash: txHash
        )

        // Wait for transaction confirmation
        return await waitForReceipt(txHash: txHash, chainId: transaction.network)
    } catch {
        print("Error signing and submitting transaction: \(error)")
        return false
    }
}

func waitForReceipt(
    txHash: String,
    chainId: String,
    maxAttempts: Int = 30,
    delaySeconds: UInt64 = 2
) async -> Bool {
    for _ in 0..<maxAttempts {
        try? await Task.sleep(nanoseconds: delaySeconds * 1_000_000_000)

        do {
            let response = try await portal.request(
                chainId,
                withMethod: .eth_getTransactionReceipt,
                andParams: [txHash]
            )

            if let innerResponse = response.result as? EthTransactionResponse,
               let status = innerResponse.result?.status {
                if status == "0x1" {
                    return true  // Transaction succeeded
                } else if status == "0x0" {
                    return false // Transaction reverted
                }
            }
        } catch {
            // Continue waiting if request fails
            continue
        }
    }

    return false // Timeout
}

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