Skip to content

Releases: mrgnlabs/marginfi-v2

mrgn-0.1.8

21 Feb 01:13
4fbfaa3

Choose a tag to compare

mrgn-0.1.8 Pre-release
Pre-release

Summary

Orders

Introduces "Orders". An Order is a stop-loss and/or take-profit trigger that a "Keeper" can permissionlessly execute. For example, a user is lending $100 in SOL and borrowing $50 in BONK. They set a take-profit at $70. If SOL goes to $120, the Keeper can execute, closing their BONK position and leaving them with $70 in SOL. A Keeper can also execute if BONK falls to $30, likewise leaving the user with $70 in SOL.

Project Zero will run Keepers initially upon feature public launch (ETA Q2/Q3 2026), but any third-party can run a Keeper. Users configure their max slippage tolerance when setting the order, Keepers are permitted to keep whatever is leftover after completing the order execution as profit, and they also get to keep the rent from the Order (currently worth about $0.25). If you are an integrator expecting to use this feature, you are strongly recommended to run your own Keepers. Keepers are permissionless, any wallet can be a Keeper.

Juplend

Juplend banks can now be used as collateral! See JUPLEND_INTEGRATION.md for more details of how juplend banks work.

Other

"Rate Limiter" feature: Implements a sliding window rate limiter system at both bank and group levels to protect against rapid fund extraction. Limits are net outflow: outflows minus inflows, so deposits/repays offset usage. Banks enforce a daily limit in tokens, and the group (all banks that live on the 0.xyz app) enforces a USD limit. Flashloans and liquidations do not affect the window.

Substantial reductions in CU and heap usage, especially in receivership liquidation flows, enabling liquidators to burn more CU on other tasks, especially for accounts with more positions.

Breaking Changes (Rust Integrators only)

  • lending_account_deposit: group account is now writable (mut).
  • lending_account_repay: group account is now writable (mut); for repay_all = true there is new remaining-accounts validation requiring risk accounts for all active balances.
  • lending_account_borrow: group account is now writable (mut).
  • kamino_deposit, solend_deposit, drift_deposit: group account is now writable (mut).
  • lending_pool_pulse_bank_price_cache: group account is now writable (mut).

Breaking Changes (everyone)

  • lending_account_withdraw, kamino_withdraw, solend_withdraw, drift_withdraw: with withdraw_all = true, now requires the bank being withdrawn to be passed last in risk accounts (formerly omitted entirely).

Example

User has balances:

[0] kamino bank (0)
[1] some balance being closed upon withdraw_all (1)
[2] drift bank (2)
[3] some mrgn bank (3)

Before this update:

[bank0, oracle0, kamino_reserve0],
[bank2, oracle2, drift_spot_market2],
[bank3, oracle3],

After this update:

[bank0, oracle0, kamino_reserve0],
[bank2, oracle2, drift_spot_market2],
[bank3, oracle3],
[bank1, oracle1, ....other accounts if this is kamino/drift/jup],

Noting that the bank to closed does NOT respect the all-accounts-must-be-ordered-by-bank rule: it is always passed last.

New Accounts

  • Order - tracks information about a single take-profit and/or stop-loss order for an asset/liability pair on the user's account.
  • ExecuteOrderRecord - an ephemeral account that is always closed in the same TX it is opened in, used to pass information between the start and end of order execution. None of these should exist in production.

New Instructions

Orders

  • PlaceOrder (user) - Place a new Stop Loss, Take Profit, or Both type Order on a pair of balances the user currently holds.
  • StartExecuteOrder (Keeper) - Keepers run this to begin the execution of an Order. Must be at the start of the tx, and EndExecuteOrder must appear last in the tx. Withdraw/Repay of the involved balances typically follows this ix. Requires a risk check of just the balances involved in the Order.
  • EndExecuteOrder (Keeper) - Must be the last tx in executing an Order. Requires a risk check of just the balances involved in the Order.
  • CloseOrder (user) - Clear an unwanted Order, user gets their rent back.
  • SetKeeperCloseFlags (user) - Enables the Keeper to close Orders via KeeperCloserOrder, typically use CloseOrder instead.
  • KeeperCloseOrder (Keeper) - Close an Order on an account where neither of the original positions exists or all the tags have been cleared by the user

Juplend

  • lendingPoolAddBank_juplend (admin) - Adds a wrapped JupLend bank to a group. The bank starts
    in Paused state and is unusable until juplend_init_position runs.
  • juplendInitPosition (permissionless, one-time per bank) - Performs a seed deposit into JupLend
    and flips the bank from Paused to Operational. This activates the bank for user flows.
  • juplendDeposit(amount) (user) - Deposit underlying tokens (native amount). Internally calls
    JupLend updateRate, deposits through CPI, verifies minted fTokens, and credits the user's
    marginfi position.
  • juplendWithdraw(amount, withdraw_all) (user) - Withdraw underlying tokens (native amount).
    Internally calls JupLend updateRate, burns fTokens via CPI, then transfers tokens from the
    withdraw intermediary ATA to the user's destination token account.

Other

  • configureBankRateLimits (admin) - Configure hourly/daily bank-level outflow limits for withdraw/borrow (in native token units).
  • configureGroupRateLimits (admin) - Configure hourly/daily group-level aggregate outflow limits (in USD). Same net-outflow model, applied across the group.

Changes to Existing Instructions

Admin-only

  • marginfi_group_configure: admin key args changed from required Pubkey to Option<Pubkey> (new_admin, new_emode_admin, new_curve_admin, new_limit_admin, new_emissions_admin, new_metadata_admin, new_risk_admin), where None now means "leave unchanged".
  • init_global_fee_state and edit_global_fee_state: added args order_init_flat_sol_fee and order_execution_max_fee.
  • lending_pool_update_emissions_parameters: Fixed a bug where the emissions admin could update other flags.

User

  • lending_account_deposit: group account is now writable (mut).
  • lending_account_repay: group account is now writable (mut); for repay_all = true there is new remaining-accounts validation requiring risk accounts for all active balances.
  • lending_account_borrow: group account is now writable (mut).
  • kamino_deposit, solend_deposit, drift_deposit: group account is now writable (mut).
  • lending_pool_pulse_bank_price_cache: group account is now writable (mut).
  • lending_account_withdraw, kamino_withdraw, solend_withdraw, drift_withdraw: with withdraw_all = true, now requires the bank being withdrawn to be passed last in risk accounts (formerly omitted entirely).
  • lending_account_repay, lending_account_withdraw, kamino_withdraw, drift_withdraw now allowed during order-execution context.

