BorrowKit

Borrowing API

The Borrowing API is a unified layer for interacting with on-chain lending/borrowing protocols. It provides:

  • Normalized market data (rates, liquidity, collateral parameters)
  • A consistent action model (supply, borrow, repay, withdraw, collateral toggles)
  • Unified account state (positions, debt, health factor, borrow power)
  • Transaction construction (unsigned payloads ready for wallet signing)
  • A consistent action lifecycle that absorbs protocol quirks like ERC-20 approvals

Request and response shapes are identical across protocols and networks. Whether the underlying venue is Aave on Ethereum or Morpho on Base, your integration code does not change.


What the Borrowing API Does

Discoverability and capability introspection

  • List supported integrations (protocol + product type)
  • See which networks each integration supports
  • See which actions are supported and the argument schema per action

Market discovery (normalized market DTOs)

  • Browse markets across protocols and chains with a consistent MarketDto
  • Compare rates, liquidity, utilization, and risk parameters using stable fields across venues

Portfolio and risk state (positions)

  • Read supplied collateral, borrowed debt, and aggregated portfolio metrics:
    • healthFactor
    • currentLtv
    • availableToBorrowUsd
    • liquidationThreshold (per collateral)
    • netApy and per-position APYs

Transaction building (intent → signable payloads)

  • Convert an intent like "supply 10,000 USDC to market X" into the on-chain transactions required:
    • ERC-20 approvals (when allowance is insufficient)
    • The protocol call itself (supply, borrow, repay, withdraw, etc.)
  • Returns unsigned, signable payloads for client-side signing

Action lifecycle

A consistent state machine across all integrations:

create action → sign + submit each tx in transactions[] → poll → SUCCESS / FAILED

What's Supported

Supported Protocol Integrations

ProviderIntegration IDNameMarket TypeNetworks
Morphomorpho-blue-borrowMorpho Blue BorrowIsolatedEthereum, Arbitrum, Avalanche, Base, BNB Chain, Gnosis, Linea, Optimism, Polygon, Sonic
Sparkspark-borrowSpark BorrowPoolEthereum
Aaveaave-borrowAave V3 BorrowPoolEthereum, Optimism, Arbitrum, Polygon, Avalanche, Base, BNB Chain, Gnosis, Sonic, Linea, Plasma
Listalista-borrowLista Lending BorrowIsolatedBNB Chain, Ethereum

The authoritative chain list is whatever GET /v1/integrations returns at runtime.

Market types: Pool vs Isolated

  • Pool markets (Aave, Spark) — shared liquidity across many assets in a single pool. A user's supplied assets and debts all live under the same pool context, and supplied assets can be toggled as collateral.
  • Isolated markets (Morpho Blue) — each (collateral asset, loan asset) pair is a standalone market with isolated risk and liquidity. In isolated markets, supply is collateral by definition and does not earn interest.

Intended Flow of Actions

1. Discover markets
   GET /v1/markets

2. (Optional) Read user state
   GET /v1/positions

3. Create an action
   POST /v1/actions  → returns ActionDto with transactions[]

4. Sign and submit each transaction in transactions[], in order:
     - sign tx.signablePayload
     - POST /v1/transactions/{tx.id}/submit { signedPayload }
     - poll GET /v1/actions/{id} until the just-submitted tx is CONFIRMED
       (action.status will return to CREATED) before signing the next one

5. Poll the action until terminal
   GET /v1/actions/{id} → status=SUCCESS (or FAILED)

6. (Async flows only) If hasNextStep is true on the action, poll until
   status=WAITING_FOR_NEXT, then POST /v1/actions/{id}/step.

A single action can require multiple transactions

For most actions (e.g., supply, repay), the protocol requires an ERC-20 approval before the main call. The API returns both the APPROVAL and the main transaction together in transactions[] on the original POST /v1/actions response.

  • totalSteps is 1 and hasNextStep is false — there is no "next step" to fetch.
  • Sign and submit each transaction in transactions[] in order (approval first, then the main call).
  • If allowance is already sufficient, the APPROVAL is omitted from transactions[].
  • Do not call /step to fetch the next transaction — they are already in the original response.

