Initialize the lending pool contract
env - The Soroban environmentpool_admin - Address with pool admin privilegesemergency_admin - Address with emergency admin privilegesprice_oracle - Address of the price oracle contracttreasury - Address of the treasury (receives protocol fees)dex_router - Address of DEX router (Soroswap router)fn initialize(
env: soroban_sdk::Env,
pool_admin: soroban_sdk::Address,
emergency_admin: soroban_sdk::Address,
price_oracle: soroban_sdk::Address,
treasury: soroban_sdk::Address,
dex_router: soroban_sdk::Address,
incentives_contract: Option,
) -> Result<(), KineticRouterError>
Supply assets to the protocol
env - The Soroban environmentcaller - The address calling this functionasset - The address of the underlying asset to supplyamount - The amount to be suppliedon_behalf_of - The address that will receive the aTokens_referral_code - Code used to register the integrator (unused for now)Ok(()) - Supply successfulErr(KineticRouterError) - Supply failed due to validation or cap limitsThis function enforces supply caps if configured. Caps are stored as whole tokens and converted to smallest units during enforcement to maximize the effective range.
fn supply(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
amount: u128,
on_behalf_of: soroban_sdk::Address,
referral_code: u32,
) -> Result<(), KineticRouterError>
fn withdraw(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
amount: u128,
to: soroban_sdk::Address,
) -> Result
fn swap_collateral(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
from_asset: soroban_sdk::Address,
to_asset: soroban_sdk::Address,
amount: u128,
min_amount_out: u128,
swap_handler: Option,
) -> Result
Set DEX router address (admin only)
fn set_dex_router(
env: soroban_sdk::Env,
router: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Get DEX router address
fn get_dex_router(env: soroban_sdk::Env) -> Option
Set DEX factory address (admin only)
fn set_dex_factory(
env: soroban_sdk::Env,
factory: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Get DEX factory address
fn get_dex_factory(env: soroban_sdk::Env) -> Option
fn borrow(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
amount: u128,
interest_rate_mode: u32,
referral_code: u32,
on_behalf_of: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn repay(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
amount: u128,
rate_mode: u32,
on_behalf_of: soroban_sdk::Address,
) -> Result
Liquidate a position
collateral_asset - The address of the underlying asset used as collateraldebt_asset - The address of the underlying borrowed asset to be repaiduser - The address of the borrower getting liquidateddebt_to_cover - The debt amount of borrowed asset to liquidatereceive_a_token - True if liquidator receives aTokens, false for underlying assetfn liquidation_call(
env: soroban_sdk::Env,
liquidator: soroban_sdk::Address,
collateral_asset: soroban_sdk::Address,
debt_asset: soroban_sdk::Address,
user: soroban_sdk::Address,
debt_to_cover: u128,
receive_a_token: bool,
) -> Result<(), KineticRouterError>
fn set_flash_loan_premium(
env: soroban_sdk::Env,
premium_bps: u128,
) -> Result<(), KineticRouterError>
Set maximum flash loan premium allowed (admin only)
max_premium_bps - Maximum premium in basis points (e.g., 100 = 1%)Ok(()) if max premium updated successfullyErr(Unauthorized) if caller is not adminfn set_flash_loan_premium_max(
env: soroban_sdk::Env,
max_premium_bps: u128,
) -> Result<(), KineticRouterError>
fn get_flash_loan_premium_max(env: soroban_sdk::Env) -> u128
fn set_hf_liquidation_threshold(
env: soroban_sdk::Env,
threshold: u128,
) -> Result<(), KineticRouterError>
fn get_hf_liquidation_threshold(env: soroban_sdk::Env) -> u128
fn set_min_swap_output_bps(
env: soroban_sdk::Env,
min_output_bps: u128,
) -> Result<(), KineticRouterError>
fn get_min_swap_output_bps(env: soroban_sdk::Env) -> u128
fn set_partial_liq_hf_threshold(
env: soroban_sdk::Env,
threshold: u128,
) -> Result<(), KineticRouterError>
fn get_partial_liq_hf_threshold(env: soroban_sdk::Env) -> u128
fn get_flash_loan_premium(env: soroban_sdk::Env) -> u128
Set extra premium charged for flash liquidations (admin only). This is on top of the regular protocol fee collected during liquidation. Set to 0 to disable the extra fee (default).
fn set_flash_liquidation_premium(
env: soroban_sdk::Env,
premium_bps: u128,
) -> Result<(), KineticRouterError>
fn get_flash_liquidation_premium(env: soroban_sdk::Env) -> u128
Sets the price tolerance for two-step liquidation execution (in basis points). Default is 300 (3%). Admin has full flexibility to set any value.
fn set_liquidation_price_tolerance(
env: soroban_sdk::Env,
tolerance_bps: u128,
) -> Result<(), KineticRouterError>
M-07
fn set_asset_staleness_threshold(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
threshold_seconds: u64,
) -> Result<(), KineticRouterError>
M-07
fn get_asset_staleness_threshold(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Option
Execute a flash loan
Flash loans are permissionless - anyone can initiate them.
The receiver contract must implement execute_operation callback.
initiator: Address initiating the flash loan (must authorize)receiver: Contract that will receive the loan and execute callbackassets: Assets to borrowamounts: Amounts to borrow for each assetparams: Arbitrary data passed to receiverThe receiver must implement execute_operation with the standard flash loan interface.
Initiator must authorize the call, but anyone can be an initiator. The receiver handles its own authorization logic.
InvalidFlashLoanParams: Invalid parametersInsufficientFlashLoanLiquidity: Not enough liquidityFlashLoanExecutionFailed: Receiver callback failedFlashLoanNotRepaid: Loan not fully repaidfn flash_loan(
env: soroban_sdk::Env,
initiator: soroban_sdk::Address,
receiver: soroban_sdk::Address,
assets: soroban_sdk::Vec,
amounts: soroban_sdk::Vec,
params: soroban_sdk::Bytes,
) -> Result<(), KineticRouterError>
Prepare a liquidation - validates and stores authorization (TX1 of 2-step liquidation) This is the expensive validation step (~40M CPU) but can fail/retry safely.
liquidator - Address executing the liquidationuser - Address being liquidateddebt_asset - Asset to repaycollateral_asset - Asset to seizedebt_to_cover - Amount of debt to repaymin_swap_out - Minimum acceptable swap output (slippage protection)swap_handler - Optional custom swap handler addressLiquidationAuthorization - Authorization token for execute_liquidationfn prepare_liquidation(
env: soroban_sdk::Env,
liquidator: soroban_sdk::Address,
user: soroban_sdk::Address,
debt_asset: soroban_sdk::Address,
collateral_asset: soroban_sdk::Address,
debt_to_cover: u128,
min_swap_out: u128,
swap_handler: Option,
) -> Result
Execute a prepared liquidation - atomic swap + debt repayment (TX2 of 2-step liquidation) Uses pre-validated data from prepare_liquidation (~60M CPU).
liquidator - Address executing the liquidation (must match authorization)user - Address being liquidated (must match authorization)debt_asset - Asset to repay (must match authorization)collateral_asset - Asset to seize (must match authorization)deadline - Transaction deadline timestampfn execute_liquidation(
env: soroban_sdk::Env,
liquidator: soroban_sdk::Address,
user: soroban_sdk::Address,
debt_asset: soroban_sdk::Address,
collateral_asset: soroban_sdk::Address,
deadline: u64,
) -> Result<(), KineticRouterError>
Set treasury address for protocol fees (admin only)
treasury - New treasury address for protocol fee collectionOk(()) if treasury updated successfullyErr(Unauthorized) if caller is not adminfn set_treasury(
env: soroban_sdk::Env,
treasury: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn get_treasury(env: soroban_sdk::Env) -> Option
F-02 Safety net for when oracle precision changes without changing the oracle address.
fn flush_oracle_config_cache(env: soroban_sdk::Env) -> Result<(), KineticRouterError>
AC-01 Must be called once after contract upgrade to prevent whitelist/blacklist bypass.
fn sync_access_control_flags(env: soroban_sdk::Env) -> Result<(), KineticRouterError>
fn set_flash_liquidation_helper(
env: soroban_sdk::Env,
helper: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn get_flash_liquidation_helper(env: soroban_sdk::Env) -> Option
Set pool configurator contract address (admin only)
configurator - Pool configurator contract addressOk(()) if configurator address updated successfullyErr(Unauthorized) if caller is not adminfn set_pool_configurator(
env: soroban_sdk::Env,
configurator: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn get_pool_configurator(env: soroban_sdk::Env) -> Option
Get available protocol reserves for an asset
Protocol reserves accumulate due to the reserve factor, which reduces supplier APY. Reserves = underlying_balance_in_atoken - total_withdrawable_supply
asset - The address of the underlying assetOk(u128) - Available reserves in smallest unitsErr - If reserve not found or calculation failsfn get_protocol_reserves(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
fn collect_protocol_reserves(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
Cover accumulated bad debt deficit for a reserve. Permissionless: anyone can inject tokens to replenish pool liquidity. Returns the actual amount covered (capped at current deficit).
fn cover_deficit(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
amount: u128,
) -> Result
Get accumulated bad debt deficit for a reserve (0 if none).
fn get_reserve_deficit(env: soroban_sdk::Env, asset: soroban_sdk::Address) -> u128
fn set_user_use_reserve_as_coll(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
use_as_collateral: bool,
) -> Result<(), KineticRouterError>
Get user account data
user - The address of the userfn get_user_account_data(
env: soroban_sdk::Env,
user: soroban_sdk::Address,
) -> Result
fn get_reserve_data(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
fn get_current_reserve_data(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
fn get_current_liquidity_index(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
fn get_current_var_borrow_idx(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
Get incentives contract address
env - The Soroban environmentOption<Address> - The incentives contract address if set, None otherwisefn get_incentives_contract(env: soroban_sdk::Env) -> Option
fn set_incentives_contract(
env: soroban_sdk::Env,
incentives: soroban_sdk::Address,
) -> Result
Get user configuration
user - The address of the userfn get_user_configuration(
env: soroban_sdk::Env,
user: soroban_sdk::Address,
) -> UserConfiguration
fn get_reserves_list(env: soroban_sdk::Env) -> soroban_sdk::Vec
L-02
fn update_reserve_state(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
fn is_paused(env: soroban_sdk::Env) -> bool
fn pause(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn unpause(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Initialize a new reserve
This function should be called by the pool configurator contract. The pool configurator should validate authorization before calling this function.
underlying_asset - The address of the underlying asseta_token_impl - The address of the aToken implementationvariable_debt_impl - The address of the variable debt token implementationinterest_rate_strategy - The address of the interest rate strategytreasury - The address of the treasuryparams - Reserve initialization parametersfn init_reserve(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
underlying_asset: soroban_sdk::Address,
a_token_impl: soroban_sdk::Address,
variable_debt_impl: soroban_sdk::Address,
interest_rate_strategy: soroban_sdk::Address,
treasury: soroban_sdk::Address,
params: InitReserveParams,
) -> Result<(), KineticRouterError>
Updates the supply cap for a reserve.
The supply cap limits the total amount that can be supplied to a reserve. Caps are stored as whole tokens (not smallest units) to maximize the effective range within the 32-bit storage limit.
env: The Soroban environmentasset: The underlying asset addresssupply_cap: New supply cap in whole tokens (0 = no cap, virtually unlimited)Ok(()): Supply cap updated successfullyErr(KineticRouterError::InvalidAmount): Invalid cap valueErr(KineticRouterError::Unauthorized): Caller is not adminEmits (sup_cap, asset) event with the new supply cap value.
fn set_reserve_supply_cap(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
supply_cap: u128,
) -> Result<(), KineticRouterError>
Updates the borrow cap for a reserve.
The borrow cap limits the total amount that can be borrowed from a reserve. Caps are stored as whole tokens (not smallest units) to maximize the effective range within the 32-bit storage limit.
env: The Soroban environmentasset: The underlying asset addressborrow_cap: New borrow cap in whole tokens (0 = no cap, virtually unlimited)Ok(()): Borrow cap updated successfullyErr(KineticRouterError::InvalidAmount): Invalid cap valueErr(KineticRouterError::Unauthorized): Caller is not adminEmits (bor_cap, asset) event with the new borrow cap value.
fn set_reserve_borrow_cap(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
borrow_cap: u128,
) -> Result<(), KineticRouterError>
Sets the minimum remaining debt after partial liquidation for a reserve. Value is in whole tokens (same convention as borrow/supply caps). Prevents dust debt positions that are uneconomical to liquidate.
fn set_reserve_min_remaining_debt(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
min_remaining_debt: u32,
) -> Result<(), KineticRouterError>
Updates the debt ceiling for a reserve.
The debt ceiling limits the total amount of debt that can be borrowed across all users for a specific reserve. This is different from borrow cap which limits per-reserve borrowing. Debt ceiling is stored as whole tokens (not smallest units).
env: The Soroban environmentasset: The underlying asset addressdebt_ceiling: New debt ceiling in whole tokens (0 = no ceiling)Ok(()): Debt ceiling updated successfullyErr(KineticRouterError::ReserveNotFound): Reserve does not existErr(KineticRouterError::Unauthorized): Caller is not adminEmits (set_cap, asset) event with (debt_ceil, debt_ceiling) value.
fn set_reserve_debt_ceiling(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
debt_ceiling: u128,
) -> Result<(), KineticRouterError>
Gets the debt ceiling for a reserve.
asset: The underlying asset addressOk(u128): Debt ceiling in whole tokens (0 = no ceiling)Err(KineticRouterError::ReserveNotFound): Reserve does not existfn get_reserve_debt_ceiling(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> Result
Update reserve configuration (called by pool configurator)
caller: The address calling this function (must be pool configurator)asset: The underlying asset addressconfiguration: New reserve configurationfn update_reserve_configuration(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
configuration: ReserveConfiguration,
) -> Result<(), KineticRouterError>
fn update_reserve_rate_strategy(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
interest_rate_strategy: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
fn update_atoken_implementation(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
a_token_impl: soroban_sdk::BytesN<32>,
) -> Result<(), KineticRouterError>
fn update_debt_token_implementation(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
debt_token_impl: soroban_sdk::BytesN<32>,
) -> Result<(), KineticRouterError>
fn drop_reserve(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
asset: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Upgrade contract WASM (admin only)
new_wasm_hash - Hash of new WASM binaryUnauthorized - Caller is not adminfn upgrade(
env: soroban_sdk::Env,
new_wasm_hash: soroban_sdk::BytesN<32>,
) -> Result<(), KineticRouterError>
fn version(env: soroban_sdk::Env) -> u32
fn get_admin(env: soroban_sdk::Env) -> Result
Propose a new upgrade admin address (two-step transfer, step 1).
Only the current admin can propose a new admin.
The proposed admin must call accept_admin to complete the transfer.
fn propose_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
pending_admin: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Accept upgrade admin role (two-step transfer, step 2). Only the pending admin can call this to finalize the transfer.
fn accept_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Cancel a pending upgrade admin proposal. Only the current admin can cancel a pending proposal.
fn cancel_admin_proposal(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Get the pending upgrade admin address, if any.
fn get_pending_admin(
env: soroban_sdk::Env,
) -> Result
Propose a new pool admin address (two-step transfer, step 1).
fn propose_pool_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
pending_admin: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Accept pool admin role (two-step transfer, step 2).
fn accept_pool_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Cancel a pending pool admin proposal.
fn cancel_pool_admin_proposal(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Get the pending pool admin address, if any.
fn get_pending_pool_admin(
env: soroban_sdk::Env,
) -> Result
Propose a new emergency admin address (two-step transfer, step 1).
fn propose_emergency_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
pending_admin: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Accept emergency admin role (two-step transfer, step 2).
fn accept_emergency_admin(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Cancel a pending emergency admin proposal.
fn cancel_emergency_admin_proposal(
env: soroban_sdk::Env,
caller: soroban_sdk::Address,
) -> Result<(), KineticRouterError>
Get the pending emergency admin address, if any.
fn get_pending_emergency_admin(
env: soroban_sdk::Env,
) -> Result
Set reserve whitelist (admin only)
caller - Pool admin addressasset - Underlying asset addresswhitelist - Addresses allowed to interact with this reserve** Note **: This function replaces the entire whitelist. To add/remove addresses, first get the current list, modify it, then set the complete new list.
Unauthorized - Caller is not adminfn set_reserve_whitelist(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
whitelist: soroban_sdk::Vec,
) -> Result<(), KineticRouterError>
fn get_reserve_whitelist(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> soroban_sdk::Vec
fn is_whitelisted_for_reserve(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
address: soroban_sdk::Address,
) -> bool
fn set_liquidation_whitelist(
env: soroban_sdk::Env,
whitelist: soroban_sdk::Vec,
) -> Result<(), KineticRouterError>
fn get_liquidation_whitelist(
env: soroban_sdk::Env,
) -> soroban_sdk::Vec
fn is_whitelisted_for_liquidation(
env: soroban_sdk::Env,
address: soroban_sdk::Address,
) -> bool
fn set_reserve_blacklist(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
blacklist: soroban_sdk::Vec,
) -> Result<(), KineticRouterError>
fn get_reserve_blacklist(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
) -> soroban_sdk::Vec
fn is_blacklisted_for_reserve(
env: soroban_sdk::Env,
asset: soroban_sdk::Address,
address: soroban_sdk::Address,
) -> bool
fn set_liquidation_blacklist(
env: soroban_sdk::Env,
blacklist: soroban_sdk::Vec,
) -> Result<(), KineticRouterError>
fn get_liquidation_blacklist(
env: soroban_sdk::Env,
) -> soroban_sdk::Vec
fn is_blacklisted_for_liquidation(
env: soroban_sdk::Env,
address: soroban_sdk::Address,
) -> bool
M-01 Only whitelisted handlers can be used for custom swaps. Empty whitelist = deny all custom handlers (only built-in DEX).
fn set_swap_handler_whitelist(
env: soroban_sdk::Env,
whitelist: soroban_sdk::Vec,
) -> Result<(), KineticRouterError>
fn get_swap_handler_whitelist(
env: soroban_sdk::Env,
) -> soroban_sdk::Vec
fn is_swap_handler_whitelisted(
env: soroban_sdk::Env,
handler: soroban_sdk::Address,
) -> bool
WP-C1 + MEDIUM-1 fix: Validate sender HF and update bitmaps for aToken transfers. Called by aToken.transfer_internal() after computing balances but before writing them. Single cross-contract call replaces separate validate + finalize to save router size.
fn validate_and_finalize_transfer(
env: soroban_sdk::Env,
underlying_asset: soroban_sdk::Address,
from: soroban_sdk::Address,
to: soroban_sdk::Address,
amount: u128,
from_balance_after: u128,
to_balance_after: u128,
) -> Result<(), KineticRouterError>