2022-05-09 23:03:46 -07:00
|
|
|
import { BN } from '@project-serum/anchor';
|
2022-04-12 07:19:58 -07:00
|
|
|
import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes';
|
2022-06-21 10:14:18 -07:00
|
|
|
import { PythHttpClient } from '@pythnetwork/client';
|
2022-04-08 08:17:26 -07:00
|
|
|
import { PublicKey } from '@solana/web3.js';
|
2022-06-21 03:38:01 -07:00
|
|
|
import { nativeI80F48ToUi } from '../utils';
|
|
|
|
import { I80F48, I80F48Dto, ZERO_I80F48 } from './I80F48';
|
2022-04-02 23:57:45 -07:00
|
|
|
|
2022-06-02 10:30:39 -07:00
|
|
|
export const QUOTE_DECIMALS = 6;
|
|
|
|
|
2022-06-21 03:38:01 -07:00
|
|
|
type OracleConfig = {
|
|
|
|
confFilter: I80F48Dto;
|
|
|
|
};
|
|
|
|
|
2022-04-02 23:57:45 -07:00
|
|
|
export class Bank {
|
2022-04-12 07:19:58 -07:00
|
|
|
public name: string;
|
2022-04-02 23:57:45 -07:00
|
|
|
public depositIndex: I80F48;
|
|
|
|
public borrowIndex: I80F48;
|
2022-05-02 09:26:25 -07:00
|
|
|
public indexedTotalDeposits: I80F48;
|
2022-05-25 17:28:14 -07:00
|
|
|
public indexedTotalBorrows: I80F48;
|
2022-06-21 03:38:01 -07:00
|
|
|
public maxRate: I80F48;
|
|
|
|
public rate0: I80F48;
|
|
|
|
public rate1: I80F48;
|
|
|
|
public util0: I80F48;
|
|
|
|
public util1: I80F48;
|
2022-06-21 10:14:18 -07:00
|
|
|
public price: number;
|
2022-04-02 23:57:45 -07:00
|
|
|
|
|
|
|
static from(
|
|
|
|
publicKey: PublicKey,
|
|
|
|
obj: {
|
2022-04-12 07:19:58 -07:00
|
|
|
name: number[];
|
2022-04-02 23:57:45 -07:00
|
|
|
group: PublicKey;
|
|
|
|
mint: PublicKey;
|
|
|
|
vault: PublicKey;
|
|
|
|
oracle: PublicKey;
|
2022-06-21 03:38:01 -07:00
|
|
|
oracleConfig: OracleConfig;
|
2022-04-02 23:57:45 -07:00
|
|
|
depositIndex: I80F48Dto;
|
|
|
|
borrowIndex: I80F48Dto;
|
|
|
|
indexedTotalDeposits: I80F48Dto;
|
|
|
|
indexedTotalBorrows: I80F48Dto;
|
2022-05-09 23:03:46 -07:00
|
|
|
lastUpdated: BN;
|
|
|
|
util0: I80F48Dto;
|
|
|
|
rate0: I80F48Dto;
|
|
|
|
util1: I80F48Dto;
|
|
|
|
rate1: I80F48Dto;
|
|
|
|
maxRate: I80F48Dto;
|
|
|
|
collectedFeesNative: I80F48Dto;
|
|
|
|
loanOriginationFeeRate: I80F48Dto;
|
|
|
|
loanFeeRate: I80F48Dto;
|
2022-04-02 23:57:45 -07:00
|
|
|
maintAssetWeight: I80F48Dto;
|
|
|
|
initAssetWeight: I80F48Dto;
|
|
|
|
maintLiabWeight: I80F48Dto;
|
|
|
|
initLiabWeight: I80F48Dto;
|
|
|
|
liquidationFee: I80F48Dto;
|
|
|
|
dust: Object;
|
|
|
|
tokenIndex: number;
|
2022-05-27 05:52:03 -07:00
|
|
|
mintDecimals: number;
|
2022-04-02 23:57:45 -07:00
|
|
|
},
|
|
|
|
) {
|
|
|
|
return new Bank(
|
|
|
|
publicKey,
|
2022-04-12 07:19:58 -07:00
|
|
|
obj.name,
|
2022-04-02 23:57:45 -07:00
|
|
|
obj.group,
|
|
|
|
obj.mint,
|
|
|
|
obj.vault,
|
|
|
|
obj.oracle,
|
2022-06-21 03:38:01 -07:00
|
|
|
obj.oracleConfig,
|
2022-04-02 23:57:45 -07:00
|
|
|
obj.depositIndex,
|
|
|
|
obj.borrowIndex,
|
|
|
|
obj.indexedTotalDeposits,
|
|
|
|
obj.indexedTotalBorrows,
|
2022-05-09 23:03:46 -07:00
|
|
|
obj.lastUpdated,
|
|
|
|
obj.util0,
|
|
|
|
obj.rate0,
|
|
|
|
obj.util1,
|
|
|
|
obj.rate1,
|
|
|
|
obj.maxRate,
|
|
|
|
obj.collectedFeesNative,
|
|
|
|
obj.loanOriginationFeeRate,
|
|
|
|
obj.loanFeeRate,
|
2022-04-02 23:57:45 -07:00
|
|
|
obj.maintAssetWeight,
|
|
|
|
obj.initAssetWeight,
|
|
|
|
obj.maintLiabWeight,
|
|
|
|
obj.initLiabWeight,
|
|
|
|
obj.liquidationFee,
|
|
|
|
obj.dust,
|
|
|
|
obj.tokenIndex,
|
2022-05-27 05:52:03 -07:00
|
|
|
obj.mintDecimals,
|
2022-04-02 23:57:45 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public publicKey: PublicKey,
|
2022-04-12 07:19:58 -07:00
|
|
|
name: number[],
|
2022-04-07 09:58:42 -07:00
|
|
|
public group: PublicKey,
|
|
|
|
public mint: PublicKey,
|
2022-04-02 23:57:45 -07:00
|
|
|
public vault: PublicKey,
|
2022-04-03 07:02:14 -07:00
|
|
|
public oracle: PublicKey,
|
2022-06-21 03:38:01 -07:00
|
|
|
oracleConfig: OracleConfig,
|
2022-04-02 23:57:45 -07:00
|
|
|
depositIndex: I80F48Dto,
|
|
|
|
borrowIndex: I80F48Dto,
|
|
|
|
indexedTotalDeposits: I80F48Dto,
|
|
|
|
indexedTotalBorrows: I80F48Dto,
|
2022-06-21 10:14:18 -07:00
|
|
|
lastUpdated: BN,
|
2022-05-09 23:03:46 -07:00
|
|
|
util0: I80F48Dto,
|
|
|
|
rate0: I80F48Dto,
|
|
|
|
util1: I80F48Dto,
|
|
|
|
rate1: I80F48Dto,
|
2022-06-21 03:38:01 -07:00
|
|
|
maxRate: I80F48Dto,
|
|
|
|
collectedFeesNative: I80F48Dto,
|
|
|
|
loanOriginationFeeRate: I80F48Dto,
|
|
|
|
loanFeeRate: I80F48Dto,
|
2022-04-02 23:57:45 -07:00
|
|
|
maintAssetWeight: I80F48Dto,
|
|
|
|
initAssetWeight: I80F48Dto,
|
|
|
|
maintLiabWeight: I80F48Dto,
|
|
|
|
initLiabWeight: I80F48Dto,
|
|
|
|
liquidationFee: I80F48Dto,
|
|
|
|
dust: Object,
|
|
|
|
public tokenIndex: number,
|
2022-05-27 05:52:03 -07:00
|
|
|
public mintDecimals: number,
|
2022-04-02 23:57:45 -07:00
|
|
|
) {
|
2022-04-12 07:19:58 -07:00
|
|
|
this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0];
|
2022-04-02 23:57:45 -07:00
|
|
|
this.depositIndex = I80F48.from(depositIndex);
|
|
|
|
this.borrowIndex = I80F48.from(borrowIndex);
|
2022-05-02 09:26:25 -07:00
|
|
|
this.indexedTotalDeposits = I80F48.from(indexedTotalDeposits);
|
2022-05-25 17:28:14 -07:00
|
|
|
this.indexedTotalBorrows = I80F48.from(indexedTotalBorrows);
|
2022-06-21 03:38:01 -07:00
|
|
|
this.maxRate = I80F48.from(maxRate);
|
|
|
|
this.util0 = I80F48.from(util0);
|
|
|
|
this.rate0 = I80F48.from(rate0);
|
|
|
|
this.util1 = I80F48.from(util1);
|
|
|
|
this.rate1 = I80F48.from(rate1);
|
2022-06-21 10:14:18 -07:00
|
|
|
this.price = undefined;
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
toString(): string {
|
|
|
|
return `Bank ${
|
|
|
|
this.tokenIndex
|
|
|
|
} deposit index - ${this.depositIndex.toNumber()}, borrow index - ${this.borrowIndex.toNumber()}`;
|
|
|
|
}
|
2022-06-21 03:38:01 -07:00
|
|
|
|
|
|
|
nativeDeposits(): I80F48 {
|
|
|
|
return this.indexedTotalDeposits.mul(this.depositIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
nativeBorrows(): I80F48 {
|
|
|
|
return this.indexedTotalBorrows.mul(this.borrowIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
uiDeposits(): number {
|
|
|
|
return nativeI80F48ToUi(
|
|
|
|
this.indexedTotalDeposits.mul(this.depositIndex),
|
|
|
|
this.mintDecimals,
|
|
|
|
).toNumber();
|
|
|
|
}
|
|
|
|
|
|
|
|
uiBorrows(): number {
|
|
|
|
return nativeI80F48ToUi(
|
|
|
|
this.indexedTotalBorrows.mul(this.borrowIndex),
|
|
|
|
this.mintDecimals,
|
|
|
|
).toNumber();
|
|
|
|
}
|
|
|
|
|
|
|
|
getBorrowRate(): I80F48 {
|
|
|
|
const totalBorrows = this.nativeBorrows();
|
|
|
|
const totalDeposits = this.nativeDeposits();
|
|
|
|
|
|
|
|
if (totalDeposits.eq(ZERO_I80F48) && totalBorrows.eq(ZERO_I80F48)) {
|
|
|
|
return ZERO_I80F48;
|
|
|
|
}
|
|
|
|
if (totalDeposits.lte(totalBorrows)) {
|
|
|
|
return this.maxRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
const utilization = totalBorrows.div(totalDeposits);
|
|
|
|
if (utilization.gt(this.util1)) {
|
|
|
|
const extraUtil = utilization.sub(this.util1);
|
|
|
|
const slope = this.maxRate
|
|
|
|
.sub(this.rate1)
|
|
|
|
.div(I80F48.fromNumber(1).sub(this.util1));
|
|
|
|
return this.rate1.add(slope.mul(extraUtil));
|
|
|
|
} else if (utilization.gt(this.util0)) {
|
|
|
|
const extraUtil = utilization.sub(this.util0);
|
|
|
|
const slope = this.maxRate
|
|
|
|
.sub(this.rate0)
|
|
|
|
.div(I80F48.fromNumber(1).sub(this.util0));
|
|
|
|
return this.rate0.add(slope.mul(extraUtil));
|
|
|
|
} else {
|
|
|
|
const slope = this.rate0.div(this.util0);
|
|
|
|
return slope.mul(utilization);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getDepositRate(): I80F48 {
|
|
|
|
const borrowRate = this.getBorrowRate();
|
|
|
|
const totalBorrows = this.nativeBorrows();
|
|
|
|
const totalDeposits = this.nativeDeposits();
|
|
|
|
|
|
|
|
if (totalDeposits.eq(ZERO_I80F48) && totalBorrows.eq(ZERO_I80F48)) {
|
|
|
|
return ZERO_I80F48;
|
|
|
|
} else if (totalDeposits.eq(ZERO_I80F48)) {
|
|
|
|
return this.maxRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
const utilization = totalBorrows.div(totalDeposits);
|
|
|
|
return utilization.mul(borrowRate);
|
|
|
|
}
|
2022-06-21 10:14:18 -07:00
|
|
|
|
|
|
|
async getOraclePrice(connection) {
|
|
|
|
const pythClient = new PythHttpClient(connection, this.oracle);
|
|
|
|
const data = await pythClient.getData();
|
|
|
|
|
|
|
|
return data.productPrice;
|
|
|
|
}
|
2022-04-02 23:57:45 -07:00
|
|
|
}
|
|
|
|
|
2022-04-03 08:26:45 -07:00
|
|
|
export class MintInfo {
|
|
|
|
static from(
|
|
|
|
publicKey: PublicKey,
|
|
|
|
obj: {
|
|
|
|
mint: PublicKey;
|
2022-06-27 02:27:17 -07:00
|
|
|
banks: PublicKey[];
|
|
|
|
vaults: PublicKey[];
|
2022-04-03 08:26:45 -07:00
|
|
|
oracle: PublicKey;
|
|
|
|
addressLookupTable: PublicKey;
|
2022-06-11 04:49:45 -07:00
|
|
|
tokenIndex: number;
|
2022-04-03 08:26:45 -07:00
|
|
|
addressLookupTableBankIndex: Number;
|
|
|
|
addressLookupTableOracleIndex: Number;
|
|
|
|
reserved: unknown;
|
|
|
|
},
|
|
|
|
) {
|
2022-06-11 04:49:45 -07:00
|
|
|
return new MintInfo(
|
|
|
|
publicKey,
|
|
|
|
obj.mint,
|
2022-06-27 02:27:17 -07:00
|
|
|
obj.banks,
|
|
|
|
obj.vaults,
|
2022-06-11 04:49:45 -07:00
|
|
|
obj.oracle,
|
|
|
|
obj.tokenIndex,
|
|
|
|
);
|
2022-04-03 08:26:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public publicKey: PublicKey,
|
2022-04-03 11:08:56 -07:00
|
|
|
public mint: PublicKey,
|
2022-06-27 02:27:17 -07:00
|
|
|
public banks: PublicKey[],
|
|
|
|
public vaults: PublicKey[],
|
2022-04-03 11:08:56 -07:00
|
|
|
public oracle: PublicKey,
|
2022-06-11 04:49:45 -07:00
|
|
|
public tokenIndex: number,
|
2022-04-03 08:26:45 -07:00
|
|
|
) {}
|
2022-06-27 02:27:17 -07:00
|
|
|
|
|
|
|
public firstBank() {
|
|
|
|
return this.banks[0];
|
|
|
|
}
|
|
|
|
public firstVault() {
|
|
|
|
return this.vaults[0];
|
|
|
|
}
|
2022-04-03 08:26:45 -07:00
|
|
|
}
|