Liquidator

  • start_liquidation instruction gating was extended: allowed pre-actions now include Juplend updateRate; allowed body instructions now include JUPLEND_WITHDRAW.
  • start_liquidation, start_deleverage, end_liquidation, end_deleverage, lending_account_start_flashloan, and lending_account_end_flashloan cannot run during Order execution.

Changes to Existing Accounts

  • FeeState
    • Added order_init_flat_sol_fee: u32 - a fixed fee paid to open Orders
    • Added order_execution_max_fee: WrappedI80F48 - Max profit Keepers can earn
  • MarginfiGroup:
    • Added rate_limiter (see GroupRateLimiter) - used to enforce global withdrawal windows
  • Bank (existing account):
    • Added rate_limiter (see BankRateLimiter) - used to enforce per-bank withdrawal windows
    • BankCache (in Bank) - various read-only fields used in receivership liquidation
      * liq_cache_flags: u8
      * liquidation_price_rt: WrappedI80F48
      * liquidation_price_rt_confidence: WrappedI80F48
      * liquidation_price_twap: WrappedI80F48
      * liquidation_price_twap_confidence: WrappedI80F48
  • MarginfiAccount
    • Added flag bit ACCOUNT_IN_ORDER_EXECUTION = 1 << 7.
    • LendingAccount (in MarginfiAccount):
      • Added last_tag_used: u16 - Enables Balances to have a unique tag for Orders
    • Balance (in LendingAccount):
      • Added tag: u16 - used for orders, not to be confused with bank_asset_tag
  • OracleSetup (used in Bank config), added variants:
    • FixedKamino = 13
    • FixedDrift = 14
    • JuplendPythPull = 15
    • JuplendSwitchboardPull = 16
    • FixedJuplend = 17

Other Information

Consolidates

Minor bugfixes

  • Fixes an issue where the test-program-remix script corrupts metadata and requires an anchor clean when the program is updated, however the tests now build to a different target directory, which increases disk usage.
  • Partially fixes a BlockhashNotFound some longer Rust tests occasionally run into.

Credits

Thank you to https://github.com/abelmarnk for contributing most of the work to the Orders feature.

Audit Information

TBD

Release information

Staging (stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct) - Approximately Feb 18, 2026
Mainnet - ETA late March 2026

mrgn - 0.1.7a

27 Jan 20:06
8100d13

Choose a tag to compare

mrgn - 0.1.7a Pre-release
Pre-release

Changes since 0.1.7-rc3

  • Fixes a bug where Drift refresh instructions were forbidden during receivership liquidation

Deployed Mainnet: Jan 27 @ ~ 6pm ET - hash 59e065

mrgn - 0.1.7-rc3

26 Jan 18:43

Choose a tag to compare

Summary

Adds Drift and Solend support for P0, enabling Drift and Solend (aka "Save") positions to be used as collateral for borrowing on P0!

Removes deprecated Arena features.

Reduce-only banks no longer count towards borrowing power. Existing account health is not affected, only new borrows.

Adds compliance functionality for AML or similar requests, enabling the administrator to freeze accounts if e.g. served with a court order demanding it. Our foundation is committed not to freeze any account unless legally ordered to do.

Terminology Notes

Drift calls their Bank-equivalent "Markets" and Solend calls them "Reserves". A "Bank" on P0 tracks a single Drift Market or Solend Reserve, much like Kamino banks track a single Kamino Reserve.

Changes since rc1

Changed the way integration accounts are stored on the Bank (see changes to Bank below)

Changes since rc2

  • Fixed a bug where the price cache wasn't being updated for Drift/Solend banks
  • Minor doc updates, updated terms of security policy
  • Minor logging improvements

Breaking Changes

Liquidators and other parties that index all banks must now accommodate for Drift and Solend banks. To detect if a Bank uses a Kamino, Drift, or Solend integration, check the config.asset_tag, and then read integration_acc_1/2/3 to get the associated Reserve, Obligation, etc as needed.

