Skip to content
This repository has been archived by the owner on Oct 14, 2022. It is now read-only.

Commit

Permalink
Merge pull request #33 from solendprotocol/deprecate-config
Browse files Browse the repository at this point in the history
Deprecate config
  • Loading branch information
solendvega authored Jun 23, 2022
2 parents 53ac85f + 1d3b646 commit 03a405b
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 94 deletions.
30 changes: 30 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "liquidate",
"type": "node",
"request": "launch",
"args": [
"${workspaceRoot}/src/liquidate.ts",
],
"runtimeArgs": [
"-r",
"ts-node/register",
"-r",
"tsconfig-paths/register"
],
"cwd": "${workspaceRoot}",
"internalConsoleOptions": "openOnSessionStart",
"runtimeExecutable": "/usr/local/bin/node",
"outputCapture": "std",
"env": {
"APP": "production",
"THROTTLE": "1000"
}
},
]
}
13 changes: 7 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-loop-func */
import got from 'got';
import { Config } from 'global';
import { MarketConfig } from 'global';

export const OBLIGATION_LEN = 1300;
export const RESERVE_LEN = 619;
Expand All @@ -25,7 +25,7 @@ function getApp() {
return app;
}

export async function getConfig(): Promise<Config> {
export async function getMarkets(): Promise<MarketConfig[]> {
let attemptCount = 0;
let backoffFactor = 1;
const maxAttempt = 10;
Expand All @@ -37,14 +37,15 @@ export async function getConfig(): Promise<Config> {
backoffFactor *= 2;
}
attemptCount += 1;
const resp = await got(`https://api.solend.fi/v1/config?deployment=${getApp()}`, { json: true });
return resp.body as Config;
const resp = await got(`https://api.solend.fi/v1/markets/configs?scope=solend&deployment=${getApp()}`, { json: true });
const data = resp.body as MarketConfig[];
return data;
} catch (error) {
console.error('error fetching /v1/config: ', error);
console.error('error fetching /v1/markets/configs ', error);
}
} while (attemptCount < maxAttempt);

throw new Error('failed to fetch /v1/config');
throw new Error('failed to fetch /v1/markets/configs');
}

