UNPKG

@kamino-finance/klend-sdk

Version:

Typescript SDK for interacting with the Kamino Lending (klend) protocol

283 lines (261 loc) 8.2 kB
import { Address, TransactionSigner } from '@solana/kit'; import { ManagerConnectionPool } from './ManagerConnectionPool'; import { PROGRAM_ID as KLEND_PROGRAM_ID } from '../../@codegen/klend/programId'; import { PROGRAM_ID as KVAULT_PROGRAM_ID } from '../../@codegen/kvault/programId'; import { PROGRAM_ID as FARMS_PROGRAM_ID } from '@kamino-finance/farms-sdk/dist/@codegen/farms/programId'; import { STAGING_PROGRAM_ID as KLEND_STAGING_PROGRAM_ID } from '../../utils/constants'; import { noopSigner, parseKeypairFile } from './keypair'; import { Chain } from './rpc'; import { KaminoMarket } from '../../classes'; import { FARMS_GLOBAL_CONFIG_MAINNET, FARMS_GLOBAL_CONFIG_DEVNET } from '../../classes/farm_utils'; import { KVAULT_STAGING_PROGRAM_ID, KVAULT_DEVNET_PROGRAM_ID, FARMS_STAGING_PROGRAM_ID, FARMS_DEVNET_PROGRAM_ID, } from '../utils/consts'; import { VaultState } from '../../@codegen/kvault/accounts'; export type Cluster = 'localnet' | 'devnet' | 'mainnet-beta'; export type SendTxMode = 'execute' | 'simulate' | 'multisig' | 'print'; export type SignerConfig = { multisigSigner?: TransactionSigner; admin?: TransactionSigner; }; export type ProgramConfig = { staging?: boolean; devnet?: boolean; klendProgramId?: Address; kvaultProgramId?: Address; farmsProgramId?: Address; farmsGlobalConfig?: Address; }; interface GetSignerProps { market?: KaminoMarket; useLendingMarketOwnerCached?: boolean; vaultState?: VaultState; useVaultPendingAdmin?: boolean; useVaultAllocationAdmin?: boolean; } export class ManagerEnv { c: ManagerConnectionPool; cluster: Cluster; signerConfig: SignerConfig; klendProgramId: Address; kvaultProgramId: Address; farmsProgramId: Address; farmsGlobalConfig: Address; constructor( c: ManagerConnectionPool, cluster: Cluster, signerConfig: SignerConfig, klendProgramId: Address, kvaultProgramId: Address, farmsProgramId: Address, farmsGlobalConfig: Address ) { this.c = c; this.cluster = cluster; this.signerConfig = signerConfig; this.klendProgramId = klendProgramId; this.kvaultProgramId = kvaultProgramId; this.farmsProgramId = farmsProgramId; this.farmsGlobalConfig = farmsGlobalConfig; } async getSigner({ market, useLendingMarketOwnerCached, vaultState, useVaultPendingAdmin, useVaultAllocationAdmin, }: GetSignerProps = {}): Promise<TransactionSigner> { function matchesAdmin(config: SignerConfig, a: Address): boolean { return config.admin !== undefined && config.admin.address === a; } if (vaultState) { if (useVaultPendingAdmin) { return matchesAdmin(this.signerConfig, vaultState.pendingAdmin) ? this.signerConfig.admin! : noopSigner(vaultState.pendingAdmin); } else if (useVaultAllocationAdmin) { return matchesAdmin(this.signerConfig, vaultState.allocationAdmin) ? this.signerConfig.admin! : noopSigner(vaultState.allocationAdmin); } else { return matchesAdmin(this.signerConfig, vaultState.vaultAdminAuthority) ? this.signerConfig.admin! : noopSigner(vaultState.vaultAdminAuthority); } } else if (market) { if (useLendingMarketOwnerCached) { return matchesAdmin(this.signerConfig, market.state.lendingMarketOwnerCached) ? this.signerConfig.admin! : noopSigner(market.state.lendingMarketOwnerCached); } else { return matchesAdmin(this.signerConfig, market.state.lendingMarketOwner) ? this.signerConfig.admin! : noopSigner(market.state.lendingMarketOwner); } } else if (this.signerConfig.admin) { return this.signerConfig.admin; } else if (this.signerConfig.multisigSigner) { return this.signerConfig.multisigSigner; } throw new Error(`No signer in config ${JSON.stringify(this.signerConfig)}`); } } function resolveProgramId( explicit: Address | undefined, ids: { staging: Address; devnet?: Address; mainnet: Address }, staging: boolean, devnet: boolean ): Address { if (explicit) return explicit; if (staging) return ids.staging; if (devnet && ids.devnet) return ids.devnet; return ids.mainnet; } function defaultProgramConfig(programConfig: ProgramConfig): Required<ProgramConfig> { const staging = programConfig.staging ?? false; const devnet = programConfig.devnet ?? false; return { staging, devnet, klendProgramId: resolveProgramId( programConfig.klendProgramId, { staging: KLEND_STAGING_PROGRAM_ID, mainnet: KLEND_PROGRAM_ID, devnet: KLEND_PROGRAM_ID, }, staging, devnet ), kvaultProgramId: resolveProgramId( programConfig.kvaultProgramId, { staging: KVAULT_STAGING_PROGRAM_ID, devnet: KVAULT_DEVNET_PROGRAM_ID, mainnet: KVAULT_PROGRAM_ID, }, staging, devnet ), farmsProgramId: resolveProgramId( programConfig.farmsProgramId, { staging: FARMS_STAGING_PROGRAM_ID, devnet: FARMS_DEVNET_PROGRAM_ID, mainnet: FARMS_PROGRAM_ID, }, staging, devnet ), farmsGlobalConfig: resolveProgramId( programConfig.farmsGlobalConfig, { staging: FARMS_GLOBAL_CONFIG_MAINNET, devnet: FARMS_GLOBAL_CONFIG_DEVNET, mainnet: FARMS_GLOBAL_CONFIG_MAINNET, }, staging, devnet ), }; } export async function initEnv( staging: boolean = false, multisig: Address | undefined = undefined, adminKeypairPath: string | undefined = undefined, rpcUrl: string | undefined = undefined, devnet: boolean = false ): Promise<ManagerEnv> { if (staging && devnet) { throw new Error('Cannot use both --staging and --devnet at the same time'); } const config = defaultProgramConfig({ staging, devnet, }); let resolvedUrl: string; if (rpcUrl) { resolvedUrl = rpcUrl; } else if (devnet && process.env.RPC_DEVNET) { resolvedUrl = process.env.RPC_DEVNET; } else if (process.env.RPC) { resolvedUrl = process.env.RPC; } else { throw 'Must specify an RPC URL'; } const resolvedAdminPath = adminKeypairPath ?? (devnet ? process.env.ADMIN_DEVNET : process.env.ADMIN); let resolvedAdmin: TransactionSigner | undefined = undefined; if (resolvedAdminPath) { resolvedAdmin = await parseKeypairFile(resolvedAdminPath); } const rpcChain = parseRpcChain(resolvedUrl, devnet); const c = new ManagerConnectionPool(rpcChain); const multisigSigner = multisig ? noopSigner(multisig) : undefined; const env: ManagerEnv = new ManagerEnv( c, rpcChain.name, { admin: resolvedAdmin, multisigSigner, }, config.klendProgramId, config.kvaultProgramId, config.farmsProgramId, config.farmsGlobalConfig ); console.log('\nSettings ⚙️'); console.log(`Multisig: ${multisig}`); console.log(`Multisig signer: ${multisigSigner?.address}`); console.log(`Admin: ${resolvedAdmin?.address}`); console.log(`Rpc: ${resolvedUrl}`); console.log(`klendProgramId: ${env.klendProgramId}`); console.log(`kvaultProgramId: ${env.kvaultProgramId}`); return env; } function parseRpcChain(rpcUrl: string, devnet: boolean = false): Chain { let chain: Chain; if (rpcUrl === 'localnet') { chain = { name: 'localnet', endpoint: { url: 'http://127.0.0.1:8899', name: 'localnet', }, wsEndpoint: { name: 'localnet', url: 'ws://127.0.0.1:8900', }, multicastEndpoints: [], }; } else if (devnet) { chain = { name: 'devnet', endpoint: { url: rpcUrl, name: 'devnet', }, wsEndpoint: { url: rpcUrl.replace('https:', 'wss:'), name: 'devnet-ws', }, multicastEndpoints: [], }; } else { chain = { name: 'mainnet-beta', endpoint: { url: rpcUrl, name: 'mainnet-beta', }, wsEndpoint: { url: rpcUrl.replace('https:', 'wss:') + '/whirligig', name: 'mainnet-beta-ws', }, multicastEndpoints: [], }; } return chain; }