Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
0xodia committed Apr 3, 2024
1 parent 8e1b892 commit 0b9fd5c
Show file tree
Hide file tree
Showing 65 changed files with 1,732 additions and 4,475 deletions.
9 changes: 7 additions & 2 deletions solend-lite/src/stores/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DEBUG_MODE } from 'common/config';
import { fetchTokensInfo, TokenMetadata } from '@solendprotocol/solend-sdk';
import { getTokenInfosFromMetadata, TokenInfo, TokenMetadata } from '@solendprotocol/solend-sdk';
import { atom } from 'jotai';
import { unqiueAssetsAtom } from './pools';
import { connectionAtom } from './settings';
Expand All @@ -15,7 +15,12 @@ export const loadMetadataAtom = atom(
const connection = get(connectionAtom);

if (mints.length) {
set(metadataAtom, await fetchTokensInfo(mints, connection, DEBUG_MODE));
const tokenInfoArray = await getTokenInfosFromMetadata(mints, connection, DEBUG_MODE)
const tokenInfo = tokenInfoArray.reduce((acc, t) => {
acc[t.address] = t;
return acc;
}, {} as Record<string, TokenInfo>);
set(metadataAtom, tokenInfo);
}
},
);
11 changes: 6 additions & 5 deletions solend-lite/src/stores/pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import { configAtom } from './config';
import BigNumber from 'bignumber.js';
import {
createObligationAddress,
ReserveType,
fetchPools,
getReservesOfPool,
parseLendingMarket,
parseRateLimiter,
PoolType,
ReserveType
} from '@solendprotocol/solend-sdk';
import { DEBUG_MODE, PROGRAM_ID } from 'common/config';
import { atomWithRefresh } from './shared';
Expand Down Expand Up @@ -118,6 +118,7 @@ export const poolsWithMetaDataAtom = atom((get) => {
const metadata = get(metadataAtom);
const pools = get(poolsAtom);

console.log(metadata);
return Object.fromEntries(
Object.values(pools).map((p) => [
p.address,
Expand All @@ -130,7 +131,7 @@ export const poolsWithMetaDataAtom = atom((get) => {
reserves: p.reserves.map((r) => ({
...r,
symbol: metadata[r.mintAddress]?.symbol,
logo: metadata[r.mintAddress]?.logoUri,
logo: metadata[r.mintAddress]?.logoURI,
})),
},
]),
Expand Down Expand Up @@ -183,11 +184,11 @@ export const selectedPoolAtom = atom(
reserves: selectedPool.reserves.map((r) => {
const addressString = r.mintAddress;
const tokenMetadata = metadata[addressString];

console.log(tokenMetadata);
return {
...r,
symbol: tokenMetadata?.symbol,
logo: tokenMetadata?.logoUri,
logo: tokenMetadata?.logoURI,
};
}),
};
Expand Down Expand Up @@ -216,9 +217,9 @@ export const selectedPoolAtom = atom(
getReservesOfPool(
new PublicKey(newSelectedPoolAddress),
connection,
switchboardProgram,
PROGRAM_ID,
currentSlot,
switchboardProgram,
DEBUG_MODE,
);

Expand Down
4 changes: 3 additions & 1 deletion solend-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const { wallet } = useWallet();
const solendWallet = await SolendWallet.initialize(wallet, connection);

// Claim rewards
const mndeRewards = solendWallet.rewards["MNDEFzGvMt87ueuHvVU9VcTqsAP5b3fTGPsHuuPA5ey"];
const mndeRewards =
solendWallet.rewards["MNDEFzGvMt87ueuHvVU9VcTqsAP5b3fTGPsHuuPA5ey"];
console.log(
"Claimable rewards:",
mndeRewards.claimableAmount / 10 ** mndeRewards.decimals
Expand Down Expand Up @@ -118,6 +119,7 @@ Due to transaction size limits of Solana, a user with a high amount of positions
Partner rewards and liquidity mining are not present on devnet.

## Publishing

`package.json` is configured for monorepo development alongside other packages. To publish the sdk, use `yarn release` to run scripts optimizing for release. Package has been marked private to prevent accidently publish with `yarn publish`.

```
Expand Down
207 changes: 33 additions & 174 deletions solend-sdk/__tests__/market.test.ts
Original file line number Diff line number Diff line change
@@ -1,191 +1,50 @@
import { Connection, Keypair, Transaction, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import BN from 'bn.js';
import * as anchor from '@project-serum/anchor';
import { parseReserve, SolendAction, SolendMarket, SolendWallet } from "../src";
import {
Connection,
PublicKey,
} from "@solana/web3.js";
import { parseObligation } from "../src";

jest.setTimeout(50_000);

describe("calculate", function () {
it ('parses reserves', async function() {

const connection = new Connection('https://api.mainnet-beta.solana.com', {
commitment: "finalized",
});

const reserve = await connection.getAccountInfo(new PublicKey('CCpirWrgNuBVLdkP2haxLTbD6XqEgaYuVXixbbpxUB6'));

const parsedReserveData = parseReserve(new PublicKey('CCpirWrgNuBVLdkP2haxLTbD6XqEgaYuVXixbbpxUB6'), reserve!);
expect(parsedReserveData!.info.liquidity.pythOracle).not.toBeUndefined();
expect(parsedReserveData!.info.liquidity.switchboardOracle).not.toBeUndefined();
});

it("reads wallet", async function () {
const connection = new Connection('https://api.mainnet-beta.solana.com', {
commitment: "finalized",
});

const keypair = Keypair.generate();
const wallet = new anchor.Wallet(keypair);
const solendWallet = await SolendWallet.initialize(
wallet,
connection,
);

const [setupIxs, claimIxs] = await solendWallet.getClaimAllIxs();

expect([...setupIxs, ...claimIxs].length).toEqual(0);
});

it("reads solend main market", async function () {
const connection = new Connection('https://api.mainnet-beta.solana.com', {
commitment: "finalized",
});

const market = await SolendMarket.initialize(
connection
);
await market.loadReserves();
await market.loadRewards();
const reserve = market.reserves.find(res => res.config.liquidityToken.symbol === 'USDC');

expect(reserve!.stats!.decimals).toEqual(6);
expect(reserve!.stats!.protocolTakeRate).toBeLessThanOrEqual(1);
});

it("reads solend devnet", async function () {
const connection = new Connection('https://api.devnet.solana.com', {
commitment: "finalized",
});

const market = await SolendMarket.initialize(
connection,
'devnet'
);
await market.loadReserves();
await market.loadRewards();
const reserve = market.reserves.find(res => res.config.liquidityToken.symbol === 'USDC');

expect(reserve!.stats!.decimals).toEqual(6);
expect(reserve!.stats!.protocolTakeRate).toBeLessThanOrEqual(1);
});

it("reads solend invictus market", async function () {
const connection = new Connection('https://api.mainnet-beta.solana.com', {
commitment: "finalized",
});

const market = await SolendMarket.initialize(
connection,
'production',
'5i8SzwX2LjpGUxLZRJ8EiYohpuKgW2FYDFhVjhGj66P1',
describe("check", function () {
it("parses obligation in both formats", async function () {
const zstdEncodedObligationData = Buffer.from(
"KLUv/QBYLQoAdBEBWZHIBwAAAAABM7MexO/4+iia6oyVTAFjLi12SQjOVE1oZb3vERv/YSsChcZ+Ktz1mRBrkyRk1BwBK+MRLDs2kN6sQJrkE/068ae2QKj1GoAIagEAUiLO4HOtZQD9iDA+OBRghg/ZNGo1t+NsujMBAQFsp+C1qN6toMtzc12/wVf8DODwcnh4K7b1j6DEElE6V8esAGvgw8KAVqMAbcvwdUngHTQhP3KK2EcTtbcXhy+sOBPKuWOvMSyN283/9ZTFWnxCDgDvyiK06ZHhKd04ANAh+wRuI+ENjqkOBTFIeS9ID9NTKr93sO+769EA4idmIqc8x1Enq7K/tz3DwaHWY5RMvzQOIJZ5G4lBjXXPHRJX7vzRwA8SAM1yFQDEt5APgChdqyVY2VtAAS80LATg+oCsBCUWCFQgLRhoqHwAcXWBMw==",
"base64"
);
await market.loadReserves();
await market.loadRewards();
const reserve = market.reserves.find(res => res.config.liquidityToken.symbol === 'USDC');
expect((await reserve!.totalBorrowAPY()).rewards).toEqual([]);
expect(reserve!.stats!.optimalUtilizationRate).toEqual(0.8);
});

it("reads permissionless", async function () {
const connection = new Connection('https://api.mainnet-beta.solana.com', {
commitment: "finalized",
});

const market = await SolendMarket.initialize(
connection,
'production',
'Ckya2fwCXDqTUg9fnWbajR6YLcSfQmPxxy5MyAoZXgyb',
const base64EncodedObligationData = Buffer.from(
"AVmRyAcAAAAAATOzHsTv+PoomuqMlUwBYy4tdkkIzlRNaGW97xEb/2ErAoXGfirc9ZkQa5MkZNQcASvjESw7NpDerECa5BP9OvGntkCo9RqACGoBAAAAAAAAUiLO4HOtZQAAAAAAAAAAAP2IMD44FGCGDwEAAAAAAADZNGo1t+NsujMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBbKfgtajeraDLc3Ndv8FX/Azg8HJ4eCu29Y+gxBJROlfHrAAAAAAAAGvgw8KAVqMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG3L8HVJ4B00IT9yithHE7W3F4cvrDgTyrljrzEsjdvN//WUxVp8Qg4AAAAAAAAAAO/KIrTpkeEp3TgAAAAAAABSIs7gc61lAAAAAAAAAAAAAAAAAAAAAABty/B1SeAdNCE/corYRxO1txeHL6w4E8q5Y68xLI3bzf/1lMVafEIOAAAAAAAAAADvyiK06ZHhKd04AAAAAAAAUiLO4HOtZQAAAAAAAAAAANAh+wQAAAAAbiPhDY6pDgUxAQAAAAAAAAAAAAAAAAAASHkvSA/TUyq/d7Dvu+vRAOInZiKnPMdRJ6uyv7c9w8Gh1mOUTL80DgAAAAAAAAAAIJZ5G4lBjXXPIfsEAAAAAB0SV+780cAPMQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"base64"
);
await market.loadReserves();
await market.loadRewards();
const reserve = market.reserves.find(res => res.config.liquidityToken.symbol === 'SLND');
expect((await reserve!.totalBorrowAPY()).rewards).toEqual([]);
expect(reserve!.stats!.optimalUtilizationRate).toEqual(0.8);
});

it("performs a deposit", async function () {
const connection = new Connection('https://api.devnet.solana.com', {
const obligationPubkey = new PublicKey(
"FfRLBU1gHm3MyqJ3KX6dBnsxxJtwVGCwKFwJrfDnceWN"
);
const connection = new Connection("https://api.mainnet-beta.solana.com", {
commitment: "finalized",
});

const depositAmount = new BN("1000");

const account = Keypair.generate();

const signature = await connection.requestAirdrop(account.publicKey, LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature);

const solendAction = await SolendAction.buildDepositTxns(
connection,
depositAmount,
"SOL",
account.publicKey,
"devnet"
const zstdEncodedObligation = await connection.getAccountInfo(
obligationPubkey
);

const sendTransaction = async (txn: Transaction, connection: Connection) => {
const { blockhash } = await connection.getRecentBlockhash();
txn.recentBlockhash = blockhash;
txn.feePayer = account.publicKey;
txn.sign(account);
return connection.sendRawTransaction(txn.serialize());
}

const txHash = await solendAction.sendTransactions(sendTransaction);

await connection.confirmTransaction(txHash, 'finalized');

const market = await SolendMarket.initialize(
connection,
'devnet',
zstdEncodedObligation!.data = zstdEncodedObligationData;
const base64EncodedObligation = await connection.getAccountInfo(
obligationPubkey
);
base64EncodedObligation!.data = base64EncodedObligationData;

const obligation = await market.fetchObligationByWallet(account.publicKey);

expect(obligation!.deposits[0].amount === depositAmount)
});

// TODO update to a non-primary pool after another pool deployed to devnet
it("performs a deposit to specific pool", async function () {
const connection = new Connection('https://api.devnet.solana.com', {
commitment: "finalized",
});

const depositAmount = new BN("1000");

const account = Keypair.generate();

const signature = await connection.requestAirdrop(account.publicKey, LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature);

const solendAction = await SolendAction.buildDepositTxns(
connection,
depositAmount,
"SOL",
account.publicKey,
"devnet",
new PublicKey("GvjoVKNjBvQcFaSKUW1gTE7DxhSpjHbE69umVR5nPuQp"),
const parsedzstdEncodedObligation = parseObligation(
obligationPubkey,
zstdEncodedObligation!,
"base64+zstd"
);

const sendTransaction = async (txn: Transaction, connection: Connection) => {
const { blockhash } = await connection.getRecentBlockhash();
txn.recentBlockhash = blockhash;
txn.feePayer = account.publicKey;
txn.sign(account);
return connection.sendRawTransaction(txn.serialize());
}

const txHash = await solendAction.sendTransactions(sendTransaction);

await connection.confirmTransaction(txHash, 'finalized');

const market = await SolendMarket.initialize(
connection,
'devnet',
const parsedbase64EncodedObligation = parseObligation(
obligationPubkey,
base64EncodedObligation!
);

const obligation = await market.fetchObligationByWallet(account.publicKey);

expect(obligation!.deposits[0].amount === depositAmount)
expect(parsedzstdEncodedObligation!).toMatchObject(
parsedbase64EncodedObligation!
);
});
});
22 changes: 22 additions & 0 deletions solend-sdk/__tests__/metatdata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
Connection,
PublicKey,
} from "@solana/web3.js";
import { getTokenInfosFromMetadata } from "../src";

jest.setTimeout(50_000);

describe("check", function () {
it("parses obligation in both formats", async function () {
const connection = new Connection("https://api.mainnet-beta.solana.com", {
commitment: "finalized",
});
const tokens = await getTokenInfosFromMetadata([
'n54ZwXEcLnc3o7zK48nhrLV4KTU5wWD4iq7Gvdt5tik',
'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263'
], connection)

console.log(tokens);
});
});

16 changes: 5 additions & 11 deletions solend-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solendprotocol/solend-sdk",
"version": "0.8.8",
"version": "1.0.0",
"private": true,
"main": "src/index.ts",
"module": "src/index.ts",
Expand All @@ -20,34 +20,28 @@
"release": "yarn build; ts-node ./prepublish.ts; yarn --cwd ./dist publish"
},
"dependencies": {
"@marinade.finance/marinade-ts-sdk": "^3.1.1",
"@mithraic-labs/psy-american": "^0.2.1",
"@metaplex-foundation/mpl-token-metadata": "^3.2.1",
"@metaplex-foundation/umi": "^0.9.1",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.1",
"@project-serum/anchor": "^0.24.2",
"@pythnetwork/client": "^2.5.1",
"@solana/buffer-layout": "^4.0.0",
"@solana/spl-token": "^0.3.6",
"@solana/web3.js": "^1.78.7",
"@solflare-wallet/utl-sdk": "^1.4.0",
"@switchboard-xyz/sbv2-lite": "^0.2.4",
"@types/bn.js": "^5.1.1",
"axios": "^0.24.0",
"bignumber.js": "^9.0.2",
"bn.js": "^5.2.0",
"buffer": "^6.0.3",
"buffer-layout": "^1.2.0",
"fzstd": "^0.1.0",
"hot-shots": "^9.3.0",
"isomorphic-fetch": "^3.0.0",
"jsbi": "^4.3.0",
"typedoc-plugin-cname": "^1.0.1"
},
"devDependencies": {
"@jup-ag/core": "^2.0.0-beta.3",
"@orca-so/sdk": "^1.2.25",
"@types/bs58": "^4.0.1",
"@types/bn.js": "^5.1.1",
"@types/jest": "^29.2.3",
"@types/node": "^20.8.6",
"@types/node-fetch": "^2.6.2",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"eslint": "^8.50.0",
Expand Down
Loading

0 comments on commit 0b9fd5c

Please sign in to comment.