Port Finance
Search…
Interacting with Port Finance via Rust
We have two crates for interacting with Port Finance program via Rust:
https://crates.io/crates/port-variable-rate-lending-instructions
crates.io
https://crates.io/crates/port-finance-staking-instructions
crates.io

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 Lending program id: port_variable_rate_lending_instructions::id() Staking program id: port_staking_instructions::id()

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
SBR
7dXHPrJtwBjQqU1pLKfkHbq9TjQAK9jTms3rnj1i3G77
You can get all the information below by parsing the reserve data. We provide the account data here for your convenience.

pToken Mint

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

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
SBR
8Td9VML1nHxQK6M8VVyzsHo32D7VBk72jSpa9U861z2A

Supply Public Keys

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

Dev Net

Lending Program id: pdQ2rQQU5zH2rDgZ7xH2azMBJegUzUyunJ5Jd637hC4
Staking program id: port_staking_instructions::id()
Lending Market: H27Quk3DSbu55T4dCr1NddTTSAezXwHU67FPCZVKLhSW

Reserve Public Keys

Asset Name
Reserve Address
SOL
6FeVStQAGPWvfWijDHF7cTWRCi7He6vTT3ubfNhe9SPt
USDC
G1CcAWGhfxhHQaivC1Sh5CWVta6P4dc7a5BDSg9ERjV1
USDT
B4dnCXcWXSXy1g3fGAmF6P2XgsLTFYaQxYpsU3VCB33Q
BTC
A8krqNC1WpWYhqUe2Y5WbLd1Zy4y2rRN5wJC8o9Scbyk
MER
FdPnmYS7Ma8jfSy7UHAN5QM6teoqwd3vLQtoU6r2Umwy
You can get all the information below by parsing the reserve data. We provide the account data here for your 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 to 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.
1
let account_seed = hashv( & [owner.as_ref(), staking_pool.as_ref(), staking_program_id.as_ref()]);
2
let stake_account_key_pair = keypair_from_seed(account_seed.as_ref()).unwrap();
3
let instructions = vec![
4
create_account(
5
&payer.pubkey(),
6
&stake_account_key_pair.pubkey(),
7
stake_account_rent,
8
StakeAccount::LEN as u64,
9
&staking_program_id
10
),
11
create_stake_account(
12
staking_program_id,
13
stake_account_key_pair.pubkey(),
14
staking_pool,
15
owner
16
)
17
];
Copied!

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 to refresh all the reserves the obligation has interacted with and the obligation itself in the same instruction before you depositing / withdrawing / repaying / liquidating.
1
let mut refresh_instructions = vec![];
2
//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.
3
let to_refresh = reserve_map.iter().filter( | (k, _) | {
4
obligation
5
.borrows
6
.iter()
7
.map(| li | li.borrow_reserve)
8
.chain(obligation.deposits.iter().map(| ob | ob.deposit_reserve))
9
.any( | r | r == * * k)
10
});
11
refresh_instructions.extend(
12
to_refresh.map( | (k, v) | refresh_reserve(lending_program_id, * k, v.liquidity.oracle_pubkey)),
13
);
14
refresh_instructions.push(
15
refresh_obligation(
16
program_id,
17
obligation_pubkey,
18
obligation
19
.deposits
20
.iter()
21
.map(|d| d.deposit_reserve)
22
.chain(obligation.borrows.iter().map(|b| b.borrow_reserve))
23
.collect(),
24
)
25
);
Copied!

Initialize Obligation

1
let mut transaction = Transaction::new_with_payer(
2
&[
3
create_account(
4
&payer.pubkey(),
5
&obligation_keypair.pubkey(),
6
rent.minimum_balance(Obligation::LEN),
7
Obligation::LEN as u64,
8
&port_finance_variable_rate_lending::id(),
9
),
10
init_obligation(
11
port_finance_variable_rate_lending::id(),
12
obligation.pubkey,
13
lending_market.pubkey,
14
user_accounts_owner.pubkey(),
15
),
16
],
17
Some( & payer.pubkey()),
18
);
Copied!

Deposit Liquidity / Collateralize

