Skip to main content

User Position

The UserPosition account tracks a user’s collateral and debt for a specific pair.

Account Structure

#[account]
pub struct UserPosition {
    // User and pair info
    pub owner: Pubkey,
    pub pair: Pubkey,
    pub collateral0_applied_min_cf_bps: u16,
    pub collateral1_applied_min_cf_bps: u16,
    
    // Collateral tracking
    pub collateral0: u64,
    pub collateral1: u64,
    
    // Debt tracking (u128 for precision)
    pub debt0_shares: u128,
    pub debt1_shares: u128,

    // PDA bump
    pub bump: u8,
}

Fields

owner
Pubkey
The owner of this position.
pair
Pubkey
The pair this position belongs to.
collateral0_applied_min_cf_bps
u16
Applied minimum collateral factor (in BPS) for borrowing token1 using token0 as collateral.
collateral1_applied_min_cf_bps
u16
Applied minimum collateral factor (in BPS) for borrowing token0 using token1 as collateral.
collateral0
u64
Amount of token0 deposited as collateral.
collateral1
u64
Amount of token1 deposited as collateral.
debt0_shares
u128
Debt shares for token0. Used to calculate actual debt including interest. Stored as u128 for precision.
debt1_shares
u128
Debt shares for token1. Used to calculate actual debt including interest. Stored as u128 for precision.
bump
u8
PDA bump seed.

PDA Derivation

The user position account is a PDA derived from:
[POSITION_SEED_PREFIX, pair, owner]
Where POSITION_SEED_PREFIX = b"gamm_position".

Debt Shares Mechanism

Debt is tracked using a share-based system that allows interest to accrue globally without updating each user’s position individually.

How It Works

Instead of storing absolute debt amounts, users hold debt shares that represent their proportion of the pool’s total debt. As interest accrues, total_debt increases but shares remain constant, so each share represents progressively more debt.

Initial Scaling

The first borrow creates shares with a 10610^6 scaling factor for precision:
shares = amount × 1,000,000

Subsequent Borrows

New shares are calculated proportionally:
new_shares = ceil(amount × total_shares / total_debt)

Debt Calculation

User’s actual debt is calculated from their shares:
user_debt = ceil(user_shares × total_debt / total_shares)

Rounding Rules (Protocol-Favorable)

OperationRoundingEffect
BorrowingRound UP sharesUser gets slightly more debt
Debt calculationRound UPUser owes slightly more
RepaymentRound DOWN shares burnedProtocol retains dust
This ensures the protocol never loses value due to rounding.

Key Methods

calculate_debt0(total_debt0: u64, total_debt0_shares: u128) -> Result<u64>

Calculates the actual debt0 amount (including accrued interest) from debt shares using ceiling division.

calculate_debt1(total_debt1: u64, total_debt1_shares: u128) -> Result<u64>

Calculates the actual debt1 amount (including accrued interest) from debt shares using ceiling division.

get_remaining_borrow_limit(pair: &Pair, debt_token: &Pubkey, applied_min_cf_bps: u16) -> Result<u64>

Calculates the remaining borrow limit for a given debt token.

get_debt_utilization_bps(pair: &Pair, debt_token: &Pubkey) -> Result<u64>

Returns the debt utilization in basis points (debt / borrow power). Values > 10000 indicate undercollateralization.

get_liquidation_price(pair: &Pair, debt_token: &Pubkey) -> Result<u64>

Returns the NAD-scaled liquidation price of the collateral. Returns u64::MAX if position is immediately unsafe, or 0 if there’s no debt.

increase_debt(pair: &mut Pair, debt_token: &Pubkey, amount: u64) -> Result<()>

Increases debt by minting new debt shares proportionally.

decrease_debt(pair: &mut Pair, debt_token: &Pubkey, amount: u64) -> Result<()>

Decreases debt by burning debt shares proportionally.

writeoff_debt(pair: &mut Pair, debt_token: &Pubkey) -> Result<()>

Writes off all debt for a token (used in liquidations).