Knowing when it's safe to submit the next transaction

The submit endpoint does not block on prior transactions in the same action. If you submit SUPPLY before APPROVAL is confirmed on-chain, it will revert with ERC20: insufficient allowance. You must wait for APPROVAL to confirm before submitting SUPPLY (same applies to APPROVAL → REPAY).

There is no per-transaction GET endpoint; poll the action and read individual transaction statuses out of ActionDto.transactions[]:

APPROVAL.status: CREATED      → not yet submitted
                 BROADCASTED  → submitted, awaiting on-chain confirmation
                 CONFIRMED    → safe to submit the next tx
                 FAILED       → bail; create a new action

Recommended polling loop:

1. Submit APPROVAL  → POST /v1/transactions/{approvalTxId}/submit
2. Loop:
     GET /v1/actions/{actionId}
     approval = action.transactions.find(t => t.type === 'APPROVAL')
     if approval.status === 'CONFIRMED' → break
     if approval.status === 'FAILED'    → bail
     sleep 2–5s
3. Submit SUPPLY    → POST /v1/transactions/{supplyTxId}/submit
4. Loop until action.status === 'SUCCESS' (or 'FAILED')

Cadence: the broadcasted-transaction tracker polls the chain on a ~5 second tick, so polling our API faster than that buys you nothing. Use 2–5s with exponential backoff up to ~10s for slow chains.

Per-transaction status: CREATED → BROADCASTED → CONFIRMED (or FAILED).

Action status: progresses CREATED → PROCESSING → SUCCESS (or FAILED), but for multi-tx single-step flows it can ping-pong:

CREATED              ← initial state, first tx ready to submit
  ↓ submit APPROVAL
PROCESSING           ← APPROVAL broadcast, awaiting confirmation
  ↓ APPROVAL confirmed, SUPPLY still CREATED
CREATED              ← action transitions back; SUPPLY is now ready to submit
  ↓ submit SUPPLY
PROCESSING           ← SUPPLY broadcast
  ↓ SUPPLY confirmed
SUCCESS              ← terminal

The action returning to CREATED after each in-flight tx confirms is the signal that the next pre-built transaction in transactions[] is ready for signing. Do not treat this as WAITING_FOR_NEXT — WAITING_FOR_NEXT is reserved for genuinely async multi-step flows where a future step has to be built server-side (see When /step is used).

Advanced escape hatch: if your signer can pre-sign both transactions with explicit incremental nonces (APPROVAL = nonce N, SUPPLY = nonce N+1), you can submit both immediately — the mempool will sequence them. Most integrators should not rely on this; just wait for CONFIRMED.

When /step is used

POST /v1/actions/:id/step is only used for genuinely async multi-step flows where the next transaction cannot be constructed until an earlier transaction has confirmed on-chain — for example, cross-chain bridges or delayed withdrawals. None of the currently supported integrations (aave-borrow, spark-borrow, morpho-blue-borrow, lista-borrow) use this pattern; they always return hasNextStep: false.


Integration Options

Option A — Drop-in Widget

The Yield.xyz Borrowing Widget is a drop-in lending and borrowing frontend you can embed in any application. It's built with React and designed to feel native inside wallets, exchanges, and fintech apps.

Under the hood the widget uses the Borrowing API to power the full lifecycle: discovery, markets, positions, action construction, signing, broadcast, and status polling.

Best for: wallets, exchanges, and fintech apps that want a turnkey UI.

Option B — Direct API Integration

Integrate natively while keeping full UI control.

Best for: custom UX or product-specific abstractions.


Key Concepts

Health Factor

healthFactor = sum(collateralUsd_i * liquidationThreshold_i) / totalBorrowedUsd
  • > 1 — safe
  • = 1 — at liquidation threshold
  • < 1 — eligible for liquidation
  • null — no debt (health factor is undefined when totalBorrowedUsd = 0)

Available on:

  • PositionDto.healthFactor — current health factor
  • ActionMetadataDto.predictedHealthFactor — preview of post-action health factor (returned on POST /v1/actions)

