Crates.io | port-variable-rate-lending-instructions |
lib.rs | port-variable-rate-lending-instructions |
version | 0.3.0 |
source | src |
created_at | 2021-07-16 03:38:50.822061 |
updated_at | 2021-12-23 06:48:25.713547 |
description | Port Finance Variable Rate Lending Porgram |
homepage | |
repository | https://github.com/port-finance |
max_upload_size | |
id | 423317 |
size | 110,767 |
A lending protocol for the Token program on the Solana blockchain inspired by Aave and Compound.
Lending Market: 6T4XxKerq744sSuj3jaoV6QiZ8acirf4TrPwQzHAoSy5
Lending program id: port_variable_rate_lending_instructions::id()
Staking program id: port_staking_instructions::id()
Asset Name | Reserve Address |
---|---|
SOL | X9ByyhmtQH3Wjku9N5obPy54DbVjZV7Z99TPJZ2rwcs |
USDC | DcENuKuYd6BWGhKfGr7eARxodqG12Bz1sN5WA8NwvLRx |
USDT | 4tqY9Hv7e8YhNQXuH75WKrZ7tTckbv2GfFVxmVcScW5s |
PAI | DSw99gXoGzvc4N7cNGU7TJ9bCWFq96NU2Cczi1TabDx2 |
SRM | ZgS3sv1tJAor2rbGMFLeJwxsEGDiHkcrR2ZaNHZUpyF |
BTC | DSST29PMCVkxo8cf5ht9LxrPoMc8jAZt98t6nuJywz8p |
MER | BnhsmYVvNjXK3TGDHLj1Yr1jBGCmD1gZMkAyCwoXsHwt |
mSOL | 9gDF5W94RowoDugxT8cM29cX8pKKQitTp2uYVrarBSQ7 |
pSOL | GRJyCEezbZQibAEfBKCRAg5YoTPP2UcRSTC7RfzoMypy |
SBR | 7dXHPrJtwBjQqU1pLKfkHbq9TjQAK9jTms3rnj1i3G77 |
Actually all the information below you can get by parsing the reserve data. Here is provided for convenience.
Asset Name | pToken Mint |
---|---|
SOL | 8ezDtNNhX91t1NbSLe8xV2PcCEfoQjEm2qDVGjt3rjhg |
USDC | FgSsGV8GByPaMERxeQJPvZRZHf7zCBhrdYtztKorJS58 |
USDT | 3RudPTAkfcq9Q9Jk8SVeCoecCBmdKMj6q5smsWzxqtqZ |
PAI | GaqxUwFGGrDouYLqghchmZU97Y1rNhyF7noMTJNvpQPa |
SRM | 77TBgKmTNtMdGrt1ewNRb56F2Xw6fNLZZj33JZ3oGwXh |
BTC | QN2HkkBaWHfYSU5bybyups9z1UHu8Eu7QeeyMbjD2JA |
MER | 6UgGnLA3Lfe8NBLAESctsUXWdP3zjMFzSLEZxS3tiaKh |
mSOL | Dt1Cuau5m5CSmun8hZstjEh9RszxAmejnq7ZaHNcuXfA |
SBR | FhraFicS7fGxHn8jfzuZ6TeTpCu8PAnQNZiT2tqM5xvv |
Asset Name | Oracle Pubkey |
---|---|
SOL | H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG |
USDC | N / A |
USDT | 3vxLXJqLqF3JG5TCbYycbKWRBbCJQLxQmBGCkyqEEefL |
PAI | N/A |
SRM | 3NBReDRTLKMQEKiLD5tGcx4kXbTf88b7f2xLS9UuGjym |
BTC | GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU |
MER | G4AQpTYKH1Fmg38VpFQbv6uKYQMpRhJzNPALhp7hqdrs |
pSOL | H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG |
SBR | 8Td9VML1nHxQK6M8VVyzsHo32D7VBk72jSpa9U861z2A |
Asset Name | Supply Public Keys |
---|---|
SOL | BLAFX12cDmsumyB6k3L6whJZqNqySaWeCmS5rVuzy3SS |
USDC | 2xPnqU4bWhUSjZ74CibY63NrtkHHw5eKntsxf8dzwiid |
USDT | QyvfrbqH7Mo8W5tHN31nzbfNiwFwqPqahjm9fnzo5EJ |
PAI | 42kNZrAuwZHLtuc7jvVX7zMfkfgwbPynqzFB3zdkAEGM |
SRM | DjhMNdgdbxNud1gmc4DUwrQqJxNbjhxiwNnhc4usSXmQ |
BTC | FZKP27Zxz9GbW86hhq3d1egzpBH5ZnYkyjQZVf86NQJ8 |
MER | 6UmrawFZgdPvMe6BLZdZCNRFz9u2TWsu5enFbTufA3a1 |
SBR | HTwd3VaDQphZgh2x7wqE2Qdndo4TT5C8fpvhKFcNh1Rt |
Lending Program id: pdQ2rQQU5zH2rDgZ7xH2azMBJegUzUyunJ5Jd637hC4
Staking program id: port_staking_instructions::id()
Lending Market: H27Quk3DSbu55T4dCr1NddTTSAezXwHU67FPCZVKLhSW
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.
Asset Name | pToken Mint |
---|---|
SOL | So11111111111111111111111111111111111111112 |
USDC | G6YKv19AeGZ6pUYUwY9D7n4Ry9ESNFa376YqwEkUkhbi |
USDT | 9NGDi2tZtNmCCp8SVLKNuGjuWAVwNF3Vap5tT8km5er9 |
BTC | EbwEYuUQHxcSHszxPBhA2nT2JxhiNwJedwjsctJnLmsC |
MER | Tm9LcR74uJHPw3zY3j3nSh5xfcyaLbvXgAtTJwbqnnp |
Asset Name | pToken Mint |
---|---|
SOL | Hk4Rp3kaPssB6hnjah3Mrqpt5CAXWGoqFT5dVsWA3TaM |
USDC | HyxraiKfdajDbYTC6MVRToEUBdevBN5M5gfyR4LC3WSF |
USDT | 4xEXmSfLFPkZaxdL98XkoxKpXEvchPVs21GYqa8DvbAm |
BTC | 95XGx3cM83Z1Bbx8pJurAHwxJjvShTJE4BtfgMWfV6NB |
MER | FQzruvtLTk6qtPNEAJHQWMVs4M9UMP9T3cGAVfUskHfP |
Asset Name | Oracle Pubkey |
---|---|
SOL | J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix |
USDC | N/A |
USDT | 38xoQ4oeJCBrcVvca2cGk7iV1dAfrmTR1kmhSCJQ8Jto |
BTC | HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J |
MER | 6Z3ejn8DCWQFBuAcw29d3A5jgahEpmycn7YDMX7yRNrn |
Asset Name | Supply Public Keys |
---|---|
SOL | AbKeR7nQdHPDddiDQ71YUsz1F138a7cJMfJVtpdYUSvE |
USDC | GAPyFes3o7S7coY9nsuhaRZBEA7DdQPHBfVdY2DdgNua |
USDT | AeGbAqYZUURTykyCsgAUfopBMqQ3eAwrDxYhXoRhiw8q |
BTC | 75iyCxiPoj3MaUVo3SynmhaN3cbLDEhd4d9VHik6Kkvr |
MER | AMjhzse1TtTcKBFw5tQPLGtVoEsL4gt9YowNnzMKEGUr |
For assets that have liquidity mining reward, you need first create a stake account in order to collateralize and get the reward.
First, get the data of the 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 the owner should be your
wallet's public key, and 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. Owner need to sign the instruction of claiming reward.
If you are using a program to create the stake account, you can use an PDA account. Actually you can use any address to create the stake account,
as long as you make sure that there is a one-one mapping between (owner, staking_pool) and stake_account, which means that for every owner and staking_pool,
you only have one stake account.
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
)
];
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.
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(),
)
);
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_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()
)
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),
)
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),
)
There will be a coming withdrawAndRedeem
instruction soon, while it is under auditing. So now, you need first
uncollateralized the asset then withdraw.
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),
)
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(),
)
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.
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(),
)
Please refer to https://github.com/port-finance/liquidator
We had an instruction with the following signature for flash loan:
pub enum LendingInstruction {
// ....
/// Make a flash loan.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` Source liquidity token account.
/// Minted by reserve liquidity mint.
/// Must match the reserve liquidity supply.
/// 1. `[writable]` Destination liquidity token account.
/// Minted by reserve liquidity mint.
/// 2. `[writable]` Reserve account.
/// 3. `[]` Lending market account.
/// 4. `[]` Derived lending market authority.
/// 5. `[]` Flash loan receiver program account.
/// Must implement an instruction that has tag of 0 and a signature of `(repay_amount: u64)`
/// This instruction must return the amount to the source liquidity account.
/// 6. `[]` Token program id.
/// 7. `[writable]` Flash loan fee receiver account.
/// Must match the reserve liquidity fee receiver.
/// 8. `[writable]` Host fee receiver.
/// .. `[any]` Additional accounts expected by the receiving program's `ReceiveFlashLoan` instruction.
FlashLoan {
/// The amount that is to be borrowed
amount: u64,
},
}
In the implementation, we do the following in order:
amount
from the source liquidity account to the destination liquidity accountReceiveFlashLoan
function (the flash loan receiver program is required to have this function with tag 0
).
The additional account required for ReceiveFlashLoan
is given from the 10th account of the FlashLoan
instruction, i.e. after host fee receiver.ReceiveFlashLoan
function.The flash loan receiver program should have a ReceiveFlashLoan
instruction which executes the user-defined operation and return the funds to the reserve in the end.
pub enum FlashLoanReceiverInstruction {
/// Receive a flash loan and perform user-defined operation and finally return the fund back.
///
/// Accounts expected:
///
/// 0. `[writable]` Source liquidity (matching the destination from above).
/// 1. `[writable]` Destination liquidity (matching the source from above).
/// 2. `[]` Token program id
/// .. `[any]` Additional accounts provided to the lending program's `FlashLoan` instruction above.
ReceiveFlashLoan {
// Amount that is loaned to the receiver program
amount: u64
}
}
You can view a sample implementation here.