> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sprinter.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Yield-Optimized Collateral Manager

> Agent skill that rebalances collateral across earn vaults to maximize yield while maintaining credit position health

## Overview

This agent skill manages which earn vaults a user's collateral sits in, automatically rebalancing to maximize yield while keeping the credit line active and healthy.

* **Compares vault yields** across supported strategies
* **Rebalances** from lower-yield to higher-yield vaults
* **Protects the position** by only rebalancing when health factor is safe

## API Endpoints Used

| Endpoint                                | Purpose                                                              |
| --------------------------------------- | -------------------------------------------------------------------- |
| `GET /credit/protocol`                  | Get available earn strategies                                        |
| `GET /credit/accounts/{account}/info`   | Check health factor and current position                             |
| `GET /credit/accounts/{account}/unlock` | Build calldata to unlock collateral from current vault               |
| `GET /credit/accounts/{account}/lock`   | Build calldata to lock collateral into new vault (with `earn` param) |

## How It Works

<Steps>
  <Step title="Check Current Position">
    The agent checks the user's credit position to ensure it's healthy enough to rebalance:

    ```bash theme={null}
    curl -X GET https://api.sprinter.tech/credit/accounts/0xUSER/info
    ```

    The agent only proceeds if `healthFactor` > 1.5 — rebalancing temporarily changes the collateral composition, so a larger buffer is needed.
  </Step>

  <Step title="Discover Available Strategies">
    The agent fetches the protocol config to get available earn strategies and their IDs:

    ```bash theme={null}
    curl -X GET https://api.sprinter.tech/credit/protocol
    ```

    This returns the credit configuration including a `strategies` field with available earn vaults. The agent compares the user's current vault yield against alternatives.
  </Step>

  <Step title="Rebalance">
    If a higher-yield vault is available, the agent unlocks collateral from the current vault and re-locks into the new one using the `earn` param:

    <Tabs>
      <Tab title="1. Unlock from Current Vault">
        ```bash theme={null}
        curl -X GET 'https://api.sprinter.tech/credit/accounts/0xUSER/unlock?amount=1000000000000000000&asset=0xCOLLATERAL_TOKEN'
        ```

        Returns `{ calls: ContractCall[] }` — execute to unlock and unwrap from the current vault.
      </Tab>

      <Tab title="2. Lock into New Vault">
        ```bash theme={null}
        curl -X GET 'https://api.sprinter.tech/credit/accounts/0xUSER/lock?amount=1000000000000000000&asset=0xCOLLATERAL_TOKEN&earn=NEW_STRATEGY_ID'
        ```

        Returns `{ calls: ContractCall[] }` — execute to wrap into the new vault and lock as collateral in one step.
      </Tab>
    </Tabs>

    <Warning>
      The unlock and lock must be executed sequentially — the collateral must be fully unlocked before it can be re-locked into a new vault.
    </Warning>
  </Step>
</Steps>

## Implementation