LTV and liquidation threshold

  • Max LTV (maxLtv) — maximum borrowable as a fraction of collateral value.
  • Liquidation threshold (liquidationThreshold) — LTV at which the position becomes liquidatable. Typically higher than maxLtv to provide a buffer.
  • Liquidation penalty (liquidationPenalty) — bonus paid to liquidators out of seized collateral.

Supply fees

Some markets are routed through a partner-specific wrapper contract that charges a fee on the supplyCollateral operation. The fee is deducted on-chain when the user supplies, and a deposit of grossAmount results in grossAmount × (1 − feeBps/10000) reaching the underlying protocol as collateral.

Currently supported on: morpho-blue-borrow. Aave and Spark pool markets have no supply fee and always return zero values.

Reading the fee on a market

Every market in GET /v1/markets carries:

FieldTypeMeaning
supplyCollateralFeeBpsstringFee rate in basis points ("50" = 0.5%). Range "0"–"500" enforced on-chain.
feeWrapperAddressstring | nullWrapper address charging the fee for this project, or null when no wrapper is configured.

feeWrapperAddress is the authoritative signal for whether a wrapper is configured for a given project. supplyCollateralFeeBps can be "0" even when a wrapper is configured (the wrapper owner can dial the fee to zero at any time).

Reading the fee on a supply action

POST /v1/actions for a supply returns the fee in metadata:

FieldTypeMeaning
feeAmountstringFee in raw units of the supplied token (e.g. "500000" = 0.50 USDC for a 100 USDC supply).
feeBpsnumberFee rate in basis points at action-creation time.

Both fields are always present on supply actions; they are "0" / 0 when no wrapper is configured or the wrapper currently charges no fees.

Two ways to size a supply action

  1. Explicit collateral. Pass amount (or amountRaw) — this is the gross amount you want to deposit. The wrapper takes its cut on-chain; the user nets amount × (1 − feeBps/10000) as collateral. metadata.feeAmount tells you exactly how much will be skimmed.
  2. Borrow-derived. Pass borrowAmount + targetLtv. The API solves for the collateral that produces the requested borrow at the target LTV, grossed up for the supply fee, and returns it as metadata.effectiveCollateralAmount. The action's on-chain supply uses that amount. ​
grossCollateral = (borrowAmount × loanPriceUsd) ÷ (targetLtv × collateralPriceUsd × (1 − feeBps/10000))

targetLtv must satisfy 0 < targetLtv ≤ market.maxLtv — values above maxLtv are rejected with 400 Bad Request.

Predicted health factor / LTV under a supply fee

metadata.predictedHealthFactor and metadata.predictedLtv reflect the net on-chain position after the wrapper has taken its fee. A supply of 1 cbBTC against a 100 bps wrapper is treated as 0.99 cbBTC of collateral when computing both metrics, matching what Morpho records on-chain.

Approval target

When a wrapper is configured for a Morpho supply, the ERC-20 APPROVAL transaction targets the wrapper address, not Morpho directly. The flow is otherwise unchanged: [APPROVAL, SUPPLY], signed and submitted in order.

Rates

  • Supply rate — APY earned on supplied assets (decimal, e.g. "0.0312" = 3.12%).
  • Borrow rate — APR paid on borrowed assets (decimal).
  • Rates are utilization-driven: higher utilization → higher borrow rates and higher supply yields.

All rates and APYs in the API are returned as decimals (e.g. "0.0567" = 5.67%), not percentages.

Minimum loan size (Lista Lending)

Lista Lending (lista-borrow) markets enforce a per-market minimum loan floor — a USD-denominated minimum (≈ $15 worth of the loan asset, converted to the loan token at its oracle price). It constrains two actions:

  • Borrow — the resulting total debt must be at least the minimum. A borrow that leaves debt below the floor is rejected with 400 Bad Request.
  • Partial repay — the remaining debt must be either zero or at least the minimum. A partial repay that would leave a dust position below the floor is rejected with 400 Bad Request; use repayAll: true to close the position instead.