If you were using the kamino_reserve or kamino_obligation fields, note that the names have changed (see Updated Fields, but they are in the same location in the buffer.

Instructions

New Instructions (users)

  • drift_deposit - deposit collateral to a drift bank to be used as collateral on P0.
  • drift_withdraw - withdraw collateral from a drift position
  • solend_deposit - - deposit collateral to a solend bank to be used as collateral on P0.
  • solend_withdraw - withdraw collateral from a solend position
  • (permissionless) lending_pool_pulse_bank_price_cache - refresh a bank's price cache, enabling read-only viewing of the price currently observed by the bank from its oracle (note: all instructions that consume prices now also update the same cached value).

New Instructions (admin only)

  • (admin) lending_pool_add_bank_drift - create a new drift bank
  • (permissionless) drift_init_user - called after lending_pool_add_bank_drift to complete Drift bank configuration.
  • (permissionless) drift_harvest_reward - shifts earned Drift rewards into the admin's global fee wallet to be distributed to depositors OTC
  • (admin) lending_pool_add_bank_solend - create a new solend bank
  • solend_init_obligation - called after lending_pool_add_bank_solend to complete Solend bank configuration.
  • (admin) set_account_freeze - toggle freeze on/off. Frozen accounts are unable to perform any action that modifies their balances. The admin will use this to comply with court-ordered legal obligations e.g. AML.

Changes to Existing Instructions (users)

None

Changes to Existing Instructions (admin only)

  • initialize_group - removed is_arena_group arg
  • configure - removed is_arena_group arg, added emode_max_init_leverage and emode_max_maint_leverage.

Updated Fields

MarginfiGroup

  • emode_max_init_leverage/emode_max_maint_leverage - controls the maximum allowed leverage used in emode for all banks in this group. Used to ensure even the emode admin is unable to accidentally set "infinite" or "under-collateralized" leverage.

Bank

  • integration_acc_1 (formerly kamino_reserve) - For Drift banks, the associated Market. For Solend banks, the associated Reserve. For Kamino banks, also the associated Reserve. For all other bank types, does nothing.
  • integration_acc_2 (formerly kamino_obligation) - For Drift banks, the associated user account, which all depositors into this bank share. For Solend banks, the associated Obligation, which all depositors into this bank share. For Kamino banks, also the associated Obligation. For all other bank types, does nothing.
  • integration_acc_3 - for Drift banks, the associated user stats, which all depositors into this bank share. For all other bank types, does nothing.

Bank.cache

For any instruction which consumes a price, the bank caches now stores that last-used price for better read-only insight into the price used by a Bank. If a Bank's timestamp is sufficiently recent, lazy consumers can use these values of lieu of fetching an actual price, but should not treat it as authoritative once the Bank's last updated timestamp is sufficiently old.

  • last_oracle_price
  • last_oracle_price_timestamp
  • last_oracle_price_confidence

MarginfiAccount

  • account_flags - added ACCOUNT_FROZEN option (1 << 6)

Notes on Drift Rewards

Drift rewards paid in the same token (e.g. USDS rewards for the USDS Market) are automatically credited to depositors at the program level, no special claim instruction is required. Drift rewards paid in a different token (E.g. JTO incentives for the SOL Market), as well as special rewards which Drift itself might "admin deposit" directly to accounts as part of various campaigns, are sent to the administrator's fee wallet, and will be distributed to users like other incentives (typically via weekly airdrop). We will announce how FUEL incentives will be handled at a later date.

Consolidates

Audits

See attached

Release Times

Staging - ~Jan 20
Mainnet - Jan 27 @ 11AM ET

mrgn - 0.1.7-rc2

20 Jan 15:36
4a2768b

Choose a tag to compare

mrgn - 0.1.7-rc2 Pre-release
Pre-release

Summary

Adds Drift and Solend support for P0, enabling Drift and Solend (aka "Save") positions to be used as collateral for borrowing on P0!

Removes deprecated Arena features.

Reduce-only banks no longer count towards borrowing power. Existing account health is not affected, only new borrows.

Adds compliance functionality for AML or similar requests, enabling the administrator to freeze accounts if e.g. served with a court order demanding it. Our foundation is committed not to freeze any account unless legally ordered to do.

Terminology Notes

Drift calls their Bank-equivalent "Markets" and Solend calls them "Reserves". A "Bank" on P0 tracks a single Drift Market or Solend Reserve, much like Kamino banks track a single Kamino Reserve.

Changes since rc1

Changed the way integration accounts are stored on the Bank (see changes to Bank below)

Breaking Changes

Liquidators and other parties that index all banks must now accommodate for Drift and Solend banks. To detect if a Bank uses a Kamino, Drift, or Solend integration, check the config.asset_tag, and then read integration_acc_1/2/3 to get the associated Reserve, Obligation, etc as needed.

If you were using the kamino_reserve or kamino_obligation fields, note that the names have changed (see Updated Fields, but they are in the same location in the buffer.

Instructions

New Instructions (users)

  • drift_deposit - deposit collateral to a drift bank to be used as collateral on P0.
  • drift_withdraw - withdraw collateral from a drift position
  • solend_deposit - - deposit collateral to a solend bank to be used as collateral on P0.
  • solend_withdraw - withdraw collateral from a solend position
  • (permissionless) lending_pool_pulse_bank_price_cache - refresh a bank's price cache, enabling read-only viewing of the price currently observed by the bank from its oracle (note: all instructions that consume prices now also update the same cached value).

New Instructions (admin only)

  • (admin) lending_pool_add_bank_drift - create a new drift bank
  • (permissionless) drift_init_user - called after lending_pool_add_bank_drift to complete Drift bank configuration.
  • (permissionless) drift_harvest_reward - shifts earned Drift rewards into the admin's global fee wallet to be distributed to depositors OTC
  • (admin) lending_pool_add_bank_solend - create a new solend bank
  • solend_init_obligation - called after lending_pool_add_bank_solend to complete Solend bank configuration.
  • (admin) set_account_freeze - toggle freeze on/off. Frozen accounts are unable to perform any action that modifies their balances. The admin will use this to comply with court-ordered legal obligations e.g. AML.

Changes to Existing Instructions (users)

None

Changes to Existing Instructions (admin only)

  • initialize_group - removed is_arena_group arg
  • configure - removed is_arena_group arg, added emode_max_init_leverage and emode_max_maint_leverage.

Updated Fields

MarginfiGroup

  • emode_max_init_leverage/emode_max_maint_leverage - controls the maximum allowed leverage used in emode for all banks in this group. Used to ensure even the emode admin is unable to accidentally set "infinite" or "under-collateralized" leverage.

Bank

  • integration_acc_1 (formerly kamino_reserve) - For Drift banks, the associated Market. For Solend banks, the associated Reserve. For Kamino banks, also the associated Reserve. For all other bank types, does nothing.
  • integration_acc_2 (formerly kamino_obligation) - For Drift banks, the associated user account, which all depositors into this bank share. For Solend banks, the associated Obligation, which all depositors into this bank share. For Kamino banks, also the associated Obligation. For all other bank types, does nothing.
  • integration_acc_3 - for Drift banks, the associated user stats, which all depositors into this bank share. For all other bank types, does nothing.

Bank.cache

For any instruction which consumes a price, the bank caches now stores that last-used price for better read-only insight into the price used by a Bank. If a Bank's timestamp is sufficiently recent, lazy consumers can use these values of lieu of fetching an actual price, but should not treat it as authoritative once the Bank's last updated timestamp is sufficiently old.

  • last_oracle_price
  • last_oracle_price_timestamp
  • last_oracle_price_confidence

MarginfiAccount

  • account_flags - added ACCOUNT_FROZEN option (1 << 6)

Notes on Drift Rewards

Drift rewards paid in the same token (e.g. USDS rewards for the USDS Market) are automatically credited to depositors at the program level, no special claim instruction is required. Drift rewards paid in a different token (E.g. JTO incentives for the SOL Market), as well as special rewards which Drift itself might "admin deposit" directly to accounts as part of various campaigns, are sent to the administrator's fee wallet, and will be distributed to users like other incentives (typically via weekly airdrop). We will announce how FUEL incentives will be handled at a later date.

Consolidates

Audits

TBD

Release Times

Staging - TBD
Mainnet - TBD (ETA ~ January 23)

mrgn - 0.1.7-rc1

17 Jan 02:37
72eb6a3

Choose a tag to compare

mrgn - 0.1.7-rc1 Pre-release
Pre-release

Summary

Adds Drift and Solend support for P0, enabling Drift and Solend (aka "Save") positions to be used as collateral for borrowing on P0!

Removes deprecated Arena features.

Reduce-only banks no longer count towards borrowing power. Existing account health is not affected, only new borrows.

Adds compliance functionality for AML or similar requests, enabling the administrator to freeze accounts if e.g. served with a court order demanding it. Our foundation is committed not to freeze any account unless legally ordered to do.

Terminology Notes

Drift calls their Bank-equivalent "Markets" and Solend calls them "Reserves". A "Bank" on P0 tracks a single Drift Market or Solend Reserve, much like Kamino banks track a single Kamino Reserve.

Breaking Changes

Liquidators and other parties that index all banks must now accommodate for Drift and Solend banks.

Instructions

New Instructions (users)

  • drift_deposit - deposit collateral to a drift bank to be used as collateral on P0.
  • drift_withdraw - withdraw collateral from a drift position
  • solend_deposit - - deposit collateral to a solend bank to be used as collateral on P0.
  • solend_withdraw - withdraw collateral from a solend position
  • (permissionless) lending_pool_pulse_bank_price_cache - refresh a bank's price cache, enabling read-only viewing of the price currently observed by the bank from its oracle (note: all instructions that consume prices now also update the same cached value).

New Instructions (admin only)

  • (admin) lending_pool_add_bank_drift - create a new drift bank
  • (permissionless) drift_init_user - called after lending_pool_add_bank_drift to complete Drift bank configuration.
  • (permissionless) drift_harvest_reward - shifts earned Drift rewards into the admin's global fee wallet to be distributed to depositors OTC
  • (admin) lending_pool_add_bank_solend - create a new solend bank
  • solend_init_obligation - called after lending_pool_add_bank_solend to complete Solend bank configuration.
  • (admin) set_account_freeze - toggle freeze on/off. Frozen accounts are unable to perform any action that modifies their balances. The admin will use this to comply with court-ordered legal obligations e.g. AML.

Changes to Existing Instructions (users)

None

Changes to Existing Instructions (admin only)

  • initialize_group - removed is_arena_group arg
  • configure - removed is_arena_group arg, added emode_max_init_leverage and emode_max_maint_leverage.

Updated Fields

MarginfiGroup

  • emode_max_init_leverage/emode_max_maint_leverage - controls the maximum allowed leverage used in emode for all banks in this group. Used to ensure even the emode admin is unable to accidentally set "infinite" or "under-collateralized" leverage.

Bank

  • drift_spot_market - for Drift banks, the associated Market. For all other bank types, does nothing.
  • drift_user - for Drift banks, the associated user account, which all depositors into this bank share. For all other bank types, does nothing.
  • drift_user_stats - for Drift banks, the associated user stats, which all depositors into this bank share. For all other bank types, does nothing.
  • solend_reserve - for Solend banks, the associated Reserve. For all other bank types, does nothing.
  • solend_obligation - for Solend banks, the associated Obligation, which all depositors into this bank share. For all other bank types, does nothing.

Bank.cache

For any instruction which consumes a price, the bank caches now stores that last-used price for better read-only insight into the price used by a Bank. If a Bank's timestamp is sufficiently recent, lazy consumers can use these values of lieu of fetching an actual price, but should not treat it as authoritative once the Bank's last updated timestamp is sufficiently old.

  • last_oracle_price
  • last_oracle_price_timestamp
  • last_oracle_price_confidence

MarginfiAccount

  • account_flags - added ACCOUNT_FROZEN option (1 << 6)

Notes on Drift Rewards

Drift rewards paid in the same token (e.g. USDS rewards for the USDS Market) are automatically credited to depositors at the program level, no special claim instruction is required. Drift rewards paid in a different token (E.g. JTO incentives for the SOL Market), as well as special rewards which Drift itself might "admin deposit" directly to accounts as part of various campaigns, are sent to the administrator's fee wallet, and will be distributed to users like other incentives (typically via weekly airdrop). We will announce how FUEL incentives will be handled at a later date.

Consolidates

Audits

TBD

Release Times

Staging - TBD
Mainnet - TBD (ETA ~ January 23)

mrgn-0.1.6a-hotfix

18 Dec 18:50

Choose a tag to compare

  • Restores support for jup and Titan swaps during receivership liquidation.

For full patches notes, see mrgn-0.1.6-rc3

mrgn - 0.1.6-rc3

17 Dec 19:45
944a9c5

Choose a tag to compare

Summary

Implementation of a seven-point curve structure that enables deeper control of interest rates at various utilization levels. Deprecates the simple three-point plateau rate/max rate model. The new curve approach allows the admin to set up to 5 points (plus the points at util = 0 and util = 100). Each point corresponding to a utilization and base interest rate. Points must be in ascending order (util increasing, rates increasing or equal to previous). To determine what the rate is at a given utilization, we linearly interpolate between the nearest two points.

Adds a fixed-price oracle type. Banks using this type have a declared price that can be read at bank.config.fixed_price. They use no external oracle, the price is completely set by the group administrator and never goes stale. This is useful for e.g. stablecoins, assets that are worthless and/or being sunset, or assets that are extremely stable in price for some other reason.

Risk management gets a major upgrade: banks that are defunct and being sunset can now be deleveraged and unwound. Deleverage is essentially a liquidation of a healthy account, but for no profit in dollar terms. We expect that banks at risk of deleveraging will get a LOT of warning (looking at you, UXD). The operation is admin-only.

Bank Metadata: an on-chain account where you can read the plain-English token name and description of a corresponding bank.

Big readme rewrites! More guides!

Bug Fixes

  • Kamino refresh instructions are now allowed before start_liquidation in receivership liquidations.

New Accounts

  • BankMetadata - 1:1 with Banks, contains the utf-8 encoded token name and a brief description. No more running to solscan to figure out what token a bank is using! A pda with one non-static seed: the bank.

New/Changed Fields

  • Banks now have:
    • zero_util_rate - The base rate at utilization = 0. A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • hundred_util_rate - The base rate at full utilization (100%). A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • points - The base rate at various points between 0 and 100% utilization, exclusive. Exactly five points large. Unused points have util = 0. Util is a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX. Rate is a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX.
    • curve_type - For all newly created banks, INTEREST_CURVE_SEVEN_POINT (1). For all banks created prior to this update, INTEREST_CURVE_LEGACY (0). After migration is completed, all banks will have INTEREST_CURVE_SEVEN_POINT.
    • OracleSetup has a new enum value: Fixed
    • bank.config.fixed_price: A WrappedI80F48, storing the declared price for Fixed banks, does nothing for banks for other OracleSetups. Note: a bank can swap from Fixed to another OracleSetup type. If it does so, this field might be populated with a junk value.
  • Groups now have:
    • deleverage_withdraw_window_cache - Limits how much the risk_admin can deleverage in a day, in the even the admin is compromised
    • risk_admin - can perform bankruptcies and deleverages
    • metadata_admin - can update bank metadata

Breaking Changes (admin instructions)

  • All config instructions affecting interest (add_pool, add_pool_with_seed, configure_bank, configure_bank_interest_only) now take fundamentally different arguments.

Breaking Changes (Everyone)

  • All instructions that require risk checks (borrow, liquidate, withdraw, etc) must now accommodate fixed-price banks. For these banks, pass just the bank in remaining_accounts, no oracle is required. See load_observation_account_metas for a Rust example. For Typescript, assuming you have a list of banks and loaded bank accounts:
   for (let i = 0; i < bankAccs.length; i++) {
     const setup = bankAccs[i].account.config.oracleSetup;
     const keys = bankAccs[i].account.config.oracleKeys;
     if ("fixed" in setup) {
       remainingAccounts.push([banks[i]]);
     } else if ("stakedWithPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1], keys[2]]);
     } else if ("kaminoPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else if ("kaminoSwitchboardPull" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else {
       remainingAccounts.push([banks[i], keys[0]]);
     }
   }

Breaking Changes (Rust integrators only)

  • in the withdraw and kamino_withdraw instructions, group is now mutable

Breaking Changes (Liquidators)

  • The classic liquidate instruction has two new arguments, now requiring the caller to declare the length of the liquidator's and liquidatee's remaining accounts slice. In normal usage, this simply looks like:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            assetOracle,
            liabOracle,
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })
  • If any asset or liability is fixed, note that you do not have to pass that oracle (see fixed oracle tests in m01/m02 for examples), e.g.:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            // no asset oracle if fixed
            // no liab oracle if fixed
            // liquidator/ee accounts also respect the fixed-oracle rules above.
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })

New/Removed instructions

  • migrate_curve (permissionless) - convert a bank from the legacy curve setup to the new seven-point approach. Does not affect bank operations in any way, the bank is immediately able to function as normal.
  • set_fixed_oracle_price (admin only) - sets a price for the bank, like a permanent oracle.
  • start/end_deleverage (risk admin only) - a permissioned liquidation: used to unwind banks that are being sunset. Functions almost identically to receivership liquidation, except that it earns no profit and can be used even on healthy accounts.
  • init_bank_metadata (permissionless) - pay the rent for a bank's metadata
  • write_bank_metadata (metadata admin only) - write token name/description to a bank's metadata
  • clone_emode (group admin or emode admin) - copy one bank's emode settings to another
  • purge_delev_balance (risk admin only) - when a bank has been fully deleveraged, and is completely out of tokens to withdraw, closes a balance without repaying it
  • configure_withdrawal_limit (admin only) - configures the amount that can be deleveraged in a given day
  • admin_super_withdraw (removed) - arena has been sunset, and this is no longer needed.
  • sort_balances (removed) - all accounts are now sorted, at all times, and this is no longer needed.

Additional Notes on Fixed Oracle Banks

  • The oracle_keys[0] slot of Fixed oracle banks is always pubkey default. Other oracle_keys[] fields might still be populated, e.g. the lst mint and sol pool for staked banks, or the reserve for the Kamino banks, this is to simplify switching these banks back into a normal oracle operational mode if needed.
  • Staked collateral banks can't take a fixed price. Kamino banks CAN (and so can future integrator banks).

