Token-lending program

A lending protocol for the Token program on the Solana blockchain inspired by Aave and Compound.

Public Keys for Port Finance

Mainnet

Lending Market: 6T4XxKerq744sSuj3jaoV6QiZ8acirf4TrPwQzHAoSy5

Reserve Public Keys

| Asset Name | Reserve Address | | :--- | ----: | | SOL | X9ByyhmtQH3Wjku9N5obPy54DbVjZV7Z99TPJZ2rwcs | | USDC | DcENuKuYd6BWGhKfGr7eARxodqG12Bz1sN5WA8NwvLRx | | USDT | 4tqY9Hv7e8YhNQXuH75WKrZ7tTckbv2GfFVxmVcScW5s | | PAI | DSw99gXoGzvc4N7cNGU7TJ9bCWFq96NU2Cczi1TabDx2 | | SRM | ZgS3sv1tJAor2rbGMFLeJwxsEGDiHkcrR2ZaNHZUpyF | | BTC | DSST29PMCVkxo8cf5ht9LxrPoMc8jAZt98t6nuJywz8p | | MER | BnhsmYVvNjXK3TGDHLj1Yr1jBGCmD1gZMkAyCwoXsHwt | | mSOL | 9gDF5W94RowoDugxT8cM29cX8pKKQitTp2uYVrarBSQ7 | | pSOL | GRJyCEezbZQibAEfBKCRAg5YoTPP2UcRSTC7RfzoMypy |

Actually all the information below you can get by parsing the reserve data. Here is provided for convenience.

pToken Mint

| Asset Name | pToken Mint | | :--- | ---: | | SOL | 8ezDtNNhX91t1NbSLe8xV2PcCEfoQjEm2qDVGjt3rjhg | | USDC | FgSsGV8GByPaMERxeQJPvZRZHf7zCBhrdYtztKorJS58 | | USDT | 3RudPTAkfcq9Q9Jk8SVeCoecCBmdKMj6q5smsWzxqtqZ | | PAI | GaqxUwFGGrDouYLqghchmZU97Y1rNhyF7noMTJNvpQPa | | SRM | 77TBgKmTNtMdGrt1ewNRb56F2Xw6fNLZZj33JZ3oGwXh | | BTC | QN2HkkBaWHfYSU5bybyups9z1UHu8Eu7QeeyMbjD2JA | | MER | 6UgGnLA3Lfe8NBLAESctsUXWdP3zjMFzSLEZxS3tiaKh | | mSOL | Dt1Cuau5m5CSmun8hZstjEh9RszxAmejnq7ZaHNcuXfA |

Oracle Public Keys

| Asset Name | Oracle Pubkey | | :--- | ---: | | SOL | H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG | | USDC | N / A | | USDT | 3vxLXJqLqF3JG5TCbYycbKWRBbCJQLxQmBGCkyqEEefL | | PAI | N/A | | SRM | 3NBReDRTLKMQEKiLD5tGcx4kXbTf88b7f2xLS9UuGjym | | BTC | GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU | | MER | G4AQpTYKH1Fmg38VpFQbv6uKYQMpRhJzNPALhp7hqdrs | | pSOL | H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG |

Supply Public Keys

| Asset Name | Supply Public Keys | | :--- | ---: | | SOL | BLAFX12cDmsumyB6k3L6whJZqNqySaWeCmS5rVuzy3SS | | USDC | 2xPnqU4bWhUSjZ74CibY63NrtkHHw5eKntsxf8dzwiid | | USDT | QyvfrbqH7Mo8W5tHN31nzbfNiwFwqPqahjm9fnzo5EJ | | PAI | 42kNZrAuwZHLtuc7jvVX7zMfkfgwbPynqzFB3zdkAEGM | | SRM | DjhMNdgdbxNud1gmc4DUwrQqJxNbjhxiwNnhc4usSXmQ | | BTC | FZKP27Zxz9GbW86hhq3d1egzpBH5ZnYkyjQZVf86NQJ8 | | MER | 6UmrawFZgdPvMe6BLZdZCNRFz9u2TWsu5enFbTufA3a1 |

Dev Net

Program id: pdQ2rQQU5zH2rDgZ7xH2azMBJegUzUyunJ5Jd637hC4 Lending Market: H27Quk3DSbu55T4dCr1NddTTSAezXwHU67FPCZVKLhSW