The other integrations (aave-borrow, spark-borrow, morpho-blue-borrow) do not enforce a minimum loan size.


API Endpoints

Integrations

List integrations

GET /v1/integrations

Returns all available lending/borrowing integrations with supported actions and argument schemas.

Response: IntegrationDto[]

[
  {
    "id": "aave-borrow",
    "providerId": "aave",
    "name": "Aave V3 Borrow",
    "networks": ["ethereum", "arbitrum", "optimism", "polygon", "avalanche", "base", "bsc", "gnosis", "sonic", "linea", "plasma"],
    "metadata": {
      "description": "Aave is a decentralized non-custodial liquidity protocol where users can participate as suppliers or borrowers.",
      "externalLink": "https://aave.com",
      "logoURI": "https://assets.stakek.it/providers/aave.svg"
    },
    "actions": [
      {
        "id": "supply",
        "label": "Supply",
        "schema": {
          "type": "object",
          "properties": {
            "marketId":  { "type": "string" },
            "amount":    { "type": "string" },
            "amountRaw": { "type": "string" }
          },
          "required": ["marketId"]
        }
      }
    ]
  }
]

Get integration by ID

GET /v1/integrations/:integrationId

Markets

List markets (paginated)

GET /v1/markets

Query parameters

ParameterTypeRequiredDefaultDescription
scopestringNoenabledenabled returns only markets enabled for your project. all returns all markets across all providers.
integrationIdstringNo—Filter by integration
networkstringNo—Filter by network
offsetnumberNo0Pagination offset
limitnumberNo25 (max 100)Page size

Response: PaginatedResponseDto<MarketDto>

{
  "items": [
    {
      "id": "aave-v3-ethereum-usdc",
      "integrationId": "aave-borrow",
      "network": "ethereum",
      "type": "pool",
      "poolAddress": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
      "loanToken": {
        "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "symbol": "USDC",
        "name": "USD Coin",
        "decimals": 6,
        "logoURI": "https://assets.stakek.it/tokens/usdc.svg"
      },
      "collateralTokens": [
        {
          "token": {
            "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
            "symbol": "WETH",
            "name": "Wrapped Ether",
            "decimals": 18,
            "logoURI": "https://assets.stakek.it/tokens/weth.svg"
          },
          "priceUsd": "2750.00",
          "maxLtv": "0.80",
          "liquidationThreshold": "0.85",
          "liquidationPenalty": "0.05",
          "supplyRate": "0.0312"
        }
      ],
      "borrowRate": "0.0487",
      "totalSupply": "1250000000.00",
      "totalSupplyRaw": "1250000000000000",
      "totalBorrow": "890000000.00",
      "totalBorrowRaw": "890000000000000",
      "availableLiquidity": "360000000.00",
      "availableLiquidityRaw": "360000000000000",
      "utilizationRate": "0.712",
      "loanTokenPriceUsd": "1.00",
      "isBorrowEnabled": true,
      "supplyCollateralFeeBps": "0",
      "feeWrapperAddress": null
    }
  ],
  "total": 142,
  "offset": 0,
  "limit": 25
}

For isolated markets (Morpho Blue), collateralTokens always has exactly one entry. For pool markets (Aave, Spark), it lists all eligible collateral assets for the pool.

Get market by ID

GET /v1/markets/:marketId

Positions

Get user positions

GET /v1/positions

Returns the user's full supply + borrow state for an integration on a network.

Query parameters

ParameterTypeRequiredDescription
integrationIdstringYesIntegration identifier
networkstringYesNetwork identifier
addressstringYesUser wallet address

Response: PositionDto

