Debt swap (simulate)
POST/v1/actions/loop/debt-swap
Swap debt between borrow positions and simulate post-trade state. Same parameters as GET. Optionally send a JSON body with current portfolio state (balanceData, aprData, positions) to receive projected post-trade metrics in the simulation field — if omitted, the API fetches balances on-chain automatically. Use the data returned by the user-positions endpoint directly — always include positions for accurate health-factor and borrow-capacity projections.
Plain-text reference — POST /v1/actions/loop/debt-swap
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
route | query | auto, bundler3, native, composer, proxy | no | Which assembly to use, for lenders that have more than one. Morpho Blue only today. |
- (omitted) —
auto: prefer the protocol’s own tooling and fall back to the composer for anything it cannot express (a cross-market pair, a margin paid in a foreign asset, anEXACT_OUTPUTclose, a chain with no bundler3 or no swap adapter). bundler3/native— force Morpho’s bundler3. A request it cannot serve becomes an ERROR instead of silently falling back, which is what makes the route testable.composer/proxy— force the 1delta composer.
The default matters: the bundler3 route asks for setAuthorization(GeneralAdapter1),
Morpho’s own audited adapter, where the composer route asks for a standing grant on ours. |
| marketUidIn | query | string | yes | Market identifier for input side (lender:chainId:address). |
| marketUidOut | query | string | yes | Market identifier for output side (lender:chainId:address). |
| slippage | query | number | yes | Slippage tolerance (basis points) |
| account | query | string | no | Account address. Include to build transaction, omit for quote-only. |
| amount | query | string | yes | Amount in wei |
| tradeType | query | 0, 1 | no | Trade type |
| irModeIn | query | 0, 1, 2 | no | Interest rate mode for repaid debt |
| irModeOut | query | 0, 1, 2 | no | Interest rate mode for new debt |
| usePendleMintRedeem | query | boolean | no | Use Pendle mint/redeem |
| isAll | query | boolean | no | Repay full debt |
| accountId | query | string | no | Account ID |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
balanceData | object | yes | Aggregated balance data for a sub-account. |
balanceData.deposits | number | no | Total deposits in USD |
balanceData.debt | number | no | Total debt in USD |
balanceData.adjustedDebt | number | no | Debt adjusted for borrow factors |
balanceData.collateral | number | no | Collateral value in USD |
balanceData.collateralAllActive | number | no | Collateral if all assets were enabled |
balanceData.borrowDiscountedCollateral | number | no | Collateral discounted by borrow factors |
balanceData.borrowDiscountedCollateralAllActive | number | no | Discounted collateral if all enabled |
balanceData.nav | number | no | Net asset value (deposits - debt) |
balanceData.deposits24h | number | no | Deposits 24h ago (for change calculation) |
balanceData.debt24h | number | no | Debt 24h ago |
balanceData.nav24h | number | no | NAV 24h ago |
balanceData.rewards | object[] | no | Pending reward token claims. Each entry represents a single reward program. |
balanceData.rewards[].asset | string | no | Reward token contract address |
balanceData.rewards[].totalRewards | number | no | Total accumulated rewards (token units) |
balanceData.rewards[].claimableRewards | number | no | Immediately claimable rewards (token units) |
aprData | object | yes | APR breakdown for a sub-account. |
aprData.apr | number | no | Net APR (deposit - borrow) |
aprData.depositApr | number | no | Weighted deposit APR |
aprData.borrowApr | number | no | Weighted borrow APR |
aprData.rewardApr | number | no | Total reward APR |
aprData.rewardDepositApr | number | no | Reward APR on deposits |
aprData.rewardBorrowApr | number | no | Reward APR on borrows |
aprData.intrinsicApr | number | no | Intrinsic yield APR (e.g., stETH staking) |
aprData.intrinsicDepositApr | number | no | Intrinsic yield APR portion from deposits |
aprData.intrinsicBorrowApr | number | no | Intrinsic yield APR portion from borrows |
aprData.rewards | object | no | Per-reward-token APR breakdown. Keys are reward token addresses. |
modeId | string | no | Mode/config key from userConfig.selectedMode (defaults to "0") |
positions | object[] | no | Current lending positions from the matching sub-account's positions array. The full LendingPosition objects returned by user-positions are accepted — only the fields in SimulationPosition are used. Always include this for accurate health-factor and borrow-capacity projections. |
positions[].marketUid | string | yes | Unique market identifier (format: {lender}:{chainId}:{address}) |
positions[].depositsUSD | number | yes | Deposit amount in USD |
positions[].debtUSD | number | yes | Variable debt in USD |
positions[].debtStableUSD | number | yes | Stable debt in USD |
positions[].collateralEnabled | boolean | yes | Whether this asset is enabled as collateral |
Response 200
| Field | Type | Description |
|---|---|---|
success | True | |
data | object | Quotes and simulation results |
data.lender | string | Protocol identifier. See the LenderId schema. |
data.quotes | object[] | Candidate routes, best output first. Execute exactly one. |
data.quotes[].deltas | object | |
data.quotes[].deltas.aggregator | string | Aggregator source |
data.quotes[].deltas.tradeInput | number | Trade input amount |
data.quotes[].deltas.tradeOutput | number | Trade output amount |
data.quotes[].deltas.deltas | object | Balance deltas |
data.quotes[].rateImpact | object[] | Projected interest-rate impact per market for THIS quote (its own trade amounts). Omitted if IRM data is unavailable. |
data.quotes[].rateImpact[].marketUid | string | Market identifier (format: lender:chainId:address) |
data.quotes[].rateImpact[].utilization | object | A current/projected pair for a single rate metric. |
data.quotes[].rateImpact[].borrowRate | object | A current/projected pair for a single rate metric. |
data.quotes[].rateImpact[].depositRate | object | A current/projected pair for a single rate metric. |
data.rateImpact | object[] | Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable. |
data.rateImpact[].marketUid | string | Market identifier (format: lender:chainId:address) |
data.rateImpact[].utilization | object | A current/projected pair for a single rate metric. |
data.rateImpact[].utilization.current | number | Current value |
data.rateImpact[].utilization.projected | number | Projected value after the action |
data.rateImpact[].borrowRate | object | A current/projected pair for a single rate metric. |
data.rateImpact[].borrowRate.current | number | Current value |
data.rateImpact[].borrowRate.projected | number | Projected value after the action |
data.rateImpact[].depositRate | object | A current/projected pair for a single rate metric. |
data.rateImpact[].depositRate.current | number | Current value |
data.rateImpact[].depositRate.projected | number | Projected value after the action |
data.simulation | object | Projected post-trade metrics, or null if simulation failed |
data.simulation.pre | object | Portfolio state before the trade. |
data.simulation.pre.healthFactor | number | Health factor before the trade (null-safe: capped at 1e18 when no debt) |
data.simulation.pre.borrowCapacity | number | Borrow capacity (USD) before the trade |
data.simulation.post | object | Projected portfolio state after the trade. |
data.simulation.post.healthFactor | number | Projected health factor after the trade |
data.simulation.post.borrowCapacity | number | Projected borrow capacity (USD) after the trade |
data.simulation.post.balanceData | object | Aggregated balance data for a sub-account. |
data.simulation.post.aprData | object | APR breakdown for a sub-account. |
data.simulationError | string | Error message if simulation failed |
data.lender | string | Protocol identifier. See the LenderId schema. |
data.quotes | object[] | Candidate routes, best output first. Execute exactly one. |
data.quotes[].deltas | object | |
data.quotes[].deltas.aggregator | string | Aggregator source |
data.quotes[].deltas.tradeInput | number | Trade input amount |
Example response
{
"success": true,
"data": {
"lender": "AAVE_V3",
"quotes": [
{
"deltas": {
"aggregator": "string",
"tradeInput": 1.0,
"tradeOutput": 1.0,
"deltas": {}
},
"rateImpact": [
{
"marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
"utilization": {
"current": 1.0,
"projected": 1.0
},
"borrowRate": {
"current": 1.0,
"projected": 1.0
},
"depositRate": {
"current": 1.0,
"projected": 1.0
}
}
]
}
],
"rateImpact": [
{
"marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
"utilization": {
"current": 1.0,
"projected": 1.0
},
"borrowRate": {
"current": 1.0,
"projected": 1.0
},
"depositRate": {
"current": 1.0,
"projected": 1.0
}
}
],
"simulation": {
"pre": {
"healthFactor": 1.85,
"borrowCapacity": 3000
},
"post": {
"healthFactor": 2.1,
"borrowCapacity": 3500,
"balanceData": {
"deposits": 10000.5,
"debt": 5000.25,
"adjustedDebt": 5500,
"collateral": 9000,
"collateralAllActive": 10000.5,
"borrowDiscountedCollateral": 8000,
"borrowDiscountedCollateralAllActive": 9000,
"nav": 5000.25,
"deposits24h": 9800,
"debt24h": 4900,
"nav24h": 4900,
"rewards": [
{
"asset": "0xc00e94Cb662C3520282E6f5717214004A7f26888",
"totalRewards": 12.5,
"claimableRewards": 12.5
}
]
},
"aprData": {
"apr": 2.5,
"depositApr": 3.5,
"borrowApr": 5.2,
"rewardApr": 1.2,
"rewardDepositApr": 0.8,
"rewardBorrowApr": 0.4,
"intrinsicApr": 0,
"intrinsicDepositApr": 0,
"intrinsicBorrowApr": 0,
"rewards": {}
}
}
},
"simulationError": "string"
},
"actions": {
"transactions": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"alternatives": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"permissions": [
{
"to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
]
}
}
Request
Responses
- 200
- 400
- 429
- 500
- 502
Quote or full build with simulation
Validation error
Rate limited. Unauthenticated callers share a per-IP budget; send an x-api-key header to lift it. Retry with exponential backoff.
Unexpected server error. Safe to retry with backoff.
An upstream data source or protocol origin failed (error.code is ORIGIN_FAILED). error.details carries the per-origin status. This is also what a missing or malformed required parameter currently returns, rather than a 400.