Reserve Public Keys

| Asset Name | Reserve Address | | :--- | ----: | | SOL | 6FeVStQAGPWvfWijDHF7cTWRCi7He6vTT3ubfNhe9SPt | | USDC | G1CcAWGhfxhHQaivC1Sh5CWVta6P4dc7a5BDSg9ERjV1 | | USDT | B4dnCXcWXSXy1g3fGAmF6P2XgsLTFYaQxYpsU3VCB33Q | | BTC | A8krqNC1WpWYhqUe2Y5WbLd1Zy4y2rRN5wJC8o9Scbyk | | MER | FdPnmYS7Ma8jfSy7UHAN5QM6teoqwd3vLQtoU6r2Umwy |

Actually all the information below you can get by parsing the reserve data. Here is provided for convenience.

Fake token Mint

| Asset Name | pToken Mint | | :--- | ---: | | SOL | So11111111111111111111111111111111111111112 | | USDC | G6YKv19AeGZ6pUYUwY9D7n4Ry9ESNFa376YqwEkUkhbi | | USDT | 9NGDi2tZtNmCCp8SVLKNuGjuWAVwNF3Vap5tT8km5er9 | | BTC | EbwEYuUQHxcSHszxPBhA2nT2JxhiNwJedwjsctJnLmsC | | MER | Tm9LcR74uJHPw3zY3j3nSh5xfcyaLbvXgAtTJwbqnnp |

pToken Mint

| Asset Name | pToken Mint | | :--- | ---: | | SOL | Hk4Rp3kaPssB6hnjah3Mrqpt5CAXWGoqFT5dVsWA3TaM | | USDC | HyxraiKfdajDbYTC6MVRToEUBdevBN5M5gfyR4LC3WSF | | USDT | 4xEXmSfLFPkZaxdL98XkoxKpXEvchPVs21GYqa8DvbAm | | BTC | 95XGx3cM83Z1Bbx8pJurAHwxJjvShTJE4BtfgMWfV6NB | | MER | FQzruvtLTk6qtPNEAJHQWMVs4M9UMP9T3cGAVfUskHfP |

Oracle Public Keys

| Asset Name | Oracle Pubkey | | :--- | ---: | | SOL | J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix | | USDC | N/A | | USDT | 38xoQ4oeJCBrcVvca2cGk7iV1dAfrmTR1kmhSCJQ8Jto | | BTC | HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J | | MER | 6Z3ejn8DCWQFBuAcw29d3A5jgahEpmycn7YDMX7yRNrn |

Supply Public Keys

| Asset Name | Supply Public Keys | | :--- | ---: | | SOL | AbKeR7nQdHPDddiDQ71YUsz1F138a7cJMfJVtpdYUSvE | | USDC | GAPyFes3o7S7coY9nsuhaRZBEA7DdQPHBfVdY2DdgNua | | USDT | AeGbAqYZUURTykyCsgAUfopBMqQ3eAwrDxYhXoRhiw8q | | BTC | 75iyCxiPoj3MaUVo3SynmhaN3cbLDEhd4d9VHik6Kkvr | | MER | AMjhzse1TtTcKBFw5tQPLGtVoEsL4gt9YowNnzMKEGUr |

Create StakeAccount

For assets that have liquidity mining reward, you need first create a stake account in order to get the reward. First, get the data of reserve you want deposit into and unpack it to get the field reserve.config.deposit_staking_pool which is the staking pool id, and generates the seed and keypair by using the Solana built in sha256 hashing function solana_sdk::hash::hashv(&[owner.as_ref(), staking_pool.as_ref(), staking_program_id.as_ref()]), where owner is the then generate the keypair for stake account by solana_sdk::signer::keypair::keypair_from_seed. Then call create_account and create_stake_account instruction to create the stake account, where the owner should be your wallet's public key. Owner need to sign the instruction of claiming reward.

rust let account_seed = hashv( & [owner.as_ref(), staking_pool.as_ref(), staking_program_id.as_ref()]); let stake_account_key_pair = keypair_from_seed(account_seed.as_ref()).unwrap(); let instructions = vec![ create_account( &payer.pubkey(), &stake_account_key_pair.pubkey(), stake_account_rent, StakeAccount::LEN as u64, &staking_program_id ), create_stake_account( staking_program_id, stake_account_key_pair.pubkey(), staking_pool, owner ) ];