{
  "address": "0x1234...",
  "integrationId": "aave-borrow",
  "network": "ethereum",
  "totalSuppliedUsd": "10000.00",
  "totalCollateralUsd": "10000.00",
  "totalBorrowedUsd": "8750.00",
  "netWorthUsd": "1250.00",
  "healthFactor": "1.37",
  "currentLtv": "0.72",
  "availableToBorrowUsd": "1250.00",
  "netApy": "0.0089",
  "supplyBalances": [
    {
      "marketId": "aave-v3-ethereum-usdc",
      "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      "tokenSymbol": "USDC",
      "balance": "10000.000000",
      "balanceRaw": "10000000000",
      "balanceUsd": "10000.00",
      "apy": "0.0312",
      "isCollateral": true,
      "pendingActions": [
        { "type": "withdraw", "label": "Withdraw USDC", "args": { "marketId": "aave-v3-ethereum-usdc" } }
      ]
    }
  ],
  "debtBalances": [
    {
      "marketId": "aave-v3-ethereum-weth",
      "tokenAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
      "tokenSymbol": "WETH",
      "balance": "2.500000000000000000",
      "balanceRaw": "2500000000000000000",
      "balanceUsd": "8750.00",
      "apy": "0.0523",
      "pendingActions": [
        { "type": "repay", "label": "Repay WETH", "args": { "marketId": "aave-v3-ethereum-weth" } }
      ]
    }
  ]
}

Notes:

  • totalSuppliedUsd is the value of all supplied assets. totalCollateralUsd is the subset enabled as collateral (only these contribute to borrow power).
  • healthFactor is null when the user has no debt.
  • All APY/rate fields are decimals ("0.0312" = 3.12%).

Actions

Create an action

POST /v1/actions      → 201 Created

Creates a lending/borrowing action and returns the unsigned transaction(s) for signing.

Request body: ActionRequestDto

FieldTypeRequiredDescription
integrationIdstringYesIntegration identifier
actionstringYesAction type (see Action types)
addressstringYesUser wallet address
argsobjectYesAction arguments (see below)

Action args

FieldTypeRequiredNotes
marketIdstringYesTarget market identifier (from GET /v1/markets)
amountstringOne of *Human-readable amount (e.g. "1.5")
amountRawstringOne of *Base units (e.g. "1500000" for 1.5 USDC)
repayAllbooleanrepay onlyFull-debt repay using the protocol's full-repay semantics. Only valid for repay. Mutually exclusive with amount / amountRaw.
tokenAddressstringNoToken address to supply / borrow / repay / withdraw
collateralTokenAddressstringPool protocols onlyRequired when providing collateralAmount on pool-based protocols (Aave). Inferred from marketId for isolated-market protocols (Morpho Blue).
collateralAmountstringNoOptional collateral amount, human-readable. Used by borrow (post collateral before borrowing) and repay (withdraw collateral after repaying).
collateralAmountRawstringNoSame as above in base units
borrowAmountstringsupply only †Desired borrow amount in human-readable units of the market's loan token. Provide together with targetLtv to have the API derive the collateral amount to supply (with the supply fee already grossed in). Mutually exclusive with amount / amountRaw.
targetLtvstringsupply only †Target loan-to-value as a decimal ("0.6" = 60%). Must satisfy 0 < targetLtv ≤ market.maxLtv — values above maxLtv return 400. Provide together with borrowAmount.

  • Provide amount or amountRaw (or repayAll: true for full-debt repay). Never combine them.
  • † borrowAmount and targetLtv must be provided together and only on supply actions. They cannot be combined with amount / amountRaw. The API returns the derived collateral as metadata.effectiveCollateralAmount and uses it as the supply amount on the transaction.

Examples

Supply (explicit collateral amount):

{
  "integrationId": "aave-borrow",
  "address": "0x1234...",
  "action": "supply",
  "args": { "marketId": "aave-v3-ethereum-usdc", "amount": "10000" }
}

Supply (derive collateral from desired borrow + target LTV):

{
  "integrationId": "morpho-blue-borrow",
  "address": "0x1234...",
  "action": "supply",
  "args": {
    "marketId": "morpho-blue-ethereum-cbbtc-usdc",
    "borrowAmount": "50000",
    "targetLtv": "0.75"
  }
}

Borrow:

{
  "integrationId": "aave-borrow",
  "address": "0x1234...",
  "action": "borrow",
  "args": { "marketId": "aave-v3-ethereum-weth", "amount": "2.5" }
}

Full-debt repay:

{
  "integrationId": "morpho-blue-borrow",
  "address": "0x1234...",
  "action": "repay",
  "args": { "marketId": "morpho-blue-borrow-ethereum-wbtc-usdt-...", "repayAll": true }
}

Response: ActionDto

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "integrationId": "aave-borrow",
  "action": "supply",
  "address": "0x1234...",
  "status": "CREATED",
  "currentStep": 1,
  "totalSteps": 1,
  "hasNextStep": false,
  "transactions": [
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "network": "ethereum",
      "chainId": "1",
      "type": "APPROVAL",
      "status": "CREATED",
      "address": "0x1234...",
      "signingFormat": "EVM_TRANSACTION",
      "signablePayload": "{\"to\":\"0xA0b8...\",\"data\":\"0x095ea7b3...\"}"
    },
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "network": "ethereum",
      "chainId": "1",
      "type": "SUPPLY",
      "status": "CREATED",
      "address": "0x1234...",
      "signingFormat": "EVM_TRANSACTION",
      "signablePayload": "{\"to\":\"0x8787...\",\"data\":\"0x617ba037...\"}"
    }
  ],
  "metadata": {
    "currentHealthFactor": "1.82",
    "predictedHealthFactor": "1.37",
    "currentLtv": "0.55",
    "predictedLtv": "0.72",
    "liquidationThreshold": "0.85",
    "predictedTotalSupplyUsd": "20000.00",
    "predictedTotalDebtUsd": "8750.00",
    "feeAmount": "0",
    "feeBps": 0
  },
  "createdAt": "2026-02-23T12:00:00.000Z"
}

Both the APPROVAL and the main transaction (here, SUPPLY) are returned together. Sign and submit them in order. APPROVAL is omitted when the user's allowance is already sufficient.

Get action

GET /v1/actions/:id

Returns the current state of an action. Use for polling status transitions.

List actions (paginated)

GET /v1/actions
ParameterTypeRequiredDescription
addressstringNoFilter by wallet
integrationIdstringNoFilter by integration
actionstringNoFilter by action type
statusstringNoFilter by single status
statusesstring[]NoFilter by multiple statuses (CSV or repeated param, e.g. ?statuses=SUCCESS,FAILED). Takes precedence over status when both are provided.
offsetnumberNoDefault 0
limitnumberNoDefault 25, max 100

Response: PaginatedResponseDto<ActionDto>

Advance to next step (async flows only)

POST /v1/actions/:id/step

For async multi-step actions (cross-chain bridges, delayed withdrawals), retrieves the next transaction(s) once the previous step is confirmed on-chain. Only call this when hasNextStep is true. None of the currently supported integrations use this pattern.


Transactions

Submit a signed transaction

POST /v1/transactions/:transactionId/submit