export const network = getApp();
Expand Down
37 changes: 32 additions & 5 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
export interface Config {
programID: string;
assets: Asset[];
oracles: Oracles;
markets: Market[];
export interface MarketConfig {
name: string;
isPrimary: boolean;
description: string;
creator: string;
address: string;
authorityAddress: string;
owner: string;
reserves: MarketConfigReserve[];
}

export interface MarketConfigReserve {
liquidityToken: LiquidityToken;
pythOracle: string;
switchboardOracle: string;
address: string;
collateralMintAddress: string;
collateralSupplyAddress: string;
liquidityAddress: string;
liquidityFeeReceiverAddress: string;
userSupplyCap: number;
}

export interface LiquidityToken {
coingeckoID: string;
decimals: number;
logo: string;
mint: string;
name: string;
symbol: string;
volume24h: string;
}

export interface Asset {
name: string;
symbol: string;
Expand Down
39 changes: 21 additions & 18 deletions src/libs/actions/liquidateAndRedeem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ import {
Transaction,
TransactionInstruction,
} from '@solana/web3.js';
import { getTokenInfo } from 'libs/utils';
import {
getTokenInfoFromMarket,
} from 'libs/utils';
import { findWhere, map } from 'underscore';
import { refreshReserveInstruction } from 'models/instructions/refreshReserve';
import { LiquidateObligationAndRedeemReserveCollateral } from 'models/instructions/LiquidateObligationAndRedeemReserveCollateral';
import { refreshObligationInstruction } from 'models/instructions/refreshObligation';
import { Config, Market } from 'global';
import { MarketConfig, MarketConfigReserve } from 'global';

export const liquidateAndRedeem = async (
connection: Connection,
config: Config,
payer: Account,
liquidityAmount: number | string,
repayTokenSymbol: string,
withdrawTokenSymbol: string,
lendingMarket: Market,
lendingMarket: MarketConfig,
obligation: any,
) => {
const ixs: TransactionInstruction[] = [];
Expand All @@ -31,30 +32,25 @@ export const liquidateAndRedeem = async (
const borrowReserves = map(obligation.info.borrows, (borrow) => borrow.borrowReserve);
const uniqReserveAddresses = [...new Set<String>(map(depositReserves.concat(borrowReserves), (reserve) => reserve.toString()))];
uniqReserveAddresses.forEach((reserveAddress) => {
const reserveInfo = findWhere(lendingMarket!.reserves, {
const reserveInfo: MarketConfigReserve = findWhere(lendingMarket!.reserves, {
address: reserveAddress,
});
const oracleInfo = findWhere(config.oracles.assets, {
asset: reserveInfo!.asset,
});
const refreshReserveIx = refreshReserveInstruction(
config,
new PublicKey(reserveAddress),
new PublicKey(oracleInfo!.priceAddress),
new PublicKey(oracleInfo!.switchboardFeedAddress),
new PublicKey(reserveInfo.pythOracle),
new PublicKey(reserveInfo.switchboardOracle),
);
ixs.push(refreshReserveIx);
});

const refreshObligationIx = refreshObligationInstruction(
config,
obligation.pubkey,
depositReserves,
borrowReserves,
);
ixs.push(refreshObligationIx);

const repayTokenInfo = getTokenInfo(config, repayTokenSymbol);
const repayTokenInfo = getTokenInfoFromMarket(lendingMarket, repayTokenSymbol);

// get account that will be repaying the reserve liquidity
const repayAccount = await Token.getAssociatedTokenAddress(
Expand All @@ -64,9 +60,17 @@ export const liquidateAndRedeem = async (
payer.publicKey,
);

const repayReserve = findWhere(lendingMarket.reserves, { asset: repayTokenSymbol });
const withdrawReserve = findWhere(lendingMarket.reserves, { asset: withdrawTokenSymbol });
const withdrawTokenInfo = getTokenInfo(config, withdrawTokenSymbol);
const reserveSymbolToReserveMap = new Map<string, MarketConfigReserve>(
lendingMarket.reserves.map((reserve) => [reserve.liquidityToken.symbol, reserve]),
);

const repayReserve: MarketConfigReserve | undefined = reserveSymbolToReserveMap.get(repayTokenSymbol);
const withdrawReserve: MarketConfigReserve | undefined = reserveSymbolToReserveMap.get(withdrawTokenSymbol);
const withdrawTokenInfo = getTokenInfoFromMarket(lendingMarket, withdrawTokenSymbol);

if (!withdrawReserve || !repayReserve) {
throw new Error('reserves are not identified');
}

const rewardedWithdrawalCollateralAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
Expand All @@ -81,7 +85,7 @@ export const liquidateAndRedeem = async (
const createUserCollateralAccountIx = Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
withdrawReserve.collateralMintAddress,
new PublicKey(withdrawReserve.collateralMintAddress),
rewardedWithdrawalCollateralAccount,
payer.publicKey,
payer.publicKey,
Expand Down Expand Up @@ -112,7 +116,6 @@ export const liquidateAndRedeem = async (

ixs.push(
LiquidateObligationAndRedeemReserveCollateral(
config,
liquidityAmount,
repayAccount,
rewardedWithdrawalCollateralAccount,
Expand Down
43 changes: 22 additions & 21 deletions src/libs/pyth.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { findWhere } from 'underscore';
import { parsePriceData } from '@pythnetwork/client';
import {
AggregatorState,
} from '@switchboard-xyz/switchboard-api';
import SwitchboardProgram from '@switchboard-xyz/sbv2-lite';
import { Connection, PublicKey } from '@solana/web3.js';
import BigNumber from 'bignumber.js';
import { Config, OracleAsset, Reserve } from 'global';
import { MarketConfig, MarketConfigReserve } from 'global';

const NULL_ORACLE = 'nu11111111111111111111111111111111111111111';
const SWITCHBOARD_V1_ADDRESS = 'DtmE9D2CSB4L5D6A15mraeEjrGMm6auWVzgaD8hK2tZM';
const SWITCHBOARD_V2_ADDRESS = 'SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f';

let switchboardV2: SwitchboardProgram | undefined;

async function getTokenOracleData(
connection: Connection,
config: Config,
oracles: OracleAsset[],
reserve: Reserve,
) {
export type TokenOracleData = {
symbol: string;
reserveAddress: string;
mintAddress: string;
decimals: BigNumber;
price: BigNumber;
};

async function getTokenOracleData(connection: Connection, reserve: MarketConfigReserve) {
let price;
const oracle = findWhere(oracles, { asset: reserve.asset });
const oracle = {
priceAddress: reserve.pythOracle,
switchboardFeedAddress: reserve.switchboardOracle,
};

if (oracle.priceAddress && oracle.priceAddress !== NULL_ORACLE) {
const pricePublicKey = new PublicKey(oracle.priceAddress);
const result = await connection.getAccountInfo(pricePublicKey);
Expand All @@ -44,21 +50,16 @@ async function getTokenOracleData(
}
}

const assetConfig = findWhere(config.assets, { symbol: oracle.asset });

return {
symbol: oracle.asset,
symbol: reserve.liquidityToken.symbol,
reserveAddress: reserve.address,
mintAddress: assetConfig.mintAddress,
decimals: new BigNumber(10 ** assetConfig.decimals),
mintAddress: reserve.liquidityToken.mint,
decimals: new BigNumber(10 ** reserve.liquidityToken.decimals),
price: new BigNumber(price!),
};
} as TokenOracleData;
}

export async function getTokensOracleData(connection: Connection, config: Config, reserves) {
const promises: any = [];
const oracles = config.oracles.assets;
reserves.forEach((reserve) => { promises.push(getTokenOracleData(connection, config, oracles, reserve)); });
const results = await Promise.all(promises);
return results;
export async function getTokensOracleData(connection: Connection, market: MarketConfig) {
const promises: Promise<any>[] = market.reserves.map((reserve) => getTokenOracleData(connection, reserve));
return Promise.all(promises);
}
Loading

0 comments on commit 03a405b

Please sign in to comment.