```typescript theme={null}
const SPRINTER_API = "https://api.sprinter.tech";
const POLL_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
const MIN_HEALTH_FACTOR = 1.5; // higher threshold during rebalancing
const MIN_YIELD_IMPROVEMENT = 0.5; // only rebalance if >= 0.5% APY improvement

interface ContractCall {
  to: string;
  data: string;
  value: string;
}

interface VaultPosition {
  strategy: string;
  amount: string;
  asset: string;
  apy: number;
}

async function getCreditInfo(account: string) {
  const res = await fetch(`${SPRINTER_API}/credit/accounts/${account}/info`);
  if (!res.ok) throw new Error(`Credit info failed: ${res.status}`);
  return res.json();
}

async function getProtocolConfig() {
  const res = await fetch(`${SPRINTER_API}/credit/protocol`);
  if (!res.ok) throw new Error(`Protocol config failed: ${res.status}`);
  return res.json();
}

async function buildUnlockCalls(
  account: string,
  amount: string,
  asset: string
): Promise<ContractCall[]> {
  const res = await fetch(
    `${SPRINTER_API}/credit/accounts/${account}/unlock?amount=${amount}&asset=${asset}`,
  );
  if (!res.ok) throw new Error(`Unlock calldata failed: ${res.status}`);
  const data = await res.json();
  return data.calls;
}

async function buildLockCalls(
  account: string,
  amount: string,
  asset: string,
  earn?: string
): Promise<ContractCall[]> {
  let url = `${SPRINTER_API}/credit/accounts/${account}/lock?amount=${amount}&asset=${asset}`;
  if (earn) url += `&earn=${earn}`;

  const res = await fetch(url);
  if (!res.ok) throw new Error(`Lock calldata failed: ${res.status}`);
  const data = await res.json();
  return data.calls;
}

async function executeCalls(calls: ContractCall[], signer: any): Promise<string> {
  let lastTxHash = "";
  for (const call of calls) {
    const tx = await signer.sendTransaction({
      to: call.to,
      data: call.data,
      value: call.value || "0",
    });
    const receipt = await tx.wait();
    if (!receipt || receipt.status !== 1) throw new Error(`Tx reverted: ${tx.hash}`);
    lastTxHash = tx.hash;
  }
  return lastTxHash;
}

async function optimizeYield(
  account: string,
  currentPosition: VaultPosition,
  signer: any
) {
  // 1. Check health factor
  const info = await getCreditInfo(account);
  const hf = parseFloat(info.data.USDC.healthFactor);

  if (hf < MIN_HEALTH_FACTOR) {
    console.log(`Health factor ${hf.toFixed(2)} too low to rebalance — skipping`);
    return;
  }

  // 2. Get available strategies from protocol config
  const config = await getProtocolConfig();
  const strategies = config.strategies ?? [];

  // 3. Find the best vault by yield
  const bestVault = strategies
    .filter((s: any) => s.id !== currentPosition.strategy)
    .sort((a: any, b: any) => b.apy - a.apy)[0];

  if (!bestVault) {
    console.log("No alternative vaults available");
    return;
  }

  const improvement = bestVault.apy - currentPosition.apy;
  if (improvement < MIN_YIELD_IMPROVEMENT) {
    console.log(
      `Best alternative: ${bestVault.apy}% APY (+${improvement.toFixed(2)}%) — below threshold`
    );
    return;
  }

  console.log(
    `Rebalancing: ${currentPosition.apy}% → ${bestVault.apy}% APY (+${improvement.toFixed(2)}%)`
  );

  // 4. Unlock from current vault
  const unlockCalls = await buildUnlockCalls(
    account,
    currentPosition.amount,
    currentPosition.asset
  );
  const unlockTx = await executeCalls(unlockCalls, signer);
  console.log(`Unlocked from ${currentPosition.strategy}: ${unlockTx}`);

  // 5. Lock into new vault with earn param
  const lockCalls = await buildLockCalls(
    account,
    currentPosition.amount,
    currentPosition.asset,
    bestVault.id
  );
  const lockTx = await executeCalls(lockCalls, signer);
  console.log(`Locked into ${bestVault.id}: ${lockTx}`);
}

// Run the optimizer loop
setInterval(() => {
  const currentPosition: VaultPosition = {
    strategy: "CURRENT_STRATEGY_ID",
    amount: "1000000000000000000",
    asset: "0xCOLLATERAL_TOKEN",
    apy: 4.2,
  };
  optimizeYield("0xUSER_ADDRESS", currentPosition, signer).catch(console.error);
}, POLL_INTERVAL_MS);
```

## Configuration

<AccordionGroup>
  <Accordion title="Poll Interval" icon="clock" defaultOpen={true}>
    Default: every 1 hour. Vault yields change slowly, so frequent polling isn't necessary. Hourly checks balance responsiveness with API usage.
  </Accordion>

  <Accordion title="Health Factor Threshold" icon="heart-pulse">
    Default: 1.5. Higher than the health monitor threshold (1.3) because rebalancing temporarily changes collateral composition. The extra buffer prevents accidental liquidation during the unwrap/wrap window.
  </Accordion>

  <Accordion title="Minimum Yield Improvement" icon="arrow-up-right-dots">
    Default: 0.5% APY. The agent only rebalances if the improvement exceeds this threshold. This prevents excessive on-chain transactions for marginal gains — each rebalance costs gas.
  </Accordion>
</AccordionGroup>

## Supported Vaults

The agent can rebalance between any earn strategies returned by the protocol config:

```bash theme={null}
curl -X GET https://api.sprinter.tech/credit/protocol
```

The `strategies` field in the response contains available earn vaults with their IDs — use these as the `earn` param when calling `/lock`.

See [Risk Management](/sprinter-credit/risk-management) for the full collateral tier table and LTV details per vault type.

## Related

<CardGroup cols={3}>
  <Card title="Credit Engine" icon="gear" href="/sprinter-credit/credit-engine">
    How collateral value and LTVs affect your credit line.
  </Card>

  <Card title="Health Monitor" icon="heart-pulse" href="/quickstart/agent-skills/health-monitor">
    Pair with the health monitor to protect positions during rebalancing.
  </Card>

  <Card title="Credit API Reference" icon="bolt" href="/api-reference/sprinter/credit/wrap-asset-into-earn-vault">
    Earn vault wrap/unwrap endpoints with interactive playground.
  </Card>
</CardGroup>