Submit either a signedPayload (the API broadcasts) or a transactionHash (you've already broadcast). Provide exactly one.

Request body: SubmitTransactionDto

FieldTypeDescription
signedPayloadstringSigned transaction payload (max 1 MB)
transactionHashstring0x + 64 hex chars

Response: SubmitTransactionResponseDto

{
  "transactionHash": "0xabc123...",
  "link": "https://etherscan.io/tx/0xabc123...",
  "status": "BROADCASTED"
}

Reference Tables

Action types

ActionDescription
supplyDeposit assets into the protocol. In pool markets (Aave/Spark) earns supply APY and is optionally enabled as collateral. In isolated markets (Morpho Blue) is collateral by definition and does not earn APY.
borrowBorrow assets against supplied collateral
repayRepay borrowed debt (partial or full via repayAll: true)
withdrawWithdraw supplied assets
enableCollateralEnable a supplied asset as collateral (pool markets only)
disableCollateralDisable a supplied asset as collateral (pool markets only)

Transaction types

TypeDescription
APPROVALERC-20 approval (pre supply / repay)
SUPPLYDeposit into the protocol
BORROWBorrow from the protocol
REPAYRepay debt
WITHDRAWWithdraw supplied assets
ENABLE_COLLATERALEnable collateral usage
DISABLE_COLLATERALDisable collateral usage

Transaction statuses

StatusMeaning
CREATEDReady for signing
BLOCKEDWaiting on a prior tx in the same action
WAITING_FOR_SIGNATURESent to signer
SIGNEDSigned by user
BROADCASTEDBroadcast submitted, awaiting inclusion
PENDINGAwaiting confirmations
CONFIRMEDConfirmed on-chain (terminal)
FAILEDFailed on-chain (terminal)
SKIPPEDNot needed (e.g., allowance already sufficient)

Action statuses

StatusMeaningTerminal
CREATEDAt least one transaction in transactions[] is CREATED and ready for signing. Can re-appear within a multi-tx single-step flow once the previously broadcast tx confirms.No
PROCESSINGAt least one transaction has been broadcast and is awaiting confirmation.No
WAITING_FOR_NEXTAsync multi-step flows only. The current step is confirmed and the next step has to be built server-side via POST /v1/actions/:id/step. None of the current borrow integrations use this.No
STALEA transaction has been broadcast for > 30 minutes without confirmation.No
SUCCESSAll transactions confirmed.Yes
FAILEDAn on-chain transaction failed.Yes
CANCELEDThe action was canceled.Yes

End-to-end Examples

Supply → Borrow

1) GET /v1/markets?integrationId=aave-borrow&network=ethereum

2) POST /v1/actions
   {
     "integrationId": "aave-borrow",
     "action": "supply",
     "address": "0x...",
     "args": { "marketId": "aave-v3-ethereum-usdc", "amount": "10000" }
   }
   → ActionDto with totalSteps=1, hasNextStep=false
   → transactions = [APPROVAL, SUPPLY]   (APPROVAL omitted if allowance is sufficient)

3) For each tx in transactions[], in order:
     - sign tx.signablePayload
     - POST /v1/transactions/{tx.id}/submit { signedPayload }

4) Poll GET /v1/actions/{id} until status=SUCCESS

5) POST /v1/actions
   {
     "integrationId": "aave-borrow",
     "action": "borrow",
     "address": "0x...",
     "args": { "marketId": "aave-v3-ethereum-weth", "amount": "2.5" }
   }
   → transactions = [BORROW]

6) Sign + submit, poll until SUCCESS

7) GET /v1/positions?integrationId=aave-borrow&network=ethereum&address=0x...

Repay → Withdraw

1) POST /v1/actions
   {
     "integrationId": "aave-borrow",
     "action": "repay",
     "address": "0x...",
     "args": { "marketId": "aave-v3-ethereum-weth", "repayAll": true }
   }
   → transactions = [APPROVAL, REPAY]   (APPROVAL omitted if allowance is sufficient)

2) Sign + submit each tx in transactions[], in order
   Poll until SUCCESS

3) POST /v1/actions
   {
     "integrationId": "aave-borrow",
     "action": "withdraw",
     "address": "0x...",
     "args": { "marketId": "aave-v3-ethereum-usdc", "amount": "10000" }
   }
   → transactions = [WITHDRAW]

4) Sign + submit, poll until SUCCESS

Monetization

Yield.xyz's borrow and lending integrations expose multiple monetization paths. The right choice depends on the protocol — some support direct fee capture via wrapper contracts, others monetize through protocol-level revenue share or curated liquidity.

Direct Fee Capture

A single wrapper contract per chain charges configurable fees inside the user's transactions. The same wrapper exposes two independent fee surfaces — borrow fees on borrowed amounts and supply fees on collateral deposited. Each has its own bps getter on the contract, can be set to zero independently, and is capped at 500 bps (5%) on-chain.

Revenue is immediate, fully attributable, and does not depend on the underlying protocol's own fee mechanics. The user's position stays on their EOA.

Currently supported on: morpho-blue-borrow. Aave and Spark pool markets have no fee wrapper today and always return zero values.

Supply Fees

A deposit fee charged on collateral supplied via supplyCollateral. A user supplying 1 cbBTC against a 50 bps wrapper deposits 1 cbBTC and ends up with 0.995 cbBTC of on-chain collateral — the 0.005 cbBTC is captured as fee.