Additional Notes on new Curves

  • add_bank_permissionless, and add_pool (kamino) now create different placeholders (using the new curve system). Staked and Kamino banks created prior to this update should still be migrated so they don't die when "accruing interest" (even though it doesn't do anything for them, since they never earn interest).
  • Banks on the legacy approach will continue to function as normal before/after migration. The legacy approach will cease to function in 1.7.

Samples

See the referenced functions for more details on how to convert to/from a float to a u32 representation or to a 5-length points slice.

Sample points creation (Rust):

points: make_points(&vec![RatePoint::new(
    p100_to_u32(I80F48!(0.9)),
    p1000_to_u32(I80F48!(1)),
)]),

Sample points creation (TS):

points: makeRatePoints([0.5], [0.6])

Audit Notes

  • See attached audit from Sec3

Consolidates:

Changes since RC1

  • CU usage optimizations
  • Minor audit fixes
  • Added clone_emode ix

Deploy Information

Hash - 65e54c
Staging - mid-November
Mainnet - 11am ET, Thursday, December 18, 2025

mrgn-0.1.6-rc2

01 Dec 17:37
22bc9f7

Choose a tag to compare

mrgn-0.1.6-rc2 Pre-release
Pre-release

Summary

Implementation of a seven-point curve structure that enables deeper control of interest rates at various utilization levels. Deprecates the simple three-point plateau rate/max rate model. The new curve approach allows the admin to set up to 5 points (plus the points at util = 0 and util = 100). Each point corresponding to a utilization and base interest rate. Points must be in ascending order (util increasing, rates increasing or equal to previous). To determine what the rate is at a given utilization, we linearly interpolate between the nearest two points.

Adds a fixed-price oracle type. Banks using this type have a declared price that can be read at bank.config.fixed_price. They use no external oracle, the price is completely set by the group administrator and never goes stale. This is useful for e.g. stablecoins, assets that are worthless and/or being sunset, or assets that are extremely stable in price for some other reason.

Risk management gets a major upgrade: banks that are defunct and being sunset can now be deleveraged and unwound. Deleverage is essentially a liquidation of a healthy account, but for no profit in dollar terms. We expect that banks at risk of deleveraging will get a LOT of warning (looking at you, UXD). The operation is admin-only.

Bank Metadata: an on-chain account where you can read the plain-English token name and description of a corresponding bank.

Big readme rewrites! More guides!

Bug Fixes

  • Kamino refresh instructions are now allowed before start_liquidation in receivership liquidations.

New Accounts

  • BankMetadata - 1:1 with Banks, contains the utf-8 encoded token name and a brief description. No more running to solscan to figure out what token a bank is using! A pda with one non-static seed: the bank.

