diff --git a/solend-sdk/.gitignore b/solend-sdk/.gitignore index 1eae0cf6..8e01851b 100644 --- a/solend-sdk/.gitignore +++ b/solend-sdk/.gitignore @@ -1,2 +1,3 @@ +__tests__/scripts.test.ts dist/ node_modules/ diff --git a/solend-sdk/__tests__/scripts.test.ts b/solend-sdk/__tests__/scripts.test.ts deleted file mode 100644 index 2e5c44ed..00000000 --- a/solend-sdk/__tests__/scripts.test.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { - ComputeBudgetProgram, - Connection, - Keypair, - PublicKey, - SystemProgram, - TransactionMessage, - VersionedTransaction, - } from "@solana/web3.js"; - import { fetchPoolByAddress, fetchPools, parseObligation, SolendActionCore, toHexString } from "../src"; - import { PriceServiceConnection } from "@pythnetwork/price-service-client"; - import { PythSolanaReceiver, pythSolanaReceiverIdl } from "@pythnetwork/pyth-solana-receiver"; - import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; - import { AnchorProvider, Program } from "@coral-xyz/anchor-30"; - import { CrossbarClient, loadLookupTables, PullFeed, SB_ON_DEMAND_PID } from "@switchboard-xyz/on-demand"; -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - createAssociatedTokenAccountIdempotent, - getAssociatedTokenAddress, - TOKEN_2022_PROGRAM_ID -} from "@solana/spl-token"; -import { createDepositAndMintWrapperTokensInstruction } from '@solendprotocol/token2022-wrapper-sdk'; -import SwitchboardProgram from "@switchboard-xyz/sbv2-lite"; -import { Wallet } from "@coral-xyz/anchor"; - - jest.setTimeout(50_000); - - const connection = new Connection("https://solendf-solendf-67c7.rpcpool.com/6096fc4b-78fc-4130-a42a-e6d4b9c37813"); - const testKey = [103,129,37,223,47,99,63,95,41,95,190,254,216,11,20,217,101,217,65,173,210,207,137,44,176,136,93,84,15,87,212,78,254,157,169,160,244,214,188,162,227,202,121,33,107,191,169,212,16,93,176,86,248,238,250,64,147,23,50,212,180,223,6,63]; - - describe("runs script", function () { - it("pulls switchboard oracle", async function () { - if (testKey.length === 0) { - throw Error('Best tested with a throwaway mainnet test account.') - } - - const provider = new AnchorProvider(connection, new NodeWallet(Keypair.fromSecretKey(new Uint8Array( - testKey - ))), {}); - - const keypair = Keypair.fromSecretKey(new Uint8Array( - testKey - )); - - const ata2022 = await createAssociatedTokenAccountIdempotent( - connection, - keypair, - new PublicKey('2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo'), - keypair.publicKey, - {}, - TOKEN_2022_PROGRAM_ID - ); - - - console.log(ata2022.toBase58()); - - const ix = await createDepositAndMintWrapperTokensInstruction( - provider.publicKey, - ata2022, - new PublicKey('2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo'), - 1000, - ); - // Get the latest context - const { - value: { blockhash }, - } = await connection.getLatestBlockhashAndContext(); - - // Get Transaction Message - const message = new TransactionMessage({ - payerKey: provider.publicKey, - recentBlockhash: blockhash, - instructions: [ix], - }).compileToV0Message(); - - // Get Versioned Transaction - const vtx = new VersionedTransaction(message); - provider.wallet.signAllTransactions([vtx]); - const sig = await connection.sendRawTransaction(vtx.serialize(), {skipPreflight: true}); - console.log(sig); - await connection.confirmTransaction(sig, 'confirmed'); - }); - - - it("deposits token2022", async function () { - if (testKey.length === 0) { - throw Error('Best tested with a throwaway mainnet test account.') - } - - const provider = new AnchorProvider(connection, new NodeWallet(Keypair.fromSecretKey(new Uint8Array( - testKey - ))), {}); - - const sb = await SwitchboardProgram.loadMainnet(connection); - const currentSlot = await provider.connection.getSlot(); - const pool = await fetchPools( - [{ - name: 'main', - address: 'HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk', - authorityAddress: 'G9Ed4rdvrBypMPAz2QH6Pi9VZADkLuvo9sPvbTvHmVTU', - reserves: [], - owner: 'BownY7uPxZ5jLjBxPNvqaWa3VD9WJvwQEUYVC5sERzET', - }], - connection, - sb, - 'BLendhFh4HGnycEDDFhbeFEUYLP4fXB5tTHMoTX8Dch5', - currentSlot - ); - - console.log(pool); - console.log(pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'].reserves.find(r => r.address === 'CejQKkwbVmrzW2DYLL2N9ySD8uey44xx4PoSZ17bQJT3')!) - const action = SolendActionCore.buildDepositTxns( - pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'], - pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'].reserves.find(r => r.address === 'CejQKkwbVmrzW2DYLL2N9ySD8uey44xx4PoSZ17bQJT3')!, - connection, - '100', - provider.wallet as Wallet, - 'beta', - undefined, - undefined, - undefined, - '2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo' - ); - - const blockhash = await provider.connection.getLatestBlockhashAndContext(); - - const txns = await (await action).getTransactions(blockhash.value) - - console.log(txns); - const txnsArray = [ - txns.preLendingTxn, - txns.lendingTxn, - txns.postLendingTxn - ].filter(Boolean) as VersionedTransaction[] - // Get Versioned Transaction - provider.wallet.signAllTransactions(txnsArray); - for (const tx of txnsArray) { - const sig = await connection.sendRawTransaction(tx.serialize(), {skipPreflight: true}); - console.log(sig); - await connection.confirmTransaction(sig, 'confirmed'); - } - }); - - it("withdraws token2022", async function () { - if (testKey.length === 0) { - throw Error('Best tested with a throwaway mainnet test account.') - } - - const provider = new AnchorProvider(connection, new NodeWallet(Keypair.fromSecretKey(new Uint8Array( - testKey - ))), {}); - - const sb = await SwitchboardProgram.loadMainnet(connection); - const currentSlot = await provider.connection.getSlot(); - const pool = await fetchPools( - [{ - name: 'main', - address: 'HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk', - authorityAddress: 'G9Ed4rdvrBypMPAz2QH6Pi9VZADkLuvo9sPvbTvHmVTU', - reserves: [], - owner: 'BownY7uPxZ5jLjBxPNvqaWa3VD9WJvwQEUYVC5sERzET', - }], - connection, - sb, - 'BLendhFh4HGnycEDDFhbeFEUYLP4fXB5tTHMoTX8Dch5', - currentSlot - ); - - console.log(pool); - console.log(pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'].reserves.find(r => r.address === 'CejQKkwbVmrzW2DYLL2N9ySD8uey44xx4PoSZ17bQJT3')!) - const action = SolendActionCore.buildWithdrawTxns( - pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'], - pool['HB1cecsgnFPBfKxEDfarVtKXEARWuViJKCqztWiFB3Uk'].reserves.find(r => r.address === 'CejQKkwbVmrzW2DYLL2N9ySD8uey44xx4PoSZ17bQJT3')!, - connection, - '100', - provider.wallet as Wallet, - 'beta', - undefined, - undefined, - undefined, - undefined, - '2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo' - ); - - const blockhash = await provider.connection.getLatestBlockhashAndContext(); - - const txns = await (await action).getTransactions(blockhash.value) - - console.log(txns); - const txnsArray = [ - txns.preLendingTxn, - txns.lendingTxn, - txns.postLendingTxn - ].filter(Boolean) as VersionedTransaction[] - // Get Versioned Transaction - provider.wallet.signAllTransactions(txnsArray); - for (const tx of txnsArray) { - const sig = await connection.sendRawTransaction(tx.serialize(), {skipPreflight: true}); - console.log(sig); - await connection.confirmTransaction(sig, 'confirmed'); - } - }); - }); - \ No newline at end of file diff --git a/solend-sdk/package.json b/solend-sdk/package.json index 5577cf27..6bef58da 100644 --- a/solend-sdk/package.json +++ b/solend-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@solendprotocol/solend-sdk", - "version": "0.13.2", + "version": "0.13.5", "private": true, "main": "src/index.ts", "module": "src/index.ts", @@ -29,7 +29,7 @@ "@solana/web3.js": "=1.92.3", "@solendprotocol/token2022-wrapper-sdk": "^1.0.1", "@solflare-wallet/utl-sdk": "^1.4.0", - "@switchboard-xyz/on-demand": "^1.2.20", + "@switchboard-xyz/on-demand": "1.2.27", "@switchboard-xyz/sbv2-lite": "^0.2.4", "axios": "^0.24.0", "bignumber.js": "^9.0.2", diff --git a/solend-sdk/src/core/actions.ts b/solend-sdk/src/core/actions.ts index d69818c2..6f061a21 100644 --- a/solend-sdk/src/core/actions.ts +++ b/solend-sdk/src/core/actions.ts @@ -68,6 +68,19 @@ import { ReserveType } from "./utils"; const SOL_PADDING_FOR_INTEREST = "1000000"; +type ActionConfigType = { + environment?: EnvironmentType, + customObligationAddress?: PublicKey, + hostAta?: PublicKey, + customObligationSeed?: string, + lookupTableAddress?: PublicKey, + tipAmount?: number, + repayReserve?: ReserveType, + token2022Mint?: string, + repayToken2022Mint?: string + debug?: boolean +} + type SupportType = | "wrap" | "unwrap" @@ -180,6 +193,8 @@ export class SolendActionCore { wallet: Wallet; + debug: boolean; + repayInfo?: { userRepayTokenAccountAddress: PublicKey; userRepayCollateralAccountAddress: PublicKey; @@ -208,26 +223,29 @@ export class SolendActionCore { amount: BN, depositReserves: Array, borrowReserves: Array, - hostAta?: PublicKey, - lookupTableAccount?: AddressLookupTableAccount, - tipAmount?: number, - repayInfo?: { - userRepayTokenAccountAddress: PublicKey; - userRepayCollateralAccountAddress: PublicKey; - repayToken2022Mint?: PublicKey; - repayWrappedAta?: PublicKey; - repayMint: PublicKey; - reserveAddress: PublicKey; - }, - token2022Mint?: PublicKey, - wrappedAta?: PublicKey + config?: { + hostAta?: PublicKey, + lookupTableAccount?: AddressLookupTableAccount, + tipAmount?: number, + repayInfo?: { + userRepayTokenAccountAddress: PublicKey; + userRepayCollateralAccountAddress: PublicKey; + repayToken2022Mint?: PublicKey; + repayWrappedAta?: PublicKey; + repayMint: PublicKey; + reserveAddress: PublicKey; + }, + token2022Mint?: PublicKey, + wrappedAta?: PublicKey, + debug?: boolean; + } ) { this.programId = programId; this.connection = connection; this.publicKey = wallet.publicKey; this.amount = new BN(amount); this.positions = positions; - this.hostAta = hostAta; + this.hostAta = config?.hostAta; this.obligationAccountInfo = obligationAccountInfo; this.pool = pool; this.seed = seed; @@ -243,12 +261,14 @@ export class SolendActionCore { this.postTxnIxs = []; this.depositReserves = depositReserves; this.borrowReserves = borrowReserves; - this.lookupTableAccount = lookupTableAccount; - this.jitoTipAmount = tipAmount ?? 9000; + this.lookupTableAccount = config?.lookupTableAccount; + this.jitoTipAmount = config?.tipAmount ?? 9000; this.wallet = wallet; - this.repayInfo = repayInfo; - this.token2022Mint = token2022Mint; - this.wrappedAta = wrappedAta; + this.repayInfo = config?.repayInfo; + this.token2022Mint = config?.token2022Mint; + this.wrappedAta = config?.wrappedAta; + // temporarily default to true + this.debug = config?.debug ?? true; } static async initialize( @@ -258,21 +278,13 @@ export class SolendActionCore { amount: BN, wallet: Wallet, connection: Connection, - environment: EnvironmentType = "production", - customObligationAddress?: PublicKey, - hostAta?: PublicKey, - customObligationSeed?: string, - lookupTableAddress?: PublicKey, - tipAmount?: number, - repayReserve?: ReserveType, - token2022Mint?: string, - repayToken2022Mint?: string + config: ActionConfigType ) { - const seed = customObligationSeed ?? pool.address.slice(0, 32); - const programId = getProgramId(environment); + const seed = config.customObligationSeed ?? pool.address.slice(0, 32); + const programId = getProgramId(config.environment ?? 'production'); const obligationAddress = - customObligationAddress ?? + config.customObligationAddress ?? (await PublicKey.createWithSeed(wallet.publicKey, seed, programId)); const obligationAccountInfo = await connection.getAccountInfo( @@ -331,21 +343,21 @@ export class SolendActionCore { true ); - const lookupTableAccount = lookupTableAddress - ? (await connection.getAddressLookupTable(lookupTableAddress)).value + const lookupTableAccount = config.lookupTableAddress + ? (await connection.getAddressLookupTable(config.lookupTableAddress)).value : undefined; - const userRepayTokenAccountAddress = repayReserve + const userRepayTokenAccountAddress = config.repayReserve ? await getAssociatedTokenAddress( - new PublicKey(repayReserve.mintAddress), + new PublicKey(config.repayReserve.mintAddress), wallet.publicKey, true ) : undefined; - const userRepayCollateralAccountAddress = repayReserve + const userRepayCollateralAccountAddress = config.repayReserve ? await getAssociatedTokenAddress( - new PublicKey(repayReserve.cTokenMint), + new PublicKey(config.repayReserve.cTokenMint), wallet.publicKey, true ) @@ -366,39 +378,41 @@ export class SolendActionCore { amount, depositReserves, borrowReserves, - hostAta, - lookupTableAccount ?? undefined, - tipAmount, - repayReserve - ? { - userRepayTokenAccountAddress: userRepayTokenAccountAddress!, - userRepayCollateralAccountAddress: - userRepayCollateralAccountAddress!, - repayToken2022Mint: repayToken2022Mint - ? new PublicKey(repayToken2022Mint) - : undefined, - repayWrappedAta: repayToken2022Mint - ? getAssociatedTokenAddressSync( - new PublicKey(repayToken2022Mint), - wallet.publicKey, - true, - TOKEN_2022_PROGRAM_ID - ) - : undefined, - repayMint: new PublicKey(repayReserve.mintAddress), - reserveAddress: new PublicKey(repayReserve.address), - } - : undefined, - token2022Mint ? new PublicKey(token2022Mint) : undefined, - token2022Mint - ? getAssociatedTokenAddressSync( - new PublicKey(token2022Mint), - wallet.publicKey, - true, - TOKEN_2022_PROGRAM_ID - ) - : undefined - ); + +{ + hostAta: config.hostAta, + lookupTableAccount: lookupTableAccount ?? undefined, + tipAmount: config.tipAmount, + repayInfo: config.repayReserve + ? { + userRepayTokenAccountAddress: userRepayTokenAccountAddress!, + userRepayCollateralAccountAddress: + userRepayCollateralAccountAddress!, + repayToken2022Mint: config.repayToken2022Mint + ? new PublicKey(config.repayToken2022Mint) + : undefined, + repayWrappedAta: config.repayToken2022Mint + ? getAssociatedTokenAddressSync( + new PublicKey(config.repayToken2022Mint), + wallet.publicKey, + true, + TOKEN_2022_PROGRAM_ID + ) + : undefined, + repayMint: new PublicKey(config.repayReserve.mintAddress), + reserveAddress: new PublicKey(config.repayReserve.address), + } + : undefined, + token2022Mint: config.token2022Mint ? new PublicKey(config.token2022Mint) : undefined, + wrappedAta: config.token2022Mint + ? getAssociatedTokenAddressSync( + new PublicKey(config.token2022Mint), + wallet.publicKey, + true, + TOKEN_2022_PROGRAM_ID + ) + : undefined + }) } static async buildForgiveTxns( @@ -408,9 +422,7 @@ export class SolendActionCore { amount: string, wallet: Wallet, obligationAddress: PublicKey, - environment: EnvironmentType = "production", - lookupTableAddress?: PublicKey, - tipAmount?: number + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -419,12 +431,10 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - obligationAddress, - undefined, - undefined, - lookupTableAddress, - tipAmount + { + ...config, + customObligationAddress: obligationAddress + } ); await axn.addSupportIxs("forgive"); @@ -439,12 +449,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - obligationAddress?: PublicKey, - obligationSeed?: string, - lookupTableAddress?: PublicKey, - tipAmount?: number, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -453,14 +458,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - obligationAddress, - undefined, - obligationSeed, - lookupTableAddress, - tipAmount, - undefined, - token2022Mint + config ); await axn.addSupportIxs("deposit"); @@ -475,12 +473,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - customObligationAddress?: PublicKey, - hostAta?: PublicKey, - lookupTableAddress?: PublicKey, - tipAmount?: number, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -489,14 +482,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - customObligationAddress, - hostAta, - undefined, - lookupTableAddress, - tipAmount, - undefined, - token2022Mint + config ); await axn.addSupportIxs("borrow"); @@ -510,9 +496,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - lookupTableAddress?: PublicKey, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -521,14 +505,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - undefined, - undefined, - undefined, - lookupTableAddress, - undefined, - undefined, - token2022Mint + config ); await axn.addSupportIxs("mint"); await axn.addDepositReserveLiquidityIx(); @@ -541,9 +518,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - lookupTableAddress?: PublicKey, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -552,14 +527,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - undefined, - undefined, - undefined, - lookupTableAddress, - undefined, - undefined, - token2022Mint + config ); await axn.addSupportIxs("redeem"); await axn.addRedeemReserveCollateralIx(); @@ -572,9 +540,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - lookupTableAddress?: PublicKey, - customObligationAddress?: PublicKey + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -583,11 +549,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - customObligationAddress, - undefined, - undefined, - lookupTableAddress + config ); await axn.addSupportIxs("depositCollateral"); await axn.addDepositObligationCollateralIx(); @@ -600,9 +562,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - lookupTableAddress?: PublicKey, - customObligationAddress?: PublicKey + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -611,11 +571,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - customObligationAddress, - undefined, - undefined, - lookupTableAddress + config ); await axn.addSupportIxs("withdrawCollateral"); @@ -630,12 +586,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - obligationAddress?: PublicKey, - obligationSeed?: string, - lookupTableAddress?: PublicKey, - tipAmount?: number, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -644,14 +595,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - obligationAddress, - undefined, - obligationSeed, - lookupTableAddress, - tipAmount, - undefined, - token2022Mint + config ); await axn.addSupportIxs("withdraw"); @@ -666,11 +610,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - customObligationAddress?: PublicKey, - lookupTableAddress?: PublicKey, - tipAmount?: number, - token2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -679,14 +619,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - customObligationAddress, - undefined, - undefined, - lookupTableAddress, - tipAmount, - undefined, - token2022Mint + config ); await axn.addSupportIxs("repay"); @@ -702,12 +635,7 @@ export class SolendActionCore { connection: Connection, amount: string, wallet: Wallet, - environment: EnvironmentType = "production", - customObligationAddress?: PublicKey, - lookupTableAddress?: PublicKey, - tipAmount?: number, - token2022Mint?: string, - repayToken2022Mint?: string + config: ActionConfigType ) { const axn = await SolendActionCore.initialize( pool, @@ -716,15 +644,7 @@ export class SolendActionCore { new BN(amount), wallet, connection, - environment, - customObligationAddress, - undefined, - undefined, - lookupTableAddress, - tipAmount, - repayReserve, - token2022Mint, - repayToken2022Mint + config ); await axn.addSupportIxs("liquidate"); @@ -838,7 +758,8 @@ export class SolendActionCore { ]; const tip = this.getTipIx(); - if (tip && this.pullPriceTxns.length > 5) { + if (tip && this.pullPriceTxns.length >= 5) { + if (this.debug) console.log('adding tip ix to lending txn'); instructions.push(tip); } @@ -877,6 +798,7 @@ export class SolendActionCore { } addForgiveIx() { + if (this.debug) console.log('adding forgive ix to lending txn'); this.lendingIxs.push( forgiveDebtInstruction( this.obligationAddress, @@ -890,6 +812,7 @@ export class SolendActionCore { } addDepositIx() { + if (this.debug) console.log('adding deposit ix to lending txn'); this.lendingIxs.push( this.amount.toString() === U64_MAX ? depositMaxReserveLiquidityAndObligationCollateralInstruction( @@ -929,6 +852,7 @@ export class SolendActionCore { } addDepositReserveLiquidityIx() { + if (this.debug) console.log('adding mint ix to lending txn'); this.lendingIxs.push( depositReserveLiquidityInstruction( this.amount, @@ -946,6 +870,7 @@ export class SolendActionCore { } addRedeemReserveCollateralIx() { + if (this.debug) console.log('adding redeem ix to lending txn'); this.lendingIxs.push( redeemReserveCollateralInstruction( this.amount, @@ -963,6 +888,7 @@ export class SolendActionCore { } async addWithdrawObligationCollateralIx() { + if (this.debug) console.log('adding withdrawCollateral ix to lending txn'); this.lendingIxs.push( withdrawObligationCollateralInstruction( this.amount, @@ -980,6 +906,7 @@ export class SolendActionCore { } addDepositObligationCollateralIx() { + if (this.debug) console.log('adding depositCollateral ix to lending txn'); this.lendingIxs.push( depositObligationCollateralInstruction( this.amount, @@ -996,6 +923,7 @@ export class SolendActionCore { } addBorrowIx() { + if (this.debug) console.log('adding borrow ix to lending txn'); this.lendingIxs.push( borrowObligationLiquidityInstruction( this.amount, @@ -1015,6 +943,7 @@ export class SolendActionCore { } async addWithdrawIx() { + if (this.debug) console.log('adding withdraw ix to lending txn'); this.lendingIxs.push( this.amount.eq(new BN(U64_MAX)) ? withdrawObligationCollateralAndRedeemReserveLiquidity( @@ -1053,6 +982,7 @@ export class SolendActionCore { } async addRepayIx() { + if (this.debug) console.log('adding repay ix to lending txn'); this.lendingIxs.push( this.amount.toString() === U64_MAX ? repayMaxObligationLiquidityInstruction( @@ -1078,6 +1008,7 @@ export class SolendActionCore { } async addLiquidateIx(repayReserve: ReserveType) { + if (this.debug) console.log('adding liquidate ix to lending txn'); if ( !this.repayInfo?.userRepayCollateralAccountAddress || !this.repayInfo?.userRepayTokenAccountAddress @@ -1151,6 +1082,7 @@ export class SolendActionCore { private async addWrapIx() { if (!this.wrappedAta || !this.token2022Mint) throw new Error("Wrapped ATA not initialized"); + if (this.debug) console.log('adding wrap ix to preTxnIxs'); this.preTxnIxs.push( createAssociatedTokenAccountIdempotentInstruction( this.publicKey, @@ -1173,6 +1105,7 @@ export class SolendActionCore { private async addUnwrapIx() { if (!this.wrappedAta || !this.token2022Mint) throw new Error("Wrapped ATA not initialized"); + if (this.debug) console.log('adding wrap ix to preTxnIxs'); this.preTxnIxs.push( createAssociatedTokenAccountIdempotentInstruction( this.publicKey, @@ -1183,6 +1116,7 @@ export class SolendActionCore { ) ); + if (this.debug) console.log('adding wrap ix to postTxnIxs'); this.postTxnIxs.push( await createWithdrawAndBurnWrapperTokensInstruction( this.publicKey, @@ -1244,6 +1178,7 @@ export class SolendActionCore { const feedAccounts = await Promise.all( sbPulledOracles.map((oracleKey) => new PullFeed(sbod as any, oracleKey)) ); + if (this.debug) console.log("Feed accounts", sbPulledOracles); const loadedFeedAccounts = await Promise.all( feedAccounts.map((acc) => acc.loadData()) ); @@ -1276,6 +1211,7 @@ export class SolendActionCore { const instructions = [priorityFeeIx, modifyComputeUnits, ix]; + if (this.debug) console.log('adding tip ix to pullPriceTxns for sbod'); instructions.push(this.getTipIx()); // Get the latest context @@ -1293,6 +1229,7 @@ export class SolendActionCore { // Get Versioned Transaction const vtx = new VersionedTransaction(message); + if (this.debug) console.log('adding sbod ix to pullPriceTxns'); this.pullPriceTxns.push(vtx); } const pythPulledOracles = oracleAccounts.filter( @@ -1315,7 +1252,7 @@ export class SolendActionCore { const needUpdate = Date.now() / 1000 - Number(priceUpdate.priceMessage.publishTime.toString()) > - 30; + 80; return needUpdate ? { @@ -1333,6 +1270,7 @@ export class SolendActionCore { .map((x) => x.priceFeedId); if (shuffledPriceIds.length) { + if (this.debug) console.log("Feed accounts", shuffledPriceIds); const priceFeedUpdateData = await priceServiceConnection.getLatestVaas( shuffledPriceIds ); @@ -1358,6 +1296,7 @@ export class SolendActionCore { } this.pullPriceTxns.push(tx); } + console.log(`adding ${transactionsWithSigners.length} txns to pullPriceTxns`); } } @@ -1420,12 +1359,14 @@ export class SolendActionCore { } private async addObligationIxs() { + if (this.debug) console.log('addObligationIxs'); if (!this.obligationAccountInfo) { const obligationAccountInfoRentExempt = await this.connection.getMinimumBalanceForRentExemption( OBLIGATION_SIZE ); + if (this.debug) console.log('adding createAccount and initObligation ix to setup txn'); this.setupIxs.push( SystemProgram.createAccountWithSeed({ fromPubkey: this.publicKey, @@ -1466,8 +1407,10 @@ export class SolendActionCore { this.hostAta && !this.lookupTableAccount ) { + if (this.debug) console.log('adding createAta ix to pre txn'); this.preTxnIxs.push(createUserTokenAccountIx); } else { + if (this.debug) console.log('adding createAta ix to setup txn'); this.setupIxs.push(createUserTokenAccountIx); } } @@ -1493,8 +1436,10 @@ export class SolendActionCore { this.hostAta && !this.lookupTableAccount ) { + if (this.debug) console.log('adding createCAta ix to pre txn'); this.preTxnIxs.push(createUserCollateralAccountIx); } else { + if (this.debug) console.log('adding createCAta ix to setup txn'); this.setupIxs.push(createUserCollateralAccountIx); } } @@ -1591,6 +1536,7 @@ export class SolendActionCore { (userWSOLAccountInfo ? 0 : rentExempt) + (sendAction ? parseInt(safeRepay.toString(), 10) : 0), }); + if (this.debug) console.log('adding transferLamports ix'); preIxs.push(transferLamportsIx); const closeWSOLAccountIx = createCloseAccountInstruction( @@ -1603,8 +1549,10 @@ export class SolendActionCore { if (userWSOLAccountInfo) { const syncIx = syncNative(solAccountAddress); if (sendAction) { + if (this.debug) console.log('adding syncIx ix'); preIxs.push(syncIx); } else { + if (this.debug) console.log('adding closeWSOLAccountIx ix'); postIxs.push(closeWSOLAccountIx); } } else { @@ -1614,7 +1562,9 @@ export class SolendActionCore { this.publicKey, NATIVE_MINT ); + if (this.debug) console.log('adding createUserWSOLAccountIx ix'); preIxs.push(createUserWSOLAccountIx); + if (this.debug) console.log('adding closeWSOLAccountIx ix'); postIxs.push(closeWSOLAccountIx); } @@ -1623,9 +1573,11 @@ export class SolendActionCore { this.hostAta && !this.lookupTableAccount ) { + if (this.debug) console.log('adding above ixs to pre and post txn'); this.preTxnIxs.push(...preIxs); this.postTxnIxs.push(...postIxs); } else { + if (this.debug) console.log('adding above ixs to lending txn'); this.setupIxs.push(...preIxs); this.cleanupIxs.push(...postIxs); } diff --git a/solend-sdk/src/core/margin.ts b/solend-sdk/src/core/margin.ts index 0a5bbbfe..71a23d03 100644 --- a/solend-sdk/src/core/margin.ts +++ b/solend-sdk/src/core/margin.ts @@ -411,10 +411,12 @@ export class Margin { this.connection, depositCollateralConfig.amount, this.wallet, - "production", - this.obligationAddress, - this.obligationSeed, - lookupTableAccount?.key + { + environment: "production", + customObligationAddress: this.obligationAddress, + customObligationSeed: this.obligationSeed, + lookupTableAddress: lookupTableAccount?.key + } ); const { preLendingTxn, lendingTxn } = await solendAction.getLegacyTransactions(); diff --git a/solend-sdk/src/instructions/initReserve.ts b/solend-sdk/src/instructions/initReserve.ts index 329bf3e9..7ea4bbcc 100644 --- a/solend-sdk/src/instructions/initReserve.ts +++ b/solend-sdk/src/instructions/initReserve.ts @@ -6,14 +6,14 @@ import { } from "@solana/web3.js"; import BN from "bn.js"; import * as Layout from "../layout"; -import { ReserveConfig } from "../state"; import { LendingInstruction } from "./instruction"; +import { InputReserveConfigParams, NULL_ORACLE } from "../core"; const BufferLayout = require("buffer-layout"); export const initReserveInstruction = ( liquidityAmount: number | BN, - config: ReserveConfig, + config: InputReserveConfigParams, sourceLiquidity: PublicKey, destinationCollateral: PublicKey, reserve: PublicKey, @@ -22,7 +22,6 @@ export const initReserveInstruction = ( liquidityFeeReceiver: PublicKey, collateralMint: PublicKey, collateralSupply: PublicKey, - pythProduct: PublicKey, pythPrice: PublicKey, switchboardFeed: PublicKey, lendingMarket: PublicKey, @@ -72,7 +71,7 @@ export const initReserveInstruction = ( instruction: LendingInstruction.InitReserve, liquidityAmount: new BN(liquidityAmount), optimalUtilizationRate: config.optimalUtilizationRate, - maxUtilizationRate: config.maxUtilizationRate, + maxUtilizationRate: config.maxUtilizationRate, loanToValueRatio: config.loanToValueRatio, liquidationBonus: config.liquidationBonus, liquidationThreshold: config.liquidationThreshold, @@ -110,7 +109,8 @@ export const initReserveInstruction = ( { pubkey: liquidityFeeReceiver, isSigner: false, isWritable: true }, { pubkey: collateralMint, isSigner: false, isWritable: true }, { pubkey: collateralSupply, isSigner: false, isWritable: true }, - { pubkey: pythProduct, isSigner: false, isWritable: false }, + // Doesn't matter what we pass in as long as it's not null + { pubkey: pythPrice, isSigner: false, isWritable: false }, { pubkey: pythPrice, isSigner: false, isWritable: false }, { pubkey: switchboardFeed, isSigner: false, isWritable: false }, { pubkey: lendingMarket, isSigner: false, isWritable: true }, diff --git a/solend-sdk/src/instructions/updateReserveConfig.ts b/solend-sdk/src/instructions/updateReserveConfig.ts index df7a515c..a7f0fd13 100644 --- a/solend-sdk/src/instructions/updateReserveConfig.ts +++ b/solend-sdk/src/instructions/updateReserveConfig.ts @@ -1,8 +1,8 @@ import { PublicKey, TransactionInstruction } from "@solana/web3.js"; import { RateLimiterConfig } from "../state/rateLimiter"; -import { ReserveConfig } from "../state/reserve"; import * as Layout from "../layout"; import { LendingInstruction } from "./instruction"; +import { NULL_ORACLE, InputReserveConfigParams } from "../core"; const BufferLayout = require("buffer-layout"); @@ -22,10 +22,9 @@ export const updateReserveConfig = ( lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, lendingMarketOwner: PublicKey, - pythProduct: PublicKey, pythPrice: PublicKey, switchboardOracle: PublicKey, - reserveConfig: ReserveConfig, + reserveConfig: InputReserveConfigParams, rateLimiterConfig: RateLimiterConfig, solendProgramAddress: PublicKey ): TransactionInstruction => { @@ -107,7 +106,7 @@ export const updateReserveConfig = ( { pubkey: lendingMarket, isSigner: false, isWritable: false }, { pubkey: lendingMarketAuthority, isSigner: false, isWritable: false }, { pubkey: lendingMarketOwner, isSigner: true, isWritable: false }, - { pubkey: pythProduct, isSigner: false, isWritable: false }, + { pubkey: NULL_ORACLE, isSigner: false, isWritable: false }, { pubkey: pythPrice, isSigner: false, isWritable: false }, { pubkey: switchboardOracle, isSigner: false, isWritable: false }, ]; diff --git a/yarn.lock b/yarn.lock index 0e483fa0..ab5f78fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1015,6 +1015,13 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@brokerloop/ttlcache@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@brokerloop/ttlcache/-/ttlcache-3.2.3.tgz#bc3c79bb381f7b43f83745eb96e86673f75d3d11" + integrity sha512-kZWoyJGBYTv1cL5oHBYEixlJysJBf2RVnub3gbclD+dwaW9aKubbHzbZ9q1q6bONosxaOqMsoBorOrZKzBDiqg== + dependencies: + "@soncodi/signal" "~2.0.7" + "@bundlr-network/client@^0.7.14": version "0.7.17" resolved "https://registry.yarnpkg.com/@bundlr-network/client/-/client-0.7.17.tgz#0a4c41534640f9038e5d7d1d8cf1cc2571b185b7" @@ -1861,7 +1868,7 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@coral-xyz/anchor-30@npm:@coral-xyz/anchor@0.30.1": +"@coral-xyz/anchor-30@npm:@coral-xyz/anchor@0.30.1", "@coral-xyz/anchor@^0.30.1": version "0.30.1" resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.1.tgz#17f3e9134c28cd0ea83574c6bab4e410bcecec5d" integrity sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ== @@ -5752,6 +5759,11 @@ decimal.js "^10.4.0" typescript "^4.8.2" +"@soncodi/signal@~2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@soncodi/signal/-/signal-2.0.7.tgz#0a2c361b02dbfdbcf4e66b78e5f711e0a13d6e83" + integrity sha512-zA2oZluZmVvgZEDjF243KWD1S2J+1SH1MVynI0O1KRgDt1lU8nqk7AK3oQfW/WpwT51L5waGSU0xKF/9BTP5Cw== + "@stablelib/aead@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" @@ -5922,20 +5934,20 @@ protobufjs "^7.2.6" yaml "^2.2.1" -"@switchboard-xyz/on-demand@^1.2.20": - version "1.2.20" - resolved "https://registry.yarnpkg.com/@switchboard-xyz/on-demand/-/on-demand-1.2.20.tgz#11dcd876cbf8bb1893f22f7895baaf63fe118d76" - integrity sha512-RC52eNZVGaRbPdb8GYc1H4nm4YRCToX2LMI8flWjV9cf7jOIkcjtPyLH/Spg8LJPe2Pa30wJlqaqSXfqYB47Xw== +"@switchboard-xyz/on-demand@1.2.27": + version "1.2.27" + resolved "https://registry.yarnpkg.com/@switchboard-xyz/on-demand/-/on-demand-1.2.27.tgz#8591d2a424a7a9a9256a996df35f408c0167fe40" + integrity sha512-acrIngoQRv6M6PiNooEFQP+0FExseKkCMecGDP1nLRz7C1hLMTgWWeUqmmfrxJh6uCafM4Cl4lpKk3pGd01XBg== dependencies: + "@brokerloop/ttlcache" "^3.2.3" "@coral-xyz/anchor-30" "npm:@coral-xyz/anchor@0.30.1" "@solana/web3.js" "^1.95.0" "@solworks/soltoolkit-sdk" "^0.0.23" "@switchboard-xyz/common" "^2.4.2" - axios "^1.2.0" + axios "^1.7.4" big.js "^6.2.1" bs58 "^5.0.0" js-yaml "^4.1.0" - node-cache "^5.1.2" protobufjs "^7.2.6" "@switchboard-xyz/sbv2-lite@^0.1.0": @@ -7702,7 +7714,7 @@ axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" -axios@^1.2.0, axios@^1.5.1, axios@^1.7.2: +axios@^1.5.1, axios@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== @@ -7711,6 +7723,15 @@ axios@^1.2.0, axios@^1.5.1, axios@^1.7.2: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.7.4: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -8566,11 +8587,6 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone@2.x: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -13595,13 +13611,6 @@ node-addon-api@^8.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.0.0.tgz#5453b7ad59dd040d12e0f1a97a6fa1c765c5c9d2" integrity sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw== -node-cache@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" - integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== - dependencies: - clone "2.x" - node-fetch-native@^1.6.1, node-fetch-native@^1.6.2, node-fetch-native@^1.6.3: version "1.6.4" resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e"