Flow:

  • 2-tx: ERC-20 approval to the wrapper → wrapper.supplyCollateral(...). No EIP-712 variant; the wrapper holds the allowance for exactly the duration of the call.
  • Approval target is the wrapper address, not Morpho. The API returns the correct spender so no client-side branching needed.

Key properties:

  • Fee basis: gross collateral submitted. Range 0–500 bps, capped on-chain.
  • Independent of the borrow fee: supplyCollateralFeeBps and borrowFeeBps are separate per-operation rates on the same wrapper — either can be zero while the other is active.
  • Predicted risk metrics are post-fee. predictedHealthFactor and predictedLtv reflect the net on-chain position, matching what Morpho records after the wrapper takes its cut.
  • Two ways to size a supply action. Pass amount/amountRaw as the gross collateral and read metadata.feeAmount to know what will be skimmed; or pass borrowAmount + targetLtv and let the API gross up the collateral so the post-fee position hits the requested LTV.

API surface:

FieldWhereMeaning
supplyCollateralFeeBpsevery GET /v1/markets itemCurrent supply fee rate in basis points ("0"–"500").
feeWrapperAddressevery GET /v1/markets itemWrapper address charging the fee for this project, or null if none configured.
metadata.feeBpsPOST /v1/actions for supplyFee rate at action-creation time. Always present; 0 when no wrapper or wrapper currently charges zero.
metadata.feeAmountPOST /v1/actions for supplyFee in raw units of the supplied (collateral) token. Always present; "0" when there is no fee.
metadata.effectiveCollateralAmountPOST /v1/actions for supply (derived)Gross collateral derived from borrowAmount + targetLtv, already grossed up for the supply fee.

Borrow Fees

A wrapper contract between the user and Morpho Blue that takes an origination fee on the borrowed amount. Borrowing is the true value unlock for a credit product, so charging there lands the fee on the right side of the equation — a user posting $100K collateral to borrow $50K is charged against the $50K, not the $100K.

The position stays on the user's EOA. One wrapper contract per chain. No per-user accounts, factories, or proxies.

Two execution modes:

ModeFlowWhen to use
borrow3-tx: authorize → wrapper borrow → deauthorizeClients that can't sign EIP-712 messages
borrowWithSig1-tx atomic: authorize + borrow + fee + deauthorize via EIP-712 sigsClients that support message signing

Key properties:

  • Fee basis: borrowed amount. Origination fee in bps, bounded on-chain by minFeeBps / maxFeeBps.
  • Dynamic pricing: exact fee is computed off-chain by the API per market and passed into each transaction — we can charge less when borrow rates are high and more when they're low, within the on-chain bounds.
  • No standing authorization: the wrapper is authorized only for the duration of the flow and deauthorized at the end. For the 3-tx variant the API enforces the deauthorize step by returning authorize, borrow, and deauthorize together in the same action's transactions[], so it always runs.
  • Wrapper only touches borrow. Supply, repay, and withdraw go directly to Morpho — no fee, no extra hops.
  • Fee splitting between Yield.xyz and the partner is settled off-chain.

Revenue Share via Protocol Mechanics

For protocols that don't lend themselves to direct fee capture, monetization runs through protocol-level revenue share or curated liquidity.

SparkLend — Integrator Revenue Share

SparkLend does not expose a commission or fee that integrators can charge on top. Spark's economics are driven by its cost of capital — the rate at which Spark borrows from Sky — and it captures a variable margin between that funding cost and user borrowing activity. A portion of that margin is shared with integrators.

  • Attribution: borrowing volume is tracked per integrator via an integrator-specific ref_code.
  • Revenue basis: a share of Spark's margin over its cost of capital — not a fixed protocol fee, and not the market's visible borrow–supply spread.
  • Variability: the margin and the integrator share are not fixed; they flex with borrowing demand, market conditions, and the scale/quality of the integrator.
  • Distribution: typically manual, paid out once a minimum borrowed-TVL threshold is reached.

###



Did this page help you?