New/Changed Fields

  • Banks now have:
    • zero_util_rate - The base rate at utilization = 0. A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • hundred_util_rate - The base rate at full utilization (100%). A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • points - The base rate at various points between 0 and 100% utilization, exclusive. Exactly five points large. Unused points have util = 0. Util is a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX. Rate is a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX.
    • curve_type - For all newly created banks, INTEREST_CURVE_SEVEN_POINT (1). For all banks created prior to this update, INTEREST_CURVE_LEGACY (0). After migration is completed, all banks will have INTEREST_CURVE_SEVEN_POINT.
    • OracleSetup has a new enum value: Fixed
    • bank.config.fixed_price: A WrappedI80F48, storing the declared price for Fixed banks, does nothing for banks for other OracleSetups. Note: a bank can swap from Fixed to another OracleSetup type. If it does so, this field might be populated with a junk value.
  • Groups now have:
    • deleverage_withdraw_window_cache - Limits how much the risk_admin can deleverage in a day, in the even the admin is compromised
    • risk_admin - can perform bankruptcies and deleverages
    • metadata_admin - can update bank metadata

Breaking Changes (admin instructions)

  • All config instructions affecting interest (add_pool, add_pool_with_seed, configure_bank, configure_bank_interest_only) now take fundamentally different arguments.

Breaking Changes (Everyone)

  • All instructions that require risk checks (borrow, liquidate, withdraw, etc) must now accommodate fixed-price banks. For these banks, pass just the bank in remaining_accounts, no oracle is required. See load_observation_account_metas for a Rust example. For Typescript, assuming you have a list of banks and loaded bank accounts:
   for (let i = 0; i < bankAccs.length; i++) {
     const setup = bankAccs[i].account.config.oracleSetup;
     const keys = bankAccs[i].account.config.oracleKeys;
     if ("fixed" in setup) {
       remainingAccounts.push([banks[i]]);
     } else if ("stakedWithPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1], keys[2]]);
     } else if ("kaminoPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else if ("kaminoSwitchboardPull" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else {
       remainingAccounts.push([banks[i], keys[0]]);
     }
   }

Breaking Changes (Liquidators)

  • The classic liquidate instruction has two new arguments, now requiring the caller to declare the length of the liquidator's and liquidatee's remaining accounts slice. In normal usage, this simply looks like:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            assetOracle,
            liabOracle,
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })
  • If any asset or liability is fixed, note that you do not have to pass that oracle (see fixed oracle tests in m01/m02 for examples), e.g.:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            // no asset oracle if fixed
            // no liab oracle if fixed
            // liquidator/ee accounts also respect the fixed-oracle rules above.
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })

New/Removed instructions

  • migrate_curve (permissionless) - convert a bank from the legacy curve setup to the new seven-point approach. Does not affect bank operations in any way, the bank is immediately able to function as normal.
  • set_fixed_oracle_price (admin only) - sets a price for the bank, like a permanent oracle.
  • start/end_deleverage (risk admin only) - a permissioned liquidation: used to unwind banks that are being sunset. Functions almost identically to receivership liquidation, except that it earns no profit and can be used even on healthy accounts.
  • init_bank_metadata (permissionless) - pay the rent for a bank's metadata
  • write_bank_metadata (metadata admin only) - write token name/description to a bank's metadata
  • admin_super_withdraw (removed) - arena has been sunset, and this is no longer needed.

Additional Notes on Fixed Oracle Banks

  • The oracle_keys[0] slot of Fixed oracle banks is always pubkey default. Other oracle_keys[] fields might still be populated, e.g. the lst mint and sol pool for staked banks, or the reserve for the Kamino banks, this is to simplify switching these banks back into a normal oracle operational mode if needed.
  • Staked collateral banks can't take a fixed price. Kamino banks CAN (and so can future integrator banks).

Additional Notes on new Curves

  • add_bank_permissionless, and add_pool (kamino) now create different placeholders (using the new curve system). Staked and Kamino banks created prior to this update should still be migrated so they don't die when "accruing interest" (even though it doesn't do anything for them, since they never earn interest).
  • Banks on the legacy approach will continue to function as normal before/after migration. The legacy approach will cease to function in 1.7.

Samples

See the referenced functions for more details on how to convert to/from a float to a u32 representation or to a 5-length points slice.

Sample points creation (Rust):

points: make_points(&vec![RatePoint::new(
    p100_to_u32(I80F48!(0.9)),
    p1000_to_u32(I80F48!(1)),
)]),

Sample points creation (TS):

points: makeRatePoints([0.5], [0.6])

Audit Notes

TBD

Consolidates:

Changes since RC1

CU usage optimizations

Deploy Information

Hash - TBD
Staging - TBD
Mainnet -TBD

mrgn - 0.1.6-rc1

17 Nov 16:58
6026554

Choose a tag to compare

mrgn - 0.1.6-rc1 Pre-release
Pre-release

Summary

Implementation of a seven-point curve structure that enables deeper control of interest rates at various utilization levels. Deprecates the simple three-point plateau rate/max rate model. The new curve approach allows the admin to set up to 5 points (plus the points at util = 0 and util = 100). Each point corresponding to a utilization and base interest rate. Points must be in ascending order (util increasing, rates increasing or equal to previous). To determine what the rate is at a given utilization, we linearly interpolate between the nearest two points.

Adds a fixed-price oracle type. Banks using this type have a declared price that can be read at bank.config.fixed_price. They use no external oracle, the price is completely set by the group administrator and never goes stale. This is useful for e.g. stablecoins, assets that are worthless and/or being sunset, or assets that are extremely stable in price for some other reason.

Risk management gets a major upgrade: banks that are defunct and being sunset can now be deleveraged and unwound. Deleverage is essentially a liquidation of a healthy account, but for no profit in dollar terms. We expect that banks at risk of deleveraging will get a LOT of warning (looking at you, UXD). The operation is admin-only.

Bank Metadata: an on-chain account where you can read the plain-English token name and description of a corresponding bank.

Big readme rewrites! More guides!

Bug Fixes

  • Kamino refresh instructions are now allowed before start_liquidation in receivership liquidations.

New Accounts

  • BankMetadata - 1:1 with Banks, contains the utf-8 encoded token name and a brief description. No more running to solscan to figure out what token a bank is using! A pda with one non-static seed: the bank.

