Compressors
Compressor contracts aggregate on-chain data efficiently. Instead of dozens of individual reads, a single compressor call returns complete protocol state.
Discovering Compressor Addresses
Use AddressProvider to find compressor addresses:
Solidity Usage
[address compressor] = addressProvider.getLatestAddressByContractType( AP_MARKET_COMPRESSOR, VERSION_RANGE_310 );
TypeScript Usage
import { AP_MARKET_COMPRESSOR, VERSION_RANGE_310 } from '@gearbox-protocol/sdk'; const [compressor] = sdk.addressProvider.mustGetLatest( AP_MARKET_COMPRESSOR, VERSION_RANGE_310 );
MarketCompressor
The primary data aggregation contract. Returns complete market state including pools, credit managers, and price oracles.
Interface
interface IMarketCompressor { function getMarkets(MarketFilter memory filter) external view returns (MarketData[] memory); function getMarketData(address pool) external view returns (MarketData memory); function getPoolState(address pool) external view returns (PoolState memory); }
MarketFilter
struct MarketFilter { address[] configurators; // Filter by Risk Curator addresses address[] pools; // Filter by specific pool addresses address underlying; // Filter by underlying token (e.g., USDC) }
Pass empty arrays and address(0) for no filtering.
Solidity Usage
import {IMarketCompressor} from "@gearbox-protocol/periphery-v3/contracts/interfaces/IMarketCompressor.sol"; import {MarketData, MarketFilter} from "@gearbox-protocol/periphery-v3/contracts/types/MarketData.sol"; IMarketCompressor compressor = IMarketCompressor(MARKET_COMPRESSOR_ADDRESS); // Get single market MarketData memory data = compressor.getMarketData(poolAddress); // Get all USDC markets MarketFilter memory filter = MarketFilter({ underlying: USDC, configurators: new address[](0), pools: new address[](0) }); MarketData[] memory usdcMarkets = compressor.getMarkets(filter);
TypeScript Usage
import { marketCompressorAbi } from '@gearbox-protocol/sdk'; // Get all markets const markets = await client.readContract({ address: compressorAddress, abi: marketCompressorAbi, functionName: 'getMarkets', args: [{ configurators: [], pools: [], underlying: '0x0000000000000000000000000000000000000000' }], }); // Get specific pool data const marketData = await client.readContract({ address: compressorAddress, abi: marketCompressorAbi, functionName: 'getMarketData', args: [poolAddress], });
MarketData Structure
struct MarketData { PoolState pool; // Pool state and rates QuotaKeeperState quotaKeeper; // Quota limits and rates CreditSuiteData[] creditManagers; // All CMs for this market PriceOracleState priceOracle; // Oracle configuration TokenData[] tokens; // Allowed collateral tokens }
Key fields in PoolState:
| Field | Type | Description |
|---|---|---|
baseParams.addr | address | Pool contract address |
availableLiquidity | uint256 | Borrowable liquidity |
dieselRate | uint256 | Share price (RAY) |
supplyRate | uint256 | Lender APY (RAY) |
baseInterestRate | uint256 | Borrower APR (RAY) |
totalAssets | uint256 | Total pool value |
Key fields in CreditSuiteData:
| Field | Type | Description |
|---|---|---|
creditManager | address | Credit Manager address |
creditFacade | address | Credit Facade address |
creditConfigurator | address | Configurator address |
debtLimits | DebtLimits | min/max debt per account |
collateralTokens | CollateralToken[] | Allowed tokens + LTs |
CreditAccountCompressor
Reads complete Credit Account state. Deployed at 0x4115…2394 on every supported chain (see Deployment Addresses).
Get the state of a single Credit Account
If you have a Credit Account address, one call returns everything: getCreditAccountData(creditAccount). No SDK setup and no Credit Manager address needed; the compressor resolves the Credit Manager itself.
The quickest way to try it is the Read Contract tab on Etherscan: paste the account address into getCreditAccountData and query.
The same call from code, with no dependencies beyond viem:
import { createPublicClient, http, parseAbi } from 'viem'; import { mainnet } from 'viem/chains'; const ACCOUNT_COMPRESSOR = '0x4115708Fc8fe6bB392De2e0C21c2C81dA2222394'; const abi = parseAbi([ 'struct TokenInfo { address token; uint256 mask; uint256 balance; uint256 quota; bool success; }', 'struct CreditAccountData { address creditAccount; address creditManager; address creditFacade; address underlying; address owner; uint40 expirationDate; uint256 enabledTokensMask; uint256 debt; uint256 accruedInterest; uint256 accruedFees; uint256 totalDebtUSD; uint256 totalValueUSD; uint256 twvUSD; uint256 totalValue; uint256 healthFactor; bool success; TokenInfo[] tokens; }', 'function getCreditAccountData(address creditAccount) view returns (CreditAccountData)', ]); const client = createPublicClient({ chain: mainnet, transport: http() }); const account = await client.readContract({ address: ACCOUNT_COMPRESSOR, abi, functionName: 'getCreditAccountData', args: ['0x...'], // Credit Account address }); console.log(account.healthFactor, account.debt, account.tokens);
CreditAccountData Structure
struct CreditAccountData { address creditAccount; address creditManager; address creditFacade; address underlying; address owner; uint40 expirationDate; uint256 enabledTokensMask; // Bitmask of enabled collateral tokens uint256 debt; // Principal, in underlying uint256 accruedInterest; // Accrued interest, in underlying uint256 accruedFees; // Accrued fees, in underlying uint256 totalDebtUSD; uint256 totalValueUSD; uint256 twvUSD; // Threshold-weighted value, USD uint256 totalValue; // In underlying uint256 healthFactor; // 10000 = 1.0; below 10000 is liquidatable bool success; // False if account state could not be computed TokenInfo[] tokens; } struct TokenInfo { address token; uint256 mask; uint256 balance; uint256 quota; bool success; }
Querying Multiple Accounts
For scanning accounts across Credit Managers (liquidation bots, monitoring), use getCreditAccounts with filters:
interface ICreditAccountCompressor { function getCreditAccountData(address creditAccount) external view returns (CreditAccountData memory); function getCreditAccounts( address creditManager, CreditAccountFilter memory caFilter, uint256 offset ) external view returns (CreditAccountData[] memory data, uint256 nextOffset); function getCreditAccounts( CreditManagerFilter memory cmFilter, CreditAccountFilter memory caFilter, uint256 offset ) external view returns (CreditAccountData[] memory data, uint256 nextOffset); function countCreditAccounts( address creditManager, CreditAccountFilter memory caFilter ) external view returns (uint256); }
Both getCreditAccounts variants also have overloads with an extra trailing uint256 limit parameter that caps the number of accounts processed per call.
Filters
struct CreditAccountFilter { address owner; // Filter by owner (address(0) = any) bool includeZeroDebt; // Include accounts with no debt uint256 minHealthFactor; // Minimum HF (0 = no min) uint256 maxHealthFactor; // Maximum HF (type(uint256).max = no max) bool reverting; // Only accounts whose state computation reverts } struct CreditManagerFilter { address[] configurators; // Filter by market configurators address[] creditManagers; // Filter by specific Credit Managers address[] pools; // Filter by pools address underlying; // Filter by underlying token (address(0) = any) }
TypeScript Usage
import { creditAccountCompressorAbi } from '@gearbox-protocol/sdk'; // Liquidatable accounts of one Credit Manager (HF < 1.0) const [data, nextOffset] = await client.readContract({ address: ACCOUNT_COMPRESSOR, abi: creditAccountCompressorAbi, functionName: 'getCreditAccounts', args: [ creditManagerAddress, { owner: '0x0000000000000000000000000000000000000000', includeZeroDebt: false, minHealthFactor: 0n, maxHealthFactor: 10000n, reverting: false, }, 0n, // offset ], });
Pagination
Querying functions process as many accounts as they can before approaching the gas limit. A non-zero nextOffset means the call stopped early; repeat the call starting from it:
let offset = 0n; const allAccounts = []; while (true) { const [data, nextOffset] = await client.readContract({ address: ACCOUNT_COMPRESSOR, abi: creditAccountCompressorAbi, functionName: 'getCreditAccounts', args: [creditManagerAddress, filter, offset], }); allAccounts.push(...data); if (nextOffset === 0n) break; offset = nextOffset; }
PriceFeedCompressor
Aggregates price feed state for oracle updates.
Interface
interface IPriceFeedCompressor { function getUpdatablePriceFeeds(address priceOracle) external view returns (PriceFeedData[] memory); function loadPriceFeedTree(address priceOracle, address token) external view returns (PriceFeedTreeNode memory); }
TypeScript Usage
// Get all feeds that need updating const feeds = await client.readContract({ address: priceFeedCompressor, abi: priceFeedCompressorAbi, functionName: 'getUpdatablePriceFeeds', args: [priceOracleAddress], }); // Filter for stale feeds const staleFeeds = feeds.filter(f => f.needsUpdate);
When to Use Compressors vs SDK
| Scenario | Approach |
|---|---|
| General market data | SDK marketRegister |
| Credit account queries | SDK services |
| One-off account lookup | Direct compressor call |
| Custom filtering logic | Direct compressor calls |
| Liquidation bots | Direct compressor (one call, many accounts) |
| Real-time monitoring | Direct compressor with specific filters |
The SDK uses compressors internally. Use direct compressor calls when you need:
- A quick single-account or single-market read without SDK setup
- Custom filter combinations not exposed by SDK
- Pagination control
Compressors are view contracts designed for off-chain eth_call reads. They are not gas-optimized, so avoid calling them from on-chain contracts.
Complete Example
TypeScript
import { createPublicClient, http } from 'viem'; import { mainnet } from 'viem/chains'; import { marketCompressorAbi, creditAccountCompressorAbi, } from '@gearbox-protocol/sdk'; const client = createPublicClient({ chain: mainnet, transport: http(), }); // Same on all chains, see /developers/deployments const MARKET_COMPRESSOR = '0x70F1753a765C4df582FFA3B8d96AB492714E8992'; const ACCOUNT_COMPRESSOR = '0x4115708Fc8fe6bB392De2e0C21c2C81dA2222394'; async function getMarketOverview() { const markets = await client.readContract({ address: MARKET_COMPRESSOR, abi: marketCompressorAbi, functionName: 'getMarkets', args: [{ configurators: [], pools: [], underlying: '0x0000000000000000000000000000000000000000' }], }); for (const market of markets) { console.log(`Pool: ${market.pool.baseParams.addr}`); console.log(` Available: ${market.pool.availableLiquidity}`); console.log(` Supply Rate: ${market.pool.supplyRate}`); } } async function findLiquidatableAccounts(creditManager: `0x${string}`) { const [accounts] = await client.readContract({ address: ACCOUNT_COMPRESSOR, abi: creditAccountCompressorAbi, functionName: 'getCreditAccounts', args: [ creditManager, { owner: '0x0000000000000000000000000000000000000000', includeZeroDebt: false, minHealthFactor: 0n, maxHealthFactor: 10000n, // HF < 1.0 reverting: false, }, 0n, ], }); return accounts; }
Solidity
function getMarketData(address pool) external view returns (MarketData memory) { return IMarketCompressor(MARKET_COMPRESSOR).getMarketData(pool); } function findLiquidatableAccounts(address creditManager) external view returns (CreditAccountData[] memory) { CreditAccountFilter memory filter = CreditAccountFilter({ owner: address(0), includeZeroDebt: false, minHealthFactor: 0, maxHealthFactor: 10000, reverting: false }); (CreditAccountData[] memory accounts,) = ICreditAccountCompressor(ACCOUNT_COMPRESSOR).getCreditAccounts( creditManager, filter, 0 ); return accounts; }