Refresh Reserves and Obligation

Assuming that you already have your stake account or you are depositing to a reserve without liquidity mining reward, you need refresh all the reserves the obligation has interacted with and the obligation itself in the same instruction before you depositing / withdrawing / repaying / liquidating.

rust let mut refresh_instructions = vec![]; //reserve_map is a map of reserve pubkey of reserve data, you can get the oracle pubkey from the reserve data or can hard code it in a config file. let to_refresh = reserve_map.iter().filter( | (k, _) | { obligation .borrows .iter() .map(| li | li.borrow_reserve) .chain(obligation.deposits.iter().map(| ob | ob.deposit_reserve)) .any( | r | r == * * k) }); refresh_instructions.extend( to_refresh.map( | (k, v) | refresh_reserve(lending_program_id, * k, v.liquidity.oracle_pubkey)), ); refresh_instructions.push( refresh_obligation( program_id, obligation_pubkey, obligation .deposits .iter() .map(|d| d.deposit_reserve) .chain(obligation.borrows.iter().map(|b| b.borrow_reserve)) .collect(), ) );

Initialize Obligation

rust let mut transaction = Transaction::new_with_payer( &[ create_account( &payer.pubkey(), &obligation_keypair.pubkey(), rent.minimum_balance(Obligation::LEN), Obligation::LEN as u64, &port_finance_variable_rate_lending::id(), ), init_obligation( port_finance_variable_rate_lending::id(), obligation.pubkey, lending_market.pubkey, user_accounts_owner.pubkey(), ), ], Some( & payer.pubkey()), );

Deposit Liquidity / Collateralize

rust deposit_reserve_liquidity( port_variable_rate_lending::id(), liquidity_amount, user_liquidity_token_account_pubkey, user_collateral_token_account_pubkey, reserve_pubkey, reserve.liquidity.supply_pubkey, reserve.collateral.mint_pubkey, self.pubkey, user_transfer_authority.pubkey() )

rust deposit_obligation_collateral( port_variable_rate_lending::id(), liquidity_amount, user_collateral_token_account_pubkey, reserve.collateral.supply_pubkey, reserve_pubkey, obligation_pubkey, lending_market.pubkey, obligation.owner, user_transfer_authority.pubkey(), Some(stake_account_pubkey), Some(staking_pool_pubkey), )

rust deposit_reserve_liquidity_and_obligation_collateral( port_variable_rate_lending::id(), liquidity_amount, user_liquidity_token_account_pubkey, user_collateral_token_account_pubkey, reserve_pubkey, reserve.liquidity.supply_pubkey, reserve.collateral.mint_pubkey, lending_market_pubkey, reserve.collateral.supply_pubkey, obligation_pubkey, obligation.owner, user_transfer_authority.pubkey(), Some(stake_account_pubkey), Some(staking_pool_pubkey), )

Withdraw

There will be a coming withdrawAndRedeem instruction soon, while it is under auditing. So now, you need first uncollateralized the asset then withdraw.

rust withdraw_obligation_collateral( port_finance_variable_rate_lending::id(), WITHDRAW_AMOUNT, reserve.collateral.supply_pubkey, sol_test_reserve.user_collateral_pubkey, sol_test_reserve.pubkey, test_obligation.pubkey, lending_market.pubkey, test_obligation.owner, Some(stake_account_pubkey), Some(staking_pool_pubkey), )

rust redeem_reserve_collateral( port_finance_variable_rate_lending::id(), COLLATERAL_AMOUNT, user_collateral_token_account_pubkey, user_liquidity_token_account_pubkey, reserve_pubkey, reserve.collateral.mint_pubkey, reserve.liquidity.supply_pubkey, lending_market.pubkey, user_transfer_authority.pubkey(), )

Repay

To repay, you can pass number greater then amount you borrow to repay all, for example you can pass u64::MAX in to repay all.

rust repay_obligation_liquidity( port_finance_variable_rate_lending::id(), liquidity_amount, user_liquidity_token_account_pubkey, reserve.liquidity.supply_pubkey, reserve_pubkey, obligation_pubkey, lending_market.pubkey, user_transfer_authority.pubkey(), )