New/Changed Fields

  • Banks now have:
    • zero_util_rate - The base rate at utilization = 0. A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • hundred_util_rate - The base rate at full utilization (100%). A %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX
    • points - The base rate at various points between 0 and 100% utilization, exclusive. Exactly five points large. Unused points have util = 0. Util is a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX. Rate is a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX.
    • curve_type - For all newly created banks, INTEREST_CURVE_SEVEN_POINT (1). For all banks created prior to this update, INTEREST_CURVE_LEGACY (0). After migration is completed, all banks will have INTEREST_CURVE_SEVEN_POINT.
    • OracleSetup has a new enum value: Fixed
    • bank.config.fixed_price: A WrappedI80F48, storing the declared price for Fixed banks, does nothing for banks for other OracleSetups. Note: a bank can swap from Fixed to another OracleSetup type. If it does so, this field might be populated with a junk value.
  • Groups now have:
    • deleverage_withdraw_window_cache - Limits how much the risk_admin can deleverage in a day, in the even the admin is compromised
    • risk_admin - can perform bankruptcies and deleverages
    • metadata_admin - can update bank metadata

Breaking Changes (admin instructions)

  • All config instructions affecting interest (add_pool, add_pool_with_seed, configure_bank, configure_bank_interest_only) now take fundamentally different arguments.

Breaking Changes (Everyone)

  • All instructions that require risk checks (borrow, liquidate, withdraw, etc) must now accommodate fixed-price banks. For these banks, pass just the bank in remaining_accounts, no oracle is required. See load_observation_account_metas for a Rust example. For Typescript, assuming you have a list of banks and loaded bank accounts:
   for (let i = 0; i < bankAccs.length; i++) {
     const setup = bankAccs[i].account.config.oracleSetup;
     const keys = bankAccs[i].account.config.oracleKeys;
     if ("fixed" in setup) {
       remainingAccounts.push([banks[i]]);
     } else if ("stakedWithPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1], keys[2]]);
     } else if ("kaminoPythPush" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else if ("kaminoSwitchboardPull" in setup) {
       remainingAccounts.push([banks[i], keys[0], keys[1]]);
     } else {
       remainingAccounts.push([banks[i], keys[0]]);
     }
   }

Breaking Changes (Liquidators)

  • The classic liquidate instruction has two new arguments, now requiring the caller to declare the length of the liquidator's and liquidatee's remaining accounts slice. In normal usage, this simply looks like:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            assetOracle,
            liabOracle,
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })
  • If any asset or liability is fixed, note that you do not have to pass that oracle (see fixed oracle tests in m01/m02 for examples), e.g.:
        await liquidateIx(liquidator.mrgnProgram, {
          //.... base accounts
          remaining: [
            // no asset oracle if fixed
            // no liab oracle if fixed
            // liquidator/ee accounts also respect the fixed-oracle rules above.
            ...liquidatorAccounts,
            ...liquidateeAccounts,
          ],
          amount: amt,
          liquidateeAccounts: liquidateeAccounts.length,
          liquidatorAccounts: liquidatorAccounts.length,
        })

New/Removed instructions

  • migrate_curve (permissionless) - convert a bank from the legacy curve setup to the new seven-point approach. Does not affect bank operations in any way, the bank is immediately able to function as normal.
  • set_fixed_oracle_price (admin only) - sets a price for the bank, like a permanent oracle.
  • start/end_deleverage (risk admin only) - a permissioned liquidation: used to unwind banks that are being sunset. Functions almost identically to receivership liquidation, except that it earns no profit and can be used even on healthy accounts.
  • init_bank_metadata (permissionless) - pay the rent for a bank's metadata
  • write_bank_metadata (metadata admin only) - write token name/description to a bank's metadata
  • admin_super_withdraw (removed) - arena has been sunset, and this is no longer needed.

Additional Notes on Fixed Oracle Banks

  • The oracle_keys[0] slot of Fixed oracle banks is always pubkey default. Other oracle_keys[] fields might still be populated, e.g. the lst mint and sol pool for staked banks, or the reserve for the Kamino banks, this is to simplify switching these banks back into a normal oracle operational mode if needed.
  • Staked collateral banks can't take a fixed price. Kamino banks CAN (and so can future integrator banks).

Additional Notes on new Curves

  • add_bank_permissionless, and add_pool (kamino) now create different placeholders (using the new curve system). Staked and Kamino banks created prior to this update should still be migrated so they don't die when "accruing interest" (even though it doesn't do anything for them, since they never earn interest).
  • Banks on the legacy approach will continue to function as normal before/after migration. The legacy approach will cease to function in 1.7.

Samples

See the referenced functions for more details on how to convert to/from a float to a u32 representation or to a 5-length points slice.

Sample points creation (Rust):

points: make_points(&vec![RatePoint::new(
    p100_to_u32(I80F48!(0.9)),
    p1000_to_u32(I80F48!(1)),
)]),

Sample points creation (TS):

points: makeRatePoints([0.5], [0.6])

Audit Notes

TBD

Consolidates:

Deploy Information

Hash - TBD
Staging - TBD
Mainnet -TBD

mrgn - 0.1.5-rc2

09 Oct 00:00
e4b467c

Choose a tag to compare

Consolidates

PR Description
#357 remove rent from various ixes
#362 Bankruptcy handling improvements for super-bankrupt banks
#364 Broad refactor
#370 Remove Pyth migration functions
#371 Remove type layout
#384 Panic button to pause protocol
#373 Last update time for user accounts
#382 PDA-based mrgn accounts
#372 New Liquidation approach "Receivership"
#386 Emode no brakes
#391 Arena sunset ix
#401 Various minor fixes
#394 Kamino Lending integration

