from compass_api_sdk import CompassAPI, models
with CompassAPI(
api_key_auth="<YOUR_API_KEY_HERE>",
) as compass_api:
res = compass_api.credit.credit_unloop(owner="0x0E407CdeBD8e078E6966ef6740540d25F5897082", chain=models.CreditUnloopRequestChain.ETHEREUM, collateral_token="WETH", borrow_token="USDC", protocol=models.CreditProtocol.AAVE, sub_account_id=0, max_slippage_percent=0.5, allow_partial=False, gas_sponsorship=False, preview=False, pricing=models.CreditUnloopRequestPricing.AUTO)
# Handle response
print(res)import { CompassApiSDK } from "@compass-labs/api-sdk";
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: "<YOUR_API_KEY_HERE>",
});
async function run() {
const result = await compassApiSDK.credit.creditUnloop({
owner: "0x0E407CdeBD8e078E6966ef6740540d25F5897082",
chain: "ethereum",
protocol: "AAVE",
collateralToken: "WETH",
borrowToken: "USDC",
maxSlippagePercent: 0.5,
allowPartial: false,
gasSponsorship: false,
});
console.log(result);
}
run();curl --request POST \
--url https://api.compasslabs.ai/v2/credit/unloop \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"owner": "0x0E407CdeBD8e078E6966ef6740540d25F5897082",
"chain": "ethereum",
"protocol": "AAVE",
"collateral_token": "WETH",
"borrow_token": "USDC",
"max_slippage_percent": 0.5,
"allow_partial": false,
"gas_sponsorship": false
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
owner: '0x0E407CdeBD8e078E6966ef6740540d25F5897082',
chain: 'ethereum',
protocol: 'AAVE',
collateral_token: 'WETH',
borrow_token: 'USDC',
max_slippage_percent: 0.5,
allow_partial: false,
gas_sponsorship: false
})
};
fetch('https://api.compasslabs.ai/v2/credit/unloop', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compasslabs.ai/v2/credit/unloop",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'owner' => '0x0E407CdeBD8e078E6966ef6740540d25F5897082',
'chain' => 'ethereum',
'protocol' => 'AAVE',
'collateral_token' => 'WETH',
'borrow_token' => 'USDC',
'max_slippage_percent' => 0.5,
'allow_partial' => false,
'gas_sponsorship' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compasslabs.ai/v2/credit/unloop"
payload := strings.NewReader("{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compasslabs.ai/v2/credit/unloop")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compasslabs.ai/v2/credit/unloop")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}"
response = http.request(request)
puts response.read_body{
"preview": {
"iterations": 123,
"total_collateral_withdrawn": "<string>",
"total_debt_repaid": "<string>",
"ending_collateral": "<string>",
"ending_debt": "<string>",
"ending_multiplier": "<string>",
"projected_ltv": "<string>",
"projected_health_factor": "<string>",
"estimated_max_dust": "<string>",
"fully_unwound": true,
"legs": []
},
"transaction": {
"chainId": "0x2105",
"data": "0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c762000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000675f4a3d",
"from": "0x4A83b4413CF41C3244027e1590E35a0F48403F0c",
"gas": "0x7a120",
"maxFeePerGas": "0x59682f00",
"maxPriorityFeePerGas": "0x3b9aca00",
"nonce": "0x5",
"to": "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
"value": "0x0"
},
"eip_712": {
"domain": {
"chainId": 8453,
"verifyingContract": "0x6B90E8B4E3E971E74C1A47a3a20976377E2dB4b1"
},
"message": {
"baseGas": "0",
"data": "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"nonce": "7",
"operation": 1,
"refundReceiver": "0x0000000000000000000000000000000000000000",
"safeTxGas": "0",
"to": "0x93C23AAE4793C14D6DF35D2A2A2234204e1559dA",
"value": "0"
},
"primaryType": "SafeTx",
"types": {
"EIP712Domain": [
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"SafeTx": [
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "data",
"type": "bytes"
},
{
"name": "operation",
"type": "uint8"
},
{
"name": "safeTxGas",
"type": "uint256"
},
{
"name": "baseGas",
"type": "uint256"
},
{
"name": "gasPrice",
"type": "uint256"
},
{
"name": "gasToken",
"type": "address"
},
{
"name": "refundReceiver",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
}
]
}
},
"swap_provider": "market",
"quote_expires_at": "2023-11-07T05:31:56Z",
"firm_available": true
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Unwind a leveraged loop
Unwind a leveraged position in ONE atomic transaction.
Withdraw collateral, swap it back to the borrow token, repay, repeat — until the position reaches target_multiplier, or until the debt is cleared exactly if you omit it. If any step fails the whole transaction reverts.
See the Leveraged Looping guide for chain coverage, vault-share collateral, shared Aave reserves and the full error list.
from compass_api_sdk import CompassAPI, models
with CompassAPI(
api_key_auth="<YOUR_API_KEY_HERE>",
) as compass_api:
res = compass_api.credit.credit_unloop(owner="0x0E407CdeBD8e078E6966ef6740540d25F5897082", chain=models.CreditUnloopRequestChain.ETHEREUM, collateral_token="WETH", borrow_token="USDC", protocol=models.CreditProtocol.AAVE, sub_account_id=0, max_slippage_percent=0.5, allow_partial=False, gas_sponsorship=False, preview=False, pricing=models.CreditUnloopRequestPricing.AUTO)
# Handle response
print(res)import { CompassApiSDK } from "@compass-labs/api-sdk";
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: "<YOUR_API_KEY_HERE>",
});
async function run() {
const result = await compassApiSDK.credit.creditUnloop({
owner: "0x0E407CdeBD8e078E6966ef6740540d25F5897082",
chain: "ethereum",
protocol: "AAVE",
collateralToken: "WETH",
borrowToken: "USDC",
maxSlippagePercent: 0.5,
allowPartial: false,
gasSponsorship: false,
});
console.log(result);
}
run();curl --request POST \
--url https://api.compasslabs.ai/v2/credit/unloop \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"owner": "0x0E407CdeBD8e078E6966ef6740540d25F5897082",
"chain": "ethereum",
"protocol": "AAVE",
"collateral_token": "WETH",
"borrow_token": "USDC",
"max_slippage_percent": 0.5,
"allow_partial": false,
"gas_sponsorship": false
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
owner: '0x0E407CdeBD8e078E6966ef6740540d25F5897082',
chain: 'ethereum',
protocol: 'AAVE',
collateral_token: 'WETH',
borrow_token: 'USDC',
max_slippage_percent: 0.5,
allow_partial: false,
gas_sponsorship: false
})
};
fetch('https://api.compasslabs.ai/v2/credit/unloop', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compasslabs.ai/v2/credit/unloop",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'owner' => '0x0E407CdeBD8e078E6966ef6740540d25F5897082',
'chain' => 'ethereum',
'protocol' => 'AAVE',
'collateral_token' => 'WETH',
'borrow_token' => 'USDC',
'max_slippage_percent' => 0.5,
'allow_partial' => false,
'gas_sponsorship' => false
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compasslabs.ai/v2/credit/unloop"
payload := strings.NewReader("{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compasslabs.ai/v2/credit/unloop")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compasslabs.ai/v2/credit/unloop")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"owner\": \"0x0E407CdeBD8e078E6966ef6740540d25F5897082\",\n \"chain\": \"ethereum\",\n \"protocol\": \"AAVE\",\n \"collateral_token\": \"WETH\",\n \"borrow_token\": \"USDC\",\n \"max_slippage_percent\": 0.5,\n \"allow_partial\": false,\n \"gas_sponsorship\": false\n}"
response = http.request(request)
puts response.read_body{
"preview": {
"iterations": 123,
"total_collateral_withdrawn": "<string>",
"total_debt_repaid": "<string>",
"ending_collateral": "<string>",
"ending_debt": "<string>",
"ending_multiplier": "<string>",
"projected_ltv": "<string>",
"projected_health_factor": "<string>",
"estimated_max_dust": "<string>",
"fully_unwound": true,
"legs": []
},
"transaction": {
"chainId": "0x2105",
"data": "0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c762000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000675f4a3d",
"from": "0x4A83b4413CF41C3244027e1590E35a0F48403F0c",
"gas": "0x7a120",
"maxFeePerGas": "0x59682f00",
"maxPriorityFeePerGas": "0x3b9aca00",
"nonce": "0x5",
"to": "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
"value": "0x0"
},
"eip_712": {
"domain": {
"chainId": 8453,
"verifyingContract": "0x6B90E8B4E3E971E74C1A47a3a20976377E2dB4b1"
},
"message": {
"baseGas": "0",
"data": "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"nonce": "7",
"operation": 1,
"refundReceiver": "0x0000000000000000000000000000000000000000",
"safeTxGas": "0",
"to": "0x93C23AAE4793C14D6DF35D2A2A2234204e1559dA",
"value": "0"
},
"primaryType": "SafeTx",
"types": {
"EIP712Domain": [
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"SafeTx": [
{
"name": "to",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "data",
"type": "bytes"
},
{
"name": "operation",
"type": "uint8"
},
{
"name": "safeTxGas",
"type": "uint256"
},
{
"name": "baseGas",
"type": "uint256"
},
{
"name": "gasPrice",
"type": "uint256"
},
{
"name": "gasToken",
"type": "address"
},
{
"name": "refundReceiver",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
}
]
}
},
"swap_provider": "market",
"quote_expires_at": "2023-11-07T05:31:56Z",
"firm_available": true
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Body
Unwind a leveraged loop: repeatedly withdraw collateral, swap it back to the borrow token at a guaranteed minimum output, and repay — all in ONE atomic transaction from the Credit Account.
The address that owns the Credit Account.
"0x0E407CdeBD8e078E6966ef6740540d25F5897082"
Blockchain network. Not every protocol is deployed on every chain — see the protocol field — and a chain with no credit venue at all returns a 422 naming the chains that do.
arbitrum, base, bsc, ethereum, hyperevm, tempo "ethereum"
Token supplied as collateral in the loop being unwound. For MORPHO it must be the market's collateral token.
"wstETH"
"WETH"
Token borrowed in the loop; withdrawn collateral is swapped back to it and used to repay. For MORPHO it must be the market's loan token.
"WETH"
"USDC"
Lending protocol to unwind: AAVE, MORPHO, or EULER. On chain=hyperevm, AAVE is Hyperlend and MORPHO is Felix; EULER is not deployed there.
AAVE, EULER, MORPHO "AAVE"
Morpho only: the bytes32 market id (from /v2/credit/morpho_markets). Required when protocol=MORPHO.
Euler only: the EVK vault the loop's collateral is in. Required when protocol=EULER.
Euler only: the EVK vault the loop borrowed from (the sub-account's controller). Required when protocol=EULER.
Euler only: the EVC sub-account (0-255) holding the looped position to unwind. 0 is the Credit Account itself.
0 <= x <= 255Target leverage after the unwind. Omit (null) for a FULL unwind: the debt is cleared exactly — accrued interest included — and all pair collateral is withdrawn back to the Credit Account. 1 runs the same exact debt close but leaves the collateral supplied (earning). A value greater than 1 delevers the position to that multiplier. Must be below the position's current multiplier.
x >= 12
Per-swap slippage tolerance in percent. Unwind dust is bounded by this per iteration, so tighter slippage means less dust.
0 < x <= 100.3
If the target cannot be reached in one transaction, return the maximum-progress plan (preview.fully_unwound=false) instead of a 400. This can happen because every withdrawal is sized to keep the health factor at or above 1.02 at that step, so a position opened very close to its liquidation threshold runs out of headroom before the target is met. A second unloop call, now from a much lower leverage, finishes the job.
false
If true, returns EIP-712 typed data for gas-sponsored execution instead of an unsigned transaction.
false
If true, build a display estimate only — no single-use firm quote is ever spent. NOTE that this guarantees only that no firm quote was spent — it does not guarantee an absent transaction: on an unwind no firm provider covers, and under pricing=market, the call still falls through to the aggregator and returns a signable transaction. On a firm-covered unwind (pricing 'auto' or 'firm') the numbers are firm-INDICATIVE, priced off the firm provider's live maker levels; otherwise they are market-priced with slippage-bounded floors. Under pricing='firm' a preview no firm provider can serve returns the advisory alone (preview=null + firm_available). Set it on every parameter-exploration call and omit it only on the build the user is about to sign.
Swap-leg routing policy. 'auto': firm quotes where a firm venue covers the pair, transparent fallback to the market aggregator otherwise. 'firm': never price on the market route — previews the firm venue cannot serve return the firm_available advisory alone (preview=null, zero aggregator calls), and executions fail with a typed error instead of silently substituting market pricing. 'market': never route through the firm venue; every leg is priced by the aggregator. max_slippage_percent applies on EVERY policy — unlike the loop, firm unwinds consume it to size the guaranteed withdraw/repay floors (the fills themselves are exact). 'firm' is incompatible with gas_sponsorship (sponsored unwinds force market routing).
auto, firm, market Response
Successful Response
The atomic unwind transaction plus its guaranteed-floor preview.
Projected end state, computed on guaranteed swap floors. Null only on pricing='firm' preview responses whose unwind the firm venue cannot serve: no leg was priced on any venue, and the response carries the firm_available advisory alone.
Show child attributes
Show child attributes
Unsigned transaction for direct execution by the owner. Present when gas_sponsorship=false — except firm-priced previews (preview=true on a firm-priced build), which carry numbers only: the firm quotes are fetched at execution time, so there is no payload to sign yet.
Show child attributes
Show child attributes
{
"chainId": "0x2105",
"data": "0x1688f0b900000000000000000000000029fcb43b46531bca003ddc8fcb67ffe91900c762000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000675f4a3d",
"from": "0x4A83b4413CF41C3244027e1590E35a0F48403F0c",
"gas": "0x7a120",
"maxFeePerGas": "0x59682f00",
"maxPriorityFeePerGas": "0x3b9aca00",
"nonce": "0x5",
"to": "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67",
"value": "0x0"
}
EIP-712 typed data for gas-sponsored execution. Present when gas_sponsorship=true.
Show child attributes
Show child attributes
{
"domain": {
"chainId": 8453,
"verifyingContract": "0x6B90E8B4E3E971E74C1A47a3a20976377E2dB4b1"
},
"message": {
"baseGas": "0",
"data": "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020",
"gasPrice": "0",
"gasToken": "0x0000000000000000000000000000000000000000",
"nonce": "7",
"operation": 1,
"refundReceiver": "0x0000000000000000000000000000000000000000",
"safeTxGas": "0",
"to": "0x93C23AAE4793C14D6DF35D2A2A2234204e1559dA",
"value": "0"
},
"primaryType": "SafeTx",
"types": {
"EIP712Domain": [
{ "name": "chainId", "type": "uint256" },
{
"name": "verifyingContract",
"type": "address"
}
],
"SafeTx": [
{ "name": "to", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "data", "type": "bytes" },
{ "name": "operation", "type": "uint8" },
{ "name": "safeTxGas", "type": "uint256" },
{ "name": "baseGas", "type": "uint256" },
{ "name": "gasPrice", "type": "uint256" },
{ "name": "gasToken", "type": "address" },
{
"name": "refundReceiver",
"type": "address"
},
{ "name": "nonce", "type": "uint256" }
]
}
}
Identifies which route priced the swap leg(s): 'market' — a DEX aggregator (pool liquidity, slippage-bounded floors) — or 'firm' — quotes, one per swap leg, each partially filled at the leg's size. On preview=true responses 'firm' means the numbers are INDICATIVE, computed from live maker price levels without spending any quote; execution fetches the firm quotes at signing time. Always present, including on fallbacks. This is the authoritative firm-vs-market signal and clients do need to read it: pricing is only what was REQUESTED (under 'auto' a firm build can fall back to market transparently), and quote_expires_at is absent on every preview — so neither substitutes for this field.
market, firm Deadline of the firm swap quotes (the earliest across the unwind's swap legs) — sign and broadcast before it or the transaction reverts on-chain; refresh by re-calling this endpoint (discard the previous payload). Present only on executable firm-priced builds; null on previews (no quote is spent for a preview).
Preview-only advisory: whether a firm-quote venue can serve this unwind's swap legs (estimated without spending any quote). Present only on preview=true responses; null otherwise. Computed on every policy — including pricing='market', whose preview numbers stay market-priced — so a client that opted out can keep the boundary visible.
Was this page helpful?