1
deposit_reserve_liquidity(
2
port_variable_rate_lending::id(),
3
liquidity_amount,
4
user_liquidity_token_account_pubkey,
5
user_collateral_token_account_pubkey,
6
reserve_pubkey,
7
reserve.liquidity.supply_pubkey,
8
reserve.collateral.mint_pubkey,
9
self.pubkey,
10
user_transfer_authority.pubkey()
11
)
Copied!
1
deposit_obligation_collateral(
2
port_variable_rate_lending::id(),
3
liquidity_amount,
4
user_collateral_token_account_pubkey,
5
reserve.collateral.supply_pubkey,
6
reserve_pubkey,
7
obligation_pubkey,
8
lending_market.pubkey,
9
obligation.owner,
10
user_transfer_authority.pubkey(),
11
Some(stake_account_pubkey),
12
Some(staking_pool_pubkey),
13
)
Copied!
1
deposit_reserve_liquidity_and_obligation_collateral(
2
port_variable_rate_lending::id(),
3
liquidity_amount,
4
user_liquidity_token_account_pubkey,
5
user_collateral_token_account_pubkey,
6
reserve_pubkey,
7
reserve.liquidity.supply_pubkey,
8
reserve.collateral.mint_pubkey,
9
lending_market_pubkey,
10
reserve.collateral.supply_pubkey,
11
obligation_pubkey,
12
obligation.owner,
13
user_transfer_authority.pubkey(),
14
Some(stake_account_pubkey),
15
Some(staking_pool_pubkey),
16
)
Copied!

Withdraw

There will be a coming withdrawAndRedeem instruction soon, while it is under auditing. So now, you need first uncollateralized the asset then withdraw.
1
withdraw_obligation_collateral(
2
port_finance_variable_rate_lending::id(),
3
WITHDRAW_AMOUNT,
4
reserve.collateral.supply_pubkey,
5
sol_test_reserve.user_collateral_pubkey,
6
sol_test_reserve.pubkey,
7
test_obligation.pubkey,
8
lending_market.pubkey,
9
test_obligation.owner,
10
Some(stake_account_pubkey),
11
Some(staking_pool_pubkey),
12
)
Copied!
1
redeem_reserve_collateral(
2
port_finance_variable_rate_lending::id(),
3
COLLATERAL_AMOUNT,
4
user_collateral_token_account_pubkey,
5
user_liquidity_token_account_pubkey,
6
reserve_pubkey,
7
reserve.collateral.mint_pubkey,
8
reserve.liquidity.supply_pubkey,
9
lending_market.pubkey,
10
user_transfer_authority.pubkey(),
11
)
Copied!

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.
1
repay_obligation_liquidity(
2
port_finance_variable_rate_lending::id(),
3
liquidity_amount,
4
user_liquidity_token_account_pubkey,
5
reserve.liquidity.supply_pubkey,
6
reserve_pubkey,
7
obligation_pubkey,
8
lending_market.pubkey,
9
user_transfer_authority.pubkey(),
10
)
Copied!

Liquidation

Please refer to https://github.com/port-finance/liquidator

Flash loan

We have an instruction with the following signature for flash loan:
1
pub enum LendingInstruction {
2
// ....
3
/// Make a flash loan.
4
///
5
/// Accounts expected by this instruction:
6
///
7
/// 0. `[writable]` Source liquidity token account.
8
/// Minted by reserve liquidity mint.
9
/// Must match the reserve liquidity supply.
10
/// 1. `[writable]` Destination liquidity token account.
11
/// Minted by reserve liquidity mint.
12
/// 2. `[writable]` Reserve account.
13
/// 3. `[]` Lending market account.
14
/// 4. `[]` Derived lending market authority.
15
/// 5. `[]` Flash loan receiver program account.
16
/// Must implement an instruction that has tag of 0 and a signature of `(repay_amount: u64)`
17
/// This instruction must return the amount to the source liquidity account.
18
/// 6. `[]` Token program id.
19
/// 7. `[writable]` Flash loan fee receiver account.
20
/// Must match the reserve liquidity fee receiver.
21
/// 8. `[writable]` Host fee receiver.
22
/// .. `[any]` Additional accounts expected by the receiving program's `ReceiveFlashLoan` instruction.
23
FlashLoan {
24
/// The amount that is to be borrowed
25
amount: u64,
26
},
27
}
Copied!
In the implementation, we do the following in order:
  1. 1.
    Perform safety checks and calculate fees
  2. 2.
    Transfer amount from the source liquidity account to the destination liquidity account
  3. 3.
    Call the ReceiveFlashLoan 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.
  4. 4.
    Check that the returned amount with the fee is in the reserve account after the completion of 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.
1
pub enum FlashLoanReceiverInstruction {
2
3
/// Receive a flash loan and perform user-defined operation and finally return the fund back.
4
///
5
/// Accounts expected:
6
///
7
/// 0. `[writable]` Source liquidity (matching the destination from above).
8
/// 1. `[writable]` Destination liquidity (matching the source from above).
9
/// 2. `[]` Token program id
10
/// .. `[any]` Additional accounts provided to the lending program's `FlashLoan` instruction above.
11
ReceiveFlashLoan {
12
// Amount that is loaned to the receiver program
13
amount: u64
14
}
15
}
Copied!
You can view a sample implementation here.
Last modified 1mo ago