Summary

  • Kamino integration is now live! Allows users to deposit into Kamino Reserves through this program, using their positions as collateral to borrow while continuing to earn any yields or rewards they would normally earn on Kamino! See #394 for more details.
  • New liquidation approach: execute liquidation without a mrgn account, simply by withdrawing and repaying as if you are the user! This novel approach also has a slightly higher premium to attract new developers to move over to it! We will support the old approach for the foreseeable future as well. See #372 for more details.
  • All on-chain structs and many on-chain utility functions are now represented in the type-crate, which can be built without any dependencies. Rust consumers no longer to need to depend on our entire program with all of its cruft and layers of dependencies!
    • Something you wanted missing from type-crate? Contact us or open an issue!
  • Fixes to loss socialization and liquidation of heavily bankrupted banks. Certain bankrupted arena banks that were formerly frozen are now eligible to be liquidated. In the future, we expect banks will not be able to enter the super-bankrupt state currently affecting arena unless a price rapidly collapses or liquidators fail for an extended time.
  • The global fee admin is now able to suspend all financial transactions in all groups in the entire protocol for a few minutes. This power has a limited number of uses per day (currently 2) and a limited time (currently 30 minutes). The purpose of this power is to respond quickly in event of a suspected hack/exploit or to give groups a brief window to conduct complex risk re-configurations. Once expired, the pause lifts permissionlessly.
  • User accounts now have a last_update timestamp that can be used to see when they were last mutated in a meaningful way, for example when they last borrowed, deposited, withdrew, etc, but not when they did a no-op task like health pulse. Backend consumers can now use this timestamp as a "dirty flag" to know when they must update a database entry.
  • User accounts can now be created as a PDA. Legacy user accounts created with a keypair will also be supported indefinitely.
  • Removes the 100% initial asset weight limit from emode, now enabling weights up to 200%. LTVs to the moon!
  • Arena sunsetting: an arena-only instruction will withdraw all funds from the remaining Arena banks, to be returned to their owners OTC. Non-arena banks, e.g. those on the main mrgn or p0 app, are unaffected.

Important for Integrators

  • Mrgn accounts can now be created with a PDA (see marginfi_account_initialize_pda). If you would like a third_party_id assigned to just your program, so you can easily fetch all accounts your program has created by CPI, contact us or open an issue in this repo. Seeds are shall-issue, and will be included in the next update, you don't need any special relationship with mrgn to request a seed.

Breaking Changes (Everyone)

  • Many instructions require an idl update. Upgrade your IDL to the one attached to this release. We removed mut from various accounts that didn't need it. The only user facing instructions are: LendingAccountBorrow/LendingAccountLiquidate -> removed mut from liquidity vault authority
  • Banks that did not yet migrate to the new Pyth oracle model are no longer supported, all functions associated with permissionless migration have been removed. Pyth banks configured in 1.3 and earlier that have not migrated will no longer be able to transact until the admin configures a new oracle. This does not affect any banks in production.
  • Any instruction involving a risk check (borrow, withdraw, liquidate, etc) must now also support Kamino banks. Unlike regular banks, these have two oracle accounts: (1) the oracle, and (2) the Kamino reserve. You can still extract these from oracle_keys.

Breaking Changes (Rust consumers)

  • The "Rent" sysvar has not been needed in a long time and has been removed from various ixes

New instructions

  • (global fee admin only) panic_pause - suspend all financial interactions (borrow, withdraw, repay, etc) for a short preset time. Can only be called twice per day and twice consecutively.
  • (global fee admin only) panic_unpause - resume all financial interactions
  • (permissionless) panic_unpause_permissionless - reset the global pause state after a pause expires (does not need to be called for ixes to resume)
  • (group admin only) lending_pool_add_bank_kamino - Adds a new Kamino bank
  • (permissionless) kamino_init_obligation - Sets up the obligation for a new Kamino bank, which must occur before it can get deposits. Typically, the admin will do this, but anyone can pay the rent if they're feeling generous.
  • (permissionless) kamino_harvest_reward - Collect "farm rewards" (aka token emissions) from a Kamino lending bank. We currently collect these funds to an account under our control, then airdrop them to users at regular intervals. More details to follow.
  • (user) marginfi_account_initialize_pda - create a mrgn account with a PDA instead of a keypair
  • (user) transfer_to_new_account_pda - transfer an existing mrgn account to a new one that is PDA-based.
  • (user) liquidate_start and liquidate_end - the novel liquidation approach no longer requires a mfi account. Calling start enables a liquidator to act as if they are the mfi user, but ONLY to withdraw or repay until the end of the tx. The user's health must improve at the end of the tx. For example, a liquidator might do [start, withdraw A, swap A->B on jup, repay B, end]. Typically the value of is A is slightly greater than B, allowing the liquidator to turn a profit.
  • (user) kamino_deposit - deposit funds to a Kamino bank
  • (user) kamino_withdraw - withdraw funds from a Kamino bank

New fields

  • user accounts now have last_update, tracking the timestamp when they were last mutated in a meaningful way.
  • user accounts generated with the new PDA approach have the following fields. user accounts generated with the legacy keypair approach use all-zeros for these fields:
    • account_index - a seed chosen arbitrarily
    • third_party_index - if < 10,000, a seed chosen arbitrarily. third parties can reserve seeds above 10,000 by contacting us.
    • bump - the bump used to derive the PDA
  • user accounts now have liquidation_record, which points to the liquidation record if they have one, or pubkey default if they've never had a receivership liquidation.
  • the global FeeState now has panic_state, which tracks if the protocol is paused and when it can unpause.
  • groups have panic_state_cache, which tracks the global fee state's panic_state when propagated (see notes below)
  • Banks now have kamino_reserve/kamino_obligation - tracks the Kamino position for Kamino banks, Pubkey Default for all other banks. Note that kamino_reserve is the second account that needs to be passed for any risk checks related to Kamino banks.
  • Banks and user balances have a new possible Asset Tag (3 - Kamino)

New accounts

  • liquidation records - 1:1 relationship with accounts. the first time any user is liquidated using the Receivership approach, the liquidator must pay to create a liquidation record. This account stores intermediate information during liquidation events and also has a record of the last few successful receivership liquidations events on the associated account

Notes on Pause

  • The pause state propagates to groups via propagate_fee_state, which is permissionless. Until propagated, the group is not participating in the pause, and can function normally. Typically, the global fee admin will queue up a pause and propagation to all major groups at the same time. Note that mrgn currently only maintains one group for the main pool you see on the app front page, but this may not always be the case, and anyone can open their own group as well. Message the global fee admin to make sure your group also receives the propagation instruction immediately after any pause.

Notes on Receivership Liquidation

  • Like our old approach, this new approach is also permissionless, we encourage third-parties to build liquidators that utilize it.
  • The classic liquidation approach will be supported indefinitely, but may be cumbersome to use with future integrations e.g. Kamino positions.

Audit Notes

Kamino-related commit hash and pr notes relate to #394, the original audit report was completed based on our internal private mirror of the public repo.

Hash: 4e7867