diff --git a/.gitignore b/.gitignore index 044f1fdc..4ff41ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .DS_Store target **/node_modules/ +.pnpm-store .yarn .vscode -dist \ No newline at end of file +dist +.env \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..2175c7c3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +Copyright 2025 Haven. All rights reserved. + +This source code is made publicly available for transparency, educational, and auditing purposes only. + +You may view and modify the code for personal or internal use. +You may **not** copy, redistribute, or use this code, in whole or in part, for any commercial purpose. + +Unauthorized commercial use or redistribution is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. diff --git a/README.md b/README.md index 5e81b39c..1e35bb51 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # Solauto +All rights reserved @RichardPatterson +## Overview Solauto is a program on the Solana blockchain that lets you manage leveraged longs & shorts on auto-pilot to maximize your gains and eliminate the risk of liquidation. -[Go to program documentation](/programs/solauto) - +See the [Program documentation](/programs/solauto/README.md) for more info on the underlying Solana program. ### Repository Dependencies @@ -11,9 +12,150 @@ Solauto is a program on the Solana blockchain that lets you manage leveraged lon - PNPM ``` -crate install shank-idl +cargo install shank-cli rustup component add rustfmt pnpm install -g ts-node ``` -Define `HELIUS_API_KEY` environment variable \ No newline at end of file +Define `IRONFORGE_API_KEY` environment variable for running package tests/scripts locally. + +### Building + +```bash +# Build typescript +pnpm build:ts + +# Build Solauto test program +pnpm build:rs:test + +# Build Solauto prod program +pnpm build:rs:prod +``` + +### Testing + +```bash +# If running rust tests, build program first +pnpm build:rs:local + +# Run all rust & typescript tests +pnpm test:all + +# Run all rust tests +pnpm test:rs:all + +# Run all typescript tests +pnpm test:ts:all +``` + +## Solauto Typescript SDK + +The Solauto typescript SDK is made for interacting with the Solauto program. This SDK provides tools for managing, & reading Solauto state data, as well as executing transactions. + +```bash +npm install @haven-fi/solauto-sdk +# or +yarn add @haven-fi/solauto-sdk +# or +pnpm add @haven-fi/solauto-sdk +``` + +## Basic Usage + +```typescript +import { PublicKey } from "@solana/web3.js"; +import { NATIVE_MINT } from "@solana/spl-token"; +import * as solauto from "@haven-fi/solauto-sdk"; + +// Create new Solauto client +const client = solauto.getClient(solauto.LendingPlatform.MARGINFI, { + signer: yourSigner, + rpcUrl: "[YOUR_RPC_URL]", +}); + +// Initialize the client +const supplyMint = NATIVE_MINT; +const debtMint = new PublicKey(solauto.USDC); +await client.initializeNewSolautoPosition({ + positionId: 1, + lpPoolAccount: solauto.getMarginfiAccounts().defaultGroup, + supplyMint, + debtMint, +}); + +// Open a position with custom settings +const [maxLtvBps, liqThresholdBps] = + await client.pos.maxLtvAndLiqThresholdBps(); +const settings: solauto.SolautoSettingsParametersInpArgs = { + boostToBps: solauto.maxBoostToBps(maxLtvBps, liqThresholdBps), + boostGap: 50, + repayToBps: solauto.maxRepayToBps(maxLtvBps, liqThresholdBps), + repayGap: 50, +}; + +const supplyUsdToDeposit = 100; +const debtUsdToBorrow = 60; +const [supplyPrice, debtPrice] = await solauto.fetchTokenPrices([ + supplyMint, + debtMint, +]); + +const transactionItems = [ + // Open position + solauto.openSolautoPosition(client, settings), + // Deposit supply (SOL) transaction + solauto.deposit( + client, + toBaseUnit( + supplyUsdToDeposit / supplyPrice, + client.pos.supplyMintInfo.decimals + ) + ), + // Borrow debt (USDC) transaction + solauto.borrow( + client, + toBaseUnit(debtUsdToBorrow / debtPrice, client.pos.debtMintInfo.decimals) + ), + // Rebalance to 0 LTV (repays all debt using collateral) + solauto.rebalance(client, 0), + // Withdraw remaining supply in position + solauto.withdraw(client, "All"), + // Close position + solauto.closeSolautoPosition(client), +]; + +// Send all transactions atomically +const txManager = await new solauto.ClientTransactionsManager({ + txHandler: client, +}); +const statuses = txManager.send(transactionItems); +``` + +## Rebalancing an existing position + +```typescript +import * as solauto from "@haven-fi/solauto-sdk"; + +// Create new Solauto client +const client = solauto.getClient(solauto.LendingPlatform.MARGINFI, { + signer: yourSigner, + rpcUrl: "[YOUR_RPC_URL]", +}); + +// Initialize the client +await client.initializeExistingSolautoPosition({ + positionId: myPositionId, +}); + +const transactionItems = [ + solauto.rebalance( + client, + undefined // Provide target liquidation utilization rate if you want a specific LTV, otherwise it will rebalance according to position's settings (default) + ), +]; + +const txManager = await new solauto.ClientTransactionsManager({ + txHandler: client, +}); +const statuses = txManager.send(transactionItems); +``` diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index e69de29b..00000000 diff --git a/generateClients.cjs b/generateClients.cjs index 4a75f9de..73edb58c 100644 --- a/generateClients.cjs +++ b/generateClients.cjs @@ -28,6 +28,49 @@ function fixAnchorIDL(idlFilename, programId) { origin: "anchor", address: programId, }; + + function flattenDefined(data) { + if (typeof data === "object" && data !== null) { + if (Array.isArray(data)) { + return data.map(flattenDefined); + } else if (data.defined && typeof data.defined === "object") { + return { ...data, defined: data.defined.name }; + } else { + return Object.keys(data).reduce((acc, key) => { + acc[key] = flattenDefined(data[key]); + return acc; + }, {}); + } + } + return data; + } + + data = flattenDefined(data); + + function replacePubkeyWithPublicKey(data) { + if (typeof data === "object" && data !== null) { + if (Array.isArray(data)) { + return data.map(replacePubkeyWithPublicKey); + } else { + return Object.keys(data).reduce((acc, key) => { + if (key === "pubkey") { + acc["publicKey"] = data[key]; + } else if (typeof data[key] === "string" && data[key] === "pubkey") { + acc[key] = "publicKey"; + } else { + acc[key] = replacePubkeyWithPublicKey(data[key]); + } + return acc; + }, {}); + } + } else if (data === "pubkey") { + return "publicKey"; + } + return data; + } + + data = replacePubkeyWithPublicKey(data); + fs.writeFileSync(idlFilePath, JSON.stringify(data, null, 2), "utf8"); } @@ -50,7 +93,9 @@ function generateTypescriptSDKForAnchorIDL(sdkDirName, idlFilename, programId) { const kinobi = k.createFromIdls([idlFilePath]); kinobi.accept( - new k.renderJavaScriptVisitor(path.join(typescriptSdkDir, sdkDirName)) + new k.renderJavaScriptVisitor( + path.join(typescriptSdkDir, "externalSdks", sdkDirName) + ) ); } @@ -92,25 +137,30 @@ async function cleanJupiterTsSDK(exclusions = []) { generateSolautoSDK(); -// generateRustSDKForAnchorIDL( -// "marginfi-sdk", -// "marginfi.json", -// "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" -// ); -// generateTypescriptSDKForAnchorIDL( -// "marginfi-sdk", -// "marginfi.json", -// "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" -// ); - generateRustSDKForAnchorIDL( - "jupiter-sdk", - "jupiter.json", - "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" + "marginfi-sdk", + "marginfi.json", + "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" ); -// generateTypescriptSDKForAnchorIDL( +generateTypescriptSDKForAnchorIDL( + "marginfi", + "marginfi.json", + "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" +); + +// generateRustSDKForAnchorIDL( // "jupiter-sdk", // "jupiter.json", // "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" // ); +// generateTypescriptSDKForAnchorIDL( +// "jupiter", +// "jupiter.json", +// "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" +// ); +// generateTypescriptSDKForAnchorIDL( +// "pyth", +// "pyth.json", +// "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT" +// ); // cleanJupiterTsSDK(["programs", "errors", "index.ts"]); diff --git a/idls/marginfi.json b/idls/marginfi.json index 1166a744..b0775aa3 100644 --- a/idls/marginfi.json +++ b/idls/marginfi.json @@ -803,6 +803,12 @@ { "name": "amount", "type": "u64" + }, + { + "name": "deposit_up_to_limit", + "type": { + "option": "bool" + } } ] }, @@ -877,7 +883,7 @@ "accounts": [ { "name": "marginfiGroup", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -2601,16 +2607,10 @@ "name": "None" }, { - "name": "PythLegacy" - }, - { - "name": "SwitchboardLegacy" - }, - { - "name": "PythPushOracle" + "name": "PythEma" }, { - "name": "SwitchboardPull" + "name": "SwitchboardV2" } ] } @@ -3065,8 +3065,8 @@ "errors": [ { "code": 6000, - "name": "MathError", - "msg": "Math error" + "name": "InternalLogicError", + "msg": "Internal Marginfi logic error" }, { "code": 6001, @@ -3091,7 +3091,7 @@ { "code": 6005, "name": "MissingPythOrBankAccount", - "msg": "Missing Pyth or Bank account" + "msg": "Missing Oracle, Bank, LST mint, or Sol Pool" }, { "code": 6006, @@ -3100,188 +3100,343 @@ }, { "code": 6007, - "name": "InvalidOracleAccount", - "msg": "Invalid Pyth account" - }, - { - "code": 6008, "name": "MissingBankAccount", "msg": "Missing Bank account" }, { - "code": 6009, + "code": 6008, "name": "InvalidBankAccount", "msg": "Invalid Bank account" }, { - "code": 6010, - "name": "BadAccountHealth", - "msg": "Bad account health" + "code": 6009, + "name": "RiskEngineInitRejected", + "msg": "RiskEngine rejected due to either bad health or stale oracles" }, { - "code": 6011, + "code": 6010, "name": "LendingAccountBalanceSlotsFull", "msg": "Lending account balance slots are full" }, { - "code": 6012, + "code": 6011, "name": "BankAlreadyExists", "msg": "Bank already exists" }, { - "code": 6013, - "name": "IllegalLiquidation", - "msg": "Illegal liquidation" + "code": 6012, + "name": "ZeroLiquidationAmount", + "msg": "Amount to liquidate must be positive" }, { - "code": 6014, + "code": 6013, "name": "AccountNotBankrupt", "msg": "Account is not bankrupt" }, { - "code": 6015, + "code": 6014, "name": "BalanceNotBadDebt", "msg": "Account balance is not bad debt" }, { - "code": 6016, + "code": 6015, "name": "InvalidConfig", "msg": "Invalid group config" }, { - "code": 6017, - "name": "StaleOracle", - "msg": "Stale oracle data" - }, - { - "code": 6018, + "code": 6016, "name": "BankPaused", "msg": "Bank paused" }, { - "code": 6019, + "code": 6017, "name": "BankReduceOnly", "msg": "Bank is ReduceOnly mode" }, { - "code": 6020, - "name": "BankAccoutNotFound", + "code": 6018, + "name": "BankAccountNotFound", "msg": "Bank is missing" }, { - "code": 6021, + "code": 6019, "name": "OperationDepositOnly", "msg": "Operation is deposit-only" }, { - "code": 6022, + "code": 6020, "name": "OperationWithdrawOnly", "msg": "Operation is withdraw-only" }, { - "code": 6023, + "code": 6021, "name": "OperationBorrowOnly", "msg": "Operation is borrow-only" }, { - "code": 6024, + "code": 6022, "name": "OperationRepayOnly", "msg": "Operation is repay-only" }, { - "code": 6025, + "code": 6023, "name": "NoAssetFound", "msg": "No asset found" }, { - "code": 6026, + "code": 6024, "name": "NoLiabilityFound", "msg": "No liability found" }, { - "code": 6027, + "code": 6025, "name": "InvalidOracleSetup", "msg": "Invalid oracle setup" }, { - "code": 6028, + "code": 6026, "name": "IllegalUtilizationRatio", "msg": "Invalid bank utilization ratio" }, { - "code": 6029, + "code": 6027, "name": "BankLiabilityCapacityExceeded", "msg": "Bank borrow cap exceeded" }, { - "code": 6030, + "code": 6028, "name": "InvalidPrice", "msg": "Invalid Price" }, { - "code": 6031, + "code": 6029, "name": "IsolatedAccountIllegalState", - "msg": "Account can have only one liablity when account is under isolated risk" + "msg": "Account can have only one liability when account is under isolated risk" }, { - "code": 6032, + "code": 6030, "name": "EmissionsAlreadySetup", "msg": "Emissions already setup" }, { - "code": 6033, + "code": 6031, "name": "OracleNotSetup", "msg": "Oracle is not set" }, { - "code": 6034, + "code": 6032, "name": "InvalidSwitchboardDecimalConversion", - "msg": "Invalid swithcboard decimal conversion" + "msg": "Invalid switchboard decimal conversion" }, { - "code": 6035, + "code": 6033, "name": "CannotCloseOutstandingEmissions", "msg": "Cannot close balance because of outstanding emissions" }, { - "code": 6036, + "code": 6034, "name": "EmissionsUpdateError", "msg": "Update emissions error" }, { - "code": 6037, + "code": 6035, "name": "AccountDisabled", "msg": "Account disabled" }, { - "code": 6038, + "code": 6036, "name": "AccountTempActiveBalanceLimitExceeded", "msg": "Account can't temporarily open 3 balances, please close a balance first" }, { - "code": 6039, + "code": 6037, "name": "AccountInFlashloan", "msg": "Illegal action during flashloan" }, { - "code": 6040, + "code": 6038, "name": "IllegalFlashloan", "msg": "Illegal flashloan" }, { - "code": 6041, + "code": 6039, "name": "IllegalFlag", "msg": "Illegal flag" }, { - "code": 6042, + "code": 6040, "name": "IllegalBalanceState", "msg": "Illegal balance state" }, { - "code": 6043, + "code": 6041, "name": "IllegalAccountAuthorityTransfer", "msg": "Illegal account authority transfer" + }, + { + "code": 6042, + "name": "Unauthorized", + "msg": "Unauthorized" + }, + { + "code": 6043, + "name": "IllegalAction", + "msg": "Invalid account authority" + }, + { + "code": 6044, + "name": "T22MintRequired", + "msg": "Token22 Banks require mint account as first remaining account" + }, + { + "code": 6045, + "name": "InvalidFeeAta", + "msg": "Invalid ATA for global fee account" + }, + { + "code": 6046, + "name": "AddedStakedPoolManually", + "msg": "Use add pool permissionless instead" + }, + { + "code": 6047, + "name": "AssetTagMismatch", + "msg": "Staked SOL accounts can only deposit staked assets and borrow SOL" + }, + { + "code": 6048, + "name": "StakePoolValidationFailed", + "msg": "Stake pool validation failed: check the stake pool, mint, or sol pool" + }, + { + "code": 6049, + "name": "SwitchboardStalePrice", + "msg": "Switchboard oracle: stale price" + }, + { + "code": 6050, + "name": "PythPushStalePrice", + "msg": "Pyth Push oracle: stale price" + }, + { + "code": 6051, + "name": "WrongNumberOfOracleAccounts", + "msg": "Oracle error: wrong number of accounts" + }, + { + "code": 6052, + "name": "WrongOracleAccountKeys", + "msg": "Oracle error: wrong account keys" + }, + { + "code": 6053, + "name": "PythPushWrongAccountOwner", + "msg": "Pyth Push oracle: wrong account owner" + }, + { + "code": 6054, + "name": "StakedPythPushWrongAccountOwner", + "msg": "Staked Pyth Push oracle: wrong account owner" + }, + { + "code": 6055, + "name": "PythPushMismatchedFeedId", + "msg": "Pyth Push oracle: mismatched feed id" + }, + { + "code": 6056, + "name": "PythPushInsufficientVerificationLevel", + "msg": "Pyth Push oracle: insufficient verification level" + }, + { + "code": 6057, + "name": "PythPushFeedIdMustBe32Bytes", + "msg": "Pyth Push oracle: feed id must be 32 Bytes" + }, + { + "code": 6058, + "name": "PythPushFeedIdNonHexCharacter", + "msg": "Pyth Push oracle: feed id contains non-hex characters" + }, + { + "code": 6059, + "name": "SwitchboardWrongAccountOwner", + "msg": "Switchboard oracle: wrong account owner" + }, + { + "code": 6060, + "name": "PythPushInvalidAccount", + "msg": "Pyth Push oracle: invalid account" + }, + { + "code": 6061, + "name": "SwitchboardInvalidAccount", + "msg": "Switchboard oracle: invalid account" + }, + { + "code": 6062, + "name": "MathError", + "msg": "Math error" + }, + { + "code": 6063, + "name": "InvalidEmissionsDestinationAccount", + "msg": "Invalid emissions destination account" + }, + { + "code": 6064, + "name": "SameAssetAndLiabilityBanks", + "msg": "Asset and liability bank cannot be the same" + }, + { + "code": 6065, + "name": "OverliquidationAttempt", + "msg": "Trying to withdraw more assets than available" + }, + { + "code": 6066, + "name": "NoLiabilitiesInLiabilityBank", + "msg": "Liability bank has no liabilities" + }, + { + "code": 6067, + "name": "AssetsInLiabilityBank", + "msg": "Liability bank has assets" + }, + { + "code": 6068, + "name": "HealthyAccount", + "msg": "Account is healthy and cannot be liquidated" + }, + { + "code": 6069, + "name": "ExhaustedLiability", + "msg": "Liability payoff too severe, exhausted liability" + }, + { + "code": 6070, + "name": "TooSeverePayoff", + "msg": "Liability payoff too severe, liability balance has assets" + }, + { + "code": 6071, + "name": "TooSevereLiquidation", + "msg": "Liquidation too severe, account above maintenance requirement" + }, + { + "code": 6072, + "name": "WorseHealthPostLiquidation", + "msg": "Liquidation would worsen account health" + }, + { + "code": 6073, + "name": "ArenaBankLimit", + "msg": "Arena groups can only support two banks" + }, + { + "code": 6074, + "name": "ArenaSettingCannotChange", + "msg": "Arena groups cannot return to non-arena status" } ], "metadata": { diff --git a/idls/past-idl-versions/solauto_v0_333450043.json b/idls/past-idl-versions/solauto_v0_333450043.json new file mode 100644 index 00000000..c8654f39 --- /dev/null +++ b/idls/past-idl-versions/solauto_v0_333450043.json @@ -0,0 +1,1688 @@ +{ + "version": "0.1.0", + "name": "solauto", + "instructions": [ + { + "name": "UpdateReferralStates", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "signerReferralState", + "isMut": true, + "isSigner": false + }, + { + "name": "referredByState", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "referredByAuthority", + "isMut": false, + "isSigner": false, + "isOptional": true + } + ], + "args": [ + { + "name": "updateReferralStatesArgs", + "type": { + "defined": "UpdateReferralStatesArgs" + } + } + ], + "discriminant": { + "type": "u8", + "value": 0 + } + }, + { + "name": "ConvertReferralFees", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "ixsSysvar", + "isMut": false, + "isSigner": false + }, + { + "name": "referralState", + "isMut": false, + "isSigner": false + }, + { + "name": "referralFeesTa", + "isMut": true, + "isSigner": false + }, + { + "name": "intermediaryTa", + "isMut": true, + "isSigner": false + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 1 + } + }, + { + "name": "ClaimReferralFees", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "signerWsolTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "referralState", + "isMut": false, + "isSigner": false + }, + { + "name": "referralFeesDestTa", + "isMut": true, + "isSigner": false + }, + { + "name": "referralFeesDestMint", + "isMut": false, + "isSigner": false + }, + { + "name": "referralAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "feesDestinationTa", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 2 + } + }, + { + "name": "UpdatePosition", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "dcaMint", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionDcaTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "signerDcaTa", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [ + { + "name": "updatePositionData", + "type": { + "defined": "UpdatePositionData" + } + } + ], + "discriminant": { + "type": "u8", + "value": 3 + } + }, + { + "name": "ClosePosition", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "protocolAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "positionSupplyTa", + "isMut": true, + "isSigner": false + }, + { + "name": "signerSupplyTa", + "isMut": true, + "isSigner": false + }, + { + "name": "positionDebtTa", + "isMut": true, + "isSigner": false + }, + { + "name": "signerDebtTa", + "isMut": true, + "isSigner": false + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 4 + } + }, + { + "name": "CancelDCA", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "dcaMint", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionDcaTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "signerDcaTa", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 5 + } + }, + { + "name": "MarginfiOpenPosition", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "marginfiProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "signerReferralState", + "isMut": false, + "isSigner": false + }, + { + "name": "referredByState", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "referredBySupplyTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "marginfiGroup", + "isMut": false, + "isSigner": false + }, + { + "name": "marginfiAccount", + "isMut": true, + "isSigner": false, + "isOptionalSigner": true + }, + { + "name": "supplyMint", + "isMut": false, + "isSigner": false + }, + { + "name": "supplyBank", + "isMut": false, + "isSigner": false + }, + { + "name": "positionSupplyTa", + "isMut": true, + "isSigner": false + }, + { + "name": "debtMint", + "isMut": false, + "isSigner": false + }, + { + "name": "debtBank", + "isMut": false, + "isSigner": false + }, + { + "name": "positionDebtTa", + "isMut": true, + "isSigner": false + }, + { + "name": "signerDebtTa", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [ + { + "name": "marginfiOpenPositionData", + "type": { + "defined": "MarginfiOpenPositionData" + } + } + ], + "discriminant": { + "type": "u8", + "value": 6 + } + }, + { + "name": "MarginfiRefreshData", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "marginfiProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "marginfiGroup", + "isMut": false, + "isSigner": false + }, + { + "name": "marginfiAccount", + "isMut": false, + "isSigner": false + }, + { + "name": "supplyBank", + "isMut": true, + "isSigner": false + }, + { + "name": "supplyPriceOracle", + "isMut": false, + "isSigner": false + }, + { + "name": "debtBank", + "isMut": true, + "isSigner": false + }, + { + "name": "debtPriceOracle", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 7 + } + }, + { + "name": "MarginfiProtocolInteraction", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "marginfiProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ataProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "marginfiGroup", + "isMut": false, + "isSigner": false + }, + { + "name": "marginfiAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "supplyBank", + "isMut": true, + "isSigner": false + }, + { + "name": "supplyPriceOracle", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionSupplyTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "vaultSupplyTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "supplyVaultAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "debtBank", + "isMut": true, + "isSigner": false + }, + { + "name": "debtPriceOracle", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionDebtTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "vaultDebtTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "debtVaultAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [ + { + "name": "solautoAction", + "type": { + "defined": "SolautoAction" + } + } + ], + "discriminant": { + "type": "u8", + "value": 8 + } + }, + { + "name": "MarginfiRebalance", + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "marginfiProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "ixsSysvar", + "isMut": false, + "isSigner": false + }, + { + "name": "solautoFeesTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "authorityReferralState", + "isMut": false, + "isSigner": false + }, + { + "name": "referredByTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "solautoPosition", + "isMut": true, + "isSigner": false + }, + { + "name": "marginfiGroup", + "isMut": false, + "isSigner": false + }, + { + "name": "marginfiAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "intermediaryTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "supplyBank", + "isMut": true, + "isSigner": false + }, + { + "name": "supplyPriceOracle", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionSupplyTa", + "isMut": true, + "isSigner": false + }, + { + "name": "authoritySupplyTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "vaultSupplyTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "supplyVaultAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "debtBank", + "isMut": true, + "isSigner": false + }, + { + "name": "debtPriceOracle", + "isMut": false, + "isSigner": false, + "isOptional": true + }, + { + "name": "positionDebtTa", + "isMut": true, + "isSigner": false + }, + { + "name": "authorityDebtTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "vaultDebtTa", + "isMut": true, + "isSigner": false, + "isOptional": true + }, + { + "name": "debtVaultAuthority", + "isMut": true, + "isSigner": false, + "isOptional": true + } + ], + "args": [ + { + "name": "rebalanceSettings", + "type": { + "defined": "RebalanceSettings" + } + } + ], + "discriminant": { + "type": "u8", + "value": 9 + } + } + ], + "accounts": [ + { + "name": "ReferralState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": { + "array": [ + "u8", + 1 + ] + } + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "referredByState", + "type": "publicKey" + }, + { + "name": "destFeesMint", + "type": "publicKey" + }, + { + "name": "lookupTable", + "type": "publicKey" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 96 + ] + } + } + ] + } + }, + { + "name": "SolautoPosition", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": { + "array": [ + "u8", + 1 + ] + } + }, + { + "name": "positionId", + "type": { + "array": [ + "u8", + 1 + ] + } + }, + { + "name": "selfManaged", + "type": { + "defined": "PodBool" + } + }, + { + "name": "positionType", + "type": { + "defined": "PositionType" + } + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "position", + "type": { + "defined": "PositionData" + } + }, + { + "name": "state", + "type": { + "defined": "PositionState" + } + }, + { + "name": "rebalance", + "type": { + "defined": "RebalanceData" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u32", + 32 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "AutomationSettingsInp", + "type": { + "kind": "struct", + "fields": [ + { + "name": "targetPeriods", + "type": "u16" + }, + { + "name": "periodsPassed", + "type": "u16" + }, + { + "name": "unixStartDate", + "type": "u64" + }, + { + "name": "intervalSeconds", + "type": "u64" + } + ] + } + }, + { + "name": "AutomationSettings", + "type": { + "kind": "struct", + "fields": [ + { + "name": "targetPeriods", + "type": "u16" + }, + { + "name": "periodsPassed", + "type": "u16" + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "unixStartDate", + "type": "u64" + }, + { + "name": "intervalSeconds", + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "TokenAmount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "baseUnit", + "type": "u64" + }, + { + "name": "baseAmountUsdValue", + "type": "u64" + } + ] + } + }, + { + "name": "PositionTokenUsage", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "decimals", + "type": "u8" + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "amountUsed", + "type": { + "defined": "TokenAmount" + } + }, + { + "name": "amountCanBeUsed", + "type": { + "defined": "TokenAmount" + } + }, + { + "name": "baseAmountMarketPriceUsd", + "type": "u64" + }, + { + "name": "flashLoanFeeBps", + "type": "u16" + }, + { + "name": "borrowFeeBps", + "type": "u16" + }, + { + "name": "padding2", + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "DCASettingsInp", + "type": { + "kind": "struct", + "fields": [ + { + "name": "automation", + "type": { + "defined": "AutomationSettingsInp" + } + }, + { + "name": "dcaInBaseUnit", + "type": "u64" + }, + { + "name": "tokenType", + "type": { + "defined": "TokenType" + } + } + ] + } + }, + { + "name": "DCASettings", + "type": { + "kind": "struct", + "fields": [ + { + "name": "automation", + "type": { + "defined": "AutomationSettings" + } + }, + { + "name": "dcaInBaseUnit", + "type": "u64" + }, + { + "name": "tokenType", + "type": { + "defined": "TokenType" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 31 + ] + } + } + ] + } + }, + { + "name": "SolautoSettingsParametersInp", + "type": { + "kind": "struct", + "fields": [ + { + "name": "boostToBps", + "type": "u16" + }, + { + "name": "boostGap", + "type": "u16" + }, + { + "name": "repayToBps", + "type": "u16" + }, + { + "name": "repayGap", + "type": "u16" + }, + { + "name": "targetBoostToBps", + "type": { + "option": "u16" + } + }, + { + "name": "automation", + "type": { + "option": { + "defined": "AutomationSettingsInp" + } + } + } + ] + } + }, + { + "name": "SolautoSettingsParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "boostToBps", + "type": "u16" + }, + { + "name": "boostGap", + "type": "u16" + }, + { + "name": "repayToBps", + "type": "u16" + }, + { + "name": "repayGap", + "type": "u16" + }, + { + "name": "targetBoostToBps", + "type": "u16" + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "automation", + "type": { + "defined": "AutomationSettings" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "PositionState", + "type": { + "kind": "struct", + "fields": [ + { + "name": "liqUtilizationRateBps", + "type": "u16" + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "netWorth", + "type": { + "defined": "TokenAmount" + } + }, + { + "name": "supply", + "type": { + "defined": "PositionTokenUsage" + } + }, + { + "name": "debt", + "type": { + "defined": "PositionTokenUsage" + } + }, + { + "name": "maxLtvBps", + "type": "u16" + }, + { + "name": "liqThresholdBps", + "type": "u16" + }, + { + "name": "padding2", + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "lastUpdated", + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": [ + "u32", + 2 + ] + } + } + ] + } + }, + { + "name": "PositionData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lendingPlatform", + "type": { + "defined": "LendingPlatform" + } + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "protocolUserAccount", + "type": "publicKey" + }, + { + "name": "protocolSupplyAccount", + "type": "publicKey" + }, + { + "name": "protocolDebtAccount", + "type": "publicKey" + }, + { + "name": "settingParams", + "type": { + "defined": "SolautoSettingsParameters" + } + }, + { + "name": "dca", + "type": { + "defined": "DCASettings" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u32", + 4 + ] + } + } + ] + } + }, + { + "name": "RebalanceData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rebalanceType", + "type": { + "defined": "SolautoRebalanceType" + } + }, + { + "name": "padding1", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "rebalanceDirection", + "type": { + "defined": "RebalanceDirection" + } + }, + { + "name": "padding2", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "flashLoanAmount", + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "UpdateReferralStatesArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "referralFeesDestMint", + "type": { + "option": "publicKey" + } + }, + { + "name": "addressLookupTable", + "type": { + "option": "publicKey" + } + } + ] + } + }, + { + "name": "MarginfiOpenPositionData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "positionType", + "type": { + "defined": "PositionType" + } + }, + { + "name": "positionData", + "type": { + "defined": "UpdatePositionData" + } + }, + { + "name": "marginfiAccountSeedIdx", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "UpdatePositionData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "positionId", + "type": "u8" + }, + { + "name": "settingParams", + "type": { + "option": { + "defined": "SolautoSettingsParametersInp" + } + } + }, + { + "name": "dca", + "type": { + "option": { + "defined": "DCASettingsInp" + } + } + } + ] + } + }, + { + "name": "RebalanceSettings", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rebalanceType", + "type": { + "defined": "SolautoRebalanceType" + } + }, + { + "name": "targetLiqUtilizationRateBps", + "type": { + "option": "u16" + } + }, + { + "name": "targetInAmountBaseUnit", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "PodBool", + "type": { + "kind": "struct", + "fields": [ + { + "name": "val", + "type": "bool" + } + ] + } + }, + { + "name": "SolautoRebalanceType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "None" + }, + { + "name": "Regular" + }, + { + "name": "DoubleRebalanceWithFL" + }, + { + "name": "FLSwapThenRebalance" + }, + { + "name": "FLRebalanceThenSwap" + } + ] + } + }, + { + "name": "SolautoAction", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Deposit", + "fields": [ + "u64" + ] + }, + { + "name": "Borrow", + "fields": [ + "u64" + ] + }, + { + "name": "Repay", + "fields": [ + { + "defined": "TokenBalanceAmount" + } + ] + }, + { + "name": "Withdraw", + "fields": [ + { + "defined": "TokenBalanceAmount" + } + ] + } + ] + } + }, + { + "name": "LendingPlatform", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Marginfi" + } + ] + } + }, + { + "name": "PositionType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Leverage" + }, + { + "name": "SafeLoan" + } + ] + } + }, + { + "name": "TokenType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Supply" + }, + { + "name": "Debt" + } + ] + } + }, + { + "name": "RebalanceDirection", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Boost" + }, + { + "name": "Repay" + } + ] + } + }, + { + "name": "TokenBalanceAmount", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Some", + "fields": [ + "u64" + ] + }, + { + "name": "All" + } + ] + } + } + ], + "errors": [ + { + "code": 0, + "name": "IncorrectAccounts", + "msg": "Missing or incorrect accounts provided for the given instruction" + }, + { + "code": 1, + "name": "FailedAccountDeserialization", + "msg": "Failed to deserialize account data" + }, + { + "code": 2, + "name": "InvalidPositionSettings", + "msg": "Invalid position settings provided" + }, + { + "code": 3, + "name": "InvalidDCASettings", + "msg": "Invalid DCA configuration provided" + }, + { + "code": 4, + "name": "InvalidAutomationData", + "msg": "Invalid automation settings provided" + }, + { + "code": 5, + "name": "InvalidRebalanceCondition", + "msg": "Invalid position condition to rebalance" + }, + { + "code": 6, + "name": "InstructionIsCPI", + "msg": "Unable to invoke instruction through a CPI" + }, + { + "code": 7, + "name": "IncorrectInstructions", + "msg": "Incorrect set of instructions in the transaction" + }, + { + "code": 8, + "name": "IncorrectDebtAdjustment", + "msg": "Incorrect swap amount provided. Likely due to high price volatility" + } + ], + "metadata": { + "origin": "shank", + "address": "AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV" + } +} \ No newline at end of file diff --git a/idls/pyth.json b/idls/pyth.json new file mode 100644 index 00000000..06ebf4f1 --- /dev/null +++ b/idls/pyth.json @@ -0,0 +1,104 @@ +{ + "address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT", + "instructions": [], + "accounts": [ + { + "name": "PriceUpdateV2Account", + "type": { + "kind": "struct", + "fields": [ + { + "name": "writeAuthority", + "type": "publicKey" + }, + { + "name": "verificationLevel", + "type": "u8" + }, + { + "name": "priceMessage", + "type": { + "defined": "PriceMessage" + } + }, + { + "name": "postedSlot", + "type": "u64" + } + ] + } + } + ], + "types": [ + { + "name": "PriceMessage", + "type": { + "kind": "struct", + "fields": [ + { + "name": "feedId", + "type": "publicKey" + }, + { + "name": "price", + "type": "i64" + }, + { + "name": "conf", + "type": "u64" + }, + { + "name": "exponent", + "type": "i32" + }, + { + "name": "publishTime", + "type": "i64" + }, + { + "name": "prevPublishTime", + "type": "i64" + }, + { + "name": "emaPrice", + "type": "i64" + }, + { + "name": "emaConf", + "type": "u64" + } + ] + } + }, + { + "name": "PriceUpdateV2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "writeAuthority", + "type": "publicKey" + }, + { + "name": "verificationLevel", + "type": "u8" + }, + { + "name": "priceMessage", + "type": { + "defined": "PriceMessage" + } + }, + { + "name": "postedSlot", + "type": "u64" + } + ] + } + } + ], + "metadata": { + "origin": "anchor", + "address": "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT" + } +} \ No newline at end of file diff --git a/idls/solauto.json b/idls/solauto.json index a4efc846..2a0c6270 100644 --- a/idls/solauto.json +++ b/idls/solauto.json @@ -158,8 +158,7 @@ { "name": "referralAuthority", "isMut": true, - "isSigner": false, - "isOptional": true + "isSigner": false }, { "name": "feesDestinationTa", @@ -258,7 +257,7 @@ "isSigner": false }, { - "name": "protocolAccount", + "name": "lpUserAccount", "isMut": true, "isSigner": false }, @@ -507,7 +506,14 @@ "isSigner": false } ], - "args": [], + "args": [ + { + "name": "priceType", + "type": { + "defined": "PriceType" + } + } + ], "discriminant": { "type": "u8", "value": 7 @@ -553,7 +559,7 @@ }, { "name": "marginfiGroup", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -691,7 +697,7 @@ }, { "name": "marginfiGroup", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -911,7 +917,7 @@ "type": { "array": [ "u32", - 32 + 20 ] } } @@ -987,74 +993,48 @@ } }, { - "name": "TokenAmount", + "name": "DCASettingsInp", "type": { "kind": "struct", "fields": [ { - "name": "baseUnit", - "type": "u64" + "name": "automation", + "type": { + "defined": "AutomationSettingsInp" + } }, { - "name": "baseAmountUsdValue", + "name": "dcaInBaseUnit", "type": "u64" + }, + { + "name": "tokenType", + "type": { + "defined": "TokenType" + } } ] } }, { - "name": "PositionTokenUsage", + "name": "DCASettings", "type": { "kind": "struct", "fields": [ { - "name": "mint", - "type": "publicKey" - }, - { - "name": "decimals", - "type": "u8" - }, - { - "name": "padding1", - "type": { - "array": [ - "u8", - 7 - ] - } - }, - { - "name": "amountUsed", - "type": { - "defined": "TokenAmount" - } - }, - { - "name": "amountCanBeUsed", + "name": "automation", "type": { - "defined": "TokenAmount" + "defined": "AutomationSettings" } }, { - "name": "baseAmountMarketPriceUsd", + "name": "dcaInBaseUnit", "type": "u64" }, { - "name": "flashLoanFeeBps", - "type": "u16" - }, - { - "name": "borrowFeeBps", - "type": "u16" - }, - { - "name": "padding2", + "name": "tokenType", "type": { - "array": [ - "u8", - 4 - ] + "defined": "TokenType" } }, { @@ -1062,7 +1042,7 @@ "type": { "array": [ "u8", - 32 + 31 ] } } @@ -1070,48 +1050,70 @@ } }, { - "name": "DCASettingsInp", + "name": "TokenAmount", "type": { "kind": "struct", "fields": [ { - "name": "automation", - "type": { - "defined": "AutomationSettingsInp" - } - }, - { - "name": "dcaInBaseUnit", + "name": "baseUnit", "type": "u64" }, { - "name": "tokenType", - "type": { - "defined": "TokenType" - } + "name": "baseAmountUsdValue", + "type": "u64" } ] } }, { - "name": "DCASettings", + "name": "PositionTokenState", "type": { "kind": "struct", "fields": [ { - "name": "automation", + "name": "mint", + "type": "publicKey" + }, + { + "name": "decimals", + "type": "u8" + }, + { + "name": "padding1", "type": { - "defined": "AutomationSettings" + "array": [ + "u8", + 5 + ] } }, { - "name": "dcaInBaseUnit", + "name": "borrowFeeBps", + "type": "u16" + }, + { + "name": "amountUsed", + "type": { + "defined": "TokenAmount" + } + }, + { + "name": "amountCanBeUsed", + "type": { + "defined": "TokenAmount" + } + }, + { + "name": "baseAmountMarketPriceUsd", "type": "u64" }, { - "name": "tokenType", + "name": "padding2", "type": { - "defined": "TokenType" + "array": [ + "u8", + 8 + ] } }, { @@ -1119,7 +1121,7 @@ "type": { "array": [ "u8", - 31 + 32 ] } } @@ -1146,20 +1148,6 @@ { "name": "repayGap", "type": "u16" - }, - { - "name": "targetBoostToBps", - "type": { - "option": "u16" - } - }, - { - "name": "automation", - "type": { - "option": { - "defined": "AutomationSettingsInp" - } - } } ] } @@ -1185,31 +1173,12 @@ "name": "repayGap", "type": "u16" }, - { - "name": "targetBoostToBps", - "type": "u16" - }, - { - "name": "padding1", - "type": { - "array": [ - "u8", - 6 - ] - } - }, - { - "name": "automation", - "type": { - "defined": "AutomationSettings" - } - }, { "name": "padding", "type": { "array": [ - "u8", - 32 + "u32", + 24 ] } } @@ -1243,13 +1212,13 @@ { "name": "supply", "type": { - "defined": "PositionTokenUsage" + "defined": "PositionTokenState" } }, { "name": "debt", "type": { - "defined": "PositionTokenUsage" + "defined": "PositionTokenState" } }, { @@ -1270,7 +1239,7 @@ } }, { - "name": "lastUpdated", + "name": "lastRefreshed", "type": "u64" }, { @@ -1306,35 +1275,33 @@ } }, { - "name": "protocolUserAccount", + "name": "lpUserAccount", "type": "publicKey" }, { - "name": "protocolSupplyAccount", + "name": "lpSupplyAccount", "type": "publicKey" }, { - "name": "protocolDebtAccount", + "name": "lpDebtAccount", "type": "publicKey" }, { - "name": "settingParams", + "name": "settings", "type": { "defined": "SolautoSettingsParameters" } }, { - "name": "dca", - "type": { - "defined": "DCASettings" - } + "name": "lpPoolAccount", + "type": "publicKey" }, { "name": "padding", "type": { "array": [ "u32", - 4 + 20 ] } } @@ -1342,14 +1309,14 @@ } }, { - "name": "RebalanceData", + "name": "TokenBalanceChange", "type": { "kind": "struct", "fields": [ { - "name": "rebalanceType", + "name": "changeType", "type": { - "defined": "SolautoRebalanceType" + "defined": "TokenBalanceChangeType" } }, { @@ -1361,6 +1328,18 @@ ] } }, + { + "name": "amountUsd", + "type": "u64" + } + ] + } + }, + { + "name": "RebalanceStateValues", + "type": { + "kind": "struct", + "fields": [ { "name": "rebalanceDirection", "type": { @@ -1368,7 +1347,7 @@ } }, { - "name": "padding2", + "name": "padding1", "type": { "array": [ "u8", @@ -1377,15 +1356,102 @@ } }, { - "name": "flashLoanAmount", + "name": "targetSupplyUsd", + "type": "u64" + }, + { + "name": "targetDebtUsd", "type": "u64" }, + { + "name": "tokenBalanceChange", + "type": { + "defined": "TokenBalanceChange" + } + }, { "name": "padding", + "type": { + "array": [ + "u32", + 4 + ] + } + } + ] + } + }, + { + "name": "RebalanceInstructionData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "active", + "type": { + "defined": "PodBool" + } + }, + { + "name": "rebalanceType", + "type": { + "defined": "SolautoRebalanceType" + } + }, + { + "name": "swapType", + "type": { + "defined": "SwapType" + } + }, + { + "name": "padding1", "type": { "array": [ "u8", - 32 + 5 + ] + } + }, + { + "name": "flashLoanAmount", + "type": "u64" + }, + { + "name": "padding", + "type": { + "array": [ + "u32", + 4 + ] + } + } + ] + } + }, + { + "name": "RebalanceData", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ixs", + "type": { + "defined": "RebalanceInstructionData" + } + }, + { + "name": "values", + "type": { + "defined": "RebalanceStateValues" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u32", + 4 ] } } @@ -1428,12 +1494,6 @@ "type": { "defined": "UpdatePositionData" } - }, - { - "name": "marginfiAccountSeedIdx", - "type": { - "option": "u64" - } } ] } @@ -1448,7 +1508,7 @@ "type": "u8" }, { - "name": "settingParams", + "name": "settings", "type": { "option": { "defined": "SolautoSettingsParametersInp" @@ -1477,6 +1537,12 @@ "defined": "SolautoRebalanceType" } }, + { + "name": "swapInAmountBaseUnit", + "type": { + "option": "u64" + } + }, { "name": "targetLiqUtilizationRateBps", "type": { @@ -1484,9 +1550,25 @@ } }, { - "name": "targetInAmountBaseUnit", + "name": "flashLoanFeeBps", "type": { - "option": "u64" + "option": "u16" + } + }, + { + "name": "priceType", + "type": { + "option": { + "defined": "PriceType" + } + } + }, + { + "name": "swapType", + "type": { + "option": { + "defined": "SwapType" + } } } ] @@ -1505,7 +1587,7 @@ } }, { - "name": "SolautoRebalanceType", + "name": "TokenBalanceChangeType", "type": { "kind": "enum", "variants": [ @@ -1513,13 +1595,16 @@ "name": "None" }, { - "name": "Regular" + "name": "PreSwapDeposit" }, { - "name": "DoubleRebalanceWithFL" + "name": "PostSwapDeposit" }, { - "name": "SingleRebalanceWithFL" + "name": "PostRebalanceWithdrawSupplyToken" + }, + { + "name": "PostRebalanceWithdrawDebtToken" } ] } @@ -1604,6 +1689,9 @@ "type": { "kind": "enum", "variants": [ + { + "name": "None" + }, { "name": "Boost" }, @@ -1613,6 +1701,54 @@ ] } }, + { + "name": "RebalanceStep", + "type": { + "kind": "enum", + "variants": [ + { + "name": "PreSwap" + }, + { + "name": "PostSwap" + } + ] + } + }, + { + "name": "SolautoRebalanceType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Regular" + }, + { + "name": "DoubleRebalanceWithFL" + }, + { + "name": "FLSwapThenRebalance" + }, + { + "name": "FLRebalanceThenSwap" + } + ] + } + }, + { + "name": "SwapType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "ExactIn" + }, + { + "name": "ExactOut" + } + ] + } + }, { "name": "TokenBalanceAmount", "type": { @@ -1629,13 +1765,27 @@ } ] } + }, + { + "name": "PriceType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Realtime" + }, + { + "name": "Ema" + } + ] + } } ], "errors": [ { "code": 0, "name": "IncorrectAccounts", - "msg": "Missing or incorrect accounts provided for the given instruction" + "msg": "Missing or incorrect accounts provided for the given instructions" }, { "code": 1, @@ -1644,38 +1794,68 @@ }, { "code": 2, - "name": "InvalidPositionSettings", - "msg": "Invalid position settings provided" + "name": "InvalidBoostToSetting", + "msg": "Invalid Boost-to param" }, { "code": 3, + "name": "InvalidBoostGapSetting", + "msg": "Invalid Boost gap param" + }, + { + "code": 4, + "name": "InvalidRepayToSetting", + "msg": "Invalid repay-to param" + }, + { + "code": 5, + "name": "InvalidRepayGapSetting", + "msg": "Invalid repay gap param" + }, + { + "code": 6, + "name": "InvalidRepayFromSetting", + "msg": "Invalid repay-from (repay-to + repay gap)" + }, + { + "code": 7, "name": "InvalidDCASettings", "msg": "Invalid DCA configuration provided" }, { - "code": 4, + "code": 8, "name": "InvalidAutomationData", "msg": "Invalid automation settings provided" }, { - "code": 5, + "code": 9, "name": "InvalidRebalanceCondition", "msg": "Invalid position condition to rebalance" }, { - "code": 6, + "code": 10, "name": "InstructionIsCPI", "msg": "Unable to invoke instruction through a CPI" }, { - "code": 7, + "code": 11, "name": "IncorrectInstructions", - "msg": "Incorrect set of instructions in the transaction" + "msg": "Incorrect set of instructions or instruction data in the transaction" }, { - "code": 8, + "code": 12, "name": "IncorrectDebtAdjustment", "msg": "Incorrect swap amount provided. Likely due to high price volatility" + }, + { + "code": 13, + "name": "InvalidRebalanceMade", + "msg": "Invalid rebalance was made. Target supply USD and target debt USD was not met" + }, + { + "code": 14, + "name": "NonAuthorityProvidedTargetLTV", + "msg": "Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority" } ], "metadata": { diff --git a/package.json b/package.json index 4657b6be..0f73de93 100644 --- a/package.json +++ b/package.json @@ -3,33 +3,45 @@ "push": "git add . && git commit -m 'wip' && git push", "format:rust": "find ./programs/* -name '*.rs' -exec rustfmt {} +", "build:rs:local": "cargo build-sbf -- --package solauto --features local", + "build:rs:staging": "cargo build-sbf -- --package solauto --features \"test staging\"", "build:rs:test": "cargo build-sbf -- --package solauto --features test", - "build:rs:prod": "cargo build-sbf", - "verified-build": "solana-verify build --library-name solauto", + "build:rs:prod": "solana-verify build --library-name solauto --base-image solanafoundation/solana-verifiable-build@sha256:f3d3a4adaa8008644fc4535373c6818275c7e35a0b07660890b4a95ef434221e", "build:ts": "cd solauto-sdk && npm run build", - "deploy:buffer": "solana program write-buffer target/deploy/solauto.so --buffer ~/.config/solana/buffer.json --max-sign-attempts 50", - "deploy:rs:test": "solana program deploy target/deploy/solauto.so --keypair ~/.config/solana/solauto-auth.json --program-id TesTjfQ6TbXv96Tv6fqr95XTZ1LYPxtkafmShN9PjBp --use-rpc --buffer ~/.config/solana/buffer.json --max-sign-attempts 50", + "deploy:buffer": "solana program write-buffer target/deploy/solauto.so --use-rpc --max-sign-attempts 50 --with-compute-unit-price 0.00001", + "deploy:rs:test": "solana program deploy target/deploy/solauto.so --program-id TesTjfQ6TbXv96Tv6fqr95XTZ1LYPxtkafmShN9PjBp --use-rpc --buffer ~/.config/solana/buffer.json --max-sign-attempts 50", "deploy:ts": "cd solauto-sdk && npm publish", "generate": "pnpm generate:idl && pnpm generate:clients", - "generate:idl": "shank idl --crate-root programs/solauto --out-dir idls --out-filename solauto.json", + "generate:idl": "shank idl --crate-root programs/solauto --out-dir idls --out-filename solauto.json -p AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV", "generate:clients": "node ./generateClients.cjs && pnpm format:rust", - "test:rs:unit": "cargo test --package solauto -- --nocapture", - "test:rs:solana": "cd programs/solauto-sdk && cargo test-sbf", - "test:rs:all": "pnpm test:rs:unit && pnpm test:rs:solana", + "test:all": "pnpm build:rs:local && pnpm test:rs:all && pnpm test:ts:all", + "test:rs:unit": "cargo test --package solauto", + "test:rs:txs": "cd programs/solauto-sdk && cargo test-sbf", + "test:rs:all": "pnpm test:rs:txs && pnpm test:rs:unit", "test:ts:unit": "cd solauto-sdk && pnpm test:unit", "test:ts:txs": "cd solauto-sdk && pnpm test:txs", "test:ts:all": "cd solauto-sdk && pnpm test:all", - "update-lut:solauto": "cd solauto-sdk && pnpm update-lut:solauto", - "update-lut:marginfi": "cd solauto-sdk && pnpm update-lut:marginfi", - "create-token-accounts": "cd solauto-sdk && pnpm create-token-accounts", - "create-ism-accounts": "cd solauto-sdk && pnpm create-sm-accounts", - "solana-verify": "solana-verify verify-from-repo --remote -um --program-id AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV https://github.com/haven-fi/solauto --library-name solauto" + "export-pda-tx": "solana-verify export-pda-tx https://github.com/haven-fi/solauto --program-id AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV --uploader B1ZqSSiAe7DKjh444gkxXAgoeyz2dZzBMwziBtt8mJ3T --encoding base58 --compute-unit-price 0 --library-name solauto --base-image solanafoundation/solana-verifiable-build@sha256:f3d3a4adaa8008644fc4535373c6818275c7e35a0b07660890b4a95ef434221e", + "verify-submit-job": "solana-verify remote submit-job --program-id AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV --uploader B1ZqSSiAe7DKjh444gkxXAgoeyz2dZzBMwziBtt8mJ3T", + "solana-verify": "solana-verify verify-from-repo --remote -um --skip-build --program-id AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV https://github.com/haven-fi/solauto --library-name solauto --base-image solanafoundation/solana-verifiable-build@sha256:f3d3a4adaa8008644fc4535373c6818275c7e35a0b07660890b4a95ef434221e", + "sandbox": "cd solauto-sdk && npx ts-node local/txSandbox.ts", + "update-lut:solauto": "cd solauto-sdk && npx ts-node local/updateSolautoLUT.ts", + "update-lut:marginfi": "cd solauto-sdk && npx ts-node local/updateMarginfiLUT.ts", + "patch-lut": "cd solauto-sdk && npx ts-node local/patchLUT.ts", + "create-token-accounts": "cd solauto-sdk && npx ts-node local/createTokenAccounts.ts", + "prepare-accounts": "pnpm create-token-accounts && pnpm update-lut:solauto && pnpm update-lut:marginfi", + "log-pos": "cd solauto-sdk && npx ts-node local/logPositions.ts", + "log-pos:test": "cd solauto-sdk && npx ts-node local/logPositions.ts --filter=false --env=Staging" }, "dependencies": { - "@metaplex-foundation/kinobi": "^0.18.5" + "@metaplex-foundation/kinobi": "^0.18.5", + "@mrgnlabs/marginfi-client-v2": "^6.0.1", + "@mrgnlabs/mrgn-common": "^2.0.2", + "borsh": "^2.0.0", + "cross-env": "^10.0.0" }, "devDependencies": { "solauto-sdk": "file:solauto-sdk", + "ts-node": "^10.9.2", "typescript": "^4.9.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ac11634..8a845db2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,55 +1,1108 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - '@metaplex-foundation/kinobi': - specifier: ^0.18.5 - version: 0.18.5(fastestsmallesttextencoderdecoder@1.0.22) +importers: + + .: + dependencies: + '@metaplex-foundation/kinobi': + specifier: ^0.18.5 + version: 0.18.5(fastestsmallesttextencoderdecoder@1.0.22) + '@mrgnlabs/marginfi-client-v2': + specifier: ^6.0.1 + version: 6.1.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@mrgnlabs/mrgn-common': + specifier: ^2.0.2 + version: 2.0.3(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + borsh: + specifier: ^2.0.0 + version: 2.0.0 + cross-env: + specifier: ^10.0.0 + version: 10.0.0 + devDependencies: + solauto-sdk: + specifier: file:solauto-sdk + version: '@haven-fi/solauto-sdk@file:solauto-sdk(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)' + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@24.0.14)(typescript@4.9.5) + typescript: + specifier: ^4.9.5 + version: 4.9.5 -devDependencies: - solauto-sdk: - specifier: file:solauto-sdk - version: file:solauto-sdk(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - typescript: - specifier: ^4.9.5 - version: 4.9.5 +packages: + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@common.js/quick-lru@7.0.0': + resolution: {integrity: sha512-DO3vApnH1vLizdWRSqzhg+S956vptHhtO+vw6PP6StJGDtaAGyfKSSPPGYWguJZu/Jlti6l6m6jB8NrKEQexLg==} + engines: {node: '>=18'} + + '@coral-xyz/anchor-errors@0.30.1': + resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} + engines: {node: '>=10'} + + '@coral-xyz/anchor-errors@0.31.1': + resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==} + engines: {node: '>=10'} + + '@coral-xyz/anchor@0.29.0': + resolution: {integrity: sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==} + engines: {node: '>=11'} + + '@coral-xyz/anchor@0.30.1': + resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} + engines: {node: '>=11'} + + '@coral-xyz/anchor@0.31.1': + resolution: {integrity: sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==} + engines: {node: '>=17'} + + '@coral-xyz/borsh@0.29.0': + resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==} + engines: {node: '>=10'} + peerDependencies: + '@solana/web3.js': ^1.68.0 + + '@coral-xyz/borsh@0.30.1': + resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} + engines: {node: '>=10'} + peerDependencies: + '@solana/web3.js': ^1.68.0 + + '@coral-xyz/borsh@0.31.1': + resolution: {integrity: sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==} + engines: {node: '>=10'} + peerDependencies: + '@solana/web3.js': ^1.69.0 + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@grpc/grpc-js@1.13.4': + resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@haven-fi/solauto-sdk@file:solauto-sdk': + resolution: {directory: solauto-sdk, type: directory} + + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@jup-ag/api@6.0.44': + resolution: {integrity: sha512-C6oYi+wvNi07VlmWhT8+ZDMqTJuF+MiHuNBE65cYuBH6C4l0JfwpXaj6mKazAdVqVFWgngr3s2+0tdF5nDH4tA==} + + '@metaplex-foundation/kinobi@0.18.5': + resolution: {integrity: sha512-qh4h4xGB+PHR5o4rcZki+wsIeZYi6R9SRV5UMqGi/rfDJXk0ckAZ+fxdam+mCc4N8HWLLWmEJbF5gx8E5K0fOA==} + + '@metaplex-foundation/umi-bundle-defaults@0.9.2': + resolution: {integrity: sha512-kV3tfvgvRjVP1p9OFOtH+ibOtN9omVJSwKr0We4/9r45e5LTj+32su0V/rixZUkG1EZzzOYBsxhtIE0kIw/Hrw==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi-downloader-http@0.9.2': + resolution: {integrity: sha512-tzPT9hBwenzTzAQg07rmsrqZfgguAXELbcJrsYMoASp5VqWFXYIP00g94KET6XLjWUXH4P1J2zoa6hGennPXHA==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + + '@metaplex-foundation/umi-eddsa-web3js@0.9.2': + resolution: {integrity: sha512-hhPCxXbYIp4BC4z9gK78sXpWLkNSrfv4ndhF5ruAkdIp7GcRVYKj0QnOUO6lGYGiIkNlw20yoTwOe1CT//OfTQ==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi-http-fetch@0.9.2': + resolution: {integrity: sha512-YCZuBu24T9ZzEDe4+w12LEZm/fO9pkyViZufGgASC5NX93814Lvf6Ssjn/hZzjfA7CvZbvLFbmujc6CV3Q/m9Q==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + + '@metaplex-foundation/umi-options@0.8.9': + resolution: {integrity: sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==} + + '@metaplex-foundation/umi-program-repository@0.9.2': + resolution: {integrity: sha512-g3+FPqXEmYsBa8eETtUE2gb2Oe3mqac0z3/Ur1TvAg5TtIy3mzRzOy/nza+sgzejnfcxcVg835rmpBaxpBnjDA==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + + '@metaplex-foundation/umi-public-keys@0.8.9': + resolution: {integrity: sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==} + + '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2': + resolution: {integrity: sha512-YRwVf6xH0jPBAUgMhEPi+UbjioAeqTXmjsN2TnmQCPAmHbrHrMRj0rlWYwFLWAgkmoxazYrXP9lqOFRrfOGAEA==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + + '@metaplex-foundation/umi-rpc-web3js@0.9.2': + resolution: {integrity: sha512-MqcsBz8B4wGl6jxsf2Jo/rAEpYReU9VCSR15QSjhvADHMmdFxCIZCCAgE+gDE2Vuanfl437VhOcP3g5Uw8C16Q==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi-serializer-data-view@0.9.2': + resolution: {integrity: sha512-5vGptadJxUxvUcyrwFZxXlEc6Q7AYySBesizCtrBFUY8w8PnF2vzmS45CP1MLySEATNH6T9mD4Rs0tLb87iQyA==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + + '@metaplex-foundation/umi-serializers-core@0.8.9': + resolution: {integrity: sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==} + + '@metaplex-foundation/umi-serializers-encodings@0.8.9': + resolution: {integrity: sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==} + + '@metaplex-foundation/umi-serializers-numbers@0.8.9': + resolution: {integrity: sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==} + + '@metaplex-foundation/umi-serializers@0.9.0': + resolution: {integrity: sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==} + + '@metaplex-foundation/umi-signer-wallet-adapters@0.9.2': + resolution: {integrity: sha512-DFG0ZFocKG8briypSkG9bGUTVsWpAgYugsl2BzTygkGExc4evWfF4Sb1F2C2w9FdrA9ESZM1gpLX9xtx5taOXg==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2': + resolution: {integrity: sha512-fR1Kf21uylMFd1Smkltmj4jTNxhqSWf416owsJ+T+cvJi2VCOcOwq/3UFzOrpz78fA0RhsajKYKj0HYsRnQI1g==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi-web3js-adapters@0.9.2': + resolution: {integrity: sha512-RQqUTtHYY9fmEMnq7s3Hiv/81flGaoI0ZVVoafnFVaQLnxU6QBKxtboRZHk43XtD9CiFh5f9izrMJX7iK7KlOA==} + peerDependencies: + '@metaplex-foundation/umi': ^0.9.2 + '@solana/web3.js': ^1.72.0 + + '@metaplex-foundation/umi@0.9.2': + resolution: {integrity: sha512-9i4Acm4pruQfJcpRrc2EauPBwkfDN0I9QTvJyZocIlKgoZwD6A6wH0PViH1AjOVG5CQCd1YI3tJd5XjYE1ElBw==} + + '@mrgnlabs/marginfi-client-v2@6.1.0': + resolution: {integrity: sha512-8T93tWsp6BbFyKocQBTxaJ1RhslnseNtMO+TWPySpr8iNaUmFax15fuA6Ml/q/wgjZHu2sqNPy501UeDnCb/kw==} + + '@mrgnlabs/mrgn-common@2.0.3': + resolution: {integrity: sha512-OrCvFJv2mCHVqTDhVRRacLO8e1yHSXP1+PrQEAuxGHTnP8qQE51yFwRR+Ne8Gz6VMaD50BxWqsgBlT8/6hOStw==} + + '@noble/curves@1.9.4': + resolution: {integrity: sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/ed25519@1.7.5': + resolution: {integrity: sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@prettier/sync@0.5.5': + resolution: {integrity: sha512-6BMtNr7aQhyNcGzmumkL0tgr1YQGfm9d7ZdmRpWqWuqpc9vZBind4xMe5NMiRECOhjuSiWHfBWLBnXkpeE90bw==} + peerDependencies: + prettier: '*' + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@pythnetwork/price-service-sdk@1.8.0': + resolution: {integrity: sha512-tFZ1thj3Zja06DzPIX2dEWSi7kIfIyqreoywvw5NQ3Z1pl5OJHQGMEhxt6Li3UCGSp2ooYZS9wl8/8XfrfrNSA==} + + '@pythnetwork/pyth-solana-receiver@0.8.2': + resolution: {integrity: sha512-WrrdwwhSYvvB5vJEL+SfPnfuxgkRKMeKdZvGFFwe6ENrMhrQCM05oDkvNNYfXATLcpQGRAyBu9l1xIxUxixpqw==} + + '@pythnetwork/solana-utils@0.4.2': + resolution: {integrity: sha512-hKo7Bcs/kDWA5Fnqhg9zJSB94NMoUDIDjHjSi/uvZOzwizISUQI6oY3LWd2CXzNh4f8djjY2BS5iNHaM4cm8Bw==} + + '@solana/buffer-layout-utils@0.2.0': + resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} + engines: {node: '>= 10'} + + '@solana/buffer-layout@4.0.1': + resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} + engines: {node: '>=5.10'} + + '@solana/codecs-core@2.0.0-preview.1': + resolution: {integrity: sha512-0y3kgFSJAOApNGefEOgLRMiXOHVmcF29Xw3zmR4LDUABvytG3ecKMXGz5oLt3vdaU2CyN3tuUVeGmQjrd0U1kw==} + + '@solana/codecs-core@2.0.0-rc.1': + resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-core@2.3.0': + resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-data-structures@2.0.0-rc.1': + resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-numbers@2.0.0-preview.1': + resolution: {integrity: sha512-NFA8itgcYUY3hkWMBpVRozd2poy1zfOvkIWZKx/D69oIMUtQTBpKrodRVBuhlBkAv12vDNkFljqVySpcMZMl7A==} + + '@solana/codecs-numbers@2.0.0-rc.1': + resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-numbers@2.3.0': + resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-strings@2.0.0-preview.1': + resolution: {integrity: sha512-kBAxE9ZD5/c8j9CkPxqc55dbo7R50Re3k94SXR+k13DZCCs37Fyn0/mAkw/S95fofbi/zsi4jSfmsT5vCZD75A==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + + '@solana/codecs-strings@2.0.0-rc.1': + resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5' + + '@solana/codecs@2.0.0-rc.1': + resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} + peerDependencies: + typescript: '>=5' + + '@solana/errors@2.0.0-preview.1': + resolution: {integrity: sha512-mnBWfLVwMH4hxwb4sWZ7G7djQCMsyymjysvUPDiF89LueTBm1hfdxUv8Cy1uUCGsJ3jO3scdPwB4noOgr0rG/g==} + hasBin: true + + '@solana/errors@2.0.0-rc.1': + resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} + hasBin: true + peerDependencies: + typescript: '>=5' + + '@solana/errors@2.3.0': + resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' + + '@solana/options@2.0.0-rc.1': + resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} + peerDependencies: + typescript: '>=5' + + '@solana/spl-token-group@0.0.7': + resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token-metadata@0.1.6': + resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token@0.1.8': + resolution: {integrity: sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==} + engines: {node: '>= 10'} + + '@solana/spl-token@0.4.13': + resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.5 + + '@solana/wallet-adapter-base@0.9.27': + resolution: {integrity: sha512-kXjeNfNFVs/NE9GPmysBRKQ/nf+foSaq3kfVSeMcO/iVgigyRmB551OjU3WyAolLG/1jeEfKLqF9fKwMCRkUqg==} + engines: {node: '>=20'} + peerDependencies: + '@solana/web3.js': ^1.98.0 + + '@solana/wallet-standard-features@1.3.0': + resolution: {integrity: sha512-ZhpZtD+4VArf6RPitsVExvgkF+nGghd1rzPjd97GmBximpnt1rsUxMOEyoIEuH3XBxPyNB6Us7ha7RHWQR+abg==} + engines: {node: '>=16'} + + '@solana/web3.js@1.77.4': + resolution: {integrity: sha512-XdN0Lh4jdY7J8FYMyucxCwzn6Ga2Sr1DHDWRbqVzk7ZPmmpSPOVWHzO67X1cVT+jNi1D6gZi2tgjHgDPuj6e9Q==} + + '@solana/web3.js@1.98.2': + resolution: {integrity: sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@switchboard-xyz/common@3.4.1': + resolution: {integrity: sha512-TropBlBYuDeBnmGHkPSmgC3clLqAxy51ZGbwk4ejAgadnszWOgYHcH7taRG4Ha17DYSCWw/LGMBKbunGo+Aoaw==} + engines: {node: '>=20'} + + '@switchboard-xyz/common@4.1.11': + resolution: {integrity: sha512-zNclN+7Be28qL5dFlScL4qGCPAe/kLUQ7umNsT9+V9WC3jB3uUdYVlzsQzJc3WVYAGn0xc6gKJ0S/XW7NAxhzQ==} + engines: {node: '>=20'} + + '@switchboard-xyz/on-demand@1.2.67': + resolution: {integrity: sha512-rtGA+9tQHb/xQnUFgVzyaOs2uKgnwP7OqYQLEOjTDdNTEEgU8OTR72ujluOEYB9LMByb7h9SRuOR353vxYQhYA==} + engines: {node: '>= 18'} + deprecated: deprecated in favor of 2.x.x + + '@switchboard-xyz/on-demand@2.17.4': + resolution: {integrity: sha512-jZtnjcI58Leet2GCABoK0+snfi3Sg4Kn997MnsE0aMQFWCQ/zsy2oI81x7Nk+kvM+3Q5OEAG7JpAsN00BMlgQw==} + engines: {node: '>= 18'} + + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@24.0.14': + resolution: {integrity: sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==} + + '@types/uuid@8.3.4': + resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + + '@types/ws@7.4.7': + resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@wallet-standard/base@1.1.0': + resolution: {integrity: sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==} + engines: {node: '>=16'} + + '@wallet-standard/features@1.1.0': + resolution: {integrity: sha512-hiEivWNztx73s+7iLxsuD1sOJ28xtRix58W7Xnz4XzzA/pF0+aicnWgjOdA10doVDEDZdUuZCIIqG96SFNlDUg==} + engines: {node: '>=16'} + + '@zod/core@0.11.6': + resolution: {integrity: sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA==} + + a-sync-waterfall@1.0.1: + resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} + + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + engines: {node: '>=0.4.0'} + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + + base-x@3.0.11: + resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} + + base-x@4.0.1: + resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} + + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + bigint-buffer@1.1.5: + resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} + engines: {node: '>= 10.0.0'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bn.js@5.2.2: + resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + + borsh@0.7.0: + resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + + borsh@2.0.0: + resolution: {integrity: sha512-kc9+BgR3zz9+cjbwM8ODoUB4fs3X3I5A/HtX7LZKxCLaMrEeDFoBpnhZY//DTS1VZBSs6S5v46RZRbZjRFspEg==} + + bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + + bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer-layout@1.2.2: + resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} + engines: {node: '>=4.5'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-env@10.0.0: + resolution: {integrity: sha512-aU8qlEK/nHYtVuN4p7UQgAwVljzMg8hB4YK5ThRqD2l/ziSnryncPNn7bMLt5cFYsKVKBh8HqLqyCoTupEUu7Q==} + engines: {node: '>=20'} + hasBin: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-hash@1.3.0: + resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} + engines: {node: '>=8'} + + crypto-hash@3.1.0: + resolution: {integrity: sha512-HR8JlZ+Dn54Lc5gGWZJxJitWbOCUzWb9/AlyONGecBnYZ+n/ONvt0gQnEzDNpJMYeRrYO7KogQ4qwyTPKnWKEw==} + engines: {node: '>=18'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + delay@5.0.0: + resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} + engines: {node: '>=10'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@10.0.0: + resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} + engines: {node: '>=10'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + + es6-promisify@5.0.0: + resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} + + fast-stable-stringify@1.0.0: + resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + + fastestsmallesttextencoderdecoder@1.0.22: + resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' -packages: + jayson@4.2.0: + resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} + engines: {node: '>=8'} + hasBin: true - /@babel/runtime@7.25.7: - resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: true + jito-ts@3.0.1: + resolution: {integrity: sha512-TSofF7KqcwyaWGjPaSYC8RDoNBY1TPRNBHdrw24bdIi7mQ5bFEDdYK3D//llw/ml8YDvcZlgd644WxhjLTS9yg==} - /@brokerloop/ttlcache@3.2.3: - resolution: {integrity: sha512-kZWoyJGBYTv1cL5oHBYEixlJysJBf2RVnub3gbclD+dwaW9aKubbHzbZ9q1q6bONosxaOqMsoBorOrZKzBDiqg==} - dependencies: - '@soncodi/signal': 2.0.7 - dev: true + js-sha256@0.11.1: + resolution: {integrity: sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==} - /@coral-xyz/anchor-errors@0.30.1: - resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-stable-stringify@1.3.0: + resolution: {integrity: sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==} + engines: {node: '>= 0.4'} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + jsonify@0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-synchronized@0.4.2: + resolution: {integrity: sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + numeral@2.0.6: + resolution: {integrity: sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==} + + nunjucks@3.2.4: + resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==} + engines: {node: '>= 6.9.0'} + hasBin: true + peerDependencies: + chokidar: ^3.3.0 + peerDependenciesMeta: + chokidar: + optional: true + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + protobufjs@7.5.3: + resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} + engines: {node: '>=12.0.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rpc-websockets@7.11.0: + resolution: {integrity: sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==} + deprecated: deprecate 7.11.0 + + rpc-websockets@7.11.2: + resolution: {integrity: sha512-pL9r5N6AVHlMN/vT98+fcO+5+/UcPLf/4tq+WUaid/PPUGS/ttJ3y8e9IqmaWKtShNAysMSjkczuEA49NuV7UQ==} + + rpc-websockets@9.1.1: + resolution: {integrity: sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-json@1.9.1: + resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + superstruct@0.14.2: + resolution: {integrity: sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==} + + superstruct@0.15.5: + resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} + + superstruct@1.0.4: + resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} + engines: {node: '>=14.0.0'} + + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + text-encoding-utf-8@1.0.2: + resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - dev: true - /@coral-xyz/anchor@0.30.1: - resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} - engines: {node: '>=11'} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + zod@4.0.0-beta.20250505T195954: + resolution: {integrity: sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg==} + +snapshots: + + '@babel/runtime@7.27.6': {} + + '@common.js/quick-lru@7.0.0': {} + + '@coral-xyz/anchor-errors@0.30.1': {} + + '@coral-xyz/anchor-errors@0.31.1': {} + + '@coral-xyz/anchor@0.29.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.8.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0 + crypto-hash: 1.3.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + snake-case: 3.0.4 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@coral-xyz/anchor@0.30.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: '@coral-xyz/anchor-errors': 0.30.1 - '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.95.4) - '@noble/hashes': 1.5.0 - '@solana/web3.js': 1.95.4 - bn.js: 5.2.1 + '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.8.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 bs58: 4.0.1 buffer-layout: 1.2.2 camelcase: 6.3.0 - cross-fetch: 3.1.8 + cross-fetch: 3.2.0 crypto-hash: 1.3.0 eventemitter3: 4.0.7 pako: 2.1.0 @@ -59,443 +1112,395 @@ packages: transitivePeerDependencies: - bufferutil - encoding + - typescript - utf-8-validate - dev: true - /@coral-xyz/borsh@0.30.1(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.68.0 + '@coral-xyz/anchor@0.31.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor-errors': 0.31.1 + '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.8.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.95.4 - bn.js: 5.2.1 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 buffer-layout: 1.2.2 - dev: true - /@jup-ag/api@6.0.29: - resolution: {integrity: sha512-1gUY1txmL/gG14VDZMjeKVqhSzeg4L2cImdC7dGLYB5DP5D5lwpR+FarvhqehEi+63/RWOUKTTU4fablGpeIUQ==} - dev: true + '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + buffer-layout: 1.2.2 + + '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + buffer-layout: 1.2.2 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@epic-web/invariant@1.0.0': {} - /@metaplex-foundation/beet-solana@0.4.0: - resolution: {integrity: sha512-B1L94N3ZGMo53b0uOSoznbuM5GBNJ8LwSeznxBxJ+OThvfHQ4B5oMUqb+0zdLRfkKGS7Q6tpHK9P+QK0j3w2cQ==} + '@grpc/grpc-js@1.13.4': dependencies: - '@metaplex-foundation/beet': 0.7.1 - '@solana/web3.js': 1.95.3 + '@grpc/proto-loader': 0.7.15 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.3 + yargs: 17.7.2 + + '@haven-fi/solauto-sdk@file:solauto-sdk(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.31.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@jup-ag/api': 6.0.44 + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-bundle-defaults': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-signer-wallet-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@switchboard-xyz/common': 3.4.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@switchboard-xyz/on-demand': 2.17.4(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + axios: 1.10.0 + big.js: 6.2.2 bs58: 5.0.0 - debug: 4.3.7 + cross-fetch: 4.1.0 + dotenv: 16.6.1 + rpc-websockets: 7.11.0 transitivePeerDependencies: - bufferutil + - debug - encoding - - supports-color + - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - dev: true - /@metaplex-foundation/beet@0.7.1: - resolution: {integrity: sha512-hNCEnS2WyCiYyko82rwuISsBY3KYpe828ubsd2ckeqZr7tl0WVLivGkoyA/qdiaaHEBGdGl71OpfWa2rqL3DiA==} + '@isaacs/ttlcache@1.4.1': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.9': dependencies: - ansicolors: 0.3.2 - bn.js: 5.2.1 - debug: 4.3.7 - transitivePeerDependencies: - - supports-color - dev: true + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - /@metaplex-foundation/cusper@0.0.2: - resolution: {integrity: sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA==} - dev: true + '@js-sdsl/ordered-map@4.4.2': {} - /@metaplex-foundation/kinobi@0.18.5(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-qh4h4xGB+PHR5o4rcZki+wsIeZYi6R9SRV5UMqGi/rfDJXk0ckAZ+fxdam+mCc4N8HWLLWmEJbF5gx8E5K0fOA==} + '@jup-ag/api@6.0.44': {} + + '@metaplex-foundation/kinobi@0.18.5(fastestsmallesttextencoderdecoder@1.0.22)': dependencies: - '@noble/hashes': 1.5.0 - '@prettier/sync': 0.5.2(prettier@3.3.3) + '@noble/hashes': 1.8.0 + '@prettier/sync': 0.5.5(prettier@3.6.2) '@solana/codecs-strings': 2.0.0-preview.1(fastestsmallesttextencoderdecoder@1.0.22) chalk: 4.1.2 - json-stable-stringify: 1.1.1 + json-stable-stringify: 1.3.0 nunjucks: 3.2.4 - prettier: 3.3.3 + prettier: 3.6.2 transitivePeerDependencies: - chokidar - fastestsmallesttextencoderdecoder - dev: false - /@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-kV3tfvgvRjVP1p9OFOtH+ibOtN9omVJSwKr0We4/9r45e5LTj+32su0V/rixZUkG1EZzzOYBsxhtIE0kIw/Hrw==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 '@metaplex-foundation/umi-downloader-http': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) + '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) '@metaplex-foundation/umi-http-fetch': 0.9.2(@metaplex-foundation/umi@0.9.2) '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2) '@metaplex-foundation/umi-rpc-chunk-get-accounts': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) + '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) '@metaplex-foundation/umi-serializer-data-view': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@solana/web3.js': 1.95.4 + '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) transitivePeerDependencies: - encoding - dev: true - /@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2): - resolution: {integrity: sha512-tzPT9hBwenzTzAQg07rmsrqZfgguAXELbcJrsYMoASp5VqWFXYIP00g94KET6XLjWUXH4P1J2zoa6hGennPXHA==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 + '@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 - dev: true - /@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-hhPCxXbYIp4BC4z9gK78sXpWLkNSrfv4ndhF5ruAkdIp7GcRVYKj0QnOUO6lGYGiIkNlw20yoTwOe1CT//OfTQ==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@noble/curves': 1.6.0 - '@solana/web3.js': 1.95.4 - dev: true + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@noble/curves': 1.9.4 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) - /@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2): - resolution: {integrity: sha512-YCZuBu24T9ZzEDe4+w12LEZm/fO9pkyViZufGgASC5NX93814Lvf6Ssjn/hZzjfA7CvZbvLFbmujc6CV3Q/m9Q==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 + '@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: true - /@metaplex-foundation/umi-options@0.8.9: - resolution: {integrity: sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==} - dev: true + '@metaplex-foundation/umi-options@0.8.9': {} - /@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2): - resolution: {integrity: sha512-g3+FPqXEmYsBa8eETtUE2gb2Oe3mqac0z3/Ur1TvAg5TtIy3mzRzOy/nza+sgzejnfcxcVg835rmpBaxpBnjDA==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 + '@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 - dev: true - /@metaplex-foundation/umi-public-keys@0.8.9: - resolution: {integrity: sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==} + '@metaplex-foundation/umi-public-keys@0.8.9': dependencies: - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - dev: true - - /@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2): - resolution: {integrity: sha512-YRwVf6xH0jPBAUgMhEPi+UbjioAeqTXmjsN2TnmQCPAmHbrHrMRj0rlWYwFLWAgkmoxazYrXP9lqOFRrfOGAEA==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 + '@metaplex-foundation/umi-serializers-encodings': 0.8.9 + + '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 - dev: true - /@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-MqcsBz8B4wGl6jxsf2Jo/rAEpYReU9VCSR15QSjhvADHMmdFxCIZCCAgE+gDE2Vuanfl437VhOcP3g5Uw8C16Q==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@solana/web3.js': 1.95.4 - dev: true + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) - /@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2): - resolution: {integrity: sha512-5vGptadJxUxvUcyrwFZxXlEc6Q7AYySBesizCtrBFUY8w8PnF2vzmS45CP1MLySEATNH6T9mD4Rs0tLb87iQyA==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 + '@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: '@metaplex-foundation/umi': 0.9.2 - dev: true - /@metaplex-foundation/umi-serializers-core@0.8.9: - resolution: {integrity: sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==} - dev: true + '@metaplex-foundation/umi-serializers-core@0.8.9': {} - /@metaplex-foundation/umi-serializers-encodings@0.8.9: - resolution: {integrity: sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==} + '@metaplex-foundation/umi-serializers-encodings@0.8.9': dependencies: '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: true - /@metaplex-foundation/umi-serializers-numbers@0.8.9: - resolution: {integrity: sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==} + '@metaplex-foundation/umi-serializers-numbers@0.8.9': dependencies: '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: true - /@metaplex-foundation/umi-serializers@0.9.0: - resolution: {integrity: sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==} + '@metaplex-foundation/umi-serializers@0.9.0': dependencies: '@metaplex-foundation/umi-options': 0.8.9 '@metaplex-foundation/umi-public-keys': 0.8.9 '@metaplex-foundation/umi-serializers-core': 0.8.9 '@metaplex-foundation/umi-serializers-encodings': 0.8.9 '@metaplex-foundation/umi-serializers-numbers': 0.8.9 - dev: true - /@metaplex-foundation/umi-signer-wallet-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-DFG0ZFocKG8briypSkG9bGUTVsWpAgYugsl2BzTygkGExc4evWfF4Sb1F2C2w9FdrA9ESZM1gpLX9xtx5taOXg==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-signer-wallet-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@solana/web3.js': 1.95.4 - dev: true + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) - /@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-fR1Kf21uylMFd1Smkltmj4jTNxhqSWf416owsJ+T+cvJi2VCOcOwq/3UFzOrpz78fA0RhsajKYKj0HYsRnQI1g==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@solana/web3.js': 1.95.4 - dev: true + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) - /@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4): - resolution: {integrity: sha512-RQqUTtHYY9fmEMnq7s3Hiv/81flGaoI0ZVVoafnFVaQLnxU6QBKxtboRZHk43XtD9CiFh5f9izrMJX7iK7KlOA==} - peerDependencies: - '@metaplex-foundation/umi': ^0.9.2 - '@solana/web3.js': ^1.72.0 + '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: '@metaplex-foundation/umi': 0.9.2 - '@solana/web3.js': 1.95.4 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) buffer: 6.0.3 - dev: true - /@metaplex-foundation/umi@0.9.2: - resolution: {integrity: sha512-9i4Acm4pruQfJcpRrc2EauPBwkfDN0I9QTvJyZocIlKgoZwD6A6wH0PViH1AjOVG5CQCd1YI3tJd5XjYE1ElBw==} + '@metaplex-foundation/umi@0.9.2': dependencies: '@metaplex-foundation/umi-options': 0.8.9 '@metaplex-foundation/umi-public-keys': 0.8.9 '@metaplex-foundation/umi-serializers': 0.9.0 - dev: true - /@noble/curves@1.6.0: - resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} - engines: {node: ^14.21.3 || >=16} + '@mrgnlabs/marginfi-client-v2@6.1.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@mrgnlabs/mrgn-common': 2.0.3(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@pythnetwork/pyth-solana-receiver': 0.8.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.1.8(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@switchboard-xyz/on-demand': 1.2.67(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bignumber.js: 9.3.1 + borsh: 2.0.0 + bs58: 6.0.0 + crypto-hash: 3.1.0 + decimal.js: 10.6.0 + superstruct: 1.0.4 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - typescript + - utf-8-validate + + '@mrgnlabs/mrgn-common@2.0.3(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/buffer-layout': 4.0.1 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bignumber.js: 9.3.1 + bs58: 6.0.0 + decimal.js: 10.6.0 + numeral: 2.0.6 + superstruct: 1.0.4 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@noble/curves@1.9.4': dependencies: - '@noble/hashes': 1.5.0 - dev: true + '@noble/hashes': 1.8.0 - /@noble/hashes@1.5.0: - resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} - engines: {node: ^14.21.3 || >=16} + '@noble/ed25519@1.7.5': {} - /@prettier/sync@0.5.2(prettier@3.3.3): - resolution: {integrity: sha512-Yb569su456XNx5BsH/Vyem7xD6g/y9iLmLUzRKM1a/dhU/D7HqqvkAG72znulXlMXztbV0iiu9O5AL8K98TzZQ==} - peerDependencies: - prettier: '*' + '@noble/hashes@1.8.0': {} + + '@prettier/sync@0.5.5(prettier@3.6.2)': dependencies: - make-synchronized: 0.2.9 - prettier: 3.3.3 - dev: false + make-synchronized: 0.4.2 + prettier: 3.6.2 - /@protobufjs/aspromise@1.1.2: - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - dev: true + '@protobufjs/aspromise@1.1.2': {} - /@protobufjs/base64@1.1.2: - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - dev: true + '@protobufjs/base64@1.1.2': {} - /@protobufjs/codegen@2.0.4: - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - dev: true + '@protobufjs/codegen@2.0.4': {} - /@protobufjs/eventemitter@1.1.0: - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - dev: true + '@protobufjs/eventemitter@1.1.0': {} - /@protobufjs/fetch@1.1.0: - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 - dev: true - /@protobufjs/float@1.0.2: - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - dev: true + '@protobufjs/float@1.0.2': {} - /@protobufjs/inquire@1.1.0: - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - dev: true + '@protobufjs/inquire@1.1.0': {} - /@protobufjs/path@1.1.2: - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - dev: true + '@protobufjs/path@1.1.2': {} - /@protobufjs/pool@1.1.0: - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - dev: true + '@protobufjs/pool@1.1.0': {} - /@protobufjs/utf8@1.1.0: - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - dev: true + '@protobufjs/utf8@1.1.0': {} - /@solana/buffer-layout-utils@0.2.0: - resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} - engines: {node: '>= 10'} + '@pythnetwork/price-service-sdk@1.8.0': + dependencies: + bn.js: 5.2.2 + + '@pythnetwork/pyth-solana-receiver@0.8.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@noble/hashes': 1.8.0 + '@pythnetwork/price-service-sdk': 1.8.0 + '@pythnetwork/solana-utils': 0.4.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@pythnetwork/solana-utils@0.4.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bs58: 5.0.0 + jito-ts: 3.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.95.4 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) bigint-buffer: 1.1.5 - bignumber.js: 9.1.2 + bignumber.js: 9.3.1 transitivePeerDependencies: - bufferutil - encoding + - typescript - utf-8-validate - dev: true - /@solana/buffer-layout@4.0.1: - resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} - engines: {node: '>=5.10'} + '@solana/buffer-layout@4.0.1': dependencies: buffer: 6.0.3 - dev: true - /@solana/codecs-core@2.0.0-preview.1: - resolution: {integrity: sha512-0y3kgFSJAOApNGefEOgLRMiXOHVmcF29Xw3zmR4LDUABvytG3ecKMXGz5oLt3vdaU2CyN3tuUVeGmQjrd0U1kw==} + '@solana/codecs-core@2.0.0-preview.1': dependencies: '@solana/errors': 2.0.0-preview.1 - dev: false - - /@solana/codecs-core@2.0.0-preview.4(typescript@4.9.5): - resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/errors': 2.0.0-preview.4(typescript@4.9.5) - typescript: 4.9.5 - dev: true - /@solana/codecs-core@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-core@2.0.0-rc.1(typescript@4.9.5)': dependencies: '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) typescript: 4.9.5 - dev: true - /@solana/codecs-data-structures@2.0.0-preview.4(typescript@4.9.5): - resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-core@2.3.0(typescript@4.9.5)': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@4.9.5) - '@solana/errors': 2.0.0-preview.4(typescript@4.9.5) + '@solana/errors': 2.3.0(typescript@4.9.5) typescript: 4.9.5 - dev: true - /@solana/codecs-data-structures@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@4.9.5)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) typescript: 4.9.5 - dev: true - /@solana/codecs-numbers@2.0.0-preview.1: - resolution: {integrity: sha512-NFA8itgcYUY3hkWMBpVRozd2poy1zfOvkIWZKx/D69oIMUtQTBpKrodRVBuhlBkAv12vDNkFljqVySpcMZMl7A==} + '@solana/codecs-numbers@2.0.0-preview.1': dependencies: '@solana/codecs-core': 2.0.0-preview.1 '@solana/errors': 2.0.0-preview.1 - dev: false - /@solana/codecs-numbers@2.0.0-preview.4(typescript@4.9.5): - resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-numbers@2.0.0-rc.1(typescript@4.9.5)': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@4.9.5) - '@solana/errors': 2.0.0-preview.4(typescript@4.9.5) + '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) + '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) typescript: 4.9.5 - dev: true - /@solana/codecs-numbers@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-numbers@2.3.0(typescript@4.9.5)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) + '@solana/codecs-core': 2.3.0(typescript@4.9.5) + '@solana/errors': 2.3.0(typescript@4.9.5) typescript: 4.9.5 - dev: true - /@solana/codecs-strings@2.0.0-preview.1(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-kBAxE9ZD5/c8j9CkPxqc55dbo7R50Re3k94SXR+k13DZCCs37Fyn0/mAkw/S95fofbi/zsi4jSfmsT5vCZD75A==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 + '@solana/codecs-strings@2.0.0-preview.1(fastestsmallesttextencoderdecoder@1.0.22)': dependencies: '@solana/codecs-core': 2.0.0-preview.1 '@solana/codecs-numbers': 2.0.0-preview.1 '@solana/errors': 2.0.0-preview.1 fastestsmallesttextencoderdecoder: 1.0.22 - dev: false - - /@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@4.9.5) - '@solana/errors': 2.0.0-preview.4(typescript@4.9.5) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 4.9.5 - dev: true - /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) fastestsmallesttextencoderdecoder: 1.0.22 typescript: 4.9.5 - dev: true - - /@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: true - /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} - peerDependencies: - typescript: '>=5' + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5) @@ -505,57 +1510,25 @@ packages: typescript: 4.9.5 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - dev: true - - /@solana/errors@2.0.0-preview.1: - resolution: {integrity: sha512-mnBWfLVwMH4hxwb4sWZ7G7djQCMsyymjysvUPDiF89LueTBm1hfdxUv8Cy1uUCGsJ3jO3scdPwB4noOgr0rG/g==} - hasBin: true - dependencies: - chalk: 5.3.0 - commander: 12.1.0 - dev: false - /@solana/errors@2.0.0-preview.4(typescript@4.9.5): - resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==} - hasBin: true - peerDependencies: - typescript: '>=5' + '@solana/errors@2.0.0-preview.1': dependencies: - chalk: 5.3.0 + chalk: 5.4.1 commander: 12.1.0 - typescript: 4.9.5 - dev: true - /@solana/errors@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} - hasBin: true - peerDependencies: - typescript: '>=5' + '@solana/errors@2.0.0-rc.1(typescript@4.9.5)': dependencies: - chalk: 5.3.0 + chalk: 5.4.1 commander: 12.1.0 typescript: 4.9.5 - dev: true - /@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==} - peerDependencies: - typescript: '>=5' + '@solana/errors@2.3.0(typescript@4.9.5)': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@4.9.5) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/errors': 2.0.0-preview.4(typescript@4.9.5) + chalk: 5.4.1 + commander: 14.0.0 typescript: 4.9.5 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: true - /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} - peerDependencies: - typescript: '>=5' + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5) @@ -565,79 +1538,44 @@ packages: typescript: 4.9.5 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - dev: true - - /@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.94.0 - dependencies: - '@solana/codecs': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.4 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript - dev: true - /@solana/spl-token-metadata@0.1.5(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-DSBlo7vjuLe/xvNn75OKKndDBkFxlqjLdWlq6rf40StnrhRn7TDntHGLZpry1cf3uzQFShqeLROGNPAJwvkPnA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.3 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - dev: true - /@solana/spl-token-metadata@0.1.5(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-DSBlo7vjuLe/xvNn75OKKndDBkFxlqjLdWlq6rf40StnrhRn7TDntHGLZpry1cf3uzQFShqeLROGNPAJwvkPnA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)': dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.4 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - dev: true - /@solana/spl-token@0.3.11(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.88.0 + '@solana/spl-token@0.1.8(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-metadata': 0.1.5(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.3 + '@babel/runtime': 7.27.6 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + bn.js: 5.2.2 buffer: 6.0.3 + buffer-layout: 1.2.2 + dotenv: 10.0.0 transitivePeerDependencies: - bufferutil - encoding - - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - dev: true - /@solana/spl-token@0.3.11(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.88.0 + '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-metadata': 0.1.5(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.4 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -645,879 +1583,589 @@ packages: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - dev: true - /@solana/spl-token@0.4.8(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.94.0 + '@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10))': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-group': 0.0.5(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/spl-token-metadata': 0.1.5(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.4 - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - dev: true + '@solana/wallet-standard-features': 1.3.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@wallet-standard/base': 1.1.0 + '@wallet-standard/features': 1.1.0 + eventemitter3: 5.0.1 - /@solana/spl-type-length-value@0.1.0: - resolution: {integrity: sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==} - engines: {node: '>=16'} + '@solana/wallet-standard-features@1.3.0': dependencies: - buffer: 6.0.3 - dev: true + '@wallet-standard/base': 1.1.0 + '@wallet-standard/features': 1.1.0 - /@solana/web3.js@1.95.3: - resolution: {integrity: sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og==} + '@solana/web3.js@1.77.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.25.7 - '@noble/curves': 1.6.0 - '@noble/hashes': 1.5.0 + '@babel/runtime': 7.27.6 + '@noble/curves': 1.9.4 + '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 - agentkeepalive: 4.5.0 + agentkeepalive: 4.6.0 bigint-buffer: 1.1.5 - bn.js: 5.2.1 + bn.js: 5.2.2 borsh: 0.7.0 bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 - jayson: 4.1.2 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.0.4 - superstruct: 2.0.2 + rpc-websockets: 7.11.2 + superstruct: 0.14.2 transitivePeerDependencies: - bufferutil - encoding - utf-8-validate - dev: true - /@solana/web3.js@1.95.4: - resolution: {integrity: sha512-sdewnNEA42ZSMxqkzdwEWi6fDgzwtJHaQa5ndUGEJYtoOnM6X5cvPmjoTUp7/k7bRrVAxfBgDnvQQHD6yhlLYw==} + '@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.25.7 - '@noble/curves': 1.6.0 - '@noble/hashes': 1.5.0 + '@babel/runtime': 7.27.6 + '@noble/curves': 1.9.4 + '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 - agentkeepalive: 4.5.0 - bigint-buffer: 1.1.5 - bn.js: 5.2.1 + '@solana/codecs-numbers': 2.3.0(typescript@4.9.5) + agentkeepalive: 4.6.0 + bn.js: 5.2.2 borsh: 0.7.0 bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 - jayson: 4.1.2 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.0.4 + rpc-websockets: 9.1.1 superstruct: 2.0.2 transitivePeerDependencies: - bufferutil - encoding + - typescript - utf-8-validate - dev: true - /@solworks/soltoolkit-sdk@0.0.23(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-O6lXT3EBR4gmcjt0/33i97VMHVEImwXGi+4TNrDDdifn3tyOUB7V6PR1VGxlavQb9hqmVai3xhedg/rmbQzX7w==} + '@swc/helpers@0.5.17': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.4 - '@types/bn.js': 5.1.6 - '@types/node': 18.19.67 - '@types/node-fetch': 2.6.12 - bn.js: 5.2.1 - decimal.js: 10.4.3 - typescript: 4.9.5 + tslib: 2.8.1 + + '@switchboard-xyz/common@3.4.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + axios: 1.10.0 + big.js: 6.2.2 + bn.js: 5.2.2 + bs58: 6.0.0 + buffer: 6.0.3 + decimal.js: 10.6.0 + js-sha256: 0.11.1 + protobufjs: 7.5.3 + yaml: 2.8.0 + zod: 4.0.0-beta.20250505T195954 transitivePeerDependencies: - bufferutil + - debug - encoding - - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - dev: true - - /@soncodi/signal@2.0.7: - resolution: {integrity: sha512-zA2oZluZmVvgZEDjF243KWD1S2J+1SH1MVynI0O1KRgDt1lU8nqk7AK3oQfW/WpwT51L5waGSU0xKF/9BTP5Cw==} - dev: true - /@sqds/multisig@2.1.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-WOiL7La+RSiJsz7jVO85yhSiiSvNMUthiWuLPeWVOoD6IYa34BEAzanF1RdXRWGglSbRFYCTkyr+Ay1WmXmSRQ==} - engines: {node: '>=14'} + '@switchboard-xyz/common@4.1.11(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: - '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.0 - '@metaplex-foundation/cusper': 0.0.2 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.3 - '@types/bn.js': 5.1.6 - assert: 2.1.0 - bn.js: 5.2.1 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + axios: 1.10.0 + big.js: 6.2.2 + bn.js: 5.2.2 + bs58: 6.0.0 buffer: 6.0.3 - invariant: 2.2.4 + decimal.js: 10.6.0 + js-sha256: 0.11.1 + protobufjs: 7.5.3 + yaml: 2.8.0 + zod: 4.0.0-beta.20250505T195954 transitivePeerDependencies: - bufferutil + - debug - encoding - - fastestsmallesttextencoderdecoder - - supports-color - typescript - utf-8-validate - dev: true - /@swc/helpers@0.5.13: - resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + '@switchboard-xyz/on-demand@1.2.67(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: - tslib: 2.7.0 - dev: true - - /@switchboard-xyz/common@2.5.5: - resolution: {integrity: sha512-/qUmZlrfQyckvHGzS5Cj2+Ocd3eE64rPjQb1eEocc5dv4HXZMqbBbpM6BwURrQhZ65i3jO1evhTcAk3TVqCA8w==} - engines: {node: '>=12'} - dependencies: - '@solana/web3.js': 1.95.4 - axios: 1.7.8 - big.js: 6.2.2 - bn.js: 5.2.1 - bs58: 5.0.0 - cron-validator: 1.3.1 - decimal.js: 10.4.3 - js-sha256: 0.11.0 - lodash: 4.17.21 - protobufjs: 7.4.0 - yaml: 2.6.1 + '@common.js/quick-lru': 7.0.0 + '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + '@switchboard-xyz/common': 3.4.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + axios: 1.10.0 + bs58: 6.0.0 + buffer: 6.0.3 + js-yaml: 4.1.0 transitivePeerDependencies: - bufferutil - debug - encoding + - typescript - utf-8-validate - dev: true - /@switchboard-xyz/on-demand@1.2.51(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-IqtAEtYdCRQqG8a3tL5WOcLgBco8Iionu60Q+hQzCslQw76zDlkToHkI+71ASulFdZ2z+2XjaKV5ZVqPcYgP7g==} - engines: {node: '>= 18'} + '@switchboard-xyz/on-demand@2.17.4(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)': dependencies: - '@brokerloop/ttlcache': 3.2.3 - '@coral-xyz/anchor-30': /@coral-xyz/anchor@0.30.1 - '@solana/web3.js': 1.95.4 - '@solworks/soltoolkit-sdk': 0.0.23(fastestsmallesttextencoderdecoder@1.0.22) - '@switchboard-xyz/common': 2.5.5 - axios: 1.7.8 - big.js: 6.2.2 - bs58: 5.0.0 + '@coral-xyz/anchor-31': '@coral-xyz/anchor@0.31.1(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10)' + '@isaacs/ttlcache': 1.4.1 + '@switchboard-xyz/common': 4.1.11(bufferutil@4.0.9)(typescript@4.9.5)(utf-8-validate@5.0.10) + axios: 1.10.0 + bs58: 6.0.0 + buffer: 6.0.3 + events: 3.3.0 + isomorphic-ws: 5.0.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) js-yaml: 4.1.0 - protobufjs: 7.4.0 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug - encoding - - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - dev: true - /@types/bn.js@5.1.6: - resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - dependencies: - '@types/node': 20.16.11 - dev: true + '@tsconfig/node10@1.0.12': {} - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/connect@3.4.38': dependencies: - '@types/node': 20.16.11 - dev: true + '@types/node': 24.0.14 + + '@types/node@12.20.55': {} - /@types/node-fetch@2.6.12: - resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node@24.0.14': dependencies: - '@types/node': 20.16.11 - form-data: 4.0.1 - dev: true + undici-types: 7.8.0 - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true + '@types/uuid@8.3.4': {} - /@types/node@18.19.67: - resolution: {integrity: sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==} + '@types/ws@7.4.7': dependencies: - undici-types: 5.26.5 - dev: true + '@types/node': 24.0.14 - /@types/node@20.16.11: - resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==} + '@types/ws@8.18.1': dependencies: - undici-types: 6.19.8 - dev: true + '@types/node': 24.0.14 - /@types/uuid@8.3.4: - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - dev: true + '@wallet-standard/base@1.1.0': {} - /@types/ws@7.4.7: - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + '@wallet-standard/features@1.1.0': dependencies: - '@types/node': 20.16.11 - dev: true + '@wallet-standard/base': 1.1.0 - /@types/ws@8.5.12: - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} - dependencies: - '@types/node': 20.16.11 - dev: true + '@zod/core@0.11.6': {} - /JSONStream@1.3.5: - resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} - hasBin: true + a-sync-waterfall@1.0.1: {} + + acorn-walk@8.3.5: dependencies: - jsonparse: 1.3.1 - through: 2.3.8 - dev: true + acorn: 8.16.0 - /a-sync-waterfall@1.0.1: - resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} - dev: false + acorn@8.16.0: {} - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 - dev: true - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - dev: false - - /ansicolors@0.3.2: - resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - - /asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: false + arg@4.1.3: {} - /assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - dependencies: - call-bind: 1.0.7 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.5 - util: 0.12.5 - dev: true + argparse@2.0.1: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true + asap@2.0.6: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - dependencies: - possible-typed-array-names: 1.0.0 - dev: true + asynckit@0.4.0: {} - /axios@1.7.8: - resolution: {integrity: sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==} + axios@1.10.0: dependencies: follow-redirects: 1.15.9 - form-data: 4.0.1 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: true - /base-x@3.0.10: - resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 - dev: true - /base-x@4.0.0: - resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} - dev: true + base-x@4.0.1: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true + base-x@5.0.1: {} - /big.js@6.2.2: - resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - dev: true + base64-js@1.5.1: {} - /bigint-buffer@1.1.5: - resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} - engines: {node: '>= 10.0.0'} - requiresBuild: true + big.js@6.2.2: {} + + bigint-buffer@1.1.5: dependencies: bindings: 1.5.0 - dev: true - /bignumber.js@9.1.2: - resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - dev: true + bignumber.js@9.3.1: {} - /bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - dev: true - /bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: true + bn.js@5.2.2: {} - /borsh@0.7.0: - resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + borsh@0.7.0: dependencies: - bn.js: 5.2.1 + bn.js: 5.2.2 bs58: 4.0.1 text-encoding-utf-8: 1.0.2 - dev: true - /bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + borsh@2.0.0: {} + + bs58@4.0.1: dependencies: - base-x: 3.0.10 - dev: true + base-x: 3.0.11 - /bs58@5.0.0: - resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + bs58@5.0.0: dependencies: - base-x: 4.0.0 - dev: true + base-x: 4.0.1 - /buffer-layout@1.2.2: - resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} - engines: {node: '>=4.5'} - dev: true + bs58@6.0.0: + dependencies: + base-x: 5.0.1 - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffer-layout@1.2.2: {} + + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /bufferutil@4.0.8: - resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} - engines: {node: '>=6.14.2'} - requiresBuild: true + bufferutil@4.0.9: dependencies: - node-gyp-build: 4.8.2 - dev: true + node-gyp-build: 4.8.4 + optional: true - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind-apply-helpers@1.0.2: dependencies: - es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + camelcase@6.3.0: {} - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: false - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.4.1: {} - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: dependencies: color-name: 1.1.4 - dev: false - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: false + color-name@1.1.4: {} - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - dev: true - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} + commander@12.1.0: {} - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: true + commander@14.0.0: {} - /commander@5.1.0: - resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} - engines: {node: '>= 6'} - dev: false + commander@2.20.3: {} + + commander@5.1.0: {} - /cron-validator@1.3.1: - resolution: {integrity: sha512-C1HsxuPCY/5opR55G5/WNzyEGDWFVG+6GLrA+fW/sCTcP6A6NTjUP2AK7B8n2PyFs90kDG2qzwm8LMheADku6A==} - dev: true + create-require@1.1.1: {} + + cross-env@10.0.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 - /cross-fetch@3.1.8: - resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: true - /cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-fetch@4.1.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: true - /crypto-hash@1.3.0: - resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} - engines: {node: '>=8'} - dev: true - - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + cross-spawn@7.0.6: dependencies: - ms: 2.1.3 - dev: true + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - /decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: true + crypto-hash@1.3.0: {} - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + crypto-hash@3.1.0: {} + + decimal.js@10.6.0: {} + + define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - dev: true + delay@5.0.0: {} - /delay@5.0.0: - resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} - engines: {node: '>=10'} - dev: true + delayed-stream@1.0.0: {} - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: true + diff@4.0.4: {} - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dot-case@3.0.4: dependencies: no-case: 3.0.4 - tslib: 2.7.0 - dev: true + tslib: 2.8.1 - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + dotenv@10.0.0: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: dependencies: - get-intrinsic: 1.2.4 + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + emoji-regex@8.0.0: {} - /es6-promise@4.2.8: - resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - dev: true + es-define-property@1.0.1: {} - /es6-promisify@5.0.0: - resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es6-promise@4.2.8: {} + + es6-promisify@5.0.0: dependencies: es6-promise: 4.2.8 - dev: true - /eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - dev: true + escalade@3.2.0: {} - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: true + eventemitter3@4.0.7: {} - /eyes@0.1.8: - resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} - engines: {node: '> 0.1.90'} - dev: true + eventemitter3@5.0.1: {} - /fast-stable-stringify@1.0.0: - resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - dev: true + events@3.3.0: {} - /fastestsmallesttextencoderdecoder@1.0.22: - resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + eyes@0.1.8: {} - /file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - dev: true + fast-stable-stringify@1.0.0: {} - /follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: true + fastestsmallesttextencoderdecoder@1.0.22: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - dev: true + file-uri-to-path@1.0.0: {} - /form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} - engines: {node: '>= 6'} + follow-redirects@1.15.9: {} + + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 - dev: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 es-errors: 1.3.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 hasown: 2.0.2 + math-intrinsics: 1.1.0 - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + get-proto@1.0.1: dependencies: - get-intrinsic: 1.2.4 + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: false + gopd@1.2.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - dependencies: - es-define-property: 1.0.0 + has-flag@4.0.0: {} - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.1.0: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 - dev: true + has-symbols: 1.1.0 - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + humanize-ms@1.2.1: dependencies: ms: 2.1.3 - dev: true - - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true - - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - dependencies: - loose-envify: 1.4.0 - dev: true - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - dev: true + ieee754@1.2.1: {} - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.2 - dev: true + isarray@2.0.5: {} - /is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - dev: true + isexe@2.0.0: {} - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - which-typed-array: 1.1.15 - dev: true - - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: false + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - /isomorphic-ws@4.0.1(ws@7.5.10): - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' + isomorphic-ws@5.0.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 7.5.10 - dev: true + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - /jayson@4.1.2: - resolution: {integrity: sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA==} - engines: {node: '>=8'} - hasBin: true + jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 '@types/node': 12.20.55 '@types/ws': 7.4.7 - JSONStream: 1.3.5 commander: 2.20.3 delay: 5.0.0 es6-promisify: 5.0.0 eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10) + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) json-stringify-safe: 5.0.1 + stream-json: 1.9.1 uuid: 8.3.2 - ws: 7.5.10 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - dev: true - /js-sha256@0.11.0: - resolution: {integrity: sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==} - dev: true + jito-ts@3.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@grpc/grpc-js': 1.13.4 + '@noble/ed25519': 1.7.5 + '@solana/web3.js': 1.77.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + agentkeepalive: 4.6.0 + dotenv: 16.6.1 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + node-fetch: 2.7.0 + superstruct: 1.0.4 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true + js-sha256@0.11.1: {} - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - dev: true - /json-stable-stringify@1.1.1: - resolution: {integrity: sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==} - engines: {node: '>= 0.4'} + json-stable-stringify@1.3.0: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 isarray: 2.0.5 jsonify: 0.0.1 object-keys: 1.1.1 - dev: false - - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true - /jsonify@0.0.1: - resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} - dev: false + json-stringify-safe@5.0.1: {} - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonify@0.0.1: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + lodash.camelcase@4.3.0: {} - /long@5.2.3: - resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} - dev: true + long@5.3.2: {} - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + lower-case@2.0.2: dependencies: - js-tokens: 4.0.0 - dev: true + tslib: 2.8.1 - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.7.0 - dev: true + make-error@1.3.6: {} - /make-synchronized@0.2.9: - resolution: {integrity: sha512-4wczOs8SLuEdpEvp3vGo83wh8rjJ78UsIk7DIX5fxdfmfMJGog4bQzxfvOwq7Q3yCHLC4jp1urPHIxRS/A93gA==} - dev: false + make-synchronized@0.4.2: {} - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: true + math-intrinsics@1.1.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-db@1.52.0: {} + + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - dev: true - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true + ms@2.1.3: {} - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + no-case@3.0.4: dependencies: lower-case: 2.0.2 - tslib: 2.7.0 - dev: true + tslib: 2.8.1 - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: true - /node-gyp-build@4.8.2: - resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==} - hasBin: true - requiresBuild: true - dev: true + node-gyp-build@4.8.4: + optional: true - /nunjucks@3.2.4: - resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==} - engines: {node: '>= 6.9.0'} - hasBin: true - peerDependencies: - chokidar: ^3.3.0 - peerDependenciesMeta: - chokidar: - optional: true + numeral@2.0.6: {} + + nunjucks@3.2.4: dependencies: a-sync-waterfall: 1.0.1 asap: 2.0.6 commander: 5.1.0 - dev: false - - /object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - dev: true - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true + object-keys@1.1.1: {} - /pako@2.1.0: - resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - dev: true + pako@2.1.0: {} - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true + path-key@3.1.1: {} - /prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} - engines: {node: '>=14'} - hasBin: true - dev: false + prettier@3.6.2: {} - /protobufjs@7.4.0: - resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} - engines: {node: '>=12.0.0'} - requiresBuild: true + protobufjs@7.5.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -1529,222 +2177,178 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.16.11 - long: 5.2.3 - dev: true + '@types/node': 24.0.14 + long: 5.3.2 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true + proxy-from-env@1.1.0: {} - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true + require-directory@2.1.1: {} - /rpc-websockets@7.11.0: - resolution: {integrity: sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==} - deprecated: deprecate 7.11.0 + rpc-websockets@7.11.0: + dependencies: + eventemitter3: 4.0.7 + uuid: 8.3.2 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + + rpc-websockets@7.11.2: dependencies: eventemitter3: 4.0.7 uuid: 8.3.2 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - bufferutil: 4.0.8 + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: true - /rpc-websockets@9.0.4: - resolution: {integrity: sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ==} + rpc-websockets@9.1.1: dependencies: - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.17 '@types/uuid': 8.3.4 - '@types/ws': 8.5.12 + '@types/ws': 8.18.1 buffer: 6.0.3 eventemitter3: 5.0.1 uuid: 8.3.2 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - bufferutil: 4.0.8 + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: true - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true + safe-buffer@5.2.1: {} - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 has-property-descriptors: 1.0.2 - /snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + snake-case@3.0.4: dependencies: dot-case: 3.0.4 - tslib: 2.7.0 - dev: true + tslib: 2.8.1 - /superstruct@0.15.5: - resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} - dev: true + stream-chain@2.2.5: {} - /superstruct@2.0.2: - resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} - engines: {node: '>=14.0.0'} - dev: true + stream-json@1.9.1: + dependencies: + stream-chain: 2.2.5 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: - has-flag: 4.0.0 - dev: false + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 - /text-encoding-utf-8@1.0.2: - resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - dev: true + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + superstruct@0.14.2: {} - /toml@3.0.0: - resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - dev: true + superstruct@0.15.5: {} - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true + superstruct@1.0.4: {} - /tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - dev: true + superstruct@2.0.2: {} - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true + text-encoding-utf-8@1.0.2: {} - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - dev: true + toml@3.0.0: {} - /utf-8-validate@5.0.10: - resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} - engines: {node: '>=6.14.2'} - requiresBuild: true + tr46@0.0.3: {} + + ts-node@10.9.2(@types/node@24.0.14)(typescript@4.9.5): dependencies: - node-gyp-build: 4.8.2 - dev: true + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 24.0.14 + acorn: 8.16.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tslib@2.8.1: {} - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + typescript@4.9.5: {} + + undici-types@7.8.0: {} + + utf-8-validate@5.0.10: dependencies: - inherits: 2.0.4 - is-arguments: 1.1.1 - is-generator-function: 1.0.10 - is-typed-array: 1.1.13 - which-typed-array: 1.1.15 - dev: true + node-gyp-build: 4.8.4 + optional: true - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: true + uuid@8.3.2: {} - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which@2.0.2: dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - dev: true - - /ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true + isexe: 2.0.0 - /ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + wrap-ansi@7.0.0: dependencies: - bufferutil: 4.0.8 + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: true - /yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} - engines: {node: '>= 14'} - hasBin: true - dev: true + ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 - file:solauto-sdk(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {directory: solauto-sdk, type: directory} - id: file:solauto-sdk - name: '@haven-fi/solauto-sdk' + y18n@5.0.8: {} + + yaml@2.8.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: dependencies: - '@coral-xyz/anchor': 0.30.1 - '@jup-ag/api': 6.0.29 - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-bundle-defaults': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@metaplex-foundation/umi-signer-wallet-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.4) - '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.4)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.4 - '@sqds/multisig': 2.1.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@switchboard-xyz/on-demand': 1.2.51(fastestsmallesttextencoderdecoder@1.0.22) - '@types/node': 20.16.11 - axios: 1.7.8 - bs58: 5.0.0 - cross-fetch: 4.0.0 - rpc-websockets: 7.11.0 - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - dev: true + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yn@3.1.1: {} + + zod@4.0.0-beta.20250505T195954: + dependencies: + '@zod/core': 0.11.6 diff --git a/programs/marginfi-sdk/Cargo.toml b/programs/marginfi-sdk/Cargo.toml index b4eb34c2..02b0bc4e 100644 --- a/programs/marginfi-sdk/Cargo.toml +++ b/programs/marginfi-sdk/Cargo.toml @@ -3,6 +3,11 @@ name = "marginfi-sdk" version = "0.1.0" edition = "2021" +[features] +default = [] +test = [] +staging = [] + [dependencies] solana-program = ">=1.16" borsh = "^0.10" diff --git a/programs/marginfi-sdk/src/generated/errors/marginfi.rs b/programs/marginfi-sdk/src/generated/errors/marginfi.rs index dbb182f2..41fe61f4 100644 --- a/programs/marginfi-sdk/src/generated/errors/marginfi.rs +++ b/programs/marginfi-sdk/src/generated/errors/marginfi.rs @@ -10,9 +10,9 @@ use thiserror::Error; #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] pub enum MarginfiError { - /// 6000 (0x1770) - Math error - #[error("Math error")] - MathError, + /// 6000 (0x1770) - Internal Marginfi logic error + #[error("Internal Marginfi logic error")] + InternalLogicError, /// 6001 (0x1771) - Invalid bank index #[error("Invalid bank index")] BankNotFound, @@ -25,123 +25,216 @@ pub enum MarginfiError { /// 6004 (0x1774) - Invalid transfer #[error("Invalid transfer")] InvalidTransfer, - /// 6005 (0x1775) - Missing Pyth or Bank account - #[error("Missing Pyth or Bank account")] + /// 6005 (0x1775) - Missing Oracle, Bank, LST mint, or Sol Pool + #[error("Missing Oracle, Bank, LST mint, or Sol Pool")] MissingPythOrBankAccount, /// 6006 (0x1776) - Missing Pyth account #[error("Missing Pyth account")] MissingPythAccount, - /// 6007 (0x1777) - Invalid Pyth account - #[error("Invalid Pyth account")] - InvalidOracleAccount, - /// 6008 (0x1778) - Missing Bank account + /// 6007 (0x1777) - Missing Bank account #[error("Missing Bank account")] MissingBankAccount, - /// 6009 (0x1779) - Invalid Bank account + /// 6008 (0x1778) - Invalid Bank account #[error("Invalid Bank account")] InvalidBankAccount, - /// 6010 (0x177A) - Bad account health - #[error("Bad account health")] - BadAccountHealth, - /// 6011 (0x177B) - Lending account balance slots are full + /// 6009 (0x1779) - RiskEngine rejected due to either bad health or stale oracles + #[error("RiskEngine rejected due to either bad health or stale oracles")] + RiskEngineInitRejected, + /// 6010 (0x177A) - Lending account balance slots are full #[error("Lending account balance slots are full")] LendingAccountBalanceSlotsFull, - /// 6012 (0x177C) - Bank already exists + /// 6011 (0x177B) - Bank already exists #[error("Bank already exists")] BankAlreadyExists, - /// 6013 (0x177D) - Illegal liquidation - #[error("Illegal liquidation")] - IllegalLiquidation, - /// 6014 (0x177E) - Account is not bankrupt + /// 6012 (0x177C) - Amount to liquidate must be positive + #[error("Amount to liquidate must be positive")] + ZeroLiquidationAmount, + /// 6013 (0x177D) - Account is not bankrupt #[error("Account is not bankrupt")] AccountNotBankrupt, - /// 6015 (0x177F) - Account balance is not bad debt + /// 6014 (0x177E) - Account balance is not bad debt #[error("Account balance is not bad debt")] BalanceNotBadDebt, - /// 6016 (0x1780) - Invalid group config + /// 6015 (0x177F) - Invalid group config #[error("Invalid group config")] InvalidConfig, - /// 6017 (0x1781) - Stale oracle data - #[error("Stale oracle data")] - StaleOracle, - /// 6018 (0x1782) - Bank paused + /// 6016 (0x1780) - Bank paused #[error("Bank paused")] BankPaused, - /// 6019 (0x1783) - Bank is ReduceOnly mode + /// 6017 (0x1781) - Bank is ReduceOnly mode #[error("Bank is ReduceOnly mode")] BankReduceOnly, - /// 6020 (0x1784) - Bank is missing + /// 6018 (0x1782) - Bank is missing #[error("Bank is missing")] - BankAccoutNotFound, - /// 6021 (0x1785) - Operation is deposit-only + BankAccountNotFound, + /// 6019 (0x1783) - Operation is deposit-only #[error("Operation is deposit-only")] OperationDepositOnly, - /// 6022 (0x1786) - Operation is withdraw-only + /// 6020 (0x1784) - Operation is withdraw-only #[error("Operation is withdraw-only")] OperationWithdrawOnly, - /// 6023 (0x1787) - Operation is borrow-only + /// 6021 (0x1785) - Operation is borrow-only #[error("Operation is borrow-only")] OperationBorrowOnly, - /// 6024 (0x1788) - Operation is repay-only + /// 6022 (0x1786) - Operation is repay-only #[error("Operation is repay-only")] OperationRepayOnly, - /// 6025 (0x1789) - No asset found + /// 6023 (0x1787) - No asset found #[error("No asset found")] NoAssetFound, - /// 6026 (0x178A) - No liability found + /// 6024 (0x1788) - No liability found #[error("No liability found")] NoLiabilityFound, - /// 6027 (0x178B) - Invalid oracle setup + /// 6025 (0x1789) - Invalid oracle setup #[error("Invalid oracle setup")] InvalidOracleSetup, - /// 6028 (0x178C) - Invalid bank utilization ratio + /// 6026 (0x178A) - Invalid bank utilization ratio #[error("Invalid bank utilization ratio")] IllegalUtilizationRatio, - /// 6029 (0x178D) - Bank borrow cap exceeded + /// 6027 (0x178B) - Bank borrow cap exceeded #[error("Bank borrow cap exceeded")] BankLiabilityCapacityExceeded, - /// 6030 (0x178E) - Invalid Price + /// 6028 (0x178C) - Invalid Price #[error("Invalid Price")] InvalidPrice, - /// 6031 (0x178F) - Account can have only one liablity when account is under isolated risk - #[error("Account can have only one liablity when account is under isolated risk")] + /// 6029 (0x178D) - Account can have only one liability when account is under isolated risk + #[error("Account can have only one liability when account is under isolated risk")] IsolatedAccountIllegalState, - /// 6032 (0x1790) - Emissions already setup + /// 6030 (0x178E) - Emissions already setup #[error("Emissions already setup")] EmissionsAlreadySetup, - /// 6033 (0x1791) - Oracle is not set + /// 6031 (0x178F) - Oracle is not set #[error("Oracle is not set")] OracleNotSetup, - /// 6034 (0x1792) - Invalid swithcboard decimal conversion - #[error("Invalid swithcboard decimal conversion")] + /// 6032 (0x1790) - Invalid switchboard decimal conversion + #[error("Invalid switchboard decimal conversion")] InvalidSwitchboardDecimalConversion, - /// 6035 (0x1793) - Cannot close balance because of outstanding emissions + /// 6033 (0x1791) - Cannot close balance because of outstanding emissions #[error("Cannot close balance because of outstanding emissions")] CannotCloseOutstandingEmissions, - /// 6036 (0x1794) - Update emissions error + /// 6034 (0x1792) - Update emissions error #[error("Update emissions error")] EmissionsUpdateError, - /// 6037 (0x1795) - Account disabled + /// 6035 (0x1793) - Account disabled #[error("Account disabled")] AccountDisabled, - /// 6038 (0x1796) - Account can't temporarily open 3 balances, please close a balance first + /// 6036 (0x1794) - Account can't temporarily open 3 balances, please close a balance first #[error("Account can't temporarily open 3 balances, please close a balance first")] AccountTempActiveBalanceLimitExceeded, - /// 6039 (0x1797) - Illegal action during flashloan + /// 6037 (0x1795) - Illegal action during flashloan #[error("Illegal action during flashloan")] AccountInFlashloan, - /// 6040 (0x1798) - Illegal flashloan + /// 6038 (0x1796) - Illegal flashloan #[error("Illegal flashloan")] IllegalFlashloan, - /// 6041 (0x1799) - Illegal flag + /// 6039 (0x1797) - Illegal flag #[error("Illegal flag")] IllegalFlag, - /// 6042 (0x179A) - Illegal balance state + /// 6040 (0x1798) - Illegal balance state #[error("Illegal balance state")] IllegalBalanceState, - /// 6043 (0x179B) - Illegal account authority transfer + /// 6041 (0x1799) - Illegal account authority transfer #[error("Illegal account authority transfer")] IllegalAccountAuthorityTransfer, + /// 6042 (0x179A) - Unauthorized + #[error("Unauthorized")] + Unauthorized, + /// 6043 (0x179B) - Invalid account authority + #[error("Invalid account authority")] + IllegalAction, + /// 6044 (0x179C) - Token22 Banks require mint account as first remaining account + #[error("Token22 Banks require mint account as first remaining account")] + T22MintRequired, + /// 6045 (0x179D) - Invalid ATA for global fee account + #[error("Invalid ATA for global fee account")] + InvalidFeeAta, + /// 6046 (0x179E) - Use add pool permissionless instead + #[error("Use add pool permissionless instead")] + AddedStakedPoolManually, + /// 6047 (0x179F) - Staked SOL accounts can only deposit staked assets and borrow SOL + #[error("Staked SOL accounts can only deposit staked assets and borrow SOL")] + AssetTagMismatch, + /// 6048 (0x17A0) - Stake pool validation failed: check the stake pool, mint, or sol pool + #[error("Stake pool validation failed: check the stake pool, mint, or sol pool")] + StakePoolValidationFailed, + /// 6049 (0x17A1) - Switchboard oracle: stale price + #[error("Switchboard oracle: stale price")] + SwitchboardStalePrice, + /// 6050 (0x17A2) - Pyth Push oracle: stale price + #[error("Pyth Push oracle: stale price")] + PythPushStalePrice, + /// 6051 (0x17A3) - Oracle error: wrong number of accounts + #[error("Oracle error: wrong number of accounts")] + WrongNumberOfOracleAccounts, + /// 6052 (0x17A4) - Oracle error: wrong account keys + #[error("Oracle error: wrong account keys")] + WrongOracleAccountKeys, + /// 6053 (0x17A5) - Pyth Push oracle: wrong account owner + #[error("Pyth Push oracle: wrong account owner")] + PythPushWrongAccountOwner, + /// 6054 (0x17A6) - Staked Pyth Push oracle: wrong account owner + #[error("Staked Pyth Push oracle: wrong account owner")] + StakedPythPushWrongAccountOwner, + /// 6055 (0x17A7) - Pyth Push oracle: mismatched feed id + #[error("Pyth Push oracle: mismatched feed id")] + PythPushMismatchedFeedId, + /// 6056 (0x17A8) - Pyth Push oracle: insufficient verification level + #[error("Pyth Push oracle: insufficient verification level")] + PythPushInsufficientVerificationLevel, + /// 6057 (0x17A9) - Pyth Push oracle: feed id must be 32 Bytes + #[error("Pyth Push oracle: feed id must be 32 Bytes")] + PythPushFeedIdMustBe32Bytes, + /// 6058 (0x17AA) - Pyth Push oracle: feed id contains non-hex characters + #[error("Pyth Push oracle: feed id contains non-hex characters")] + PythPushFeedIdNonHexCharacter, + /// 6059 (0x17AB) - Switchboard oracle: wrong account owner + #[error("Switchboard oracle: wrong account owner")] + SwitchboardWrongAccountOwner, + /// 6060 (0x17AC) - Pyth Push oracle: invalid account + #[error("Pyth Push oracle: invalid account")] + PythPushInvalidAccount, + /// 6061 (0x17AD) - Switchboard oracle: invalid account + #[error("Switchboard oracle: invalid account")] + SwitchboardInvalidAccount, + /// 6062 (0x17AE) - Math error + #[error("Math error")] + MathError, + /// 6063 (0x17AF) - Invalid emissions destination account + #[error("Invalid emissions destination account")] + InvalidEmissionsDestinationAccount, + /// 6064 (0x17B0) - Asset and liability bank cannot be the same + #[error("Asset and liability bank cannot be the same")] + SameAssetAndLiabilityBanks, + /// 6065 (0x17B1) - Trying to withdraw more assets than available + #[error("Trying to withdraw more assets than available")] + OverliquidationAttempt, + /// 6066 (0x17B2) - Liability bank has no liabilities + #[error("Liability bank has no liabilities")] + NoLiabilitiesInLiabilityBank, + /// 6067 (0x17B3) - Liability bank has assets + #[error("Liability bank has assets")] + AssetsInLiabilityBank, + /// 6068 (0x17B4) - Account is healthy and cannot be liquidated + #[error("Account is healthy and cannot be liquidated")] + HealthyAccount, + /// 6069 (0x17B5) - Liability payoff too severe, exhausted liability + #[error("Liability payoff too severe, exhausted liability")] + ExhaustedLiability, + /// 6070 (0x17B6) - Liability payoff too severe, liability balance has assets + #[error("Liability payoff too severe, liability balance has assets")] + TooSeverePayoff, + /// 6071 (0x17B7) - Liquidation too severe, account above maintenance requirement + #[error("Liquidation too severe, account above maintenance requirement")] + TooSevereLiquidation, + /// 6072 (0x17B8) - Liquidation would worsen account health + #[error("Liquidation would worsen account health")] + WorseHealthPostLiquidation, + /// 6073 (0x17B9) - Arena groups can only support two banks + #[error("Arena groups can only support two banks")] + ArenaBankLimit, + /// 6074 (0x17BA) - Arena groups cannot return to non-arena status + #[error("Arena groups cannot return to non-arena status")] + ArenaSettingCannotChange, } impl solana_program::program_error::PrintProgramError for MarginfiError { diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_borrow.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_borrow.rs index 1965eb3f..b698fbe5 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_borrow.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_borrow.rs @@ -88,12 +88,12 @@ impl LendingAccountBorrow { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountBorrowInstructionData { +pub struct LendingAccountBorrowInstructionData { discriminator: [u8; 8], } impl LendingAccountBorrowInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [4, 126, 116, 53, 48, 5, 212, 31], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_close_balance.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_close_balance.rs index 75cf5638..0aea68f7 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_close_balance.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_close_balance.rs @@ -58,12 +58,12 @@ impl LendingAccountCloseBalance { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountCloseBalanceInstructionData { +pub struct LendingAccountCloseBalanceInstructionData { discriminator: [u8; 8], } impl LendingAccountCloseBalanceInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [245, 54, 41, 4, 243, 202, 31, 17], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_deposit.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_deposit.rs index 39409d5f..d3b69219 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_deposit.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_deposit.rs @@ -82,12 +82,12 @@ impl LendingAccountDeposit { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountDepositInstructionData { +pub struct LendingAccountDepositInstructionData { discriminator: [u8; 8], } impl LendingAccountDepositInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [171, 94, 235, 103, 82, 64, 212, 140], } @@ -98,6 +98,7 @@ impl LendingAccountDepositInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct LendingAccountDepositInstructionArgs { pub amount: u64, + pub deposit_up_to_limit: Option, } /// Instruction builder for `LendingAccountDeposit`. @@ -121,6 +122,7 @@ pub struct LendingAccountDepositBuilder { bank_liquidity_vault: Option, token_program: Option, amount: Option, + deposit_up_to_limit: Option, __remaining_accounts: Vec, } @@ -178,6 +180,12 @@ impl LendingAccountDepositBuilder { self.amount = Some(amount); self } + /// `[optional argument]` + #[inline(always)] + pub fn deposit_up_to_limit(&mut self, deposit_up_to_limit: bool) -> &mut Self { + self.deposit_up_to_limit = Some(deposit_up_to_limit); + self + } /// Add an aditional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -215,6 +223,7 @@ impl LendingAccountDepositBuilder { }; let args = LendingAccountDepositInstructionArgs { amount: self.amount.clone().expect("amount is not set"), + deposit_up_to_limit: self.deposit_up_to_limit.clone(), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -406,6 +415,7 @@ impl<'a, 'b> LendingAccountDepositCpiBuilder<'a, 'b> { bank_liquidity_vault: None, token_program: None, amount: None, + deposit_up_to_limit: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -468,6 +478,12 @@ impl<'a, 'b> LendingAccountDepositCpiBuilder<'a, 'b> { self.instruction.amount = Some(amount); self } + /// `[optional argument]` + #[inline(always)] + pub fn deposit_up_to_limit(&mut self, deposit_up_to_limit: bool) -> &mut Self { + self.instruction.deposit_up_to_limit = Some(deposit_up_to_limit); + self + } /// Add an additional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -511,6 +527,7 @@ impl<'a, 'b> LendingAccountDepositCpiBuilder<'a, 'b> { ) -> solana_program::entrypoint::ProgramResult { let args = LendingAccountDepositInstructionArgs { amount: self.instruction.amount.clone().expect("amount is not set"), + deposit_up_to_limit: self.instruction.deposit_up_to_limit.clone(), }; let instruction = LendingAccountDepositCpi { __program: self.instruction.__program, @@ -562,6 +579,7 @@ struct LendingAccountDepositCpiBuilderInstruction<'a, 'b> { bank_liquidity_vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, token_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, amount: Option, + deposit_up_to_limit: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_end_flashloan.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_end_flashloan.rs index 7a8fb312..7e9a4012 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_end_flashloan.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_end_flashloan.rs @@ -47,12 +47,12 @@ impl LendingAccountEndFlashloan { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountEndFlashloanInstructionData { +pub struct LendingAccountEndFlashloanInstructionData { discriminator: [u8; 8], } impl LendingAccountEndFlashloanInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [105, 124, 201, 106, 153, 2, 8, 156], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_liquidate.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_liquidate.rs index c129fff7..d9564597 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_liquidate.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_liquidate.rs @@ -101,12 +101,12 @@ impl LendingAccountLiquidate { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountLiquidateInstructionData { +pub struct LendingAccountLiquidateInstructionData { discriminator: [u8; 8], } impl LendingAccountLiquidateInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [214, 169, 151, 213, 251, 167, 86, 219], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_repay.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_repay.rs index f8ce75e6..c33f3d79 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_repay.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_repay.rs @@ -82,12 +82,12 @@ impl LendingAccountRepay { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountRepayInstructionData { +pub struct LendingAccountRepayInstructionData { discriminator: [u8; 8], } impl LendingAccountRepayInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [79, 209, 172, 177, 222, 51, 173, 151], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_settle_emissions.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_settle_emissions.rs index 781ccff7..5051fcbd 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_settle_emissions.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_settle_emissions.rs @@ -46,12 +46,12 @@ impl LendingAccountSettleEmissions { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountSettleEmissionsInstructionData { +pub struct LendingAccountSettleEmissionsInstructionData { discriminator: [u8; 8], } impl LendingAccountSettleEmissionsInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [161, 58, 136, 174, 242, 223, 156, 176], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_start_flashloan.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_start_flashloan.rs index 719e73d3..6bfef77a 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_start_flashloan.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_start_flashloan.rs @@ -59,12 +59,12 @@ impl LendingAccountStartFlashloan { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountStartFlashloanInstructionData { +pub struct LendingAccountStartFlashloanInstructionData { discriminator: [u8; 8], } impl LendingAccountStartFlashloanInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [14, 131, 33, 220, 81, 186, 180, 107], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw.rs index fa481ea5..040c74ea 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw.rs @@ -41,7 +41,7 @@ impl LendingAccountWithdraw { remaining_accounts: &[solana_program::instruction::AccountMeta], ) -> solana_program::instruction::Instruction { let mut accounts = Vec::with_capacity(8 + remaining_accounts.len()); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( + accounts.push(solana_program::instruction::AccountMeta::new( self.marginfi_group, false, )); @@ -88,12 +88,12 @@ impl LendingAccountWithdraw { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountWithdrawInstructionData { +pub struct LendingAccountWithdrawInstructionData { discriminator: [u8; 8], } impl LendingAccountWithdrawInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [36, 72, 74, 19, 210, 210, 192, 192], } @@ -111,7 +111,7 @@ pub struct LendingAccountWithdrawInstructionArgs { /// /// ### Accounts: /// -/// 0. `[]` marginfi_group +/// 0. `[writable]` marginfi_group /// 1. `[writable]` marginfi_account /// 2. `[signer]` signer /// 3. `[writable]` bank @@ -345,7 +345,7 @@ impl<'a, 'b> LendingAccountWithdrawCpi<'a, 'b> { )], ) -> solana_program::entrypoint::ProgramResult { let mut accounts = Vec::with_capacity(8 + remaining_accounts.len()); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( + accounts.push(solana_program::instruction::AccountMeta::new( *self.marginfi_group.key, false, )); @@ -395,7 +395,7 @@ impl<'a, 'b> LendingAccountWithdrawCpi<'a, 'b> { accounts, data, }; - let mut account_infos = Box::new(Vec::with_capacity(8 + 1 + remaining_accounts.len())); + let mut account_infos = Vec::with_capacity(8 + 1 + remaining_accounts.len()); account_infos.push(self.__program.clone()); account_infos.push(self.marginfi_group.clone()); account_infos.push(self.marginfi_account.clone()); @@ -421,7 +421,7 @@ impl<'a, 'b> LendingAccountWithdrawCpi<'a, 'b> { /// /// ### Accounts: /// -/// 0. `[]` marginfi_group +/// 0. `[writable]` marginfi_group /// 1. `[writable]` marginfi_account /// 2. `[signer]` signer /// 3. `[writable]` bank diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw_emissions.rs b/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw_emissions.rs index 91b3dd6b..6df91932 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw_emissions.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_account_withdraw_emissions.rs @@ -88,12 +88,12 @@ impl LendingAccountWithdrawEmissions { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingAccountWithdrawEmissionsInstructionData { +pub struct LendingAccountWithdrawEmissionsInstructionData { discriminator: [u8; 8], } impl LendingAccountWithdrawEmissionsInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [234, 22, 84, 214, 118, 176, 140, 170], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_accrue_bank_interest.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_accrue_bank_interest.rs index 33e3527e..e8ab51a5 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_accrue_bank_interest.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_accrue_bank_interest.rs @@ -46,12 +46,12 @@ impl LendingPoolAccrueBankInterest { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolAccrueBankInterestInstructionData { +pub struct LendingPoolAccrueBankInterestInstructionData { discriminator: [u8; 8], } impl LendingPoolAccrueBankInterestInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [108, 201, 30, 87, 47, 65, 97, 188], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank.rs index afc80581..d1a52061 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank.rs @@ -123,12 +123,12 @@ impl LendingPoolAddBank { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolAddBankInstructionData { +pub struct LendingPoolAddBankInstructionData { discriminator: [u8; 8], } impl LendingPoolAddBankInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [215, 68, 72, 78, 208, 218, 103, 182], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank_with_seed.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank_with_seed.rs index acb8407d..5f39ee49 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank_with_seed.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_add_bank_with_seed.rs @@ -123,12 +123,12 @@ impl LendingPoolAddBankWithSeed { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolAddBankWithSeedInstructionData { +pub struct LendingPoolAddBankWithSeedInstructionData { discriminator: [u8; 8], } impl LendingPoolAddBankWithSeedInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [76, 211, 213, 171, 117, 78, 158, 76], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_collect_bank_fees.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_collect_bank_fees.rs index c99a10ba..a9513db8 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_collect_bank_fees.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_collect_bank_fees.rs @@ -76,12 +76,12 @@ impl LendingPoolCollectBankFees { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolCollectBankFeesInstructionData { +pub struct LendingPoolCollectBankFeesInstructionData { discriminator: [u8; 8], } impl LendingPoolCollectBankFeesInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [201, 5, 215, 116, 230, 92, 75, 150], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_configure_bank.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_configure_bank.rs index ab64319c..c7eeefd5 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_configure_bank.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_configure_bank.rs @@ -62,12 +62,12 @@ impl LendingPoolConfigureBank { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolConfigureBankInstructionData { +pub struct LendingPoolConfigureBankInstructionData { discriminator: [u8; 8], } impl LendingPoolConfigureBankInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [121, 173, 156, 40, 93, 148, 56, 237], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_handle_bankruptcy.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_handle_bankruptcy.rs index f4e582c3..a7a17279 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_handle_bankruptcy.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_handle_bankruptcy.rs @@ -81,12 +81,12 @@ impl LendingPoolHandleBankruptcy { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolHandleBankruptcyInstructionData { +pub struct LendingPoolHandleBankruptcyInstructionData { discriminator: [u8; 8], } impl LendingPoolHandleBankruptcyInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [162, 11, 56, 139, 90, 128, 70, 173], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_setup_emissions.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_setup_emissions.rs index e136b0aa..d1325aa6 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_setup_emissions.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_setup_emissions.rs @@ -93,12 +93,12 @@ impl LendingPoolSetupEmissions { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolSetupEmissionsInstructionData { +pub struct LendingPoolSetupEmissionsInstructionData { discriminator: [u8; 8], } impl LendingPoolSetupEmissionsInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [206, 97, 120, 172, 113, 204, 169, 70], } diff --git a/programs/marginfi-sdk/src/generated/instructions/lending_pool_update_emissions_parameters.rs b/programs/marginfi-sdk/src/generated/instructions/lending_pool_update_emissions_parameters.rs index 854c3ca6..5c6696b6 100644 --- a/programs/marginfi-sdk/src/generated/instructions/lending_pool_update_emissions_parameters.rs +++ b/programs/marginfi-sdk/src/generated/instructions/lending_pool_update_emissions_parameters.rs @@ -81,12 +81,12 @@ impl LendingPoolUpdateEmissionsParameters { } #[derive(BorshDeserialize, BorshSerialize)] -struct LendingPoolUpdateEmissionsParametersInstructionData { +pub struct LendingPoolUpdateEmissionsParametersInstructionData { discriminator: [u8; 8], } impl LendingPoolUpdateEmissionsParametersInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [55, 213, 224, 168, 153, 53, 197, 40], } diff --git a/programs/marginfi-sdk/src/generated/instructions/marginfi_account_initialize.rs b/programs/marginfi-sdk/src/generated/instructions/marginfi_account_initialize.rs index 2060b68a..9d59e497 100644 --- a/programs/marginfi-sdk/src/generated/instructions/marginfi_account_initialize.rs +++ b/programs/marginfi-sdk/src/generated/instructions/marginfi_account_initialize.rs @@ -65,12 +65,12 @@ impl MarginfiAccountInitialize { } #[derive(BorshDeserialize, BorshSerialize)] -struct MarginfiAccountInitializeInstructionData { +pub struct MarginfiAccountInitializeInstructionData { discriminator: [u8; 8], } impl MarginfiAccountInitializeInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [43, 78, 61, 255, 148, 52, 249, 154], } diff --git a/programs/marginfi-sdk/src/generated/instructions/marginfi_group_configure.rs b/programs/marginfi-sdk/src/generated/instructions/marginfi_group_configure.rs index fbbc3b9d..53933669 100644 --- a/programs/marginfi-sdk/src/generated/instructions/marginfi_group_configure.rs +++ b/programs/marginfi-sdk/src/generated/instructions/marginfi_group_configure.rs @@ -53,12 +53,12 @@ impl MarginfiGroupConfigure { } #[derive(BorshDeserialize, BorshSerialize)] -struct MarginfiGroupConfigureInstructionData { +pub struct MarginfiGroupConfigureInstructionData { discriminator: [u8; 8], } impl MarginfiGroupConfigureInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [62, 199, 81, 78, 33, 13, 236, 61], } diff --git a/programs/marginfi-sdk/src/generated/instructions/marginfi_group_initialize.rs b/programs/marginfi-sdk/src/generated/instructions/marginfi_group_initialize.rs index 21fa0a57..5dc1cc70 100644 --- a/programs/marginfi-sdk/src/generated/instructions/marginfi_group_initialize.rs +++ b/programs/marginfi-sdk/src/generated/instructions/marginfi_group_initialize.rs @@ -52,12 +52,12 @@ impl MarginfiGroupInitialize { } #[derive(BorshDeserialize, BorshSerialize)] -struct MarginfiGroupInitializeInstructionData { +pub struct MarginfiGroupInitializeInstructionData { discriminator: [u8; 8], } impl MarginfiGroupInitializeInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [255, 67, 67, 26, 94, 31, 34, 20], } diff --git a/programs/marginfi-sdk/src/generated/instructions/set_account_flag.rs b/programs/marginfi-sdk/src/generated/instructions/set_account_flag.rs index 09f0da95..84d6f4e7 100644 --- a/programs/marginfi-sdk/src/generated/instructions/set_account_flag.rs +++ b/programs/marginfi-sdk/src/generated/instructions/set_account_flag.rs @@ -56,12 +56,12 @@ impl SetAccountFlag { } #[derive(BorshDeserialize, BorshSerialize)] -struct SetAccountFlagInstructionData { +pub struct SetAccountFlagInstructionData { discriminator: [u8; 8], } impl SetAccountFlagInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [56, 238, 18, 207, 193, 82, 138, 174], } diff --git a/programs/marginfi-sdk/src/generated/instructions/set_new_account_authority.rs b/programs/marginfi-sdk/src/generated/instructions/set_new_account_authority.rs index 6c72ca18..ef380178 100644 --- a/programs/marginfi-sdk/src/generated/instructions/set_new_account_authority.rs +++ b/programs/marginfi-sdk/src/generated/instructions/set_new_account_authority.rs @@ -65,12 +65,12 @@ impl SetNewAccountAuthority { } #[derive(BorshDeserialize, BorshSerialize)] -struct SetNewAccountAuthorityInstructionData { +pub struct SetNewAccountAuthorityInstructionData { discriminator: [u8; 8], } impl SetNewAccountAuthorityInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [153, 162, 50, 84, 182, 201, 74, 179], } diff --git a/programs/marginfi-sdk/src/generated/instructions/unset_account_flag.rs b/programs/marginfi-sdk/src/generated/instructions/unset_account_flag.rs index fd0a80f1..12e24ab0 100644 --- a/programs/marginfi-sdk/src/generated/instructions/unset_account_flag.rs +++ b/programs/marginfi-sdk/src/generated/instructions/unset_account_flag.rs @@ -56,12 +56,12 @@ impl UnsetAccountFlag { } #[derive(BorshDeserialize, BorshSerialize)] -struct UnsetAccountFlagInstructionData { +pub struct UnsetAccountFlagInstructionData { discriminator: [u8; 8], } impl UnsetAccountFlagInstructionData { - fn new() -> Self { + pub fn new() -> Self { Self { discriminator: [56, 81, 56, 85, 92, 49, 255, 70], } diff --git a/programs/marginfi-sdk/src/generated/programs.rs b/programs/marginfi-sdk/src/generated/programs.rs index bd4b2e1e..fa7763f8 100644 --- a/programs/marginfi-sdk/src/generated/programs.rs +++ b/programs/marginfi-sdk/src/generated/programs.rs @@ -5,7 +5,10 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use solana_program::{pubkey, pubkey::Pubkey}; +use solana_program::{ pubkey, pubkey::Pubkey }; -/// `marginfi` program ID. +#[cfg(feature = "staging")] +pub const MARGINFI_ID: Pubkey = pubkey!("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"); + +#[cfg(not(feature = "staging"))] pub const MARGINFI_ID: Pubkey = pubkey!("MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA"); diff --git a/programs/marginfi-sdk/src/generated/types/interest_rate_config.rs b/programs/marginfi-sdk/src/generated/types/interest_rate_config.rs index f996df89..ba0ded9f 100644 --- a/programs/marginfi-sdk/src/generated/types/interest_rate_config.rs +++ b/programs/marginfi-sdk/src/generated/types/interest_rate_config.rs @@ -22,5 +22,7 @@ pub struct InterestRateConfig { pub insurance_ir_fee: WrappedI80F48, pub protocol_fixed_fee_apr: WrappedI80F48, pub protocol_ir_fee: WrappedI80F48, - pub padding: [[u64; 2]; 8], + pub protocol_origination_fee: WrappedI80F48, + pub _padding0: [u8; 16], + pub _padding1: [[u8; 32]; 3], } diff --git a/programs/solauto-sdk/src/generated/accounts/solauto_position.rs b/programs/solauto-sdk/src/generated/accounts/solauto_position.rs index 533595f0..79a172ff 100644 --- a/programs/solauto-sdk/src/generated/accounts/solauto_position.rs +++ b/programs/solauto-sdk/src/generated/accounts/solauto_position.rs @@ -30,7 +30,7 @@ pub struct SolautoPosition { pub position: PositionData, pub state: PositionState, pub rebalance: RebalanceData, - pub padding: [u32; 32], + pub padding: [u32; 20], } impl SolautoPosition { diff --git a/programs/solauto-sdk/src/generated/errors/solauto.rs b/programs/solauto-sdk/src/generated/errors/solauto.rs index bd77a38b..88334ec8 100644 --- a/programs/solauto-sdk/src/generated/errors/solauto.rs +++ b/programs/solauto-sdk/src/generated/errors/solauto.rs @@ -10,33 +10,51 @@ use thiserror::Error; #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] pub enum SolautoError { - /// 0 (0x0) - Missing or incorrect accounts provided for the given instruction - #[error("Missing or incorrect accounts provided for the given instruction")] + /// 0 (0x0) - Missing or incorrect accounts provided for the given instructions + #[error("Missing or incorrect accounts provided for the given instructions")] IncorrectAccounts, /// 1 (0x1) - Failed to deserialize account data #[error("Failed to deserialize account data")] FailedAccountDeserialization, - /// 2 (0x2) - Invalid position settings provided - #[error("Invalid position settings provided")] - InvalidPositionSettings, - /// 3 (0x3) - Invalid DCA configuration provided + /// 2 (0x2) - Invalid Boost-to param + #[error("Invalid Boost-to param")] + InvalidBoostToSetting, + /// 3 (0x3) - Invalid Boost gap param + #[error("Invalid Boost gap param")] + InvalidBoostGapSetting, + /// 4 (0x4) - Invalid repay-to param + #[error("Invalid repay-to param")] + InvalidRepayToSetting, + /// 5 (0x5) - Invalid repay gap param + #[error("Invalid repay gap param")] + InvalidRepayGapSetting, + /// 6 (0x6) - Invalid repay-from (repay-to + repay gap) + #[error("Invalid repay-from (repay-to + repay gap)")] + InvalidRepayFromSetting, + /// 7 (0x7) - Invalid DCA configuration provided #[error("Invalid DCA configuration provided")] InvalidDCASettings, - /// 4 (0x4) - Invalid automation settings provided + /// 8 (0x8) - Invalid automation settings provided #[error("Invalid automation settings provided")] InvalidAutomationData, - /// 5 (0x5) - Invalid position condition to rebalance + /// 9 (0x9) - Invalid position condition to rebalance #[error("Invalid position condition to rebalance")] InvalidRebalanceCondition, - /// 6 (0x6) - Unable to invoke instruction through a CPI + /// 10 (0xA) - Unable to invoke instruction through a CPI #[error("Unable to invoke instruction through a CPI")] InstructionIsCPI, - /// 7 (0x7) - Incorrect set of instructions in the transaction - #[error("Incorrect set of instructions in the transaction")] + /// 11 (0xB) - Incorrect set of instructions or instruction data in the transaction + #[error("Incorrect set of instructions or instruction data in the transaction")] IncorrectInstructions, - /// 8 (0x8) - Incorrect swap amount provided. Likely due to high price volatility + /// 12 (0xC) - Incorrect swap amount provided. Likely due to high price volatility #[error("Incorrect swap amount provided. Likely due to high price volatility")] IncorrectDebtAdjustment, + /// 13 (0xD) - Invalid rebalance was made. Target supply USD and target debt USD was not met + #[error("Invalid rebalance was made. Target supply USD and target debt USD was not met")] + InvalidRebalanceMade, + /// 14 (0xE) - Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority + #[error("Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority")] + NonAuthorityProvidedTargetLTV, } impl solana_program::program_error::PrintProgramError for SolautoError { diff --git a/programs/solauto-sdk/src/generated/instructions/claim_referral_fees.rs b/programs/solauto-sdk/src/generated/instructions/claim_referral_fees.rs index bd639068..b53145a8 100644 --- a/programs/solauto-sdk/src/generated/instructions/claim_referral_fees.rs +++ b/programs/solauto-sdk/src/generated/instructions/claim_referral_fees.rs @@ -28,7 +28,7 @@ pub struct ClaimReferralFees { pub referral_fees_dest_mint: solana_program::pubkey::Pubkey, - pub referral_authority: Option, + pub referral_authority: solana_program::pubkey::Pubkey, pub fees_destination_ta: Option, } @@ -85,17 +85,10 @@ impl ClaimReferralFees { self.referral_fees_dest_mint, false, )); - if let Some(referral_authority) = self.referral_authority { - accounts.push(solana_program::instruction::AccountMeta::new( - referral_authority, - false, - )); - } else { - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - crate::SOLAUTO_ID, - false, - )); - } + accounts.push(solana_program::instruction::AccountMeta::new( + self.referral_authority, + false, + )); if let Some(fees_destination_ta) = self.fees_destination_ta { accounts.push(solana_program::instruction::AccountMeta::new( fees_destination_ta, @@ -144,7 +137,7 @@ impl ClaimReferralFeesInstructionData { /// 6. `[]` referral_state /// 7. `[writable]` referral_fees_dest_ta /// 8. `[]` referral_fees_dest_mint -/// 9. `[writable, optional]` referral_authority +/// 9. `[writable]` referral_authority /// 10. `[writable, optional]` fees_destination_ta #[derive(Default)] pub struct ClaimReferralFeesBuilder { @@ -225,13 +218,12 @@ impl ClaimReferralFeesBuilder { self.referral_fees_dest_mint = Some(referral_fees_dest_mint); self } - /// `[optional account]` #[inline(always)] pub fn referral_authority( &mut self, - referral_authority: Option, + referral_authority: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.referral_authority = referral_authority; + self.referral_authority = Some(referral_authority); self } /// `[optional account]` @@ -285,7 +277,9 @@ impl ClaimReferralFeesBuilder { referral_fees_dest_mint: self .referral_fees_dest_mint .expect("referral_fees_dest_mint is not set"), - referral_authority: self.referral_authority, + referral_authority: self + .referral_authority + .expect("referral_authority is not set"), fees_destination_ta: self.fees_destination_ta, }; @@ -313,7 +307,7 @@ pub struct ClaimReferralFeesCpiAccounts<'a, 'b> { pub referral_fees_dest_mint: &'b solana_program::account_info::AccountInfo<'a>, - pub referral_authority: Option<&'b solana_program::account_info::AccountInfo<'a>>, + pub referral_authority: &'b solana_program::account_info::AccountInfo<'a>, pub fees_destination_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, } @@ -341,7 +335,7 @@ pub struct ClaimReferralFeesCpi<'a, 'b> { pub referral_fees_dest_mint: &'b solana_program::account_info::AccountInfo<'a>, - pub referral_authority: Option<&'b solana_program::account_info::AccountInfo<'a>>, + pub referral_authority: &'b solana_program::account_info::AccountInfo<'a>, pub fees_destination_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, } @@ -443,17 +437,10 @@ impl<'a, 'b> ClaimReferralFeesCpi<'a, 'b> { *self.referral_fees_dest_mint.key, false, )); - if let Some(referral_authority) = self.referral_authority { - accounts.push(solana_program::instruction::AccountMeta::new( - *referral_authority.key, - false, - )); - } else { - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - crate::SOLAUTO_ID, - false, - )); - } + accounts.push(solana_program::instruction::AccountMeta::new( + *self.referral_authority.key, + false, + )); if let Some(fees_destination_ta) = self.fees_destination_ta { accounts.push(solana_program::instruction::AccountMeta::new( *fees_destination_ta.key, @@ -494,9 +481,7 @@ impl<'a, 'b> ClaimReferralFeesCpi<'a, 'b> { account_infos.push(self.referral_state.clone()); account_infos.push(self.referral_fees_dest_ta.clone()); account_infos.push(self.referral_fees_dest_mint.clone()); - if let Some(referral_authority) = self.referral_authority { - account_infos.push(referral_authority.clone()); - } + account_infos.push(self.referral_authority.clone()); if let Some(fees_destination_ta) = self.fees_destination_ta { account_infos.push(fees_destination_ta.clone()); } @@ -525,7 +510,7 @@ impl<'a, 'b> ClaimReferralFeesCpi<'a, 'b> { /// 6. `[]` referral_state /// 7. `[writable]` referral_fees_dest_ta /// 8. `[]` referral_fees_dest_mint -/// 9. `[writable, optional]` referral_authority +/// 9. `[writable]` referral_authority /// 10. `[writable, optional]` fees_destination_ta pub struct ClaimReferralFeesCpiBuilder<'a, 'b> { instruction: Box>, @@ -620,13 +605,12 @@ impl<'a, 'b> ClaimReferralFeesCpiBuilder<'a, 'b> { self.instruction.referral_fees_dest_mint = Some(referral_fees_dest_mint); self } - /// `[optional account]` #[inline(always)] pub fn referral_authority( &mut self, - referral_authority: Option<&'b solana_program::account_info::AccountInfo<'a>>, + referral_authority: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.referral_authority = referral_authority; + self.instruction.referral_authority = Some(referral_authority); self } /// `[optional account]` @@ -718,7 +702,10 @@ impl<'a, 'b> ClaimReferralFeesCpiBuilder<'a, 'b> { .referral_fees_dest_mint .expect("referral_fees_dest_mint is not set"), - referral_authority: self.instruction.referral_authority, + referral_authority: self + .instruction + .referral_authority + .expect("referral_authority is not set"), fees_destination_ta: self.instruction.fees_destination_ta, }; diff --git a/programs/solauto-sdk/src/generated/instructions/close_position.rs b/programs/solauto-sdk/src/generated/instructions/close_position.rs index 68fb0855..b03aeb5c 100644 --- a/programs/solauto-sdk/src/generated/instructions/close_position.rs +++ b/programs/solauto-sdk/src/generated/instructions/close_position.rs @@ -20,7 +20,7 @@ pub struct ClosePosition { pub solauto_position: solana_program::pubkey::Pubkey, - pub protocol_account: solana_program::pubkey::Pubkey, + pub lp_user_account: solana_program::pubkey::Pubkey, pub position_supply_ta: solana_program::pubkey::Pubkey, @@ -62,7 +62,7 @@ impl ClosePosition { false, )); accounts.push(solana_program::instruction::AccountMeta::new( - self.protocol_account, + self.lp_user_account, false, )); accounts.push(solana_program::instruction::AccountMeta::new( @@ -112,7 +112,7 @@ impl ClosePositionInstructionData { /// 2. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) /// 3. `[optional]` ata_program (default to `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`) /// 4. `[writable]` solauto_position -/// 5. `[writable]` protocol_account +/// 5. `[writable]` lp_user_account /// 6. `[writable]` position_supply_ta /// 7. `[writable]` signer_supply_ta /// 8. `[writable]` position_debt_ta @@ -124,7 +124,7 @@ pub struct ClosePositionBuilder { token_program: Option, ata_program: Option, solauto_position: Option, - protocol_account: Option, + lp_user_account: Option, position_supply_ta: Option, signer_supply_ta: Option, position_debt_ta: Option, @@ -168,11 +168,11 @@ impl ClosePositionBuilder { self } #[inline(always)] - pub fn protocol_account( + pub fn lp_user_account( &mut self, - protocol_account: solana_program::pubkey::Pubkey, + lp_user_account: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.protocol_account = Some(protocol_account); + self.lp_user_account = Some(lp_user_account); self } #[inline(always)] @@ -236,7 +236,7 @@ impl ClosePositionBuilder { "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" )), solauto_position: self.solauto_position.expect("solauto_position is not set"), - protocol_account: self.protocol_account.expect("protocol_account is not set"), + lp_user_account: self.lp_user_account.expect("lp_user_account is not set"), position_supply_ta: self .position_supply_ta .expect("position_supply_ta is not set"), @@ -261,7 +261,7 @@ pub struct ClosePositionCpiAccounts<'a, 'b> { pub solauto_position: &'b solana_program::account_info::AccountInfo<'a>, - pub protocol_account: &'b solana_program::account_info::AccountInfo<'a>, + pub lp_user_account: &'b solana_program::account_info::AccountInfo<'a>, pub position_supply_ta: &'b solana_program::account_info::AccountInfo<'a>, @@ -287,7 +287,7 @@ pub struct ClosePositionCpi<'a, 'b> { pub solauto_position: &'b solana_program::account_info::AccountInfo<'a>, - pub protocol_account: &'b solana_program::account_info::AccountInfo<'a>, + pub lp_user_account: &'b solana_program::account_info::AccountInfo<'a>, pub position_supply_ta: &'b solana_program::account_info::AccountInfo<'a>, @@ -310,7 +310,7 @@ impl<'a, 'b> ClosePositionCpi<'a, 'b> { token_program: accounts.token_program, ata_program: accounts.ata_program, solauto_position: accounts.solauto_position, - protocol_account: accounts.protocol_account, + lp_user_account: accounts.lp_user_account, position_supply_ta: accounts.position_supply_ta, signer_supply_ta: accounts.signer_supply_ta, position_debt_ta: accounts.position_debt_ta, @@ -372,7 +372,7 @@ impl<'a, 'b> ClosePositionCpi<'a, 'b> { false, )); accounts.push(solana_program::instruction::AccountMeta::new( - *self.protocol_account.key, + *self.lp_user_account.key, false, )); accounts.push(solana_program::instruction::AccountMeta::new( @@ -412,7 +412,7 @@ impl<'a, 'b> ClosePositionCpi<'a, 'b> { account_infos.push(self.token_program.clone()); account_infos.push(self.ata_program.clone()); account_infos.push(self.solauto_position.clone()); - account_infos.push(self.protocol_account.clone()); + account_infos.push(self.lp_user_account.clone()); account_infos.push(self.position_supply_ta.clone()); account_infos.push(self.signer_supply_ta.clone()); account_infos.push(self.position_debt_ta.clone()); @@ -438,7 +438,7 @@ impl<'a, 'b> ClosePositionCpi<'a, 'b> { /// 2. `[]` token_program /// 3. `[]` ata_program /// 4. `[writable]` solauto_position -/// 5. `[writable]` protocol_account +/// 5. `[writable]` lp_user_account /// 6. `[writable]` position_supply_ta /// 7. `[writable]` signer_supply_ta /// 8. `[writable]` position_debt_ta @@ -456,7 +456,7 @@ impl<'a, 'b> ClosePositionCpiBuilder<'a, 'b> { token_program: None, ata_program: None, solauto_position: None, - protocol_account: None, + lp_user_account: None, position_supply_ta: None, signer_supply_ta: None, position_debt_ta: None, @@ -506,11 +506,11 @@ impl<'a, 'b> ClosePositionCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn protocol_account( + pub fn lp_user_account( &mut self, - protocol_account: &'b solana_program::account_info::AccountInfo<'a>, + lp_user_account: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.protocol_account = Some(protocol_account); + self.instruction.lp_user_account = Some(lp_user_account); self } #[inline(always)] @@ -611,10 +611,10 @@ impl<'a, 'b> ClosePositionCpiBuilder<'a, 'b> { .solauto_position .expect("solauto_position is not set"), - protocol_account: self + lp_user_account: self .instruction - .protocol_account - .expect("protocol_account is not set"), + .lp_user_account + .expect("lp_user_account is not set"), position_supply_ta: self .instruction @@ -650,7 +650,7 @@ struct ClosePositionCpiBuilderInstruction<'a, 'b> { token_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ata_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, solauto_position: Option<&'b solana_program::account_info::AccountInfo<'a>>, - protocol_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + lp_user_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, position_supply_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, signer_supply_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, position_debt_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, diff --git a/programs/solauto-sdk/src/generated/instructions/marginfi_open_position.rs b/programs/solauto-sdk/src/generated/instructions/marginfi_open_position.rs index 5a44524c..446e2b78 100644 --- a/programs/solauto-sdk/src/generated/instructions/marginfi_open_position.rs +++ b/programs/solauto-sdk/src/generated/instructions/marginfi_open_position.rs @@ -192,7 +192,6 @@ impl MarginfiOpenPositionInstructionData { pub struct MarginfiOpenPositionInstructionArgs { pub position_type: PositionType, pub position_data: UpdatePositionData, - pub marginfi_account_seed_idx: Option, } /// Instruction builder for `MarginfiOpenPosition`. @@ -241,7 +240,6 @@ pub struct MarginfiOpenPositionBuilder { signer_debt_ta: Option, position_type: Option, position_data: Option, - marginfi_account_seed_idx: Option, __remaining_accounts: Vec, } @@ -389,12 +387,6 @@ impl MarginfiOpenPositionBuilder { self.position_data = Some(position_data); self } - /// `[optional argument]` - #[inline(always)] - pub fn marginfi_account_seed_idx(&mut self, marginfi_account_seed_idx: u64) -> &mut Self { - self.marginfi_account_seed_idx = Some(marginfi_account_seed_idx); - self - } /// Add an aditional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -457,7 +449,6 @@ impl MarginfiOpenPositionBuilder { .position_data .clone() .expect("position_data is not set"), - marginfi_account_seed_idx: self.marginfi_account_seed_idx.clone(), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -821,7 +812,6 @@ impl<'a, 'b> MarginfiOpenPositionCpiBuilder<'a, 'b> { signer_debt_ta: None, position_type: None, position_data: None, - marginfi_account_seed_idx: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -989,12 +979,6 @@ impl<'a, 'b> MarginfiOpenPositionCpiBuilder<'a, 'b> { self.instruction.position_data = Some(position_data); self } - /// `[optional argument]` - #[inline(always)] - pub fn marginfi_account_seed_idx(&mut self, marginfi_account_seed_idx: u64) -> &mut Self { - self.instruction.marginfi_account_seed_idx = Some(marginfi_account_seed_idx); - self - } /// Add an additional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -1047,7 +1031,6 @@ impl<'a, 'b> MarginfiOpenPositionCpiBuilder<'a, 'b> { .position_data .clone() .expect("position_data is not set"), - marginfi_account_seed_idx: self.instruction.marginfi_account_seed_idx.clone(), }; let instruction = MarginfiOpenPositionCpi { __program: self.instruction.__program, @@ -1157,7 +1140,6 @@ struct MarginfiOpenPositionCpiBuilderInstruction<'a, 'b> { signer_debt_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, position_type: Option, position_data: Option, - marginfi_account_seed_idx: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/programs/solauto-sdk/src/generated/instructions/marginfi_rebalance.rs b/programs/solauto-sdk/src/generated/instructions/marginfi_rebalance.rs index 51c587d7..f60a9c17 100644 --- a/programs/solauto-sdk/src/generated/instructions/marginfi_rebalance.rs +++ b/programs/solauto-sdk/src/generated/instructions/marginfi_rebalance.rs @@ -5,7 +5,9 @@ //! [https://github.com/metaplex-foundation/kinobi] //! +use crate::generated::types::PriceType; use crate::generated::types::SolautoRebalanceType; +use crate::generated::types::SwapType; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -290,8 +292,11 @@ impl MarginfiRebalanceInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MarginfiRebalanceInstructionArgs { pub rebalance_type: SolautoRebalanceType, + pub swap_in_amount_base_unit: Option, pub target_liq_utilization_rate_bps: Option, - pub target_in_amount_base_unit: Option, + pub flash_loan_fee_bps: Option, + pub price_type: Option, + pub swap_type: Option, } /// Instruction builder for `MarginfiRebalance`. @@ -351,8 +356,11 @@ pub struct MarginfiRebalanceBuilder { vault_debt_ta: Option, debt_vault_authority: Option, rebalance_type: Option, + swap_in_amount_base_unit: Option, target_liq_utilization_rate_bps: Option, - target_in_amount_base_unit: Option, + flash_loan_fee_bps: Option, + price_type: Option, + swap_type: Option, __remaining_accounts: Vec, } @@ -560,6 +568,12 @@ impl MarginfiRebalanceBuilder { } /// `[optional argument]` #[inline(always)] + pub fn swap_in_amount_base_unit(&mut self, swap_in_amount_base_unit: u64) -> &mut Self { + self.swap_in_amount_base_unit = Some(swap_in_amount_base_unit); + self + } + /// `[optional argument]` + #[inline(always)] pub fn target_liq_utilization_rate_bps( &mut self, target_liq_utilization_rate_bps: u16, @@ -569,8 +583,20 @@ impl MarginfiRebalanceBuilder { } /// `[optional argument]` #[inline(always)] - pub fn target_in_amount_base_unit(&mut self, target_in_amount_base_unit: u64) -> &mut Self { - self.target_in_amount_base_unit = Some(target_in_amount_base_unit); + pub fn flash_loan_fee_bps(&mut self, flash_loan_fee_bps: u16) -> &mut Self { + self.flash_loan_fee_bps = Some(flash_loan_fee_bps); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn price_type(&mut self, price_type: PriceType) -> &mut Self { + self.price_type = Some(price_type); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn swap_type(&mut self, swap_type: SwapType) -> &mut Self { + self.swap_type = Some(swap_type); self } /// Add an aditional account to the instruction. @@ -633,8 +659,11 @@ impl MarginfiRebalanceBuilder { .rebalance_type .clone() .expect("rebalance_type is not set"), + swap_in_amount_base_unit: self.swap_in_amount_base_unit.clone(), target_liq_utilization_rate_bps: self.target_liq_utilization_rate_bps.clone(), - target_in_amount_base_unit: self.target_in_amount_base_unit.clone(), + flash_loan_fee_bps: self.flash_loan_fee_bps.clone(), + price_type: self.price_type.clone(), + swap_type: self.swap_type.clone(), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -1150,8 +1179,11 @@ impl<'a, 'b> MarginfiRebalanceCpiBuilder<'a, 'b> { vault_debt_ta: None, debt_vault_authority: None, rebalance_type: None, + swap_in_amount_base_unit: None, target_liq_utilization_rate_bps: None, - target_in_amount_base_unit: None, + flash_loan_fee_bps: None, + price_type: None, + swap_type: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -1375,6 +1407,12 @@ impl<'a, 'b> MarginfiRebalanceCpiBuilder<'a, 'b> { } /// `[optional argument]` #[inline(always)] + pub fn swap_in_amount_base_unit(&mut self, swap_in_amount_base_unit: u64) -> &mut Self { + self.instruction.swap_in_amount_base_unit = Some(swap_in_amount_base_unit); + self + } + /// `[optional argument]` + #[inline(always)] pub fn target_liq_utilization_rate_bps( &mut self, target_liq_utilization_rate_bps: u16, @@ -1384,8 +1422,20 @@ impl<'a, 'b> MarginfiRebalanceCpiBuilder<'a, 'b> { } /// `[optional argument]` #[inline(always)] - pub fn target_in_amount_base_unit(&mut self, target_in_amount_base_unit: u64) -> &mut Self { - self.instruction.target_in_amount_base_unit = Some(target_in_amount_base_unit); + pub fn flash_loan_fee_bps(&mut self, flash_loan_fee_bps: u16) -> &mut Self { + self.instruction.flash_loan_fee_bps = Some(flash_loan_fee_bps); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn price_type(&mut self, price_type: PriceType) -> &mut Self { + self.instruction.price_type = Some(price_type); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn swap_type(&mut self, swap_type: SwapType) -> &mut Self { + self.instruction.swap_type = Some(swap_type); self } /// Add an additional account to the instruction. @@ -1435,11 +1485,14 @@ impl<'a, 'b> MarginfiRebalanceCpiBuilder<'a, 'b> { .rebalance_type .clone() .expect("rebalance_type is not set"), + swap_in_amount_base_unit: self.instruction.swap_in_amount_base_unit.clone(), target_liq_utilization_rate_bps: self .instruction .target_liq_utilization_rate_bps .clone(), - target_in_amount_base_unit: self.instruction.target_in_amount_base_unit.clone(), + flash_loan_fee_bps: self.instruction.flash_loan_fee_bps.clone(), + price_type: self.instruction.price_type.clone(), + swap_type: self.instruction.swap_type.clone(), }; let instruction = MarginfiRebalanceCpi { __program: self.instruction.__program, @@ -1560,8 +1613,11 @@ struct MarginfiRebalanceCpiBuilderInstruction<'a, 'b> { vault_debt_ta: Option<&'b solana_program::account_info::AccountInfo<'a>>, debt_vault_authority: Option<&'b solana_program::account_info::AccountInfo<'a>>, rebalance_type: Option, + swap_in_amount_base_unit: Option, target_liq_utilization_rate_bps: Option, - target_in_amount_base_unit: Option, + flash_loan_fee_bps: Option, + price_type: Option, + swap_type: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/programs/solauto-sdk/src/generated/instructions/marginfi_refresh_data.rs b/programs/solauto-sdk/src/generated/instructions/marginfi_refresh_data.rs index 69e352e0..089770a2 100644 --- a/programs/solauto-sdk/src/generated/instructions/marginfi_refresh_data.rs +++ b/programs/solauto-sdk/src/generated/instructions/marginfi_refresh_data.rs @@ -5,6 +5,7 @@ //! [https://github.com/metaplex-foundation/kinobi] //! +use crate::generated::types::PriceType; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -30,12 +31,16 @@ pub struct MarginfiRefreshData { } impl MarginfiRefreshData { - pub fn instruction(&self) -> solana_program::instruction::Instruction { - self.instruction_with_remaining_accounts(&[]) + pub fn instruction( + &self, + args: MarginfiRefreshDataInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) } #[allow(clippy::vec_init_then_push)] pub fn instruction_with_remaining_accounts( &self, + args: MarginfiRefreshDataInstructionArgs, remaining_accounts: &[solana_program::instruction::AccountMeta], ) -> solana_program::instruction::Instruction { let mut accounts = Vec::with_capacity(9 + remaining_accounts.len()); @@ -76,9 +81,11 @@ impl MarginfiRefreshData { false, )); accounts.extend_from_slice(remaining_accounts); - let data = MarginfiRefreshDataInstructionData::new() + let mut data = MarginfiRefreshDataInstructionData::new() .try_to_vec() .unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); solana_program::instruction::Instruction { program_id: crate::SOLAUTO_ID, @@ -99,6 +106,12 @@ impl MarginfiRefreshDataInstructionData { } } +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct MarginfiRefreshDataInstructionArgs { + pub price_type: PriceType, +} + /// Instruction builder for `MarginfiRefreshData`. /// /// ### Accounts: @@ -123,6 +136,7 @@ pub struct MarginfiRefreshDataBuilder { debt_bank: Option, debt_price_oracle: Option, solauto_position: Option, + price_type: Option, __remaining_accounts: Vec, } @@ -190,6 +204,11 @@ impl MarginfiRefreshDataBuilder { self.solauto_position = Some(solauto_position); self } + #[inline(always)] + pub fn price_type(&mut self, price_type: PriceType) -> &mut Self { + self.price_type = Some(price_type); + self + } /// Add an aditional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -225,8 +244,11 @@ impl MarginfiRefreshDataBuilder { .expect("debt_price_oracle is not set"), solauto_position: self.solauto_position.expect("solauto_position is not set"), }; + let args = MarginfiRefreshDataInstructionArgs { + price_type: self.price_type.clone().expect("price_type is not set"), + }; - accounts.instruction_with_remaining_accounts(&self.__remaining_accounts) + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) } } @@ -273,12 +295,15 @@ pub struct MarginfiRefreshDataCpi<'a, 'b> { pub debt_price_oracle: &'b solana_program::account_info::AccountInfo<'a>, pub solauto_position: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: MarginfiRefreshDataInstructionArgs, } impl<'a, 'b> MarginfiRefreshDataCpi<'a, 'b> { pub fn new( program: &'b solana_program::account_info::AccountInfo<'a>, accounts: MarginfiRefreshDataCpiAccounts<'a, 'b>, + args: MarginfiRefreshDataInstructionArgs, ) -> Self { Self { __program: program, @@ -291,6 +316,7 @@ impl<'a, 'b> MarginfiRefreshDataCpi<'a, 'b> { debt_bank: accounts.debt_bank, debt_price_oracle: accounts.debt_price_oracle, solauto_position: accounts.solauto_position, + __args: args, } } #[inline(always)] @@ -370,9 +396,11 @@ impl<'a, 'b> MarginfiRefreshDataCpi<'a, 'b> { is_writable: remaining_account.2, }) }); - let data = MarginfiRefreshDataInstructionData::new() + let mut data = MarginfiRefreshDataInstructionData::new() .try_to_vec() .unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); let instruction = solana_program::instruction::Instruction { program_id: crate::SOLAUTO_ID, @@ -432,6 +460,7 @@ impl<'a, 'b> MarginfiRefreshDataCpiBuilder<'a, 'b> { debt_bank: None, debt_price_oracle: None, solauto_position: None, + price_type: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -508,6 +537,11 @@ impl<'a, 'b> MarginfiRefreshDataCpiBuilder<'a, 'b> { self.instruction.solauto_position = Some(solauto_position); self } + #[inline(always)] + pub fn price_type(&mut self, price_type: PriceType) -> &mut Self { + self.instruction.price_type = Some(price_type); + self + } /// Add an additional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -549,6 +583,13 @@ impl<'a, 'b> MarginfiRefreshDataCpiBuilder<'a, 'b> { &self, signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { + let args = MarginfiRefreshDataInstructionArgs { + price_type: self + .instruction + .price_type + .clone() + .expect("price_type is not set"), + }; let instruction = MarginfiRefreshDataCpi { __program: self.instruction.__program, @@ -590,6 +631,7 @@ impl<'a, 'b> MarginfiRefreshDataCpiBuilder<'a, 'b> { .instruction .solauto_position .expect("solauto_position is not set"), + __args: args, }; instruction.invoke_signed_with_remaining_accounts( signers_seeds, @@ -609,6 +651,7 @@ struct MarginfiRefreshDataCpiBuilderInstruction<'a, 'b> { debt_bank: Option<&'b solana_program::account_info::AccountInfo<'a>>, debt_price_oracle: Option<&'b solana_program::account_info::AccountInfo<'a>>, solauto_position: Option<&'b solana_program::account_info::AccountInfo<'a>>, + price_type: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/programs/solauto-sdk/src/generated/types/mod.rs b/programs/solauto-sdk/src/generated/types/mod.rs index 9e9099f0..447e7c40 100644 --- a/programs/solauto-sdk/src/generated/types/mod.rs +++ b/programs/solauto-sdk/src/generated/types/mod.rs @@ -13,16 +13,23 @@ pub(crate) mod r#lending_platform; pub(crate) mod r#pod_bool; pub(crate) mod r#position_data; pub(crate) mod r#position_state; -pub(crate) mod r#position_token_usage; +pub(crate) mod r#position_token_state; pub(crate) mod r#position_type; +pub(crate) mod r#price_type; pub(crate) mod r#rebalance_data; pub(crate) mod r#rebalance_direction; +pub(crate) mod r#rebalance_instruction_data; +pub(crate) mod r#rebalance_state_values; +pub(crate) mod r#rebalance_step; pub(crate) mod r#solauto_action; pub(crate) mod r#solauto_rebalance_type; pub(crate) mod r#solauto_settings_parameters; pub(crate) mod r#solauto_settings_parameters_inp; +pub(crate) mod r#swap_type; pub(crate) mod r#token_amount; pub(crate) mod r#token_balance_amount; +pub(crate) mod r#token_balance_change; +pub(crate) mod r#token_balance_change_type; pub(crate) mod r#token_type; pub(crate) mod r#update_position_data; @@ -34,15 +41,22 @@ pub use self::r#lending_platform::*; pub use self::r#pod_bool::*; pub use self::r#position_data::*; pub use self::r#position_state::*; -pub use self::r#position_token_usage::*; +pub use self::r#position_token_state::*; pub use self::r#position_type::*; +pub use self::r#price_type::*; pub use self::r#rebalance_data::*; pub use self::r#rebalance_direction::*; +pub use self::r#rebalance_instruction_data::*; +pub use self::r#rebalance_state_values::*; +pub use self::r#rebalance_step::*; pub use self::r#solauto_action::*; pub use self::r#solauto_rebalance_type::*; pub use self::r#solauto_settings_parameters::*; pub use self::r#solauto_settings_parameters_inp::*; +pub use self::r#swap_type::*; pub use self::r#token_amount::*; pub use self::r#token_balance_amount::*; +pub use self::r#token_balance_change::*; +pub use self::r#token_balance_change_type::*; pub use self::r#token_type::*; pub use self::r#update_position_data::*; diff --git a/programs/solauto-sdk/src/generated/types/position_data.rs b/programs/solauto-sdk/src/generated/types/position_data.rs index b242e31b..810803c2 100644 --- a/programs/solauto-sdk/src/generated/types/position_data.rs +++ b/programs/solauto-sdk/src/generated/types/position_data.rs @@ -5,7 +5,6 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use crate::generated::types::DCASettings; use crate::generated::types::LendingPlatform; use crate::generated::types::SolautoSettingsParameters; use borsh::BorshDeserialize; @@ -21,18 +20,22 @@ pub struct PositionData { feature = "serde", serde(with = "serde_with::As::") )] - pub protocol_user_account: Pubkey, + pub lp_user_account: Pubkey, #[cfg_attr( feature = "serde", serde(with = "serde_with::As::") )] - pub protocol_supply_account: Pubkey, + pub lp_supply_account: Pubkey, #[cfg_attr( feature = "serde", serde(with = "serde_with::As::") )] - pub protocol_debt_account: Pubkey, - pub setting_params: SolautoSettingsParameters, - pub dca: DCASettings, - pub padding: [u32; 4], + pub lp_debt_account: Pubkey, + pub settings: SolautoSettingsParameters, + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub lp_pool_account: Pubkey, + pub padding: [u32; 20], } diff --git a/programs/solauto-sdk/src/generated/types/position_state.rs b/programs/solauto-sdk/src/generated/types/position_state.rs index 7cb41a62..5e104988 100644 --- a/programs/solauto-sdk/src/generated/types/position_state.rs +++ b/programs/solauto-sdk/src/generated/types/position_state.rs @@ -5,7 +5,7 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use crate::generated::types::PositionTokenUsage; +use crate::generated::types::PositionTokenState; use crate::generated::types::TokenAmount; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -16,11 +16,11 @@ pub struct PositionState { pub liq_utilization_rate_bps: u16, pub padding1: [u8; 6], pub net_worth: TokenAmount, - pub supply: PositionTokenUsage, - pub debt: PositionTokenUsage, + pub supply: PositionTokenState, + pub debt: PositionTokenState, pub max_ltv_bps: u16, pub liq_threshold_bps: u16, pub padding2: [u8; 4], - pub last_updated: u64, + pub last_refreshed: u64, pub padding: [u32; 2], } diff --git a/programs/solauto-sdk/src/generated/types/position_token_usage.rs b/programs/solauto-sdk/src/generated/types/position_token_state.rs similarity index 87% rename from programs/solauto-sdk/src/generated/types/position_token_usage.rs rename to programs/solauto-sdk/src/generated/types/position_token_state.rs index c4ded889..23cbccac 100644 --- a/programs/solauto-sdk/src/generated/types/position_token_usage.rs +++ b/programs/solauto-sdk/src/generated/types/position_token_state.rs @@ -12,19 +12,18 @@ use solana_program::pubkey::Pubkey; #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct PositionTokenUsage { +pub struct PositionTokenState { #[cfg_attr( feature = "serde", serde(with = "serde_with::As::") )] pub mint: Pubkey, pub decimals: u8, - pub padding1: [u8; 7], + pub padding1: [u8; 5], + pub borrow_fee_bps: u16, pub amount_used: TokenAmount, pub amount_can_be_used: TokenAmount, pub base_amount_market_price_usd: u64, - pub flash_loan_fee_bps: u16, - pub borrow_fee_bps: u16, - pub padding2: [u8; 4], + pub padding2: [u8; 8], pub padding: [u8; 32], } diff --git a/programs/solauto-sdk/src/generated/types/price_type.rs b/programs/solauto-sdk/src/generated/types/price_type.rs new file mode 100644 index 00000000..1591656e --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/price_type.rs @@ -0,0 +1,19 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use num_derive::FromPrimitive; + +#[derive( + BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash, FromPrimitive, +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PriceType { + Realtime, + Ema, +} diff --git a/programs/solauto-sdk/src/generated/types/rebalance_data.rs b/programs/solauto-sdk/src/generated/types/rebalance_data.rs index 2b6f4256..c4454894 100644 --- a/programs/solauto-sdk/src/generated/types/rebalance_data.rs +++ b/programs/solauto-sdk/src/generated/types/rebalance_data.rs @@ -5,18 +5,15 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use crate::generated::types::RebalanceDirection; -use crate::generated::types::SolautoRebalanceType; +use crate::generated::types::RebalanceInstructionData; +use crate::generated::types::RebalanceStateValues; use borsh::BorshDeserialize; use borsh::BorshSerialize; #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RebalanceData { - pub rebalance_type: SolautoRebalanceType, - pub padding1: [u8; 7], - pub rebalance_direction: RebalanceDirection, - pub padding2: [u8; 7], - pub flash_loan_amount: u64, - pub padding: [u8; 32], + pub ixs: RebalanceInstructionData, + pub values: RebalanceStateValues, + pub padding: [u32; 4], } diff --git a/programs/solauto-sdk/src/generated/types/rebalance_direction.rs b/programs/solauto-sdk/src/generated/types/rebalance_direction.rs index 70154bea..29eff2f2 100644 --- a/programs/solauto-sdk/src/generated/types/rebalance_direction.rs +++ b/programs/solauto-sdk/src/generated/types/rebalance_direction.rs @@ -14,6 +14,7 @@ use num_derive::FromPrimitive; )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum RebalanceDirection { + None, Boost, Repay, } diff --git a/programs/solauto-sdk/src/generated/types/rebalance_instruction_data.rs b/programs/solauto-sdk/src/generated/types/rebalance_instruction_data.rs new file mode 100644 index 00000000..1a8117e9 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/rebalance_instruction_data.rs @@ -0,0 +1,23 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use crate::generated::types::PodBool; +use crate::generated::types::SolautoRebalanceType; +use crate::generated::types::SwapType; +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct RebalanceInstructionData { + pub active: PodBool, + pub rebalance_type: SolautoRebalanceType, + pub swap_type: SwapType, + pub padding1: [u8; 5], + pub flash_loan_amount: u64, + pub padding: [u32; 4], +} diff --git a/programs/solauto-sdk/src/generated/types/rebalance_state_values.rs b/programs/solauto-sdk/src/generated/types/rebalance_state_values.rs new file mode 100644 index 00000000..f8b6f606 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/rebalance_state_values.rs @@ -0,0 +1,22 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use crate::generated::types::RebalanceDirection; +use crate::generated::types::TokenBalanceChange; +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct RebalanceStateValues { + pub rebalance_direction: RebalanceDirection, + pub padding1: [u8; 7], + pub target_supply_usd: u64, + pub target_debt_usd: u64, + pub token_balance_change: TokenBalanceChange, + pub padding: [u32; 4], +} diff --git a/programs/solauto-sdk/src/generated/types/rebalance_step.rs b/programs/solauto-sdk/src/generated/types/rebalance_step.rs new file mode 100644 index 00000000..ca51ed93 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/rebalance_step.rs @@ -0,0 +1,19 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use num_derive::FromPrimitive; + +#[derive( + BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash, FromPrimitive, +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum RebalanceStep { + PreSwap, + PostSwap, +} diff --git a/programs/solauto-sdk/src/generated/types/solauto_rebalance_type.rs b/programs/solauto-sdk/src/generated/types/solauto_rebalance_type.rs index 054d51bb..176c2180 100644 --- a/programs/solauto-sdk/src/generated/types/solauto_rebalance_type.rs +++ b/programs/solauto-sdk/src/generated/types/solauto_rebalance_type.rs @@ -14,8 +14,8 @@ use num_derive::FromPrimitive; )] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SolautoRebalanceType { - None, Regular, DoubleRebalanceWithFL, - SingleRebalanceWithFL, + FLSwapThenRebalance, + FLRebalanceThenSwap, } diff --git a/programs/solauto-sdk/src/generated/types/solauto_settings_parameters.rs b/programs/solauto-sdk/src/generated/types/solauto_settings_parameters.rs index 06f63fce..22094a51 100644 --- a/programs/solauto-sdk/src/generated/types/solauto_settings_parameters.rs +++ b/programs/solauto-sdk/src/generated/types/solauto_settings_parameters.rs @@ -5,7 +5,6 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use crate::generated::types::AutomationSettings; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -16,8 +15,5 @@ pub struct SolautoSettingsParameters { pub boost_gap: u16, pub repay_to_bps: u16, pub repay_gap: u16, - pub target_boost_to_bps: u16, - pub padding1: [u8; 6], - pub automation: AutomationSettings, - pub padding: [u8; 32], + pub padding: [u32; 24], } diff --git a/programs/solauto-sdk/src/generated/types/solauto_settings_parameters_inp.rs b/programs/solauto-sdk/src/generated/types/solauto_settings_parameters_inp.rs index 56bf6c34..a9b45ad4 100644 --- a/programs/solauto-sdk/src/generated/types/solauto_settings_parameters_inp.rs +++ b/programs/solauto-sdk/src/generated/types/solauto_settings_parameters_inp.rs @@ -5,7 +5,6 @@ //! [https://github.com/metaplex-foundation/kinobi] //! -use crate::generated::types::AutomationSettingsInp; use borsh::BorshDeserialize; use borsh::BorshSerialize; @@ -16,6 +15,4 @@ pub struct SolautoSettingsParametersInp { pub boost_gap: u16, pub repay_to_bps: u16, pub repay_gap: u16, - pub target_boost_to_bps: Option, - pub automation: Option, } diff --git a/programs/solauto-sdk/src/generated/types/swap_type.rs b/programs/solauto-sdk/src/generated/types/swap_type.rs new file mode 100644 index 00000000..492c7aa5 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/swap_type.rs @@ -0,0 +1,19 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use num_derive::FromPrimitive; + +#[derive( + BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash, FromPrimitive, +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum SwapType { + ExactIn, + ExactOut, +} diff --git a/programs/solauto-sdk/src/generated/types/token_balance_change.rs b/programs/solauto-sdk/src/generated/types/token_balance_change.rs new file mode 100644 index 00000000..e296c3c4 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/token_balance_change.rs @@ -0,0 +1,18 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use crate::generated::types::TokenBalanceChangeType; +use borsh::BorshDeserialize; +use borsh::BorshSerialize; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TokenBalanceChange { + pub change_type: TokenBalanceChangeType, + pub padding1: [u8; 7], + pub amount_usd: u64, +} diff --git a/programs/solauto-sdk/src/generated/types/token_balance_change_type.rs b/programs/solauto-sdk/src/generated/types/token_balance_change_type.rs new file mode 100644 index 00000000..a748a9f0 --- /dev/null +++ b/programs/solauto-sdk/src/generated/types/token_balance_change_type.rs @@ -0,0 +1,22 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! [https://github.com/metaplex-foundation/kinobi] +//! + +use borsh::BorshDeserialize; +use borsh::BorshSerialize; +use num_derive::FromPrimitive; + +#[derive( + BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Hash, FromPrimitive, +)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TokenBalanceChangeType { + None, + PreSwapDeposit, + PostSwapDeposit, + PostRebalanceWithdrawSupplyToken, + PostRebalanceWithdrawDebtToken, +} diff --git a/programs/solauto-sdk/src/generated/types/update_position_data.rs b/programs/solauto-sdk/src/generated/types/update_position_data.rs index 1aed1631..894a6ba1 100644 --- a/programs/solauto-sdk/src/generated/types/update_position_data.rs +++ b/programs/solauto-sdk/src/generated/types/update_position_data.rs @@ -14,6 +14,6 @@ use borsh::BorshSerialize; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UpdatePositionData { pub position_id: u8, - pub setting_params: Option, + pub settings: Option, pub dca: Option, } diff --git a/programs/solauto-sdk/tests/close_position.rs b/programs/solauto-sdk/tests/close_position.rs index 7d988210..c50d5b6f 100644 --- a/programs/solauto-sdk/tests/close_position.rs +++ b/programs/solauto-sdk/tests/close_position.rs @@ -22,7 +22,7 @@ mod close_position { .unwrap() .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let current_supply_balance = 3445655; data.general @@ -79,7 +79,7 @@ mod close_position { .unwrap() .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let tx = Transaction::new_signed_with_payer( &[data.general.close_position_ix().signer(temp_account.pubkey()).instruction()], @@ -99,16 +99,14 @@ mod close_position { .unwrap() .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let temp_account = Keypair::new(); let fake_supply_ta = get_associated_token_address( &temp_account.pubkey(), &data.general.supply_mint.pubkey() ); - data.general - .create_ata(temp_account.pubkey(), &data.general.supply_mint).await - .unwrap(); + data.general.create_ata(temp_account.pubkey(), &data.general.supply_mint).await.unwrap(); // Fake position supply token account let err = data.general @@ -129,19 +127,12 @@ mod close_position { &temp_account.pubkey(), &data.general.debt_mint.pubkey() ); - data.general - .create_ata(temp_account.pubkey(), &data.general.debt_mint).await - .unwrap(); + data.general.create_ata(temp_account.pubkey(), &data.general.debt_mint).await.unwrap(); // Fake position debt token account let err = data.general .execute_instructions( - vec![ - data.general - .close_position_ix() - .position_debt_ta(fake_debt_ta) - .instruction() - ], + vec![data.general.close_position_ix().position_debt_ta(fake_debt_ta).instruction()], None ).await .unwrap_err(); diff --git a/programs/solauto-sdk/tests/general.rs b/programs/solauto-sdk/tests/general.rs index 416836eb..9d5c4e0c 100644 --- a/programs/solauto-sdk/tests/general.rs +++ b/programs/solauto-sdk/tests/general.rs @@ -7,7 +7,6 @@ mod general { use solana_sdk::{ instruction::InstructionError, program_pack::Pack, - pubkey::Pubkey, rent::Rent, signature::Keypair, signer::Signer, @@ -49,7 +48,7 @@ mod general { ); data.general.ctx.banks_client.process_transaction(tx).await.unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); } #[tokio::test] @@ -79,7 +78,7 @@ mod general { ); data.general.ctx.banks_client.process_transaction(tx).await.unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let account = data.general.ctx.banks_client.get_account(data.general.position_supply_ta).await.unwrap(); assert!(account.is_some()); @@ -98,7 +97,7 @@ mod general { .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let solauto_position = data.general.solauto_position.clone(); let mut data = MarginfiTestData::new(&args).await; @@ -111,7 +110,7 @@ mod general { .execute_instructions( vec![ data - .open_position_ix(Some(data.general.default_setting_params.clone()), None) + .open_position_ix(Some(data.general.default_settings.clone()), None) // Pass incorrect solauto position for the given signer .solauto_position(solauto_position) .instruction(), @@ -122,98 +121,4 @@ mod general { assert_instruction_error!(err, InstructionError::MissingRequiredSignature); } - - #[tokio::test] - async fn cancel_dca() { - let args = GeneralArgs::new(); - let mut data = MarginfiTestData::new(&args).await; - data.test_prefixtures().await - .unwrap() - .general.create_referral_state_accounts().await - .unwrap(); - - let dca_amount = 50_000; - data.general - .mint_tokens_to_ta( - data.general.debt_mint, - data.general.signer_debt_ta, - dca_amount - ).await - .unwrap(); - - let active_dca = DCASettingsInp { - automation: AutomationSettingsInp { - unix_start_date: (Utc::now().timestamp() as u64) - 1, - interval_seconds: 60 * 60 * 24, - periods_passed: 0, - target_periods: 5, - }, - dca_in_base_unit: dca_amount, - token_type: TokenType::Debt - }; - data.open_position( - Some(data.general.default_setting_params.clone()), - Some(active_dca.clone()) - ).await.unwrap(); - - data.general - .execute_instructions(vec![data.general.cancel_dca_ix().instruction()], None).await - .unwrap(); - - let solauto_position = data.general.deserialize_account_data::( - data.general.solauto_position - ).await; - assert!(solauto_position.position.dca.automation.target_periods == 0); - assert!(solauto_position.position.dca.dca_in_base_unit == 0); - - let signer_debt_ta = data.general.unpack_account_data::( - data.general.signer_debt_ta - ).await; - assert!(signer_debt_ta.amount == dca_amount); - } - - #[tokio::test] - async fn cancel_dca_incorrect_signer() { - let temp_account = Keypair::new(); - let mut args = GeneralArgs::new(); - args.fund_account(temp_account.pubkey()); - let mut data = MarginfiTestData::new(&args).await; - data.test_prefixtures().await - .unwrap() - .general.create_referral_state_accounts().await - .unwrap(); - - let dca_amount = 50_000; - data.general - .mint_tokens_to_ta( - data.general.debt_mint, - data.general.signer_debt_ta, - dca_amount - ).await - .unwrap(); - - let active_dca = DCASettingsInp { - automation: AutomationSettingsInp { - unix_start_date: (Utc::now().timestamp() as u64) - 1, - interval_seconds: 60 * 60 * 24, - periods_passed: 0, - target_periods: 5, - }, - dca_in_base_unit: dca_amount, - token_type: TokenType::Debt - }; - data.open_position( - Some(data.general.default_setting_params.clone()), - Some(active_dca.clone()) - ).await.unwrap(); - - let tx = Transaction::new_signed_with_payer( - &[data.general.cancel_dca_ix().signer(temp_account.pubkey()).instruction()], - Some(&temp_account.pubkey()), - &[&temp_account], - data.general.ctx.last_blockhash - ); - let err = data.general.ctx.banks_client.process_transaction(tx).await.unwrap_err(); - assert_instruction_error!(err, InstructionError::MissingRequiredSignature); - } } diff --git a/programs/solauto-sdk/tests/marginfi_open_position.rs b/programs/solauto-sdk/tests/marginfi_open_position.rs index d66ce18b..d2bbfcc2 100644 --- a/programs/solauto-sdk/tests/marginfi_open_position.rs +++ b/programs/solauto-sdk/tests/marginfi_open_position.rs @@ -32,15 +32,13 @@ mod open_position { .general.create_referral_state_accounts().await .unwrap(); - let setting_params = SolautoSettingsParametersInp { + let settings = SolautoSettingsParametersInp { boost_to_bps: 5000, boost_gap: 500, repay_to_bps: 7500, repay_gap: 500, - automation: None, - target_boost_to_bps: None, }; - data.open_position(Some(setting_params.clone()), None).await.unwrap(); + data.open_position(Some(settings.clone()), None).await.unwrap(); let solauto_position = data.general.deserialize_account_data::( data.general.solauto_position @@ -50,57 +48,12 @@ mod open_position { assert!(solauto_position.authority == data.general.ctx.payer.pubkey()); let position = &solauto_position.position; - assert!(position.setting_params.boost_to_bps == setting_params.boost_to_bps); + assert!(position.settings.boost_to_bps == settings.boost_to_bps); assert!(position.lending_platform == LendingPlatform::Marginfi); assert!(solauto_position.state.supply.mint == data.general.supply_mint.pubkey()); assert!(solauto_position.state.debt.mint == data.general.debt_mint.pubkey()); - assert!(position.protocol_user_account == data.marginfi_account); - } - - #[tokio::test] - async fn std_open_position_with_dca() { - let args = GeneralArgs::new(); - let mut data = MarginfiTestData::new(&args).await; - data.test_prefixtures().await - .unwrap() - .general.create_referral_state_accounts().await - .unwrap(); - - let dca_amount = 50_000; - data.general - .mint_tokens_to_ta( - data.general.debt_mint, - data.general.signer_debt_ta, - dca_amount - ).await - .unwrap(); - - let active_dca = DCASettingsInp { - automation: AutomationSettingsInp { - unix_start_date: (Utc::now().timestamp() as u64) - 1, - interval_seconds: 60 * 60 * 24, - periods_passed: 0, - target_periods: 5, - }, - dca_in_base_unit: dca_amount, - token_type: TokenType::Debt - }; - data.open_position( - Some(data.general.default_setting_params.clone()), - Some(active_dca.clone()) - ).await.unwrap(); - - let position_account = data.general.deserialize_account_data::( - data.general.solauto_position - ).await; - let position = &position_account.position; - assert!(&position.dca.automation.target_periods == &active_dca.automation.target_periods); - assert!(position.dca.dca_in_base_unit == dca_amount); - - let position_debt_ta = data.general.unpack_account_data::( - data.general.position_debt_ta.clone() - ).await; - assert!(position_debt_ta.amount == dca_amount); + assert!(position.lp_user_account == data.marginfi_account); + assert!(position.lp_pool_account == data.marginfi_group); } #[tokio::test] @@ -117,7 +70,7 @@ mod open_position { let tx = Transaction::new_signed_with_payer( &[ data - .open_position_ix(Some(data.general.default_setting_params.clone()), None) + .open_position_ix(Some(data.general.default_settings.clone()), None) .signer(temp_account.pubkey()) .instruction(), ], @@ -140,7 +93,7 @@ mod open_position { .unwrap(); let mut open_position_ix = data.open_position_ix( - Some(data.general.default_setting_params.clone()), + Some(data.general.default_settings.clone()), None ); diff --git a/programs/solauto-sdk/tests/marginfi_protocol_interaction.rs b/programs/solauto-sdk/tests/marginfi_protocol_interaction.rs index 58e2f3d3..f84b19e0 100644 --- a/programs/solauto-sdk/tests/marginfi_protocol_interaction.rs +++ b/programs/solauto-sdk/tests/marginfi_protocol_interaction.rs @@ -22,7 +22,7 @@ mod marginfi_protocol_interaction { .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let tx = Transaction::new_signed_with_payer( &[ diff --git a/programs/solauto-sdk/tests/test_utils.rs b/programs/solauto-sdk/tests/test_utils.rs index 1b3564ff..5733909c 100644 --- a/programs/solauto-sdk/tests/test_utils.rs +++ b/programs/solauto-sdk/tests/test_utils.rs @@ -13,11 +13,7 @@ use solana_sdk::{ system_instruction, transaction::Transaction, }; -use solauto::{ - constants::SOLAUTO_FEES_WALLET, - instructions::protocol_interaction, - state::referral_state::ReferralState, -}; +use solauto::{ constants::SOLAUTO_FEES_WALLET, state::referral_state::ReferralState }; use solauto_sdk::{ generated::{ instructions::{ @@ -41,7 +37,6 @@ use solauto_sdk::{ }; use spl_associated_token_account::{ get_associated_token_address, instruction as ata_instruction }; use spl_token::{ instruction as token_instruction, state::Mint }; -use rand::{ Rng, thread_rng }; #[macro_export] macro_rules! assert_instruction_error { @@ -120,7 +115,7 @@ pub struct GeneralTestData<'a> { pub position_debt_ta: Pubkey, pub signer_debt_ta: Pubkey, - pub default_setting_params: SolautoSettingsParametersInp, + pub default_settings: SolautoSettingsParametersInp, } impl<'a> GeneralTestData<'a> { @@ -204,13 +199,11 @@ impl<'a> GeneralTestData<'a> { position_debt_ta, signer_debt_ta, - default_setting_params: SolautoSettingsParametersInp { + default_settings: SolautoSettingsParametersInp { boost_to_bps: 5000, boost_gap: 500, repay_to_bps: 7500, repay_gap: 500, - automation: None, - target_boost_to_bps: None, }, } } @@ -373,7 +366,7 @@ impl<'a> GeneralTestData<'a> { .referral_state(self.signer_referral_state) .referral_fees_dest_ta(self.signer_referral_dest_ta) .referral_fees_dest_mint(self.referral_fees_dest_mint.pubkey()) - .referral_authority(Some(self.ctx.payer.pubkey())) + .referral_authority(self.ctx.payer.pubkey()) .fees_destination_ta( Some( get_associated_token_address( @@ -399,13 +392,13 @@ impl<'a> GeneralTestData<'a> { pub fn update_position_ix( &self, - setting_params: Option, + settings: Option, dca: Option ) -> UpdatePositionBuilder { let mut builder = UpdatePositionBuilder::new(); let position_data = UpdatePositionData { position_id: self.position_id, - setting_params, + settings, dca, }; builder @@ -435,7 +428,7 @@ impl<'a> GeneralTestData<'a> { .signer_supply_ta(self.signer_supply_ta) .position_debt_ta(self.position_debt_ta) .signer_debt_ta(self.signer_debt_ta) - .protocol_account(self.solauto_position); + .lp_user_account(self.solauto_position); builder } @@ -455,7 +448,6 @@ pub struct MarginfiTestData<'a> { pub general: GeneralTestData<'a>, pub marginfi_account: Pubkey, pub marginfi_account_keypair: Option, - pub marginfi_account_seed_idx: Option, pub marginfi_group: Pubkey, } @@ -464,16 +456,11 @@ impl<'a> MarginfiTestData<'a> { let general = GeneralTestData::new(args, MARGINFI_PROGRAM).await; let marginfi_group = Keypair::new().pubkey(); - let marginfi_account_seed_idx = if args.position_id != 0 { - let mut rng = thread_rng(); - let random_number: u64 = rng.gen(); - Some(random_number) - } else { - None - }; let (marginfi_account, marginfi_account_keypair) = if args.position_id != 0 { - let seed_idx = marginfi_account_seed_idx.unwrap().to_le_bytes(); - let marginfi_account_seeds = &[general.solauto_position.as_ref(), seed_idx.as_ref()]; + let marginfi_account_seeds = &[ + general.solauto_position.as_ref(), + marginfi_group.as_ref(), + ]; let (marginfi_account, _) = Pubkey::find_program_address( marginfi_account_seeds.as_slice(), &SOLAUTO_ID @@ -488,7 +475,6 @@ impl<'a> MarginfiTestData<'a> { general, marginfi_account, marginfi_account_keypair, - marginfi_account_seed_idx, marginfi_group, } } @@ -518,13 +504,13 @@ impl<'a> MarginfiTestData<'a> { pub fn open_position_ix( &self, - setting_params: Option, + settings: Option, dca: Option ) -> MarginfiOpenPositionBuilder { let mut builder = MarginfiOpenPositionBuilder::new(); let position_data = UpdatePositionData { position_id: self.general.position_id, - setting_params, + settings, dca, }; builder @@ -545,9 +531,6 @@ impl<'a> MarginfiTestData<'a> { .position_debt_ta(self.general.position_debt_ta) .position_type(PositionType::Leverage) .position_data(position_data); - if self.marginfi_account_seed_idx.is_some() { - builder.marginfi_account_seed_idx(self.marginfi_account_seed_idx.unwrap()); - } builder } diff --git a/programs/solauto-sdk/tests/update_position.rs b/programs/solauto-sdk/tests/update_position.rs index 9c952787..70a570a2 100644 --- a/programs/solauto-sdk/tests/update_position.rs +++ b/programs/solauto-sdk/tests/update_position.rs @@ -36,47 +36,14 @@ mod update_position { ).await .unwrap(); - data.open_position( - Some(data.general.default_setting_params.clone()), + Some(data.general.default_settings.clone()), None ).await.unwrap(); let solauto_position = data.general.deserialize_account_data::( data.general.solauto_position ).await; - assert!(solauto_position.position.dca.automation.target_periods == 0); - assert!(solauto_position.position.dca.dca_in_base_unit == 0); - - // Update position's settings and add a DCA - let dca_out_automation = AutomationSettingsInp { - unix_start_date: (Utc::now().timestamp() as u64) - 1, - interval_seconds: 60 * 60 * 10, - periods_passed: 0, - target_periods: 5, - }; - let new_settings = SolautoSettingsParametersInp { - boost_to_bps: 2000, - boost_gap: 1000, - repay_to_bps: 7500, - repay_gap: 500, - automation: Some(dca_out_automation.clone()), - target_boost_to_bps: Some(0), - }; - let new_dca = DCASettingsInp { - automation: dca_out_automation, - dca_in_base_unit: 0, - token_type: TokenType::Debt - }; - data.general - .update_position(Some(new_settings.clone()), Some(new_dca.clone())).await - .unwrap(); - - let solauto_position = data.general.deserialize_account_data::( - data.general.solauto_position - ).await; - assert!(solauto_position.position.setting_params.automation.target_periods == new_settings.automation.as_ref().unwrap().target_periods); - assert!(solauto_position.position.dca.dca_in_base_unit == new_dca.dca_in_base_unit); } #[tokio::test] @@ -89,12 +56,12 @@ mod update_position { .unwrap() .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let tx = Transaction::new_signed_with_payer( &[ data.general - .update_position_ix(Some(data.general.default_setting_params.clone()), None) + .update_position_ix(Some(data.general.default_settings.clone()), None) .signer(temp_account.pubkey()) .instruction(), ], @@ -115,7 +82,7 @@ mod update_position { .general.create_referral_state_accounts().await .unwrap(); - data.open_position(Some(data.general.default_setting_params.clone()), None).await.unwrap(); + data.open_position(Some(data.general.default_settings.clone()), None).await.unwrap(); let temp_wallet = Keypair::new().pubkey(); let fake_debt_ta = get_associated_token_address( diff --git a/programs/solauto-sdk/tests/update_referral_states.rs b/programs/solauto-sdk/tests/update_referral_states.rs index 6dcec1f0..44a8f7d2 100644 --- a/programs/solauto-sdk/tests/update_referral_states.rs +++ b/programs/solauto-sdk/tests/update_referral_states.rs @@ -16,10 +16,10 @@ mod update_referral_states { #[tokio::test] async fn update_referral_states() { - // Create referral state for signer let args = GeneralArgs::new(); let mut data = MarginfiTestData::new(&args).await; + // Create referral state for signer data.general .execute_instructions( vec![data.general.update_referral_states_ix().instruction()], diff --git a/programs/solauto/Cargo.toml b/programs/solauto/Cargo.toml index bdb23caa..c2ea350e 100644 --- a/programs/solauto/Cargo.toml +++ b/programs/solauto/Cargo.toml @@ -14,8 +14,8 @@ no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] default = [] -test = [] -local = [] +test = ["marginfi-sdk/test"] +staging = ["marginfi-sdk/staging"] [net] git-fetch-with-cli = true diff --git a/programs/solauto/README.md b/programs/solauto/README.md index 70f03553..6501acba 100644 --- a/programs/solauto/README.md +++ b/programs/solauto/README.md @@ -2,14 +2,11 @@ Solauto is a program on the Solana blockchain that lets you manage leveraged longs & shorts on auto-pilot to maximize your gains and eliminate the risk of liquidation. -Solauto is (currently only) built on-top of mrgnlend (Marginfi) - a lending and borrowing platform that allows for a wallet to hold & maintain a leverage position through various instructions (lend, borrow, withdraw, repay). - - ## Definitions -#### Marginfi Account (account state) +#### LP user account (account state) -A Marginfi account is the Solana state data that keeps track of a wallet's supply & debt states, i.e. how much SOL has been lent, how much USDC has been borrowed, etc. +An LP (lending platform) user account is the Solana state data that keeps track of a wallet's supply & debt states, i.e. how much SOL has been lent, how much USDC has been borrowed, etc. #### Solauto referral state (account state) @@ -17,34 +14,29 @@ A Solauto referral state should be 1:1 for each user that interacts with Solauto A referral state also exists to collect referral fees (in token accounts tied to the referral state). Only the pubkey of the referral statecan claim the referral fees collected in that referral state. - #### Solauto position (account state) A Solauto position is state data that keeps track of various pubkeys, setting parameters, and balances. A Solauto position is a PDA, defined by the seeds [position_id (u8), authority (pubkey)]. A Solauto position can be self-managed or not. Any position_id set as 0 is self-managed. Any position_id > 0 is NOT self-managed. -A self-managed position essentially means it does not take ownership (authority) for the Marginfi account being used in the desired transaction. Any NON self-managed position will sign CPI calls through the Solauto position PDA, as the Solauto position WILL be the authority over the Marginfi account. - +A self-managed position essentially means it does not take ownership (authority) for the LP user account being used in the desired transaction. Any NON self-managed position will sign CPI calls through the Solauto position PDA, as the Solauto position WILL be the authority over the LP user account. #### Solauto manager (wallet) A special wallet that can sign specific transactions (such as rebalancing), for any NON self-managed Solauto position, with restrictions on the permissions of what it can do. This wallet handles rebalancing Solauto positions, as well as managing DCA actions over time. - #### Setting parameters The most crucial data on a Solauto position. This defines the ranges at when a rebalance is necessary and allowed. It is defined by 4 values: boost_from, boost_to, repay_from, and repay_to. This is what ensures that a leveraged position through Solauto can never get liquidated. - ## Program infrastructure Solauto uses [shank](https://crates.io/crates/shank) to define the instructions & accounts inside of [src/types/instruction.rs](src/types/instruction.rs). -Solauto also imports instruction & account types from marginfi-sdk and jupiter-sdk. These crates are generated with [metaplex kinobi](https://github.com/metaplex-foundation/kinobi) using the idls generated inside of `idls/...`. These should be updated with the idls located on mainnet at all times. - +Solauto also imports types from marginfi-sdk and jupiter-sdk. These crates are generated with [metaplex kinobi](https://github.com/metaplex-foundation/kinobi) using the idls generated inside of `idls/...`. These should be updated with the idls located on mainnet at all times. ## Instructions -#### Update referral states +#### Update referral states Required to create a referral state before other instructions. Can be updated to set the referred_by or also the lookup table. @@ -76,15 +68,11 @@ The most crucial instruction and the core logic of the program: rebalance the So More info can be found in the [rebalance section.](#rebalance) -#### Cancel DCA - -Cancels the active DCA on the Solauto position. Only allowed to be invoked by the Solauto position authority. - #### Close position Close the Solauto position and return all account rents. Only allowed to be invoked by the Solauto position authority. -#### Update position +#### Update position Update the Solauto position setting parameters or active DCA @@ -94,7 +82,6 @@ A rebalance can be successful under one of the 4 conditions: - A boost (if liq utilization rate is < boost_from) - A repay (if liq utilization rate is > repay_from) -- A DCA period is eligible - A target liquidation utilization rate has been provided, and the position authority is signing If none of the conditions are met, the Solauto rebalance instruction will fail. @@ -103,7 +90,7 @@ If a rebalance is increasing leverage, Solauto will borrow extra debt, move it t If a rebalance is decreasing leverage, Solauto will withdraw some supply, move it to a token account, swap it to the debt token, and then repay debt using that balance. -A rebalance will consist of multiple instructions that must exist together in the same transaction. A rebalance will be one of the 3 available sets, depending on the posiiton's state and what must be done. +A rebalance will consist of multiple instructions that must exist together in the same transaction. A rebalance will be one of the 4 available sets, depending on the posiiton's state and what must be done. 1. Regular @@ -119,14 +106,21 @@ A rebalance will consist of multiple instructions that must exist together in th - Rebalance - flash repay -3. Single rebalance with flash loan +3. Flash loan swap then rebalance - Flash borrow - Jup swap - Rebalance - Flash repay -Depending on the rebalance set type, and the current position's state, the rebalance instruction will behave differently. +3. Flash loan rebalance then swap + +- Flash borrow +- Rebalance +- Jup swap +- Flash repay + +Depending on the rebalance set type, and the current position's state, the rebalance instruction will behave differently. A rebalance instruction will always validate the jup swap data & accounts to ensure there is no fee taken and the destination goes to the right token account. diff --git a/programs/solauto/src/clients/marginfi.rs b/programs/solauto/src/clients/marginfi.rs index 24bf7c0d..e676253a 100644 --- a/programs/solauto/src/clients/marginfi.rs +++ b/programs/solauto/src/clients/marginfi.rs @@ -5,7 +5,6 @@ use marginfi_sdk::generated::{ instructions::*, types::{ Balance, OracleSetup }, }; -use fixed_macro::types::I80F48; use pyth_sdk_solana::state::SolanaPriceAccount; use pyth_solana_receiver_sdk::price_update::PriceUpdateV2; use solana_program::{ @@ -18,28 +17,34 @@ use solana_program::{ sysvar::Sysvar, }; use std::{ cmp::min, ops::{ Div, Mul, Sub } }; -use switchboard_v2::AggregatorAccountData; use switchboard_on_demand::PullFeedAccountData; +use switchboard_v2::AggregatorAccountData; use crate::{ state::solauto_position::SolautoPosition, types::{ + errors::SolautoError, instruction::{ accounts::{ Context, MarginfiOpenPositionAccounts }, SolautoStandardAccounts, }, lending_protocol::{ LendingProtocolClient, LendingProtocolTokenAccounts }, shared::{ + AccountMetaFlags, DeserializedAccount, + PriceType, RefreshStateProps, - RefreshedTokenData, - SolautoError, + RefreshedTokenState, TokenBalanceAmount, + TokenType, }, }, - utils::{ math_utils::{self, i80f48_to_f64}, solana_utils::*, solauto_utils::*, validation_utils::* }, + utils::{ math_utils::*, solana_utils::*, solauto_utils::*, validation_utils::* }, }; +const CONF_INTERVAL_MULTIPLE: f64 = 2.12; +const STD_DEV_MULTIPLE: f64 = 1.96; + pub struct MarginfiBankAccounts<'a> { pub bank: DeserializedAccount<'a, Bank>, pub price_oracle: Option<&'a AccountInfo<'a>>, @@ -50,7 +55,7 @@ pub struct MarginfiBankAccounts<'a> { pub struct MarginfiClient<'a> { signer: &'a AccountInfo<'a>, program: &'a AccountInfo<'a>, - marginfi_account: DeserializedAccount<'a, MarginfiAccount>, + marginfi_account: &'a AccountInfo<'a>, marginfi_group: &'a AccountInfo<'a>, supply: MarginfiBankAccounts<'a>, debt: MarginfiBankAccounts<'a>, @@ -59,8 +64,7 @@ pub struct MarginfiClient<'a> { impl<'a> MarginfiClient<'a> { pub fn initialize<'c>( ctx: &'c Context<'a, MarginfiOpenPositionAccounts<'a>>, - solauto_position: &'c DeserializedAccount<'a, SolautoPosition>, - marignfi_acc_seed_idx: Option + solauto_position: &'c DeserializedAccount<'a, SolautoPosition> ) -> ProgramResult { if account_has_data(ctx.accounts.marginfi_account) { return Ok(()); @@ -78,10 +82,9 @@ impl<'a> MarginfiClient<'a> { } ); if marginfi_account_owner.key == solauto_position.account_info.key { - let seed_idx_bytes = marignfi_acc_seed_idx.unwrap().to_le_bytes(); let mut marginfi_account_seeds = vec![ solauto_position.account_info.key.as_ref(), - seed_idx_bytes.as_ref() + ctx.accounts.marginfi_group.key.as_ref() ]; let (_, bump) = Pubkey::find_program_address( marginfi_account_seeds.as_slice(), @@ -132,9 +135,7 @@ impl<'a> MarginfiClient<'a> { Ok(Self { signer, program, - marginfi_account: DeserializedAccount:: - ::zerocopy(Some(marginfi_account))? - .unwrap(), + marginfi_account, marginfi_group, supply, debt, @@ -167,21 +168,17 @@ impl<'a> MarginfiClient<'a> { let supply_bank = DeserializedAccount::::zerocopy(Some(supply_bank))?.unwrap(); let debt_bank = DeserializedAccount::::zerocopy(Some(debt_bank))?.unwrap(); - let max_ltv = math_utils - ::i80f48_to_f64(I80F48::from_le_bytes(supply_bank.data.config.asset_weight_init.value)) - .div( - math_utils::i80f48_to_f64( - I80F48::from_le_bytes(debt_bank.data.config.liability_weight_init.value) - ) - ); + let max_ltv = i80f48_to_f64( + I80F48::from_le_bytes(supply_bank.data.config.asset_weight_init.value) + ).div( + i80f48_to_f64(I80F48::from_le_bytes(debt_bank.data.config.liability_weight_init.value)) + ); - let liq_threshold = math_utils - ::i80f48_to_f64(I80F48::from_le_bytes(supply_bank.data.config.asset_weight_maint.value)) - .div( - math_utils::i80f48_to_f64( - I80F48::from_le_bytes(debt_bank.data.config.liability_weight_maint.value) - ) - ); + let liq_threshold = i80f48_to_f64( + I80F48::from_le_bytes(supply_bank.data.config.asset_weight_maint.value) + ).div( + i80f48_to_f64(I80F48::from_le_bytes(debt_bank.data.config.liability_weight_maint.value)) + ); Ok((max_ltv, liq_threshold)) } @@ -190,17 +187,23 @@ impl<'a> MarginfiClient<'a> { account_balances: &[Balance], supply_bank: &'a AccountInfo<'a>, price_oracle: &'a AccountInfo<'a>, + price_type: PriceType, mut max_ltv: f64 - ) -> Result<(RefreshedTokenData, f64), ProgramError> { + ) -> Result<(RefreshedTokenState, f64), ProgramError> { let bank = DeserializedAccount::::zerocopy(Some(supply_bank))?.unwrap(); let asset_share_value = I80F48::from_le_bytes(bank.data.asset_share_value.value); - let market_price = MarginfiClient::load_price(&bank, price_oracle)?; + let market_price = MarginfiClient::load_price( + &bank, + price_oracle, + price_type, + TokenType::Supply + )?; let supply_shares = MarginfiClient::get_account_balance(account_balances, &bank, true); let base_unit_account_deposits = if supply_shares.is_some() { - math_utils::i80f48_to_u64(supply_shares.unwrap().mul(asset_share_value)) + i80f48_to_u64(supply_shares.unwrap().mul(asset_share_value)) } else { 0 }; @@ -212,12 +215,10 @@ impl<'a> MarginfiClient<'a> { total_deposited ); - let bank_deposits_usd_value = math_utils - ::from_base_unit::( - math_utils::i80f48_to_f64(total_deposited), - bank.data.mint_decimals - ) - .mul(market_price); + let bank_deposits_usd_value = from_base_unit::( + i80f48_to_f64(total_deposited), + bank.data.mint_decimals + ).mul(market_price); if bank.data.config.total_asset_value_init_limit != 0 && bank_deposits_usd_value > (bank.data.config.total_asset_value_init_limit as f64) @@ -229,11 +230,11 @@ impl<'a> MarginfiClient<'a> { } Ok(( - RefreshedTokenData { + RefreshedTokenState { mint: bank.data.mint, decimals: bank.data.mint_decimals, amount_used: base_unit_account_deposits, - amount_can_be_used: math_utils::i80f48_to_u64(base_unit_deposit_room_available), + amount_can_be_used: i80f48_to_u64(base_unit_deposit_room_available), market_price, borrow_fee_bps: None, }, @@ -244,17 +245,23 @@ impl<'a> MarginfiClient<'a> { pub fn get_debt_token_usage( account_balances: &[Balance], debt_bank: &'a AccountInfo<'a>, - price_oracle: &'a AccountInfo<'a> - ) -> Result { + price_oracle: &'a AccountInfo<'a>, + price_type: PriceType + ) -> Result { let bank = DeserializedAccount::::zerocopy(Some(debt_bank))?.unwrap(); let liability_share_value = I80F48::from_le_bytes(bank.data.liability_share_value.value); - let market_price = MarginfiClient::load_price(&bank, price_oracle)?; + let market_price = MarginfiClient::load_price( + &bank, + price_oracle, + price_type, + TokenType::Debt + )?; let debt_shares = MarginfiClient::get_account_balance(account_balances, &bank, false); let base_unit_account_debt = if debt_shares.is_some() { - math_utils::i80f48_to_u64(debt_shares.unwrap().mul(liability_share_value)) + i80f48_to_u64(debt_shares.unwrap().mul(liability_share_value)) } else { 0 }; @@ -268,26 +275,35 @@ impl<'a> MarginfiClient<'a> { let base_unit_supply_available = total_deposited.sub(total_borrows); let amount_can_be_used = min( - bank.data.config.borrow_limit.saturating_sub(math_utils::i80f48_to_u64(total_borrows)), - math_utils::i80f48_to_u64(base_unit_supply_available) + bank.data.config.borrow_limit.saturating_sub(i80f48_to_u64(total_borrows)), + i80f48_to_u64(base_unit_supply_available) ); - Ok(RefreshedTokenData { + let borrow_fee_bps = i80f48_to_f64( + I80F48::from_le_bytes( + bank.data.config.interest_rate_config.protocol_origination_fee.value + ) + ) + .mul(10_000.0) + .round() as u16; + + Ok(RefreshedTokenState { mint: bank.data.mint, decimals: bank.data.mint_decimals, amount_used: base_unit_account_debt, amount_can_be_used, market_price, - borrow_fee_bps: None, + borrow_fee_bps: Some(borrow_fee_bps), }) } - pub fn get_updated_state( + pub fn get_updated_state<'b>( marginfi_account: &DeserializedAccount, supply_bank: &'a AccountInfo<'a>, supply_price_oracle: &'a AccountInfo<'a>, debt_bank: &'a AccountInfo<'a>, - debt_price_oracle: &'a AccountInfo<'a> + debt_price_oracle: &'a AccountInfo<'a>, + price_type: PriceType ) -> Result { let (max_ltv, liq_threshold) = MarginfiClient::get_max_ltv_and_liq_threshold( supply_bank, @@ -298,13 +314,15 @@ impl<'a> MarginfiClient<'a> { let debt = MarginfiClient::get_debt_token_usage( account_balances, debt_bank, - debt_price_oracle + debt_price_oracle, + price_type )?; let (supply, max_ltv) = MarginfiClient::get_supply_token_usage( account_balances, supply_bank, supply_price_oracle, + price_type, max_ltv )?; @@ -318,14 +336,10 @@ impl<'a> MarginfiClient<'a> { pub fn load_price( bank: &DeserializedAccount, - price_oracle: &AccountInfo + price_oracle: &AccountInfo, + price_type: PriceType, + token_type: TokenType ) -> Result { - // TODO: Don't validate this until Marginfi's sorted out their price oracle issues and this is congruent with what they use. - // if price_oracle.key != &bank.data.config.oracle_keys[0] { - // msg!("Incorrect price oracle provided"); - // return Err(SolautoError::IncorrectAccounts.into()); - // } - let clock = Clock::get()?; let max_price_age = 120; // Default used by Marginfi is 60 @@ -339,22 +353,13 @@ impl<'a> MarginfiClient<'a> { } OracleSetup::PythLegacy => { let price_feed = SolanaPriceAccount::account_info_to_feed(price_oracle)?; - let price_result = price_feed - .get_ema_price_no_older_than(clock.unix_timestamp, max_price_age) - .unwrap(); - - let price = if price_result.expo == 0 { - price_result.price as f64 - } else if price_result.expo < 0 { - math_utils::from_base_unit::( - price_result.price, - price_result.expo.unsigned_abs() - ) + + let price = if price_type == PriceType::Ema { + let price_result = price_feed.get_price_unchecked(); + derive_value(price_result.price, price_result.expo) } else { - math_utils::to_base_unit::( - price_result.price, - price_result.expo.unsigned_abs() - ) + let price_result = price_feed.get_price_unchecked(); + derive_value(price_result.price, price_result.expo) }; Ok(price) @@ -363,29 +368,27 @@ impl<'a> MarginfiClient<'a> { let price_feed_data = price_oracle.try_borrow_data()?; let price_feed = PriceUpdateV2::deserialize(&mut &price_feed_data.as_ref()[8..])?; - let feed_id = &bank.data.config.oracle_keys[0].to_bytes(); - let price_result = price_feed - .get_price_no_older_than(&clock, max_price_age, feed_id) - .map_err(|e| { - msg!("Pyth push oracle error: {:?}", e); - ProgramError::Custom(0) - })?; - - let price = if price_result.exponent == 0 { - price_result.price as f64 - } else if price_result.exponent < 0 { - math_utils::from_base_unit::( - price_result.price, - price_result.exponent.unsigned_abs() + let (price, conf, exponent) = if price_type == PriceType::Ema { + ( + price_feed.price_message.ema_price, + price_feed.price_message.ema_conf, + price_feed.price_message.exponent, ) } else { - math_utils::to_base_unit::( - price_result.price, - price_result.exponent.unsigned_abs() + ( + price_feed.price_message.price, + price_feed.price_message.conf, + price_feed.price_message.exponent, ) }; - Ok(price) + Ok( + with_conf_interval( + derive_value(price, exponent), + derive_value(conf as i64, exponent).mul(CONF_INTERVAL_MULTIPLE), + token_type + ) + ) } OracleSetup::SwitchboardLegacy => { let data = price_oracle.data.borrow(); @@ -396,10 +399,7 @@ impl<'a> MarginfiClient<'a> { let price = if sw_decimal.scale == 0 { sw_decimal.mantissa as f64 } else { - math_utils::from_base_unit::( - sw_decimal.mantissa, - sw_decimal.scale - ) + from_base_unit::(sw_decimal.mantissa, sw_decimal.scale) }; Ok(price) @@ -410,12 +410,15 @@ impl<'a> MarginfiClient<'a> { |_| SolautoError::IncorrectAccounts )?; - let price = I80F48::from_num(feed.result.value) + let price = (feed.result.value as f64) + // 10^18 + .div(1000000000000000000.0); + let conf_interval = (feed.result.std_dev as f64) // 10^18 - .checked_div(I80F48!(1000000000000000000)) - .unwrap(); + .div(1000000000000000000.0) + .mul(STD_DEV_MULTIPLE); - Ok(i80f48_to_f64(price)) + Ok(with_conf_interval(price, conf_interval, token_type)) } } } @@ -434,6 +437,22 @@ impl<'a> MarginfiClient<'a> { ); cpi.invoke() } + + pub fn compose_remaining_accounts(accounts: Vec) -> Vec { + let mut banks_and_oracles: Vec<(AccountMetaFlags, AccountMetaFlags)> = accounts + .chunks(2) + .filter_map(|chunk| { + if chunk.len() == 2 { Some((chunk[0], chunk[1])) } else { None } + }) + .collect(); + + banks_and_oracles.sort_by(|a, b| b.0.0.key.to_string().cmp(&a.0.0.key.to_string())); + + banks_and_oracles + .into_iter() + .flat_map(|(a, b)| vec![a, b]) + .collect() + } } impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { @@ -468,7 +487,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { self.program, LendingAccountDepositCpiAccounts { marginfi_group: self.marginfi_group, - marginfi_account: self.marginfi_account.account_info, + marginfi_account: self.marginfi_account, signer: authority, bank: self.supply.bank.account_info, signer_token_account, @@ -477,6 +496,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { }, LendingAccountDepositInstructionArgs { amount: base_unit_amount, + deposit_up_to_limit: Some(true), } ); @@ -501,7 +521,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { self.program, LendingAccountWithdrawCpiAccounts { marginfi_group: self.marginfi_group, - marginfi_account: self.marginfi_account.account_info, + marginfi_account: self.marginfi_account, signer: authority, bank: self.supply.bank.account_info, destination_token_account: destination, @@ -515,7 +535,11 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { } ); - let active_balances = self.marginfi_account.data.lending_account.balances + let marginfi_account_data = DeserializedAccount:: + ::zerocopy(Some(self.marginfi_account))? + .unwrap().data; + + let active_balances = marginfi_account_data.lending_account.balances .iter() .filter(|balance| balance.active == 1) .collect::>(); @@ -525,7 +549,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { let mut withdrawing_all = amount == TokenBalanceAmount::All; if !withdrawing_all && active_balances.len() == 1 { let asset_shares = I80F48::from_le_bytes(active_balances[0].asset_shares.value); - let supply_balance = math_utils::i80f48_to_u64( + let supply_balance = i80f48_to_u64( asset_shares.mul( I80F48::from_le_bytes(self.supply.bank.data.asset_share_value.value) ) @@ -549,10 +573,12 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { if !std_accounts.solauto_position.data.self_managed.val { cpi.invoke_signed_with_remaining_accounts( &[std_accounts.solauto_position.data.seeds_with_bump().as_slice()], - remaining_accounts.as_slice() + MarginfiClient::compose_remaining_accounts(remaining_accounts).as_slice() ) } else { - cpi.invoke_with_remaining_accounts(remaining_accounts.as_slice()) + cpi.invoke_with_remaining_accounts( + MarginfiClient::compose_remaining_accounts(remaining_accounts).as_slice() + ) } } @@ -568,7 +594,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { self.program, LendingAccountBorrowCpiAccounts { marginfi_group: self.marginfi_group, - marginfi_account: self.marginfi_account.account_info, + marginfi_account: self.marginfi_account, signer: authority, bank: self.debt.bank.account_info, destination_token_account: destination, @@ -581,7 +607,9 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { } ); - let mut remaining_accounts = Vec::with_capacity(4); + let mut remaining_accounts: Vec = Vec::::with_capacity( + 4 + ); remaining_accounts.push((self.supply.bank.account_info, false, false)); remaining_accounts.push((self.supply.price_oracle.unwrap(), false, false)); remaining_accounts.push((self.debt.bank.account_info, false, true)); @@ -590,10 +618,12 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { if !std_accounts.solauto_position.data.self_managed.val { cpi.invoke_signed_with_remaining_accounts( &[std_accounts.solauto_position.data.seeds_with_bump().as_slice()], - remaining_accounts.as_slice() + MarginfiClient::compose_remaining_accounts(remaining_accounts).as_slice() ) } else { - cpi.invoke_with_remaining_accounts(remaining_accounts.as_slice()) + cpi.invoke_with_remaining_accounts( + MarginfiClient::compose_remaining_accounts(remaining_accounts).as_slice() + ) } } @@ -616,7 +646,7 @@ impl<'a> LendingProtocolClient<'a> for MarginfiClient<'a> { self.program, LendingAccountRepayCpiAccounts { marginfi_group: self.marginfi_group, - marginfi_account: self.marginfi_account.account_info, + marginfi_account: self.marginfi_account, signer: authority, bank: self.debt.bank.account_info, signer_token_account: signer_token_account, diff --git a/programs/solauto/src/constants.rs b/programs/solauto/src/constants.rs index 3e938222..d8681b25 100644 --- a/programs/solauto/src/constants.rs +++ b/programs/solauto/src/constants.rs @@ -1,22 +1,11 @@ -use solana_program::pubkey::Pubkey; +use solana_program::{pubkey, pubkey::Pubkey}; -// AprYCPiVeKMCgjQ2ZufwChMzvQ5kFjJo2ekTLSkXsQDm -pub const SOLAUTO_FEES_WALLET: Pubkey = Pubkey::new_from_array([ - 145, 251, 126, 53, 245, 169, 146, 209, 147, 243, 95, 78, 165, 119, 126, 212, 48, 177, 204, 152, - 35, 228, 216, 122, 54, 147, 76, 46, 180, 66, 110, 112, -]); +pub const SOLAUTO_FEES_WALLET: Pubkey = pubkey!("AprYCPiVeKMCgjQ2ZufwChMzvQ5kFjJo2ekTLSkXsQDm"); +pub const SOLAUTO_MANAGER: Pubkey = pubkey!("MNGRcX4nc7quPdzBbNKJ4ScK5EE73JnwJVGxuJXhHCY"); +pub const WSOL_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112"); -// MNGRcX4nc7quPdzBbNKJ4ScK5EE73JnwJVGxuJXhHCY -pub const SOLAUTO_MANAGER: Pubkey = Pubkey::new_from_array([ - 5, 55, 169, 98, 27, 47, 114, 199, 85, 110, 166, 51, 214, 240, 77, 15, 168, 150, 137, 164, 201, - 23, 114, 118, 157, 17, 1, 163, 52, 164, 52, 29, -]); - -// So11111111111111111111111111111111111111112 -pub const WSOL_MINT: Pubkey = Pubkey::new_from_array([ - 6, 155, 136, 87, 254, 171, 129, 132, 251, 104, 127, 99, 70, 24, 192, 53, 218, 196, 57, 220, 26, - 235, 59, 85, 152, 160, 240, 0, 0, 0, 0, 1, -]); +pub const MARGINFI_PROD_PROGRAM: Pubkey = pubkey!("MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA"); +pub const MARGINFI_STAGING_PROGRAM: Pubkey = pubkey!("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"); pub const USD_DECIMALS: u8 = 9; @@ -25,3 +14,7 @@ pub const MIN_REPAY_GAP_BPS: u16 = 50; pub const MIN_BOOST_GAP_BPS: u16 = 50; pub const MAX_BASIS_POINTS: u16 = 10000; + +pub const REFERRER_PERCENTAGE: f64 = 0.15; + +pub const OFFSET_FROM_MAX_LTV: f64 = 0.005; \ No newline at end of file diff --git a/programs/solauto/src/entrypoint.rs b/programs/solauto/src/entrypoint.rs index ee309313..480a5783 100644 --- a/programs/solauto/src/entrypoint.rs +++ b/programs/solauto/src/entrypoint.rs @@ -31,7 +31,9 @@ pub fn process_instruction<'a>( Instruction::ClosePosition => process_close_position_instruction(accounts), Instruction::CancelDCA => process_cancel_dca(accounts), - Instruction::MarginfiRefreshData => process_marginfi_refresh_data(accounts), + Instruction::MarginfiRefreshData(price_type) => { + process_marginfi_refresh_data(accounts, price_type) + } Instruction::MarginfiProtocolInteraction(action) => { process_marginfi_interaction_instruction(accounts, action) } diff --git a/programs/solauto/src/instructions/close_position.rs b/programs/solauto/src/instructions/close_position.rs index 908a349e..106db22b 100644 --- a/programs/solauto/src/instructions/close_position.rs +++ b/programs/solauto/src/instructions/close_position.rs @@ -5,7 +5,7 @@ use crate::{ state::solauto_position::SolautoPosition, types::{ instruction::accounts::{ClosePositionAccounts, Context}, - shared::DeserializedAccount, + shared::{DeserializedAccount, SplTokenTransferArgs}, }, utils::{solana_utils, solauto_utils}, }; @@ -24,11 +24,13 @@ pub fn close_position_ta<'a>( if position_ta_data.amount > 0 && position_ta_data.mint != WSOL_MINT { solana_utils::spl_token_transfer( ctx.accounts.token_program, - position_ta, - solauto_position.account_info, - signer_ta, - position_ta_data.amount, - Some(solauto_position_seeds), + SplTokenTransferArgs { + source: position_ta, + authority: solauto_position.account_info, + recipient: signer_ta, + amount: position_ta_data.amount, + authority_seeds: Some(solauto_position_seeds), + }, )?; } diff --git a/programs/solauto/src/instructions/open_position.rs b/programs/solauto/src/instructions/open_position.rs index e19850a1..610088d4 100644 --- a/programs/solauto/src/instructions/open_position.rs +++ b/programs/solauto/src/instructions/open_position.rs @@ -15,7 +15,6 @@ use self::solana_utils::account_has_data; pub fn marginfi_open_position<'a>( ctx: Context<'a, MarginfiOpenPositionAccounts<'a>>, mut solauto_position: DeserializedAccount<'a, SolautoPosition>, - marginfi_account_seed_idx: Option, ) -> ProgramResult { initialize_solauto_position( &mut solauto_position, @@ -30,7 +29,7 @@ pub fn marginfi_open_position<'a>( ctx.accounts.signer_debt_ta, )?; - MarginfiClient::initialize(&ctx, &solauto_position, marginfi_account_seed_idx) + MarginfiClient::initialize(&ctx, &solauto_position) } fn initialize_solauto_position<'a, 'b>( @@ -74,13 +73,5 @@ fn initialize_solauto_position<'a, 'b>( debt_mint, )?; - solauto_utils::initiate_dca_in_if_necessary( - token_program, - solauto_position, - Some(position_debt_ta), - signer, - signer_debt_ta, - )?; - ix_utils::update_data(solauto_position) } diff --git a/programs/solauto/src/instructions/rebalance.rs b/programs/solauto/src/instructions/rebalance.rs index a66ec6c8..ee0534ae 100644 --- a/programs/solauto/src/instructions/rebalance.rs +++ b/programs/solauto/src/instructions/rebalance.rs @@ -2,23 +2,26 @@ use std::ops::Sub; use marginfi_sdk::generated::accounts::Bank; use solana_program::{ - clock::Clock, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, + clock::Clock, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, }; use crate::{ + check, clients::marginfi::MarginfiClient, - state::solauto_position::{SolautoPosition, SolautoRebalanceType}, + rebalance::solauto_fees::SolautoFeesBps, + state::solauto_position::SolautoPosition, types::{ + errors::SolautoError, instruction::{ accounts::{Context, MarginfiRebalanceAccounts}, RebalanceSettings, SolautoStandardAccounts, }, lending_protocol::{LendingProtocolClient, LendingProtocolTokenAccounts}, - shared::{DeserializedAccount, RebalanceStep}, + shared::{DeserializedAccount, RebalanceStep, SolautoRebalanceType, TokenType}, solauto_manager::{SolautoManager, SolautoManagerAccounts}, }, - utils::{ix_utils, solauto_utils}, + utils::ix_utils, }; use super::refresh; @@ -59,11 +62,16 @@ pub fn marginfi_rebalance<'a>( let solauto_manager_accounts = SolautoManagerAccounts::from(supply_tas, debt_tas, ctx.accounts.intermediary_ta, None)?; - if rebalance_step == RebalanceStep::Initial - || std_accounts.solauto_position.data.rebalance.rebalance_type - == SolautoRebalanceType::SingleRebalanceWithFL + let rebalance_type = std_accounts + .solauto_position + .data + .rebalance + .ixs + .rebalance_type; + if rebalance_step == RebalanceStep::PreSwap + || rebalance_type == SolautoRebalanceType::FLSwapThenRebalance { - if needs_refresh(&std_accounts.solauto_position, &args)? { + if needs_refresh(&std_accounts.solauto_position)? { refresh::marginfi_refresh_accounts( ctx.accounts.marginfi_program, ctx.accounts.marginfi_group, @@ -73,28 +81,22 @@ pub fn marginfi_rebalance<'a>( ctx.accounts.debt_bank, ctx.accounts.debt_price_oracle.unwrap(), &mut std_accounts.solauto_position, + args.price_type.unwrap().clone(), )?; } else { - std_accounts - .solauto_position - .data - .state - .supply - .update_market_price(MarginfiClient::load_price( - &DeserializedAccount::::zerocopy(Some(ctx.accounts.supply_bank))? - .unwrap(), - ctx.accounts.supply_price_oracle.unwrap(), - )?); - std_accounts - .solauto_position - .data - .state - .debt - .update_market_price(MarginfiClient::load_price( - &DeserializedAccount::::zerocopy(Some(ctx.accounts.debt_bank))?.unwrap(), - ctx.accounts.debt_price_oracle.unwrap(), - )?); - std_accounts.solauto_position.data.refresh_state(); + let supply_price = MarginfiClient::load_price( + &DeserializedAccount::::zerocopy(Some(ctx.accounts.supply_bank))?.unwrap(), + ctx.accounts.supply_price_oracle.unwrap(), + args.price_type.unwrap().clone(), + TokenType::Supply, + )?; + let debt_price = MarginfiClient::load_price( + &DeserializedAccount::::zerocopy(Some(ctx.accounts.debt_bank))?.unwrap(), + ctx.accounts.debt_price_oracle.unwrap(), + args.price_type.unwrap().clone(), + TokenType::Debt, + )?; + update_token_prices(&mut std_accounts, supply_price, debt_price); } } @@ -107,9 +109,28 @@ pub fn marginfi_rebalance<'a>( ) } +fn update_token_prices<'a>( + std_accounts: &mut Box>, + supply_price: f64, + debt_price: f64, +) { + std_accounts + .solauto_position + .data + .state + .supply + .update_market_price(supply_price); + std_accounts + .solauto_position + .data + .state + .debt + .update_market_price(debt_price); + std_accounts.solauto_position.data.refresh_state(); +} + fn needs_refresh( solauto_position: &DeserializedAccount, - args: &RebalanceSettings, ) -> Result { if solauto_position.data.self_managed.val { return Ok(true); @@ -117,32 +138,11 @@ fn needs_refresh( let current_timestamp = Clock::get()?.unix_timestamp as u64; - if solauto_position - .data - .position - .setting_params - .automation - .is_active() - { - return Ok(solauto_position - .data - .position - .setting_params - .automation - .eligible_for_next_period(current_timestamp)); - } - - // In case we did a refresh in this same transaction before this ix - if current_timestamp.sub(solauto_position.data.state.last_updated) <= 2 { + // In case we did a refresh recently + if current_timestamp.sub(solauto_position.data.state.last_refreshed) <= 2 { return Ok(false); } - if args.target_liq_utilization_rate_bps.is_some() - && args.target_liq_utilization_rate_bps.unwrap() == 0 - { - return Ok(true); - } - Ok(false) } @@ -153,16 +153,22 @@ fn rebalance<'a>( rebalance_step: RebalanceStep, args: RebalanceSettings, ) -> ProgramResult { - if args.target_liq_utilization_rate_bps.is_some() - && std_accounts.signer.key != &std_accounts.solauto_position.data.authority - { - msg!( - "Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority" - ); - return Err(ProgramError::InvalidInstructionData.into()); - } + check!( + args.target_liq_utilization_rate_bps.is_none() + || std_accounts.signer.key == &std_accounts.solauto_position.data.authority, + SolautoError::NonAuthorityProvidedTargetLTV + ); + check!( + std_accounts.authority_referral_state.is_some(), + SolautoError::IncorrectAccounts + ); + #[cfg(not(feature = "test"))] + check!( + args.flash_loan_fee_bps.unwrap_or(0) <= 150, + SolautoError::IncorrectInstructions + ); - let fees_bps = solauto_utils::SolautoFeesBps::from( + let fees_bps = SolautoFeesBps::from( std_accounts .authority_referral_state .as_ref() @@ -185,11 +191,7 @@ fn rebalance<'a>( std_accounts, Some(fees_bps), )?; - if rebalance_step == RebalanceStep::Initial { - solauto_manager.begin_rebalance(&args)?; - } else { - solauto_manager.finish_rebalance(&args)?; - } + solauto_manager.rebalance(args, rebalance_step)?; ix_utils::update_data(&mut solauto_manager.std_accounts.solauto_position) } diff --git a/programs/solauto/src/instructions/referral_fees.rs b/programs/solauto/src/instructions/referral_fees.rs index c4c0d379..3f3beb44 100644 --- a/programs/solauto/src/instructions/referral_fees.rs +++ b/programs/solauto/src/instructions/referral_fees.rs @@ -6,7 +6,7 @@ use crate::{ state::referral_state::ReferralState, types::{ instruction::accounts::{ClaimReferralFeesAccounts, Context, ConvertReferralFeesAccounts}, - shared::DeserializedAccount, + shared::{DeserializedAccount, SplTokenTransferArgs}, }, utils::solana_utils, }; @@ -19,11 +19,13 @@ pub fn convert_referral_fees( solana_utils::spl_token_transfer( ctx.accounts.token_program, - ctx.accounts.referral_fees_ta, - ctx.accounts.referral_state, - ctx.accounts.intermediary_ta, - balance, - Some(&referral_state.data.seeds_with_bump()), + SplTokenTransferArgs { + source: ctx.accounts.referral_fees_ta, + authority: ctx.accounts.referral_state, + recipient: ctx.accounts.intermediary_ta, + amount: balance, + authority_seeds: Some(&referral_state.data.seeds_with_bump()), + }, )?; Ok(()) @@ -50,11 +52,13 @@ pub fn claim_referral_fees( let account_rent = rent.minimum_balance(TokenAccount::LEN); solana_utils::spl_token_transfer( ctx.accounts.token_program, - ctx.accounts.referral_fees_dest_ta, - ctx.accounts.referral_state, - ctx.accounts.signer_wsol_ta.unwrap(), - account_rent, - Some(referral_state_seeds), + SplTokenTransferArgs { + source: ctx.accounts.referral_fees_dest_ta, + authority: ctx.accounts.referral_state, + recipient: ctx.accounts.signer_wsol_ta.unwrap(), + amount: account_rent, + authority_seeds: Some(referral_state_seeds), + }, )?; solana_utils::close_token_account( @@ -78,7 +82,7 @@ pub fn claim_referral_fees( solana_utils::close_token_account( ctx.accounts.token_program, ctx.accounts.referral_fees_dest_ta, - ctx.accounts.referral_authority.unwrap(), + ctx.accounts.referral_authority, ctx.accounts.referral_state, Some(referral_state_seeds), )?; @@ -96,7 +100,7 @@ pub fn claim_referral_fees( ctx.accounts.token_program, ctx.accounts.system_program, ctx.accounts.signer, - ctx.accounts.referral_authority.unwrap(), + ctx.accounts.referral_authority, ctx.accounts.fees_destination_ta.unwrap(), ctx.accounts.referral_fees_dest_mint, )?; @@ -106,11 +110,13 @@ pub fn claim_referral_fees( solana_utils::spl_token_transfer( ctx.accounts.token_program, - ctx.accounts.referral_fees_dest_ta, - ctx.accounts.referral_state, - ctx.accounts.fees_destination_ta.unwrap(), - balance, - Some(referral_state_seeds), + SplTokenTransferArgs { + source: ctx.accounts.referral_fees_dest_ta, + authority: ctx.accounts.referral_state, + recipient: ctx.accounts.fees_destination_ta.unwrap(), + amount: balance, + authority_seeds: Some(referral_state_seeds), + }, )?; } diff --git a/programs/solauto/src/instructions/refresh.rs b/programs/solauto/src/instructions/refresh.rs index a53be341..f7f1ed5d 100644 --- a/programs/solauto/src/instructions/refresh.rs +++ b/programs/solauto/src/instructions/refresh.rs @@ -6,7 +6,10 @@ use solana_program::{ use crate::{ clients::marginfi::MarginfiClient, state::solauto_position::SolautoPosition, - types::{shared::DeserializedAccount, solauto_manager::SolautoManager}, + types::{ + shared::{DeserializedAccount, PriceType}, + solauto_manager::SolautoManager, + }, utils::ix_utils, }; @@ -19,6 +22,7 @@ pub fn marginfi_refresh_accounts<'a, 'b>( debt_bank: &'a AccountInfo<'a>, debt_price_oracle: &'a AccountInfo<'a>, solauto_position: &'b mut DeserializedAccount, + price_type: PriceType, ) -> ProgramResult { MarginfiClient::refresh_bank(marginfi_program, marginfi_group, supply_bank)?; MarginfiClient::refresh_bank(marginfi_program, marginfi_group, debt_bank)?; @@ -32,6 +36,7 @@ pub fn marginfi_refresh_accounts<'a, 'b>( supply_price_oracle, debt_bank, debt_price_oracle, + price_type, )?; SolautoManager::refresh_position(&mut solauto_position.data, updated_state, Clock::get()?)?; diff --git a/programs/solauto/src/instructions/update_position.rs b/programs/solauto/src/instructions/update_position.rs index 5c616767..b625c163 100644 --- a/programs/solauto/src/instructions/update_position.rs +++ b/programs/solauto/src/instructions/update_position.rs @@ -1,15 +1,15 @@ -use solana_program::{clock::Clock, entrypoint::ProgramResult, msg, sysvar::Sysvar}; +use solana_program::{clock::Clock, entrypoint::ProgramResult, sysvar::Sysvar}; use crate::{ - state::solauto_position::{DCASettings, SolautoPosition, SolautoSettingsParameters}, + state::solauto_position::{SolautoPosition, SolautoSettingsParameters}, types::{ instruction::{ accounts::{Context, UpdatePositionAccounts}, UpdatePositionData, }, - shared::{DeserializedAccount, SolautoError, TokenType}, + shared::DeserializedAccount, }, - utils::{ix_utils, solana_utils, solauto_utils, validation_utils}, + utils::{ix_utils, validation_utils}, }; pub fn update_position<'a>( @@ -17,64 +17,12 @@ pub fn update_position<'a>( mut solauto_position: DeserializedAccount<'a, SolautoPosition>, new_data: UpdatePositionData, ) -> ProgramResult { - if new_data.dca.is_some() { - update_dca(&ctx, &mut solauto_position, &new_data)?; + if new_data.settings.is_some() { + solauto_position.data.position.settings = + SolautoSettingsParameters::from(new_data.settings.unwrap()); } - if new_data.setting_params.is_some() { - solauto_position.data.position.setting_params = - SolautoSettingsParameters::from(new_data.setting_params.unwrap()); - } - - let current_timestamp = Clock::get()?.unix_timestamp as u64; - validation_utils::validate_position_settings(&solauto_position.data, current_timestamp)?; - validation_utils::validate_dca_settings(&solauto_position.data.position, current_timestamp)?; + validation_utils::validate_position_settings(&solauto_position.data)?; ix_utils::update_data(&mut solauto_position) } - -fn update_dca<'a, 'b>( - ctx: &'b Context>, - solauto_position: &'b mut DeserializedAccount<'a, SolautoPosition>, - new_data: &'b UpdatePositionData, -) -> ProgramResult { - let new_dca = new_data.dca.as_ref().unwrap(); - - if solauto_position.data.position.dca.is_active() && solauto_position.data.position.dca.dca_in() - { - msg!("Existing DCA must be canceled first through the cancel DCA instruction"); - return Err(SolautoError::IncorrectInstructions.into()); - } - - solauto_position.data.position.dca = DCASettings::from(new_data.dca.as_ref().unwrap().clone()); - - if new_dca.dca_in_base_unit > 0 { - if (new_dca.token_type == TokenType::Debt - && solauto_position.data.state.debt.mint != *ctx.accounts.dca_mint.unwrap().key) - || (new_dca.token_type == TokenType::Supply - && solauto_position.data.state.supply.mint != *ctx.accounts.dca_mint.unwrap().key) - { - msg!("Incorrect dca mint account provided for the given DCA"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - solana_utils::init_ata_if_needed( - ctx.accounts.token_program, - ctx.accounts.system_program, - ctx.accounts.signer, - solauto_position.account_info, - ctx.accounts.position_dca_ta.unwrap(), - ctx.accounts.dca_mint.unwrap(), - )?; - - solauto_utils::initiate_dca_in_if_necessary( - ctx.accounts.token_program, - solauto_position, - ctx.accounts.position_dca_ta, - ctx.accounts.signer, - ctx.accounts.signer_dca_ta, - )?; - } - - Ok(()) -} diff --git a/programs/solauto/src/lib.rs b/programs/solauto/src/lib.rs index ad684f9b..5bde0348 100644 --- a/programs/solauto/src/lib.rs +++ b/programs/solauto/src/lib.rs @@ -2,7 +2,9 @@ pub mod clients; pub mod constants; pub mod entrypoint; pub mod instructions; +pub mod macros; pub mod processors; +pub mod rebalance; pub mod state; pub mod types; pub mod utils; diff --git a/programs/solauto/src/macros.rs b/programs/solauto/src/macros.rs new file mode 100644 index 00000000..0f6190c2 --- /dev/null +++ b/programs/solauto/src/macros.rs @@ -0,0 +1,52 @@ +#[macro_export] +macro_rules! check { + ($cond:expr, $err:expr) => { + if !($cond) { + let error_code = $err; + solana_program::msg!( + "Error \"{}\" thrown at {}:{}", + error_code, + file!(), + line!() + ); + return Err(error_code.into()); + } + }; + + ( + $cond:expr, + $err:expr, + $($arg:tt)* + ) => { + if !($cond) { + let error_code = $err; + solana_program::msg!( + "Error \"{}\" thrown at {}:{}", + error_code, + file!(), + line!() + ); + solana_program::msg!($($arg)*); + return Err(error_code.into()); + } + }; +} + +#[macro_export] +macro_rules! error_if { + ($cond:expr, $err:expr) => { + if ($cond) { + let error_code = $err; + solana_program::msg!("Error \"{}\" thrown at {}:{}", error_code, file!(), line!()); + return Err(error_code.into()); + } + }; +} + +#[macro_export] +macro_rules! derive_pod_traits { + ($type:ty) => { + unsafe impl Zeroable for $type {} + unsafe impl Pod for $type {} + }; +} diff --git a/programs/solauto/src/processors/marginfi.rs b/programs/solauto/src/processors/marginfi.rs index a8406cbd..69e4a554 100644 --- a/programs/solauto/src/processors/marginfi.rs +++ b/programs/solauto/src/processors/marginfi.rs @@ -1,14 +1,13 @@ -use rebalance_utils::get_rebalance_step; -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, sysvar::Sysvar, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ + check, clients::marginfi::MarginfiClient, instructions::{open_position, protocol_interaction, rebalance, refresh}, + rebalance::utils::set_rebalance_ixs_data, state::{referral_state::ReferralState, solauto_position::SolautoPosition}, types::{ + errors::SolautoError, instruction::{ accounts::{ MarginfiOpenPositionAccounts, MarginfiProtocolInteractionAccounts, @@ -16,7 +15,7 @@ use crate::{ }, MarginfiOpenPositionData, RebalanceSettings, SolautoAction, SolautoStandardAccounts, }, - shared::{DeserializedAccount, LendingPlatform, SolautoError}, + shared::{DeserializedAccount, LendingPlatform, PriceType}, }, utils::*, }; @@ -49,20 +48,12 @@ pub fn process_marginfi_open_position_instruction<'a>( ctx.accounts.debt_mint, ctx.accounts.debt_bank, ctx.accounts.marginfi_account, + ctx.accounts.marginfi_group, max_ltv, liq_threshold, )?; if !solauto_position.data.self_managed.val { - let current_timestamp = Clock::get()?.unix_timestamp as u64; - validation_utils::validate_position_settings(&solauto_position.data, current_timestamp)?; - validation_utils::validate_dca_settings( - &solauto_position.data.position, - current_timestamp, - )?; - } - if solauto_position.data.self_managed.val && args.marginfi_account_seed_idx.is_some() { - msg!("Provided a Marginfi account seed index on a self-managed index"); - return Err(ProgramError::InvalidInstructionData.into()); + validation_utils::validate_position_settings(&solauto_position.data)?; } if ctx.accounts.referred_by_supply_ta.is_some() { @@ -108,14 +99,13 @@ pub fn process_marginfi_open_position_instruction<'a>( false, )?; - open_position::marginfi_open_position( - ctx, - std_accounts.solauto_position, - args.marginfi_account_seed_idx, - ) + open_position::marginfi_open_position(ctx, std_accounts.solauto_position) } -pub fn process_marginfi_refresh_data<'a>(accounts: &'a [AccountInfo<'a>]) -> ProgramResult { +pub fn process_marginfi_refresh_data<'a>( + accounts: &'a [AccountInfo<'a>], + price_type: PriceType, +) -> ProgramResult { msg!("Instruction: Marginfi refresh data"); let ctx = MarginfiRefreshDataAccounts::context(accounts)?; let mut solauto_position = @@ -148,6 +138,7 @@ pub fn process_marginfi_refresh_data<'a>(accounts: &'a [AccountInfo<'a>]) -> Pro ctx.accounts.debt_bank, ctx.accounts.debt_price_oracle, &mut solauto_position, + price_type, ) } @@ -216,23 +207,14 @@ pub fn process_marginfi_rebalance<'a>( false, )?; - // TODO: For DCAing-out - if ctx.accounts.position_authority.is_some() - && &std_accounts.solauto_position.data.authority - != ctx.accounts.position_authority.unwrap().key - { - msg!("Incorrect position authority provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + ctx.accounts.position_authority.is_none() + || &std_accounts.solauto_position.data.authority + == ctx.accounts.position_authority.unwrap().key, + SolautoError::IncorrectAccounts + ); - let rebalance_step = get_rebalance_step( - &mut std_accounts, - &args, - vec![ - ctx.accounts.position_supply_ta.key, - ctx.accounts.position_debt_ta.key, - ], - )?; + let rebalance_step = set_rebalance_ixs_data(&mut std_accounts, &args)?; rebalance::marginfi_rebalance(ctx, std_accounts, rebalance_step, args) } diff --git a/programs/solauto/src/processors/position.rs b/programs/solauto/src/processors/position.rs index 8fda5c89..d7fa466a 100644 --- a/programs/solauto/src/processors/position.rs +++ b/programs/solauto/src/processors/position.rs @@ -1,18 +1,16 @@ -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, sysvar::Sysvar, -}; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg}; use crate::{ instructions::{close_position, update_position}, state::solauto_position::SolautoPosition, types::{ instruction::{ - accounts::{CancelDCAAccounts, ClosePositionAccounts, UpdatePositionAccounts}, + accounts::{ClosePositionAccounts, UpdatePositionAccounts}, UpdatePositionData, }, - shared::{DeserializedAccount, SolautoError}, + shared::DeserializedAccount, }, - utils::{ix_utils, solauto_utils, validation_utils}, + utils::validation_utils, }; pub fn process_update_position_instruction<'a>( @@ -26,7 +24,7 @@ pub fn process_update_position_instruction<'a>( .unwrap(); validation_utils::validate_instruction(ctx.accounts.signer, &solauto_position, true, true)?; - validation_utils::validate_sysvar_accounts( + validation_utils::validate_standard_programs( Some(ctx.accounts.system_program), Some(ctx.accounts.token_program), None, @@ -54,7 +52,7 @@ pub fn process_close_position_instruction<'a>(accounts: &'a [AccountInfo<'a>]) - .unwrap(); validation_utils::validate_instruction(ctx.accounts.signer, &solauto_position, true, true)?; - validation_utils::validate_sysvar_accounts( + validation_utils::validate_standard_programs( Some(ctx.accounts.system_program), Some(ctx.accounts.token_program), Some(ctx.accounts.ata_program), @@ -76,7 +74,7 @@ pub fn process_close_position_instruction<'a>(accounts: &'a [AccountInfo<'a>]) - if !cfg!(feature = "local") { validation_utils::validate_no_active_balances( - ctx.accounts.protocol_account, + ctx.accounts.lp_user_account, solauto_position.data.position.lending_platform, )?; } @@ -85,47 +83,6 @@ pub fn process_close_position_instruction<'a>(accounts: &'a [AccountInfo<'a>]) - } pub fn process_cancel_dca<'a>(accounts: &'a [AccountInfo<'a>]) -> ProgramResult { - msg!("Instruction: Cancel DCA"); - let ctx = CancelDCAAccounts::context(accounts)?; - let mut solauto_position = - DeserializedAccount::::zerocopy(Some(ctx.accounts.solauto_position))? - .unwrap(); - - validation_utils::validate_instruction(ctx.accounts.signer, &solauto_position, true, true)?; - validation_utils::validate_sysvar_accounts( - Some(ctx.accounts.system_program), - Some(ctx.accounts.token_program), - Some(ctx.accounts.ata_program), - None, - None, - )?; - - if !solauto_position.data.position.dca.is_active() { - msg!("No active DCA exists on the provided Solauto position"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - validation_utils::validate_token_account( - &solauto_position, - ctx.accounts.position_dca_ta, - Some(solauto_position.data.position.dca.token_type), - None, - )?; - - solauto_utils::cancel_dca_in( - ctx.accounts.signer, - ctx.accounts.system_program, - ctx.accounts.token_program, - &mut solauto_position, - ctx.accounts.dca_mint, - ctx.accounts.position_dca_ta, - ctx.accounts.signer_dca_ta, - )?; - - validation_utils::validate_position_settings( - &solauto_position.data, - Clock::get()?.unix_timestamp as u64, - )?; - - ix_utils::update_data(&mut solauto_position) + // TODO + Ok(()) } diff --git a/programs/solauto/src/processors/referral_state.rs b/programs/solauto/src/processors/referral_state.rs index 7a6f7fe6..6898c946 100644 --- a/programs/solauto/src/processors/referral_state.rs +++ b/programs/solauto/src/processors/referral_state.rs @@ -7,13 +7,15 @@ use solana_program::{ program_error::ProgramError, sysvar::instructions::{load_current_index_checked, load_instruction_at_checked}, }; -use spl_associated_token_account::get_associated_token_address; use crate::{ + check, constants::WSOL_MINT, + error_if, instructions::referral_fees, state::referral_state::ReferralState, types::{ + errors::SolautoError, instruction::{ accounts::{ ClaimReferralFeesAccounts, ConvertReferralFeesAccounts, @@ -21,7 +23,7 @@ use crate::{ }, UpdateReferralStatesArgs, }, - shared::{DeserializedAccount, SolautoError}, + shared::DeserializedAccount, }, utils::{ix_utils, solauto_utils, validation_utils}, }; @@ -33,19 +35,17 @@ pub fn process_update_referral_states<'a>( msg!("Instruction: Update referral states"); let ctx = UpdateReferralStatesAccounts::context(accounts)?; - if !ctx.accounts.signer.is_signer { - msg!("Missing required referral signer"); - return Err(ProgramError::MissingRequiredSignature.into()); - } - - if ctx.accounts.referred_by_authority.is_some() - && ctx.accounts.referred_by_authority.unwrap().key == ctx.accounts.signer.key - { - msg!("Cannot set the referred by as the same as the referral state authority"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + ctx.accounts.signer.is_signer, + ProgramError::MissingRequiredSignature + ); + check!( + ctx.accounts.referred_by_authority.is_none() + || ctx.accounts.referred_by_authority.unwrap().key != ctx.accounts.signer.key, + SolautoError::IncorrectAccounts + ); - validation_utils::validate_sysvar_accounts( + validation_utils::validate_standard_programs( Some(ctx.accounts.system_program), None, None, @@ -92,7 +92,7 @@ pub fn process_convert_referral_fees<'a>(accounts: &'a [AccountInfo<'a>]) -> Pro DeserializedAccount::::zerocopy(Some(ctx.accounts.referral_state))?.unwrap(); validation_utils::validate_referral_signer(&referral_state, ctx.accounts.signer, true)?; - validation_utils::validate_sysvar_accounts( + validation_utils::validate_standard_programs( Some(ctx.accounts.system_program), Some(ctx.accounts.token_program), Some(ctx.accounts.ata_program), @@ -100,21 +100,21 @@ pub fn process_convert_referral_fees<'a>(accounts: &'a [AccountInfo<'a>]) -> Pro Some(ctx.accounts.ixs_sysvar), )?; - if !validation_utils::token_account_owned_by( - ctx.accounts.referral_fees_ta, - ctx.accounts.referral_state.key, - None, - )? { - msg!("Provided incorrect token account for the given referral state account"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + validation_utils::token_account_owned_by( + ctx.accounts.referral_fees_ta, + ctx.accounts.referral_state.key, + None + )?, + SolautoError::IncorrectAccounts + ); let current_ix_idx = load_current_index_checked(ctx.accounts.ixs_sysvar)?; let current_ix = load_instruction_at_checked(current_ix_idx as usize, ctx.accounts.ixs_sysvar)?; - if current_ix.program_id != crate::ID || get_stack_height() > TRANSACTION_LEVEL_STACK_HEIGHT { - msg!("Instruction is CPI"); - return Err(SolautoError::InstructionIsCPI.into()); - } + error_if!( + current_ix.program_id != crate::ID || get_stack_height() > TRANSACTION_LEVEL_STACK_HEIGHT, + SolautoError::InstructionIsCPI + ); let mut index = current_ix_idx; loop { @@ -126,25 +126,12 @@ pub fn process_convert_referral_fees<'a>(accounts: &'a [AccountInfo<'a>]) -> Pro let jup_swap = ix_utils::InstructionChecker::from_anchor( ctx.accounts.ixs_sysvar, - JUPITER_ID, + vec![JUPITER_ID], vec!["route", "shared_accounts_route"], current_ix_idx, ); - // Continues to break due to the fact that Jupiter keeps changing their program - // ix_utils::validate_jup_instruction( - // ctx.accounts.ixs_sysvar, - // (current_ix_idx + 1) as usize, - // &[&get_associated_token_address( - // ctx.accounts.referral_state.key, - // &referral_state.data.dest_fees_mint, - // )], - // )?; - - if !jup_swap.matches(1) { - msg!("Missing Jup swap as next transaction"); - return Err(SolautoError::IncorrectInstructions.into()); - } + check!(jup_swap.matches(1), SolautoError::IncorrectInstructions); referral_fees::convert_referral_fees(ctx, referral_state) } @@ -157,14 +144,7 @@ pub fn process_claim_referral_fees<'a>(accounts: &'a [AccountInfo<'a>]) -> Progr DeserializedAccount::::zerocopy(Some(ctx.accounts.referral_state))?.unwrap(); validation_utils::validate_referral_signer(&referral_state, ctx.accounts.signer, true)?; - if ctx.accounts.referral_authority.is_some() - && ctx.accounts.referral_authority.unwrap().key != &referral_state.data.authority - { - msg!("Provided incorrect referral authority"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - validation_utils::validate_sysvar_accounts( + validation_utils::validate_standard_programs( Some(ctx.accounts.system_program), Some(ctx.accounts.token_program), None, @@ -172,36 +152,37 @@ pub fn process_claim_referral_fees<'a>(accounts: &'a [AccountInfo<'a>]) -> Progr None, )?; - if ctx.accounts.referral_fees_dest_ta.key - != &get_associated_token_address( + check!( + ctx.accounts.referral_authority.key == &referral_state.data.authority, + SolautoError::IncorrectAccounts + ); + check!( + validation_utils::correct_token_account( + ctx.accounts.referral_fees_dest_ta.key, ctx.accounts.referral_state.key, - &referral_state.data.dest_fees_mint, - ) - { - msg!("Provided incorrect referral_fees_dest_ta account"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - if ctx.accounts.referral_fees_dest_mint.key != &referral_state.data.dest_fees_mint { - msg!("Provided incorrect referral_fees_dest_mint account"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - if referral_state.data.dest_fees_mint != WSOL_MINT && ctx.accounts.fees_destination_ta.is_none() - { - msg!("Missing fees destination token account when the token mint is not wSOL"); - return Err(SolautoError::IncorrectAccounts.into()); - } + &referral_state.data.dest_fees_mint + ), + SolautoError::IncorrectAccounts + ); + check!( + ctx.accounts.referral_fees_dest_mint.key == &referral_state.data.dest_fees_mint, + SolautoError::IncorrectAccounts + ); + error_if!( + referral_state.data.dest_fees_mint != WSOL_MINT + && ctx.accounts.fees_destination_ta.is_none(), + SolautoError::IncorrectAccounts + ); - if ctx.accounts.fees_destination_ta.is_some() - && ctx.accounts.fees_destination_ta.unwrap().key - != &get_associated_token_address( + if ctx.accounts.fees_destination_ta.is_some() { + check!( + validation_utils::correct_token_account( + ctx.accounts.fees_destination_ta.unwrap().key, &referral_state.data.authority, - &referral_state.data.dest_fees_mint, - ) - { - msg!("Provided incorrect fees_destination_ta for the given referral state and destination token mint"); - return Err(SolautoError::IncorrectAccounts.into()); + &referral_state.data.dest_fees_mint + ), + SolautoError::IncorrectAccounts + ); } referral_fees::claim_referral_fees(ctx, referral_state) diff --git a/programs/solauto/src/rebalance/mod.rs b/programs/solauto/src/rebalance/mod.rs new file mode 100644 index 00000000..d5e32b83 --- /dev/null +++ b/programs/solauto/src/rebalance/mod.rs @@ -0,0 +1,4 @@ +pub mod rebalancer; +pub mod rebalancer_tests; +pub mod solauto_fees; +pub mod utils; diff --git a/programs/solauto/src/rebalance/rebalancer.rs b/programs/solauto/src/rebalance/rebalancer.rs new file mode 100644 index 00000000..565390d5 --- /dev/null +++ b/programs/solauto/src/rebalance/rebalancer.rs @@ -0,0 +1,487 @@ +use std::{ + cmp::min, + ops::{Add, Mul}, +}; + +use solana_program::{entrypoint::ProgramResult, program_error::ProgramError}; + +use crate::{ + check, + state::solauto_position::{ + PositionTokenState, RebalanceData, SolautoPosition, TokenBalanceChangeType, + }, + types::{ + errors::SolautoError, + instruction::RebalanceSettings, + shared::{ + RebalanceDirection, RebalanceStep, SolautoRebalanceType, SwapType, TokenBalanceAmount, + }, + solauto::{ + FromLendingPlatformAction, SolautoAccount, SolautoCpiAction, + SolautoSplTokenTransferArgs, + }, + }, + utils::math_utils::{ + calc_fee_amount, from_bps, from_rounded_usd_value, usd_value_to_base_unit, + }, +}; + +use super::{ + solauto_fees::SolautoFeesBps, + utils::{eligible_for_rebalance, get_rebalance_values}, +}; + +pub struct TokenAccountData { + pub balance: u64, +} +impl TokenAccountData { + pub fn from(balance: u64) -> Self { + Self { balance } + } + pub fn new() -> Self { + Self { balance: 0 } + } +} + +pub struct SolautoPositionData<'a> { + pub data: &'a mut Box, + pub supply_ta: TokenAccountData, + pub debt_ta: TokenAccountData, +} + +pub struct RebalancerData<'a> { + pub rebalance_args: RebalanceSettings, + pub solauto_position: SolautoPositionData<'a>, + pub solauto_fees_bps: SolautoFeesBps, + pub referred_by: bool, +} + +pub struct RebalanceResult { + pub finished: bool, +} + +pub struct Rebalancer<'a> { + actions: Vec, + pub data: RebalancerData<'a>, +} + +impl<'a> Rebalancer<'a> { + pub fn new(data: RebalancerData<'a>) -> Self { + Self { + actions: Vec::::new(), + data, + } + } + + pub fn actions(&self) -> &Vec { + &self.actions + } + + pub fn reset_actions(&mut self) { + self.actions = Vec::new(); + } + + fn position_data(&self) -> &Box { + &self.data.solauto_position.data + } + + fn position_supply_ta(&self) -> &TokenAccountData { + &self.data.solauto_position.supply_ta + } + + fn position_debt_ta(&self) -> &TokenAccountData { + &self.data.solauto_position.debt_ta + } + + fn rebalance_data(&self) -> &RebalanceData { + &self.position_data().rebalance + } + + fn is_boost(&self) -> bool { + self.rebalance_data().values.rebalance_direction == RebalanceDirection::Boost + } + + fn set_rebalance_data(&mut self) -> ProgramResult { + if self.rebalance_data().values_set() { + return Ok(()); + } + + check!( + self.data + .rebalance_args + .target_liq_utilization_rate_bps + .is_some() + || eligible_for_rebalance(self.position_data()), + SolautoError::InvalidRebalanceCondition + ); + + self.data.solauto_position.data.rebalance.values = get_rebalance_values( + self.position_data(), + &self.data.rebalance_args, + &self.data.solauto_fees_bps, + )?; + + Ok(()) + } + + fn calc_additional_amount( + &self, + rounded_usd_value: u64, + token_usage: PositionTokenState, + max: Option, + ) -> u64 { + let base_unit_amount = usd_value_to_base_unit( + from_rounded_usd_value(rounded_usd_value), + token_usage.decimals, + token_usage.market_price(), + ); + + if max.is_some() { + min(base_unit_amount, max.unwrap()) + } else { + base_unit_amount + } + } + + fn get_dynamic_balance(&self) -> (u64, SolautoAccount) { + let (ta, account) = if self.is_boost() { + ( + self.position_supply_ta(), + SolautoAccount::SolautoPositionSupplyTa, + ) + } else { + ( + self.position_debt_ta(), + SolautoAccount::SolautoPositionDebtTa, + ) + }; + + let balance = ta.balance; + // Subtract current balances that are attributed to DCA / limit order in + + (balance, account) + } + + fn transfer_to_authority_if_needed(&mut self, base_unit_amount: u64) { + if self.position_data().self_managed.val { + let (solauto_position_ta, authority_ta) = if self.is_boost() { + ( + SolautoAccount::SolautoPositionSupplyTa, + SolautoAccount::AuthoritySupplyTa, + ) + } else { + ( + SolautoAccount::SolautoPositionDebtTa, + SolautoAccount::AuthorityDebtTa, + ) + }; + self.actions.push(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: solauto_position_ta, + to_wallet_ta: authority_ta, + amount: base_unit_amount, + }, + )); + } + } + + fn pull_liquidity_from_lp(&mut self, base_unit_amount: u64, destination_ta: SolautoAccount) { + if self.is_boost() { + self.actions + .push(SolautoCpiAction::Borrow(FromLendingPlatformAction { + amount: base_unit_amount, + to_wallet_ta: destination_ta, + })); + } else { + self.actions + .push(SolautoCpiAction::Withdraw(FromLendingPlatformAction { + amount: TokenBalanceAmount::Some(base_unit_amount), + to_wallet_ta: destination_ta, + })); + } + } + + fn put_liquidity_in_lp(&mut self, base_unit_amount: u64) { + self.transfer_to_authority_if_needed(base_unit_amount); + + if self.is_boost() { + self.actions + .push(SolautoCpiAction::Deposit(base_unit_amount)); + } else { + let token_balance_amount = if self + .data + .rebalance_args + .target_liq_utilization_rate_bps + .is_some() + && self + .data + .rebalance_args + .target_liq_utilization_rate_bps + .unwrap() + == 0 + { + TokenBalanceAmount::All + } else { + TokenBalanceAmount::Some(min( + self.position_data().state.debt.amount_used.base_unit, + base_unit_amount, + )) + }; + self.actions + .push(SolautoCpiAction::Repay(token_balance_amount)); + } + } + + fn get_additional_amount_before_swap(&mut self) -> u64 { + if !self + .rebalance_data() + .values + .token_balance_change + .requires_one() + { + return 0; + } + + let token_balance_change = self.rebalance_data().values.token_balance_change; + let mut amount = 0; + + let action = match token_balance_change.change_type { + TokenBalanceChangeType::PreSwapDeposit => { + Some(SolautoCpiAction::Deposit(self.calc_additional_amount( + token_balance_change.amount_usd, + self.position_data().state.supply, + Some(self.data.solauto_position.supply_ta.balance), + ))) + } + TokenBalanceChangeType::PostSwapDeposit => { + amount = self.calc_additional_amount( + token_balance_change.amount_usd, + self.position_data().state.debt, + Some(self.data.solauto_position.debt_ta.balance), + ); + Some(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + amount, + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: SolautoAccount::SolautoPositionDebtTa, + to_wallet_ta: SolautoAccount::IntermediaryTa, + }, + )) + } + TokenBalanceChangeType::PostRebalanceWithdrawDebtToken => { + amount = self.calc_additional_amount( + token_balance_change.amount_usd, + self.position_data().state.supply, + Some(self.position_supply_ta().balance), + ); + Some(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + amount, + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: SolautoAccount::SolautoPositionSupplyTa, + to_wallet_ta: SolautoAccount::IntermediaryTa, + }, + )) + } + _ => None, + }; + + if action.is_some() { + self.actions.push(action.unwrap()); + } + + amount + } + + fn get_additional_amount_after_swap(&mut self) -> u64 { + if !self + .rebalance_data() + .values + .token_balance_change + .requires_one() + { + return 0; + } + + let token_balance_change = self.rebalance_data().values.token_balance_change; + let mut amount = 0; + + let action = match token_balance_change.change_type { + TokenBalanceChangeType::PostRebalanceWithdrawSupplyToken => { + amount = self.calc_additional_amount( + token_balance_change.amount_usd, + self.position_data().state.supply, + None, + ); + Some(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + amount, + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: SolautoAccount::SolautoPositionSupplyTa, + to_wallet_ta: SolautoAccount::AuthoritySupplyTa, // TODO: what if this is native mint + }, + )) + } + TokenBalanceChangeType::PostRebalanceWithdrawDebtToken => { + amount = self.calc_additional_amount( + token_balance_change.amount_usd, + self.position_data().state.debt, + None, + ); + Some(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + amount, + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: SolautoAccount::SolautoPositionDebtTa, + to_wallet_ta: SolautoAccount::AuthorityDebtTa, // TODO: what if this is native mint + }, + )) + } + _ => None, + }; + + if action.is_some() { + self.actions.push(action.unwrap()); + } + + amount + } + + fn payout_fee( + &mut self, + available_balance: u64, + fee_pct_bps: u16, + position_ta: SolautoAccount, + destination_ta: SolautoAccount, + ) -> Result { + let fee_amount = calc_fee_amount(available_balance, fee_pct_bps); + self.actions.push(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: position_ta, + to_wallet_ta: destination_ta, + amount: fee_amount, + }, + )); + + Ok(fee_amount) + } + + fn payout_fees(&mut self, available_balance: u64) -> Result { + let rebalance_direction = &self.rebalance_data().values.rebalance_direction; + let position_ta = if self.is_boost() { + SolautoAccount::SolautoPositionSupplyTa + } else { + SolautoAccount::SolautoPositionDebtTa + }; + let fee_payout = self.data.solauto_fees_bps.fetch_fees(rebalance_direction); + if fee_payout.total == 0 { + return Ok(available_balance); + } + + let solauto_fees = self.payout_fee( + available_balance, + fee_payout.solauto, + position_ta, + SolautoAccount::SolautoFeesTa, + )?; + + let referrer_fees = if self.data.referred_by { + self.payout_fee( + available_balance, + fee_payout.referrer, + position_ta, + SolautoAccount::ReferredByTa, + )? + } else { + 0 + }; + + Ok(available_balance - solauto_fees - referrer_fees) + } + + fn repay_flash_loan_if_necessary(&mut self) -> ProgramResult { + if matches!( + self.rebalance_data().ixs.rebalance_type, + SolautoRebalanceType::DoubleRebalanceWithFL + | SolautoRebalanceType::FLRebalanceThenSwap + | SolautoRebalanceType::FLSwapThenRebalance + ) { + let flash_loan_amount = self.rebalance_data().ixs.flash_loan_amount; + + let fl_repay_amount = if self.rebalance_data().ixs.swap_type == SwapType::ExactOut { + self.data.rebalance_args.swap_in_amount_base_unit.unwrap() + } else { + check!(flash_loan_amount != 0, SolautoError::IncorrectInstructions); + let flash_loan_fee_bps = self.data.rebalance_args.flash_loan_fee_bps.unwrap_or(0); + flash_loan_amount.add( + (flash_loan_amount as f64) + .mul(from_bps(flash_loan_fee_bps)) + .ceil() as u64, + ) + }; + + self.pull_liquidity_from_lp(fl_repay_amount, SolautoAccount::IntermediaryTa); + } + + Ok(()) + } + + fn finish_rebalance(&mut self, dynamic_balance: u64) -> ProgramResult { + let amount_to_put_in_lp = self.payout_fees(dynamic_balance)?; + self.put_liquidity_in_lp(amount_to_put_in_lp); + self.repay_flash_loan_if_necessary()?; + Ok(()) + } + + fn pre_swap_rebalance(&mut self) -> Result { + self.set_rebalance_data()?; + + let amount_to_swap = self.data.rebalance_args.swap_in_amount_base_unit.unwrap(); + let additional_amount_to_swap = self.get_additional_amount_before_swap(); + + if self.rebalance_data().ixs.swap_type == SwapType::ExactOut { + let (dynamic_balance, _) = self.get_dynamic_balance(); + self.finish_rebalance(dynamic_balance)?; + Ok(RebalanceResult { finished: true }) + } else { + let amount_to_pull_from_lp = amount_to_swap - additional_amount_to_swap; + self.pull_liquidity_from_lp(amount_to_pull_from_lp, SolautoAccount::IntermediaryTa); + Ok(RebalanceResult { finished: false }) + } + } + + fn post_swap_rebalance(&mut self) -> Result { + self.set_rebalance_data()?; + + let additional_amount_after_swap = self.get_additional_amount_after_swap(); + let (dynamic_balance, balance_ta) = self.get_dynamic_balance(); + let balance_leftover = dynamic_balance - additional_amount_after_swap; + + if self.rebalance_data().ixs.swap_type == SwapType::ExactOut { + self.actions.push(SolautoCpiAction::SplTokenTransfer( + SolautoSplTokenTransferArgs { + from_wallet: SolautoAccount::SolautoPosition, + from_wallet_ta: balance_ta, + to_wallet_ta: SolautoAccount::IntermediaryTa, + amount: balance_leftover, + }, + )); + } else { + self.finish_rebalance(balance_leftover)?; + } + + Ok(RebalanceResult { finished: true }) + } + + pub fn rebalance( + &mut self, + rebalance_step: RebalanceStep, + ) -> Result { + match rebalance_step { + RebalanceStep::PreSwap => self.pre_swap_rebalance(), + RebalanceStep::PostSwap => self.post_swap_rebalance(), + } + } +} diff --git a/programs/solauto/src/rebalance/rebalancer_tests.rs b/programs/solauto/src/rebalance/rebalancer_tests.rs new file mode 100644 index 00000000..ed315258 --- /dev/null +++ b/programs/solauto/src/rebalance/rebalancer_tests.rs @@ -0,0 +1,858 @@ +use std::ops::{Div, Mul}; + +use solana_program::pubkey::Pubkey; + +use crate::{ + state::solauto_position::{ + PositionData, PositionState, RebalanceData, SolautoPosition, SolautoSettingsParameters, + SolautoSettingsParametersInp, + }, + types::{ + instruction::RebalanceSettings, + shared::{ + PodBool, PositionType, RebalanceDirection, RefreshedTokenState, SwapType, + TokenBalanceAmount, + }, + solauto::{PositionValues, SolautoAccount, SolautoCpiAction}, + }, + utils::{ + math_utils::{ + from_base_unit, from_bps, from_rounded_usd_value, get_liq_utilization_rate_bps, + round_to_decimals, to_base_unit, + }, + solauto_utils::update_token_state, + validation_utils, + }, +}; + +use super::{ + rebalancer::{Rebalancer, RebalancerData, SolautoPositionData, TokenAccountData}, + solauto_fees::SolautoFeesBps, +}; + +const TEST_TOKEN_DECIMALS: u8 = 9; +const SOLAUTO_FEE_BPS: u16 = 50; +const BORROW_FEE_BPS: u16 = 50; +const FLASH_LOAN_FEE_BPS: u16 = 50; +const SUPPLY_PRICE: f64 = 100.0; +const DEBT_PRICE: f64 = 1.0; +const MAX_LTV_BPS: u16 = 6400; +const LIQ_THRESHOLD_BPS: u16 = 8181; + +pub struct FakePosition<'a> { + values: &'a PositionValues, + settings: SolautoSettingsParametersInp, + max_ltv_bps: Option, + liq_threshold_bps: Option, +} + +fn create_position<'a>(pos: &FakePosition<'a>) -> Box { + let mut state = PositionState::default(); + state.max_ltv_bps = pos.max_ltv_bps.unwrap_or(6400); + state.liq_threshold_bps = pos.liq_threshold_bps.unwrap_or(8181); + state.liq_utilization_rate_bps = get_liq_utilization_rate_bps( + pos.values.supply_usd, + pos.values.debt_usd, + from_bps(state.liq_threshold_bps), + ); + + let supply_mint = Pubkey::new_unique(); + let debt_mint = Pubkey::new_unique(); + state.supply.mint = supply_mint; + state.debt.mint = debt_mint; + + update_token_state( + &mut state.supply, + &(RefreshedTokenState { + amount_used: to_base_unit(pos.values.supply_usd / SUPPLY_PRICE, TEST_TOKEN_DECIMALS), + amount_can_be_used: 0, + mint: supply_mint, + decimals: TEST_TOKEN_DECIMALS, + market_price: SUPPLY_PRICE, + borrow_fee_bps: None, + }), + ); + update_token_state( + &mut state.debt, + &(RefreshedTokenState { + amount_used: to_base_unit(pos.values.debt_usd / DEBT_PRICE, TEST_TOKEN_DECIMALS), + amount_can_be_used: 0, + mint: debt_mint, + decimals: TEST_TOKEN_DECIMALS, + market_price: DEBT_PRICE, + borrow_fee_bps: Some(BORROW_FEE_BPS), + }), + ); + + let mut position_data = PositionData::default(); + position_data.settings = SolautoSettingsParameters::from(pos.settings); + + let position = SolautoPosition::new( + 1, + Pubkey::new_unique(), + PositionType::Leverage, + position_data, + state, + ); + + Box::new(position) +} + +pub struct FakeRebalance<'a> { + pos: &'a mut Box, + position_supply_ta_balance: Option, + position_debt_ta_balance: Option, + rebalance_direction: RebalanceDirection, +} + +fn create_rebalancer<'a>( + data: FakeRebalance<'a>, + rebalance_args: RebalanceSettings, + flash_loan_amount: Option, +) -> Rebalancer<'a> { + data.pos.rebalance.ixs.rebalance_type = rebalance_args.rebalance_type; + data.pos.rebalance.ixs.flash_loan_amount = flash_loan_amount.unwrap_or(0); + data.pos.rebalance.ixs.active = PodBool::new(true); + data.pos.rebalance.ixs.swap_type = rebalance_args.swap_type.unwrap_or(SwapType::default()); + + let solauto_fees = SolautoFeesBps::from_mock(SOLAUTO_FEE_BPS, false); + + let rebalancer = Rebalancer::new(RebalancerData { + rebalance_args, + solauto_position: SolautoPositionData { + data: data.pos, + supply_ta: TokenAccountData::from(data.position_supply_ta_balance.unwrap_or(0)), + debt_ta: TokenAccountData::from(data.position_debt_ta_balance.unwrap_or(0)), + }, + solauto_fees_bps: solauto_fees, + referred_by: false, + }); + + rebalancer +} + +fn credit_token_account<'a>( + rebalancer: &mut Rebalancer<'a>, + ta_creditor: &mut TaCreditor, + ta_pk: SolautoAccount, + base_unit_amount: i64, +) { + let credit_ta = |ta: &mut TokenAccountData| { + println!("Crediting token account with {}", base_unit_amount); + if base_unit_amount > 0 { + ta.balance += base_unit_amount as u64; + } else { + ta.balance = ta.balance.saturating_sub((base_unit_amount * -1) as u64); + } + }; + + if ta_pk == SolautoAccount::SolautoPositionSupplyTa { + credit_ta(&mut rebalancer.data.solauto_position.supply_ta); + } else if ta_pk == SolautoAccount::SolautoPositionDebtTa { + credit_ta(&mut rebalancer.data.solauto_position.debt_ta); + } else if ta_pk == SolautoAccount::IntermediaryTa { + credit_ta(&mut ta_creditor.intermediary_ta); + } else if ta_pk == SolautoAccount::AuthoritySupplyTa { + credit_ta(&mut ta_creditor.authority_supply_ta); + } else if ta_pk == SolautoAccount::AuthorityDebtTa { + credit_ta(&mut ta_creditor.authority_debt_ta); + } else { + println!("Couldn't find token account"); + } +} + +fn apply_actions<'a>(rebalancer: &mut Rebalancer<'a>, ta_creditor: &mut TaCreditor) { + println!("Actions: {}", rebalancer.actions().len()); + + for action in rebalancer.actions().clone() { + match action { + SolautoCpiAction::Deposit(amount) => { + println!("Deposit {}", amount); + rebalancer + .data + .solauto_position + .data + .state + .supply + .update_usage(amount as i64); + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::SolautoPositionSupplyTa, + (amount as i64) * -1, + ); + } + SolautoCpiAction::Withdraw(data) => { + let base_unit_amount = if let TokenBalanceAmount::Some(amount) = data.amount { + amount + } else { + rebalancer + .data + .solauto_position + .data + .state + .supply + .amount_used + .base_unit + }; + println!("Withdraw {}", base_unit_amount); + rebalancer + .data + .solauto_position + .data + .state + .supply + .update_usage((base_unit_amount as i64) * -1); + credit_token_account( + rebalancer, + ta_creditor, + data.to_wallet_ta, + base_unit_amount as i64, + ); + } + SolautoCpiAction::Borrow(data) => { + println!("Borrow {}", data.amount); + rebalancer + .data + .solauto_position + .data + .state + .debt + .update_usage(data.amount as i64); + credit_token_account( + rebalancer, + ta_creditor, + data.to_wallet_ta, + data.amount as i64, + ); + } + SolautoCpiAction::Repay(data) => { + let base_unit_amount = if let TokenBalanceAmount::Some(amount) = data { + amount + } else { + rebalancer + .data + .solauto_position + .data + .state + .debt + .amount_used + .base_unit + }; + println!("Repay {}", base_unit_amount); + rebalancer + .data + .solauto_position + .data + .state + .debt + .update_usage((base_unit_amount as i64) * -1); + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::SolautoPositionDebtTa, + (base_unit_amount as i64) * -1, + ); + } + SolautoCpiAction::SplTokenTransfer(args) => { + println!("Transfer from {}", args.amount); + credit_token_account( + rebalancer, + ta_creditor, + args.from_wallet_ta, + (args.amount as i64) * -1, + ); + credit_token_account( + rebalancer, + ta_creditor, + args.to_wallet_ta, + args.amount as i64, + ); + } + } + } + + rebalancer.reset_actions(); +} + +fn perform_swap<'a>( + rebalancer: &mut Rebalancer<'a>, + ta_creditor: &mut TaCreditor, + rebalance_direction: &RebalanceDirection, + to_solauto_position_ta: bool, +) -> u64 { + let (input_price, output_price) = if rebalance_direction == &RebalanceDirection::Boost { + (DEBT_PRICE, SUPPLY_PRICE) + } else { + (SUPPLY_PRICE, DEBT_PRICE) + }; + + let swap_usd_value = + from_base_unit::(ta_creditor.intermediary_ta.balance, TEST_TOKEN_DECIMALS) + .mul(input_price); + + println!("Swapping ${}", swap_usd_value); + + ta_creditor.intermediary_ta.balance = 0; + + let output_amount = + to_base_unit::(swap_usd_value.div(output_price), TEST_TOKEN_DECIMALS); + + if to_solauto_position_ta { + if rebalance_direction == &RebalanceDirection::Boost { + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::SolautoPositionSupplyTa, + output_amount as i64, + ); + } else { + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::SolautoPositionDebtTa, + output_amount as i64, + ); + } + } + + output_amount +} + +fn validate_rebalance<'a>(rebalancer: &mut Rebalancer<'a>) { + assert_eq!( + round_to_decimals( + from_rounded_usd_value( + rebalancer + .data + .solauto_position + .data + .rebalance + .values + .target_debt_usd + ), + 4 + ), + round_to_decimals( + rebalancer + .data + .solauto_position + .data + .state + .debt + .amount_used + .usd_value(), + 4 + ), + "Incorrect debt usd. Expected (left) vs. actual (right)" + ); + assert_eq!( + round_to_decimals( + from_rounded_usd_value( + rebalancer + .data + .solauto_position + .data + .rebalance + .values + .target_supply_usd + ), + 4 + ), + round_to_decimals( + rebalancer + .data + .solauto_position + .data + .state + .supply + .amount_used + .usd_value(), + 4 + ), + "Incorrect supply usd. Expected (left) vs. actual (right)" + ); + + assert!(validation_utils::validate_rebalance(rebalancer.data.solauto_position.data).is_ok()); + rebalancer.data.solauto_position.data.rebalance = RebalanceData::default(); +} + +pub struct TaCreditor { + pub intermediary_ta: TokenAccountData, + pub authority_supply_ta: TokenAccountData, + pub authority_debt_ta: TokenAccountData, +} +impl TaCreditor { + pub fn new() -> Self { + Self { + intermediary_ta: TokenAccountData { balance: 0 }, + authority_supply_ta: TokenAccountData { balance: 0 }, + authority_debt_ta: TokenAccountData { balance: 0 }, + } + } +} + +mod tests { + use std::ops::{Add, Div}; + + use crate::{ + types::{ + shared::{RebalanceStep, SolautoRebalanceType, SwapType}, + solauto::RebalanceFeesBps, + }, + utils::math_utils::{get_debt_adjustment, get_max_boost_to_bps, get_max_repay_to_bps}, + }; + + use super::*; + + #[test] + fn test_standard_rebalance_boost() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 25.0, + }; + let rebalance_to = 3800; + let rebalance_direction = RebalanceDirection::Boost; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: rebalance_to, + repay_gap: 50, + repay_to_bps: get_max_repay_to_bps(MAX_LTV_BPS, LIQ_THRESHOLD_BPS), + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: 0, + }), + ); + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::Regular, + target_liq_utilization_rate_bps: None, + swap_in_amount_base_unit: Some(to_base_unit( + debt_adjustment.debt_adjustment_usd.div(DEBT_PRICE), + TEST_TOKEN_DECIMALS, + )), + flash_loan_fee_bps: None, + swap_type: Some(SwapType::ExactIn), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + None, + ); + let ta_creditor = &mut TaCreditor::new(); + + let res = rebalancer.rebalance(RebalanceStep::PreSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + perform_swap(rebalancer, ta_creditor, &rebalance_direction, true); + + let res = rebalancer.rebalance(RebalanceStep::PostSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + } + + #[test] + fn test_standard_rebalance_repay() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 50.0, + }; + let rebalance_to = 5000; + let rebalance_direction = RebalanceDirection::Repay; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: 2000, + repay_gap: 50, + repay_to_bps: rebalance_to, + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: 0, + }), + ); + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::Regular, + target_liq_utilization_rate_bps: None, + swap_in_amount_base_unit: Some(to_base_unit( + debt_adjustment.debt_adjustment_usd.abs().div(SUPPLY_PRICE), + TEST_TOKEN_DECIMALS, + )), + flash_loan_fee_bps: None, + swap_type: Some(SwapType::ExactIn), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + None, + ); + let ta_creditor = &mut TaCreditor::new(); + + let res = rebalancer.rebalance(RebalanceStep::PreSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + perform_swap(rebalancer, ta_creditor, &rebalance_direction, true); + + let res = rebalancer.rebalance(RebalanceStep::PostSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + } + + // TODO: when token balance changes pre-swap are needed + // #[test] + // fn test_double_rebalance_fl_boost() {} + + // #[test] + // fn test_double_rebalance_fl_repay() {} + + #[test] + fn test_swap_then_rebalance_boost() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 0.0, + }; + let rebalance_to = get_max_boost_to_bps(MAX_LTV_BPS, LIQ_THRESHOLD_BPS); + let rebalance_direction = RebalanceDirection::Boost; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: rebalance_to, + repay_gap: 50, + repay_to_bps: rebalance_to, + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: FLASH_LOAN_FEE_BPS, + }), + ); + let flash_borrow = to_base_unit( + debt_adjustment.debt_adjustment_usd.abs().div(DEBT_PRICE), + TEST_TOKEN_DECIMALS, + ); + + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::FLSwapThenRebalance, + target_liq_utilization_rate_bps: None, + swap_in_amount_base_unit: Some(flash_borrow), + flash_loan_fee_bps: Some(FLASH_LOAN_FEE_BPS), + swap_type: Some(SwapType::ExactIn), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + Some(flash_borrow), + ); + let ta_creditor = &mut TaCreditor::new(); + + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::IntermediaryTa, + flash_borrow as i64, + ); + + perform_swap(rebalancer, ta_creditor, &rebalance_direction, true); + + let res = rebalancer.rebalance(RebalanceStep::PostSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + } + + #[test] + fn test_swap_then_rebalance_repay() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 70.0, + }; + let rebalance_to = 1000; + let rebalance_direction = RebalanceDirection::Repay; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: 3000, + repay_gap: 50, + repay_to_bps: get_max_repay_to_bps(MAX_LTV_BPS, LIQ_THRESHOLD_BPS), + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: FLASH_LOAN_FEE_BPS, + }), + ); + let flash_borrow = to_base_unit( + debt_adjustment.debt_adjustment_usd.abs().div(SUPPLY_PRICE), + TEST_TOKEN_DECIMALS, + ); + + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::FLSwapThenRebalance, + target_liq_utilization_rate_bps: Some(rebalance_to), + swap_in_amount_base_unit: Some(flash_borrow), + flash_loan_fee_bps: Some(FLASH_LOAN_FEE_BPS), + swap_type: Some(SwapType::ExactIn), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + Some(flash_borrow), + ); + let ta_creditor = &mut TaCreditor::new(); + + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::IntermediaryTa, + flash_borrow as i64, + ); + + perform_swap(rebalancer, ta_creditor, &rebalance_direction, true); + + let res = rebalancer.rebalance(RebalanceStep::PostSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + } + + // TODO: double rebalance with flash loan exact out swap will not work currently. What needs to change? + + #[test] + fn test_rebalance_then_swap_repay() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 70.0, + }; + let rebalance_to = 1000; + let rebalance_direction = RebalanceDirection::Repay; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: rebalance_to, + repay_gap: 50, + repay_to_bps: rebalance_to, + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: 0, + }), + ); + + println!("{}", debt_adjustment.debt_adjustment_usd.abs()); + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::FLRebalanceThenSwap, + target_liq_utilization_rate_bps: None, + swap_in_amount_base_unit: Some(to_base_unit( + debt_adjustment.debt_adjustment_usd.abs().div(SUPPLY_PRICE), + TEST_TOKEN_DECIMALS, + )), + flash_loan_fee_bps: None, + swap_type: Some(SwapType::ExactOut), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + None, + ); + let ta_creditor = &mut TaCreditor::new(); + + let flash_borrow = to_base_unit( + debt_adjustment.debt_adjustment_usd.abs().div(DEBT_PRICE), + TEST_TOKEN_DECIMALS, + ); + credit_token_account( + rebalancer, + ta_creditor, + SolautoAccount::SolautoPositionDebtTa, + flash_borrow, + ); + + let res = rebalancer.rebalance(RebalanceStep::PreSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + + let swap_output_amount = perform_swap(rebalancer, ta_creditor, &rebalance_direction, false); + + assert_eq!( + round_to_decimals(from_base_unit(swap_output_amount, TEST_TOKEN_DECIMALS), 4), + round_to_decimals(from_base_unit(flash_borrow as u64, TEST_TOKEN_DECIMALS), 4), + "Flash loan repayment (left) doesn't match expected (right)" + ); + } + + #[test] + fn test_target_liq_utilization_rate_rebalance() { + let pos_values = PositionValues { + supply_usd: 100.0, + debt_usd: 25.0, + }; + let rebalance_to = 3500; + let rebalance_direction = RebalanceDirection::Boost; + + let settings = SolautoSettingsParametersInp { + boost_gap: 50, + boost_to_bps: rebalance_to + 300, + repay_gap: 50, + repay_to_bps: get_max_repay_to_bps(MAX_LTV_BPS, LIQ_THRESHOLD_BPS), + }; + let mut position = create_position( + &(FakePosition { + values: &pos_values, + settings, + max_ltv_bps: Some(MAX_LTV_BPS), + liq_threshold_bps: Some(LIQ_THRESHOLD_BPS), + }), + ); + let debt_adjustment = get_debt_adjustment( + LIQ_THRESHOLD_BPS, + &pos_values, + rebalance_to, + &(RebalanceFeesBps { + solauto: SOLAUTO_FEE_BPS, + lp_borrow: BORROW_FEE_BPS, + flash_loan: 0, + }), + ); + let rebalance_args = RebalanceSettings { + rebalance_type: SolautoRebalanceType::Regular, + target_liq_utilization_rate_bps: Some(rebalance_to), + swap_in_amount_base_unit: Some(to_base_unit( + debt_adjustment.debt_adjustment_usd.div(DEBT_PRICE), + TEST_TOKEN_DECIMALS, + )), + flash_loan_fee_bps: None, + swap_type: Some(SwapType::ExactIn), + price_type: None, + }; + let rebalancer = &mut create_rebalancer( + FakeRebalance { + pos: &mut position, + position_supply_ta_balance: None, + position_debt_ta_balance: None, + rebalance_direction: rebalance_direction.clone(), + }, + rebalance_args, + None, + ); + let ta_creditor = &mut TaCreditor::new(); + + let res = rebalancer.rebalance(RebalanceStep::PreSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + perform_swap(rebalancer, ta_creditor, &rebalance_direction, true); + + let res = rebalancer.rebalance(RebalanceStep::PostSwap); + assert!(res.is_ok()); + apply_actions(rebalancer, ta_creditor); + + validate_rebalance(rebalancer); + } +} diff --git a/programs/solauto/src/rebalance/solauto_fees.rs b/programs/solauto/src/rebalance/solauto_fees.rs new file mode 100644 index 00000000..d033de4b --- /dev/null +++ b/programs/solauto/src/rebalance/solauto_fees.rs @@ -0,0 +1,94 @@ +use std::ops::Mul; + +use crate::{constants::REFERRER_PERCENTAGE, types::shared::RebalanceDirection}; + +pub struct FeePayout { + pub solauto: u16, + pub referrer: u16, + pub total: u16, +} + +#[derive(Clone, Copy)] +pub struct SolautoFeesBps { + has_been_referred: bool, + target_liq_utilization_rate_bps: Option, + position_net_worth_usd: f64, + mock_fee_bps: Option, +} +impl SolautoFeesBps { + pub fn from_mock(total_fees_bps: u16, has_been_referred: bool) -> Self { + Self { + mock_fee_bps: Some(total_fees_bps), + has_been_referred: has_been_referred, + target_liq_utilization_rate_bps: None, + position_net_worth_usd: 0.0, + } + } + pub fn from( + has_been_referred: bool, + target_liq_utilization_rate_bps: Option, + position_net_worth_usd: f64, + ) -> Self { + Self { + has_been_referred, + target_liq_utilization_rate_bps, + position_net_worth_usd, + mock_fee_bps: None, + } + } + pub fn fetch_fees(&self, rebalance_direction: &RebalanceDirection) -> FeePayout { + if self.mock_fee_bps.is_some() { + let fee_bps = self.mock_fee_bps.unwrap(); + let (solauto_fee, referrer_fee) = if self.has_been_referred { + ( + (fee_bps as f64).mul(0.85).floor() as u16, + (fee_bps as f64).mul(0.15).floor() as u16, + ) + } else { + (fee_bps, 0) + }; + return FeePayout { + total: fee_bps, + solauto: solauto_fee, + referrer: referrer_fee, + }; + } + + let min_size: f64 = 10000.0; // Minimum position size + let max_size: f64 = 250000.0; // Maximum position size + let max_fee_bps: f64 = 50.0; // Fee in basis points for min_size (0.5%) + let min_fee_bps: f64 = 25.0; // Fee in basis points for max_size (0.25%) + let k = 1.5; + + let mut fee_bps: f64; + if self.target_liq_utilization_rate_bps.is_some() { + if self.target_liq_utilization_rate_bps.unwrap() == 0 { + fee_bps = 15.0; + } else { + fee_bps = 10.0; + } + } else if rebalance_direction == &RebalanceDirection::Repay { + fee_bps = 25.0; + } else if self.position_net_worth_usd <= min_size { + fee_bps = max_fee_bps; + } else if self.position_net_worth_usd >= max_size { + fee_bps = min_fee_bps; + } else { + let t = (self.position_net_worth_usd.ln() - min_size.ln()) + / (max_size.ln() - min_size.ln()); + fee_bps = (min_fee_bps + (max_fee_bps - min_fee_bps) * (1.0 - t.powf(k))).round(); + } + + let mut referrer_fee = 0.0; + if self.has_been_referred { + fee_bps = fee_bps * (1.0 - REFERRER_PERCENTAGE); + referrer_fee = fee_bps.mul(REFERRER_PERCENTAGE).floor(); + } + + FeePayout { + solauto: (fee_bps - referrer_fee) as u16, + referrer: referrer_fee as u16, + total: fee_bps as u16, + } + } +} diff --git a/programs/solauto/src/rebalance/utils.rs b/programs/solauto/src/rebalance/utils.rs new file mode 100644 index 00000000..0042c5da --- /dev/null +++ b/programs/solauto/src/rebalance/utils.rs @@ -0,0 +1,172 @@ +use solana_program::program_error::ProgramError; + +use crate::{ + state::solauto_position::{ + RebalanceInstructionData, RebalanceStateValues, SolautoPosition, TokenBalanceChange, + TokenBalanceChangeType, + }, + types::{ + errors::SolautoError, + instruction::{RebalanceSettings, SolautoStandardAccounts}, + shared::{RebalanceDirection, RebalanceStep, SolautoRebalanceType, SwapType}, + solauto::{PositionValues, RebalanceFeesBps}, + }, + utils::{ + ix_utils::{ + get_flash_borrow_ix_idx, get_marginfi_flash_loan_amount, + validate_rebalance_instructions, + }, + math_utils::{from_rounded_usd_value, get_debt_adjustment}, + }, +}; + +use super::solauto_fees::SolautoFeesBps; + +pub fn set_rebalance_ixs_data( + std_accounts: &mut Box, + args: &RebalanceSettings, +) -> Result { + let has_rebalance_data = std_accounts.solauto_position.data.rebalance.active(); + + if !has_rebalance_data { + validate_rebalance_instructions(std_accounts, args.rebalance_type)?; + + let fl_borrow_ix_idx = get_flash_borrow_ix_idx(std_accounts, args.rebalance_type)?; + let flash_loan_amount = if fl_borrow_ix_idx.is_some() { + get_marginfi_flash_loan_amount(std_accounts.ixs_sysvar.unwrap(), fl_borrow_ix_idx)? + } else { + 0 + }; + + std_accounts.solauto_position.data.rebalance.ixs = RebalanceInstructionData::from( + args.rebalance_type, + flash_loan_amount, + args.swap_type.unwrap_or(SwapType::default()), + ); + } + + let rebalance_step = if !has_rebalance_data + && matches!( + std_accounts + .solauto_position + .data + .rebalance + .ixs + .rebalance_type, + SolautoRebalanceType::Regular + | SolautoRebalanceType::DoubleRebalanceWithFL + | SolautoRebalanceType::FLRebalanceThenSwap + ) { + RebalanceStep::PreSwap + } else { + RebalanceStep::PostSwap + }; + + Ok(rebalance_step) +} + +pub fn eligible_for_rebalance(solauto_position: &Box) -> bool { + // TODO: DCA, limit orders, take profit, stop loss, etc. + + solauto_position.state.liq_utilization_rate_bps <= solauto_position.boost_from_bps() + || solauto_position.state.liq_utilization_rate_bps >= solauto_position.repay_from_bps() +} + +fn get_target_liq_utilization_rate_bps( + solauto_position: &Box, + rebalance_args: &RebalanceSettings, + token_balance_change: &Option, +) -> Result { + if rebalance_args.target_liq_utilization_rate_bps.is_some() { + return Ok(rebalance_args.target_liq_utilization_rate_bps.unwrap()); + } + + if solauto_position.state.liq_utilization_rate_bps >= solauto_position.repay_from_bps() { + return Ok(solauto_position.position.settings.repay_to_bps); + } else if solauto_position.state.liq_utilization_rate_bps <= solauto_position.boost_from_bps() { + return Ok(solauto_position.position.settings.boost_to_bps); + } else if token_balance_change.is_some() { + // TODO: DCA, limit orders, take profit, stop loss, etc. + return Ok(solauto_position.state.liq_utilization_rate_bps); + } + + Err(SolautoError::InvalidRebalanceCondition.into()) +} + +fn get_token_balance_change() -> Option { + // TODO: DCA, limit orders, take profit, stop loss, etc. + None +} + +fn get_adjusted_position_values( + solauto_position: &Box, + token_balance_change: &Option, +) -> PositionValues { + let mut supply_usd = solauto_position.state.supply.amount_used.usd_value(); + let debt_usd = solauto_position.state.debt.amount_used.usd_value(); + + if token_balance_change.is_some() { + let tb = token_balance_change.as_ref().unwrap(); + match tb.change_type { + TokenBalanceChangeType::PreSwapDeposit | TokenBalanceChangeType::PostSwapDeposit => { + supply_usd += from_rounded_usd_value(tb.amount_usd); + } + TokenBalanceChangeType::PostRebalanceWithdrawDebtToken + | TokenBalanceChangeType::PostRebalanceWithdrawSupplyToken => { + supply_usd -= from_rounded_usd_value(tb.amount_usd); + } + _ => {} + } + } + + return PositionValues { + supply_usd, + debt_usd, + }; +} + +fn get_rebalance_direction( + solauto_position: &Box, + target_ltv_bps: u16, +) -> RebalanceDirection { + if solauto_position.state.liq_utilization_rate_bps < target_ltv_bps { + RebalanceDirection::Boost + } else { + RebalanceDirection::Repay + } +} + +pub fn get_rebalance_values( + solauto_position: &Box, + rebalance_args: &RebalanceSettings, + solauto_fees_bps: &SolautoFeesBps, +) -> Result { + let token_balance_change = get_token_balance_change(); + let target_liq_utilization_rate_bps = get_target_liq_utilization_rate_bps( + solauto_position, + rebalance_args, + &token_balance_change, + )?; + let rebalance_direction = + get_rebalance_direction(solauto_position, target_liq_utilization_rate_bps); + let position = get_adjusted_position_values(solauto_position, &token_balance_change); + let fees = RebalanceFeesBps { + solauto: solauto_fees_bps.fetch_fees(&rebalance_direction).total, + lp_borrow: solauto_position.state.debt.borrow_fee_bps, + flash_loan: rebalance_args.flash_loan_fee_bps.unwrap_or(0), + }; + + let debt_adjustment = get_debt_adjustment( + solauto_position.state.liq_threshold_bps, + &position, + target_liq_utilization_rate_bps, + &fees, + ); + + return Ok(RebalanceStateValues::from( + rebalance_direction, + debt_adjustment.end_result.supply_usd, + debt_adjustment.end_result.debt_usd, + token_balance_change, + )); +} diff --git a/programs/solauto/src/state/automation.rs b/programs/solauto/src/state/automation.rs new file mode 100644 index 00000000..e6a2f168 --- /dev/null +++ b/programs/solauto/src/state/automation.rs @@ -0,0 +1,180 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use bytemuck::{Pod, Zeroable}; +use num_traits::{FromPrimitive, ToPrimitive}; +use shank::ShankType; +use std::{ + cmp::min, + ops::{Add, Div, Mul, Sub}, +}; + +use crate::types::shared::TokenType; + +#[derive(BorshDeserialize, Clone, Debug, Copy, Default)] +pub struct AutomationSettingsInp { + pub target_periods: u16, + pub periods_passed: u16, + pub unix_start_date: u64, + pub interval_seconds: u64, +} + +#[repr(C, align(8))] +#[derive( + ShankType, BorshSerialize, BorshDeserialize, Clone, Debug, Default, Copy, Pod, Zeroable, +)] +pub struct AutomationSettings { + /// The target number of periods + pub target_periods: u16, + /// How many periods have already passed + pub periods_passed: u16, + _padding1: [u8; 4], + /// The unix timestamp (in seconds) start date of DCA + pub unix_start_date: u64, + /// The interval in seconds between each DCA + pub interval_seconds: u64, + _padding: [u8; 32], +} + +impl AutomationSettings { + pub fn from(args: AutomationSettingsInp) -> Self { + Self { + target_periods: args.target_periods, + periods_passed: args.periods_passed, + unix_start_date: args.unix_start_date, + interval_seconds: args.interval_seconds, + _padding1: [0; 4], + _padding: [0; 32], + } + } + #[inline(always)] + pub fn is_active(&self) -> bool { + self.target_periods > 0 + } + #[inline(always)] + pub fn eligible_for_next_period(&self, curr_unix_timestamp: u64) -> bool { + if self.periods_passed == 0 { + curr_unix_timestamp >= self.unix_start_date + } else { + curr_unix_timestamp + >= self + .unix_start_date + .add(self.interval_seconds.mul(self.periods_passed as u64)) + } + } + pub fn updated_amount_from_automation( + &self, + curr_amt: T, + target_amt: T, + curr_unix_timestamp: u64, + ) -> T { + let curr_amt_f64 = curr_amt.to_f64().unwrap(); + let target_amt_f64 = target_amt.to_f64().unwrap(); + let current_rate_diff = curr_amt_f64 - target_amt_f64; + let progress_pct = (1.0).div( + self.target_periods + .sub(self.new_periods_passed(curr_unix_timestamp) - 1) as f64, + ); + let new_amt = curr_amt_f64 - current_rate_diff * progress_pct; + + T::from_f64(new_amt).unwrap() + } + #[inline(always)] + pub fn new_periods_passed(&self, curr_unix_timestamp: u64) -> u16 { + min( + self.target_periods, + (((curr_unix_timestamp.saturating_sub(self.unix_start_date) as f64) + / (self.interval_seconds as f64)) + .floor() as u16) + + 1, + ) + } +} + +#[derive(BorshDeserialize, Clone, Debug, Copy, Default)] +pub struct DCASettingsInp { + pub automation: AutomationSettingsInp, + pub dca_in_base_unit: u64, + pub token_type: TokenType, +} + +#[repr(C, align(8))] +#[derive( + ShankType, BorshSerialize, BorshDeserialize, Clone, Debug, Default, Copy, Pod, Zeroable, +)] +pub struct DCASettings { + pub automation: AutomationSettings, + // Gradually add more to the position during the DCA period. If this is 0, then a DCA-out is assumed. + pub dca_in_base_unit: u64, + pub token_type: TokenType, + _padding: [u8; 31], +} + +impl DCASettings { + pub fn from(args: DCASettingsInp) -> Self { + Self { + automation: AutomationSettings::from(args.automation), + dca_in_base_unit: args.dca_in_base_unit, + token_type: args.token_type, + _padding: [0; 31], + } + } + #[inline(always)] + pub fn dca_in(&self) -> bool { + self.dca_in_base_unit > 0 + } + #[inline(always)] + pub fn is_active(&self) -> bool { + self.automation.is_active() + } +} + +mod tests { + use super::*; + + #[test] + fn validate_period_eligibility() { + let automation = AutomationSettings::from(AutomationSettingsInp { + target_periods: 4, + periods_passed: 3, + unix_start_date: 0, + interval_seconds: 5, + }); + + assert!(automation.eligible_for_next_period(2 * 5 + 4) == false); + assert!(automation.eligible_for_next_period(3 * 5) == true); + } + + #[test] + fn validate_new_periods_passed() { + let automation = AutomationSettings::from(AutomationSettingsInp { + target_periods: 4, + periods_passed: 0, + unix_start_date: 0, + interval_seconds: 5, + }); + + assert!(automation.new_periods_passed(0) == 1); + assert!(automation.new_periods_passed(4 * 5) == 4); + assert!(automation.new_periods_passed(5 * 5) == 4); + } + + #[test] + fn validate_updated_automation_value() { + let mut automation = AutomationSettings::from(AutomationSettingsInp { + target_periods: 4, + periods_passed: 0, + unix_start_date: 0, + interval_seconds: 5, + }); + + assert!(automation.updated_amount_from_automation(10.0, 0.0, 0) == 7.5); + + automation.periods_passed = 1; + assert!(automation.updated_amount_from_automation(7.5, 0.0, 5) == 5.0); + + automation.periods_passed = 2; + assert!(automation.updated_amount_from_automation(5.0, 0.0, 10) == 2.5); + + automation.periods_passed = 3; + assert!(automation.updated_amount_from_automation(2.5, 0.0, 15) == 0.0); + } +} diff --git a/programs/solauto/src/state/mod.rs b/programs/solauto/src/state/mod.rs index 0f2f9df5..052e4f7e 100644 --- a/programs/solauto/src/state/mod.rs +++ b/programs/solauto/src/state/mod.rs @@ -1,2 +1,3 @@ +pub mod automation; pub mod referral_state; pub mod solauto_position; diff --git a/programs/solauto/src/state/referral_state.rs b/programs/solauto/src/state/referral_state.rs index 7aa2e445..0b4da833 100644 --- a/programs/solauto/src/state/referral_state.rs +++ b/programs/solauto/src/state/referral_state.rs @@ -49,6 +49,9 @@ impl ReferralState { seeds.push(&self.bump); seeds } + pub fn is_referred(&self) -> bool { + self.referred_by_state != Pubkey::default() + } } mod tests { diff --git a/programs/solauto/src/state/solauto_position.rs b/programs/solauto/src/state/solauto_position.rs index 7ddc3e46..d9ded2cd 100644 --- a/programs/solauto/src/state/solauto_position.rs +++ b/programs/solauto/src/state/solauto_position.rs @@ -1,103 +1,23 @@ use borsh::{BorshDeserialize, BorshSerialize}; use bytemuck::{Pod, Zeroable}; -use num_traits::{FromPrimitive, ToPrimitive}; use shank::{ShankAccount, ShankType}; use solana_program::{msg, pubkey::Pubkey}; -use std::{ - cmp::min, - ops::{Add, Div, Mul, Sub}, -}; +use std::{cmp::min, ops::Mul}; use crate::{ constants::USD_DECIMALS, - types::shared::{PodBool, PositionType, RebalanceDirection, TokenType}, + derive_pod_traits, + types::shared::{ + LendingPlatform, PodBool, PositionType, RebalanceDirection, SolautoRebalanceType, SwapType, + TokenType, + }, utils::math_utils::{ - from_base_unit, from_bps, get_liq_utilization_rate_bps, net_worth_base_amount, to_base_unit, + base_unit_to_usd_value, from_bps, from_rounded_usd_value, get_liq_utilization_rate_bps, + get_max_boost_to_bps, get_max_repay_from_bps, get_max_repay_to_bps, net_worth_base_amount, + to_base_unit, to_rounded_usd_value, }, }; -use crate::types::shared::LendingPlatform; - -#[derive(BorshDeserialize, Clone, Debug, Copy, Default)] -pub struct AutomationSettingsInp { - pub target_periods: u16, - pub periods_passed: u16, - pub unix_start_date: u64, - pub interval_seconds: u64, -} - -#[repr(C, align(8))] -#[derive( - ShankType, BorshSerialize, BorshDeserialize, Clone, Debug, Default, Copy, Pod, Zeroable, -)] -pub struct AutomationSettings { - /// The target number of periods - pub target_periods: u16, - /// How many periods have already passed - pub periods_passed: u16, - _padding1: [u8; 4], - /// The unix timestamp (in seconds) start date of DCA - pub unix_start_date: u64, - /// The interval in seconds between each DCA - pub interval_seconds: u64, - _padding: [u8; 32], -} - -impl AutomationSettings { - pub fn from(args: AutomationSettingsInp) -> Self { - Self { - target_periods: args.target_periods, - periods_passed: args.periods_passed, - unix_start_date: args.unix_start_date, - interval_seconds: args.interval_seconds, - _padding1: [0; 4], - _padding: [0; 32], - } - } - #[inline(always)] - pub fn is_active(&self) -> bool { - self.target_periods > 0 - } - #[inline(always)] - pub fn eligible_for_next_period(&self, curr_unix_timestamp: u64) -> bool { - if self.periods_passed == 0 { - curr_unix_timestamp >= self.unix_start_date - } else { - curr_unix_timestamp - >= self - .unix_start_date - .add(self.interval_seconds.mul(self.periods_passed as u64)) - } - } - pub fn updated_amount_from_automation( - &self, - curr_amt: T, - target_amt: T, - curr_unix_timestamp: u64, - ) -> T { - let curr_amt_f64 = curr_amt.to_f64().unwrap(); - let target_amt_f64 = target_amt.to_f64().unwrap(); - let current_rate_diff = curr_amt_f64 - target_amt_f64; - let progress_pct = (1.0).div( - self.target_periods - .sub(self.new_periods_passed(curr_unix_timestamp) - 1) as f64, - ); - let new_amt = curr_amt_f64 - current_rate_diff * progress_pct; - - T::from_f64(new_amt).unwrap() - } - #[inline(always)] - pub fn new_periods_passed(&self, curr_unix_timestamp: u64) -> u16 { - min( - self.target_periods, - (((curr_unix_timestamp.saturating_sub(self.unix_start_date) as f64) - / (self.interval_seconds as f64)) - .floor() as u16) - + 1, - ) - } -} - #[repr(C, align(8))] #[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] pub struct TokenAmount { @@ -109,37 +29,36 @@ pub struct TokenAmount { impl TokenAmount { #[inline(always)] pub fn usd_value(&self) -> f64 { - from_base_unit::(self.base_amount_usd_value, USD_DECIMALS) + from_rounded_usd_value(self.base_amount_usd_value) } pub fn update_usd_value(&mut self, market_price: f64, token_decimals: u8) { - self.base_amount_usd_value = to_base_unit::( - from_base_unit::(self.base_unit, token_decimals).mul(market_price), - USD_DECIMALS, - ); + self.base_amount_usd_value = to_rounded_usd_value(base_unit_to_usd_value( + self.base_unit, + token_decimals, + market_price, + )); } } #[repr(C, align(8))] #[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] -pub struct PositionTokenUsage { +pub struct PositionTokenState { pub mint: Pubkey, pub decimals: u8, - _padding1: [u8; 7], + _padding1: [u8; 5], + pub borrow_fee_bps: u16, pub amount_used: TokenAmount, pub amount_can_be_used: TokenAmount, /// Denominated by 9 decimal places base_amount_market_price_usd: u64, - // TODO: Flash loan fees are currently not considered in debt adjustment calculations - pub flash_loan_fee_bps: u16, - pub borrow_fee_bps: u16, - _padding2: [u8; 4], + _padding2: [u8; 8], _padding: [u8; 32], } -impl PositionTokenUsage { +impl PositionTokenState { #[inline(always)] pub fn market_price(&self) -> f64 { - from_base_unit::(self.base_amount_market_price_usd, USD_DECIMALS) + from_rounded_usd_value(self.base_amount_market_price_usd) } fn update_usd_values(&mut self) { self.amount_used @@ -171,58 +90,17 @@ impl PositionTokenUsage { } pub fn update_market_price(&mut self, market_price: f64) { msg!("New {} price: {}", self.mint, market_price); - self.base_amount_market_price_usd = - to_base_unit::(market_price, USD_DECIMALS); + self.base_amount_market_price_usd = to_base_unit(market_price, USD_DECIMALS); self.update_usd_values(); } } -#[derive(BorshDeserialize, Clone, Debug, Copy, Default)] -pub struct DCASettingsInp { - pub automation: AutomationSettingsInp, - pub dca_in_base_unit: u64, - pub token_type: TokenType, -} - -#[repr(C, align(8))] -#[derive( - ShankType, BorshSerialize, BorshDeserialize, Clone, Debug, Default, Copy, Pod, Zeroable, -)] -pub struct DCASettings { - pub automation: AutomationSettings, - // Gradually add more to the position during the DCA period. If this is 0, then a DCA-out is assumed. - pub dca_in_base_unit: u64, - pub token_type: TokenType, - _padding: [u8; 31], -} - -impl DCASettings { - pub fn from(args: DCASettingsInp) -> Self { - Self { - automation: AutomationSettings::from(args.automation), - dca_in_base_unit: args.dca_in_base_unit, - token_type: args.token_type, - _padding: [0; 31], - } - } - #[inline(always)] - pub fn dca_in(&self) -> bool { - self.dca_in_base_unit > 0 - } - #[inline(always)] - pub fn is_active(&self) -> bool { - self.automation.is_active() - } -} - #[derive(BorshDeserialize, Clone, Debug, Copy, Default)] pub struct SolautoSettingsParametersInp { pub boost_to_bps: u16, pub boost_gap: u16, pub repay_to_bps: u16, pub repay_gap: u16, - pub target_boost_to_bps: Option, - pub automation: Option, } #[repr(C, align(8))] @@ -238,45 +116,19 @@ pub struct SolautoSettingsParameters { pub repay_to_bps: u16, /// repay_gap basis points above repay_to_bps is the liquidation utilization rate at which to begin a rebalance pub repay_gap: u16, - /// If slowly adjusting the boost_to_bps with automation, this must be set - pub target_boost_to_bps: u16, - _padding1: [u8; 6], - /// Data required if providing a target_boost_to_bps - pub automation: AutomationSettings, - _padding: [u8; 32], + _padding: [u32; 24], } impl SolautoSettingsParameters { pub fn from(args: SolautoSettingsParametersInp) -> Self { - let target_boost_to_bps = if args.target_boost_to_bps.is_some() { - args.target_boost_to_bps.unwrap() - } else { - 0 - }; - let automation = if args.automation.is_some() { - AutomationSettings::from(args.automation.unwrap()) - } else { - AutomationSettings::default() - }; Self { boost_to_bps: args.boost_to_bps, boost_gap: args.boost_gap, repay_to_bps: args.repay_to_bps, repay_gap: args.repay_gap, - target_boost_to_bps, - automation, - _padding1: [0; 6], - _padding: [0; 32], + _padding: [0; 24], } } - #[inline(always)] - pub fn boost_from_bps(&self) -> u16 { - self.boost_to_bps.saturating_sub(self.boost_gap) - } - #[inline(always)] - pub fn repay_from_bps(&self) -> u16 { - self.repay_to_bps.add(self.repay_gap) - } } #[repr(C, align(8))] @@ -287,13 +139,13 @@ pub struct PositionState { /// Denominated by 9 decimal places pub net_worth: TokenAmount, - pub supply: PositionTokenUsage, - pub debt: PositionTokenUsage, + pub supply: PositionTokenState, + pub debt: PositionTokenState, pub max_ltv_bps: u16, pub liq_threshold_bps: u16, _padding2: [u8; 4], - pub last_updated: u64, + pub last_refreshed: u64, _padding: [u32; 2], } @@ -302,42 +154,118 @@ pub struct PositionState { pub struct PositionData { pub lending_platform: LendingPlatform, _padding1: [u8; 7], - pub protocol_user_account: Pubkey, - pub protocol_supply_account: Pubkey, - pub protocol_debt_account: Pubkey, - pub setting_params: SolautoSettingsParameters, - pub dca: DCASettings, - _padding: [u32; 4], + pub lp_user_account: Pubkey, + pub lp_supply_account: Pubkey, + pub lp_debt_account: Pubkey, + pub settings: SolautoSettingsParameters, + pub lp_pool_account: Pubkey, + _padding: [u32; 20], } #[repr(u8)] -#[derive(ShankType, BorshDeserialize, BorshSerialize, Clone, Debug, Default, PartialEq, Copy)] -pub enum SolautoRebalanceType { +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub enum TokenBalanceChangeType { #[default] None, - Regular, - DoubleRebalanceWithFL, - SingleRebalanceWithFL, + PreSwapDeposit, + PostSwapDeposit, + PostRebalanceWithdrawSupplyToken, + PostRebalanceWithdrawDebtToken, } - -unsafe impl Zeroable for SolautoRebalanceType {} -unsafe impl Pod for SolautoRebalanceType {} +derive_pod_traits!(TokenBalanceChangeType); #[repr(C, align(8))] #[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] -pub struct RebalanceData { - pub rebalance_type: SolautoRebalanceType, +pub struct TokenBalanceChange { + pub change_type: TokenBalanceChangeType, _padding1: [u8; 7], + // Denominated in 9 decimal places + pub amount_usd: u64, +} + +impl TokenBalanceChange { + pub fn requires_one(&self) -> bool { + self.change_type != TokenBalanceChangeType::None + } +} + +#[repr(C, align(8))] +#[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] +pub struct RebalanceStateValues { pub rebalance_direction: RebalanceDirection, - _padding2: [u8; 7], + _padding1: [u8; 7], + // Denominated in 9 decimal places + pub target_supply_usd: u64, + // Denominated in 9 decimal places + pub target_debt_usd: u64, + pub token_balance_change: TokenBalanceChange, + _padding: [u32; 4], +} + +impl RebalanceStateValues { + pub fn from( + rebalance_direction: RebalanceDirection, + target_supply_usd: f64, + target_debt_usd: f64, + token_balance_change: Option, + ) -> Self { + let tb_change = if token_balance_change.is_some() { + token_balance_change.unwrap() + } else { + TokenBalanceChange::default() + }; + Self { + rebalance_direction, + _padding1: [0; 7], + target_supply_usd: to_rounded_usd_value(target_supply_usd), + target_debt_usd: to_rounded_usd_value(target_debt_usd), + token_balance_change: tb_change, + _padding: [0; 4], + } + } +} + +#[repr(C, align(8))] +#[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] +pub struct RebalanceInstructionData { + pub active: PodBool, + pub rebalance_type: SolautoRebalanceType, + pub swap_type: SwapType, + _padding1: [u8; 5], pub flash_loan_amount: u64, - _padding: [u8; 32], + _padding: [u32; 4], +} +impl RebalanceInstructionData { + pub fn from( + rebalance_type: SolautoRebalanceType, + flash_loan_amount: u64, + swap_type: SwapType, + ) -> Self { + Self { + active: PodBool::new(true), + rebalance_type, + swap_type, + _padding1: [0; 5], + flash_loan_amount, + _padding: [0; 4], + } + } +} + +#[repr(C, align(8))] +#[derive(ShankType, BorshSerialize, Clone, Debug, Default, Copy, Pod, Zeroable)] +pub struct RebalanceData { + pub ixs: RebalanceInstructionData, + pub values: RebalanceStateValues, + _padding: [u32; 4], } impl RebalanceData { - #[inline(always)] pub fn active(&self) -> bool { - self.rebalance_type != SolautoRebalanceType::None + self.ixs.active.val + } + pub fn values_set(&self) -> bool { + self.values.rebalance_direction != RebalanceDirection::None } } @@ -353,11 +281,12 @@ pub struct SolautoPosition { pub position: PositionData, pub state: PositionState, pub rebalance: RebalanceData, - _padding: [u32; 32], + _padding: [u32; 20], } impl SolautoPosition { pub const LEN: usize = 832; + pub fn new( position_id: u8, authority: Pubkey, @@ -377,23 +306,33 @@ impl SolautoPosition { position, state, rebalance: RebalanceData::default(), - _padding: [0; 32], + _padding: [0; 20], } } + + #[inline(always)] + pub fn pubkey(&self) -> Pubkey { + let (pubkey, _) = Pubkey::find_program_address(self.seeds().as_ref(), &crate::ID); + pubkey + } + #[inline(always)] pub fn position_id(&self) -> u8 { self.position_id[0] } + #[inline(always)] pub fn seeds<'a>(&'a self) -> Vec<&'a [u8]> { vec![&self.position_id, self.authority.as_ref()] } + #[inline(always)] pub fn seeds_with_bump<'a>(&'a self) -> Vec<&'a [u8]> { let mut seeds = self.seeds(); seeds.push(&self.bump); seeds } + pub fn refresh_state(&mut self) { let supply_usd = self.state.supply.amount_used.usd_value(); let debt_usd = self.state.debt.amount_used.usd_value(); @@ -414,12 +353,14 @@ impl SolautoPosition { .net_worth .update_usd_value(self.state.supply.market_price(), self.state.supply.decimals); msg!( - "New liquidation utilization rate: {}, (${}, ${})", + "New liquidation utilization rate: {}, (${}, ${}). Max: {}", self.state.liq_utilization_rate_bps, supply_usd, - debt_usd + debt_usd, + get_max_boost_to_bps(self.state.max_ltv_bps, self.state.liq_threshold_bps) ); } + pub fn update_usage(&mut self, token_type: TokenType, base_unit_amount_update: i64) { if token_type == TokenType::Supply { self.state.supply.update_usage(base_unit_amount_update); @@ -436,6 +377,36 @@ impl SolautoPosition { msg!("Supply USD < debt USD"); } } + + #[inline(always)] + pub fn boost_to_bps(&self) -> u16 { + min( + self.position.settings.boost_to_bps, + get_max_boost_to_bps(self.state.max_ltv_bps, self.state.liq_threshold_bps), + ) + } + + #[inline(always)] + pub fn boost_from_bps(&self) -> u16 { + self.boost_to_bps() + .saturating_sub(self.position.settings.boost_gap) + } + + #[inline(always)] + pub fn repay_to_bps(&self) -> u16 { + min( + self.position.settings.repay_to_bps, + get_max_repay_to_bps(self.state.max_ltv_bps, self.state.liq_threshold_bps), + ) + } + + #[inline(always)] + pub fn repay_from_bps(&self) -> u16 { + min( + self.position.settings.repay_to_bps + self.position.settings.repay_gap, + get_max_repay_from_bps(self.state.max_ltv_bps, self.state.liq_threshold_bps), + ) + } } mod tests { @@ -456,52 +427,4 @@ mod tests { ); assert!(std::mem::size_of_val(&solauto_position) == SolautoPosition::LEN); } - - #[test] - fn validate_period_eligibility() { - let automation = AutomationSettings::from(AutomationSettingsInp { - target_periods: 4, - periods_passed: 3, - unix_start_date: 0, - interval_seconds: 5, - }); - - assert!(automation.eligible_for_next_period(2 * 5 + 4) == false); - assert!(automation.eligible_for_next_period(3 * 5) == true); - } - - #[test] - fn validate_new_periods_passed() { - let automation = AutomationSettings::from(AutomationSettingsInp { - target_periods: 4, - periods_passed: 0, - unix_start_date: 0, - interval_seconds: 5, - }); - - assert!(automation.new_periods_passed(0) == 1); - assert!(automation.new_periods_passed(4 * 5) == 4); - assert!(automation.new_periods_passed(5 * 5) == 4); - } - - #[test] - fn validate_updated_automation_value() { - let mut automation = AutomationSettings::from(AutomationSettingsInp { - target_periods: 4, - periods_passed: 0, - unix_start_date: 0, - interval_seconds: 5, - }); - - assert!(automation.updated_amount_from_automation(10.0, 0.0, 0) == 7.5); - - automation.periods_passed = 1; - assert!(automation.updated_amount_from_automation(7.5, 0.0, 5) == 5.0); - - automation.periods_passed = 2; - assert!(automation.updated_amount_from_automation(5.0, 0.0, 10) == 2.5); - - automation.periods_passed = 3; - assert!(automation.updated_amount_from_automation(2.5, 0.0, 15) == 0.0); - } } diff --git a/programs/solauto/src/types/errors.rs b/programs/solauto/src/types/errors.rs new file mode 100644 index 00000000..136ce11b --- /dev/null +++ b/programs/solauto/src/types/errors.rs @@ -0,0 +1,42 @@ +use solana_program::program_error::ProgramError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SolautoError { + #[error("Missing or incorrect accounts provided for the given instructions")] + IncorrectAccounts, + #[error("Failed to deserialize account data")] + FailedAccountDeserialization, + #[error("Invalid Boost-to param")] + InvalidBoostToSetting, + #[error("Invalid Boost gap param")] + InvalidBoostGapSetting, + #[error("Invalid repay-to param")] + InvalidRepayToSetting, + #[error("Invalid repay gap param")] + InvalidRepayGapSetting, + #[error("Invalid repay-from (repay-to + repay gap)")] + InvalidRepayFromSetting, + #[error("Invalid DCA configuration provided")] + InvalidDCASettings, + #[error("Invalid automation settings provided")] + InvalidAutomationData, + #[error("Invalid position condition to rebalance")] + InvalidRebalanceCondition, + #[error("Unable to invoke instruction through a CPI")] + InstructionIsCPI, + #[error("Incorrect set of instructions or instruction data in the transaction")] + IncorrectInstructions, + #[error("Incorrect swap amount provided. Likely due to high price volatility")] + IncorrectDebtAdjustment, + #[error("Invalid rebalance was made. Target supply USD and target debt USD was not met")] + InvalidRebalanceMade, + #[error("Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority")] + NonAuthorityProvidedTargetLTV, +} + +impl From for ProgramError { + fn from(e: SolautoError) -> Self { + ProgramError::Custom(e as u32) + } +} diff --git a/programs/solauto/src/types/instruction.rs b/programs/solauto/src/types/instruction.rs index ac86d0b7..aee070b2 100644 --- a/programs/solauto/src/types/instruction.rs +++ b/programs/solauto/src/types/instruction.rs @@ -2,11 +2,13 @@ use borsh::BorshDeserialize; use shank::{ShankContext, ShankInstruction, ShankType}; use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; -use crate::state::{ - referral_state::ReferralState, - solauto_position::{ - DCASettingsInp, SolautoPosition, SolautoRebalanceType, SolautoSettingsParametersInp, +use crate::{ + state::{ + automation::DCASettingsInp, + referral_state::ReferralState, + solauto_position::{SolautoPosition, SolautoSettingsParametersInp}, }, + types::shared::SolautoRebalanceType, }; use super::shared::*; @@ -45,7 +47,7 @@ pub enum Instruction { #[account(name = "referral_state")] #[account(mut, name = "referral_fees_dest_ta")] #[account(name = "referral_fees_dest_mint")] - #[account(mut, optional, name = "referral_authority")] + #[account(mut, name = "referral_authority")] #[account(mut, optional, name = "fees_destination_ta")] ClaimReferralFees, @@ -65,7 +67,7 @@ pub enum Instruction { #[account(name = "token_program")] #[account(name = "ata_program")] #[account(mut, name = "solauto_position")] - #[account(mut, name = "protocol_account")] + #[account(mut, name = "lp_user_account")] #[account(mut, name = "position_supply_ta")] #[account(mut, name = "signer_supply_ta")] #[account(mut, name = "position_debt_ta")] @@ -115,7 +117,7 @@ pub enum Instruction { #[account(mut, name = "debt_bank")] #[account(name = "debt_price_oracle")] #[account(mut, name = "solauto_position")] - MarginfiRefreshData, + MarginfiRefreshData(PriceType), /// Marginfi protocol interaction. Can only be invoked by the authority of the position #[account(signer, name = "signer")] @@ -125,7 +127,7 @@ pub enum Instruction { #[account(name = "ata_program")] #[account(name = "rent")] #[account(mut, name = "solauto_position")] - #[account(name = "marginfi_group")] + #[account(mut, name = "marginfi_group")] #[account(mut, name = "marginfi_account")] #[account(mut, name = "supply_bank")] #[account(optional, name = "supply_price_oracle")] @@ -150,7 +152,7 @@ pub enum Instruction { #[account(mut, optional, name = "referred_by_ta")] #[account(mut, optional, name = "position_authority")] #[account(mut, name = "solauto_position")] - #[account(name = "marginfi_group")] + #[account(mut, name = "marginfi_group")] #[account(mut, name = "marginfi_account")] #[account(optional, mut, name = "intermediary_ta")] #[account(mut, name = "supply_bank")] @@ -182,8 +184,6 @@ pub struct UpdateReferralStatesArgs { pub struct MarginfiOpenPositionData { pub position_type: PositionType, pub position_data: UpdatePositionData, - /// Marginfi account seed index if the position is Solauto-managed - pub marginfi_account_seed_idx: Option, } #[derive(BorshDeserialize, Clone, Debug)] @@ -191,7 +191,7 @@ pub struct UpdatePositionData { /// ID of the Solauto position pub position_id: u8, /// Setting parameters for the position - pub setting_params: Option, + pub settings: Option, /// New DCA data to initiate on the position pub dca: Option, } @@ -211,10 +211,13 @@ pub enum SolautoAction { #[derive(BorshDeserialize, Clone, Debug, Default, ShankType)] pub struct RebalanceSettings { pub rebalance_type: SolautoRebalanceType, + /// The in-amount to use in the token swap. Gets validated by the program. + pub swap_in_amount_base_unit: Option, /// Target liq utilization rate. Only used/allowed if signed by the position authority. pub target_liq_utilization_rate_bps: Option, - /// The amount to use in the token swap. Gets validated by the program. - pub target_in_amount_base_unit: Option, + pub flash_loan_fee_bps: Option, + pub price_type: Option, + pub swap_type: Option, } pub struct SolautoStandardAccounts<'a> { diff --git a/programs/solauto/src/types/mod.rs b/programs/solauto/src/types/mod.rs index b676354a..c474fb06 100644 --- a/programs/solauto/src/types/mod.rs +++ b/programs/solauto/src/types/mod.rs @@ -1,4 +1,6 @@ +pub mod errors; pub mod instruction; pub mod lending_protocol; pub mod shared; +pub mod solauto; pub mod solauto_manager; diff --git a/programs/solauto/src/types/shared.rs b/programs/solauto/src/types/shared.rs index 922e8b86..c2e31943 100644 --- a/programs/solauto/src/types/shared.rs +++ b/programs/solauto/src/types/shared.rs @@ -11,37 +11,18 @@ use solana_program::{ program_pack::{IsInitialized, Pack}, }; use std::fmt; -use thiserror::Error; + +use crate::derive_pod_traits; + +use super::errors::SolautoError; #[repr(u8)] -#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, PartialEq, Copy)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] pub enum LendingPlatform { + #[default] Marginfi, } - -unsafe impl Zeroable for LendingPlatform {} -unsafe impl Pod for LendingPlatform {} - -impl Default for LendingPlatform { - fn default() -> Self { - LendingPlatform::Marginfi - } -} - -#[repr(C)] -#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, PartialEq, Copy)] -pub struct PodBool { - pub val: bool, -} - -unsafe impl Zeroable for PodBool {} -unsafe impl Pod for PodBool {} - -impl PodBool { - pub fn new(val: bool) -> Self { - Self { val } - } -} +derive_pod_traits!(LendingPlatform); #[repr(u8)] #[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] @@ -50,9 +31,7 @@ pub enum PositionType { Leverage, SafeLoan, } - -unsafe impl Zeroable for PositionType {} -unsafe impl Pod for PositionType {} +derive_pod_traits!(PositionType); #[repr(u8)] #[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] @@ -61,9 +40,7 @@ pub enum TokenType { Supply, Debt, } - -unsafe impl Zeroable for TokenType {} -unsafe impl Pod for TokenType {} +derive_pod_traits!(TokenType); impl fmt::Display for TokenType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -75,18 +52,46 @@ impl fmt::Display for TokenType { } #[repr(u8)] -#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, Default, PartialEq, Copy)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] pub enum RebalanceDirection { #[default] + None, Boost, Repay, } +derive_pod_traits!(RebalanceDirection); -unsafe impl Zeroable for RebalanceDirection {} -unsafe impl Pod for RebalanceDirection {} +#[repr(u8)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub enum RebalanceStep { + #[default] + PreSwap, + PostSwap, +} +derive_pod_traits!(RebalanceStep); + +#[repr(u8)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub enum SolautoRebalanceType { + #[default] + Regular, + DoubleRebalanceWithFL, + FLSwapThenRebalance, + FLRebalanceThenSwap, +} +derive_pod_traits!(SolautoRebalanceType); + +#[repr(u8)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub enum SwapType { + #[default] + ExactIn, + ExactOut, +} +derive_pod_traits!(SwapType); #[derive(Debug)] -pub struct RefreshedTokenData { +pub struct RefreshedTokenState { pub mint: Pubkey, pub decimals: u8, pub amount_used: u64, @@ -99,8 +104,8 @@ pub struct RefreshedTokenData { pub struct RefreshStateProps { pub max_ltv: f64, pub liq_threshold: f64, - pub supply: RefreshedTokenData, - pub debt: RefreshedTokenData, + pub supply: RefreshedTokenState, + pub debt: RefreshedTokenState, } #[derive(BorshDeserialize, BorshSerialize, Clone, Debug, PartialEq)] @@ -109,10 +114,34 @@ pub enum TokenBalanceAmount { All, } -#[derive(Debug, PartialEq)] -pub enum RebalanceStep { - Initial, - Final, +pub struct SplTokenTransferArgs<'a, 'b> { + pub source: &'a AccountInfo<'a>, + pub authority: &'a AccountInfo<'a>, + pub recipient: &'a AccountInfo<'a>, + pub amount: u64, + pub authority_seeds: Option<&'b Vec<&'b [u8]>>, +} + +#[repr(C)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub struct PodBool { + pub val: bool, +} + +derive_pod_traits!(PodBool); + +impl PodBool { + pub fn new(val: bool) -> Self { + Self { val } + } +} + +#[repr(u8)] +#[derive(BorshDeserialize, BorshSerialize, Clone, Debug, ShankType, Default, PartialEq, Copy)] +pub enum PriceType { + #[default] + Realtime, + Ema, } #[derive(Clone)] @@ -151,30 +180,4 @@ impl<'a, T: Pack + IsInitialized> DeserializedAccount<'a, T> { } } -#[derive(Error, Debug)] -pub enum SolautoError { - #[error("Missing or incorrect accounts provided for the given instruction")] - IncorrectAccounts, - #[error("Failed to deserialize account data")] - FailedAccountDeserialization, - #[error("Invalid position settings provided")] - InvalidPositionSettings, - #[error("Invalid DCA configuration provided")] - InvalidDCASettings, - #[error("Invalid automation settings provided")] - InvalidAutomationData, - #[error("Invalid position condition to rebalance")] - InvalidRebalanceCondition, - #[error("Unable to invoke instruction through a CPI")] - InstructionIsCPI, - #[error("Incorrect set of instructions in the transaction")] - IncorrectInstructions, - #[error("Incorrect swap amount provided. Likely due to high price volatility")] - IncorrectDebtAdjustment, -} - -impl From for ProgramError { - fn from(e: SolautoError) -> Self { - ProgramError::Custom(e as u32) - } -} +pub type AccountMetaFlags<'a> = (&'a AccountInfo<'a>, bool, bool); diff --git a/programs/solauto/src/types/solauto.rs b/programs/solauto/src/types/solauto.rs new file mode 100644 index 00000000..83602ba1 --- /dev/null +++ b/programs/solauto/src/types/solauto.rs @@ -0,0 +1,55 @@ +use super::shared::TokenBalanceAmount; + +#[derive(Clone, Copy, Eq, PartialEq, Hash)] +pub enum SolautoAccount { + SolautoPosition, + SolautoPositionSupplyTa, + SolautoPositionDebtTa, + AuthoritySupplyTa, + AuthorityDebtTa, + IntermediaryTa, + SolautoFeesTa, + ReferredByTa, + SupplyMint, + DebtMint, +} + +#[derive(Clone)] +pub struct FromLendingPlatformAction { + pub amount: T, + pub to_wallet_ta: SolautoAccount, +} + +#[derive(Clone)] +pub struct SolautoSplTokenTransferArgs { + pub from_wallet: SolautoAccount, + pub from_wallet_ta: SolautoAccount, + pub to_wallet_ta: SolautoAccount, + pub amount: u64, +} + +#[derive(Clone)] +pub enum SolautoCpiAction { + Deposit(u64), + Borrow(FromLendingPlatformAction), + Repay(TokenBalanceAmount), + Withdraw(FromLendingPlatformAction), + SplTokenTransfer(SolautoSplTokenTransferArgs), +} + +#[derive(Copy, Clone)] +pub struct PositionValues { + pub supply_usd: f64, + pub debt_usd: f64, +} + +pub struct RebalanceFeesBps { + pub solauto: u16, + pub lp_borrow: u16, + pub flash_loan: u16, +} + +pub struct DebtAdjustment { + pub debt_adjustment_usd: f64, + pub end_result: PositionValues, +} diff --git a/programs/solauto/src/types/solauto_manager.rs b/programs/solauto/src/types/solauto_manager.rs index 8d5114a5..19d7e9fb 100644 --- a/programs/solauto/src/types/solauto_manager.rs +++ b/programs/solauto/src/types/solauto_manager.rs @@ -1,25 +1,28 @@ -use math_utils::{from_bps, to_bps}; +use std::collections::HashMap; + +use math_utils::to_bps; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, sysvar::Sysvar, -}; -use spl_associated_token_account::get_associated_token_address; -use std::{ - cmp::min, - ops::{Add, Div, Mul}, + program_error::ProgramError, }; -use validation_utils::validate_debt_adjustment; use super::{ instruction::{RebalanceSettings, SolautoAction, SolautoStandardAccounts}, lending_protocol::{LendingProtocolClient, LendingProtocolTokenAccounts}, - shared::{RebalanceDirection, RefreshStateProps, SolautoError, TokenBalanceAmount, TokenType}, + shared::{ + RebalanceStep, RefreshStateProps, SplTokenTransferArgs, TokenBalanceAmount, TokenType, + }, + solauto::{SolautoAccount, SolautoCpiAction}, }; use crate::{ + check, constants::SOLAUTO_FEES_WALLET, - state::solauto_position::{ - AutomationSettings, RebalanceData, SolautoPosition, SolautoRebalanceType, + rebalance::{ + rebalancer::{Rebalancer, RebalancerData, SolautoPositionData, TokenAccountData}, + solauto_fees::SolautoFeesBps, }, + state::solauto_position::{RebalanceData, SolautoPosition}, + types::errors::SolautoError, utils::*, }; @@ -27,14 +30,14 @@ pub struct SolautoManagerAccounts<'a> { pub supply: LendingProtocolTokenAccounts<'a>, pub debt: LendingProtocolTokenAccounts<'a>, pub intermediary_ta: Option<&'a AccountInfo<'a>>, - pub solauto_fees: Option, + pub solauto_fees: Option, } impl<'a> SolautoManagerAccounts<'a> { pub fn from( supply: LendingProtocolTokenAccounts<'a>, debt: LendingProtocolTokenAccounts<'a>, intermediary_ta: Option<&'a AccountInfo<'a>>, - solauto_fees: Option, + solauto_fees: Option, ) -> Result { Ok(Self { supply, @@ -49,7 +52,7 @@ pub struct SolautoManager<'a> { pub client: Box + 'a>, pub accounts: SolautoManagerAccounts<'a>, pub std_accounts: Box>, - pub solauto_fees_bps: Option, + pub solauto_fees_bps: Option, } impl<'a> SolautoManager<'a> { @@ -57,7 +60,7 @@ impl<'a> SolautoManager<'a> { client: Box + 'a>, accounts: SolautoManagerAccounts<'a>, std_accounts: Box>, - solauto_fees_bps: Option, + solauto_fees_bps: Option, ) -> Result { client.validate(&std_accounts)?; Ok(Self { @@ -68,13 +71,19 @@ impl<'a> SolautoManager<'a> { }) } + fn position_data(&self) -> &Box { + &self.std_accounts.solauto_position.data + } + fn deposit(&mut self, base_unit_amount: u64) -> ProgramResult { + msg!("Depositing {}", base_unit_amount); self.update_usage(base_unit_amount as i64, TokenType::Supply); self.client.deposit(base_unit_amount, &self.std_accounts)?; Ok(()) } fn borrow(&mut self, base_unit_amount: u64, destination: &'a AccountInfo<'a>) -> ProgramResult { + msg!("Borrowing {}", base_unit_amount); self.update_usage(base_unit_amount as i64, TokenType::Debt); self.client .borrow(base_unit_amount, destination, &self.std_accounts)?; @@ -87,18 +96,11 @@ impl<'a> SolautoManager<'a> { destination: &'a AccountInfo<'a>, ) -> ProgramResult { let base_unit_amount = match amount { - TokenBalanceAmount::All => { - self.std_accounts - .solauto_position - .data - .state - .supply - .amount_used - .base_unit - } + TokenBalanceAmount::All => self.position_data().state.supply.amount_used.base_unit, TokenBalanceAmount::Some(num) => num, }; + msg!("Withdrawing {}", base_unit_amount); self.update_usage((base_unit_amount as i64) * -1, TokenType::Supply); self.client .withdraw(amount, destination, &self.std_accounts)?; @@ -107,27 +109,19 @@ impl<'a> SolautoManager<'a> { fn repay(&mut self, amount: TokenBalanceAmount) -> ProgramResult { let base_unit_amount = match amount { - TokenBalanceAmount::All => { - self.std_accounts - .solauto_position - .data - .state - .debt - .amount_used - .base_unit - } + TokenBalanceAmount::All => self.position_data().state.debt.amount_used.base_unit, TokenBalanceAmount::Some(num) => num, }; + msg!("Repaying {}", base_unit_amount); self.update_usage((base_unit_amount as i64) * -1, TokenType::Debt); self.client.repay(amount, &self.std_accounts)?; Ok(()) } fn update_usage(&mut self, base_unit_amount: i64, token_type: TokenType) { - if !self.std_accounts.solauto_position.data.self_managed.val - || self.std_accounts.solauto_position.data.rebalance.active() - { + let position_data = self.position_data(); + if !position_data.self_managed.val || position_data.rebalance.active() { self.std_accounts .solauto_position .data @@ -135,337 +129,119 @@ impl<'a> SolautoManager<'a> { } } - pub fn protocol_interaction(&mut self, action: SolautoAction) -> ProgramResult { - match action { - SolautoAction::Deposit(base_unit_amount) => { - self.deposit(base_unit_amount)?; - } - SolautoAction::Borrow(base_unit_amount) => { - self.borrow( - base_unit_amount, - self.accounts.debt.position_ta.as_ref().unwrap(), - )?; - } - SolautoAction::Repay(amount) => { - self.repay(amount)?; - } - SolautoAction::Withdraw(amount) => { - self.withdraw(amount, self.accounts.supply.position_ta.as_ref().unwrap())?; - } - } - Ok(()) - } - - pub fn begin_rebalance(&mut self, rebalance_args: &RebalanceSettings) -> ProgramResult { - let (debt_adjustment_usd, amount_to_dca_in) = rebalance_utils::get_rebalance_values( - &mut self.std_accounts.solauto_position.data, - rebalance_args, - self.solauto_fees_bps.as_ref().unwrap(), - Clock::get()?.unix_timestamp as u64, - )?; - - if amount_to_dca_in.is_some() { - if self - .std_accounts - .solauto_position - .data - .position - .dca - .token_type - == TokenType::Supply - { - self.deposit(amount_to_dca_in.unwrap())?; - } else { - solana_utils::spl_token_transfer( - self.std_accounts.token_program, - self.accounts.debt.position_ta.as_ref().unwrap(), - self.std_accounts.solauto_position.account_info, - self.accounts.intermediary_ta.unwrap(), - amount_to_dca_in.unwrap(), - Some(&self.std_accounts.solauto_position.data.seeds_with_bump()), - )?; - } - } - - if self - .std_accounts - .solauto_position - .data - .rebalance - .rebalance_type - == SolautoRebalanceType::DoubleRebalanceWithFL - { - validate_debt_adjustment( - &self.std_accounts.solauto_position.data, - self.std_accounts - .solauto_position - .data - .rebalance - .flash_loan_amount, - debt_adjustment_usd, - )?; - return Ok(()); - } - - let increasing_leverage = debt_adjustment_usd > 0.0; - - let token = if increasing_leverage { - Box::new(&self.std_accounts.solauto_position.data.state.debt) - } else { - Box::new(&self.std_accounts.solauto_position.data.state.supply) - }; - - let base_unit_amount = math_utils::to_base_unit::( - debt_adjustment_usd.abs().div(token.market_price()), - token.decimals, - ); - - if increasing_leverage { - let final_amount = if rebalance_args.target_in_amount_base_unit.is_some() { - rebalance_args.target_in_amount_base_unit.unwrap() - } else { - base_unit_amount - }; - self.borrow(final_amount, self.accounts.intermediary_ta.unwrap()) - } else { - let final_amount = if rebalance_args.target_in_amount_base_unit.is_some() { - rebalance_args.target_in_amount_base_unit.unwrap() - } else { - min( - self.std_accounts - .solauto_position - .data - .state - .supply - .amount_used - .base_unit, - base_unit_amount, - ) - }; - self.withdraw( - TokenBalanceAmount::Some(final_amount), - self.accounts.intermediary_ta.unwrap(), - ) - } - } - - pub fn finish_rebalance(&mut self, rebalance_args: &RebalanceSettings) -> ProgramResult { - let rebalance_data = &self.std_accounts.solauto_position.data.rebalance; - let rebalance_type = rebalance_data.rebalance_type; - let flash_loan_amount = rebalance_data.flash_loan_amount; - - if rebalance_type == SolautoRebalanceType::SingleRebalanceWithFL { - let (debt_adjustment_usd, _) = rebalance_utils::get_rebalance_values( - &mut self.std_accounts.solauto_position.data, - rebalance_args, - self.solauto_fees_bps.as_ref().unwrap(), - Clock::get()?.unix_timestamp as u64, - )?; - validate_debt_adjustment( - &self.std_accounts.solauto_position.data, - flash_loan_amount, - debt_adjustment_usd, - )?; - } - - let boosting = self - .std_accounts - .solauto_position - .data - .rebalance - .rebalance_direction - == RebalanceDirection::Boost; - let mut available_balance = if boosting { - solauto_utils::safe_unpack_token_account(self.accounts.supply.position_ta)? + fn get_token_account_data(&self, account: Option<&'a AccountInfo<'a>>) -> TokenAccountData { + TokenAccountData::from( + solauto_utils::safe_unpack_token_account(account) .unwrap() - .data - .amount - } else { - solauto_utils::safe_unpack_token_account(self.accounts.debt.position_ta)? .unwrap() .data - .amount - }; + .amount, + ) + } - if !self.std_accounts.solauto_position.data.self_managed.val { - let dca_in_base_unit = self - .std_accounts - .solauto_position - .data - .position - .dca - .dca_in_base_unit; - let dca_token_type = self - .std_accounts - .solauto_position - .data - .position - .dca - .token_type; - if boosting && dca_token_type == TokenType::Supply { - available_balance -= dca_in_base_unit; - } else if !boosting && dca_token_type == TokenType::Debt { - available_balance -= dca_in_base_unit; - } - } + fn to_account_info(&self, acc: &SolautoAccount) -> &'a AccountInfo<'a> { + let mut map: HashMap> = HashMap::new(); - let transfer_to_authority_ta = |token_accounts: &LendingProtocolTokenAccounts<'a>, - amount: u64| { - solana_utils::spl_token_transfer( - self.std_accounts.token_program, - token_accounts.position_ta.clone().unwrap(), - self.std_accounts.solauto_position.account_info, - token_accounts.authority_ta.clone().unwrap(), - amount, - Some(&self.std_accounts.solauto_position.data.seeds_with_bump()), - ) - }; + map.insert( + SolautoAccount::SolautoPosition, + Some(self.std_accounts.solauto_position.account_info), + ); + map.insert( + SolautoAccount::SolautoPositionSupplyTa, + self.accounts.supply.position_ta, + ); + map.insert( + SolautoAccount::SolautoPositionDebtTa, + self.accounts.debt.position_ta, + ); + map.insert( + SolautoAccount::AuthoritySupplyTa, + self.accounts.supply.authority_ta, + ); + map.insert( + SolautoAccount::AuthorityDebtTa, + self.accounts.debt.authority_ta, + ); + map.insert( + SolautoAccount::IntermediaryTa, + self.accounts.intermediary_ta, + ); + map.insert( + SolautoAccount::SolautoFeesTa, + self.std_accounts.solauto_fees_ta, + ); + map.insert( + SolautoAccount::ReferredByTa, + self.std_accounts.referred_by_ta, + ); - let amount_after_fees = self.payout_fees(available_balance)?; + map.get(acc).unwrap().unwrap() + } - if boosting { - if self.std_accounts.solauto_position.data.self_managed.val { - transfer_to_authority_ta(&self.accounts.supply, amount_after_fees)?; - } - self.deposit(amount_after_fees)?; - } else if available_balance > 0 { - if self.std_accounts.solauto_position.data.self_managed.val { - transfer_to_authority_ta(&self.accounts.debt, amount_after_fees)?; - } - let final_amount = if rebalance_args.target_liq_utilization_rate_bps.is_some() - && rebalance_args.target_liq_utilization_rate_bps.unwrap() == 0 - { - TokenBalanceAmount::All - } else { - TokenBalanceAmount::Some(min( - self.std_accounts - .solauto_position - .data - .state - .debt - .amount_used - .base_unit, - amount_after_fees, - )) - }; - self.repay(final_amount)?; - } else { - msg!("Missing required position liquidity to rebalance position"); - return Err(SolautoError::IncorrectInstructions.into()); - } + fn get_rebalancer(&mut self, rebalance_args: RebalanceSettings) -> Rebalancer { + let position_supply_ta = self.get_token_account_data(self.accounts.supply.position_ta); + let position_debt_ta = self.get_token_account_data(self.accounts.debt.position_ta); - if flash_loan_amount > 0 { - let flash_loan_fee_bps = if boosting { - self.std_accounts - .solauto_position - .data - .state - .supply - .flash_loan_fee_bps - } else { - self.std_accounts - .solauto_position + Rebalancer::new(RebalancerData { + rebalance_args, + solauto_position: SolautoPositionData { + data: &mut self.std_accounts.solauto_position.data, + supply_ta: position_supply_ta, + debt_ta: position_debt_ta, + }, + solauto_fees_bps: self.solauto_fees_bps.unwrap().clone(), + referred_by: self.std_accounts.authority_referral_state.is_some() + && self + .std_accounts + .authority_referral_state + .as_ref() + .unwrap() .data - .state - .debt - .flash_loan_fee_bps - }; - let final_amount = flash_loan_amount - .add((flash_loan_amount as f64).mul(from_bps(flash_loan_fee_bps)) as u64); - if boosting { - self.borrow(final_amount, self.accounts.intermediary_ta.unwrap())?; - } else { - self.withdraw( - TokenBalanceAmount::Some(final_amount), - self.accounts.intermediary_ta.unwrap(), - )?; - } - } - - self.std_accounts.solauto_position.data.rebalance = RebalanceData::default(); - Ok(()) + .is_referred(), + }) } - fn payout_fees(&self, total_available_balance: u64) -> Result { - if self.std_accounts.authority_referral_state.is_none() { - msg!( - "Missing referral account when we are boosting leverage. Referral accounts are required" - ); - return Err(SolautoError::IncorrectAccounts.into()); - } - - let rebalance_direction = self + fn execute_cpi_actions(&mut self, actions: Vec) -> ProgramResult { + let owned_seeds: Vec> = self .std_accounts .solauto_position .data - .rebalance - .rebalance_direction; - let token_mint = if rebalance_direction == RebalanceDirection::Boost { - self.std_accounts.solauto_position.data.state.supply.mint - } else { - self.std_accounts.solauto_position.data.state.debt.mint - }; - - let position_ta = if rebalance_direction == RebalanceDirection::Boost { - self.accounts.supply.position_ta.unwrap() - } else { - self.accounts.debt.position_ta.unwrap() - }; - let fee_payout = self - .solauto_fees_bps - .as_ref() - .unwrap() - .fetch_fees(rebalance_direction); - - if fee_payout.total == 0 { - return Ok(total_available_balance); - } - - let solauto_fees = (total_available_balance as f64).mul(from_bps(fee_payout.total)) as u64; - if self.std_accounts.solauto_fees_ta.unwrap().key - != &get_associated_token_address(&SOLAUTO_FEES_WALLET, &token_mint) - { - msg!("Incorrect Solauto fees token account"); - return Err(SolautoError::IncorrectAccounts.into()); - } - solana_utils::spl_token_transfer( - self.std_accounts.token_program, - position_ta, - self.std_accounts.solauto_position.account_info, - self.std_accounts.solauto_fees_ta.unwrap(), - solauto_fees, - Some(&self.std_accounts.solauto_position.data.seeds_with_bump()), - )?; - - let referrer_fees = - (total_available_balance as f64).mul(from_bps(fee_payout.referrer)) as u64; - if referrer_fees > 0 { - if self.std_accounts.referred_by_ta.unwrap().key - != &get_associated_token_address( - &self - .std_accounts - .authority_referral_state - .as_ref() - .unwrap() - .data - .referred_by_state, - &token_mint, - ) - { - msg!("Incorrect referral fee token account"); - return Err(SolautoError::IncorrectAccounts.into()); + .seeds_with_bump() + .iter() + .map(|s| s.to_vec()) + .collect(); + let seeds_vec: Vec<&[u8]> = owned_seeds.iter().map(|v| v.as_slice()).collect(); + + for action in actions { + match action { + SolautoCpiAction::Deposit(amount) => self.deposit(amount)?, + SolautoCpiAction::Withdraw(data) => { + self.withdraw(data.amount, self.to_account_info(&data.to_wallet_ta))?; + } + SolautoCpiAction::Borrow(data) => { + self.borrow(data.amount, self.to_account_info(&data.to_wallet_ta))?; + } + SolautoCpiAction::Repay(amount) => self.repay(amount)?, + SolautoCpiAction::SplTokenTransfer(data) => { + let authority_seeds = if &data.from_wallet == &SolautoAccount::SolautoPosition { + Some(&seeds_vec) + } else { + None + }; + solana_utils::spl_token_transfer( + self.std_accounts.token_program, + SplTokenTransferArgs { + amount: data.amount, + source: self.to_account_info(&data.from_wallet_ta), + authority: self.to_account_info(&data.from_wallet), + recipient: self.to_account_info(&data.to_wallet_ta), + authority_seeds, + }, + )?; + } } - solana_utils::spl_token_transfer( - self.std_accounts.token_program, - position_ta, - self.std_accounts.solauto_position.account_info, - self.std_accounts.referred_by_ta.unwrap(), - referrer_fees, - Some(&self.std_accounts.solauto_position.data.seeds_with_bump()), - )?; } - - Ok(total_available_balance - solauto_fees - referrer_fees) + Ok(()) } pub fn refresh_position( @@ -473,6 +249,7 @@ impl<'a> SolautoManager<'a> { updated_data: RefreshStateProps, clock: Clock, ) -> ProgramResult { + // Update mint addresses if self-managed if solauto_position.self_managed.val { solauto_position.state.supply.mint = updated_data.supply.mint; solauto_position.state.debt.mint = updated_data.debt.mint; @@ -481,24 +258,8 @@ impl<'a> SolautoManager<'a> { solauto_position.state.max_ltv_bps = to_bps(updated_data.max_ltv); solauto_position.state.liq_threshold_bps = to_bps(updated_data.liq_threshold); - solauto_position.state.supply.decimals = updated_data.supply.decimals; - solauto_position.state.debt.decimals = updated_data.debt.decimals; - - solauto_position.state.supply.amount_used.base_unit = updated_data.supply.amount_used; - solauto_position.state.supply.amount_can_be_used.base_unit = - updated_data.supply.amount_can_be_used; - solauto_position - .state - .supply - .update_market_price(updated_data.supply.market_price); - - solauto_position.state.debt.amount_used.base_unit = updated_data.debt.amount_used; - solauto_position.state.debt.amount_can_be_used.base_unit = - updated_data.debt.amount_can_be_used; - solauto_position - .state - .debt - .update_market_price(updated_data.debt.market_price); + solauto_utils::update_token_state(&mut solauto_position.state.supply, &updated_data.supply); + solauto_utils::update_token_state(&mut solauto_position.state.debt, &updated_data.debt); solauto_position.state.net_worth.base_unit = math_utils::net_worth_base_amount( solauto_position.state.supply.amount_used.usd_value(), @@ -512,42 +273,88 @@ impl<'a> SolautoManager<'a> { ); solauto_position.refresh_state(); - solauto_position.state.last_updated = clock.unix_timestamp as u64; + solauto_position.state.last_refreshed = clock.unix_timestamp as u64; - if solauto_position.self_managed.val { - return Ok(()); - } + Ok(()) + } - if solauto_position - .position - .setting_params - .automation - .is_active() - { - let automation = &solauto_position.position.setting_params.automation; - - if automation.eligible_for_next_period(clock.unix_timestamp as u64) { - let current_timestamp = Clock::get()?.unix_timestamp as u64; - solauto_position.position.setting_params.boost_to_bps = automation - .updated_amount_from_automation( - solauto_position.position.setting_params.boost_to_bps, - solauto_position.position.setting_params.target_boost_to_bps, - current_timestamp, - ); - - let new_periods_passed = automation.new_periods_passed(current_timestamp); - if new_periods_passed == automation.target_periods { - solauto_position.position.setting_params.automation = - AutomationSettings::default(); - solauto_position.position.setting_params.target_boost_to_bps = 0; - } else { - solauto_position - .position - .setting_params - .automation - .periods_passed = new_periods_passed; - } + pub fn protocol_interaction(&mut self, action: SolautoAction) -> ProgramResult { + match action { + SolautoAction::Deposit(base_unit_amount) => { + self.deposit(base_unit_amount)?; + } + SolautoAction::Borrow(base_unit_amount) => { + self.borrow( + base_unit_amount, + self.accounts.debt.position_ta.as_ref().unwrap(), + )?; + } + SolautoAction::Repay(amount) => { + self.repay(amount)?; } + SolautoAction::Withdraw(amount) => { + self.withdraw(amount, self.accounts.supply.position_ta.as_ref().unwrap())?; + } + } + Ok(()) + } + + fn validate_fee_token_accounts(&self) -> ProgramResult { + let mints = vec![ + self.std_accounts.solauto_position.data.state.supply.mint, + self.std_accounts.solauto_position.data.state.debt.mint, + ]; + + if self.std_accounts.solauto_fees_ta.is_some() { + check!( + validation_utils::valid_token_account_for_mints( + self.std_accounts.solauto_fees_ta.as_ref().unwrap().key, + &SOLAUTO_FEES_WALLET, + &mints + ), + SolautoError::IncorrectAccounts + ); + } + + if self.std_accounts.referred_by_ta.is_some() { + check!( + validation_utils::valid_token_account_for_mints( + self.std_accounts.referred_by_ta.as_ref().unwrap().key, + &self + .std_accounts + .authority_referral_state + .as_ref() + .unwrap() + .data + .referred_by_state, + &mints + ), + SolautoError::IncorrectAccounts + ); + } + + Ok(()) + } + + pub fn rebalance( + &mut self, + rebalance_args: RebalanceSettings, + rebalance_step: RebalanceStep, + ) -> ProgramResult { + self.validate_fee_token_accounts()?; + + let (actions, finished) = { + let mut rebalancer = self.get_rebalancer(rebalance_args.clone()); + let rebalance_result = rebalancer.rebalance(rebalance_step)?; + let actions = rebalancer.actions().clone(); + (actions, rebalance_result.finished) + }; + + self.execute_cpi_actions(actions)?; + + if finished { + validation_utils::validate_rebalance(&self.std_accounts.solauto_position.data)?; + self.std_accounts.solauto_position.data.rebalance = RebalanceData::default(); } Ok(()) diff --git a/programs/solauto/src/utils/ix_utils.rs b/programs/solauto/src/utils/ix_utils.rs index 491ba775..ab32fb0a 100644 --- a/programs/solauto/src/utils/ix_utils.rs +++ b/programs/solauto/src/utils/ix_utils.rs @@ -1,26 +1,31 @@ use borsh::{BorshDeserialize, BorshSerialize}; -use jupiter_sdk::generated::instructions::{ - ExactOutRouteInstructionArgs, RouteWithTokenLedgerInstructionArgs, - SharedAccountsExactOutRouteInstructionArgs, SharedAccountsRouteWithTokenLedgerInstructionArgs, -}; +use jupiter_sdk::JUPITER_ID; use marginfi_sdk::generated::instructions::LendingAccountBorrowInstructionArgs; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, hash::hash, - instruction::Instruction, + instruction::{get_stack_height, Instruction, TRANSACTION_LEVEL_STACK_HEIGHT}, msg, program::invoke, program_error::ProgramError, pubkey::Pubkey, sanitize::SanitizeError, serialize_utils::{read_pubkey, read_slice, read_u16}, + sysvar::instructions::load_current_index_checked, }; use super::solana_utils::invoke_instruction; use crate::{ + check, + constants::{MARGINFI_PROD_PROGRAM, MARGINFI_STAGING_PROGRAM}, + error_if, state::solauto_position::SolautoPosition, - types::shared::{DeserializedAccount, SolautoError}, + types::{ + errors::SolautoError, + instruction::{SolautoStandardAccounts, SOLAUTO_REBALANCE_IX_DISCRIMINATORS}, + shared::{DeserializedAccount, SolautoRebalanceType}, + }, }; pub fn update_data(account: &mut DeserializedAccount) -> ProgramResult { @@ -102,8 +107,7 @@ fn pick_ix_data(req: PickIxDataReq) -> Result { let instruction_data_len = read_u16(&mut current, &data)? as usize; let data_start = data_start_idx.unwrap_or(0) as usize; - let data_end = - data_start + (data_len.unwrap_or((instruction_data_len - data_start) as u64) as usize); + let data_end = data_start + (data_len.unwrap_or(instruction_data_len as u64) as usize); current += data_start; let picked_data = read_slice(&mut current, &data, data_end)?; @@ -116,173 +120,20 @@ fn pick_ix_data(req: PickIxDataReq) -> Result { }) } -/// Validates the jup swap: -/// - The swap does NOT have a platform fee -/// - The destination token account is one of the expected destination token accounts -/// -/// Returns the slippage_fee_bps -pub fn validate_jup_instruction<'a>( - ixs_sysvar: &'a AccountInfo<'a>, - ix_idx: usize, - expected_destination_tas: &[&Pubkey], -) -> Result<(Pubkey, u16), ProgramError> { - let resp = pick_ix_data(PickIxDataReq { - ixs_sysvar, - ix_idx, - data_start_idx: Some(0), - data_len: Some(8), // Only pick the data discriminator - account_indices: None, - }) - .expect("Should pick data"); - - let discriminator = - u64::from_le_bytes(resp.data.try_into().expect("Slice with incorrect length")); - - let route_with_token_ledger = get_anchor_ix_discriminator("route_with_token_ledger"); - let shared_accounts_route_with_token_ledger = - get_anchor_ix_discriminator("shared_accounts_route_with_token_ledger"); - let exact_out_route = get_anchor_ix_discriminator("exact_out_route"); - let shared_accounts_exact_out_route = - get_anchor_ix_discriminator("shared_accounts_exact_out_route"); - - let result: Result<(u16, u8, Pubkey, Pubkey), ProgramError> = - if discriminator == route_with_token_ledger { - let resp = pick_ix_data(PickIxDataReq { - ixs_sysvar, - ix_idx, - data_start_idx: Some(8), // Skip data discriminator - data_len: None, - account_indices: Some(vec![2, 4]), - }) - .expect("Should pick data"); - - let args = Box::new(RouteWithTokenLedgerInstructionArgs::deserialize( - &mut resp.data.as_slice(), - )?); - - let return_data = ( - args.slippage_bps, - args.platform_fee_bps, - resp.accounts[0], - resp.accounts[1], - ); - drop(args); - Ok(return_data) - } else if discriminator == shared_accounts_route_with_token_ledger { - let resp = pick_ix_data(PickIxDataReq { - ixs_sysvar, - ix_idx, - data_start_idx: Some(8), // Skip data discriminator - data_len: None, - account_indices: Some(vec![3, 6]), - }) - .expect("Should pick data"); - - let args = Box::new( - SharedAccountsRouteWithTokenLedgerInstructionArgs::deserialize( - &mut resp.data.as_slice(), - )?, - ); - - let return_data = ( - args.slippage_bps, - args.platform_fee_bps, - resp.accounts[0], - resp.accounts[1], - ); - drop(args); - Ok(return_data) - } else if discriminator == exact_out_route { - let resp = pick_ix_data(PickIxDataReq { - ixs_sysvar, - ix_idx, - data_start_idx: Some(8), // Skip data discriminator - data_len: None, - account_indices: Some(vec![2, 4]), - }) - .expect("Should pick data"); - - let args = Box::new(ExactOutRouteInstructionArgs::deserialize( - &mut resp.data.as_slice(), - )?); - - let return_data = ( - args.slippage_bps, - args.platform_fee_bps, - resp.accounts[0], - resp.accounts[1], - ); - drop(args); - Ok(return_data) - } else if discriminator == shared_accounts_exact_out_route { - let resp = pick_ix_data(PickIxDataReq { - ixs_sysvar, - ix_idx, - data_start_idx: Some(8), // Skip data discriminator - data_len: None, - account_indices: Some(vec![3, 6]), - }) - .expect("Should pick data"); - - let args = Box::new(SharedAccountsExactOutRouteInstructionArgs::deserialize( - &mut resp.data.as_slice(), - )?); - - let return_data = ( - args.slippage_bps, - args.platform_fee_bps, - resp.accounts[0], - resp.accounts[1], - ); - drop(args); - Ok(return_data) - } else { - msg!("Unsupported JUP instruction"); - Err(SolautoError::IncorrectInstructions.into()) - }; - - let (slippage_fee_bps, platform_fee, source_ta, destination_ta) = result.unwrap(); - - if platform_fee > 0 { - msg!("Cannot include a platform fee in a token swap"); - return Err(SolautoError::IncorrectInstructions.into()); - } - - if !expected_destination_tas - .iter() - .any(|x| x == &&destination_ta) - { - msg!("Moving funds into an incorrect token account"); - return Err(SolautoError::IncorrectInstructions.into()); - } - - Ok((source_ta, slippage_fee_bps)) -} - pub fn get_marginfi_flash_loan_amount<'a>( ixs_sysvar: &'a AccountInfo<'a>, - ix_idx: usize, - expected_destination_tas: Option<&[&Pubkey]>, + ix_idx: Option, ) -> Result { - let res = pick_ix_data(PickIxDataReq { + let data = pick_ix_data(PickIxDataReq { ixs_sysvar, - ix_idx, + ix_idx: ix_idx.unwrap(), data_start_idx: Some(8), data_len: Some(8), account_indices: Some(vec![4]), }) - .expect("Should pick data"); - let args = LendingAccountBorrowInstructionArgs::deserialize(&mut res.data.as_slice())?; - - if expected_destination_tas.is_some() - && !expected_destination_tas - .unwrap() - .iter() - .any(|x| x == &&res.accounts[0]) - { - msg!("Moving funds into an incorrect token account"); - return Err(SolautoError::IncorrectInstructions.into()); - } + .expect("Should retrieve flash loan amount"); + + let args = LendingAccountBorrowInstructionArgs::deserialize(&mut data.data.as_slice())?; return Ok(args.amount); } @@ -297,7 +148,7 @@ pub fn get_anchor_ix_discriminator(instruction_name: &str) -> u64 { pub struct InstructionChecker<'a> { anchor_program: bool, ixs_sysvar: &'a AccountInfo<'a>, - program_id: Pubkey, + program_ids: Vec, ix_discriminators: Option>, anchor_ix_discriminators: Option>, curr_ix_idx: u16, @@ -305,14 +156,14 @@ pub struct InstructionChecker<'a> { impl<'a> InstructionChecker<'a> { pub fn from( ixs_sysvar: &'a AccountInfo<'a>, - program_id: Pubkey, + program_ids: Vec, ix_discriminators: Option>, curr_ix_idx: u16, ) -> Self { Self { anchor_program: false, ixs_sysvar, - program_id, + program_ids, ix_discriminators, anchor_ix_discriminators: None, curr_ix_idx, @@ -320,7 +171,7 @@ impl<'a> InstructionChecker<'a> { } pub fn from_anchor( ixs_sysvar: &'a AccountInfo<'a>, - program_id: Pubkey, + program_ids: Vec, ix_names: Vec<&str>, curr_ix_idx: u16, ) -> Self { @@ -331,14 +182,14 @@ impl<'a> InstructionChecker<'a> { Self { anchor_program: true, ixs_sysvar, - program_id, + program_ids, ix_discriminators: None, anchor_ix_discriminators: Some(ix_discriminators), curr_ix_idx, } } fn ix_matches(&self, program_id: Pubkey, discriminator_data: &[u8]) -> bool { - if program_id == self.program_id { + if self.program_ids.iter().any(|prog| prog == &program_id) { if self.anchor_program { let discriminator = u64::from_le_bytes( discriminator_data[0..8] @@ -387,3 +238,97 @@ impl<'a> InstructionChecker<'a> { return self.ix_matches(data.program_id, &data.data); } } + +pub fn validate_rebalance_instructions( + std_accounts: &Box, + rebalance_type: SolautoRebalanceType, +) -> ProgramResult { + let ixs_sysvar = std_accounts.ixs_sysvar.unwrap(); + + let current_ix_idx = load_current_index_checked(ixs_sysvar)?; + error_if!( + get_stack_height() > TRANSACTION_LEVEL_STACK_HEIGHT, + SolautoError::InstructionIsCPI + ); + + let solauto_rebalance = InstructionChecker::from( + ixs_sysvar, + vec![crate::ID], + Some(SOLAUTO_REBALANCE_IX_DISCRIMINATORS.to_vec()), + current_ix_idx, + ); + let jup_swap = InstructionChecker::from_anchor( + ixs_sysvar, + vec![JUPITER_ID], + vec![ + "route", + "shared_accounts_route", + "route_with_token_ledger", + "shared_accounts_route_with_token_ledger", + "exact_out_route", + "shared_accounts_exact_out_route", + ], + current_ix_idx, + ); + + let next_ix = 1; + let ix_2_after = 2; + let prev_ix = -1; + + let valid_ixs = match rebalance_type { + SolautoRebalanceType::Regular | SolautoRebalanceType::DoubleRebalanceWithFL => { + jup_swap.matches(next_ix) && solauto_rebalance.matches(ix_2_after) + } + SolautoRebalanceType::FLSwapThenRebalance => jup_swap.matches(prev_ix), + SolautoRebalanceType::FLRebalanceThenSwap => jup_swap.matches(next_ix), + }; + + check!(valid_ixs, SolautoError::IncorrectInstructions); + + Ok(()) +} + +pub fn get_flash_borrow_ix_idx( + std_accounts: &Box, + rebalance_type: SolautoRebalanceType, +) -> Result, ProgramError> { + let ixs_sysvar = std_accounts.ixs_sysvar.unwrap(); + let current_ix_idx = load_current_index_checked(ixs_sysvar)?; + + let prev_ix = -1; + let ix_2_before = -2; + + let marginfi_borrow = InstructionChecker::from_anchor( + ixs_sysvar, + vec![MARGINFI_PROD_PROGRAM, MARGINFI_STAGING_PROGRAM], + vec!["lending_account_borrow"], + current_ix_idx, + ); + + let borrow_ix_idx = match rebalance_type { + SolautoRebalanceType::DoubleRebalanceWithFL => { + if current_ix_idx > 0 && marginfi_borrow.matches(prev_ix) { + Some(((current_ix_idx as i16) + prev_ix) as usize) + } else { + None + } + } + SolautoRebalanceType::FLSwapThenRebalance => { + if current_ix_idx > 1 && marginfi_borrow.matches(ix_2_before) { + Some(((current_ix_idx as i16) + ix_2_before) as usize) + } else { + None + } + } + SolautoRebalanceType::FLRebalanceThenSwap => { + if current_ix_idx > 0 && marginfi_borrow.matches(prev_ix) { + Some(((current_ix_idx as i16) + prev_ix) as usize) + } else { + None + } + } + _ => None, + }; + + Ok(borrow_ix_idx) +} diff --git a/programs/solauto/src/utils/math_utils.rs b/programs/solauto/src/utils/math_utils.rs index c8454b6e..fd6bdd9e 100644 --- a/programs/solauto/src/utils/math_utils.rs +++ b/programs/solauto/src/utils/math_utils.rs @@ -2,10 +2,16 @@ use fixed::types::I80F48; use num_traits::{FromPrimitive, ToPrimitive}; use std::{ cmp::min, - ops::{Div, Mul}, + ops::{Add, Div, Mul, Sub}, }; -use crate::constants::{MAX_BASIS_POINTS, MIN_REPAY_GAP_BPS, USD_DECIMALS}; +use crate::{ + constants::{MAX_BASIS_POINTS, MIN_REPAY_GAP_BPS, OFFSET_FROM_MAX_LTV, USD_DECIMALS}, + types::{ + shared::TokenType, + solauto::{DebtAdjustment, PositionValues, RebalanceFeesBps}, + }, +}; #[inline(always)] pub fn from_base_unit(base_units: T, decimals: U) -> V @@ -20,15 +26,26 @@ where } #[inline(always)] -pub fn to_base_unit(value: T, decimals: U) -> V +pub fn to_base_unit(value: T, decimals: U) -> V where T: ToPrimitive, U: Into, V: FromPrimitive, { let factor = (10u64).pow(decimals.into()) as f64; - let base_units = value.to_f64().unwrap_or(0.0).mul(factor); - V::from_f64(base_units).unwrap() + let base_units = value.to_f64().unwrap_or(0.0) * factor; + let adjusted = if base_units < 0.0 { 0.0 } else { base_units }; + V::from_f64(adjusted).unwrap_or_else(|| V::from_f64(0.0).unwrap()) +} + +#[inline(always)] +pub fn to_rounded_usd_value(usd_value: f64) -> u64 { + to_base_unit(usd_value, USD_DECIMALS) +} + +#[inline(always)] +pub fn from_rounded_usd_value(usd_value: u64) -> f64 { + from_base_unit(usd_value, USD_DECIMALS) } #[inline(always)] @@ -38,6 +55,11 @@ pub fn base_unit_to_usd_value(base_unit: u64, decimals: u8, market_price: f64) - .mul(market_price) } +#[inline(always)] +pub fn usd_value_to_base_unit(usd_value: f64, decimals: u8, market_price: f64) -> u64 { + to_base_unit(usd_value.abs().div(market_price), decimals) +} + #[inline(always)] pub fn from_bps(value_bps: u16) -> f64 { (value_bps as f64).div(MAX_BASIS_POINTS as f64) @@ -50,7 +72,11 @@ pub fn to_bps(value: f64) -> u16 { #[inline(always)] pub fn i80f48_to_u64(value: I80F48) -> u64 { - value.to_num::() + if value < 0.0 { + 0 + } else { + value.to_num::() + } } #[inline(always)] @@ -69,7 +95,7 @@ pub fn get_liq_utilization_rate_bps(supply_usd: f64, debt_usd: f64, liq_threshol #[inline(always)] pub fn net_worth_usd_base_amount(supply_usd: f64, debt_usd: f64) -> u64 { - to_base_unit::(supply_usd - debt_usd, USD_DECIMALS) + to_base_unit(supply_usd - debt_usd, USD_DECIMALS) } #[inline(always)] @@ -84,38 +110,7 @@ pub fn net_worth_base_amount( USD_DECIMALS, ) .div(supply_market_price as f64); - to_base_unit::(supply_net_worth, supply_decimals) -} - -/// Calculates the debt adjustment in USD in order to reach the target_liq_utilization_rate -/// -/// # Parameters -/// * `liq_threshold` - The liquidation threshold of the supplied asset -/// * `total_supply_usd` - Total USD value of supplied asset -/// * `total_debt_usd` - Total USD value of debt asset -/// * `target_liq_utilization_rate_bps` - Target utilization rate -/// * `adjustment_fee_bps` - Adjustment fee. On boosts this would be the Solauto fee. If deleveraging this would be None -/// -/// # Returns -/// The USD value of the debt adjustment. Positive if debt needs to increase, negative if debt needs to decrease. This amount is inclusive of the adjustment fee -/// -pub fn get_std_debt_adjustment_usd( - liq_threshold: f64, - total_supply_usd: f64, - total_debt_usd: f64, - target_liq_utilization_rate_bps: u16, - adjustment_fee_bps: u16, -) -> f64 { - let adjustment_fee = if adjustment_fee_bps > 0 { - from_bps(adjustment_fee_bps) - } else { - 0.0 - }; - - let target_liq_utilization_rate = from_bps(target_liq_utilization_rate_bps); - - (target_liq_utilization_rate * total_supply_usd * liq_threshold - total_debt_usd) - / (1.0 - target_liq_utilization_rate * (1.0 - adjustment_fee) * liq_threshold) + to_base_unit(supply_net_worth, supply_decimals) } #[inline(always)] @@ -132,7 +127,11 @@ pub fn get_max_liq_utilization_rate_bps( pub fn get_max_repay_from_bps(max_ltv_bps: u16, liq_threshold_bps: u16) -> u16 { min( 9000, - get_max_liq_utilization_rate_bps(max_ltv_bps, liq_threshold_bps - 1000, 0.01), + get_max_liq_utilization_rate_bps( + max_ltv_bps, + liq_threshold_bps - 1000, + OFFSET_FROM_MAX_LTV, + ), ) } @@ -140,7 +139,7 @@ pub fn get_max_repay_from_bps(max_ltv_bps: u16, liq_threshold_bps: u16) -> u16 { pub fn get_max_repay_to_bps(max_ltv_bps: u16, liq_threshold_bps: u16) -> u16 { min( get_max_repay_from_bps(max_ltv_bps, liq_threshold_bps) - MIN_REPAY_GAP_BPS, - get_max_liq_utilization_rate_bps(max_ltv_bps, liq_threshold_bps, 0.01), + get_max_liq_utilization_rate_bps(max_ltv_bps, liq_threshold_bps, OFFSET_FROM_MAX_LTV), ) } @@ -148,68 +147,235 @@ pub fn get_max_repay_to_bps(max_ltv_bps: u16, liq_threshold_bps: u16) -> u16 { pub fn get_max_boost_to_bps(max_ltv_bps: u16, liq_threshold_bps: u16) -> u16 { min( get_max_repay_to_bps(max_ltv_bps, liq_threshold_bps), - get_max_liq_utilization_rate_bps(max_ltv_bps, liq_threshold_bps, 0.01), + get_max_liq_utilization_rate_bps(max_ltv_bps, liq_threshold_bps, OFFSET_FROM_MAX_LTV), ) } +pub fn derive_value(price: i64, exponent: i32) -> f64 { + if exponent == 0 { + price as f64 + } else if exponent < 0 { + from_base_unit(price, exponent.unsigned_abs()) + } else { + to_base_unit(price, exponent.unsigned_abs()) + } +} + +#[inline(always)] +pub fn calc_fee_amount(value: u64, fee_pct_bps: u16) -> u64 { + (value as f64).mul(from_bps(fee_pct_bps)) as u64 +} + +pub fn round_to_decimals(value: f64, decimals: u32) -> f64 { + let multiplier = (10_f64).powi(decimals as i32); + (value * multiplier).round() / multiplier +} + +pub fn with_conf_interval(price: f64, conf_interval: f64, token_type: TokenType) -> f64 { + let final_conf = f64::min(conf_interval, price.mul(0.05)); + + if token_type == TokenType::Supply { + price - final_conf + } else { + price + final_conf + } +} + +fn apply_debt_adjustment_usd( + debt_adjustment_usd: f64, + pos: &PositionValues, + fees: &RebalanceFeesBps, +) -> PositionValues { + let mut new_pos = pos.clone(); + let is_boost = debt_adjustment_usd > 0.0; + + let da_minus_solauto_fees = + debt_adjustment_usd.sub(debt_adjustment_usd.mul(from_bps(fees.solauto))); + let da_with_flash_loan = debt_adjustment_usd.mul(1.0 + from_bps(fees.flash_loan)); + + if is_boost { + new_pos.supply_usd += da_minus_solauto_fees; + new_pos.debt_usd += + da_with_flash_loan.mul_add(from_bps(fees.lp_borrow), da_with_flash_loan); + } else { + new_pos.supply_usd += da_with_flash_loan; + new_pos.debt_usd += da_minus_solauto_fees; + } + + new_pos +} + +pub fn get_debt_adjustment( + liq_threshold_bps: u16, + pos: &PositionValues, + target_liq_utilization_rate_bps: u16, + fees: &RebalanceFeesBps, +) -> DebtAdjustment { + let liq_threshold = from_bps(liq_threshold_bps); + let is_boost = get_liq_utilization_rate_bps(pos.supply_usd, pos.debt_usd, liq_threshold) + < target_liq_utilization_rate_bps; + + let target_utilization_rate = from_bps(target_liq_utilization_rate_bps); + let actualized_fee = (1.0).sub(from_bps(fees.solauto)); + let fl_fee = from_bps(fees.flash_loan); + let lp_borrow_fee = from_bps(fees.lp_borrow); + + let debt_adjustment_usd = if is_boost { + (target_utilization_rate * liq_threshold * pos.supply_usd - pos.debt_usd) + / ((1.0).add(lp_borrow_fee).add(fl_fee) + - target_utilization_rate * actualized_fee * liq_threshold) + } else { + (target_utilization_rate * liq_threshold * pos.supply_usd - pos.debt_usd) + / (actualized_fee - target_utilization_rate * liq_threshold * (1.0).add(fl_fee)) + }; + + let new_pos = apply_debt_adjustment_usd(debt_adjustment_usd, pos, fees); + + DebtAdjustment { + debt_adjustment_usd, + end_result: new_pos, + } +} + #[cfg(test)] mod tests { use std::ops::Sub; use super::*; - fn round_to_places(value: f64, places: u32) -> f64 { - let multiplier = (10_f64).powi(places as i32); - (value * multiplier).round() / multiplier + const INIT_ASSET_WEIGHT_1: f64 = 0.8; + const MAINT_ASSET_WEIGHT_1: f64 = 0.9; + const INIT_ASSET_WEIGHT_2: f64 = 0.5; + const MAINT_ASSET_WEIGHT_2: f64 = 0.65; + const INIT_LIAB_WEIGHT_1: f64 = 1.25; + const MAINT_LIAB_WEIGHT_1: f64 = 1.1; + + struct AssetWeights { + pub init_asset_weight: f64, + pub init_debt_weight: f64, + pub maint_asset_weight: f64, + pub maint_debt_weight: f64, + } + + impl AssetWeights { + pub fn max_ltv(&self) -> f64 { + self.init_asset_weight.div(self.init_debt_weight) + } + pub fn liq_threshold(&self) -> f64 { + self.maint_asset_weight.div(self.maint_debt_weight) + } + pub fn max_boost_to_bps(&self) -> u16 { + get_max_boost_to_bps(to_bps(self.max_ltv()), to_bps(self.liq_threshold())) + } } fn test_debt_adjustment_calculation( - mut supply_usd: f64, - mut debt_usd: f64, + weights: &AssetWeights, + supply_usd: f64, + debt_usd: f64, target_liq_utilization_rate: f64, ) { - let supply_weight = 0.899999976158142; - let debt_weight = 1.100000023841858; - let liq_threshold = supply_weight.div(debt_weight); // ~0.81 + let AssetWeights { + maint_asset_weight, + maint_debt_weight, + .. + } = weights; + let liq_threshold = weights.liq_threshold(); - let debt_adjustment = get_std_debt_adjustment_usd( + let is_boost = from_bps(get_liq_utilization_rate_bps( + supply_usd, + debt_usd, liq_threshold, + )) < target_liq_utilization_rate; + + let target_liq_utilization_rate_bps = to_bps(target_liq_utilization_rate); + let position = PositionValues { supply_usd, debt_usd, - to_bps(target_liq_utilization_rate), - 0, + }; + let fees = RebalanceFeesBps { + solauto: 50, + lp_borrow: 50, + flash_loan: 50, + }; + let debt_adjustment = get_debt_adjustment( + to_bps(liq_threshold), + &position, + target_liq_utilization_rate_bps, + &fees, ); - supply_usd += debt_adjustment; - debt_usd += debt_adjustment; + let (new_supply_usd, new_debt_usd) = ( + debt_adjustment.end_result.supply_usd, + debt_adjustment.end_result.debt_usd, + ); let new_liq_utilization_rate_bps = - get_liq_utilization_rate_bps(supply_usd, debt_usd, liq_threshold); - assert!( - round_to_places(from_bps(new_liq_utilization_rate_bps), 2) - == round_to_places(target_liq_utilization_rate, 2) - ); + get_liq_utilization_rate_bps(new_supply_usd, new_debt_usd, liq_threshold); let marginfi_liq_utilization_rate = (1.0).sub( - supply_usd - .mul(supply_weight) - .sub(debt_usd.mul(debt_weight)) - .div(supply_usd.mul(supply_weight)), + new_supply_usd + .mul(maint_asset_weight) + .sub(new_debt_usd.mul(maint_debt_weight)) + .div(new_supply_usd.mul(maint_asset_weight)), ); - assert!( - round_to_places(marginfi_liq_utilization_rate, 4) - == round_to_places(target_liq_utilization_rate, 4) + println!( + "Boost: {}. {}, {}, {}", + is_boost, + target_liq_utilization_rate, + from_bps(new_liq_utilization_rate_bps), + marginfi_liq_utilization_rate + ); + + assert_eq!( + round_to_decimals(from_bps(new_liq_utilization_rate_bps), 3), + round_to_decimals(target_liq_utilization_rate, 3) + ); + assert_eq!( + round_to_decimals(marginfi_liq_utilization_rate, 3), + round_to_decimals(target_liq_utilization_rate, 3) ); } + + fn test_debt_adjustment_for_weights(weights: &AssetWeights) { + test_debt_adjustment_calculation(weights, 100.0, 80.0, 0.8); + test_debt_adjustment_calculation(weights, 10.0, 2.0, 0.1); + test_debt_adjustment_calculation(weights, 30.0, 5.0, 0.5); + test_debt_adjustment_calculation(weights, 44334.0, 24534.0, 0.5); + test_debt_adjustment_calculation(weights, 7644.0, 434.0, 0.8); + test_debt_adjustment_calculation(weights, 10444.0, 7454.0, 0.2); + test_debt_adjustment_calculation(weights, 1340444.0, 7454.0, 0.35); + test_debt_adjustment_calculation(weights, 1000000.0, 519999.0, 0.65); + test_debt_adjustment_calculation( + weights, + 3453.0, + 1345.0, + from_bps(weights.max_boost_to_bps()), + ); + } + + #[test] + fn test_weights_1_debt_adjustment() { + test_debt_adjustment_for_weights( + &(AssetWeights { + init_asset_weight: INIT_ASSET_WEIGHT_1, + init_debt_weight: INIT_LIAB_WEIGHT_1, + maint_asset_weight: MAINT_ASSET_WEIGHT_1, + maint_debt_weight: MAINT_LIAB_WEIGHT_1, + }), + ); + } + #[test] - fn test_std_debt_adjustment() { - test_debt_adjustment_calculation(100.0, 80.0, 0.8); - test_debt_adjustment_calculation(30.0, 24.0, 0.5); - test_debt_adjustment_calculation(44334.0, 24534.0, 0.5); - test_debt_adjustment_calculation(7644.0, 434.0, 0.8); - test_debt_adjustment_calculation(10444.0, 7454.0, 0.2); - test_debt_adjustment_calculation(1340444.0, 7454.0, 0.35); - test_debt_adjustment_calculation(1000000.0, 519999.0, 0.65); + fn test_weights_2_debt_adjustment() { + test_debt_adjustment_for_weights( + &(AssetWeights { + init_asset_weight: INIT_ASSET_WEIGHT_2, + init_debt_weight: INIT_LIAB_WEIGHT_1, + maint_asset_weight: MAINT_ASSET_WEIGHT_2, + maint_debt_weight: MAINT_LIAB_WEIGHT_1, + }), + ); } } diff --git a/programs/solauto/src/utils/mod.rs b/programs/solauto/src/utils/mod.rs index 329cb3e5..340e162f 100644 --- a/programs/solauto/src/utils/mod.rs +++ b/programs/solauto/src/utils/mod.rs @@ -1,6 +1,5 @@ pub mod ix_utils; pub mod math_utils; -pub mod rebalance_utils; pub mod solana_utils; pub mod solauto_utils; pub mod validation_utils; diff --git a/programs/solauto/src/utils/rebalance_utils.rs b/programs/solauto/src/utils/rebalance_utils.rs deleted file mode 100644 index da1f301f..00000000 --- a/programs/solauto/src/utils/rebalance_utils.rs +++ /dev/null @@ -1,854 +0,0 @@ -use jupiter_sdk::JUPITER_ID; -use marginfi_sdk::MARGINFI_ID; -use math_utils::from_bps; -use solana_program::{ - instruction::{get_stack_height, TRANSACTION_LEVEL_STACK_HEIGHT}, - msg, - program_error::ProgramError, - pubkey::Pubkey, - sysvar::instructions::load_current_index_checked, -}; -use std::{cmp::max, ops::Mul}; -use validation_utils::validate_debt_adjustment; - -use crate::{ - state::solauto_position::{DCASettings, PositionData, SolautoPosition, SolautoRebalanceType}, - types::{ - instruction::{ - RebalanceSettings, SolautoStandardAccounts, SOLAUTO_REBALANCE_IX_DISCRIMINATORS, - }, - shared::{RebalanceDirection, RebalanceStep, SolautoError, TokenType}, - }, -}; - -use super::*; - -pub struct RebalanceInstructionIndices { - pub jup_swap: usize, - pub marginfi_flash_borrow: Option, -} - -#[inline(always)] -pub fn validate_rebalance_instructions( - std_accounts: &mut Box, - rebalance_type: SolautoRebalanceType, -) -> Result { - let ixs_sysvar = std_accounts.ixs_sysvar.unwrap(); - - let current_ix_idx = load_current_index_checked(ixs_sysvar)?; - if get_stack_height() > TRANSACTION_LEVEL_STACK_HEIGHT { - msg!("Instruction is CPI"); - return Err(SolautoError::InstructionIsCPI.into()); - } - - let solauto_rebalance = ix_utils::InstructionChecker::from( - ixs_sysvar, - crate::ID, - Some(SOLAUTO_REBALANCE_IX_DISCRIMINATORS.to_vec()), - current_ix_idx, - ); - let jup_swap = ix_utils::InstructionChecker::from_anchor( - ixs_sysvar, - JUPITER_ID, - vec![ - "route", - "shared_accounts_route", - "route_with_token_ledger", - "shared_accounts_route_with_token_ledger", - "exact_out_route", - "shared_accounts_exact_out_route", - ], - current_ix_idx, - ); - let marginfi_start_fl = ix_utils::InstructionChecker::from_anchor( - ixs_sysvar, - MARGINFI_ID, - vec!["lending_account_start_flashloan"], - current_ix_idx, - ); - let marginfi_borrow = ix_utils::InstructionChecker::from_anchor( - ixs_sysvar, - MARGINFI_ID, - vec!["lending_account_borrow"], - current_ix_idx, - ); - let marginfi_end_fl = ix_utils::InstructionChecker::from_anchor( - ixs_sysvar, - MARGINFI_ID, - vec!["lending_account_end_flashloan"], - current_ix_idx, - ); - let marginfi_repay = ix_utils::InstructionChecker::from_anchor( - ixs_sysvar, - MARGINFI_ID, - vec!["lending_account_repay"], - current_ix_idx, - ); - - let next_ix = 1; - let ix_2_after = 2; - let ix_3_after = 3; - let ix_4_after = 4; - let prev_ix = -1; - let ix_2_before = -2; - let ix_3_before = -3; - - if (rebalance_type == SolautoRebalanceType::Regular - || rebalance_type == SolautoRebalanceType::None) - && jup_swap.matches(next_ix) - && solauto_rebalance.matches(ix_2_after) - { - std_accounts.solauto_position.data.rebalance.rebalance_type = SolautoRebalanceType::Regular; - Ok(RebalanceInstructionIndices { - jup_swap: ((current_ix_idx as i16) + next_ix) as usize, - marginfi_flash_borrow: None, - }) - } else if (rebalance_type == SolautoRebalanceType::DoubleRebalanceWithFL - || rebalance_type == SolautoRebalanceType::None) - && marginfi_start_fl.matches(ix_2_before) - && marginfi_borrow.matches(prev_ix) - && jup_swap.matches(next_ix) - && solauto_rebalance.matches(ix_2_after) - && marginfi_repay.matches(ix_3_after) - && marginfi_end_fl.matches(ix_4_after) - { - std_accounts.solauto_position.data.rebalance.rebalance_type = - SolautoRebalanceType::DoubleRebalanceWithFL; - Ok(RebalanceInstructionIndices { - jup_swap: ((current_ix_idx as i16) + next_ix) as usize, - marginfi_flash_borrow: Some(((current_ix_idx as i16) + prev_ix) as usize), - }) - } else if (rebalance_type == SolautoRebalanceType::SingleRebalanceWithFL - || rebalance_type == SolautoRebalanceType::None) - && marginfi_start_fl.matches(ix_3_before) - && marginfi_borrow.matches(ix_2_before) - && jup_swap.matches(prev_ix) - && marginfi_repay.matches(next_ix) - && marginfi_end_fl.matches(ix_2_after) - { - std_accounts.solauto_position.data.rebalance.rebalance_type = - SolautoRebalanceType::SingleRebalanceWithFL; - Ok(RebalanceInstructionIndices { - jup_swap: ((current_ix_idx as i16) + prev_ix) as usize, - marginfi_flash_borrow: Some(((current_ix_idx as i16) + ix_2_before) as usize), - }) - } else { - msg!("Incorrect rebalance instructions"); - Err(SolautoError::IncorrectInstructions.into()) - } -} - -pub fn get_rebalance_step( - std_accounts: &mut Box, - args: &RebalanceSettings, - position_tas: Vec<&Pubkey>, -) -> Result { - let has_rebalance_data = std_accounts.solauto_position.data.rebalance.active(); - if !has_rebalance_data { - let ix_indices = validate_rebalance_instructions(std_accounts, args.rebalance_type)?; - - // Deserializing jup swap ix to pull slippage bps causes OOM issues, will return to this in the future if there is a solution. - // let (swap_source_ta, price_slippage_bps) = ix_utils::validate_jup_instruction( - // std_accounts.ixs_sysvar.unwrap(), - // ix_indices.jup_swap, - // position_tas.as_slice(), - // )?; - - if ix_indices.marginfi_flash_borrow.is_some() { - std_accounts - .solauto_position - .data - .rebalance - .flash_loan_amount = ix_utils::get_marginfi_flash_loan_amount( - std_accounts.ixs_sysvar.unwrap(), - ix_indices.marginfi_flash_borrow.unwrap(), - None, // &[&swap_source_ta], - )?; - } - } - - let rebalance_step = if !has_rebalance_data - && (std_accounts.solauto_position.data.rebalance.rebalance_type - == SolautoRebalanceType::Regular - || std_accounts.solauto_position.data.rebalance.rebalance_type - == SolautoRebalanceType::DoubleRebalanceWithFL) - { - RebalanceStep::Initial - } else { - RebalanceStep::Final - }; - - Ok(rebalance_step) -} - -#[inline(always)] -fn get_additional_amount_to_dca_in( - position: &mut PositionData, - current_unix_timestamp: u64, -) -> (Option, Option) { - if !position.dca.dca_in() { - return (None, None); - } - - let updated_dca_balance = position.dca.automation.updated_amount_from_automation( - position.dca.dca_in_base_unit, - 0, - current_unix_timestamp, - ); - let amount_to_dca_in = position - .dca - .dca_in_base_unit - .saturating_sub(updated_dca_balance); - - position.dca.dca_in_base_unit = updated_dca_balance; - - (Some(amount_to_dca_in), Some(position.dca.token_type)) -} - -#[inline(always)] -fn get_target_liq_utilization_rate_from_dca( - solauto_position: &mut SolautoPosition, - current_unix_timestamp: u64, -) -> Result { - let curr_liq_utilization_rate_bps = solauto_position.state.liq_utilization_rate_bps; - let position = &mut solauto_position.position; - - let target_rate_bps = { - if position.dca.dca_in() { - max( - curr_liq_utilization_rate_bps, - position.setting_params.boost_to_bps, - ) - } else { - position.setting_params.boost_to_bps - } - }; - - let new_periods_passed = position - .dca - .automation - .new_periods_passed(current_unix_timestamp); - if new_periods_passed == position.dca.automation.target_periods { - position.dca = DCASettings::default(); - } else { - position.dca.automation.periods_passed = new_periods_passed; - } - - Ok(target_rate_bps) -} - -#[inline(always)] -fn get_std_target_liq_utilization_rate( - solauto_position: &SolautoPosition, -) -> Result { - let setting_params = solauto_position.position.setting_params.clone(); - - let target_rate_bps: Result = - if solauto_position.state.liq_utilization_rate_bps >= setting_params.repay_from_bps() { - Ok(setting_params.repay_to_bps) - } else if solauto_position.state.liq_utilization_rate_bps <= setting_params.boost_from_bps() - { - Ok(setting_params.boost_to_bps) - } else { - msg!( - "Invalid rebalance condition. Current utilizatiion rate is: {}", - solauto_position.state.liq_utilization_rate_bps - ); - return Err(SolautoError::InvalidRebalanceCondition.into()); - }; - - Ok(target_rate_bps.unwrap()) -} - -#[inline(always)] -fn is_dca_instruction( - solauto_position: &SolautoPosition, - current_unix_timestamp: u64, -) -> Result { - let position_data = &solauto_position.position; - - if solauto_position.state.liq_utilization_rate_bps - >= position_data.setting_params.repay_from_bps() - { - return Ok(false); - } - - if !position_data.dca.is_active() { - return Ok(false); - } - - if !position_data - .dca - .automation - .eligible_for_next_period(current_unix_timestamp) - { - if solauto_position.state.liq_utilization_rate_bps - <= position_data.setting_params.boost_from_bps() - { - return Ok(false); - } else { - msg!("DCA rebalance was initiated too early"); - return Err(SolautoError::InvalidRebalanceCondition.into()); - } - } - - Ok(true) -} - -#[inline(always)] -fn get_target_rate_and_dca_amount( - solauto_position: &mut SolautoPosition, - rebalance_args: &RebalanceSettings, - current_unix_timestamp: u64, -) -> Result<(u16, Option, Option), ProgramError> { - if rebalance_args.target_liq_utilization_rate_bps.is_some() { - return Ok(( - rebalance_args.target_liq_utilization_rate_bps.unwrap(), - None, - None, - )); - } - - let dca_instruction = is_dca_instruction(solauto_position, current_unix_timestamp)?; - - let (target_liq_utilization_rate_bps, amount_to_dca_in, dca_token_type) = match dca_instruction - { - true => { - let target_liq_utilization_rate_bps = - get_target_liq_utilization_rate_from_dca(solauto_position, current_unix_timestamp)?; - let (amount_to_dca_in, dca_token_type) = get_additional_amount_to_dca_in( - &mut solauto_position.position, - current_unix_timestamp, - ); - - ( - target_liq_utilization_rate_bps, - amount_to_dca_in, - dca_token_type, - ) - } - false => ( - get_std_target_liq_utilization_rate(solauto_position)?, - None, - None, - ), - }; - - Ok(( - target_liq_utilization_rate_bps, - amount_to_dca_in, - dca_token_type, - )) -} - -pub fn get_rebalance_values( - solauto_position: &mut SolautoPosition, - args: &RebalanceSettings, - solauto_fees_bps: &solauto_utils::SolautoFeesBps, - current_unix_timestamp: u64, -) -> Result<(f64, Option), ProgramError> { - let (target_liq_utilization_rate_bps, amount_to_dca_in, dca_token_type) = - get_target_rate_and_dca_amount(solauto_position, args, current_unix_timestamp)?; - - solauto_position.rebalance.rebalance_direction = if amount_to_dca_in.is_some() - || solauto_position.state.liq_utilization_rate_bps <= target_liq_utilization_rate_bps - { - RebalanceDirection::Boost - } else { - RebalanceDirection::Repay - }; - let fees_bps = solauto_fees_bps.fetch_fees(solauto_position.rebalance.rebalance_direction); - - let amount_to_dca_in_usd = if amount_to_dca_in.is_some() { - let (decimals, market_price) = if dca_token_type.unwrap() == TokenType::Supply { - ( - solauto_position.state.supply.decimals, - solauto_position.state.supply.market_price(), - ) - } else { - ( - solauto_position.state.debt.decimals, - solauto_position.state.debt.market_price(), - ) - }; - - math_utils::from_base_unit::(amount_to_dca_in.unwrap(), decimals) - .mul(market_price) - } else { - 0.0 - }; - - let total_supply_usd = - solauto_position.state.supply.amount_used.usd_value() + amount_to_dca_in_usd; - - let debt_adjustment_usd = math_utils::get_std_debt_adjustment_usd( - from_bps(solauto_position.state.liq_threshold_bps), - total_supply_usd, - solauto_position.state.debt.amount_used.usd_value(), - target_liq_utilization_rate_bps, - fees_bps.total, - ); - - if args.target_in_amount_base_unit.is_some() { - validate_debt_adjustment( - solauto_position, - args.target_in_amount_base_unit.unwrap(), - debt_adjustment_usd, - )?; - } - - Ok((debt_adjustment_usd, amount_to_dca_in)) -} - -#[cfg(test)] -mod tests { - use solana_program::pubkey::Pubkey; - use std::ops::{Add, Div, Sub}; - use tests::math_utils::{from_base_unit, to_base_unit}; - - use crate::{ - state::solauto_position::{ - AutomationSettingsInp, DCASettings, DCASettingsInp, PositionState, PositionTokenUsage, - RebalanceData, SolautoSettingsParameters, - }, - types::shared::{PositionType, TokenType}, - utils::math_utils, - }; - - use super::*; - - const BOOST_TO_BPS: u16 = 5000; - const REPAY_TO_BPS: u16 = 7500; - - fn assert_bps_within_margin_of_error(result_bps: u16, expected_bps: u16) { - println!("{}, {}", result_bps, expected_bps); - assert!(result_bps >= expected_bps.saturating_sub(1) && result_bps <= expected_bps + 1); - } - - // CHANGING THESE WILL BREAK TESTS - fn default_setting_params() -> SolautoSettingsParameters { - let mut settings = SolautoSettingsParameters::default(); - settings.boost_to_bps = BOOST_TO_BPS; - settings.boost_gap = 1000; - settings.repay_to_bps = REPAY_TO_BPS; - settings.repay_gap = 500; - settings - } - - fn standard_solauto_position( - setting_params: SolautoSettingsParameters, - active_dca: Option, - current_liq_utilization_rate_bps: u16, - ) -> SolautoPosition { - let mut data = PositionData::default(); - data.setting_params = setting_params; - - data.dca = if active_dca.is_some() { - active_dca.unwrap() - } else { - DCASettings::default() - }; - - let mut position = SolautoPosition::new( - 1, - Pubkey::default(), - PositionType::default(), - data, - PositionState::default(), - ); - - position.state.liq_threshold_bps = 8000; - position.state.max_ltv_bps = 6500; - position.state.liq_utilization_rate_bps = current_liq_utilization_rate_bps; - - let supply_market_price = 100.0; - let supply_amount = 1000.0; - position.state.supply = create_token_usage( - supply_market_price, - 6, - supply_amount.mul(supply_market_price), - ); - - let debt_usd = supply_amount - .mul(supply_market_price) - .mul(from_bps(position.state.liq_threshold_bps)) - .mul(from_bps(current_liq_utilization_rate_bps)); - position.state.debt = create_token_usage(1.0, 6, debt_usd); - - position.rebalance = RebalanceData::default(); - - position - } - - fn create_token_usage( - market_price: f64, - decimals: u8, - amount_used_usd: f64, - ) -> PositionTokenUsage { - let mut token_usage = PositionTokenUsage::default(); - token_usage.decimals = decimals; - token_usage.amount_used.base_unit = - to_base_unit::(amount_used_usd.div(market_price), decimals); - token_usage.update_market_price(market_price); - token_usage - } - - fn test_rebalance( - current_timestamp: Option, - current_liq_utilization_rate_bps: u16, - setting_params: Option, - dca_settings: Option, - mut rebalance_args: Option, - ) -> Result<(SolautoPosition, f64, Option), ProgramError> { - let settings = setting_params.map_or_else(|| default_setting_params(), |settings| settings); - let mut solauto_position = standard_solauto_position( - settings.clone(), - dca_settings.clone(), - current_liq_utilization_rate_bps, - ); - - if rebalance_args.is_none() { - rebalance_args = Some(RebalanceSettings::default()); - } - - let solauto_fees = solauto_utils::SolautoFeesBps::from( - false, - rebalance_args - .clone() - .map_or(None, |args| args.target_liq_utilization_rate_bps), - solauto_position.state.net_worth.usd_value(), - ); - - let (debt_adjustment_usd, amount_to_dca_in) = get_rebalance_values( - &mut solauto_position, - rebalance_args.as_ref().unwrap(), - &solauto_fees, - current_timestamp.map_or_else(|| 0, |timestamp| timestamp), - )?; - - Ok((solauto_position, debt_adjustment_usd, amount_to_dca_in)) - } - - fn rebalance_with_std_validation( - current_timestamp: Option, - current_liq_utilization_rate_bps: u16, - mut expected_liq_utilization_rate_bps: u16, - setting_params: Option, - dca_settings: Option, - rebalance_args: Option, - ) -> Result { - let (mut solauto_position, debt_adjustment_usd, amount_to_dca_in) = test_rebalance( - current_timestamp, - current_liq_utilization_rate_bps, - setting_params, - dca_settings.clone(), - rebalance_args.clone(), - )?; - - let boosting = amount_to_dca_in.is_some() - || current_liq_utilization_rate_bps <= expected_liq_utilization_rate_bps; - if boosting { - expected_liq_utilization_rate_bps = max( - expected_liq_utilization_rate_bps, - current_liq_utilization_rate_bps, - ); - } - let rebalance_direction = if boosting { - RebalanceDirection::Boost - } else { - RebalanceDirection::Repay - }; - - let adjustment_fee_bps = solauto_utils::SolautoFeesBps::from( - false, - rebalance_args.map_or(None, |args| args.target_liq_utilization_rate_bps), - solauto_position.state.net_worth.usd_value(), - ) - .fetch_fees(rebalance_direction) - .total; - - let amount_to_dca_in_usd = if amount_to_dca_in.is_some() { - if dca_settings.as_ref().unwrap().token_type == TokenType::Supply { - from_base_unit::( - amount_to_dca_in.unwrap(), - solauto_position.state.supply.decimals, - ) - .mul(solauto_position.state.supply.market_price()) - } else { - from_base_unit::( - amount_to_dca_in.unwrap(), - solauto_position.state.debt.decimals, - ) - .mul(solauto_position.state.debt.market_price()) - } - } else { - 0.0 - }; - - let expected_debt_adjustment_usd = math_utils::get_std_debt_adjustment_usd( - from_bps(solauto_position.state.liq_threshold_bps), - solauto_position.state.supply.amount_used.usd_value() + amount_to_dca_in_usd, - solauto_position.state.debt.amount_used.usd_value(), - expected_liq_utilization_rate_bps, - adjustment_fee_bps, - ); - println!("{}, {}", debt_adjustment_usd, expected_debt_adjustment_usd); - assert!(debt_adjustment_usd == expected_debt_adjustment_usd); - - // Factor into account the adjustment fee - let mut supply_adjustment = (expected_debt_adjustment_usd + amount_to_dca_in_usd) - .sub(expected_debt_adjustment_usd.mul(from_bps(adjustment_fee_bps))); - if dca_settings.is_some() && dca_settings.as_ref().unwrap().token_type == TokenType::Debt { - supply_adjustment -= amount_to_dca_in_usd.mul(from_bps(adjustment_fee_bps)); - } - let supply_adjustment = supply_adjustment.div(solauto_position.state.supply.market_price()); - - solauto_position.update_usage( - TokenType::Supply, - to_base_unit::(supply_adjustment, solauto_position.state.supply.decimals), - ); - - let debt_adjustment = - expected_debt_adjustment_usd.div(solauto_position.state.debt.market_price()); - solauto_position.update_usage( - TokenType::Debt, - to_base_unit::(debt_adjustment, solauto_position.state.debt.decimals), - ); - - assert_bps_within_margin_of_error( - solauto_position.state.liq_utilization_rate_bps, - expected_liq_utilization_rate_bps, - ); - - Ok(solauto_position) - } - - #[test] - fn test_invalid_rebalance_condition() { - let result = test_rebalance(None, 6250, None, None, None); - assert!(result.is_err()); - assert!(result.unwrap_err() == SolautoError::InvalidRebalanceCondition.into()); - - let result = test_rebalance(None, 4001, None, None, None); - assert!(result.is_err()); - assert!(result.unwrap_err() == SolautoError::InvalidRebalanceCondition.into()); - - let result = test_rebalance(None, 7999, None, None, None); - assert!(result.is_err()); - assert!(result.unwrap_err() == SolautoError::InvalidRebalanceCondition.into()); - } - - #[test] - fn test_repay() { - rebalance_with_std_validation(None, REPAY_TO_BPS + 534, REPAY_TO_BPS, None, None, None) - .unwrap(); - rebalance_with_std_validation(None, REPAY_TO_BPS + 1003, REPAY_TO_BPS, None, None, None) - .unwrap(); - rebalance_with_std_validation(None, REPAY_TO_BPS + 1743, REPAY_TO_BPS, None, None, None) - .unwrap(); - } - - #[test] - fn test_boost() { - rebalance_with_std_validation(None, BOOST_TO_BPS - 3657, BOOST_TO_BPS, None, None, None) - .unwrap(); - rebalance_with_std_validation(None, BOOST_TO_BPS - 2768, BOOST_TO_BPS, None, None, None) - .unwrap(); - rebalance_with_std_validation(None, BOOST_TO_BPS - 1047, BOOST_TO_BPS, None, None, None) - .unwrap(); - } - - #[test] - fn test_authority_rebalance() { - let target_liq_utilization_rate_bps = BOOST_TO_BPS + (REPAY_TO_BPS - BOOST_TO_BPS) / 2; - rebalance_with_std_validation( - None, - BOOST_TO_BPS - 3657, - target_liq_utilization_rate_bps, - None, - None, - Some(RebalanceSettings { - rebalance_type: SolautoRebalanceType::Regular, - target_liq_utilization_rate_bps: Some(target_liq_utilization_rate_bps), - target_in_amount_base_unit: None, - }), - ) - .unwrap(); - } - - fn test_dca_rebalance_with_std_validation( - current_timestamp: Option, - current_liq_utilization_rate_bps: u16, - dca_settings: DCASettings, - setting_params: Option, - ) -> Result { - let settings = - Some(setting_params.map_or_else(|| default_setting_params(), |settings| settings)); - - let expected_liq_utilization_rate_bps = if dca_settings.dca_in() { - max( - current_liq_utilization_rate_bps, - settings.as_ref().unwrap().boost_to_bps, - ) - } else { - settings.as_ref().unwrap().boost_to_bps - }; - - let timestamp = current_timestamp.map_or_else( - || { - dca_settings.automation.unix_start_date.add( - dca_settings - .automation - .interval_seconds - .mul(dca_settings.automation.periods_passed as u64), - ) - }, - |timestamp| timestamp, - ); - let solauto_position = rebalance_with_std_validation( - Some(timestamp), - current_liq_utilization_rate_bps, - expected_liq_utilization_rate_bps, - settings.clone(), - Some(dca_settings.clone()), - None, - )?; - - let new_periods_passed = dca_settings.automation.new_periods_passed(timestamp); - if new_periods_passed == dca_settings.automation.target_periods { - assert!( - !solauto_position.position.dca.is_active(), - "DCA is still active when it shouldn't be" - ); - } else { - assert!( - solauto_position.position.dca.automation.periods_passed == new_periods_passed, - "periods_passed != new_periods_passed" - ); - } - - Ok(solauto_position) - } - - #[test] - fn test_invalid_dca_condition() { - let result = test_dca_rebalance_with_std_validation( - Some(9), - BOOST_TO_BPS + 500, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 4, - periods_passed: 2, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit: 0, - token_type: TokenType::Supply, - }), - None, - ); - assert!( - result.is_err() - && result.unwrap_err() == SolautoError::InvalidRebalanceCondition.into() - ); - } - - #[test] - fn test_dca_in() { - let dca_in_base_unit: u64 = 10000000; - - // curr_liq_utilization_rate_bps > setting_params.boost_to_bps - test_dca_rebalance_with_std_validation( - None, - BOOST_TO_BPS + 1000, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 10, - periods_passed: 4, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit, - token_type: TokenType::Debt, - }), - None, - ) - .unwrap(); - - // curr_liq_utilization_rate_bps < setting_params.boost_to_bps - test_dca_rebalance_with_std_validation( - None, - BOOST_TO_BPS - 1000, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 10, - periods_passed: 4, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit, - token_type: TokenType::Debt, - }), - None, - ) - .unwrap(); - - // curr_liq_utilization_rate_bps == setting_params.boost_to_bps - // last dca period - test_dca_rebalance_with_std_validation( - None, - BOOST_TO_BPS, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 10, - periods_passed: 9, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit, - token_type: TokenType::Debt, - }), - None, - ) - .unwrap(); - } - - #[test] - fn test_dca_out() { - // curr_liq_utilization_rate_bps > setting_params.boost_to_bps - test_dca_rebalance_with_std_validation( - None, - BOOST_TO_BPS + 1000, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 5, - periods_passed: 3, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit: 0, - token_type: TokenType::Supply, - }), - None, - ) - .unwrap(); - - // curr_liq_utilization_rate_bps < setting_params.boost_to_bps - test_dca_rebalance_with_std_validation( - None, - BOOST_TO_BPS - 1000, - DCASettings::from(DCASettingsInp { - automation: AutomationSettingsInp { - target_periods: 5, - periods_passed: 3, - unix_start_date: 0, - interval_seconds: 5, - }, - dca_in_base_unit: 0, - token_type: TokenType::Supply, - }), - None, - ) - .unwrap(); - } -} diff --git a/programs/solauto/src/utils/solana_utils.rs b/programs/solauto/src/utils/solana_utils.rs index 3fb7aa15..3c4cc00c 100644 --- a/programs/solauto/src/utils/solana_utils.rs +++ b/programs/solauto/src/utils/solana_utils.rs @@ -2,19 +2,20 @@ use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, - msg, program::{invoke, invoke_signed}, pubkey::Pubkey, rent::Rent, system_instruction, sysvar::Sysvar, }; -use spl_associated_token_account::{ - get_associated_token_address, instruction::create_associated_token_account, -}; +use spl_associated_token_account::instruction::create_associated_token_account; use spl_token::instruction as spl_instruction; -use crate::types::shared::SolautoError; +use crate::{ + check, + types::{errors::SolautoError, shared::SplTokenTransferArgs}, + utils::validation_utils::correct_token_account, +}; pub fn account_has_data(account: &AccountInfo) -> bool { !account.data.borrow().is_empty() @@ -40,10 +41,7 @@ pub fn init_account<'a>( account_seed: Option>, space: usize, ) -> ProgramResult { - if account_has_data(account) { - msg!("{} has already been created", account.key); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!(!account_has_data(account), SolautoError::IncorrectAccounts); let rent = &Rent::from_account_info(rent_sysvar)?; let required_lamports = rent @@ -78,15 +76,10 @@ pub fn init_ata_if_needed<'a, 'b>( token_account: &'a AccountInfo<'a>, token_mint: &'a AccountInfo<'a>, ) -> ProgramResult { - if &get_associated_token_address(wallet.key, token_mint.key) != token_account.key { - msg!( - "Token account {} is not correct for the given token mint ({}) & wallet ({})", - token_account.key, - token_mint.key, - wallet.key - ); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + correct_token_account(token_account.key, wallet.key, token_mint.key), + SolautoError::IncorrectAccounts + ); if account_has_data(token_account) { return Ok(()); @@ -118,25 +111,25 @@ pub fn system_transfer<'a>( ) } -pub fn spl_token_transfer<'a>( +pub fn spl_token_transfer<'a, 'b>( token_program: &'a AccountInfo<'a>, - source: &'a AccountInfo<'a>, - authority: &'a AccountInfo<'a>, - recipient: &'a AccountInfo<'a>, - amount: u64, - authority_seeds: Option<&Vec<&[u8]>>, + args: SplTokenTransferArgs<'a, 'b>, ) -> ProgramResult { invoke_instruction( &spl_instruction::transfer( token_program.key, - source.key, - recipient.key, - authority.key, + args.source.key, + args.recipient.key, + args.authority.key, &[], - amount, + args.amount, )?, - &[source.clone(), recipient.clone(), authority.clone()], - authority_seeds, + &[ + args.source.clone(), + args.recipient.clone(), + args.authority.clone(), + ], + args.authority_seeds, ) } diff --git a/programs/solauto/src/utils/solauto_utils.rs b/programs/solauto/src/utils/solauto_utils.rs index 5ca2030b..a9319718 100644 --- a/programs/solauto/src/utils/solauto_utils.rs +++ b/programs/solauto/src/utils/solauto_utils.rs @@ -1,27 +1,27 @@ use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, sysvar::Sysvar + account_info::AccountInfo, clock::Clock, program_error::ProgramError, pubkey::Pubkey, + sysvar::Sysvar, }; -use spl_associated_token_account::get_associated_token_address; use spl_token::state::{Account as TokenAccount, Mint}; -use std::ops::Mul; use super::{ math_utils::to_bps, - solana_utils::{account_has_data, init_account, init_ata_if_needed, spl_token_transfer}, + solana_utils::{account_has_data, init_account}, }; use crate::{ + check, constants::WSOL_MINT, state::{ referral_state::ReferralState, solauto_position::{ - DCASettings, PositionData, PositionState, SolautoPosition, SolautoSettingsParameters, + PositionData, PositionState, PositionTokenState, SolautoPosition, + SolautoSettingsParameters, }, }, types::{ + errors::SolautoError, instruction::UpdatePositionData, - shared::{ - DeserializedAccount, LendingPlatform, PositionType, RebalanceDirection, SolautoError, - }, + shared::{DeserializedAccount, LendingPlatform, PositionType, RefreshedTokenState}, }, }; @@ -43,23 +43,24 @@ pub fn create_new_solauto_position<'a>( update_position_data: UpdatePositionData, lending_platform: LendingPlatform, supply_mint: &'a AccountInfo<'a>, - protocol_supply_account: &'a AccountInfo<'a>, + lp_supply_account: &'a AccountInfo<'a>, debt_mint: &'a AccountInfo<'a>, - protocol_debt_account: &'a AccountInfo<'a>, - protocol_user_account: &'a AccountInfo<'a>, + lp_debt_account: &'a AccountInfo<'a>, + lp_user_account: &'a AccountInfo<'a>, + lp_pool_account: &'a AccountInfo<'a>, max_ltv: f64, liq_threshold: f64, ) -> Result, ProgramError> { - if account_has_data(solauto_position) { - msg!("Cannot open new position on an existing Solauto position"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + !account_has_data(solauto_position), + SolautoError::IncorrectAccounts + ); - let data = if update_position_data.setting_params.is_some() { - if update_position_data.position_id == 0 { - msg!("Position ID 0 is reserved for self-managed positions"); - return Err(ProgramError::InvalidInstructionData.into()); - } + let data = if update_position_data.settings.is_some() { + check!( + update_position_data.position_id > 0, + SolautoError::IncorrectInstructions + ); let supply = DeserializedAccount::::unpack(Some(supply_mint))?.unwrap(); let debt = DeserializedAccount::::unpack(Some(debt_mint))?.unwrap(); @@ -70,19 +71,16 @@ pub fn create_new_solauto_position<'a>( state.debt.decimals = debt.data.decimals; state.max_ltv_bps = to_bps(max_ltv); state.liq_threshold_bps = to_bps(liq_threshold); - state.last_updated = Clock::get()?.unix_timestamp as u64; + state.last_refreshed = Clock::get()?.unix_timestamp as u64; let mut position_data = PositionData::default(); position_data.lending_platform = lending_platform; - position_data.setting_params = - SolautoSettingsParameters::from(*update_position_data.setting_params.as_ref().unwrap()); - position_data.protocol_user_account = *protocol_user_account.key; - position_data.protocol_supply_account = *protocol_supply_account.key; - position_data.protocol_debt_account = *protocol_debt_account.key; - - if update_position_data.dca.is_some() { - position_data.dca = DCASettings::from(*update_position_data.dca.as_ref().unwrap()); - } + position_data.settings = + SolautoSettingsParameters::from(*update_position_data.settings.as_ref().unwrap()); + position_data.lp_user_account = *lp_user_account.key; + position_data.lp_supply_account = *lp_supply_account.key; + position_data.lp_debt_account = *lp_debt_account.key; + position_data.lp_pool_account = *lp_pool_account.key; Box::new(SolautoPosition::new( update_position_data.position_id, @@ -177,183 +175,20 @@ pub fn create_or_update_referral_state<'a>( referral_state_account.data.seeds_with_bump().as_slice(), &crate::ID, )?; - if referral_state.key != &expected_referral_state_address { - msg!("Invalid referral position account given for the provided authority"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + referral_state.key == &expected_referral_state_address, + SolautoError::IncorrectAccounts + ); Ok(referral_state_account) } -pub fn initiate_dca_in_if_necessary<'a, 'b>( - token_program: &'a AccountInfo<'a>, - solauto_position: &'b mut DeserializedAccount<'a, SolautoPosition>, - position_debt_ta: Option<&'a AccountInfo<'a>>, - signer: &'a AccountInfo<'a>, - signer_dca_ta: Option<&'a AccountInfo<'a>>, -) -> ProgramResult { - if solauto_position.data.self_managed.val { - return Ok(()); - } - - let position = &mut solauto_position.data.position; - if !position.dca.is_active() { - return Ok(()); - } - - if !position.dca.dca_in() { - return Ok(()); - } - - if position_debt_ta.is_none() || signer_dca_ta.is_none() { - msg!("Missing required accounts in order to initiate DCA-in"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - if position_debt_ta.unwrap().key - != &get_associated_token_address( - solauto_position.account_info.key, - &solauto_position.data.state.debt.mint, - ) - { - msg!("Incorrect position token account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } - - let signer_token_account = TokenAccount::unpack(&signer_dca_ta.unwrap().data.borrow())?; - let balance = signer_token_account.amount; - - if position.dca.dca_in_base_unit > balance { - msg!("Provided greater DCA-in value than exists in the signer debt token account"); - return Err(ProgramError::InvalidInstructionData.into()); - } - - spl_token_transfer( - token_program, - signer_dca_ta.unwrap(), - signer, - position_debt_ta.unwrap(), - position.dca.dca_in_base_unit, - None, - )?; - - Ok(()) -} - -pub fn cancel_dca_in<'a, 'b>( - signer: &'a AccountInfo<'a>, - system_program: &'a AccountInfo<'a>, - token_program: &'a AccountInfo<'a>, - solauto_position: &'b mut DeserializedAccount<'a, SolautoPosition>, - dca_mint: Option<&'a AccountInfo<'a>>, - position_dca_ta: Option<&'a AccountInfo<'a>>, - signer_dca_ta: Option<&'a AccountInfo<'a>>, -) -> ProgramResult { - let active_dca = &solauto_position.data.position.dca; - - if active_dca.dca_in() { - if dca_mint.is_none() || position_dca_ta.is_none() || signer_dca_ta.is_none() { - msg!( - "Requires dca_mint, position_dca_ta & signer_dca_ta in order to cancel the active DCA-in" - ); - return Err(SolautoError::IncorrectAccounts.into()); - } - - let dca_ta_current_balance = - TokenAccount::unpack(&position_dca_ta.unwrap().data.borrow())?.amount; - if dca_ta_current_balance == 0 { - return Ok(()); - } - - init_ata_if_needed( - token_program, - system_program, - signer, - signer, - signer_dca_ta.unwrap(), - dca_mint.unwrap(), - )?; - - spl_token_transfer( - token_program, - position_dca_ta.unwrap(), - solauto_position.account_info, - signer_dca_ta.unwrap(), - dca_ta_current_balance, - Some(&solauto_position.data.seeds_with_bump()), - )?; - } - - solauto_position.data.position.dca = DCASettings::default(); - Ok(()) -} - -pub struct FeePayout { - pub solauto: u16, - pub referrer: u16, - pub total: u16, -} -pub struct SolautoFeesBps { - has_been_referred: bool, - target_liq_utilization_rate_bps: Option, - position_net_worth_usd: f64, -} -impl SolautoFeesBps { - pub fn from( - has_been_referred: bool, - target_liq_utilization_rate_bps: Option, - position_net_worth_usd: f64, - ) -> Self { - Self { - has_been_referred, - target_liq_utilization_rate_bps, - position_net_worth_usd, - } - } - pub fn fetch_fees(&self, rebalance_direction: RebalanceDirection) -> FeePayout { - let min_size: f64 = 10000.0; // Minimum position size - let max_size: f64 = 500000.0; // Maximum position size - let max_fee_bps: f64 = 200.0; // Fee in basis points for min_size (2%) - let min_fee_bps: f64 = 50.0; // Fee in basis points for max_size (0.5%) - let k = 1.5; - - let mut fee_bps: f64; - if self.target_liq_utilization_rate_bps.is_some() - && self.target_liq_utilization_rate_bps.unwrap() == 0 - { - return FeePayout { - solauto: 0, - referrer: 0, - total: 0, - }; - } - - if self.target_liq_utilization_rate_bps.is_some() - || rebalance_direction == RebalanceDirection::Repay - { - fee_bps = 25.0; - } else if self.position_net_worth_usd <= min_size { - fee_bps = max_fee_bps; - } else if self.position_net_worth_usd >= max_size { - fee_bps = min_fee_bps; - } else { - let t = (self.position_net_worth_usd.ln() - min_size.ln()) - / (max_size.ln() - min_size.ln()); - fee_bps = (min_fee_bps + (max_fee_bps - min_fee_bps) * (1.0 - t.powf(k))).round(); - } - - let mut referrer_fee = 0.0; - if self.has_been_referred { - fee_bps = fee_bps * 0.9; - referrer_fee = fee_bps.mul(0.15).floor() - }; - - FeePayout { - solauto: (fee_bps - referrer_fee) as u16, - referrer: referrer_fee as u16, - total: fee_bps as u16, - } - } +pub fn update_token_state(token_state: &mut PositionTokenState, token_data: &RefreshedTokenState) { + token_state.decimals = token_data.decimals; + token_state.amount_used.base_unit = token_data.amount_used; + token_state.amount_can_be_used.base_unit = token_data.amount_can_be_used; + token_state.update_market_price(token_data.market_price); + token_state.borrow_fee_bps = token_data.borrow_fee_bps.unwrap_or(0); } pub fn safe_unpack_token_account<'a>( diff --git a/programs/solauto/src/utils/validation_utils.rs b/programs/solauto/src/utils/validation_utils.rs index 14b748c7..17303cd4 100644 --- a/programs/solauto/src/utils/validation_utils.rs +++ b/programs/solauto/src/utils/validation_utils.rs @@ -1,9 +1,6 @@ -use std::ops::{Div, Mul}; +use std::ops::Div; -use marginfi_sdk::{ - generated::accounts::{Bank, MarginfiAccount}, - MARGINFI_ID, -}; +use marginfi_sdk::generated::accounts::{Bank, MarginfiAccount}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, @@ -17,21 +14,26 @@ use spl_associated_token_account::{get_associated_token_address, ID as ata_progr use spl_token::ID as token_program_id; use crate::{ - constants::{MAX_BASIS_POINTS, MIN_BOOST_GAP_BPS, MIN_REPAY_GAP_BPS, SOLAUTO_MANAGER}, + check, + constants::{ + MARGINFI_PROD_PROGRAM, MARGINFI_STAGING_PROGRAM, MIN_BOOST_GAP_BPS, MIN_REPAY_GAP_BPS, + SOLAUTO_MANAGER, + }, + error_if, state::{ - referral_state::ReferralState, - solauto_position::{AutomationSettings, PositionData, SolautoPosition}, + automation::AutomationSettings, referral_state::ReferralState, + solauto_position::SolautoPosition, }, types::{ + errors::SolautoError, instruction::SolautoStandardAccounts, - shared::{DeserializedAccount, LendingPlatform, SolautoError, TokenType}, + shared::{DeserializedAccount, LendingPlatform, TokenType}, }, + utils::math_utils::from_rounded_usd_value, }; use super::{ - math_utils::{ - from_base_unit, get_max_boost_to_bps, get_max_repay_from_bps, get_max_repay_to_bps, - }, + math_utils::{get_max_boost_to_bps, get_max_repay_from_bps, get_max_repay_to_bps}, solauto_utils::safe_unpack_token_account, }; @@ -48,7 +50,7 @@ pub fn generic_instruction_validation( solauto_managed_only_ix, )?; validate_lending_program_account(accounts.lending_protocol, lending_platform)?; - validate_sysvar_accounts( + validate_standard_programs( Some(accounts.system_program), Some(accounts.token_program), accounts.ata_program, @@ -65,7 +67,7 @@ pub fn generic_instruction_validation( )?; } - // The solauto_fees_ta is validated at payout before transfer + // The solauto_fees_ta is validated during rebalance in solauto_manager.rs because it requires up-to-date state Ok(()) } @@ -75,10 +77,7 @@ pub fn validate_instruction( authority_signer_only_ix: bool, solauto_managed_only_ix: bool, ) -> ProgramResult { - if !signer.is_signer { - msg!("Signer account is not a signer"); - return Err(ProgramError::MissingRequiredSignature.into()); - } + check!(&signer.is_signer, ProgramError::MissingRequiredSignature); let position_authority = solauto_position.data.authority; let authority_signed = || { @@ -102,103 +101,61 @@ pub fn validate_instruction( return Err(ProgramError::MissingRequiredSignature.into()); } - if solauto_managed_only_ix && solauto_position.data.self_managed.val { - msg!("Cannot perform the desired instruction on a self-managed position"); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + solauto_managed_only_ix && solauto_position.data.self_managed.val, + SolautoError::IncorrectAccounts + ); - if solauto_position.data.self_managed.val && signer.key == &SOLAUTO_MANAGER { - msg!("Solauto manager cannot sign an instruction on a self-managed position"); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + solauto_position.data.self_managed.val && signer.key == &SOLAUTO_MANAGER, + SolautoError::IncorrectAccounts + ); Ok(()) } -pub fn validate_position_settings( - solauto_position: &SolautoPosition, - current_unix_timestamp: u64, -) -> ProgramResult { - let invalid_params = |error_msg| { - msg!(error_msg); - Err(SolautoError::InvalidPositionSettings.into()) - }; - - let data = &solauto_position.position; - if data.setting_params.repay_to_bps < data.setting_params.boost_to_bps { - return invalid_params("repay_to_bps value must be greater than boost_to_bps value"); - } - let max_boost_to = get_max_boost_to_bps( +pub fn validate_position_settings(solauto_position: &SolautoPosition) -> ProgramResult { + let max_boost_to_bps = get_max_boost_to_bps( solauto_position.state.max_ltv_bps, solauto_position.state.liq_threshold_bps, ); - if data.setting_params.boost_to_bps > max_boost_to { - return invalid_params( - format!("Exceeds the maximum boost-to of {}", max_boost_to).as_str(), - ); - } - if data.setting_params.repay_to_bps < data.setting_params.target_boost_to_bps { - return invalid_params("repay_to_bps value must be greater than target_boost_to_bps value"); - } - - if data.setting_params.repay_gap < MIN_REPAY_GAP_BPS { - return invalid_params( - format!("repay_gap must be {} or greater", MIN_REPAY_GAP_BPS).as_str(), - ); - } - if data.setting_params.boost_gap < MIN_BOOST_GAP_BPS { - return invalid_params( - format!("boost_gap must be {} or greater", MIN_BOOST_GAP_BPS).as_str(), - ); - } - - if data.setting_params.automation.is_active() { - validate_automation_settings(&data.setting_params.automation, current_unix_timestamp)?; - } - - if data.setting_params.target_boost_to_bps > MAX_BASIS_POINTS { - return invalid_params( - format!("target_boost_to_bps must be less than {}", MAX_BASIS_POINTS).as_str(), - ); - } - let max_repay_to_bps = get_max_repay_to_bps( solauto_position.state.max_ltv_bps, solauto_position.state.liq_threshold_bps, ); - if data.setting_params.repay_to_bps > max_repay_to_bps { - return invalid_params( - format!("For the given max_ltv and liq_threshold of the supplied asset, repay_to_bps must be lower or equal to {} in order to bring the utilization rate to an allowed position", max_repay_to_bps).as_str() - ); - } let max_repay_from_bps = get_max_repay_from_bps( solauto_position.state.max_ltv_bps, solauto_position.state.liq_threshold_bps, ); - if data.setting_params.repay_from_bps() > max_repay_from_bps { - return invalid_params( - format!( - "repay_to_bps + repay_gap must be equal-to or below {}", - max_repay_from_bps - ) - .as_str(), - ); - } + let data = &solauto_position.position; + check!( + data.settings.repay_to_bps >= data.settings.boost_to_bps, + SolautoError::InvalidRepayToSetting + ); + check!( + data.settings.boost_to_bps <= max_boost_to_bps, + SolautoError::InvalidBoostToSetting + ); + check!( + data.settings.repay_gap >= MIN_REPAY_GAP_BPS, + SolautoError::InvalidRepayGapSetting + ); + check!( + data.settings.boost_gap >= MIN_BOOST_GAP_BPS, + SolautoError::InvalidBoostGapSetting + ); + check!( + data.settings.repay_to_bps <= max_repay_to_bps, + SolautoError::InvalidRepayToSetting + ); + check!( + data.settings.repay_to_bps + data.settings.repay_gap <= max_repay_from_bps, + SolautoError::InvalidRepayFromSetting + ); Ok(()) } -pub fn validate_dca_settings( - position: &PositionData, - current_unix_timestamp: u64, -) -> ProgramResult { - if position.dca.is_active() { - return Ok(()); - } - - validate_automation_settings(&position.dca.automation, current_unix_timestamp) -} - pub fn validate_automation_settings( automation: &AutomationSettings, current_unix_timestamp: u64, @@ -235,43 +192,43 @@ pub fn validate_lending_program_account( ) -> ProgramResult { match lending_platform { LendingPlatform::Marginfi => { - if *program.key != MARGINFI_ID { - msg!("Incorrect Marginfi program account"); - return Err(ProgramError::IncorrectProgramId.into()); - } + check!( + *program.key == MARGINFI_PROD_PROGRAM || *program.key == MARGINFI_STAGING_PROGRAM, + SolautoError::IncorrectAccounts + ); } } // We don't need to check more than this, as lending protocols have their own account checks and will fail during CPI if there is an issue with the provided accounts Ok(()) } -pub fn validate_sysvar_accounts( +pub fn validate_standard_programs( system_program: Option<&AccountInfo>, token_program: Option<&AccountInfo>, ata_program: Option<&AccountInfo>, rent: Option<&AccountInfo>, ixs_sysvar: Option<&AccountInfo>, ) -> ProgramResult { - if system_program.is_some() && system_program.unwrap().key != &system_program_id { - msg!("Incorrect system program account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } - if token_program.is_some() && token_program.unwrap().key != &token_program_id { - msg!("Incorrect token program account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } - if ata_program.is_some() && ata_program.unwrap().key != &ata_program_id { - msg!("Incorrect ata program account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } - if rent.is_some() && rent.unwrap().key != &rent_program_id { - msg!("Incorrect rent program account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } - if ixs_sysvar.is_some() && ixs_sysvar.unwrap().key != &ixs_sysvar_id { - msg!("Incorrect ixs sysvar program account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!( + system_program.is_none() || system_program.unwrap().key == &system_program_id, + SolautoError::IncorrectAccounts + ); + check!( + token_program.is_none() || token_program.unwrap().key == &token_program_id, + SolautoError::IncorrectAccounts + ); + check!( + ata_program.is_none() || ata_program.unwrap().key == &ata_program_id, + SolautoError::IncorrectAccounts + ); + check!( + rent.is_none() || rent.unwrap().key == &rent_program_id, + SolautoError::IncorrectAccounts + ); + check!( + ixs_sysvar.is_none() || ixs_sysvar.unwrap().key == &ixs_sysvar_id, + SolautoError::IncorrectAccounts + ); Ok(()) } @@ -285,21 +242,21 @@ pub fn validate_referral_accounts<'a>( authority_referral_state.data.seeds_with_bump().as_slice(), &crate::ID, )?; - if &authority_referral_state.data.authority != referral_state_authority - || &referral_state_pda != authority_referral_state.account_info.key - { - msg!("Invalid referral state account given for the provided authority"); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + &authority_referral_state.data.authority != referral_state_authority + || &referral_state_pda != authority_referral_state.account_info.key, + SolautoError::IncorrectAccounts + ); let authority_referred_by_state = &authority_referral_state.data.referred_by_state; - if validate_ta && authority_referred_by_state != &Pubkey::default() && referred_by_ta.is_none() - { - msg!("Did not provide a referred_by_ta when the authority been referred"); - return Err(SolautoError::IncorrectAccounts.into()); - } - // The referred_by_ta is further validated at payout before transfer + error_if!( + validate_ta + && authority_referred_by_state != &Pubkey::default() + && referred_by_ta.is_none(), + SolautoError::IncorrectAccounts + ); + // The referred_by_ta is validated during rebalance in solauto_manager.rs because it requires up-to-date state Ok(()) } @@ -313,34 +270,31 @@ pub fn validate_marginfi_bank<'a>( } let bank = DeserializedAccount::::zerocopy(Some(marginfi_bank))?.unwrap(); - if &bank.data.mint != mint { - msg!("Provided incorrect bank account"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!(&bank.data.mint == mint, SolautoError::IncorrectAccounts); + Ok(()) } pub fn validate_lending_program_accounts_with_position<'a>( lending_platform: LendingPlatform, solauto_position: &DeserializedAccount, - protocol_user_account: &'a AccountInfo<'a>, - protocol_supply_account: &'a AccountInfo<'a>, - protocol_debt_account: &'a AccountInfo<'a>, + lp_user_account: &'a AccountInfo<'a>, + lp_supply_account: &'a AccountInfo<'a>, + lp_debt_account: &'a AccountInfo<'a>, ) -> ProgramResult { let supply_mint = &solauto_position.data.state.supply.mint; let debt_mint = &solauto_position.data.state.debt.mint; - if !solauto_position.data.self_managed.val - && protocol_user_account.key != &solauto_position.data.position.protocol_user_account - { - msg!("Incorrect protocol-owned account"); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + !solauto_position.data.self_managed.val + && lp_user_account.key != &solauto_position.data.position.lp_user_account, + SolautoError::IncorrectAccounts + ); match lending_platform { LendingPlatform::Marginfi => { - validate_marginfi_bank(protocol_supply_account, &supply_mint)?; - validate_marginfi_bank(protocol_debt_account, &debt_mint)?; + validate_marginfi_bank(lp_supply_account, &supply_mint)?; + validate_marginfi_bank(lp_debt_account, &debt_mint)?; } } @@ -393,13 +347,12 @@ pub fn validate_token_account<'a>( let associated_authority_ta = get_associated_token_address(&solauto_position.data.authority, mint_key); - if source_ta.is_some() - && source_ta.unwrap().key != &associated_position_ta - && source_ta.unwrap().key != &associated_authority_ta - { - msg!("Incorrect token account {}", source_ta.unwrap().key); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + source_ta.is_some() + && source_ta.unwrap().key != &associated_position_ta + && source_ta.unwrap().key != &associated_authority_ta, + SolautoError::IncorrectAccounts + ); Ok(()) } @@ -429,47 +382,41 @@ pub fn validate_referral_signer( referral_state.data.seeds_with_bump().as_slice(), &crate::ID, )?; - if &referral_state_pda != referral_state.account_info.key { - msg!("Incorrect referral state account provided"); - return Err(SolautoError::IncorrectAccounts.into()); - } + check!(&signer.is_signer, ProgramError::MissingRequiredSignature); + check!( + &referral_state_pda == referral_state.account_info.key, + SolautoError::IncorrectAccounts + ); - if !signer.is_signer { - msg!("Missing required referral signer"); - return Err(ProgramError::MissingRequiredSignature.into()); - } - if signer.key != &referral_state.data.authority - && (!allow_solauto_manager || signer.key != &SOLAUTO_MANAGER) - { - msg!("Instruction has not been signed by the right account"); - return Err(SolautoError::IncorrectAccounts.into()); - } + error_if!( + signer.key != &referral_state.data.authority + && (!allow_solauto_manager || signer.key != &SOLAUTO_MANAGER), + SolautoError::IncorrectAccounts + ); Ok(()) } pub fn validate_no_active_balances<'a>( - protocol_account: &'a AccountInfo<'a>, + lp_user_account: &'a AccountInfo<'a>, lending_platform: LendingPlatform, ) -> ProgramResult { if lending_platform == LendingPlatform::Marginfi { let marginfi_account = - DeserializedAccount::::zerocopy(Some(protocol_account))?.unwrap(); - if marginfi_account - .data - .lending_account - .balances - .iter() - .filter(|balance| balance.active == 1) - .collect::>() - .len() - > 0 - { - msg!( - "Marginfi account has active balances. Ensure all debt is repaid and supply tokens are withdrawn before closing position" - ); - return Err(SolautoError::IncorrectAccounts.into()); - } + DeserializedAccount::::zerocopy(Some(lp_user_account))?.unwrap(); + + check!( + marginfi_account + .data + .lending_account + .balances + .iter() + .filter(|balance| balance.active == 1) + .collect::>() + .len() + == 0, + SolautoError::IncorrectAccounts + ); Ok(()) } else { @@ -478,44 +425,80 @@ pub fn validate_no_active_balances<'a>( } } -pub fn validate_debt_adjustment( - solauto_position: &SolautoPosition, - provided_base_unit_amount: u64, - expected_debt_adjustment_usd: f64, -) -> ProgramResult { - let token = if expected_debt_adjustment_usd > 0.0 { - solauto_position.state.debt - } else { - solauto_position.state.supply - }; +pub fn validate_rebalance(solauto_position: &SolautoPosition) -> ProgramResult { + let curr_supply_usd = solauto_position.state.supply.amount_used.usd_value(); + let curr_debt_usd = solauto_position.state.debt.amount_used.usd_value(); - let amount_usd = from_base_unit::(provided_base_unit_amount, token.decimals) - .mul(token.market_price()); + let target_supply_usd = + from_rounded_usd_value(solauto_position.rebalance.values.target_supply_usd); + let target_debt_usd = from_rounded_usd_value(solauto_position.rebalance.values.target_debt_usd); - // Checking if within specified range due to varying price volatility - if (amount_usd - expected_debt_adjustment_usd.abs()) - .abs() - .div(amount_usd) - > 0.75 - { - msg!("Base unit amount provided: {}", provided_base_unit_amount); - msg!( - "Provided debt adjustment was not what was expected (Provided: ${} vs. expected: ${})", - amount_usd.abs(), - expected_debt_adjustment_usd.abs() - ); - return Err(SolautoError::IncorrectDebtAdjustment.into()); - } + msg!( + "Supply expected vs. actual: {}, {}", + target_supply_usd, + curr_supply_usd + ); + msg!( + "Debt expected vs. actual: {}, {}", + target_debt_usd, + curr_debt_usd + ); + check!( + value_gte_with_threshold(curr_supply_usd, target_supply_usd, 0.15), + SolautoError::InvalidRebalanceMade + ); + check!( + value_lte_with_threshold(curr_debt_usd, target_debt_usd, 0.15), + SolautoError::InvalidRebalanceMade + ); Ok(()) } +pub fn correct_token_account(token_account: &Pubkey, wallet: &Pubkey, mint: &Pubkey) -> bool { + token_account == &get_associated_token_address(wallet, mint) +} + +pub fn valid_token_account_for_mints( + token_account: &Pubkey, + wallet: &Pubkey, + mints: &Vec, +) -> bool { + mints + .iter() + .any(|mint| correct_token_account(token_account, wallet, mint)) +} + +pub fn value_lte_with_threshold(value: f64, target_value: f64, threshold: f64) -> bool { + if target_value == 0.0 { + return value == 0.0; + } + value < target_value || (value - target_value).abs().div(target_value) < threshold +} + +pub fn value_gte_with_threshold(value: f64, target_value: f64, threshold: f64) -> bool { + if target_value == 0.0 { + return value == 0.0; + } + value > target_value || (value - target_value).abs().div(target_value) < threshold +} + +pub fn value_match_with_threshold(value: f64, target_value: f64, threshold: f64) -> bool { + if target_value == 0.0 { + return value == 0.0; + } + (value - target_value).abs().div(target_value) < threshold +} + #[cfg(test)] mod tests { use crate::{ - state::solauto_position::{ - AutomationSettings, AutomationSettingsInp, PositionState, SolautoSettingsParameters, - SolautoSettingsParametersInp, + state::{ + automation::{AutomationSettings, AutomationSettingsInp}, + solauto_position::{ + PositionData, PositionState, SolautoSettingsParameters, + SolautoSettingsParametersInp, + }, }, types::shared::PositionType, }; @@ -525,7 +508,7 @@ mod tests { fn test_position_settings(settings: SolautoSettingsParameters, liq_threshold_bps: u16) { let mut position_data = PositionData::default(); position_data.lending_platform = LendingPlatform::Marginfi; - position_data.setting_params = settings; + position_data.settings = settings; let mut position_state = PositionState::default(); position_state.max_ltv_bps = 6500; @@ -538,7 +521,7 @@ mod tests { position_data, position_state, ); - let result = validate_position_settings(&solauto_position, 0); + let result = validate_position_settings(&solauto_position); assert!(result.is_err()); } @@ -604,13 +587,6 @@ mod tests { }), default_liq_threshold_bps, ); - test_position_settings( - SolautoSettingsParameters::from(SolautoSettingsParametersInp { - target_boost_to_bps: Some(10340), - ..default_settings_args - }), - default_liq_threshold_bps, - ); } fn test_automation_settings(current_timestamp: u64, automation_settings: AutomationSettings) { diff --git a/solauto-sdk/README.md b/solauto-sdk/README.md index eed98b40..23280571 100644 --- a/solauto-sdk/README.md +++ b/solauto-sdk/README.md @@ -1,3 +1,103 @@ -# Solauto SDK +## Solauto Typescript SDK -TODO \ No newline at end of file +Solauto is a program on the Solana blockchain that lets you manage leveraged longs & shorts on auto-pilot to maximize your gains and eliminate the risk of liquidation. The typescript SDK is made for interacting with the Solauto program. This SDK provides tools for managing, & reading Solauto state data, as well as executing transactions. + +## Basic Usage + +```typescript +import { PublicKey } from "@solana/web3.js"; +import { NATIVE_MINT } from "@solana/spl-token"; +import * as solauto from "@haven-fi/solauto-sdk"; + +// Create new Solauto client +const client = solauto.getClient(solauto.LendingPlatform.MARGINFI, { + signer: yourSigner, + rpcUrl: "[YOUR_RPC_URL]", +}); + +// Initialize the client +const supplyMint = NATIVE_MINT; +const debtMint = new PublicKey(solauto.USDC); +await client.initializeNewSolautoPosition({ + positionId: 1, + lpPoolAccount: solauto.getMarginfiAccounts().defaultGroup, + supplyMint, + debtMint, +}); + +// Open a position with custom settings +const [maxLtvBps, liqThresholdBps] = + await client.pos.maxLtvAndLiqThresholdBps(); +const settings: solauto.SolautoSettingsParametersInpArgs = { + boostToBps: solauto.maxBoostToBps(maxLtvBps, liqThresholdBps), + boostGap: 50, + repayToBps: solauto.maxRepayToBps(maxLtvBps, liqThresholdBps), + repayGap: 50, +}; + +const supplyUsdToDeposit = 100; +const debtUsdToBorrow = 60; +const [supplyPrice, debtPrice] = await solauto.fetchTokenPrices([ + supplyMint, + debtMint, +]); + +const transactionItems = [ + // Open position + solauto.openSolautoPosition(client, settings), + // Deposit supply (SOL) transaction + solauto.deposit( + client, + toBaseUnit( + supplyUsdToDeposit / supplyPrice, + client.pos.supplyMintInfo.decimals + ) + ), + // Borrow debt (USDC) transaction + solauto.borrow( + client, + toBaseUnit(debtUsdToBorrow / debtPrice, client.pos.debtMintInfo.decimals) + ), + // Rebalance to 0 LTV (repays all debt using collateral) + solauto.rebalance(client, 0), + // Withdraw remaining supply in position + solauto.withdraw(client, "All"), + // Close position + solauto.closeSolautoPosition(client), +]; + +// Send all transactions atomically +const txManager = await new solauto.ClientTransactionsManager({ + txHandler: client, +}); +const statuses = txManager.send(transactionItems); +``` + +## Rebalancing an existing position + +```typescript +import * as solauto from "@haven-fi/solauto-sdk"; + +// Create new Solauto client +const client = solauto.getClient(solauto.LendingPlatform.MARGINFI, { + signer: yourSigner, + rpcUrl: "[YOUR_RPC_URL]", +}); + +// Initialize the client +await client.initializeExistingSolautoPosition({ + positionId: myPositionId, +}); + +const transactionItems = [ + solauto.rebalance( + client, + undefined // Provide target liquidation utilization rate if you want a specific LTV, otherwise it will rebalance according to position's settings (default) + ), +]; + +const txManager = await new solauto.ClientTransactionsManager({ + txHandler: client, +}); +const statuses = txManager.send(transactionItems); +``` diff --git a/solauto-sdk/local/createISMAccounts.ts b/solauto-sdk/local/createISMAccounts.ts deleted file mode 100644 index d4e4041b..00000000 --- a/solauto-sdk/local/createISMAccounts.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - createSignerFromKeypair, - publicKey, - signerIdentity, - transactionBuilder, -} from "@metaplex-foundation/umi"; -import { - buildHeliusApiUrl, - getSolanaRpcConnection, - sendSingleOptimizedTransaction, -} from "../src/utils/solanaUtils"; -import { marginfiAccountInitialize, safeFetchAllMarginfiAccount } from "../src/marginfi-sdk"; -import { MARGINFI_ACCOUNTS, SOLAUTO_MANAGER } from "../src/constants"; -import { getSecretKey } from "./shared"; -import { updateSolautoLut } from "./updateSolautoLUT"; -import { getAllMarginfiAccountsByAuthority } from "../src/utils"; - -async function createIntermediarySolautoManagerAccounts() { - let [connection, umi] = getSolanaRpcConnection(buildHeliusApiUrl(process.env.HELIUS_API_KEY!)); - - const secretKey = getSecretKey("solauto-manager"); - const signerKeypair = umi.eddsa.createKeypairFromSecretKey(secretKey); - const signer = createSignerFromKeypair(umi, signerKeypair); - - umi = umi.use(signerIdentity(signer)); - - const accounts = await getAllMarginfiAccountsByAuthority(umi, SOLAUTO_MANAGER, undefined, false); - const data = await safeFetchAllMarginfiAccount(umi, accounts.map(x => publicKey(x.marginfiAccount))); - const existingMarginfiGroups = data.map(x => x.group.toString()); - - for (const group of Object.keys(MARGINFI_ACCOUNTS)) { - if (existingMarginfiGroups.includes(group.toString())) { - console.log("Already have Solauto Manager Marginfi Account for group:", group); - continue; - } - - const marginfiAccount = createSignerFromKeypair( - umi, - umi.eddsa.generateKeypair() - ); - - const tx = marginfiAccountInitialize(umi, { - marginfiAccount, - marginfiGroup: publicKey(group), - authority: signer, - feePayer: signer, - }); - - await sendSingleOptimizedTransaction( - umi, - connection, - transactionBuilder().add(tx) - ); - - await updateSolautoLut([marginfiAccount.publicKey.toString()]); - } -} - -createIntermediarySolautoManagerAccounts(); diff --git a/solauto-sdk/local/createTokenAccounts.ts b/solauto-sdk/local/createTokenAccounts.ts index 33248835..2d317d20 100644 --- a/solauto-sdk/local/createTokenAccounts.ts +++ b/solauto-sdk/local/createTokenAccounts.ts @@ -1,11 +1,16 @@ import { PublicKey } from "@solana/web3.js"; +import { + createSignerFromKeypair, + publicKey, + signerIdentity, + transactionBuilder, +} from "@metaplex-foundation/umi"; import { ALL_SUPPORTED_TOKENS, + LOCAL_IRONFORGE_API_URL, SOLAUTO_FEES_WALLET, - SOLAUTO_MANAGER, } from "../src/constants"; import { - buildHeliusApiUrl, createAssociatedTokenAccountUmiIx, getSolanaRpcConnection, getTokenAccount, @@ -13,17 +18,9 @@ import { zip, } from "../src/utils"; import { getSecretKey } from "./shared"; -import { - createSignerFromKeypair, - publicKey, - signerIdentity, - transactionBuilder, -} from "@metaplex-foundation/umi"; async function createTokenAccounts(wallet: PublicKey) { - let [connection, umi] = getSolanaRpcConnection( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!) - ); + let [connection, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); const secretKey = getSecretKey(); const signerKeypair = umi.eddsa.createKeypairFromSecretKey(secretKey); @@ -53,4 +50,4 @@ async function createTokenAccounts(wallet: PublicKey) { } } -createTokenAccounts(SOLAUTO_FEES_WALLET); +createTokenAccounts(SOLAUTO_FEES_WALLET).catch((e) => console.log(e)); diff --git a/solauto-sdk/local/logPositions.ts b/solauto-sdk/local/logPositions.ts new file mode 100644 index 00000000..fe9b55c2 --- /dev/null +++ b/solauto-sdk/local/logPositions.ts @@ -0,0 +1,184 @@ +import { PublicKey } from "@solana/web3.js"; +import path from "path"; +import { config } from "dotenv"; +import { + fetchTokenPrices, + getPositionExBulk, + getSolanaRpcConnection, + getSolautoManagedPositions, + LOCAL_IRONFORGE_API_URL, + ProgramEnv, + SOLAUTO_PROD_PROGRAM, + SOLAUTO_TEST_PROGRAM, +} from "../src"; + +config({ path: path.join(__dirname, ".env") }); + +export function roundToDecimals(value: number, decimals: number = 2): number { + if (!value || isNaN(value)) { + return value; + } + + let roundedValue: number | undefined; + do { + const factor = Math.pow(10, decimals ?? 2); + roundedValue = Math.round(value * factor) / factor; + decimals += 1; + } while (!roundedValue || decimals >= 10); + + return roundedValue; +} + +export function formatNumberToShortForm( + num: number, + decimals?: number +): string { + if (decimals === undefined) { + decimals = 1; + } + if (num >= 1_000_000_000) { + return ( + (num / 1_000_000_000) + .toFixed(decimals) + .replace(new RegExp(`\\.0{${decimals}}$`), "") + "B" + ); + } + if (num >= 1_000_000) { + return ( + (num / 1_000_000) + .toFixed(decimals) + .replace(new RegExp(`\\.0{${decimals}}$`), "") + "M" + ); + } + if (num >= 1_000) { + return ( + (num / 1_000) + .toFixed(decimals) + .replace(new RegExp(`\\.0{${decimals}}$`), "") + "K" + ); + } + return num.toFixed(decimals).replace(new RegExp(`\\.0{${decimals}}$`), ""); +} + +export function formatNumber( + num: number, + decimals?: number, + shortFormAtThreshold?: number, + decimalsAtShortform?: number +): string { + if (shortFormAtThreshold !== undefined && num > shortFormAtThreshold) { + return formatNumberToShortForm(num, decimalsAtShortform); + } else { + return num < 1 + ? roundToDecimals(num, decimals).toString() + : new Intl.NumberFormat("en-US").format( + decimals !== undefined ? roundToDecimals(num, decimals) : num + ); + } +} + +async function main(filterWhitelist: boolean, programEnv: ProgramEnv = "Prod") { + const [_, umi] = getSolanaRpcConnection( + LOCAL_IRONFORGE_API_URL, + programEnv === "Prod" ? SOLAUTO_PROD_PROGRAM : SOLAUTO_TEST_PROGRAM + ); + + let positions = await getSolautoManagedPositions(umi); + + if (filterWhitelist) { + const addressWhitelist = process.env.ADDRESS_WHITELIST?.split(",") ?? []; + positions = positions.filter( + (x) => !addressWhitelist.includes(x.authority.toString()) + ); + } + + const positionsEx = await getPositionExBulk( + umi, + positions.map((x) => new PublicKey(x.publicKey!)) + ); + + const tokensUsed = Array.from( + new Set( + positions.flatMap((x) => [ + x.supplyMint!.toString(), + x.debtMint!.toString(), + ]) + ) + ); + await fetchTokenPrices(tokensUsed.map((x) => new PublicKey(x))); + + console.log("\n\n"); + + let unhealthyPositions = 0; + let awaitingBoostPositions = 0; + + const sortedPositions = positionsEx.sort( + (a, b) => a.netWorthUsd() - b.netWorthUsd() + ); + for (const pos of sortedPositions) { + const actionToTake = pos.eligibleForRebalance(0); + + const repayFrom = pos.settings!.repayToBps + pos.settings!.repayGap; + const unhealthy = actionToTake === "repay"; + const healthText = unhealthy + ? `(Unhealthy: ${pos.liqUtilizationRateBps() - repayFrom}bps)` + : ""; + if (unhealthy) { + unhealthyPositions += 1; + } + + const awaitingBoost = actionToTake === "boost"; + const boostText = awaitingBoost ? " (awaiting boost)" : ""; + if (awaitingBoost) { + awaitingBoostPositions += 1; + } + + console.log( + pos.publicKey.toString(), + `(${pos.authority.toString()} ${pos.positionId})` + ); + console.log( + `${pos.strategyName}: $${formatNumber(pos.netWorthUsd(), 2, 10000, 2)} ${healthText} ${boostText}\n` + ); + } + + console.log( + "\nTotal positions:", + positionsEx.length, + unhealthyPositions ? ` (unhealthy: ${unhealthyPositions})` : "", + awaitingBoostPositions ? ` (awaiting boost: ${awaitingBoostPositions})` : "" + ); + console.log( + "Total users:", + Array.from(new Set(positionsEx.map((x) => x.authority.toString()))).length + ); + + const tvl = positionsEx + .map((x) => x.supplyUsd()) + .reduce((acc, curr) => acc + curr, 0); + const netWorth = positionsEx + .map((x) => x.netWorthUsd()) + .reduce((acc, curr) => acc + curr, 0); + + console.log(`TVL: $${formatNumber(tvl, 2, 10000, 2)}`); + console.log(`Total net worth: $${formatNumber(netWorth, 2, 10000, 2)}`); +} + +const args = process.argv.slice(2); // Skip the first 2 (node + script path) + +const parsedArgs: Record = {}; +for (const arg of args) { + if (arg.startsWith("--")) { + const [key, val] = arg.replace(/^--/, "").split("="); + parsedArgs[key] = val ?? true; + } +} + +console.log("Parsed flags:", parsedArgs); + +const filterWhitelist = + "filter" in parsedArgs ? parsedArgs["filter"] === "true" : true; +const programEnv = + "env" in parsedArgs ? (parsedArgs["env"] as ProgramEnv) : undefined; + +main(filterWhitelist, programEnv).then((x) => x); diff --git a/solauto-sdk/local/patchLUT.ts b/solauto-sdk/local/patchLUT.ts new file mode 100644 index 00000000..63d73b08 --- /dev/null +++ b/solauto-sdk/local/patchLUT.ts @@ -0,0 +1,96 @@ +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { + fetchAllReferralState, + getPositionExBulk, + getReferralState, + getSolanaRpcConnection, + getSolautoManagedPositions, + getTokenAccount, + LOCAL_IRONFORGE_API_URL, + SOLAUTO_PROD_PROGRAM, +} from "../src"; +import { PublicKey } from "@solana/web3.js"; +import { updateLookupTable } from "./shared"; + +let [conn, umi] = getSolanaRpcConnection( + LOCAL_IRONFORGE_API_URL, + SOLAUTO_PROD_PROGRAM +); + +async function getMissingAccounts() { + const allMissingAccounts: string[] = []; + const allPositions = await getSolautoManagedPositions(umi); + const positions = await getPositionExBulk( + umi, + allPositions.map((x) => x.publicKey!) + ); + + const referralStates = positions.map((x) => + getReferralState( + x.authority, + toWeb3JsPublicKey(umi.programs.get("solauto").publicKey) + ) + ); + const referralStatesData = await fetchAllReferralState( + umi, + referralStates.map((x) => fromWeb3JsPublicKey(x)) + ); + + const users = Array.from( + new Set(positions.map((x) => x.authority.toString())) + ); + const usersRequiringPatchLut = []; + for (const user of users) { + const authority = new PublicKey(user); + const referralState = referralStatesData.find((x) => + toWeb3JsPublicKey(x.authority).equals(authority) + )!; + const lookupTable = referralState.lookupTable; + + const existingUserLUTAccounts = + ( + await conn.getAddressLookupTable(toWeb3JsPublicKey(lookupTable), { + commitment: "confirmed", + }) + ).value?.state?.addresses ?? []; + + const userPositions = positions.filter((x) => + x.authority.equals(authority) + ); + + let requiredAccounts = userPositions.flatMap((x) => { + return [ + getTokenAccount(authority, x.supplyMint), + getTokenAccount(authority, x.debtMint), + x.publicKey, + x.lpUserAccount!, + getTokenAccount(x.publicKey, x.supplyMint), + getTokenAccount(x.publicKey, x.debtMint), + ].map((x) => x.toString()); + }); + requiredAccounts = Array.from(new Set(requiredAccounts)); + + const missingAccounts = requiredAccounts.filter( + (x) => + existingUserLUTAccounts.find((y) => y.toString() === x) === undefined + ); + + if (missingAccounts.length) { + console.log("\nMissing accounts for", referralState.publicKey.toString()); + console.log(missingAccounts); + allMissingAccounts.push(...missingAccounts); + usersRequiringPatchLut.push(user); + } + } + + console.log("Users requiring patch LUT:"); + console.log(Array.from(new Set(usersRequiringPatchLut))); + return allMissingAccounts; +} + +getMissingAccounts().then(async (accs) => { + // await updateLookupTable(accs, new PublicKey(PATCH_LUT)); +}); diff --git a/solauto-sdk/local/shared.ts b/solauto-sdk/local/shared.ts index 23f52ae4..1f162236 100644 --- a/solauto-sdk/local/shared.ts +++ b/solauto-sdk/local/shared.ts @@ -8,7 +8,11 @@ import { VersionedTransaction, PublicKey, } from "@solana/web3.js"; -import { buildHeliusApiUrl, getSolanaRpcConnection } from "../src/utils/solanaUtils"; +import { + getSolanaRpcConnection, + getBatches, + LOCAL_IRONFORGE_API_URL, +} from "../src"; function loadSecretKey(keypairPath: string) { const secretKey = JSON.parse(fs.readFileSync(keypairPath, "utf8")); @@ -21,20 +25,24 @@ export function getSecretKey(keypairFilename: string = "id"): Uint8Array { ); } -const keypair = Keypair.fromSecretKey(getSecretKey("solauto-auth")); -const [connection, _] = getSolanaRpcConnection(buildHeliusApiUrl(process.env.HELIUS_API_KEY ?? "")); +const keypair = Keypair.fromSecretKey(getSecretKey("solauto-fees")); +const [connection, _] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); -async function createAndSendV0Tx(txInstructions: TransactionInstruction[]) { +export async function createAndSendV0Tx( + txInstructions: TransactionInstruction[], + payer: Keypair, + otherSigners?: Keypair[] +) { let latestBlockhash = await connection.getLatestBlockhash("finalized"); const messageV0 = new TransactionMessage({ - payerKey: keypair.publicKey, + payerKey: payer.publicKey, recentBlockhash: latestBlockhash.blockhash, instructions: txInstructions, }).compileToV0Message(); const transaction = new VersionedTransaction(messageV0); - transaction.sign([keypair]); + transaction.sign([payer, ...(otherSigners ?? [])]); const txid = await connection.sendTransaction(transaction, { maxRetries: 5, @@ -46,7 +54,7 @@ async function createAndSendV0Tx(txInstructions: TransactionInstruction[]) { lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, }); if (confirmation.value.err) { - throw new Error(confirmation.value.err.toString()); + throw confirmation.value.err; } console.log(txid); } @@ -61,25 +69,39 @@ async function addAddressesIfNeeded( .map((x) => new PublicKey(x)); if (addresses.length > 0) { - await createAndSendV0Tx([ - AddressLookupTableProgram.extendLookupTable({ - payer: keypair.publicKey, - authority: keypair.publicKey, - lookupTable: lookupTableAddress, - addresses, - }), - ]); + const batches = getBatches(addresses, 20); + for (const addressBatch of batches) { + console.log(addressBatch.map((x) => x.toString())); + await createAndSendV0Tx( + [ + AddressLookupTableProgram.extendLookupTable({ + payer: keypair.publicKey, + authority: keypair.publicKey, + lookupTable: lookupTableAddress, + addresses: addressBatch, + }), + ], + keypair + ); + } } } +const CACHE: { [key: string]: string[] } = {}; + export async function updateLookupTable( accounts: string[], lookupTableAddress?: PublicKey ) { - let lookupTable = lookupTableAddress - ? await connection.getAddressLookupTable(lookupTableAddress) - : null; - if (lookupTable === null) { + if (lookupTableAddress && !(lookupTableAddress.toString() in CACHE)) { + const lookupTable = + await connection.getAddressLookupTable(lookupTableAddress); + CACHE[lookupTableAddress.toString()] = ( + lookupTable?.value?.state?.addresses ?? [] + ).map((x) => x.toString()); + } + + if (!lookupTableAddress) { const [createLutIx, addr] = AddressLookupTableProgram.createLookupTable({ authority: keypair.publicKey, payer: keypair.publicKey, @@ -87,11 +109,11 @@ export async function updateLookupTable( }); lookupTableAddress = addr; console.log("Lookup Table Address:", lookupTableAddress.toString()); - createAndSendV0Tx([createLutIx]); + await createAndSendV0Tx([createLutIx], keypair); + CACHE[lookupTableAddress.toString()] = []; } - const existingAccounts = - lookupTable?.value?.state.addresses.map((x) => x.toString()) ?? []; + const existingAccounts = CACHE[lookupTableAddress.toString()]; console.log("Existing accounts: ", existingAccounts.length); await addAddressesIfNeeded(lookupTableAddress!, existingAccounts, accounts); diff --git a/solauto-sdk/local/txSandbox.ts b/solauto-sdk/local/txSandbox.ts new file mode 100644 index 00000000..6d335371 --- /dev/null +++ b/solauto-sdk/local/txSandbox.ts @@ -0,0 +1,162 @@ +import { Keypair, PublicKey } from "@solana/web3.js"; +import { createSignerFromKeypair, publicKey, signerIdentity } from "@metaplex-foundation/umi"; +import { fromWeb3JsKeypair } from "@metaplex-foundation/umi-web3js-adapters"; +import { + buildSwbSubmitResponseTx, + ClientTransactionsManager, + consoleLog, + fetchBank, + getBatches, + getClient, + getPositionExBulk, + getSolanaRpcConnection, + getSolautoManagedPositions, + JITO_SOL, + LendingPlatform, + LOCAL_IRONFORGE_API_URL, + PriceType, + PriorityFeeSetting, + ProgramEnv, + rebalance, + safeFetchBank, + safeFetchMarginfiAccount, + sendSingleOptimizedTransaction, + SOLAUTO_PROD_PROGRAM, + SOLAUTO_TEST_PROGRAM, + SolautoClient, + TransactionItem, +} from "../src"; +import { getSecretKey } from "./shared"; + +const payForTransaction = true; +const testProgram = false; +const lpEnv: ProgramEnv = "Prod"; + +let [, umi] = getSolanaRpcConnection( + LOCAL_IRONFORGE_API_URL, + testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM, + lpEnv +); + +const signer = createSignerFromKeypair( + umi, + fromWeb3JsKeypair(Keypair.fromSecretKey(getSecretKey("solauto-manager"))) +); + +export async function main() { + const client = getClient(LendingPlatform.Marginfi, { + signer, + showLogs: true, + rpcUrl: LOCAL_IRONFORGE_API_URL, + programId: testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM, + lpEnv, + }); + + await client.initializeExistingSolautoPosition({ + positionId: 1, + authority: new PublicKey("61rtn5tzVkesapo6Cz83SPoShUfAePSxJsqniuF2wRKC"), + // lpUserAccount: new PublicKey( + // "GEokw9jqbh6d1xUNA3qaeYFFetbSR5Y1nt7C3chwwgSz" + // ), + }); + + const transactionItems = [rebalance(client)]; + + const txManager = new ClientTransactionsManager({ + txHandler: client, + txRunType: payForTransaction ? "normal" : "only-simulate", + priorityFeeSetting: PriorityFeeSetting.Default, + retryConfig: { totalRetries: 2 }, + }); + const statuses = await txManager.send(transactionItems); + consoleLog(statuses); +} + +async function refreshAll() { + const allPositions = await getSolautoManagedPositions(umi); + const positions = await getPositionExBulk( + umi, + allPositions.map((x) => new PublicKey(x.publicKey!)) + ); + + let client: SolautoClient | undefined; + const transactionItems: TransactionItem[] = []; + for (const pos of positions) { + client = getClient(pos.lendingPlatform, { + signer, + showLogs: true, + rpcUrl: LOCAL_IRONFORGE_API_URL, + programId: testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM, + lpEnv, + }); + + await client!.initialize({ + positionId: pos.positionId, + authority: pos.authority, + }); + + const ix = client!.refreshIx(PriceType.Realtime); + transactionItems.push( + new TransactionItem( + async () => ({ tx: ix }), + `refresh ${pos.authority} (${pos.positionId})` + ) + ); + } + + const txBatches = getBatches(transactionItems, 15); + + for (const batch of txBatches) { + const txManager = new ClientTransactionsManager({ + txHandler: client!, + txRunType: payForTransaction ? "normal" : "only-simulate", + priorityFeeSetting: PriorityFeeSetting.Default, + retryConfig: { totalRetries: 2 }, + }); + const statuses = await txManager.send(batch); + consoleLog(statuses); + } +} + +async function testSwbOracleUpdate() { + (globalThis as any).SHOW_LOGS = true; + + let [conn, umiLocal] = getSolanaRpcConnection( + LOCAL_IRONFORGE_API_URL, + testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM, + lpEnv + ); + umiLocal = umiLocal.use(signerIdentity(signer)); + + const mint = new PublicKey(JITO_SOL); + console.log("Building SWB oracle update tx for JitoSOL..."); + + const result = await buildSwbSubmitResponseTx(conn, signer, mint); + if (!result) { + console.log("No oracle update needed"); + return; + } + + console.log("Instructions count:", result.tx.getInstructions().length); + console.log("Lookup tables:", result.lookupTableAddresses); + + console.log("Sending transaction..."); + const sig = await sendSingleOptimizedTransaction( + umiLocal, + conn, + result.tx, + payForTransaction ? "normal" : "only-simulate", + PriorityFeeSetting.Default + ); + + if (sig) { + const bs58 = await import("bs58"); + console.log("Transaction signature:", bs58.default.encode(sig)); + } else { + console.log("Transaction simulation complete (no sig returned)"); + } +} + +// main(); +// refreshAll(); +testSwbOracleUpdate(); diff --git a/solauto-sdk/local/updateMarginfiLUT.ts b/solauto-sdk/local/updateMarginfiLUT.ts index 1a378bbf..e77ae3ec 100644 --- a/solauto-sdk/local/updateMarginfiLUT.ts +++ b/solauto-sdk/local/updateMarginfiLUT.ts @@ -1,35 +1,104 @@ -import { PublicKey } from "@solana/web3.js"; -import { MARGINFI_ACCOUNTS_LOOKUP_TABLE } from "../src/constants/marginfiAccounts"; +import { Keypair } from "@solana/web3.js"; import { - MARGINFI_ACCOUNTS, - DEFAULT_MARGINFI_GROUP, -} from "../src/constants/marginfiAccounts"; -import { MARGINFI_PROGRAM_ID } from "../src/marginfi-sdk"; -import { updateLookupTable } from "./shared"; + createSignerFromKeypair, + publicKey, + signerIdentity, +} from "@metaplex-foundation/umi"; +import { + fromWeb3JsKeypair, + toWeb3JsInstruction, + toWeb3JsKeypair, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { + getEmptyMarginfiAccountsByAuthority, + getSolanaRpcConnection, + SOLAUTO_MANAGER, + marginfiAccountInitialize, + LOCAL_IRONFORGE_API_URL, + getMarginfiAccounts, + getAllBankRelatedAccounts, + umiWithMarginfiProgram, + ProgramEnv, +} from "../src"; +import { createAndSendV0Tx, getSecretKey, updateLookupTable } from "./shared"; + +const programEnv: ProgramEnv = "Prod"; +const mfiAccounts = getMarginfiAccounts(programEnv); -const LOOKUP_TABLE_ADDRESS = new PublicKey(MARGINFI_ACCOUNTS_LOOKUP_TABLE); +const LOOKUP_TABLE_ADDRESS = mfiAccounts.lookupTable; +let [, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); +umi = umiWithMarginfiProgram( + umi.use( + signerIdentity( + createSignerFromKeypair(umi, umi.eddsa.generateKeypair()), + true + ) + ), + programEnv +); + +const solautoManagerKeypair = Keypair.fromSecretKey( + getSecretKey("solauto-manager") +); +const solautoManager = createSignerFromKeypair( + umi, + fromWeb3JsKeypair(solautoManagerKeypair) +); async function addBanks() { - for (const group in MARGINFI_ACCOUNTS) { - for (const key in MARGINFI_ACCOUNTS[group]) { - const accounts = MARGINFI_ACCOUNTS[group][key]; + const accounts = await getAllBankRelatedAccounts( + umi, + mfiAccounts.bankAccounts + ); + await updateLookupTable( + accounts.map((x) => x.toString()), + LOOKUP_TABLE_ADDRESS + ); +} + +async function addImfiAccounts() { + const imfiAccounts = await getEmptyMarginfiAccountsByAuthority( + umi, + SOLAUTO_MANAGER + ); + + const iMfiAccountsPerGrp = 2; + for (const group in mfiAccounts.bankAccounts) { + const emptyAccs = imfiAccounts.filter((x) => x.group.toString() === group); + if (emptyAccs.length >= iMfiAccountsPerGrp) { await updateLookupTable( - [ - group, - accounts.bank, - accounts.liquidityVault, - accounts.vaultAuthority, - accounts.priceOracle, - ], + emptyAccs.map((x) => x.publicKey.toString()), LOOKUP_TABLE_ADDRESS ); + } else { + for (let i = 0; i < iMfiAccountsPerGrp - emptyAccs.length; i++) { + console.log("Creating Imfi account for group:", group); + const iMfiAccountKeypair = umi.eddsa.generateKeypair(); + const iMfiAccount = createSignerFromKeypair(umi, iMfiAccountKeypair); + const umiIx = marginfiAccountInitialize(umi, { + marginfiAccount: iMfiAccount, + marginfiGroup: publicKey(group), + authority: solautoManager, + feePayer: solautoManager, + }); + const ix = toWeb3JsInstruction(umiIx.getInstructions()[0]); + await createAndSendV0Tx([ix], solautoManagerKeypair, [ + toWeb3JsKeypair(iMfiAccountKeypair), + ]); + await updateLookupTable( + [iMfiAccount.publicKey.toString()], + LOOKUP_TABLE_ADDRESS + ); + } } } } updateLookupTable( - [DEFAULT_MARGINFI_GROUP, MARGINFI_PROGRAM_ID], + [mfiAccounts.defaultGroup.toString(), mfiAccounts.program.toString()], LOOKUP_TABLE_ADDRESS ); -addBanks(); +addBanks().then((x) => x); + +addImfiAccounts().then((x) => x); diff --git a/solauto-sdk/local/updateSolautoLUT.ts b/solauto-sdk/local/updateSolautoLUT.ts index d17edc4d..c5d3e3f1 100644 --- a/solauto-sdk/local/updateSolautoLUT.ts +++ b/solauto-sdk/local/updateSolautoLUT.ts @@ -1,23 +1,19 @@ import { PublicKey } from "@solana/web3.js"; -import { getTokenAccounts } from "../src/utils/accountUtils"; import { SOLAUTO_FEES_WALLET, SOLAUTO_MANAGER, -} from "../src/constants/generalAccounts"; -import { ALL_SUPPORTED_TOKENS } from "../src/constants/tokenConstants"; -import { updateLookupTable } from "./shared"; -import { SOLAUTO_LUT, STANDARD_LUT_ACCOUNTS, -} from "../src/constants/solautoConstants"; -import { - buildHeliusApiUrl, - getAllMarginfiAccountsByAuthority, - getSolanaRpcConnection, -} from "../src/utils"; -import { SWITCHBOARD_PRICE_FEED_IDS } from "../src/constants/switchboardConstants"; + SWITCHBOARD_PRICE_FEED_IDS, + ALL_SUPPORTED_TOKENS, + getTokenAccounts, + PYTH_ORACLE_ACCOUNTS, +} from "../src"; +import { updateLookupTable } from "./shared"; -const LOOKUP_TABLE_ADDRESS = new PublicKey(SOLAUTO_LUT); +const LOOKUP_TABLE_ADDRESS = Boolean(SOLAUTO_LUT) + ? new PublicKey(SOLAUTO_LUT) + : undefined; const solautoManagerTokenAccounts = getTokenAccounts( SOLAUTO_MANAGER, ALL_SUPPORTED_TOKENS.map((x) => new PublicKey(x)) @@ -28,22 +24,14 @@ const solautoFeeWalletTokenAccounts = getTokenAccounts( ); export async function updateSolautoLut(additionalAccounts?: string[]) { - const [_, umi] = getSolanaRpcConnection( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!) - ); - const ismAccounts = await getAllMarginfiAccountsByAuthority( - umi, - SOLAUTO_MANAGER - ); - return updateLookupTable( [ ...STANDARD_LUT_ACCOUNTS, ...ALL_SUPPORTED_TOKENS, ...solautoManagerTokenAccounts.map((x) => x.toString()), ...solautoFeeWalletTokenAccounts.map((x) => x.toString()), - ...ismAccounts.map((x) => x.marginfiAccount.toString()), - ...Object.values(SWITCHBOARD_PRICE_FEED_IDS), + ...Object.values(SWITCHBOARD_PRICE_FEED_IDS).map((x) => x.feedId), + ...Object.values(PYTH_ORACLE_ACCOUNTS), ...(additionalAccounts ?? []), ], LOOKUP_TABLE_ADDRESS diff --git a/solauto-sdk/package.json b/solauto-sdk/package.json index 7641e4a4..563e3e9b 100644 --- a/solauto-sdk/package.json +++ b/solauto-sdk/package.json @@ -1,37 +1,36 @@ { "name": "@haven-fi/solauto-sdk", - "version": "1.0.340", + "version": "1.0.820", "main": "dist/index.js", "types": "dist/index.d.ts", "description": "Typescript SDK for the Solauto program on the Solana blockchain", "author": "Chelioso", - "license": "MIT", "scripts": { "build": "rm -rf dist && npx tsc", - "test:txs": "ts-mocha -p ./tsconfig.json -t 1000000 tests/transactions/**/*.ts", - "test:unit": "ts-mocha -p ./tsconfig.json -t 1000000 tests/unit/**/*.ts", "test:all": "pnpm test:unit && pnpm test:txs", - "update-lut:solauto": "npx ts-node local/updateSolautoLUT.ts", - "update-lut:marginfi": "npx ts-node local/updateMarginfiLUT.ts", - "create-ism-accounts": "npx ts-node local/createISMAccounts.ts", - "create-token-accounts": "npx ts-node local/createTokenAccounts.ts" + "test:txs": "ts-mocha -p ./tsconfig.json -t 1000000 tests/transactions/**/*.ts", + "test:unit": "ts-mocha -p ./tsconfig.json -t 1000000 tests/unit/**/*.ts" }, "dependencies": { - "@coral-xyz/anchor": "^0.30.1", - "@jup-ag/api": "^6.0.24", + "@coral-xyz/anchor": "=0.31.1", + "@jup-ag/api": "=6.0.44", "@metaplex-foundation/umi": "^0.9.1", "@metaplex-foundation/umi-bundle-defaults": "^0.9.1", "@metaplex-foundation/umi-signer-wallet-adapters": "^0.9.1", "@metaplex-foundation/umi-web3js-adapters": "^0.9.1", "@solana/spl-token": "^0.4.0", - "@solana/web3.js": "^1.95.4", - "@switchboard-xyz/on-demand": "^1.2.51", + "@solana/web3.js": "=1.98.2", + "@switchboard-xyz/common": "=3.4.1", + "@switchboard-xyz/on-demand": "=2.17.4", "axios": "^1.7.8", + "big.js": "^6.2.2", "bs58": "^5.0.0", "cross-fetch": "^4.0.0", + "dotenv": "^16.4.7", "rpc-websockets": "7.11.0" }, "devDependencies": { + "@types/big.js": "^6.2.2", "@types/chai": "^4.3.0", "@types/mocha": "^9.1.1", "@types/node": "^20.16.11", diff --git a/solauto-sdk/pnpm-lock.yaml b/solauto-sdk/pnpm-lock.yaml index 3b97bbef..55b8462e 100644 --- a/solauto-sdk/pnpm-lock.yaml +++ b/solauto-sdk/pnpm-lock.yaml @@ -1,696 +1,1381 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - '@coral-xyz/anchor': - specifier: ^0.30.1 - version: 0.30.1 - '@jup-ag/api': - specifier: ^6.0.24 - version: 6.0.29 - '@metaplex-foundation/umi': - specifier: ^0.9.1 - version: 0.9.2 - '@metaplex-foundation/umi-bundle-defaults': - specifier: ^0.9.1 - version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@metaplex-foundation/umi-signer-wallet-adapters': - specifier: ^0.9.1 - version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@metaplex-foundation/umi-web3js-adapters': - specifier: ^0.9.1 - version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@solana/spl-token': - specifier: ^0.4.0 - version: 0.4.8(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/web3.js': - specifier: ^1.95.4 - version: 1.95.5 - '@switchboard-xyz/on-demand': - specifier: ^1.2.51 - version: 1.2.51(fastestsmallesttextencoderdecoder@1.0.22) - axios: - specifier: ^1.7.8 - version: 1.7.8 - bs58: - specifier: ^5.0.0 - version: 5.0.0 - cross-fetch: - specifier: ^4.0.0 - version: 4.0.0 - rpc-websockets: - specifier: 7.11.0 - version: 7.11.0 - -devDependencies: - '@types/chai': - specifier: ^4.3.0 - version: 4.3.20 - '@types/mocha': - specifier: ^9.1.1 - version: 9.1.1 - '@types/node': - specifier: ^20.16.11 - version: 20.16.11 - chai: - specifier: ^4.3.4 - version: 4.5.0 - mocha: - specifier: ^9.2.2 - version: 9.2.2 - ts-mocha: - specifier: ^10.0.0 - version: 10.0.0(mocha@9.2.2) - typescript: - specifier: ^5.5.4 - version: 5.6.3 +importers: + + .: + dependencies: + '@coral-xyz/anchor': + specifier: '=0.31.1' + version: 0.31.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@jup-ag/api': + specifier: '=6.0.44' + version: 6.0.44 + '@metaplex-foundation/umi': + specifier: ^0.9.1 + version: 0.9.2 + '@metaplex-foundation/umi-bundle-defaults': + specifier: ^0.9.1 + version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-signer-wallet-adapters': + specifier: ^0.9.1 + version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-web3js-adapters': + specifier: ^0.9.1 + version: 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/spl-token': + specifier: ^0.4.0 + version: 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/web3.js': + specifier: '=1.98.2' + version: 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@switchboard-xyz/common': + specifier: '=3.4.1' + version: 3.4.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@switchboard-xyz/on-demand': + specifier: '=2.17.4' + version: 2.17.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + axios: + specifier: ^1.7.8 + version: 1.10.0 + big.js: + specifier: ^6.2.2 + version: 6.2.2 + bs58: + specifier: ^5.0.0 + version: 5.0.0 + cross-fetch: + specifier: ^4.0.0 + version: 4.1.0 + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + rpc-websockets: + specifier: 7.11.0 + version: 7.11.0 + devDependencies: + '@types/big.js': + specifier: ^6.2.2 + version: 6.2.2 + '@types/chai': + specifier: ^4.3.0 + version: 4.3.20 + '@types/mocha': + specifier: ^9.1.1 + version: 9.1.1 + '@types/node': + specifier: ^20.16.11 + version: 20.19.8 + chai: + specifier: ^4.3.4 + version: 4.5.0 + mocha: + specifier: ^9.2.2 + version: 9.2.2 + ts-mocha: + specifier: ^10.0.0 + version: 10.1.0(mocha@9.2.2) + typescript: + specifier: ^5.5.4 + version: 5.8.3 packages: - /@babel/runtime@7.25.7: - resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==} + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: false - - /@brokerloop/ttlcache@3.2.3: - resolution: {integrity: sha512-kZWoyJGBYTv1cL5oHBYEixlJysJBf2RVnub3gbclD+dwaW9aKubbHzbZ9q1q6bONosxaOqMsoBorOrZKzBDiqg==} - dependencies: - '@soncodi/signal': 2.0.7 - dev: false - /@coral-xyz/anchor-errors@0.30.1: - resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} + '@coral-xyz/anchor-errors@0.31.1': + resolution: {integrity: sha512-NhNEku4F3zzUSBtrYz84FzYWm48+9OvmT1Hhnwr6GnPQry2dsEqH/ti/7ASjjpoFTWRnPXrjAIT1qM6Isop+LQ==} engines: {node: '>=10'} - dev: false - /@coral-xyz/anchor@0.30.1: - resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} - engines: {node: '>=11'} - dependencies: - '@coral-xyz/anchor-errors': 0.30.1 - '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.95.5) - '@noble/hashes': 1.5.0 - '@solana/web3.js': 1.95.5 - bn.js: 5.2.1 - bs58: 4.0.1 - buffer-layout: 1.2.2 - camelcase: 6.3.0 - cross-fetch: 3.1.8 - crypto-hash: 1.3.0 - eventemitter3: 4.0.7 - pako: 2.1.0 - snake-case: 3.0.4 - superstruct: 0.15.5 - toml: 3.0.0 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - dev: false + '@coral-xyz/anchor@0.31.1': + resolution: {integrity: sha512-QUqpoEK+gi2S6nlYc2atgT2r41TT3caWr/cPUEL8n8Md9437trZ68STknq897b82p5mW0XrTBNOzRbmIRJtfsA==} + engines: {node: '>=17'} - /@coral-xyz/borsh@0.30.1(@solana/web3.js@1.95.5): - resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} + '@coral-xyz/borsh@0.31.1': + resolution: {integrity: sha512-9N8AU9F0ubriKfNE3g1WF0/4dtlGXoBN/hd1PvbNBamBNwRgHxH4P+o3Zt7rSEloW1HUs6LfZEchlx9fW7POYw==} engines: {node: '>=10'} peerDependencies: - '@solana/web3.js': ^1.68.0 - dependencies: - '@solana/web3.js': 1.95.5 - bn.js: 5.2.1 - buffer-layout: 1.2.2 - dev: false + '@solana/web3.js': ^1.69.0 + + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} - /@jup-ag/api@6.0.29: - resolution: {integrity: sha512-1gUY1txmL/gG14VDZMjeKVqhSzeg4L2cImdC7dGLYB5DP5D5lwpR+FarvhqehEi+63/RWOUKTTU4fablGpeIUQ==} - dev: false + '@jup-ag/api@6.0.44': + resolution: {integrity: sha512-C6oYi+wvNi07VlmWhT8+ZDMqTJuF+MiHuNBE65cYuBH6C4l0JfwpXaj6mKazAdVqVFWgngr3s2+0tdF5nDH4tA==} - /@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-bundle-defaults@0.9.2': resolution: {integrity: sha512-kV3tfvgvRjVP1p9OFOtH+ibOtN9omVJSwKr0We4/9r45e5LTj+32su0V/rixZUkG1EZzzOYBsxhtIE0kIw/Hrw==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-downloader-http': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@metaplex-foundation/umi-http-fetch': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-rpc-chunk-get-accounts': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@metaplex-foundation/umi-serializer-data-view': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@solana/web3.js': 1.95.5 - transitivePeerDependencies: - - encoding - dev: false - /@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2): + '@metaplex-foundation/umi-downloader-http@0.9.2': resolution: {integrity: sha512-tzPT9hBwenzTzAQg07rmsrqZfgguAXELbcJrsYMoASp5VqWFXYIP00g94KET6XLjWUXH4P1J2zoa6hGennPXHA==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - dev: false - /@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-eddsa-web3js@0.9.2': resolution: {integrity: sha512-hhPCxXbYIp4BC4z9gK78sXpWLkNSrfv4ndhF5ruAkdIp7GcRVYKj0QnOUO6lGYGiIkNlw20yoTwOe1CT//OfTQ==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@noble/curves': 1.6.0 - '@solana/web3.js': 1.95.5 - dev: false - /@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2): + '@metaplex-foundation/umi-http-fetch@0.9.2': resolution: {integrity: sha512-YCZuBu24T9ZzEDe4+w12LEZm/fO9pkyViZufGgASC5NX93814Lvf6Ssjn/hZzjfA7CvZbvLFbmujc6CV3Q/m9Q==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - dev: false - /@metaplex-foundation/umi-options@0.8.9: + '@metaplex-foundation/umi-options@0.8.9': resolution: {integrity: sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==} - dev: false - /@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2): + '@metaplex-foundation/umi-program-repository@0.9.2': resolution: {integrity: sha512-g3+FPqXEmYsBa8eETtUE2gb2Oe3mqac0z3/Ur1TvAg5TtIy3mzRzOy/nza+sgzejnfcxcVg835rmpBaxpBnjDA==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - dev: false - /@metaplex-foundation/umi-public-keys@0.8.9: + '@metaplex-foundation/umi-public-keys@0.8.9': resolution: {integrity: sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==} - dependencies: - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - dev: false - /@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2): + '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2': resolution: {integrity: sha512-YRwVf6xH0jPBAUgMhEPi+UbjioAeqTXmjsN2TnmQCPAmHbrHrMRj0rlWYwFLWAgkmoxazYrXP9lqOFRrfOGAEA==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - dev: false - /@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-rpc-web3js@0.9.2': resolution: {integrity: sha512-MqcsBz8B4wGl6jxsf2Jo/rAEpYReU9VCSR15QSjhvADHMmdFxCIZCCAgE+gDE2Vuanfl437VhOcP3g5Uw8C16Q==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@solana/web3.js': 1.95.5 - dev: false - /@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2): + '@metaplex-foundation/umi-serializer-data-view@0.9.2': resolution: {integrity: sha512-5vGptadJxUxvUcyrwFZxXlEc6Q7AYySBesizCtrBFUY8w8PnF2vzmS45CP1MLySEATNH6T9mD4Rs0tLb87iQyA==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - dev: false - /@metaplex-foundation/umi-serializers-core@0.8.9: + '@metaplex-foundation/umi-serializers-core@0.8.9': resolution: {integrity: sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==} - dev: false - /@metaplex-foundation/umi-serializers-encodings@0.8.9: + '@metaplex-foundation/umi-serializers-encodings@0.8.9': resolution: {integrity: sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==} - dependencies: - '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: false - /@metaplex-foundation/umi-serializers-numbers@0.8.9: + '@metaplex-foundation/umi-serializers-numbers@0.8.9': resolution: {integrity: sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==} - dependencies: - '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: false - /@metaplex-foundation/umi-serializers@0.9.0: + '@metaplex-foundation/umi-serializers@0.9.0': resolution: {integrity: sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==} - dependencies: - '@metaplex-foundation/umi-options': 0.8.9 - '@metaplex-foundation/umi-public-keys': 0.8.9 - '@metaplex-foundation/umi-serializers-core': 0.8.9 - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - '@metaplex-foundation/umi-serializers-numbers': 0.8.9 - dev: false - /@metaplex-foundation/umi-signer-wallet-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-signer-wallet-adapters@0.9.2': resolution: {integrity: sha512-DFG0ZFocKG8briypSkG9bGUTVsWpAgYugsl2BzTygkGExc4evWfF4Sb1F2C2w9FdrA9ESZM1gpLX9xtx5taOXg==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@solana/web3.js': 1.95.5 - dev: false - /@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2': resolution: {integrity: sha512-fR1Kf21uylMFd1Smkltmj4jTNxhqSWf416owsJ+T+cvJi2VCOcOwq/3UFzOrpz78fA0RhsajKYKj0HYsRnQI1g==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5) - '@solana/web3.js': 1.95.5 - dev: false - /@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.5): + '@metaplex-foundation/umi-web3js-adapters@0.9.2': resolution: {integrity: sha512-RQqUTtHYY9fmEMnq7s3Hiv/81flGaoI0ZVVoafnFVaQLnxU6QBKxtboRZHk43XtD9CiFh5f9izrMJX7iK7KlOA==} peerDependencies: '@metaplex-foundation/umi': ^0.9.2 '@solana/web3.js': ^1.72.0 - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@solana/web3.js': 1.95.5 - buffer: 6.0.3 - dev: false - /@metaplex-foundation/umi@0.9.2: + '@metaplex-foundation/umi@0.9.2': resolution: {integrity: sha512-9i4Acm4pruQfJcpRrc2EauPBwkfDN0I9QTvJyZocIlKgoZwD6A6wH0PViH1AjOVG5CQCd1YI3tJd5XjYE1ElBw==} - dependencies: - '@metaplex-foundation/umi-options': 0.8.9 - '@metaplex-foundation/umi-public-keys': 0.8.9 - '@metaplex-foundation/umi-serializers': 0.9.0 - dev: false - /@noble/curves@1.6.0: - resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} + '@noble/curves@1.9.4': + resolution: {integrity: sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==} engines: {node: ^14.21.3 || >=16} - dependencies: - '@noble/hashes': 1.5.0 - dev: false - /@noble/hashes@1.5.0: - resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - dev: false - /@protobufjs/aspromise@1.1.2: + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - dev: false - /@protobufjs/base64@1.1.2: + '@protobufjs/base64@1.1.2': resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - dev: false - /@protobufjs/codegen@2.0.4: + '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - dev: false - /@protobufjs/eventemitter@1.1.0: + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - dev: false - /@protobufjs/fetch@1.1.0: + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - dev: false - /@protobufjs/float@1.0.2: + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - dev: false - /@protobufjs/inquire@1.1.0: + '@protobufjs/inquire@1.1.0': resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - dev: false - /@protobufjs/path@1.1.2: + '@protobufjs/path@1.1.2': resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - dev: false - /@protobufjs/pool@1.1.0: + '@protobufjs/pool@1.1.0': resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - dev: false - /@protobufjs/utf8@1.1.0: + '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - dev: false - /@solana/buffer-layout-utils@0.2.0: + '@solana/buffer-layout-utils@0.2.0': resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} engines: {node: '>= 10'} - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.95.5 - bigint-buffer: 1.1.5 - bignumber.js: 9.1.2 - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - dev: false - /@solana/buffer-layout@4.0.1: + '@solana/buffer-layout@4.0.1': resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} engines: {node: '>=5.10'} - dependencies: - buffer: 6.0.3 - dev: false - /@solana/codecs-core@2.0.0-preview.4(typescript@5.6.3): - resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==} + '@solana/codecs-core@2.0.0-rc.1': + resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} peerDependencies: typescript: '>=5' - dependencies: - '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) - typescript: 5.6.3 - dev: false - /@solana/codecs-core@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} + '@solana/codecs-core@2.3.0': + resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-data-structures@2.0.0-rc.1': + resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-numbers@2.0.0-rc.1': + resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-numbers@2.3.0': + resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/codecs-strings@2.0.0-rc.1': + resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5' + + '@solana/codecs@2.0.0-rc.1': + resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} + peerDependencies: + typescript: '>=5' + + '@solana/errors@2.0.0-rc.1': + resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} + hasBin: true + peerDependencies: + typescript: '>=5' + + '@solana/errors@2.3.0': + resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' + + '@solana/options@2.0.0-rc.1': + resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: typescript: '>=5' + + '@solana/spl-token-group@0.0.7': + resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token-metadata@0.1.6': + resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token@0.4.13': + resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.5 + + '@solana/web3.js@1.98.2': + resolution: {integrity: sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@switchboard-xyz/common@3.4.1': + resolution: {integrity: sha512-TropBlBYuDeBnmGHkPSmgC3clLqAxy51ZGbwk4ejAgadnszWOgYHcH7taRG4Ha17DYSCWw/LGMBKbunGo+Aoaw==} + engines: {node: '>=20'} + + '@switchboard-xyz/common@4.1.1': + resolution: {integrity: sha512-H5esycuUKAzS1SGqV6DZuBME7ZaGLHJc1jP7tgLGjZ2rZSoCRPthPLvHnQAAV3A18uomTyM8aSARyYYEAZnsFA==} + engines: {node: '>=20'} + + '@switchboard-xyz/on-demand@2.17.4': + resolution: {integrity: sha512-jZtnjcI58Leet2GCABoK0+snfi3Sg4Kn997MnsE0aMQFWCQ/zsy2oI81x7Nk+kvM+3Q5OEAG7JpAsN00BMlgQw==} + engines: {node: '>= 18'} + + '@types/big.js@6.2.2': + resolution: {integrity: sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==} + + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/mocha@9.1.1': + resolution: {integrity: sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@20.19.8': + resolution: {integrity: sha512-HzbgCY53T6bfu4tT7Aq3TvViJyHjLjPNaAS3HOuMc9pw97KHsUtXNX4L+wu59g1WnjsZSko35MbEqnO58rihhw==} + + '@types/uuid@8.3.4': + resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + + '@types/ws@7.4.7': + resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@ungap/promise-all-settled@1.1.2': + resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} + + '@zod/core@0.11.6': + resolution: {integrity: sha512-03Bv82fFSfjDAvMfdHHdGSS6SOJs0iCcJlWJv1kJHRtoTT02hZpyip/2Lk6oo4l4FtjuwTrsEQTwg/LD8I7dJA==} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ansi-colors@4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base-x@3.0.11: + resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} + + base-x@4.0.1: + resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} + + base-x@5.0.1: + resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + big.js@6.2.2: + resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + + bigint-buffer@1.1.5: + resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} + engines: {node: '>= 10.0.0'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bn.js@5.2.2: + resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} + + borsh@0.7.0: + resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + + bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-layout@1.2.2: + resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} + engines: {node: '>=4.5'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-fetch@4.1.0: + resolution: {integrity: sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==} + + debug@4.3.3: + resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + delay@5.0.0: + resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} + engines: {node: '>=10'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + + diff@5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + + es6-promisify@5.0.0: + resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} + + fast-stable-stringify@1.0.0: + resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + + fastestsmallesttextencoderdecoder@1.0.22: + resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + deprecated: Glob versions prior to v9 are no longer supported + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + growl@1.10.5: + resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} + engines: {node: '>=4.x'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + jayson@4.2.0: + resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} + engines: {node: '>=8'} + hasBin: true + + js-sha256@0.11.1: + resolution: {integrity: sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@4.2.1: + resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mocha@9.2.2: + resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} + engines: {node: '>= 12.0.0'} + hasBin: true + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.1: + resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + protobufjs@7.5.3: + resolution: {integrity: sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==} + engines: {node: '>=12.0.0'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rpc-websockets@7.11.0: + resolution: {integrity: sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==} + deprecated: deprecate 7.11.0 + + rpc-websockets@9.1.1: + resolution: {integrity: sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + serialize-javascript@6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-json@1.9.1: + resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + superstruct@0.15.5: + resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} + + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + text-encoding-utf-8@1.0.2: + resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-mocha@10.1.0: + resolution: {integrity: sha512-T0C0Xm3/WqCuF2tpa0GNGESTBoKZaiqdUP8guNv4ZY316AFXlyidnrzQ1LUrCT0Wb1i3J0zFTgOh/55Un44WdA==} + engines: {node: '>= 6.X.X'} + hasBin: true + peerDependencies: + mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X || ^11.X.X + + ts-node@7.0.1: + resolution: {integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==} + engines: {node: '>=4.2.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + workerpool@6.2.0: + resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yn@2.0.0: + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} + engines: {node: '>=4'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@4.0.0-beta.20250505T195954: + resolution: {integrity: sha512-iB8WvxkobVIXMARvQu20fKvbS7mUTiYRpcD8OQV1xjRhxO0EEpYIRJBk6yfBzHAHEdOSDh3SxDITr5Eajr2vtg==} + +snapshots: + + '@babel/runtime@7.27.6': {} + + '@coral-xyz/anchor-errors@0.31.1': {} + + '@coral-xyz/anchor@0.31.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor-errors': 0.31.1 + '@coral-xyz/borsh': 0.31.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.8.0 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.2.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@coral-xyz/borsh@0.31.1(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + bn.js: 5.2.2 + buffer-layout: 1.2.2 + + '@isaacs/ttlcache@1.4.1': {} + + '@jup-ag/api@6.0.44': {} + + '@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-downloader-http': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-http-fetch': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-rpc-chunk-get-accounts': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@metaplex-foundation/umi-serializer-data-view': 0.9.2(@metaplex-foundation/umi@0.9.2) + '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - encoding + + '@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + + '@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@noble/curves': 1.9.4 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@metaplex-foundation/umi-options@0.8.9': {} + + '@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + + '@metaplex-foundation/umi-public-keys@0.8.9': + dependencies: + '@metaplex-foundation/umi-serializers-encodings': 0.8.9 + + '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2)': + dependencies: + '@metaplex-foundation/umi': 0.9.2 + + '@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) - typescript: 4.9.5 - dev: false + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - /@solana/codecs-core@2.0.0-rc.1(typescript@5.6.3): - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2)': dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi': 0.9.2 - /@solana/codecs-data-structures@2.0.0-preview.4(typescript@5.6.3): - resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-serializers-core@0.8.9': {} + + '@metaplex-foundation/umi-serializers-encodings@0.8.9': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) - '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi-serializers-core': 0.8.9 - /@solana/codecs-data-structures@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-serializers-numbers@0.8.9': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) - typescript: 4.9.5 - dev: false + '@metaplex-foundation/umi-serializers-core': 0.8.9 - /@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.6.3): - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-serializers@0.9.0': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi-options': 0.8.9 + '@metaplex-foundation/umi-public-keys': 0.8.9 + '@metaplex-foundation/umi-serializers-core': 0.8.9 + '@metaplex-foundation/umi-serializers-encodings': 0.8.9 + '@metaplex-foundation/umi-serializers-numbers': 0.8.9 - /@solana/codecs-numbers@2.0.0-preview.4(typescript@5.6.3): - resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-signer-wallet-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) - '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - /@solana/codecs-numbers@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) - typescript: 4.9.5 - dev: false + '@metaplex-foundation/umi': 0.9.2 + '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - /@solana/codecs-numbers@2.0.0-rc.1(typescript@5.6.3): - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} - peerDependencies: - typescript: '>=5' + '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi': 0.9.2 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + buffer: 6.0.3 - /@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' + '@metaplex-foundation/umi@0.9.2': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) - '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.6.3 - dev: false + '@metaplex-foundation/umi-options': 0.8.9 + '@metaplex-foundation/umi-public-keys': 0.8.9 + '@metaplex-foundation/umi-serializers': 0.9.0 - /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' + '@noble/curves@1.9.4': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 4.9.5 - dev: false + '@noble/hashes': 1.8.0 - /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' + '@noble/hashes@1.8.0': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.6.3 - dev: false + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 - /@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==} - peerDependencies: - typescript: '>=5' + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - typescript: 5.6.3 + '@solana/buffer-layout': 4.0.1 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + bigint-buffer: 1.1.5 + bignumber.js: 9.3.1 transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false + - bufferutil + - encoding + - typescript + - utf-8-validate - /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} - peerDependencies: - typescript: '>=5' + '@solana/buffer-layout@4.0.1': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false + buffer: 6.0.3 - /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-core@2.0.0-rc.1(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - typescript: 5.6.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 - /@solana/errors@2.0.0-preview.4(typescript@5.6.3): - resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==} - hasBin: true - peerDependencies: - typescript: '>=5' + '@solana/codecs-core@2.3.0(typescript@5.8.3)': dependencies: - chalk: 5.3.0 - commander: 12.1.0 - typescript: 5.6.3 - dev: false + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - /@solana/errors@2.0.0-rc.1(typescript@4.9.5): - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} - hasBin: true - peerDependencies: - typescript: '>=5' + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.3)': dependencies: - chalk: 5.3.0 - commander: 12.1.0 - typescript: 4.9.5 - dev: false + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 - /@solana/errors@2.0.0-rc.1(typescript@5.6.3): - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} - hasBin: true - peerDependencies: - typescript: '>=5' + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.3)': dependencies: - chalk: 5.3.0 - commander: 12.1.0 - typescript: 5.6.3 - dev: false + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 - /@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-numbers@2.3.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.6.3) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/errors': 2.0.0-preview.4(typescript@5.6.3) - typescript: 5.6.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} - peerDependencies: - typescript: '>=5' + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/errors': 2.0.0-rc.1(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - dev: false + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.8.3 - /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} - peerDependencies: - typescript: '>=5' + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) - typescript: 5.6.3 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - dev: false - /@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.94.0 + '@solana/errors@2.0.0-rc.1(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.5 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript - dev: false + chalk: 5.4.1 + commander: 12.1.0 + typescript: 5.8.3 - /@solana/spl-token-metadata@0.1.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-DSBlo7vjuLe/xvNn75OKKndDBkFxlqjLdWlq6rf40StnrhRn7TDntHGLZpry1cf3uzQFShqeLROGNPAJwvkPnA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 + '@solana/errors@2.3.0(typescript@5.8.3)': + dependencies: + chalk: 5.4.1 + commander: 14.0.0 + typescript: 5.8.3 + + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.5 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - - typescript - dev: false - /@solana/spl-token-metadata@0.1.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-DSBlo7vjuLe/xvNn75OKKndDBkFxlqjLdWlq6rf40StnrhRn7TDntHGLZpry1cf3uzQFShqeLROGNPAJwvkPnA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.5 + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - dev: false - /@solana/spl-token@0.3.11(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5): - resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.88.0 + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-metadata': 0.1.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.5 - buffer: 6.0.3 + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - typescript - - utf-8-validate - dev: false - /@solana/spl-token@0.4.8(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3): - resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.94.0 + '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-group': 0.0.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/spl-token-metadata': 0.1.5(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) - '@solana/web3.js': 1.95.5 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -698,364 +1383,238 @@ packages: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - dev: false - - /@solana/spl-type-length-value@0.1.0: - resolution: {integrity: sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==} - engines: {node: '>=16'} - dependencies: - buffer: 6.0.3 - dev: false - /@solana/web3.js@1.95.5: - resolution: {integrity: sha512-hU9cBrbg1z6gEjLH9vwIckGBVB78Ijm0iZFNk4ocm5OD82piPwuk3MeQ1rfiKD9YQtr95krrcaopb49EmQJlRg==} + '@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.25.7 - '@noble/curves': 1.6.0 - '@noble/hashes': 1.5.0 + '@babel/runtime': 7.27.6 + '@noble/curves': 1.9.4 + '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 - agentkeepalive: 4.5.0 - bigint-buffer: 1.1.5 - bn.js: 5.2.1 + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + agentkeepalive: 4.6.0 + bn.js: 5.2.2 borsh: 0.7.0 bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 - jayson: 4.1.2 + jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.0.4 + rpc-websockets: 9.1.1 superstruct: 2.0.2 transitivePeerDependencies: - bufferutil - encoding + - typescript - utf-8-validate - dev: false - /@solworks/soltoolkit-sdk@0.0.23(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-O6lXT3EBR4gmcjt0/33i97VMHVEImwXGi+4TNrDDdifn3tyOUB7V6PR1VGxlavQb9hqmVai3xhedg/rmbQzX7w==} + '@swc/helpers@0.5.17': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.5)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5) - '@solana/web3.js': 1.95.5 - '@types/bn.js': 5.1.6 - '@types/node': 18.19.65 - '@types/node-fetch': 2.6.12 - bn.js: 5.2.1 - decimal.js: 10.4.3 - typescript: 4.9.5 + tslib: 2.8.1 + + '@switchboard-xyz/common@3.4.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + axios: 1.10.0 + big.js: 6.2.2 + bn.js: 5.2.2 + bs58: 6.0.0 + buffer: 6.0.3 + decimal.js: 10.6.0 + js-sha256: 0.11.1 + protobufjs: 7.5.3 + yaml: 2.8.0 + zod: 4.0.0-beta.20250505T195954 transitivePeerDependencies: - bufferutil + - debug - encoding - - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - dev: false - /@soncodi/signal@2.0.7: - resolution: {integrity: sha512-zA2oZluZmVvgZEDjF243KWD1S2J+1SH1MVynI0O1KRgDt1lU8nqk7AK3oQfW/WpwT51L5waGSU0xKF/9BTP5Cw==} - dev: false - - /@swc/helpers@0.5.13: - resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} - dependencies: - tslib: 2.7.0 - dev: false - - /@switchboard-xyz/common@2.5.5: - resolution: {integrity: sha512-/qUmZlrfQyckvHGzS5Cj2+Ocd3eE64rPjQb1eEocc5dv4HXZMqbBbpM6BwURrQhZ65i3jO1evhTcAk3TVqCA8w==} - engines: {node: '>=12'} + '@switchboard-xyz/common@4.1.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/web3.js': 1.95.5 - axios: 1.7.8 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + axios: 1.10.0 big.js: 6.2.2 - bn.js: 5.2.1 - bs58: 5.0.0 - cron-validator: 1.3.1 - decimal.js: 10.4.3 - js-sha256: 0.11.0 - lodash: 4.17.21 - protobufjs: 7.4.0 - yaml: 2.6.1 + bn.js: 5.2.2 + bs58: 6.0.0 + buffer: 6.0.3 + decimal.js: 10.6.0 + js-sha256: 0.11.1 + protobufjs: 7.5.3 + yaml: 2.8.0 + zod: 4.0.0-beta.20250505T195954 transitivePeerDependencies: - bufferutil - debug - encoding + - typescript - utf-8-validate - dev: false - /@switchboard-xyz/on-demand@1.2.51(fastestsmallesttextencoderdecoder@1.0.22): - resolution: {integrity: sha512-IqtAEtYdCRQqG8a3tL5WOcLgBco8Iionu60Q+hQzCslQw76zDlkToHkI+71ASulFdZ2z+2XjaKV5ZVqPcYgP7g==} - engines: {node: '>= 18'} + '@switchboard-xyz/on-demand@2.17.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@brokerloop/ttlcache': 3.2.3 - '@coral-xyz/anchor-30': /@coral-xyz/anchor@0.30.1 - '@solana/web3.js': 1.95.5 - '@solworks/soltoolkit-sdk': 0.0.23(fastestsmallesttextencoderdecoder@1.0.22) - '@switchboard-xyz/common': 2.5.5 - axios: 1.7.8 - big.js: 6.2.2 - bs58: 5.0.0 + '@coral-xyz/anchor-31': '@coral-xyz/anchor@0.31.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)' + '@isaacs/ttlcache': 1.4.1 + '@switchboard-xyz/common': 4.1.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + axios: 1.10.0 + bs58: 6.0.0 + buffer: 6.0.3 + events: 3.3.0 + isomorphic-ws: 5.0.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) js-yaml: 4.1.0 - protobufjs: 7.4.0 + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug - encoding - - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - dev: false - /@types/bn.js@5.1.6: - resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - dependencies: - '@types/node': 20.16.11 - dev: false + '@types/big.js@6.2.2': {} - /@types/chai@4.3.20: - resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} - dev: true + '@types/chai@4.3.20': {} - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/connect@3.4.38': dependencies: - '@types/node': 20.16.11 - dev: false + '@types/node': 20.19.8 - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - requiresBuild: true - dev: true + '@types/json5@0.0.29': optional: true - /@types/mocha@9.1.1: - resolution: {integrity: sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==} - dev: true - - /@types/node-fetch@2.6.12: - resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} - dependencies: - '@types/node': 20.16.11 - form-data: 4.0.1 - dev: false - - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: false + '@types/mocha@9.1.1': {} - /@types/node@18.19.65: - resolution: {integrity: sha512-Ay5BZuO1UkTmVHzZJNvZKw/E+iB3GQABb6kijEz89w2JrfhNA+M/ebp18pfz9Gqe9ywhMC8AA8yC01lZq48J+Q==} - dependencies: - undici-types: 5.26.5 - dev: false + '@types/node@12.20.55': {} - /@types/node@20.16.11: - resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==} + '@types/node@20.19.8': dependencies: - undici-types: 6.19.8 + undici-types: 6.21.0 - /@types/uuid@8.3.4: - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - dev: false + '@types/uuid@8.3.4': {} - /@types/ws@7.4.7: - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + '@types/ws@7.4.7': dependencies: - '@types/node': 20.16.11 - dev: false + '@types/node': 20.19.8 - /@types/ws@8.5.12: - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + '@types/ws@8.18.1': dependencies: - '@types/node': 20.16.11 - dev: false + '@types/node': 20.19.8 - /@ungap/promise-all-settled@1.1.2: - resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} - dev: true + '@ungap/promise-all-settled@1.1.2': {} - /JSONStream@1.3.5: - resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} - hasBin: true - dependencies: - jsonparse: 1.3.1 - through: 2.3.8 - dev: false + '@zod/core@0.11.6': {} - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 - dev: false - /ansi-colors@4.1.1: - resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} - engines: {node: '>=6'} - dev: true + ansi-colors@4.1.1: {} - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true + ansi-regex@5.0.1: {} - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - dev: true - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - dev: true + arrify@1.0.1: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false + asynckit@0.4.0: {} - /axios@1.7.8: - resolution: {integrity: sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==} + axios@1.10.0: dependencies: follow-redirects: 1.15.9 - form-data: 4.0.1 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: false - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /base-x@3.0.10: - resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 - dev: false - /base-x@4.0.0: - resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} - dev: false + base-x@4.0.1: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: false + base-x@5.0.1: {} - /big.js@6.2.2: - resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - dev: false + base64-js@1.5.1: {} - /bigint-buffer@1.1.5: - resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} - engines: {node: '>= 10.0.0'} - requiresBuild: true + big.js@6.2.2: {} + + bigint-buffer@1.1.5: dependencies: bindings: 1.5.0 - dev: false - /bignumber.js@9.1.2: - resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - dev: false + bignumber.js@9.3.1: {} - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - dev: false - /bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: false + bn.js@5.2.2: {} - /borsh@0.7.0: - resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + borsh@0.7.0: dependencies: - bn.js: 5.2.1 + bn.js: 5.2.2 bs58: 4.0.1 text-encoding-utf-8: 1.0.2 - dev: false - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true + browser-stdout@1.3.1: {} - /bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + bs58@4.0.1: dependencies: - base-x: 3.0.10 - dev: false + base-x: 3.0.11 - /bs58@5.0.0: - resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + bs58@5.0.0: dependencies: - base-x: 4.0.0 - dev: false + base-x: 4.0.1 - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true + bs58@6.0.0: + dependencies: + base-x: 5.0.1 - /buffer-layout@1.2.2: - resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} - engines: {node: '>=4.5'} - dev: false + buffer-from@1.1.2: {} - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffer-layout@1.2.2: {} + + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: false - /bufferutil@4.0.8: - resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} - engines: {node: '>=6.14.2'} - requiresBuild: true + bufferutil@4.0.9: dependencies: - node-gyp-build: 4.8.2 - dev: false + node-gyp-build: 4.8.4 + optional: true - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 - /chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} + camelcase@6.3.0: {} + + chai@4.5.0: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -1064,30 +1623,19 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.1.0 - dev: true - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false + chalk@5.4.1: {} - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} + chokidar@3.5.3: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -1098,247 +1646,169 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - dev: true - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true + color-name@1.1.4: {} - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - dev: false - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: false + commander@12.1.0: {} - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false + commander@14.0.0: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + commander@2.20.3: {} - /cron-validator@1.3.1: - resolution: {integrity: sha512-C1HsxuPCY/5opR55G5/WNzyEGDWFVG+6GLrA+fW/sCTcP6A6NTjUP2AK7B8n2PyFs90kDG2qzwm8LMheADku6A==} - dev: false + concat-map@0.0.1: {} - /cross-fetch@3.1.8: - resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: false - /cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-fetch@4.1.0: dependencies: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: false - - /crypto-hash@1.3.0: - resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} - engines: {node: '>=8'} - dev: false - /debug@4.3.3(supports-color@8.1.1): - resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.3(supports-color@8.1.1): dependencies: ms: 2.1.2 + optionalDependencies: supports-color: 8.1.1 - dev: true - /decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - dev: true + decamelize@4.0.0: {} - /decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: false + decimal.js@10.6.0: {} - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.1.0 - dev: true - /delay@5.0.0: - resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} - engines: {node: '>=10'} - dev: false + delay@5.0.0: {} - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false + delayed-stream@1.0.0: {} + + diff@3.5.0: {} + + diff@5.0.0: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 - /diff@3.5.0: - resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} - engines: {node: '>=0.3.1'} - dev: true + emoji-regex@8.0.0: {} - /diff@5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} - dev: true + es-define-property@1.0.1: {} - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: dependencies: - no-case: 3.0.4 - tslib: 2.7.0 - dev: false + es-errors: 1.3.0 - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 - /es6-promise@4.2.8: - resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - dev: false + es6-promise@4.2.8: {} - /es6-promisify@5.0.0: - resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} + es6-promisify@5.0.0: dependencies: es6-promise: 4.2.8 - dev: false - /escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - dev: true + escalade@3.2.0: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true + escape-string-regexp@4.0.0: {} - /eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - dev: false + eventemitter3@4.0.7: {} - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: false + eventemitter3@5.0.1: {} - /eyes@0.1.8: - resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} - engines: {node: '> 0.1.90'} - dev: false + events@3.3.0: {} - /fast-stable-stringify@1.0.0: - resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - dev: false + eyes@0.1.8: {} - /fastestsmallesttextencoderdecoder@1.0.22: - resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} - dev: false + fast-stable-stringify@1.0.0: {} - /file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - dev: false + fastestsmallesttextencoderdecoder@1.0.22: {} - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - dev: true + flat@5.0.2: {} - /follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false + follow-redirects@1.15.9: {} - /form-data@4.0.1: - resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} - engines: {node: '>= 6'} + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 - dev: false - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + function-bind@1.1.2: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-caller-file@2.0.5: {} - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + get-func-name@2.0.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -1346,225 +1816,137 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /growl@1.10.5: - resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} - engines: {node: '>=4.x'} - dev: true + gopd@1.2.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: true + growl@1.10.5: {} - /he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: true + has-flag@4.0.0: {} - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + humanize-ms@1.2.1: dependencies: ms: 2.1.3 - dev: false - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: false + ieee754@1.2.1: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true + inherits@2.0.4: {} - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: true + is-plain-obj@2.1.0: {} - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /isomorphic-ws@4.0.1(ws@7.5.10): - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 7.5.10 - dev: false + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - /jayson@4.1.2: - resolution: {integrity: sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA==} - engines: {node: '>=8'} - hasBin: true + isomorphic-ws@5.0.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 '@types/node': 12.20.55 '@types/ws': 7.4.7 - JSONStream: 1.3.5 commander: 2.20.3 delay: 5.0.0 es6-promisify: 5.0.0 eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10) + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) json-stringify-safe: 5.0.1 + stream-json: 1.9.1 uuid: 8.3.2 - ws: 7.5.10 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - dev: false - /js-sha256@0.11.0: - resolution: {integrity: sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q==} - dev: false + js-sha256@0.11.1: {} - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: false + json-stringify-safe@5.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - requiresBuild: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: true optional: true - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: false - - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: false - - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /long@5.2.3: - resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} - dev: false + long@5.3.2: {} - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.7.0 - dev: false + make-error@1.3.6: {} - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + math-intrinsics@1.1.0: {} - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - dev: false - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 - dev: true + brace-expansion: 1.1.12 - /minimatch@4.2.1: - resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==} - engines: {node: '>=10'} + minimatch@4.2.1: dependencies: - brace-expansion: 1.1.11 - dev: true + brace-expansion: 1.1.12 - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + minimist@1.2.8: {} - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 - dev: true - /mocha@9.2.2: - resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} - engines: {node: '>= 12.0.0'} - hasBin: true + mocha@9.2.2: dependencies: '@ungap/promise-all-settled': 1.1.2 ansi-colors: 4.1.1 @@ -1590,98 +1972,45 @@ packages: yargs: 16.2.0 yargs-parser: 20.2.4 yargs-unparser: 2.0.0 - dev: true - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.2: {} - /nanoid@3.3.1: - resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + ms@2.1.3: {} - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - dependencies: - lower-case: 2.0.2 - tslib: 2.7.0 - dev: false + nanoid@3.3.1: {} - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: false - /node-gyp-build@4.8.2: - resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==} - hasBin: true - requiresBuild: true - dev: false + node-gyp-build@4.8.4: + optional: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /pako@2.1.0: - resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - dev: false + pako@2.1.0: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picomatch@2.3.1: {} - /protobufjs@7.4.0: - resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} - engines: {node: '>=12.0.0'} - requiresBuild: true + protobufjs@7.5.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -1693,182 +2022,107 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.16.11 - long: 5.2.3 - dev: false + '@types/node': 20.19.8 + long: 5.3.2 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false + proxy-from-env@1.1.0: {} - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - dev: true - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: false - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /rpc-websockets@7.11.0: - resolution: {integrity: sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==} - deprecated: deprecate 7.11.0 + rpc-websockets@7.11.0: dependencies: eventemitter3: 4.0.7 uuid: 8.3.2 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - bufferutil: 4.0.8 + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: false - /rpc-websockets@9.0.4: - resolution: {integrity: sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ==} + rpc-websockets@9.1.1: dependencies: - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.17 '@types/uuid': 8.3.4 - '@types/ws': 8.5.12 + '@types/ws': 8.18.1 buffer: 6.0.3 eventemitter3: 5.0.1 uuid: 8.3.2 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - bufferutil: 4.0.8 + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: false - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /serialize-javascript@6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + serialize-javascript@6.0.0: dependencies: randombytes: 2.1.0 - dev: true - /snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.7.0 - dev: false - - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true + source-map@0.6.1: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + stream-chain@2.2.5: {} + + stream-json@1.9.1: + dependencies: + stream-chain: 2.2.5 + + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - requiresBuild: true - dev: true + strip-bom@3.0.0: optional: true - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true + strip-json-comments@3.1.1: {} - /superstruct@0.15.5: - resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} - dev: false + superstruct@0.15.5: {} - /superstruct@2.0.2: - resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} - engines: {node: '>=14.0.0'} - dev: false + superstruct@2.0.2: {} - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - dev: true - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - dev: true - - /text-encoding-utf-8@1.0.2: - resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - dev: false - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: false + text-encoding-utf-8@1.0.2: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toml@3.0.0: - resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - dev: false + toml@3.0.0: {} - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: false + tr46@0.0.3: {} - /ts-mocha@10.0.0(mocha@9.2.2): - resolution: {integrity: sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==} - engines: {node: '>= 6.X.X'} - hasBin: true - peerDependencies: - mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X + ts-mocha@10.1.0(mocha@9.2.2): dependencies: mocha: 9.2.2 ts-node: 7.0.1 optionalDependencies: tsconfig-paths: 3.15.0 - dev: true - /ts-node@7.0.1: - resolution: {integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==} - engines: {node: '>=4.2.0'} - hasBin: true + ts-node@7.0.1: dependencies: arrify: 1.0.1 buffer-from: 1.1.2 @@ -1878,153 +2132,75 @@ packages: mkdirp: 0.5.6 source-map-support: 0.5.21 yn: 2.0.0 - dev: true - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - requiresBuild: true + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: true optional: true - /tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - dev: false - - /type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - dev: true - - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: false + tslib@2.8.1: {} - /typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} - engines: {node: '>=14.17'} - hasBin: true + type-detect@4.1.0: {} - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: false + typescript@5.8.3: {} - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.21.0: {} - /utf-8-validate@5.0.10: - resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} - engines: {node: '>=6.14.2'} - requiresBuild: true + utf-8-validate@5.0.10: dependencies: - node-gyp-build: 4.8.2 - dev: false + node-gyp-build: 4.8.4 + optional: true - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: false + uuid@8.3.2: {} - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: false + webidl-conversions@3.0.1: {} - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: false - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /workerpool@6.2.0: - resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} - dev: true + workerpool@6.2.0: {} - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + wrappy@1.0.2: {} - /ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false + ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 - /ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dependencies: - bufferutil: 4.0.8 + ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 utf-8-validate: 5.0.10 - dev: false - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} - engines: {node: '>= 14'} - hasBin: true - dev: false + yaml@2.8.0: {} - /yargs-parser@20.2.4: - resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} - engines: {node: '>=10'} - dev: true + yargs-parser@20.2.4: {} - /yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} + yargs-unparser@2.0.0: dependencies: camelcase: 6.3.0 decamelize: 4.0.0 flat: 5.0.2 is-plain-obj: 2.1.0 - dev: true - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 escalade: 3.2.0 @@ -2033,14 +2209,11 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.4 - dev: true - /yn@2.0.0: - resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} - engines: {node: '>=4'} - dev: true + yn@2.0.0: {} - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yocto-queue@0.1.0: {} + + zod@4.0.0-beta.20250505T195954: + dependencies: + '@zod/core': 0.11.6 diff --git a/solauto-sdk/src/clients/index.ts b/solauto-sdk/src/clients/index.ts deleted file mode 100644 index 37402211..00000000 --- a/solauto-sdk/src/clients/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './solautoMarginfiClient'; -export * from './solautoClient'; -export * from './referralStateManager'; -export * from './txHandler'; \ No newline at end of file diff --git a/solauto-sdk/src/clients/solautoMarginfiClient.ts b/solauto-sdk/src/clients/solautoMarginfiClient.ts deleted file mode 100644 index 45efdbec..00000000 --- a/solauto-sdk/src/clients/solautoMarginfiClient.ts +++ /dev/null @@ -1,706 +0,0 @@ -import { - fromWeb3JsPublicKey, - toWeb3JsPublicKey, -} from "@metaplex-foundation/umi-web3js-adapters"; -import { - Signer, - TransactionBuilder, - publicKey, - PublicKey as UmiPublicKey, - transactionBuilder, - createSignerFromKeypair, - AccountMeta, -} from "@metaplex-foundation/umi"; -import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; -import { SolautoClient, SolautoClientArgs } from "./solautoClient"; -import { MarginfiAssetAccounts } from "../types/accounts"; -import { - DEFAULT_MARGINFI_GROUP, - MARGINFI_ACCOUNTS, - MARGINFI_ACCOUNTS_LOOKUP_TABLE, -} from "../constants/marginfiAccounts"; -import { - DCASettingsInpArgs, - LendingPlatform, - PositionState, - PositionType, - RebalanceDirection, - SolautoActionArgs, - SolautoRebalanceTypeArgs, - SolautoSettingsParametersInpArgs, - marginfiOpenPosition, - marginfiProtocolInteraction, - marginfiRebalance, - marginfiRefreshData, -} from "../generated"; -import { getMarginfiAccountPDA, getTokenAccount } from "../utils/accountUtils"; -import { generateRandomU64 } from "../utils/generalUtils"; -import { - MARGINFI_PROGRAM_ID, - MarginfiAccount, - lendingAccountBorrow, - lendingAccountDeposit, - lendingAccountEndFlashloan, - lendingAccountRepay, - lendingAccountStartFlashloan, - lendingAccountWithdraw, - marginfiAccountInitialize, - safeFetchAllMarginfiAccount, - safeFetchMarginfiAccount, -} from "../marginfi-sdk"; -import { - FlashLoanDetails, - RebalanceValues, -} from "../utils/solauto/rebalanceUtils"; -import { - findMarginfiAccounts, - getAllMarginfiAccountsByAuthority, - getMarginfiAccountPositionState, - getMaxLtvAndLiqThreshold, -} from "../utils/marginfiUtils"; -import { bytesToI80F48, fromBaseUnit, toBps } from "../utils/numberUtils"; -import { QuoteResponse } from "@jup-ag/api"; -import { safeGetPrice } from "../utils"; - -export interface SolautoMarginfiClientArgs extends SolautoClientArgs { - marginfiAccount?: PublicKey | Signer; - marginfiAccountSeedIdx?: bigint; - marginfiGroup?: PublicKey; -} - -export class SolautoMarginfiClient extends SolautoClient { - private initialized: boolean = false; - - public marginfiProgram!: PublicKey; - - public marginfiAccountSeedIdx: bigint = BigInt(0); - public marginfiAccount!: PublicKey | Signer; - public marginfiAccountPk!: PublicKey; - public marginfiGroup!: PublicKey; - - public marginfiSupplyAccounts!: MarginfiAssetAccounts; - public marginfiDebtAccounts!: MarginfiAssetAccounts; - - public supplyPriceOracle!: PublicKey; - public debtPriceOracle!: PublicKey; - - // For flash loans - public intermediaryMarginfiAccountSigner?: Signer; - public intermediaryMarginfiAccountPk!: PublicKey; - public intermediaryMarginfiAccount?: MarginfiAccount; - - async initialize(args: SolautoMarginfiClientArgs) { - await super.initialize(args); - - this.lendingPlatform = LendingPlatform.Marginfi; - - if (this.selfManaged) { - this.marginfiAccount = - args.marginfiAccount ?? - createSignerFromKeypair(this.umi, this.umi.eddsa.generateKeypair()); - } else { - this.marginfiAccountSeedIdx = generateRandomU64(); - this.marginfiAccount = this.solautoPositionData - ? toWeb3JsPublicKey( - this.solautoPositionData.position.protocolUserAccount - ) - : getMarginfiAccountPDA( - this.solautoPosition, - this.marginfiAccountSeedIdx, - this.programId - ); - } - this.marginfiAccountPk = - "publicKey" in this.marginfiAccount - ? toWeb3JsPublicKey(this.marginfiAccount.publicKey) - : this.marginfiAccount; - - const marginfiAccountData = !args.new - ? await safeFetchMarginfiAccount( - this.umi, - publicKey(this.marginfiAccountPk), - { commitment: "confirmed" } - ) - : null; - this.marginfiGroup = new PublicKey( - marginfiAccountData - ? marginfiAccountData.group.toString() - : (args.marginfiGroup ?? DEFAULT_MARGINFI_GROUP) - ); - - this.marginfiSupplyAccounts = - MARGINFI_ACCOUNTS[this.marginfiGroup.toString()][ - this.supplyMint.toString() - ]!; - this.marginfiDebtAccounts = - MARGINFI_ACCOUNTS[this.marginfiGroup.toString()][ - this.debtMint.toString() - ]!; - - // TODO: Don't dynamically pull oracle from bank until Marginfi sorts out their price oracle issues. - // const [supplyBank, debtBank] = await safeFetchAllBank(this.umi, [ - // publicKey(this.marginfiSupplyAccounts.bank), - // publicKey(this.marginfiDebtAccounts.bank), - // ]); - // this.supplyPriceOracle = toWeb3JsPublicKey(supplyBank.config.oracleKeys[0]); - // this.debtPriceOracle = toWeb3JsPublicKey(debtBank.config.oracleKeys[0]); - this.supplyPriceOracle = new PublicKey( - this.marginfiSupplyAccounts.priceOracle - ); - this.debtPriceOracle = new PublicKey(this.marginfiDebtAccounts.priceOracle); - - if (!this.initialized) { - await this.setIntermediaryMarginfiDetails(); - } - this.initialized = true; - } - - async setIntermediaryMarginfiDetails() { - const existingMarginfiAccounts = ( - await getAllMarginfiAccountsByAuthority( - this.umi, - toWeb3JsPublicKey(this.signer.publicKey), - this.marginfiGroup - ) - ) - .filter((x) => !x.marginfiAccount.equals(this.marginfiAccountPk)) - .sort((a, b) => - a.marginfiAccount.toString().localeCompare(b.marginfiAccount.toString()) - ); - const compatibleMarginfiAccounts = - existingMarginfiAccounts.length > 0 - ? ( - await safeFetchAllMarginfiAccount( - this.umi, - existingMarginfiAccounts.map((x) => publicKey(x.marginfiAccount)) - ) - ).filter( - (x) => - x.group.toString() === this.marginfiGroup.toString() && - x.lendingAccount.balances.find( - (y) => - y.bankPk.toString() !== PublicKey.default.toString() && - (Math.round(bytesToI80F48(y.assetShares.value)) != 0 || - Math.round(bytesToI80F48(y.liabilityShares.value)) != 0) - ) === undefined - ) - : []; - - this.intermediaryMarginfiAccountSigner = - compatibleMarginfiAccounts.length > 0 - ? undefined - : createSignerFromKeypair(this.umi, this.umi.eddsa.generateKeypair()); - this.intermediaryMarginfiAccountPk = - compatibleMarginfiAccounts.length > 0 - ? toWeb3JsPublicKey(compatibleMarginfiAccounts[0].publicKey) - : toWeb3JsPublicKey(this.intermediaryMarginfiAccountSigner!.publicKey); - this.intermediaryMarginfiAccount = - compatibleMarginfiAccounts.length > 0 - ? compatibleMarginfiAccounts[0] - : undefined; - } - - protocolAccount(): PublicKey { - return this.marginfiAccountPk; - } - - defaultLookupTables(): string[] { - return [MARGINFI_ACCOUNTS_LOOKUP_TABLE, ...super.defaultLookupTables()]; - } - - lutAccountsToAdd(): PublicKey[] { - return [ - ...super.lutAccountsToAdd(), - this.marginfiAccountPk, - ...(this.signer.publicKey.toString() === this.authority.toString() - ? [this.intermediaryMarginfiAccountPk] - : []), - ]; - } - - async maxLtvAndLiqThresholdBps(): Promise<[number, number] | undefined> { - const result = await super.maxLtvAndLiqThresholdBps(); - if (result) { - return result; - } else if ( - this.supplyMint.equals(PublicKey.default) || - this.debtMint.equals(PublicKey.default) - ) { - return [0, 0]; - } else { - const [maxLtv, liqThreshold] = await getMaxLtvAndLiqThreshold( - this.umi, - this.marginfiGroup, - { - mint: this.supplyMint, - }, - { - mint: this.debtMint, - } - ); - this.maxLtvBps = toBps(maxLtv); - this.liqThresholdBps = toBps(liqThreshold); - return [this.maxLtvBps, this.liqThresholdBps]; - } - } - - marginfiAccountInitialize(marginfiAccount: Signer): TransactionBuilder { - return marginfiAccountInitialize(this.umi, { - marginfiAccount: marginfiAccount, - marginfiGroup: publicKey(this.marginfiGroup), - authority: this.signer, - feePayer: this.signer, - }); - } - - openPosition( - settingParams?: SolautoSettingsParametersInpArgs, - dca?: DCASettingsInpArgs - ): TransactionBuilder { - return super - .openPosition(settingParams, dca) - .add(this.marginfiOpenPositionIx(settingParams, dca)); - } - - private marginfiOpenPositionIx( - settingParams?: SolautoSettingsParametersInpArgs, - dca?: DCASettingsInpArgs, - positionType?: PositionType - ): TransactionBuilder { - let signerDebtTa: UmiPublicKey | undefined = undefined; - if (dca) { - signerDebtTa = publicKey(this.signerDebtTa); - } - - return marginfiOpenPosition(this.umi, { - signer: this.signer, - marginfiProgram: publicKey(MARGINFI_PROGRAM_ID), - signerReferralState: publicKey(this.referralState), - referredByState: this.referredByState - ? publicKey(this.referredByState) - : undefined, - referredBySupplyTa: this.referredBySupplyTa() - ? publicKey(this.referredBySupplyTa()!) - : undefined, - solautoPosition: publicKey(this.solautoPosition), - marginfiGroup: publicKey(this.marginfiGroup), - marginfiAccount: - "publicKey" in this.marginfiAccount - ? (this.marginfiAccount as Signer) - : publicKey(this.marginfiAccount), - supplyMint: publicKey(this.supplyMint), - supplyBank: publicKey(this.marginfiSupplyAccounts.bank), - positionSupplyTa: publicKey(this.positionSupplyTa), - debtMint: publicKey(this.debtMint), - debtBank: publicKey(this.marginfiDebtAccounts.bank), - positionDebtTa: publicKey(this.positionDebtTa), - signerDebtTa: signerDebtTa, - positionType: positionType ?? PositionType.Leverage, - positionData: { - positionId: this.positionId!, - settingParams: settingParams ?? null, - dca: dca ?? null, - }, - marginfiAccountSeedIdx: !this.selfManaged - ? this.marginfiAccountSeedIdx - : null, - }); - } - - refresh(): TransactionBuilder { - return marginfiRefreshData(this.umi, { - signer: this.signer, - marginfiProgram: publicKey(MARGINFI_PROGRAM_ID), - marginfiGroup: publicKey(this.marginfiGroup), - marginfiAccount: publicKey(this.marginfiAccount), - supplyBank: publicKey(this.marginfiSupplyAccounts.bank), - supplyPriceOracle: publicKey(this.supplyPriceOracle), - debtBank: publicKey(this.marginfiDebtAccounts.bank), - debtPriceOracle: publicKey(this.debtPriceOracle), - solautoPosition: publicKey(this.solautoPosition), - }); - } - - protocolInteraction(args: SolautoActionArgs): TransactionBuilder { - let tx = super.protocolInteraction(args); - - if (this.selfManaged) { - return tx.add(this.marginfiProtocolInteractionIx(args)); - } else { - return tx.add(this.marginfiSolautoProtocolInteractionIx(args)); - } - } - - private marginfiProtocolInteractionIx(args: SolautoActionArgs) { - switch (args.__kind) { - case "Deposit": { - return lendingAccountDeposit(this.umi, { - amount: args.fields[0], - signer: this.signer, - signerTokenAccount: publicKey(this.signerSupplyTa), - marginfiAccount: publicKey(this.marginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - bank: publicKey(this.marginfiSupplyAccounts.bank), - bankLiquidityVault: publicKey( - this.marginfiSupplyAccounts.liquidityVault - ), - }); - } - case "Borrow": { - return lendingAccountBorrow(this.umi, { - amount: args.fields[0], - signer: this.signer, - destinationTokenAccount: publicKey(this.signerDebtTa), - marginfiAccount: publicKey(this.marginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - bank: publicKey(this.marginfiDebtAccounts.bank), - bankLiquidityVault: publicKey( - this.marginfiDebtAccounts.liquidityVault - ), - bankLiquidityVaultAuthority: publicKey( - this.marginfiDebtAccounts.vaultAuthority - ), - }); - } - case "Repay": { - return lendingAccountRepay(this.umi, { - amount: - args.fields[0].__kind === "Some" ? args.fields[0].fields[0] : 0, - repayAll: args.fields[0].__kind === "All" ? true : false, - signer: this.signer, - signerTokenAccount: publicKey(this.signerDebtTa), - marginfiAccount: publicKey(this.marginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - bank: publicKey(this.marginfiDebtAccounts.bank), - bankLiquidityVault: publicKey( - this.marginfiDebtAccounts.liquidityVault - ), - }); - } - case "Withdraw": { - return lendingAccountWithdraw(this.umi, { - amount: - args.fields[0].__kind === "Some" ? args.fields[0].fields[0] : 0, - withdrawAll: args.fields[0].__kind === "All" ? true : false, - signer: this.signer, - destinationTokenAccount: publicKey(this.signerSupplyTa), - marginfiAccount: publicKey(this.marginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - bank: publicKey(this.marginfiSupplyAccounts.bank), - bankLiquidityVault: publicKey( - this.marginfiSupplyAccounts.liquidityVault - ), - bankLiquidityVaultAuthority: publicKey( - this.marginfiSupplyAccounts.vaultAuthority - ), - }); - } - } - } - - private marginfiSolautoProtocolInteractionIx( - args: SolautoActionArgs - ): TransactionBuilder { - let positionSupplyTa: UmiPublicKey | undefined = undefined; - let vaultSupplyTa: UmiPublicKey | undefined = undefined; - let supplyVaultAuthority: UmiPublicKey | undefined = undefined; - if (args.__kind === "Deposit" || args.__kind === "Withdraw") { - positionSupplyTa = publicKey( - args.__kind === "Withdraw" || this.selfManaged - ? this.signerSupplyTa - : this.positionSupplyTa - ); - vaultSupplyTa = publicKey(this.marginfiSupplyAccounts.liquidityVault); - supplyVaultAuthority = publicKey( - this.marginfiSupplyAccounts.vaultAuthority - ); - } - - let positionDebtTa: UmiPublicKey | undefined = undefined; - let vaultDebtTa: UmiPublicKey | undefined = undefined; - let debtVaultAuthority: UmiPublicKey | undefined = undefined; - if (args.__kind === "Borrow" || args.__kind === "Repay") { - positionDebtTa = publicKey( - args.__kind === "Borrow" || this.selfManaged - ? this.signerDebtTa - : this.positionDebtTa - ); - vaultDebtTa = publicKey(this.marginfiDebtAccounts.liquidityVault); - debtVaultAuthority = publicKey(this.marginfiDebtAccounts.vaultAuthority); - } - - let supplyPriceOracle: UmiPublicKey | undefined = undefined; - let debtPriceOracle: UmiPublicKey | undefined = undefined; - if (args.__kind === "Withdraw" || args.__kind === "Borrow") { - supplyPriceOracle = publicKey(this.supplyPriceOracle); - debtPriceOracle = publicKey(this.debtPriceOracle); - } - - return marginfiProtocolInteraction(this.umi, { - signer: this.signer, - marginfiProgram: publicKey(MARGINFI_PROGRAM_ID), - solautoPosition: publicKey(this.solautoPosition), - marginfiGroup: publicKey(this.marginfiGroup), - marginfiAccount: publicKey(this.marginfiAccountPk), - supplyBank: publicKey(this.marginfiSupplyAccounts.bank), - supplyPriceOracle, - positionSupplyTa, - vaultSupplyTa, - supplyVaultAuthority, - debtBank: publicKey(this.marginfiDebtAccounts.bank), - debtPriceOracle, - positionDebtTa, - vaultDebtTa, - debtVaultAuthority, - solautoAction: args, - }); - } - - rebalance( - rebalanceStep: "A" | "B", - jupQuote: QuoteResponse, - rebalanceType: SolautoRebalanceTypeArgs, - rebalanceValues: RebalanceValues, - flashLoan?: FlashLoanDetails, - targetLiqUtilizationRateBps?: number - ): TransactionBuilder { - const inputIsSupply = new PublicKey(jupQuote.inputMint).equals( - this.supplyMint - ); - const outputIsSupply = new PublicKey(jupQuote.outputMint).equals( - this.supplyMint - ); - const needSupplyAccounts = - (inputIsSupply && rebalanceStep === "A") || - (outputIsSupply && rebalanceStep === "B") || - (inputIsSupply && flashLoan !== undefined && rebalanceStep == "B"); - const needDebtAccounts = - (!inputIsSupply && rebalanceStep === "A") || - (!outputIsSupply && rebalanceStep === "B") || - (!inputIsSupply && flashLoan !== undefined && rebalanceStep == "B"); - - return marginfiRebalance(this.umi, { - signer: this.signer, - marginfiProgram: publicKey(MARGINFI_PROGRAM_ID), - ixsSysvar: publicKey(SYSVAR_INSTRUCTIONS_PUBKEY), - solautoFeesTa: - rebalanceStep === "B" - ? publicKey( - rebalanceValues.rebalanceDirection === RebalanceDirection.Boost - ? this.solautoFeesSupplyTa - : this.solautoFeesDebtTa - ) - : undefined, - authorityReferralState: publicKey(this.referralState), - referredByTa: this.referredByState - ? publicKey( - rebalanceValues.rebalanceDirection === RebalanceDirection.Boost - ? this.referredBySupplyTa()! - : this.referredByDebtTa()! - ) - : undefined, - positionAuthority: - rebalanceValues.rebalanceAction === "dca" - ? publicKey(this.authority) - : undefined, - solautoPosition: publicKey(this.solautoPosition), - marginfiGroup: publicKey(this.marginfiGroup), - marginfiAccount: publicKey(this.marginfiAccountPk), - intermediaryTa: publicKey( - getTokenAccount( - toWeb3JsPublicKey(this.signer.publicKey), - new PublicKey(jupQuote.inputMint) - ) - ), - supplyBank: publicKey(this.marginfiSupplyAccounts.bank), - supplyPriceOracle: publicKey(this.supplyPriceOracle), - positionSupplyTa: publicKey(this.positionSupplyTa), - authoritySupplyTa: this.selfManaged - ? publicKey(getTokenAccount(this.authority, this.supplyMint)) - : undefined, - vaultSupplyTa: needSupplyAccounts - ? publicKey(this.marginfiSupplyAccounts.liquidityVault) - : undefined, - supplyVaultAuthority: needSupplyAccounts - ? publicKey(this.marginfiSupplyAccounts.vaultAuthority) - : undefined, - debtBank: publicKey(this.marginfiDebtAccounts.bank), - debtPriceOracle: publicKey(this.debtPriceOracle), - positionDebtTa: publicKey(this.positionDebtTa), - authorityDebtTa: this.selfManaged - ? publicKey(getTokenAccount(this.authority, this.debtMint)) - : undefined, - vaultDebtTa: needDebtAccounts - ? publicKey(this.marginfiDebtAccounts.liquidityVault) - : undefined, - debtVaultAuthority: needDebtAccounts - ? publicKey(this.marginfiDebtAccounts.vaultAuthority) - : undefined, - rebalanceType, - targetLiqUtilizationRateBps: targetLiqUtilizationRateBps ?? null, - targetInAmountBaseUnit: - rebalanceStep === "A" ? parseInt(jupQuote.inAmount) : null, - }); - } - - flashBorrow( - flashLoanDetails: FlashLoanDetails, - destinationTokenAccount: PublicKey - ): TransactionBuilder { - const bank = flashLoanDetails.mint.equals(this.supplyMint) - ? this.marginfiSupplyAccounts - : this.marginfiDebtAccounts; - return transactionBuilder() - .add( - lendingAccountStartFlashloan(this.umi, { - endIndex: 0, // We set this after building the transaction - ixsSysvar: publicKey(SYSVAR_INSTRUCTIONS_PUBKEY), - marginfiAccount: publicKey(this.intermediaryMarginfiAccountPk), - signer: this.signer, - }) - ) - .add( - lendingAccountBorrow(this.umi, { - amount: flashLoanDetails.baseUnitAmount, - bank: publicKey(bank.bank), - bankLiquidityVault: publicKey(bank.liquidityVault), - bankLiquidityVaultAuthority: publicKey(bank.vaultAuthority), - destinationTokenAccount: publicKey(destinationTokenAccount), - marginfiAccount: publicKey(this.intermediaryMarginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - signer: this.signer, - }) - ); - } - - flashRepay(flashLoanDetails: FlashLoanDetails): TransactionBuilder { - const accounts = flashLoanDetails.mint.equals(this.supplyMint) - ? { data: this.marginfiSupplyAccounts, oracle: this.supplyPriceOracle } - : { data: this.marginfiDebtAccounts, oracle: this.debtPriceOracle }; - - const remainingAccounts: AccountMeta[] = []; - let includedFlashLoanToken = false; - - if (this.intermediaryMarginfiAccount) { - this.intermediaryMarginfiAccount.lendingAccount.balances.forEach( - async (x) => { - if (x.active) { - if (x.bankPk === accounts.data.bank) { - includedFlashLoanToken = true; - } - - // TODO: Don't dynamically pull from bank until Marginfi sorts out their price oracle issues. - // const bankData = await safeFetchBank(this.umi, publicKey(accounts.data.bank)); - // const priceOracle = bankData!.config.oracleKeys[0]; - const priceOracle = publicKey( - findMarginfiAccounts(toWeb3JsPublicKey(x.bankPk)).priceOracle - ); - - remainingAccounts.push( - ...[ - { - pubkey: x.bankPk, - isSigner: false, - isWritable: false, - }, - { - pubkey: priceOracle, - isSigner: false, - isWritable: false, - }, - ] - ); - } - } - ); - } - if (!this.intermediaryMarginfiAccount || !includedFlashLoanToken) { - remainingAccounts.push( - ...[ - { - pubkey: fromWeb3JsPublicKey(new PublicKey(accounts.data.bank)), - isSigner: false, - isWritable: false, - }, - { - pubkey: fromWeb3JsPublicKey(new PublicKey(accounts.oracle)), - isSigner: false, - isWritable: false, - }, - ] - ); - } - - return transactionBuilder() - .add( - lendingAccountRepay(this.umi, { - amount: flashLoanDetails.baseUnitAmount, - repayAll: null, - bank: publicKey(accounts.data.bank), - bankLiquidityVault: publicKey(accounts.data.liquidityVault), - marginfiAccount: publicKey(this.intermediaryMarginfiAccountPk), - marginfiGroup: publicKey(this.marginfiGroup), - signer: this.signer, - signerTokenAccount: publicKey( - getTokenAccount( - toWeb3JsPublicKey(this.signer.publicKey), - flashLoanDetails.mint - ) - ), - }) - ) - .add( - lendingAccountEndFlashloan(this.umi, { - marginfiAccount: publicKey(this.intermediaryMarginfiAccountPk), - signer: this.signer, - }).addRemainingAccounts(remainingAccounts) - ); - } - - async getFreshPositionState(): Promise { - const state = await super.getFreshPositionState(); - if (state) { - return state; - } - - const useDesignatedMint = - !this.selfManaged && - (this.solautoPositionData === null || - !toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority)); - - const freshState = await getMarginfiAccountPositionState( - this.umi, - { pk: this.marginfiAccountPk }, - this.marginfiGroup, - useDesignatedMint ? { mint: this.supplyMint } : undefined, - useDesignatedMint ? { mint: this.debtMint } : undefined, - this.livePositionUpdates - ); - - if (freshState) { - this.log("Fresh state", freshState); - const supplyPrice = safeGetPrice(freshState?.supply.mint)!; - const debtPrice = safeGetPrice(freshState?.debt.mint)!; - this.log("Supply price: ", supplyPrice); - this.log("Debt price: ", debtPrice); - this.log("Liq threshold bps:", freshState.liqThresholdBps); - this.log("Liq utilization rate bps:", freshState.liqUtilizationRateBps); - this.log( - "Supply USD:", - fromBaseUnit( - freshState.supply.amountUsed.baseUnit, - freshState.supply.decimals - ) * supplyPrice - ); - this.log( - "Debt USD:", - fromBaseUnit( - freshState.debt.amountUsed.baseUnit, - freshState.debt.decimals - ) * debtPrice - ); - } - - return freshState; - } -} diff --git a/solauto-sdk/src/clients/txHandler.ts b/solauto-sdk/src/clients/txHandler.ts deleted file mode 100644 index 2459f057..00000000 --- a/solauto-sdk/src/clients/txHandler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Umi } from "@metaplex-foundation/umi"; -import { Connection, PublicKey } from "@solana/web3.js"; -import { - consoleLog, - getSolanaRpcConnection, -} from "../utils"; -import { SOLAUTO_PROD_PROGRAM } from "../constants"; - -export abstract class TxHandler { - public connection!: Connection; - public umi!: Umi; - - constructor( - public rpcUrl: string, - localTest?: boolean, - public programId: PublicKey = SOLAUTO_PROD_PROGRAM - ) { - const [connection, umi] = getSolanaRpcConnection(this.rpcUrl, this.programId); - this.connection = connection; - this.umi = umi; - - if (!(globalThis as any).LOCAL_TEST && localTest) { - (globalThis as any).LOCAL_TEST = Boolean(localTest); - } - } - - log(...args: any[]): void { - consoleLog(...args); - } - - abstract defaultLookupTables(): string[]; - - abstract resetLiveTxUpdates(success?: boolean): Promise; -} diff --git a/solauto-sdk/src/constants/README.md b/solauto-sdk/src/constants/README.md deleted file mode 100644 index 01238c8b..00000000 --- a/solauto-sdk/src/constants/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Instructions for supporting new token: - -- add public key to tokenConstants.ts -- add price feed in pythConstants.ts for it -- create ATA for solauto fees wallet -- `pnpm update-solauto-lut` -- `pnpm test:ts:unit` \ No newline at end of file diff --git a/solauto-sdk/src/constants/generalAccounts.ts b/solauto-sdk/src/constants/generalConstants.ts similarity index 53% rename from solauto-sdk/src/constants/generalAccounts.ts rename to solauto-sdk/src/constants/generalConstants.ts index 4273244c..08682b23 100644 --- a/solauto-sdk/src/constants/generalAccounts.ts +++ b/solauto-sdk/src/constants/generalConstants.ts @@ -1,5 +1,10 @@ import { PublicKey } from "@solana/web3.js"; +import { buildIronforgeApiUrl } from "../utils"; export const USD_DECIMALS = 9; export const SOLAUTO_FEES_WALLET = new PublicKey("AprYCPiVeKMCgjQ2ZufwChMzvQ5kFjJo2ekTLSkXsQDm"); -export const SOLAUTO_MANAGER = new PublicKey("MNGRcX4nc7quPdzBbNKJ4ScK5EE73JnwJVGxuJXhHCY"); \ No newline at end of file +export const SOLAUTO_MANAGER = new PublicKey("MNGRcX4nc7quPdzBbNKJ4ScK5EE73JnwJVGxuJXhHCY"); + +export const LOCAL_IRONFORGE_API_URL = buildIronforgeApiUrl(process.env.IRONFORGE_API_KEY!); + +export const BASIS_POINTS = 10000; diff --git a/solauto-sdk/src/constants/index.ts b/solauto-sdk/src/constants/index.ts index 4e040d41..016ae8d9 100644 --- a/solauto-sdk/src/constants/index.ts +++ b/solauto-sdk/src/constants/index.ts @@ -1,5 +1,6 @@ -export * from './generalAccounts'; -export * from './marginfiAccounts'; -export * from './pythConstants'; -export * from './solautoConstants'; -export * from './tokenConstants'; +export * from "./generalConstants"; +export * from "./marginfiAccounts"; +export * from "./pythConstants"; +export * from "./solautoConstants"; +export * from "./switchboardConstants"; +export * from "./tokenConstants"; diff --git a/solauto-sdk/src/constants/marginfiAccounts.ts b/solauto-sdk/src/constants/marginfiAccounts.ts index 7c6baa0f..62e68d65 100644 --- a/solauto-sdk/src/constants/marginfiAccounts.ts +++ b/solauto-sdk/src/constants/marginfiAccounts.ts @@ -1,147 +1,164 @@ +import { PublicKey } from "@solana/web3.js"; import { NATIVE_MINT } from "@solana/spl-token"; import * as tokens from "./tokenConstants"; import { MarginfiAssetAccounts } from "../types/accounts"; -import { PublicKey } from "@solana/web3.js"; -import { SWITCHBOARD_PRICE_FEED_IDS } from "./switchboardConstants"; +import { ProgramEnv } from "../types"; -export const DEFAULT_MARGINFI_GROUP = - "4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8"; +const MARGINFI_PROD_PROGRAM = new PublicKey( + "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" +); +const MARGINFI_STAGING_PROGRAM = new PublicKey( + "stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct" +); -export const DEFAULT_PUBKEY = PublicKey.default.toString(); +const PROD_DEFAULT_MARGINFI_GROUP = + "4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8"; -const USDC_PRICE_ORACLE = "Dpw1EAVrSB1ibxiDQyTAW6Zip3J4Btk2x4SgApQCeFbX"; +const STAGING_DEFAULT_MARGINFI_GROUP = + "FCPfpHA69EbS8f9KKSreTRkXbzFpunsKuYf5qNmnJjpo"; -export const MARGINFI_ACCOUNTS: { +export type MarginfiBankAccountsMap = { [group: string]: { [token: string]: MarginfiAssetAccounts }; -} = { - [DEFAULT_MARGINFI_GROUP.toString()]: { +}; + +const MARGINFI_STAGING_ACCOUNTS: MarginfiBankAccountsMap = { + [STAGING_DEFAULT_MARGINFI_GROUP]: { + [NATIVE_MINT.toString()]: { + bank: "3evdJSa25nsUiZzEUzd92UNa13TPRJrje1dRyiQP5Lhp", + liquidityVault: "FVXESa7wCd1tf3o9LGroBc3Ym8Gpcq1HdsLek6oo7Ykv", + vaultAuthority: "DuYk1WGq8UsjW5ThLtaehFLrnJeupTjtF7TaPzja9LBQ", + }, + [tokens.USDC]: { + bank: "Ek5JSFJFD8QgXM6rPDCzf31XhDp1q3xezaWYSkJWqbqc", + liquidityVault: "6n7xXMni5WJKUMb4Vm5Zis6UaUtGF5xEGmagZNVWWoKB", + vaultAuthority: "9mXNyA5yS4WSTpYUa5gi3yBrSJWWM7XNPNDWRzWGjdVe", + }, + [tokens.USDT]: { + bank: "4WFCsVXwfnQvZG52VvPwae7CtL13PUFVWdjkw5YCRBo6", + liquidityVault: "BFSyniKfXU9rHqUvHLKeZMivQsAKWTE7HZ1fW6vZcvJp", + vaultAuthority: "Fgxe3SUMzcNuLtT9Xkv8QhhWXCmu4VxynzUMwCF4HvJd", + }, + }, +}; + +const MARGINFI_PROD_ACCOUNTS: MarginfiBankAccountsMap = { + [PROD_DEFAULT_MARGINFI_GROUP.toString()]: { [NATIVE_MINT.toString()]: { bank: "CCKtUs6Cgwo4aaQUmBPmyoApH2gUDErxNZCAntD6LYGh", liquidityVault: "2eicbpitfJXDwqCuFAmPgDP7t2oUotnAzbGzRKLMgSLe", vaultAuthority: "DD3AeAssFvjqTvRTrRAtpfjkBF8FpVKnFuwnMLN9haXD", - priceOracle: "7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE", }, [tokens.B_SOL]: { bank: "6hS9i46WyTq1KXcoa2Chas2Txh9TJAVr6n1t3tnrE23K", liquidityVault: "2WMipeKDB2CENxbzdmnVrRbsxCA2LY6kCtBe6AAqDP9p", vaultAuthority: "8RcZHucpVHkHWRRdMhJZsxBK9mqKSYnMKGqtF84U8YEo", - priceOracle: "5cN76Xm2Dtx9MnrQqBDeZZRsWruTTcw37UruznAdSvvE", }, [tokens.M_SOL]: { bank: "22DcjMZrMwC5Bpa5AGBsmjc5V9VuQrXG6N9ZtdUNyYGE", liquidityVault: "B6HqNn83a2bLqo4i5ygjLHJgD11ePtQksUyx4MjD55DV", vaultAuthority: "6YxGd65JbXzgFGWjE44jsyVeCnZp7Bb1wfL9jDia1n8w", - priceOracle: "5CKzb9j4ChgLUt8Gfm5CNGLN6khXKiqMbnGAW4cgXgxK", }, [tokens.JITO_SOL]: { bank: "Bohoc1ikHLD7xKJuzTyiTyCwzaL5N7ggJQu75A8mKYM8", liquidityVault: "38VGtXd2pDPq9FMh1z6AVjcHCoHgvWyMhdNyamDTeeks", vaultAuthority: "7Ng54qf7BrCcZLqXmKA9WSR7SVRn4q6RX1YpLksBQ21A", - priceOracle: "AxaxyeDT8JnWERSaTKvFXvPKkEdxnamKSqpWbsSjYg1g", }, [tokens.LST]: { bank: "DMoqjmsuoru986HgfjqrKEvPv8YBufvBGADHUonkadC5", liquidityVault: "DMQUXpb6K5L8osgV4x3NeEPUoJCf2VBgnA8FQusDjSou", vaultAuthority: "6PWVauGLhBFHUJspsnBVZHr56ZnbvmhSD2gS7czBHGpE", - priceOracle: "7aT9A5knp62jVvnEW33xaWopaPHa3Y7ggULyYiUsDhu8", }, [tokens.INF]: { bank: "AwLRW3aPMMftXEjgWhTkYwM9CGBHdtKecvahCJZBwAqY", liquidityVault: "HQ1CGcqRshMhuonTGTnnmgw9ffcXxizGdZ6F6PKffWWi", vaultAuthority: "AEZb1XH5bfLwqk3hBKDuLfWyJKdLTgDPCkgn64BJKcvV", - priceOracle: "DCNeMcAZGU7pAsqu5q2xMKerq35BnUuTkagt1dkR1xB", }, [tokens.H_SOL]: { bank: "GJCi1uj3kYPZ64puA5sLUiCQfFapxT2xnREzrbDzFkYY", liquidityVault: "8M97jkdr4rJtPnQ4yQ9stD6qVwaUvjrBdDPDbHJnPJLf", vaultAuthority: "8x7mgTn5RvHR8Tn3CJqexSuQwrs6MLEy8csuXCDVvvpt", - priceOracle: SWITCHBOARD_PRICE_FEED_IDS[tokens.H_SOL.toString()], }, [tokens.JUP_SOL]: { bank: "8LaUZadNqtzuCG7iCvZd7d5cbquuYfv19KjAg6GPuuCb", liquidityVault: "B1zjqKPoYp9bTMhzFADaAvjyGb49FMitLpi6P3Pa3YR6", vaultAuthority: "93Qqsge2jHVsWLd8vas4cWghrsZJooMUr5JKN5DtcfMX", - priceOracle: SWITCHBOARD_PRICE_FEED_IDS[tokens.JUP_SOL.toString()], }, [tokens.JUP]: { bank: "Guu5uBc8k1WK1U2ihGosNaCy57LSgCkpWAabtzQqrQf8", liquidityVault: "4w49W4fNDn778wsBa6TNq9hvebZKU17ymsptrEZ8zrsm", vaultAuthority: "2MBwwAhL3c73Jy7HkWd9ofzh1bU39JBabrZCFQR2tUof", - priceOracle: "7dbob1psH1iZBS7qPsm3Kwbf5DzSXK8Jyg31CTgTnxH5", }, [tokens.JTO]: { bank: "EdB7YADw4XUt6wErT8kHGCUok4mnTpWGzPUU9rWDebzb", liquidityVault: "3bY1DEkXodGmPMG5f7ABA12228MBG5JdAAKf5cgkB6G1", vaultAuthority: "H2b4f2fGSKFortxwzrMZBnYVfr2yrKVUakg4Md9be3Wv", - priceOracle: "7ajR2zA4MGMMTqRAVjghTKqPPn4kbrj3pYkAVRVwTGzP", }, [tokens.JLP]: { bank: "Amtw3n7GZe5SWmyhMhaFhDTi39zbTkLeWErBsmZXwpDa", liquidityVault: "9xfyL8gxbV77VvhdgFmacHyLEG4h7d2eDWkSMfhXUPQ", vaultAuthority: "F4RSGd4BRXscCqAVG3rFLiPVpo7v6j1drVqnvSM3rBKH", - priceOracle: "B1wxT1VK3i6DqCeXNdTHT5hBAJkLa3rxuTqBc7E7XRXV", }, [tokens.WBTC]: { bank: "BKsfDJCMbYep6gr9pq8PsmJbb5XGLHbAJzUV8vmorz7a", liquidityVault: "CMNdnjfaDQZo3VMoX31wZQBnSGu5FMmb1CnBaU4tApZk", vaultAuthority: "7P2TQHYgVJkXv1VPaREsL5Pi1gnNjVif5aF3pJewZ9kj", - priceOracle: "4cSM2e6rvbGQUFiJbqytoVMi5GgghSMr8LwVrT9VPSPo", }, [tokens.WETH]: { bank: "BkUyfXjbBBALcfZvw76WAFRvYQ21xxMWWeoPtJrUqG3z", liquidityVault: "AxPJtiTEDksJWvCqNHCziK4uUcabqfmwW41dqtZrPFkp", vaultAuthority: "ELXogWuyXrFyUG1vevffVbEhVxdFrHf2GCJTtRGKBWdM", - priceOracle: "42amVS4KgzR9rA28tkVYqVXjq9Qa8dcZQMbH5EYFX6XC", }, [tokens.HNT]: { bank: "JBcir4DPRPYVUpks9hkS1jtHMXejfeBo4xJGv3AYYHg6", liquidityVault: "E8Q7u5e9L9Uykx16em75ERT9wfbBPtkNL8gsRjoP8GB9", vaultAuthority: "AjsyrYpgaH275DBSnvNWdGK33hVycSFuXN87FKnX6fVY", - priceOracle: "4DdmDswskDxXGpwHrXUfn2CNUm9rt21ac79GHNTN3J33", }, [tokens.PYTH]: { bank: "E4td8i8PT2BZkMygzW4MGHCv2KPPs57dvz5W2ZXf9Twu", liquidityVault: "DUrAkkaMAckzes7so9T5frXm9YFFgjAAm3MMwHwTfVJq", vaultAuthority: "9b5KdVnbbfEQ2qhLeFjWvcAx2VWe9XHx7ZgayZyL9a6C", - priceOracle: "8vjchtMuJNY4oFQdTi8yCe6mhCaNBFaUbktT482TpLPS", }, [tokens.USDC]: { bank: "2s37akK2eyBbp8DZgCm7RtsaEz8eJP3Nxd4urLHQv7yB", liquidityVault: "7jaiZR5Sk8hdYN9MxTpczTcwbWpb5WEoxSANuUwveuat", vaultAuthority: "3uxNepDbmkDNq6JhRja5Z8QwbTrfmkKP8AKZV5chYDGG", - priceOracle: USDC_PRICE_ORACLE, }, [tokens.USDT]: { bank: "HmpMfL8942u22htC4EMiWgLX931g3sacXFR6KjuLgKLV", liquidityVault: "77t6Fi9qj4s4z22K1toufHtstM8rEy7Y3ytxik7mcsTy", vaultAuthority: "9r6z6KgkEytHCdQWNxvDQH98PsfU98f1m5PCg47mY2XE", - priceOracle: "HT2PLQBcG5EiCcNSaMHAjSgd9F98ecpATbk4Sk5oYuM", }, [tokens.BONK]: { bank: "DeyH7QxWvnbbaVB4zFrf4hoq7Q8z1ZT14co42BGwGtfM", liquidityVault: "7FdQsXmCW3N5JQbknj3F9Yqq73er9VZJjGhEEMS8Ct2A", vaultAuthority: "26kcZkdjJc94PdhqiLiEaGiLCYgAVVUfpDaZyK4cqih3", - priceOracle: "DBE3N8uNjhKPRHfANdwGvCZghWXyLPdqdSbEW2XFwBiX", }, [tokens.WIF]: { bank: "9dpu8KL5ABYiD3WP2Cnajzg1XaotcJvZspv29Y1Y3tn1", liquidityVault: "4kT3EXc5dDVndUU9mV6EH3Jh3CSEvpcCZjuMkwqrtxUy", vaultAuthority: "9gNrvvZ9RuTyRWooiEEypwcXU6kyXW8yWuhXU8tWUH5L", - priceOracle: "6B23K3tkb51vLZA14jcEQVCA1pfHptzEHFA93V5dYwbT", }, + [tokens.PUMP]: { + bank: "61Qx9kgWo9RVtPHf8Rku6gbaUtcnzgkpAuifQBUcMRVK", + liquidityVault: "6FxnrCaJNHLyrFy6EPtqFjoUK5iRxDQbaVo1bA69nDhH", + vaultAuthority: "6VL2QW6sy8cjxL24xHL7eFbnUSi35DMJd8AzRwssSs9y" + }, + [tokens.CBBTC]: { + bank: "Ac4KV5K5isDqtABtg6h5DiwzZMe3Sp9bc3pBiCUvUpaQ", + liquidityVault: "FvrJsHu7X9jkT9rTuR6Kfs6wSjPvjV6zhH4gVSiJ8LFT", + vaultAuthority: "jfBsYE2sAkNj1pwE6U1gQ515JU5vn9huZMDYVvZagb4", + } }, ["DQ2jqDJw9uzTwttf6h6r217BQ7kws3jZbJXDkfbCJa1q"]: { [tokens.POPCAT]: { bank: "845oEvt1oduoBj5zQxTr21cWWaUVnRjGerJuW3yMo2nn", liquidityVault: "At6R64ip51zay4dT6k1WnVGETSMcaiY5vggD5DVTgxri", vaultAuthority: "dNraDCWb5usDSoW4kD1Mi2E9WsNu6EABcQZqnrDfjNb", - priceOracle: SWITCHBOARD_PRICE_FEED_IDS[tokens.POPCAT.toString()], }, [tokens.USDC]: { bank: "EXrnNVfLagt3j4hCHSD9WqK75o6dkZBtjpnrSrSC78MA", liquidityVault: "D9HSUYz3Rg2cTH65dUPaQS1MYxofNTeLecsAjiBgVPur", vaultAuthority: "5ivKgJnxQ9CewJcKYSPQUiQFdfJki6YS87FqohnMSsFM", - priceOracle: USDC_PRICE_ORACLE, }, }, ["EpzY5EYF1A5eFDRfjtsPXSYMPmEx1FXKaXPnouTMF4dm"]: { @@ -149,13 +166,11 @@ export const MARGINFI_ACCOUNTS: { bank: "3J5rKmCi7JXG6qmiobFJyAidVTnnNAMGj4jomfBxKGRM", liquidityVault: "863K9YPVT3xbUGFZevrQJLqMux3UdRkwNQ6usAp4hJyy", vaultAuthority: "Qsv2rnNRdv59AwRU3YmGPMCTdKT41CDAKyYAr4srCJR", - priceOracle: SWITCHBOARD_PRICE_FEED_IDS[tokens.RETARDIO.toString()], }, [tokens.USDC]: { bank: "6cgYhBFWCc5sNHxkvSRhd5H9AdAHR41zKwuF37HmLry5", liquidityVault: "7orVfNL5ZjqvdSaDgYLgBk4i5B3AnwFXNqqAvJbx6DFy", vaultAuthority: "G4Azxk4PYtNRmDZkJppYo3rNAinkZXzYpQPG5dVDh4Nj", - priceOracle: USDC_PRICE_ORACLE, }, }, ["G1rt3EpQ43K3bY457rhukQGRAo2QxydFAGRKqnjKzyr5"]: { @@ -163,16 +178,76 @@ export const MARGINFI_ACCOUNTS: { bank: "Dj3PndQ3j1vuga5ApiFWWAfQ4h3wBtgS2SeLZBT2LD4g", liquidityVault: "BRcRMDVPBQzXNXWtSS6bNotcGxhVsxfiAt1qf8nFVUpx", vaultAuthority: "36SgFh1qBRyj1PEhsn7Kg9Sfwbrn7rHP7kvTM5o5n6AL", - priceOracle: SWITCHBOARD_PRICE_FEED_IDS[tokens.BILLY.toString()], }, [tokens.USDC]: { bank: "A7vBgCowCYeja7GTc3pyqUBdC9Gkue2gWaMjGZW38meM", liquidityVault: "DBGhZ8TJTG2Pacdva27zY9etaro24o1tTA3LToSjYHbx", vaultAuthority: "Cg6BCqkGny7A2AXCV8rikhHXM82wqqfzmdsTobEeTQkH", - priceOracle: USDC_PRICE_ORACLE, + }, + }, + ["DESG67cExEcw7d6MmENLEzaocR8pLrhfiw9VrNtGWUKD"]: { + [tokens.HMTR]: { + bank: "Br3yzg2WSb81RaFWK9UsKtq8fD5viwooZG34mKqQWxdM", + liquidityVault: "J45Je52qv2rDBuCQWPwp3bjRhf3bGzRWhKZtGDuLooCX", + vaultAuthority: "CKDsAKjNruDSz4tmUairh8PDGD1Rqh9WMTLWERYnnZrH", + }, + [tokens.USDC]: { + bank: "9yNnhJ8c1vGbu3DMf6eeeUi6TDJ2ddGgaRA88rL2R3rP", + liquidityVault: "4U1UBjXrPrW7JuQ894JbLUBqcb5LFfK9rfkWFwT7EdQ9", + vaultAuthority: "CY74V1r48kuuHA6APD3AaU2oPV1mBqe9srikrQQSHNR6", }, }, }; -export const MARGINFI_ACCOUNTS_LOOKUP_TABLE = +const MARGINFI_PROD_ACCOUNTS_LOOKUP_TABLE = "GAjmWmBPcH5Gxbiykasydj6RsCEaCLyHEvK6kHdFigc6"; + +const MARGINFI_STAGING_ACCOUNTS_LOOKUP_TABLE = + "EoEVYjz3MnsX6fKyxrwJkRhzMCHKjj6dvnjTCHoZLMc7"; + +export interface MarginfiProgramAccounts { + program: PublicKey; + defaultGroup: PublicKey; + lookupTable: PublicKey; + bankAccounts: MarginfiBankAccountsMap; +} + +export function getMarginfiAccounts( + programEnv?: ProgramEnv, + marginfiGroup?: PublicKey +): MarginfiProgramAccounts { + if (programEnv === undefined) { + if (Boolean(marginfiGroup)) { + programEnv = Object.keys(MARGINFI_PROD_ACCOUNTS).includes( + marginfiGroup!.toString() + ) + ? "Prod" + : "Staging"; + } else { + programEnv = "Prod"; + } + } + + if (programEnv === "Prod") { + return { + program: MARGINFI_PROD_PROGRAM, + defaultGroup: new PublicKey(PROD_DEFAULT_MARGINFI_GROUP), + lookupTable: new PublicKey(MARGINFI_PROD_ACCOUNTS_LOOKUP_TABLE), + bankAccounts: MARGINFI_PROD_ACCOUNTS, + }; + } else { + return { + program: MARGINFI_STAGING_PROGRAM, + defaultGroup: new PublicKey(STAGING_DEFAULT_MARGINFI_GROUP), + lookupTable: new PublicKey(MARGINFI_STAGING_ACCOUNTS_LOOKUP_TABLE), + bankAccounts: MARGINFI_STAGING_ACCOUNTS, + }; + } +} + +export function isMarginfiProgram(programId: PublicKey) { + return ( + programId.equals(MARGINFI_PROD_PROGRAM) || + programId.equals(MARGINFI_STAGING_PROGRAM) + ); +} diff --git a/solauto-sdk/src/constants/pythConstants.ts b/solauto-sdk/src/constants/pythConstants.ts index a783911e..a015b261 100644 --- a/solauto-sdk/src/constants/pythConstants.ts +++ b/solauto-sdk/src/constants/pythConstants.ts @@ -1,5 +1,10 @@ import { NATIVE_MINT } from "@solana/spl-token"; import * as tokens from "./tokenConstants"; +import { PublicKey } from "@solana/web3.js"; + +export const PYTH_PUSH_PROGRAM = new PublicKey( + "pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT" +); // https://pyth.network/developers/price-feed-ids#solana-stable export const PYTH_PRICE_FEED_IDS = { @@ -37,4 +42,12 @@ export const PYTH_PRICE_FEED_IDS = { "0x72b021217ca3fe68922a19aaf990109cb9d84e9ad004b4d2025ad6f529314419", [tokens.WIF]: "0x4ca4beeca86f0d164160323817a4e42b10010a724c2217c6ee41b54cd4cc61fc", + [tokens.PUMP]: + "0x7a01fca212788bba7c5bf8c9efd576a8a722f070d2c17596ff7bb609b8d5c3b9", + [tokens.CBBTC]: + "0x2817d7bfe5c64b8ea956e9a26f573ef64e72e4d7891f2d6af9bcc93f7aff9a97", +}; + +export const PYTH_ORACLE_ACCOUNTS = { + [tokens.PUMP]: "HMm3GPbdnqGwbkTnUUqCFsH8AMHDdEC3Lg8gcPD3HJSH", }; diff --git a/solauto-sdk/src/constants/solautoConstants.ts b/solauto-sdk/src/constants/solautoConstants.ts index cc8d1e25..e6995179 100644 --- a/solauto-sdk/src/constants/solautoConstants.ts +++ b/solauto-sdk/src/constants/solautoConstants.ts @@ -9,8 +9,8 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { SOLAUTO_MANAGER } from "./generalAccounts"; -import { JUPITER_PROGRAM_ID } from "../jupiter-sdk"; +import { SOLAUTO_MANAGER } from "./generalConstants"; +import { JUPITER_PROGRAM_ID } from "../externalSdks/jupiter"; export const SOLAUTO_PROD_PROGRAM = new PublicKey( "AutoyKBRaHSBHy9RsmXCZMy6nNFAg5FYijrvZyQcNLV" @@ -19,18 +19,39 @@ export const SOLAUTO_TEST_PROGRAM = new PublicKey( "TesTjfQ6TbXv96Tv6fqr95XTZ1LYPxtkafmShN9PjBp" ); -(globalThis as any).LOCAL_TEST = false; +(globalThis as any).SHOW_LOGS = false; -export const BASIS_POINTS = 10000; - -export const MIN_POSITION_STATE_FRESHNESS_SECS = 5; export const MIN_REPAY_GAP_BPS = 50; export const MIN_BOOST_GAP_BPS = 50; export const MIN_USD_SUPPORTED_POSITION = 1000; -export const PRICES: { [key: string]: { price: number; time: number } } = {}; +export const OFFSET_FROM_MAX_LTV = 0.005; + +export const REFERRER_PERCENTAGE = 0.15; + +interface PriceCache { + realtimePrice: number; + confInterval: number; + emaPrice: number; + emaConfInterval: number; + time: number; +} +export const PRICES: { [key: string]: PriceCache } = {}; + +export const CHORES_TX_NAME = "account chores"; + +export const JITO_TIP_ACCOUNTS = [ + "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5", + "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe", + "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY", + "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49", + "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh", + "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt", + "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL", + "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", +]; -export const SOLAUTO_LUT = "9D4xwZwDf46n9ft5gQxZzq3rBbdRXsXojKQLZbBdskPY"; +export const SOLAUTO_LUT = "8b7KefQDroVLGao71J5H3hFwABeyMCgCrLpXWssNFhk9"; export const STANDARD_LUT_ACCOUNTS = [ PublicKey.default, SOLAUTO_PROD_PROGRAM, @@ -45,4 +66,4 @@ export const STANDARD_LUT_ACCOUNTS = [ JUPITER_PROGRAM_ID, ].map((x) => x.toString()); -export const JITO_BLOCK_ENGINE = "https://mainnet.block-engine.jito.wtf"; \ No newline at end of file +export const UPDATE_ORACLE_TX_NAME = "update oracle"; diff --git a/solauto-sdk/src/constants/switchboardConstants.ts b/solauto-sdk/src/constants/switchboardConstants.ts index cc8c438e..dafd62fd 100644 --- a/solauto-sdk/src/constants/switchboardConstants.ts +++ b/solauto-sdk/src/constants/switchboardConstants.ts @@ -1,10 +1,57 @@ +import { NATIVE_MINT } from "@solana/spl-token"; import * as tokens from "./tokenConstants"; +interface SwitchboardFeed { + feedId: string; + feedHash: string; +} + // https://beta.ondemand.switchboard.xyz/solana/mainnet -export const SWITCHBOARD_PRICE_FEED_IDS: { [key: string]: string } = { - [tokens.JUP_SOL]: "HX5WM3qzogAfRCjBUWwnniLByMfFrjm1b5yo4KoWGR27", - [tokens.H_SOL]: "1snBjCaHejZqQsAqkELAKNaqUrDNNCr7zmNX6qaQCzg", - [tokens.POPCAT]: "FTcFqwCjAgv2VMaowQ9XSBcesVzFzZkaju25nKio6bki", - [tokens.RETARDIO]: "EvPXnpMoyrj4B6P55LDP2VpSLkTBWom2SqL4486ToNhM", - [tokens.BILLY]: "uBe4er4VSMgYvBNwSbFctSShRXNPkCfaPh7zHMBFeBi", +export const SWITCHBOARD_PRICE_FEED_IDS: { [key: string]: SwitchboardFeed } = { + [NATIVE_MINT.toString()]: { + feedId: "4Hmd6PdjVA9auCoScE12iaBogfwS4ZXQ6VZoBeqanwWW", + feedHash: "0x1f42dfb21efb24828b99fbe70e6d139cd665aafcbb1706bccd8f2a9b12562db6", + }, + [tokens.JUP_SOL]: { + feedId: "HX5WM3qzogAfRCjBUWwnniLByMfFrjm1b5yo4KoWGR27", + feedHash: + "0xc02f22d47b20b43bafde474328ac027283dbd7bb443660f5ec414c93faec56dc", + }, + [tokens.JITO_SOL]: { + feedId: "5htZ4vPKPjAEg8EJv6JHcaCetMM4XehZo8znQvrp6Ur3", + feedHash: "0x44f5a7676baf4c6e5eb26fdde2695e7e3e0971071b76ddc622fa71528092ca25" + }, + [tokens.LST]: { + feedId: "BWK8Wnybb7rPteNMqJs9uWoqdfYApNym6WgE59BwLe1v", + feedHash: "0x74e140c452fe29cced80898a51961ed1410c1fe0efbdc970fe5aac986427419e" + }, + [tokens.JTO]: { + feedId: "A9RnpLxxtAS2TR3HtSMNJfsKpRPvkLbBkGZ6gKziSPLr", + feedHash: "0xa5bc3b659791facc5edfd8e74942c79d7e8d59f0f913dcdd9bcfb33a2fceb575" + }, + [tokens.H_SOL]: { + feedId: "1snBjCaHejZqQsAqkELAKNaqUrDNNCr7zmNX6qaQCzg", + feedHash: + "0x59206aa3da593cd2312bde1930cf3368f6119a650229e147060be4fc2fcd1367", + }, + [tokens.POPCAT]: { + feedId: "FTcFqwCjAgv2VMaowQ9XSBcesVzFzZkaju25nKio6bki", + feedHash: + "0xeb4f9a43024f8f33786b7291404510af8e94a66e1acb44953a3137878ee7033f", + }, + [tokens.RETARDIO]: { + feedId: "EvPXnpMoyrj4B6P55LDP2VpSLkTBWom2SqL4486ToNhM", + feedHash: + "0x982d968a0608046986aec84d95ae884c4dc2140f0b3e14ed7b8161ada573d18b", + }, + [tokens.BILLY]: { + feedId: "uBe4er4VSMgYvBNwSbFctSShRXNPkCfaPh7zHMBFeBi", + feedHash: + "0xbbd0d393111ff1ad7cc1a2f15ce24b61d4d6b3e99e440aa77572bd7f1da9afbe", + }, + [tokens.HMTR]: { + feedId: "F2hKL67W4ZDe9k7ZrJKnp2LhWrgqg2JQTkJf2dgBggRD", + feedHash: + "0x61999c4f8a03208b5b5b50663323b1ef8d0acbb3642ec79053b33b5768605fb5", + }, }; diff --git a/solauto-sdk/src/constants/tokenConstants.ts b/solauto-sdk/src/constants/tokenConstants.ts index bd3b1f95..4258da82 100644 --- a/solauto-sdk/src/constants/tokenConstants.ts +++ b/solauto-sdk/src/constants/tokenConstants.ts @@ -23,6 +23,9 @@ export const WIF = "EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm"; export const POPCAT = "7GCihgDB8fe6KNjn2MYtkzZcRjQy3t9GHdC8uHYmW2hr"; export const RETARDIO = "6ogzHhzdrQr9Pgv6hZ2MNze7UrzBMAFyBBWUYp1Fhitx"; export const BILLY = "3B5wuUrMEi5yATD7on46hKfej3pfmd7t1RKgrsN3pump"; +export const HMTR = "7JhmUcZrrfhyt5nTSu3AfsrUq2L9992a7AhwdSDxdoL2"; +export const PUMP = "pumpCmXqMfrsAkQ5r49WcJnRayYRqmXz6ae8H7H9Dfn"; +export const CBBTC = "cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij"; export const ALL_SUPPORTED_TOKENS = [ NATIVE_MINT.toString(), @@ -47,10 +50,14 @@ export const ALL_SUPPORTED_TOKENS = [ POPCAT, RETARDIO, BILLY, + HMTR, + PUMP, + CBBTC, ]; -interface TokenInfo { +export interface TokenInfo { ticker: string; + name?: string; decimals: number; isStableCoin?: boolean; isLST?: boolean; @@ -65,21 +72,25 @@ export const TOKEN_INFO: { [key: string]: TokenInfo } = { }, [NATIVE_MINT.toString()]: { ticker: "SOL", + name: "Solana", decimals: 9, isMajor: true, }, [B_SOL]: { ticker: "bSOL", + name: "Blaze SOL", decimals: 9, isLST: true, }, [M_SOL]: { ticker: "mSOL", + name: "Marinade SOL", decimals: 9, isLST: true, }, [JITO_SOL]: { ticker: "JitoSOL", + name: "JITO SOL", decimals: 9, isLST: true, }, @@ -90,43 +101,52 @@ export const TOKEN_INFO: { [key: string]: TokenInfo } = { }, [INF]: { ticker: "INF", + name: "Infinity", decimals: 9, isLST: true, }, [H_SOL]: { ticker: "hSOL", + name: "Helius SOL", decimals: 9, isLST: true, }, [JUP_SOL]: { ticker: "JupSOL", + name: "Jupiter SOL", decimals: 9, isLST: true, }, [JUP]: { ticker: "JUP", + name: "Jupiter", decimals: 6, }, [JTO]: { ticker: "JTO", + name: "Jito", decimals: 9, }, [JLP]: { ticker: "JLP", + name: "Jupiter Liquidity Provider", decimals: 6, }, [WBTC]: { ticker: "WBTC", + name: "Wrapped Bitcoin", decimals: 8, isMajor: true, }, [WETH]: { ticker: "WETH", + name: "Wrapped Ethereum", decimals: 8, isMajor: true, }, [HNT]: { ticker: "HNT", + name: "Helium Network Token", decimals: 8, }, [PYTH]: { @@ -135,11 +155,13 @@ export const TOKEN_INFO: { [key: string]: TokenInfo } = { }, [USDC]: { ticker: "USDC", + name: "USD Circle", decimals: 6, isStableCoin: true, }, [USDT]: { ticker: "USDT", + name: "USD Tether", decimals: 6, isStableCoin: true, }, @@ -150,6 +172,7 @@ export const TOKEN_INFO: { [key: string]: TokenInfo } = { }, [WIF]: { ticker: "WIF", + name: "Dog Wif Hat", decimals: 6, isMeme: true, }, @@ -168,4 +191,28 @@ export const TOKEN_INFO: { [key: string]: TokenInfo } = { decimals: 6, isMeme: true, }, + [HMTR]: { + ticker: "HMTR", + name: "Hampter", + decimals: 0, + isMeme: true, + }, + [PUMP]: { + ticker: "PUMP", + name: "Pump.fun", + decimals: 6, + }, + [CBBTC]: { + ticker: "CBBTC", + name: "Coinbase BTC", + decimals: 8, + isMajor: true, + }, +}; + +export const MAJORS_PRIO = { + [WBTC]: 0, + [CBBTC]: 0, + [WETH]: 1, + [NATIVE_MINT.toString()]: 2, }; diff --git a/solauto-sdk/src/externalSdks/index.ts b/solauto-sdk/src/externalSdks/index.ts new file mode 100644 index 00000000..993974aa --- /dev/null +++ b/solauto-sdk/src/externalSdks/index.ts @@ -0,0 +1,3 @@ +export * from "./jupiter"; +export * from "./marginfi"; +export * from "./pyth"; \ No newline at end of file diff --git a/solauto-sdk/src/jupiter-sdk/errors/index.ts b/solauto-sdk/src/externalSdks/jupiter/errors/index.ts similarity index 100% rename from solauto-sdk/src/jupiter-sdk/errors/index.ts rename to solauto-sdk/src/externalSdks/jupiter/errors/index.ts diff --git a/solauto-sdk/src/jupiter-sdk/errors/jupiter.ts b/solauto-sdk/src/externalSdks/jupiter/errors/jupiter.ts similarity index 100% rename from solauto-sdk/src/jupiter-sdk/errors/jupiter.ts rename to solauto-sdk/src/externalSdks/jupiter/errors/jupiter.ts diff --git a/solauto-sdk/src/jupiter-sdk/index.ts b/solauto-sdk/src/externalSdks/jupiter/index.ts similarity index 100% rename from solauto-sdk/src/jupiter-sdk/index.ts rename to solauto-sdk/src/externalSdks/jupiter/index.ts diff --git a/solauto-sdk/src/jupiter-sdk/programs/index.ts b/solauto-sdk/src/externalSdks/jupiter/programs/index.ts similarity index 100% rename from solauto-sdk/src/jupiter-sdk/programs/index.ts rename to solauto-sdk/src/externalSdks/jupiter/programs/index.ts diff --git a/solauto-sdk/src/jupiter-sdk/programs/jupiter.ts b/solauto-sdk/src/externalSdks/jupiter/programs/jupiter.ts similarity index 100% rename from solauto-sdk/src/jupiter-sdk/programs/jupiter.ts rename to solauto-sdk/src/externalSdks/jupiter/programs/jupiter.ts diff --git a/solauto-sdk/src/marginfi-sdk/accounts/bank.ts b/solauto-sdk/src/externalSdks/marginfi/accounts/bank.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/accounts/bank.ts rename to solauto-sdk/src/externalSdks/marginfi/accounts/bank.ts diff --git a/solauto-sdk/src/marginfi-sdk/accounts/index.ts b/solauto-sdk/src/externalSdks/marginfi/accounts/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/accounts/index.ts rename to solauto-sdk/src/externalSdks/marginfi/accounts/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/accounts/marginfiAccount.ts b/solauto-sdk/src/externalSdks/marginfi/accounts/marginfiAccount.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/accounts/marginfiAccount.ts rename to solauto-sdk/src/externalSdks/marginfi/accounts/marginfiAccount.ts diff --git a/solauto-sdk/src/marginfi-sdk/accounts/marginfiGroup.ts b/solauto-sdk/src/externalSdks/marginfi/accounts/marginfiGroup.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/accounts/marginfiGroup.ts rename to solauto-sdk/src/externalSdks/marginfi/accounts/marginfiGroup.ts diff --git a/solauto-sdk/src/marginfi-sdk/errors/index.ts b/solauto-sdk/src/externalSdks/marginfi/errors/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/errors/index.ts rename to solauto-sdk/src/externalSdks/marginfi/errors/index.ts diff --git a/solauto-sdk/src/externalSdks/marginfi/errors/marginfi.ts b/solauto-sdk/src/externalSdks/marginfi/errors/marginfi.ts new file mode 100644 index 00000000..a292e474 --- /dev/null +++ b/solauto-sdk/src/externalSdks/marginfi/errors/marginfi.ts @@ -0,0 +1,1111 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Program, ProgramError } from '@metaplex-foundation/umi'; + +type ProgramErrorConstructor = new ( + program: Program, + cause?: Error +) => ProgramError; +const codeToErrorMap: Map = new Map(); +const nameToErrorMap: Map = new Map(); + +/** InternalLogicError: Internal Marginfi logic error */ +export class InternalLogicErrorError extends ProgramError { + override readonly name: string = 'InternalLogicError'; + + readonly code: number = 0x1770; // 6000 + + constructor(program: Program, cause?: Error) { + super('Internal Marginfi logic error', program, cause); + } +} +codeToErrorMap.set(0x1770, InternalLogicErrorError); +nameToErrorMap.set('InternalLogicError', InternalLogicErrorError); + +/** BankNotFound: Invalid bank index */ +export class BankNotFoundError extends ProgramError { + override readonly name: string = 'BankNotFound'; + + readonly code: number = 0x1771; // 6001 + + constructor(program: Program, cause?: Error) { + super('Invalid bank index', program, cause); + } +} +codeToErrorMap.set(0x1771, BankNotFoundError); +nameToErrorMap.set('BankNotFound', BankNotFoundError); + +/** LendingAccountBalanceNotFound: Lending account balance not found */ +export class LendingAccountBalanceNotFoundError extends ProgramError { + override readonly name: string = 'LendingAccountBalanceNotFound'; + + readonly code: number = 0x1772; // 6002 + + constructor(program: Program, cause?: Error) { + super('Lending account balance not found', program, cause); + } +} +codeToErrorMap.set(0x1772, LendingAccountBalanceNotFoundError); +nameToErrorMap.set( + 'LendingAccountBalanceNotFound', + LendingAccountBalanceNotFoundError +); + +/** BankAssetCapacityExceeded: Bank deposit capacity exceeded */ +export class BankAssetCapacityExceededError extends ProgramError { + override readonly name: string = 'BankAssetCapacityExceeded'; + + readonly code: number = 0x1773; // 6003 + + constructor(program: Program, cause?: Error) { + super('Bank deposit capacity exceeded', program, cause); + } +} +codeToErrorMap.set(0x1773, BankAssetCapacityExceededError); +nameToErrorMap.set('BankAssetCapacityExceeded', BankAssetCapacityExceededError); + +/** InvalidTransfer: Invalid transfer */ +export class InvalidTransferError extends ProgramError { + override readonly name: string = 'InvalidTransfer'; + + readonly code: number = 0x1774; // 6004 + + constructor(program: Program, cause?: Error) { + super('Invalid transfer', program, cause); + } +} +codeToErrorMap.set(0x1774, InvalidTransferError); +nameToErrorMap.set('InvalidTransfer', InvalidTransferError); + +/** MissingPythOrBankAccount: Missing Oracle, Bank, LST mint, or Sol Pool */ +export class MissingPythOrBankAccountError extends ProgramError { + override readonly name: string = 'MissingPythOrBankAccount'; + + readonly code: number = 0x1775; // 6005 + + constructor(program: Program, cause?: Error) { + super('Missing Oracle, Bank, LST mint, or Sol Pool', program, cause); + } +} +codeToErrorMap.set(0x1775, MissingPythOrBankAccountError); +nameToErrorMap.set('MissingPythOrBankAccount', MissingPythOrBankAccountError); + +/** MissingPythAccount: Missing Pyth account */ +export class MissingPythAccountError extends ProgramError { + override readonly name: string = 'MissingPythAccount'; + + readonly code: number = 0x1776; // 6006 + + constructor(program: Program, cause?: Error) { + super('Missing Pyth account', program, cause); + } +} +codeToErrorMap.set(0x1776, MissingPythAccountError); +nameToErrorMap.set('MissingPythAccount', MissingPythAccountError); + +/** MissingBankAccount: Missing Bank account */ +export class MissingBankAccountError extends ProgramError { + override readonly name: string = 'MissingBankAccount'; + + readonly code: number = 0x1777; // 6007 + + constructor(program: Program, cause?: Error) { + super('Missing Bank account', program, cause); + } +} +codeToErrorMap.set(0x1777, MissingBankAccountError); +nameToErrorMap.set('MissingBankAccount', MissingBankAccountError); + +/** InvalidBankAccount: Invalid Bank account */ +export class InvalidBankAccountError extends ProgramError { + override readonly name: string = 'InvalidBankAccount'; + + readonly code: number = 0x1778; // 6008 + + constructor(program: Program, cause?: Error) { + super('Invalid Bank account', program, cause); + } +} +codeToErrorMap.set(0x1778, InvalidBankAccountError); +nameToErrorMap.set('InvalidBankAccount', InvalidBankAccountError); + +/** RiskEngineInitRejected: RiskEngine rejected due to either bad health or stale oracles */ +export class RiskEngineInitRejectedError extends ProgramError { + override readonly name: string = 'RiskEngineInitRejected'; + + readonly code: number = 0x1779; // 6009 + + constructor(program: Program, cause?: Error) { + super( + 'RiskEngine rejected due to either bad health or stale oracles', + program, + cause + ); + } +} +codeToErrorMap.set(0x1779, RiskEngineInitRejectedError); +nameToErrorMap.set('RiskEngineInitRejected', RiskEngineInitRejectedError); + +/** LendingAccountBalanceSlotsFull: Lending account balance slots are full */ +export class LendingAccountBalanceSlotsFullError extends ProgramError { + override readonly name: string = 'LendingAccountBalanceSlotsFull'; + + readonly code: number = 0x177a; // 6010 + + constructor(program: Program, cause?: Error) { + super('Lending account balance slots are full', program, cause); + } +} +codeToErrorMap.set(0x177a, LendingAccountBalanceSlotsFullError); +nameToErrorMap.set( + 'LendingAccountBalanceSlotsFull', + LendingAccountBalanceSlotsFullError +); + +/** BankAlreadyExists: Bank already exists */ +export class BankAlreadyExistsError extends ProgramError { + override readonly name: string = 'BankAlreadyExists'; + + readonly code: number = 0x177b; // 6011 + + constructor(program: Program, cause?: Error) { + super('Bank already exists', program, cause); + } +} +codeToErrorMap.set(0x177b, BankAlreadyExistsError); +nameToErrorMap.set('BankAlreadyExists', BankAlreadyExistsError); + +/** ZeroLiquidationAmount: Amount to liquidate must be positive */ +export class ZeroLiquidationAmountError extends ProgramError { + override readonly name: string = 'ZeroLiquidationAmount'; + + readonly code: number = 0x177c; // 6012 + + constructor(program: Program, cause?: Error) { + super('Amount to liquidate must be positive', program, cause); + } +} +codeToErrorMap.set(0x177c, ZeroLiquidationAmountError); +nameToErrorMap.set('ZeroLiquidationAmount', ZeroLiquidationAmountError); + +/** AccountNotBankrupt: Account is not bankrupt */ +export class AccountNotBankruptError extends ProgramError { + override readonly name: string = 'AccountNotBankrupt'; + + readonly code: number = 0x177d; // 6013 + + constructor(program: Program, cause?: Error) { + super('Account is not bankrupt', program, cause); + } +} +codeToErrorMap.set(0x177d, AccountNotBankruptError); +nameToErrorMap.set('AccountNotBankrupt', AccountNotBankruptError); + +/** BalanceNotBadDebt: Account balance is not bad debt */ +export class BalanceNotBadDebtError extends ProgramError { + override readonly name: string = 'BalanceNotBadDebt'; + + readonly code: number = 0x177e; // 6014 + + constructor(program: Program, cause?: Error) { + super('Account balance is not bad debt', program, cause); + } +} +codeToErrorMap.set(0x177e, BalanceNotBadDebtError); +nameToErrorMap.set('BalanceNotBadDebt', BalanceNotBadDebtError); + +/** InvalidConfig: Invalid group config */ +export class InvalidConfigError extends ProgramError { + override readonly name: string = 'InvalidConfig'; + + readonly code: number = 0x177f; // 6015 + + constructor(program: Program, cause?: Error) { + super('Invalid group config', program, cause); + } +} +codeToErrorMap.set(0x177f, InvalidConfigError); +nameToErrorMap.set('InvalidConfig', InvalidConfigError); + +/** BankPaused: Bank paused */ +export class BankPausedError extends ProgramError { + override readonly name: string = 'BankPaused'; + + readonly code: number = 0x1780; // 6016 + + constructor(program: Program, cause?: Error) { + super('Bank paused', program, cause); + } +} +codeToErrorMap.set(0x1780, BankPausedError); +nameToErrorMap.set('BankPaused', BankPausedError); + +/** BankReduceOnly: Bank is ReduceOnly mode */ +export class BankReduceOnlyError extends ProgramError { + override readonly name: string = 'BankReduceOnly'; + + readonly code: number = 0x1781; // 6017 + + constructor(program: Program, cause?: Error) { + super('Bank is ReduceOnly mode', program, cause); + } +} +codeToErrorMap.set(0x1781, BankReduceOnlyError); +nameToErrorMap.set('BankReduceOnly', BankReduceOnlyError); + +/** BankAccountNotFound: Bank is missing */ +export class BankAccountNotFoundError extends ProgramError { + override readonly name: string = 'BankAccountNotFound'; + + readonly code: number = 0x1782; // 6018 + + constructor(program: Program, cause?: Error) { + super('Bank is missing', program, cause); + } +} +codeToErrorMap.set(0x1782, BankAccountNotFoundError); +nameToErrorMap.set('BankAccountNotFound', BankAccountNotFoundError); + +/** OperationDepositOnly: Operation is deposit-only */ +export class OperationDepositOnlyError extends ProgramError { + override readonly name: string = 'OperationDepositOnly'; + + readonly code: number = 0x1783; // 6019 + + constructor(program: Program, cause?: Error) { + super('Operation is deposit-only', program, cause); + } +} +codeToErrorMap.set(0x1783, OperationDepositOnlyError); +nameToErrorMap.set('OperationDepositOnly', OperationDepositOnlyError); + +/** OperationWithdrawOnly: Operation is withdraw-only */ +export class OperationWithdrawOnlyError extends ProgramError { + override readonly name: string = 'OperationWithdrawOnly'; + + readonly code: number = 0x1784; // 6020 + + constructor(program: Program, cause?: Error) { + super('Operation is withdraw-only', program, cause); + } +} +codeToErrorMap.set(0x1784, OperationWithdrawOnlyError); +nameToErrorMap.set('OperationWithdrawOnly', OperationWithdrawOnlyError); + +/** OperationBorrowOnly: Operation is borrow-only */ +export class OperationBorrowOnlyError extends ProgramError { + override readonly name: string = 'OperationBorrowOnly'; + + readonly code: number = 0x1785; // 6021 + + constructor(program: Program, cause?: Error) { + super('Operation is borrow-only', program, cause); + } +} +codeToErrorMap.set(0x1785, OperationBorrowOnlyError); +nameToErrorMap.set('OperationBorrowOnly', OperationBorrowOnlyError); + +/** OperationRepayOnly: Operation is repay-only */ +export class OperationRepayOnlyError extends ProgramError { + override readonly name: string = 'OperationRepayOnly'; + + readonly code: number = 0x1786; // 6022 + + constructor(program: Program, cause?: Error) { + super('Operation is repay-only', program, cause); + } +} +codeToErrorMap.set(0x1786, OperationRepayOnlyError); +nameToErrorMap.set('OperationRepayOnly', OperationRepayOnlyError); + +/** NoAssetFound: No asset found */ +export class NoAssetFoundError extends ProgramError { + override readonly name: string = 'NoAssetFound'; + + readonly code: number = 0x1787; // 6023 + + constructor(program: Program, cause?: Error) { + super('No asset found', program, cause); + } +} +codeToErrorMap.set(0x1787, NoAssetFoundError); +nameToErrorMap.set('NoAssetFound', NoAssetFoundError); + +/** NoLiabilityFound: No liability found */ +export class NoLiabilityFoundError extends ProgramError { + override readonly name: string = 'NoLiabilityFound'; + + readonly code: number = 0x1788; // 6024 + + constructor(program: Program, cause?: Error) { + super('No liability found', program, cause); + } +} +codeToErrorMap.set(0x1788, NoLiabilityFoundError); +nameToErrorMap.set('NoLiabilityFound', NoLiabilityFoundError); + +/** InvalidOracleSetup: Invalid oracle setup */ +export class InvalidOracleSetupError extends ProgramError { + override readonly name: string = 'InvalidOracleSetup'; + + readonly code: number = 0x1789; // 6025 + + constructor(program: Program, cause?: Error) { + super('Invalid oracle setup', program, cause); + } +} +codeToErrorMap.set(0x1789, InvalidOracleSetupError); +nameToErrorMap.set('InvalidOracleSetup', InvalidOracleSetupError); + +/** IllegalUtilizationRatio: Invalid bank utilization ratio */ +export class IllegalUtilizationRatioError extends ProgramError { + override readonly name: string = 'IllegalUtilizationRatio'; + + readonly code: number = 0x178a; // 6026 + + constructor(program: Program, cause?: Error) { + super('Invalid bank utilization ratio', program, cause); + } +} +codeToErrorMap.set(0x178a, IllegalUtilizationRatioError); +nameToErrorMap.set('IllegalUtilizationRatio', IllegalUtilizationRatioError); + +/** BankLiabilityCapacityExceeded: Bank borrow cap exceeded */ +export class BankLiabilityCapacityExceededError extends ProgramError { + override readonly name: string = 'BankLiabilityCapacityExceeded'; + + readonly code: number = 0x178b; // 6027 + + constructor(program: Program, cause?: Error) { + super('Bank borrow cap exceeded', program, cause); + } +} +codeToErrorMap.set(0x178b, BankLiabilityCapacityExceededError); +nameToErrorMap.set( + 'BankLiabilityCapacityExceeded', + BankLiabilityCapacityExceededError +); + +/** InvalidPrice: Invalid Price */ +export class InvalidPriceError extends ProgramError { + override readonly name: string = 'InvalidPrice'; + + readonly code: number = 0x178c; // 6028 + + constructor(program: Program, cause?: Error) { + super('Invalid Price', program, cause); + } +} +codeToErrorMap.set(0x178c, InvalidPriceError); +nameToErrorMap.set('InvalidPrice', InvalidPriceError); + +/** IsolatedAccountIllegalState: Account can have only one liability when account is under isolated risk */ +export class IsolatedAccountIllegalStateError extends ProgramError { + override readonly name: string = 'IsolatedAccountIllegalState'; + + readonly code: number = 0x178d; // 6029 + + constructor(program: Program, cause?: Error) { + super( + 'Account can have only one liability when account is under isolated risk', + program, + cause + ); + } +} +codeToErrorMap.set(0x178d, IsolatedAccountIllegalStateError); +nameToErrorMap.set( + 'IsolatedAccountIllegalState', + IsolatedAccountIllegalStateError +); + +/** EmissionsAlreadySetup: Emissions already setup */ +export class EmissionsAlreadySetupError extends ProgramError { + override readonly name: string = 'EmissionsAlreadySetup'; + + readonly code: number = 0x178e; // 6030 + + constructor(program: Program, cause?: Error) { + super('Emissions already setup', program, cause); + } +} +codeToErrorMap.set(0x178e, EmissionsAlreadySetupError); +nameToErrorMap.set('EmissionsAlreadySetup', EmissionsAlreadySetupError); + +/** OracleNotSetup: Oracle is not set */ +export class OracleNotSetupError extends ProgramError { + override readonly name: string = 'OracleNotSetup'; + + readonly code: number = 0x178f; // 6031 + + constructor(program: Program, cause?: Error) { + super('Oracle is not set', program, cause); + } +} +codeToErrorMap.set(0x178f, OracleNotSetupError); +nameToErrorMap.set('OracleNotSetup', OracleNotSetupError); + +/** InvalidSwitchboardDecimalConversion: Invalid switchboard decimal conversion */ +export class InvalidSwitchboardDecimalConversionError extends ProgramError { + override readonly name: string = 'InvalidSwitchboardDecimalConversion'; + + readonly code: number = 0x1790; // 6032 + + constructor(program: Program, cause?: Error) { + super('Invalid switchboard decimal conversion', program, cause); + } +} +codeToErrorMap.set(0x1790, InvalidSwitchboardDecimalConversionError); +nameToErrorMap.set( + 'InvalidSwitchboardDecimalConversion', + InvalidSwitchboardDecimalConversionError +); + +/** CannotCloseOutstandingEmissions: Cannot close balance because of outstanding emissions */ +export class CannotCloseOutstandingEmissionsError extends ProgramError { + override readonly name: string = 'CannotCloseOutstandingEmissions'; + + readonly code: number = 0x1791; // 6033 + + constructor(program: Program, cause?: Error) { + super( + 'Cannot close balance because of outstanding emissions', + program, + cause + ); + } +} +codeToErrorMap.set(0x1791, CannotCloseOutstandingEmissionsError); +nameToErrorMap.set( + 'CannotCloseOutstandingEmissions', + CannotCloseOutstandingEmissionsError +); + +/** EmissionsUpdateError: Update emissions error */ +export class EmissionsUpdateErrorError extends ProgramError { + override readonly name: string = 'EmissionsUpdateError'; + + readonly code: number = 0x1792; // 6034 + + constructor(program: Program, cause?: Error) { + super('Update emissions error', program, cause); + } +} +codeToErrorMap.set(0x1792, EmissionsUpdateErrorError); +nameToErrorMap.set('EmissionsUpdateError', EmissionsUpdateErrorError); + +/** AccountDisabled: Account disabled */ +export class AccountDisabledError extends ProgramError { + override readonly name: string = 'AccountDisabled'; + + readonly code: number = 0x1793; // 6035 + + constructor(program: Program, cause?: Error) { + super('Account disabled', program, cause); + } +} +codeToErrorMap.set(0x1793, AccountDisabledError); +nameToErrorMap.set('AccountDisabled', AccountDisabledError); + +/** AccountTempActiveBalanceLimitExceeded: Account can't temporarily open 3 balances, please close a balance first */ +export class AccountTempActiveBalanceLimitExceededError extends ProgramError { + override readonly name: string = 'AccountTempActiveBalanceLimitExceeded'; + + readonly code: number = 0x1794; // 6036 + + constructor(program: Program, cause?: Error) { + super( + "Account can't temporarily open 3 balances, please close a balance first", + program, + cause + ); + } +} +codeToErrorMap.set(0x1794, AccountTempActiveBalanceLimitExceededError); +nameToErrorMap.set( + 'AccountTempActiveBalanceLimitExceeded', + AccountTempActiveBalanceLimitExceededError +); + +/** AccountInFlashloan: Illegal action during flashloan */ +export class AccountInFlashloanError extends ProgramError { + override readonly name: string = 'AccountInFlashloan'; + + readonly code: number = 0x1795; // 6037 + + constructor(program: Program, cause?: Error) { + super('Illegal action during flashloan', program, cause); + } +} +codeToErrorMap.set(0x1795, AccountInFlashloanError); +nameToErrorMap.set('AccountInFlashloan', AccountInFlashloanError); + +/** IllegalFlashloan: Illegal flashloan */ +export class IllegalFlashloanError extends ProgramError { + override readonly name: string = 'IllegalFlashloan'; + + readonly code: number = 0x1796; // 6038 + + constructor(program: Program, cause?: Error) { + super('Illegal flashloan', program, cause); + } +} +codeToErrorMap.set(0x1796, IllegalFlashloanError); +nameToErrorMap.set('IllegalFlashloan', IllegalFlashloanError); + +/** IllegalFlag: Illegal flag */ +export class IllegalFlagError extends ProgramError { + override readonly name: string = 'IllegalFlag'; + + readonly code: number = 0x1797; // 6039 + + constructor(program: Program, cause?: Error) { + super('Illegal flag', program, cause); + } +} +codeToErrorMap.set(0x1797, IllegalFlagError); +nameToErrorMap.set('IllegalFlag', IllegalFlagError); + +/** IllegalBalanceState: Illegal balance state */ +export class IllegalBalanceStateError extends ProgramError { + override readonly name: string = 'IllegalBalanceState'; + + readonly code: number = 0x1798; // 6040 + + constructor(program: Program, cause?: Error) { + super('Illegal balance state', program, cause); + } +} +codeToErrorMap.set(0x1798, IllegalBalanceStateError); +nameToErrorMap.set('IllegalBalanceState', IllegalBalanceStateError); + +/** IllegalAccountAuthorityTransfer: Illegal account authority transfer */ +export class IllegalAccountAuthorityTransferError extends ProgramError { + override readonly name: string = 'IllegalAccountAuthorityTransfer'; + + readonly code: number = 0x1799; // 6041 + + constructor(program: Program, cause?: Error) { + super('Illegal account authority transfer', program, cause); + } +} +codeToErrorMap.set(0x1799, IllegalAccountAuthorityTransferError); +nameToErrorMap.set( + 'IllegalAccountAuthorityTransfer', + IllegalAccountAuthorityTransferError +); + +/** Unauthorized: Unauthorized */ +export class UnauthorizedError extends ProgramError { + override readonly name: string = 'Unauthorized'; + + readonly code: number = 0x179a; // 6042 + + constructor(program: Program, cause?: Error) { + super('Unauthorized', program, cause); + } +} +codeToErrorMap.set(0x179a, UnauthorizedError); +nameToErrorMap.set('Unauthorized', UnauthorizedError); + +/** IllegalAction: Invalid account authority */ +export class IllegalActionError extends ProgramError { + override readonly name: string = 'IllegalAction'; + + readonly code: number = 0x179b; // 6043 + + constructor(program: Program, cause?: Error) { + super('Invalid account authority', program, cause); + } +} +codeToErrorMap.set(0x179b, IllegalActionError); +nameToErrorMap.set('IllegalAction', IllegalActionError); + +/** T22MintRequired: Token22 Banks require mint account as first remaining account */ +export class T22MintRequiredError extends ProgramError { + override readonly name: string = 'T22MintRequired'; + + readonly code: number = 0x179c; // 6044 + + constructor(program: Program, cause?: Error) { + super( + 'Token22 Banks require mint account as first remaining account', + program, + cause + ); + } +} +codeToErrorMap.set(0x179c, T22MintRequiredError); +nameToErrorMap.set('T22MintRequired', T22MintRequiredError); + +/** InvalidFeeAta: Invalid ATA for global fee account */ +export class InvalidFeeAtaError extends ProgramError { + override readonly name: string = 'InvalidFeeAta'; + + readonly code: number = 0x179d; // 6045 + + constructor(program: Program, cause?: Error) { + super('Invalid ATA for global fee account', program, cause); + } +} +codeToErrorMap.set(0x179d, InvalidFeeAtaError); +nameToErrorMap.set('InvalidFeeAta', InvalidFeeAtaError); + +/** AddedStakedPoolManually: Use add pool permissionless instead */ +export class AddedStakedPoolManuallyError extends ProgramError { + override readonly name: string = 'AddedStakedPoolManually'; + + readonly code: number = 0x179e; // 6046 + + constructor(program: Program, cause?: Error) { + super('Use add pool permissionless instead', program, cause); + } +} +codeToErrorMap.set(0x179e, AddedStakedPoolManuallyError); +nameToErrorMap.set('AddedStakedPoolManually', AddedStakedPoolManuallyError); + +/** AssetTagMismatch: Staked SOL accounts can only deposit staked assets and borrow SOL */ +export class AssetTagMismatchError extends ProgramError { + override readonly name: string = 'AssetTagMismatch'; + + readonly code: number = 0x179f; // 6047 + + constructor(program: Program, cause?: Error) { + super( + 'Staked SOL accounts can only deposit staked assets and borrow SOL', + program, + cause + ); + } +} +codeToErrorMap.set(0x179f, AssetTagMismatchError); +nameToErrorMap.set('AssetTagMismatch', AssetTagMismatchError); + +/** StakePoolValidationFailed: Stake pool validation failed: check the stake pool, mint, or sol pool */ +export class StakePoolValidationFailedError extends ProgramError { + override readonly name: string = 'StakePoolValidationFailed'; + + readonly code: number = 0x17a0; // 6048 + + constructor(program: Program, cause?: Error) { + super( + 'Stake pool validation failed: check the stake pool, mint, or sol pool', + program, + cause + ); + } +} +codeToErrorMap.set(0x17a0, StakePoolValidationFailedError); +nameToErrorMap.set('StakePoolValidationFailed', StakePoolValidationFailedError); + +/** SwitchboardStalePrice: Switchboard oracle: stale price */ +export class SwitchboardStalePriceError extends ProgramError { + override readonly name: string = 'SwitchboardStalePrice'; + + readonly code: number = 0x17a1; // 6049 + + constructor(program: Program, cause?: Error) { + super('Switchboard oracle: stale price', program, cause); + } +} +codeToErrorMap.set(0x17a1, SwitchboardStalePriceError); +nameToErrorMap.set('SwitchboardStalePrice', SwitchboardStalePriceError); + +/** PythPushStalePrice: Pyth Push oracle: stale price */ +export class PythPushStalePriceError extends ProgramError { + override readonly name: string = 'PythPushStalePrice'; + + readonly code: number = 0x17a2; // 6050 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: stale price', program, cause); + } +} +codeToErrorMap.set(0x17a2, PythPushStalePriceError); +nameToErrorMap.set('PythPushStalePrice', PythPushStalePriceError); + +/** WrongNumberOfOracleAccounts: Oracle error: wrong number of accounts */ +export class WrongNumberOfOracleAccountsError extends ProgramError { + override readonly name: string = 'WrongNumberOfOracleAccounts'; + + readonly code: number = 0x17a3; // 6051 + + constructor(program: Program, cause?: Error) { + super('Oracle error: wrong number of accounts', program, cause); + } +} +codeToErrorMap.set(0x17a3, WrongNumberOfOracleAccountsError); +nameToErrorMap.set( + 'WrongNumberOfOracleAccounts', + WrongNumberOfOracleAccountsError +); + +/** WrongOracleAccountKeys: Oracle error: wrong account keys */ +export class WrongOracleAccountKeysError extends ProgramError { + override readonly name: string = 'WrongOracleAccountKeys'; + + readonly code: number = 0x17a4; // 6052 + + constructor(program: Program, cause?: Error) { + super('Oracle error: wrong account keys', program, cause); + } +} +codeToErrorMap.set(0x17a4, WrongOracleAccountKeysError); +nameToErrorMap.set('WrongOracleAccountKeys', WrongOracleAccountKeysError); + +/** PythPushWrongAccountOwner: Pyth Push oracle: wrong account owner */ +export class PythPushWrongAccountOwnerError extends ProgramError { + override readonly name: string = 'PythPushWrongAccountOwner'; + + readonly code: number = 0x17a5; // 6053 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: wrong account owner', program, cause); + } +} +codeToErrorMap.set(0x17a5, PythPushWrongAccountOwnerError); +nameToErrorMap.set('PythPushWrongAccountOwner', PythPushWrongAccountOwnerError); + +/** StakedPythPushWrongAccountOwner: Staked Pyth Push oracle: wrong account owner */ +export class StakedPythPushWrongAccountOwnerError extends ProgramError { + override readonly name: string = 'StakedPythPushWrongAccountOwner'; + + readonly code: number = 0x17a6; // 6054 + + constructor(program: Program, cause?: Error) { + super('Staked Pyth Push oracle: wrong account owner', program, cause); + } +} +codeToErrorMap.set(0x17a6, StakedPythPushWrongAccountOwnerError); +nameToErrorMap.set( + 'StakedPythPushWrongAccountOwner', + StakedPythPushWrongAccountOwnerError +); + +/** PythPushMismatchedFeedId: Pyth Push oracle: mismatched feed id */ +export class PythPushMismatchedFeedIdError extends ProgramError { + override readonly name: string = 'PythPushMismatchedFeedId'; + + readonly code: number = 0x17a7; // 6055 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: mismatched feed id', program, cause); + } +} +codeToErrorMap.set(0x17a7, PythPushMismatchedFeedIdError); +nameToErrorMap.set('PythPushMismatchedFeedId', PythPushMismatchedFeedIdError); + +/** PythPushInsufficientVerificationLevel: Pyth Push oracle: insufficient verification level */ +export class PythPushInsufficientVerificationLevelError extends ProgramError { + override readonly name: string = 'PythPushInsufficientVerificationLevel'; + + readonly code: number = 0x17a8; // 6056 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: insufficient verification level', program, cause); + } +} +codeToErrorMap.set(0x17a8, PythPushInsufficientVerificationLevelError); +nameToErrorMap.set( + 'PythPushInsufficientVerificationLevel', + PythPushInsufficientVerificationLevelError +); + +/** PythPushFeedIdMustBe32Bytes: Pyth Push oracle: feed id must be 32 Bytes */ +export class PythPushFeedIdMustBe32BytesError extends ProgramError { + override readonly name: string = 'PythPushFeedIdMustBe32Bytes'; + + readonly code: number = 0x17a9; // 6057 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: feed id must be 32 Bytes', program, cause); + } +} +codeToErrorMap.set(0x17a9, PythPushFeedIdMustBe32BytesError); +nameToErrorMap.set( + 'PythPushFeedIdMustBe32Bytes', + PythPushFeedIdMustBe32BytesError +); + +/** PythPushFeedIdNonHexCharacter: Pyth Push oracle: feed id contains non-hex characters */ +export class PythPushFeedIdNonHexCharacterError extends ProgramError { + override readonly name: string = 'PythPushFeedIdNonHexCharacter'; + + readonly code: number = 0x17aa; // 6058 + + constructor(program: Program, cause?: Error) { + super( + 'Pyth Push oracle: feed id contains non-hex characters', + program, + cause + ); + } +} +codeToErrorMap.set(0x17aa, PythPushFeedIdNonHexCharacterError); +nameToErrorMap.set( + 'PythPushFeedIdNonHexCharacter', + PythPushFeedIdNonHexCharacterError +); + +/** SwitchboardWrongAccountOwner: Switchboard oracle: wrong account owner */ +export class SwitchboardWrongAccountOwnerError extends ProgramError { + override readonly name: string = 'SwitchboardWrongAccountOwner'; + + readonly code: number = 0x17ab; // 6059 + + constructor(program: Program, cause?: Error) { + super('Switchboard oracle: wrong account owner', program, cause); + } +} +codeToErrorMap.set(0x17ab, SwitchboardWrongAccountOwnerError); +nameToErrorMap.set( + 'SwitchboardWrongAccountOwner', + SwitchboardWrongAccountOwnerError +); + +/** PythPushInvalidAccount: Pyth Push oracle: invalid account */ +export class PythPushInvalidAccountError extends ProgramError { + override readonly name: string = 'PythPushInvalidAccount'; + + readonly code: number = 0x17ac; // 6060 + + constructor(program: Program, cause?: Error) { + super('Pyth Push oracle: invalid account', program, cause); + } +} +codeToErrorMap.set(0x17ac, PythPushInvalidAccountError); +nameToErrorMap.set('PythPushInvalidAccount', PythPushInvalidAccountError); + +/** SwitchboardInvalidAccount: Switchboard oracle: invalid account */ +export class SwitchboardInvalidAccountError extends ProgramError { + override readonly name: string = 'SwitchboardInvalidAccount'; + + readonly code: number = 0x17ad; // 6061 + + constructor(program: Program, cause?: Error) { + super('Switchboard oracle: invalid account', program, cause); + } +} +codeToErrorMap.set(0x17ad, SwitchboardInvalidAccountError); +nameToErrorMap.set('SwitchboardInvalidAccount', SwitchboardInvalidAccountError); + +/** MathError: Math error */ +export class MathErrorError extends ProgramError { + override readonly name: string = 'MathError'; + + readonly code: number = 0x17ae; // 6062 + + constructor(program: Program, cause?: Error) { + super('Math error', program, cause); + } +} +codeToErrorMap.set(0x17ae, MathErrorError); +nameToErrorMap.set('MathError', MathErrorError); + +/** InvalidEmissionsDestinationAccount: Invalid emissions destination account */ +export class InvalidEmissionsDestinationAccountError extends ProgramError { + override readonly name: string = 'InvalidEmissionsDestinationAccount'; + + readonly code: number = 0x17af; // 6063 + + constructor(program: Program, cause?: Error) { + super('Invalid emissions destination account', program, cause); + } +} +codeToErrorMap.set(0x17af, InvalidEmissionsDestinationAccountError); +nameToErrorMap.set( + 'InvalidEmissionsDestinationAccount', + InvalidEmissionsDestinationAccountError +); + +/** SameAssetAndLiabilityBanks: Asset and liability bank cannot be the same */ +export class SameAssetAndLiabilityBanksError extends ProgramError { + override readonly name: string = 'SameAssetAndLiabilityBanks'; + + readonly code: number = 0x17b0; // 6064 + + constructor(program: Program, cause?: Error) { + super('Asset and liability bank cannot be the same', program, cause); + } +} +codeToErrorMap.set(0x17b0, SameAssetAndLiabilityBanksError); +nameToErrorMap.set( + 'SameAssetAndLiabilityBanks', + SameAssetAndLiabilityBanksError +); + +/** OverliquidationAttempt: Trying to withdraw more assets than available */ +export class OverliquidationAttemptError extends ProgramError { + override readonly name: string = 'OverliquidationAttempt'; + + readonly code: number = 0x17b1; // 6065 + + constructor(program: Program, cause?: Error) { + super('Trying to withdraw more assets than available', program, cause); + } +} +codeToErrorMap.set(0x17b1, OverliquidationAttemptError); +nameToErrorMap.set('OverliquidationAttempt', OverliquidationAttemptError); + +/** NoLiabilitiesInLiabilityBank: Liability bank has no liabilities */ +export class NoLiabilitiesInLiabilityBankError extends ProgramError { + override readonly name: string = 'NoLiabilitiesInLiabilityBank'; + + readonly code: number = 0x17b2; // 6066 + + constructor(program: Program, cause?: Error) { + super('Liability bank has no liabilities', program, cause); + } +} +codeToErrorMap.set(0x17b2, NoLiabilitiesInLiabilityBankError); +nameToErrorMap.set( + 'NoLiabilitiesInLiabilityBank', + NoLiabilitiesInLiabilityBankError +); + +/** AssetsInLiabilityBank: Liability bank has assets */ +export class AssetsInLiabilityBankError extends ProgramError { + override readonly name: string = 'AssetsInLiabilityBank'; + + readonly code: number = 0x17b3; // 6067 + + constructor(program: Program, cause?: Error) { + super('Liability bank has assets', program, cause); + } +} +codeToErrorMap.set(0x17b3, AssetsInLiabilityBankError); +nameToErrorMap.set('AssetsInLiabilityBank', AssetsInLiabilityBankError); + +/** HealthyAccount: Account is healthy and cannot be liquidated */ +export class HealthyAccountError extends ProgramError { + override readonly name: string = 'HealthyAccount'; + + readonly code: number = 0x17b4; // 6068 + + constructor(program: Program, cause?: Error) { + super('Account is healthy and cannot be liquidated', program, cause); + } +} +codeToErrorMap.set(0x17b4, HealthyAccountError); +nameToErrorMap.set('HealthyAccount', HealthyAccountError); + +/** ExhaustedLiability: Liability payoff too severe, exhausted liability */ +export class ExhaustedLiabilityError extends ProgramError { + override readonly name: string = 'ExhaustedLiability'; + + readonly code: number = 0x17b5; // 6069 + + constructor(program: Program, cause?: Error) { + super('Liability payoff too severe, exhausted liability', program, cause); + } +} +codeToErrorMap.set(0x17b5, ExhaustedLiabilityError); +nameToErrorMap.set('ExhaustedLiability', ExhaustedLiabilityError); + +/** TooSeverePayoff: Liability payoff too severe, liability balance has assets */ +export class TooSeverePayoffError extends ProgramError { + override readonly name: string = 'TooSeverePayoff'; + + readonly code: number = 0x17b6; // 6070 + + constructor(program: Program, cause?: Error) { + super( + 'Liability payoff too severe, liability balance has assets', + program, + cause + ); + } +} +codeToErrorMap.set(0x17b6, TooSeverePayoffError); +nameToErrorMap.set('TooSeverePayoff', TooSeverePayoffError); + +/** TooSevereLiquidation: Liquidation too severe, account above maintenance requirement */ +export class TooSevereLiquidationError extends ProgramError { + override readonly name: string = 'TooSevereLiquidation'; + + readonly code: number = 0x17b7; // 6071 + + constructor(program: Program, cause?: Error) { + super( + 'Liquidation too severe, account above maintenance requirement', + program, + cause + ); + } +} +codeToErrorMap.set(0x17b7, TooSevereLiquidationError); +nameToErrorMap.set('TooSevereLiquidation', TooSevereLiquidationError); + +/** WorseHealthPostLiquidation: Liquidation would worsen account health */ +export class WorseHealthPostLiquidationError extends ProgramError { + override readonly name: string = 'WorseHealthPostLiquidation'; + + readonly code: number = 0x17b8; // 6072 + + constructor(program: Program, cause?: Error) { + super('Liquidation would worsen account health', program, cause); + } +} +codeToErrorMap.set(0x17b8, WorseHealthPostLiquidationError); +nameToErrorMap.set( + 'WorseHealthPostLiquidation', + WorseHealthPostLiquidationError +); + +/** ArenaBankLimit: Arena groups can only support two banks */ +export class ArenaBankLimitError extends ProgramError { + override readonly name: string = 'ArenaBankLimit'; + + readonly code: number = 0x17b9; // 6073 + + constructor(program: Program, cause?: Error) { + super('Arena groups can only support two banks', program, cause); + } +} +codeToErrorMap.set(0x17b9, ArenaBankLimitError); +nameToErrorMap.set('ArenaBankLimit', ArenaBankLimitError); + +/** ArenaSettingCannotChange: Arena groups cannot return to non-arena status */ +export class ArenaSettingCannotChangeError extends ProgramError { + override readonly name: string = 'ArenaSettingCannotChange'; + + readonly code: number = 0x17ba; // 6074 + + constructor(program: Program, cause?: Error) { + super('Arena groups cannot return to non-arena status', program, cause); + } +} +codeToErrorMap.set(0x17ba, ArenaSettingCannotChangeError); +nameToErrorMap.set('ArenaSettingCannotChange', ArenaSettingCannotChangeError); + +/** + * Attempts to resolve a custom program error from the provided error code. + * @category Errors + */ +export function getMarginfiErrorFromCode( + code: number, + program: Program, + cause?: Error +): ProgramError | null { + const constructor = codeToErrorMap.get(code); + return constructor ? new constructor(program, cause) : null; +} + +/** + * Attempts to resolve a custom program error from the provided error name, i.e. 'Unauthorized'. + * @category Errors + */ +export function getMarginfiErrorFromName( + name: string, + program: Program, + cause?: Error +): ProgramError | null { + const constructor = nameToErrorMap.get(name); + return constructor ? new constructor(program, cause) : null; +} diff --git a/solauto-sdk/src/marginfi-sdk/index.ts b/solauto-sdk/src/externalSdks/marginfi/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/index.ts rename to solauto-sdk/src/externalSdks/marginfi/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/index.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/index.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountBorrow.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountBorrow.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountBorrow.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountBorrow.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountCloseBalance.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountCloseBalance.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountCloseBalance.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountCloseBalance.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountDeposit.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountDeposit.ts similarity index 95% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountDeposit.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountDeposit.ts index d9e7dfe0..be96bd2d 100644 --- a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountDeposit.ts +++ b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountDeposit.ts @@ -8,6 +8,8 @@ import { Context, + Option, + OptionOrNullable, Pda, PublicKey, Signer, @@ -17,7 +19,9 @@ import { import { Serializer, array, + bool, mapSerializer, + option, struct, u64, u8, @@ -43,10 +47,12 @@ export type LendingAccountDepositInstructionAccounts = { export type LendingAccountDepositInstructionData = { discriminator: Array; amount: bigint; + depositUpToLimit: Option; }; export type LendingAccountDepositInstructionDataArgs = { amount: number | bigint; + depositUpToLimit: OptionOrNullable; }; export function getLendingAccountDepositInstructionDataSerializer(): Serializer< @@ -62,6 +68,7 @@ export function getLendingAccountDepositInstructionDataSerializer(): Serializer< [ ['discriminator', array(u8(), { size: 8 })], ['amount', u64()], + ['depositUpToLimit', option(bool())], ], { description: 'LendingAccountDepositInstructionData' } ), diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountEndFlashloan.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountEndFlashloan.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountEndFlashloan.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountEndFlashloan.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountLiquidate.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountLiquidate.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountLiquidate.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountLiquidate.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountRepay.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountRepay.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountRepay.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountRepay.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountSettleEmissions.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountSettleEmissions.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountSettleEmissions.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountSettleEmissions.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountStartFlashloan.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountStartFlashloan.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountStartFlashloan.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountStartFlashloan.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountWithdraw.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountWithdraw.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountWithdraw.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountWithdraw.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingAccountWithdrawEmissions.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountWithdrawEmissions.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingAccountWithdrawEmissions.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingAccountWithdrawEmissions.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAccrueBankInterest.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAccrueBankInterest.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAccrueBankInterest.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAccrueBankInterest.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAddBank.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAddBank.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAddBank.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAddBank.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAddBankWithSeed.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAddBankWithSeed.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolAddBankWithSeed.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolAddBankWithSeed.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolCollectBankFees.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolCollectBankFees.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolCollectBankFees.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolCollectBankFees.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolConfigureBank.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolConfigureBank.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolConfigureBank.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolConfigureBank.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolHandleBankruptcy.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolHandleBankruptcy.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolHandleBankruptcy.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolHandleBankruptcy.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolSetupEmissions.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolSetupEmissions.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolSetupEmissions.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolSetupEmissions.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/lendingPoolUpdateEmissionsParameters.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolUpdateEmissionsParameters.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/lendingPoolUpdateEmissionsParameters.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/lendingPoolUpdateEmissionsParameters.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/marginfiAccountInitialize.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/marginfiAccountInitialize.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/marginfiAccountInitialize.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/marginfiAccountInitialize.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/marginfiGroupConfigure.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/marginfiGroupConfigure.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/marginfiGroupConfigure.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/marginfiGroupConfigure.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/marginfiGroupInitialize.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/marginfiGroupInitialize.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/marginfiGroupInitialize.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/marginfiGroupInitialize.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/setAccountFlag.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/setAccountFlag.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/setAccountFlag.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/setAccountFlag.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/setNewAccountAuthority.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/setNewAccountAuthority.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/setNewAccountAuthority.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/setNewAccountAuthority.ts diff --git a/solauto-sdk/src/marginfi-sdk/instructions/unsetAccountFlag.ts b/solauto-sdk/src/externalSdks/marginfi/instructions/unsetAccountFlag.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/instructions/unsetAccountFlag.ts rename to solauto-sdk/src/externalSdks/marginfi/instructions/unsetAccountFlag.ts diff --git a/solauto-sdk/src/marginfi-sdk/programs/index.ts b/solauto-sdk/src/externalSdks/marginfi/programs/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/programs/index.ts rename to solauto-sdk/src/externalSdks/marginfi/programs/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/programs/marginfi.ts b/solauto-sdk/src/externalSdks/marginfi/programs/marginfi.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/programs/marginfi.ts rename to solauto-sdk/src/externalSdks/marginfi/programs/marginfi.ts diff --git a/solauto-sdk/src/marginfi-sdk/shared/index.ts b/solauto-sdk/src/externalSdks/marginfi/shared/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/shared/index.ts rename to solauto-sdk/src/externalSdks/marginfi/shared/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/accountEventHeader.ts b/solauto-sdk/src/externalSdks/marginfi/types/accountEventHeader.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/accountEventHeader.ts rename to solauto-sdk/src/externalSdks/marginfi/types/accountEventHeader.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/balance.ts b/solauto-sdk/src/externalSdks/marginfi/types/balance.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/balance.ts rename to solauto-sdk/src/externalSdks/marginfi/types/balance.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/balanceDecreaseType.ts b/solauto-sdk/src/externalSdks/marginfi/types/balanceDecreaseType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/balanceDecreaseType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/balanceDecreaseType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/balanceIncreaseType.ts b/solauto-sdk/src/externalSdks/marginfi/types/balanceIncreaseType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/balanceIncreaseType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/balanceIncreaseType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/balanceSide.ts b/solauto-sdk/src/externalSdks/marginfi/types/balanceSide.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/balanceSide.ts rename to solauto-sdk/src/externalSdks/marginfi/types/balanceSide.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/bankConfig.ts b/solauto-sdk/src/externalSdks/marginfi/types/bankConfig.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/bankConfig.ts rename to solauto-sdk/src/externalSdks/marginfi/types/bankConfig.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/bankConfigCompact.ts b/solauto-sdk/src/externalSdks/marginfi/types/bankConfigCompact.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/bankConfigCompact.ts rename to solauto-sdk/src/externalSdks/marginfi/types/bankConfigCompact.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/bankOperationalState.ts b/solauto-sdk/src/externalSdks/marginfi/types/bankOperationalState.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/bankOperationalState.ts rename to solauto-sdk/src/externalSdks/marginfi/types/bankOperationalState.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/bankVaultType.ts b/solauto-sdk/src/externalSdks/marginfi/types/bankVaultType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/bankVaultType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/bankVaultType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/groupEventHeader.ts b/solauto-sdk/src/externalSdks/marginfi/types/groupEventHeader.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/groupEventHeader.ts rename to solauto-sdk/src/externalSdks/marginfi/types/groupEventHeader.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/index.ts b/solauto-sdk/src/externalSdks/marginfi/types/index.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/index.ts rename to solauto-sdk/src/externalSdks/marginfi/types/index.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/interestRateConfig.ts b/solauto-sdk/src/externalSdks/marginfi/types/interestRateConfig.ts similarity index 88% rename from solauto-sdk/src/marginfi-sdk/types/interestRateConfig.ts rename to solauto-sdk/src/externalSdks/marginfi/types/interestRateConfig.ts index 22ad00b4..41b35791 100644 --- a/solauto-sdk/src/marginfi-sdk/types/interestRateConfig.ts +++ b/solauto-sdk/src/externalSdks/marginfi/types/interestRateConfig.ts @@ -11,6 +11,7 @@ import { array, struct, u128, + u32, } from '@metaplex-foundation/umi/serializers'; import { WrappedI80F48, @@ -26,7 +27,8 @@ export type InterestRateConfig = { insuranceIrFee: WrappedI80F48; protocolFixedFeeApr: WrappedI80F48; protocolIrFee: WrappedI80F48; - padding: Array; + protocolOriginationFee: WrappedI80F48; + padding: Array; }; export type InterestRateConfigArgs = { @@ -37,6 +39,7 @@ export type InterestRateConfigArgs = { insuranceIrFee: WrappedI80F48Args; protocolFixedFeeApr: WrappedI80F48Args; protocolIrFee: WrappedI80F48Args; + protocolOriginationFee: WrappedI80F48Args; padding: Array; }; @@ -53,7 +56,8 @@ export function getInterestRateConfigSerializer(): Serializer< ['insuranceIrFee', getWrappedI80F48Serializer()], ['protocolFixedFeeApr', getWrappedI80F48Serializer()], ['protocolIrFee', getWrappedI80F48Serializer()], - ['padding', array(u128(), { size: 8 })], + ['protocolOriginationFee', getWrappedI80F48Serializer()], + ['padding', array(u32(), { size: 28 })], ], { description: 'InterestRateConfig' } ) as Serializer; diff --git a/solauto-sdk/src/marginfi-sdk/types/interestRateConfigCompact.ts b/solauto-sdk/src/externalSdks/marginfi/types/interestRateConfigCompact.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/interestRateConfigCompact.ts rename to solauto-sdk/src/externalSdks/marginfi/types/interestRateConfigCompact.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/interestRateConfigOpt.ts b/solauto-sdk/src/externalSdks/marginfi/types/interestRateConfigOpt.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/interestRateConfigOpt.ts rename to solauto-sdk/src/externalSdks/marginfi/types/interestRateConfigOpt.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/lendingAccount.ts b/solauto-sdk/src/externalSdks/marginfi/types/lendingAccount.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/lendingAccount.ts rename to solauto-sdk/src/externalSdks/marginfi/types/lendingAccount.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/liquidationBalances.ts b/solauto-sdk/src/externalSdks/marginfi/types/liquidationBalances.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/liquidationBalances.ts rename to solauto-sdk/src/externalSdks/marginfi/types/liquidationBalances.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/oracleConfig.ts b/solauto-sdk/src/externalSdks/marginfi/types/oracleConfig.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/oracleConfig.ts rename to solauto-sdk/src/externalSdks/marginfi/types/oracleConfig.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/oraclePriceType.ts b/solauto-sdk/src/externalSdks/marginfi/types/oraclePriceType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/oraclePriceType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/oraclePriceType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/oracleSetup.ts b/solauto-sdk/src/externalSdks/marginfi/types/oracleSetup.ts similarity index 96% rename from solauto-sdk/src/marginfi-sdk/types/oracleSetup.ts rename to solauto-sdk/src/externalSdks/marginfi/types/oracleSetup.ts index d4588edb..2240a90a 100644 --- a/solauto-sdk/src/marginfi-sdk/types/oracleSetup.ts +++ b/solauto-sdk/src/externalSdks/marginfi/types/oracleSetup.ts @@ -14,6 +14,7 @@ export enum OracleSetup { SwitchboardLegacy, PythPushOracle, SwitchboardPull, + StakedWithPythPush, } export type OracleSetupArgs = OracleSetup; diff --git a/solauto-sdk/src/marginfi-sdk/types/priceBias.ts b/solauto-sdk/src/externalSdks/marginfi/types/priceBias.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/priceBias.ts rename to solauto-sdk/src/externalSdks/marginfi/types/priceBias.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/requirementType.ts b/solauto-sdk/src/externalSdks/marginfi/types/requirementType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/requirementType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/requirementType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/riskRequirementType.ts b/solauto-sdk/src/externalSdks/marginfi/types/riskRequirementType.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/riskRequirementType.ts rename to solauto-sdk/src/externalSdks/marginfi/types/riskRequirementType.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/riskTier.ts b/solauto-sdk/src/externalSdks/marginfi/types/riskTier.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/riskTier.ts rename to solauto-sdk/src/externalSdks/marginfi/types/riskTier.ts diff --git a/solauto-sdk/src/marginfi-sdk/types/wrappedI80F48.ts b/solauto-sdk/src/externalSdks/marginfi/types/wrappedI80F48.ts similarity index 100% rename from solauto-sdk/src/marginfi-sdk/types/wrappedI80F48.ts rename to solauto-sdk/src/externalSdks/marginfi/types/wrappedI80F48.ts diff --git a/solauto-sdk/src/externalSdks/pyth/accounts/index.ts b/solauto-sdk/src/externalSdks/pyth/accounts/index.ts new file mode 100644 index 00000000..6d07a305 --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/accounts/index.ts @@ -0,0 +1,9 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +export * from './priceUpdateV2Account'; diff --git a/solauto-sdk/src/externalSdks/pyth/accounts/priceUpdateV2Account.ts b/solauto-sdk/src/externalSdks/pyth/accounts/priceUpdateV2Account.ts new file mode 100644 index 00000000..bd065d65 --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/accounts/priceUpdateV2Account.ts @@ -0,0 +1,179 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Account, + Context, + Pda, + PublicKey, + RpcAccount, + RpcGetAccountOptions, + RpcGetAccountsOptions, + assertAccountExists, + deserializeAccount, + gpaBuilder, + publicKey as toPublicKey, +} from '@metaplex-foundation/umi'; +import { + Serializer, + array, + mapSerializer, + publicKey as publicKeySerializer, + struct, + u64, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { + PriceMessage, + PriceMessageArgs, + getPriceMessageSerializer, +} from '../types'; + +export type PriceUpdateV2Account = Account; + +export type PriceUpdateV2AccountAccountData = { + discriminator: Array; + writeAuthority: PublicKey; + verificationLevel: number; + priceMessage: PriceMessage; + postedSlot: bigint; +}; + +export type PriceUpdateV2AccountAccountDataArgs = { + writeAuthority: PublicKey; + verificationLevel: number; + priceMessage: PriceMessageArgs; + postedSlot: number | bigint; +}; + +export function getPriceUpdateV2AccountAccountDataSerializer(): Serializer< + PriceUpdateV2AccountAccountDataArgs, + PriceUpdateV2AccountAccountData +> { + return mapSerializer< + PriceUpdateV2AccountAccountDataArgs, + any, + PriceUpdateV2AccountAccountData + >( + struct( + [ + ['discriminator', array(u8(), { size: 8 })], + ['writeAuthority', publicKeySerializer()], + ['verificationLevel', u8()], + ['priceMessage', getPriceMessageSerializer()], + ['postedSlot', u64()], + ], + { description: 'PriceUpdateV2AccountAccountData' } + ), + (value) => ({ + ...value, + discriminator: [93, 77, 250, 192, 211, 157, 97, 248], + }) + ) as Serializer< + PriceUpdateV2AccountAccountDataArgs, + PriceUpdateV2AccountAccountData + >; +} + +export function deserializePriceUpdateV2Account( + rawAccount: RpcAccount +): PriceUpdateV2Account { + return deserializeAccount( + rawAccount, + getPriceUpdateV2AccountAccountDataSerializer() + ); +} + +export async function fetchPriceUpdateV2Account( + context: Pick, + publicKey: PublicKey | Pda, + options?: RpcGetAccountOptions +): Promise { + const maybeAccount = await context.rpc.getAccount( + toPublicKey(publicKey, false), + options + ); + assertAccountExists(maybeAccount, 'PriceUpdateV2Account'); + return deserializePriceUpdateV2Account(maybeAccount); +} + +export async function safeFetchPriceUpdateV2Account( + context: Pick, + publicKey: PublicKey | Pda, + options?: RpcGetAccountOptions +): Promise { + const maybeAccount = await context.rpc.getAccount( + toPublicKey(publicKey, false), + options + ); + return maybeAccount.exists + ? deserializePriceUpdateV2Account(maybeAccount) + : null; +} + +export async function fetchAllPriceUpdateV2Account( + context: Pick, + publicKeys: Array, + options?: RpcGetAccountsOptions +): Promise { + const maybeAccounts = await context.rpc.getAccounts( + publicKeys.map((key) => toPublicKey(key, false)), + options + ); + return maybeAccounts.map((maybeAccount) => { + assertAccountExists(maybeAccount, 'PriceUpdateV2Account'); + return deserializePriceUpdateV2Account(maybeAccount); + }); +} + +export async function safeFetchAllPriceUpdateV2Account( + context: Pick, + publicKeys: Array, + options?: RpcGetAccountsOptions +): Promise { + const maybeAccounts = await context.rpc.getAccounts( + publicKeys.map((key) => toPublicKey(key, false)), + options + ); + return maybeAccounts + .filter((maybeAccount) => maybeAccount.exists) + .map((maybeAccount) => + deserializePriceUpdateV2Account(maybeAccount as RpcAccount) + ); +} + +export function getPriceUpdateV2AccountGpaBuilder( + context: Pick +) { + const programId = context.programs.getPublicKey( + '', + 'pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT' + ); + return gpaBuilder(context, programId) + .registerFields<{ + discriminator: Array; + writeAuthority: PublicKey; + verificationLevel: number; + priceMessage: PriceMessageArgs; + postedSlot: number | bigint; + }>({ + discriminator: [0, array(u8(), { size: 8 })], + writeAuthority: [8, publicKeySerializer()], + verificationLevel: [40, u8()], + priceMessage: [41, getPriceMessageSerializer()], + postedSlot: [125, u64()], + }) + .deserializeUsing((account) => + deserializePriceUpdateV2Account(account) + ) + .whereField('discriminator', [93, 77, 250, 192, 211, 157, 97, 248]); +} + +export function getPriceUpdateV2AccountSize(): number { + return 133; +} diff --git a/solauto-sdk/src/externalSdks/pyth/index.ts b/solauto-sdk/src/externalSdks/pyth/index.ts new file mode 100644 index 00000000..ef2456a7 --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/index.ts @@ -0,0 +1,10 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +export * from './accounts'; +export * from './types'; diff --git a/solauto-sdk/src/externalSdks/pyth/types/index.ts b/solauto-sdk/src/externalSdks/pyth/types/index.ts new file mode 100644 index 00000000..7ac88f9e --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/types/index.ts @@ -0,0 +1,10 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +export * from './priceMessage'; +export * from './priceUpdateV2'; diff --git a/solauto-sdk/src/externalSdks/pyth/types/priceMessage.ts b/solauto-sdk/src/externalSdks/pyth/types/priceMessage.ts new file mode 100644 index 00000000..d887822d --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/types/priceMessage.ts @@ -0,0 +1,58 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { PublicKey } from '@metaplex-foundation/umi'; +import { + Serializer, + i32, + i64, + publicKey as publicKeySerializer, + struct, + u64, +} from '@metaplex-foundation/umi/serializers'; + +export type PriceMessage = { + feedId: PublicKey; + price: bigint; + conf: bigint; + exponent: number; + publishTime: bigint; + prevPublishTime: bigint; + emaPrice: bigint; + emaConf: bigint; +}; + +export type PriceMessageArgs = { + feedId: PublicKey; + price: number | bigint; + conf: number | bigint; + exponent: number; + publishTime: number | bigint; + prevPublishTime: number | bigint; + emaPrice: number | bigint; + emaConf: number | bigint; +}; + +export function getPriceMessageSerializer(): Serializer< + PriceMessageArgs, + PriceMessage +> { + return struct( + [ + ['feedId', publicKeySerializer()], + ['price', i64()], + ['conf', u64()], + ['exponent', i32()], + ['publishTime', i64()], + ['prevPublishTime', i64()], + ['emaPrice', i64()], + ['emaConf', u64()], + ], + { description: 'PriceMessage' } + ) as Serializer; +} diff --git a/solauto-sdk/src/externalSdks/pyth/types/priceUpdateV2.ts b/solauto-sdk/src/externalSdks/pyth/types/priceUpdateV2.ts new file mode 100644 index 00000000..caa8c835 --- /dev/null +++ b/solauto-sdk/src/externalSdks/pyth/types/priceUpdateV2.ts @@ -0,0 +1,46 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { PublicKey } from '@metaplex-foundation/umi'; +import { + Serializer, + publicKey as publicKeySerializer, + struct, + u64, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { PriceMessage, PriceMessageArgs, getPriceMessageSerializer } from '.'; + +export type PriceUpdateV2 = { + writeAuthority: PublicKey; + verificationLevel: number; + priceMessage: PriceMessage; + postedSlot: bigint; +}; + +export type PriceUpdateV2Args = { + writeAuthority: PublicKey; + verificationLevel: number; + priceMessage: PriceMessageArgs; + postedSlot: number | bigint; +}; + +export function getPriceUpdateV2Serializer(): Serializer< + PriceUpdateV2Args, + PriceUpdateV2 +> { + return struct( + [ + ['writeAuthority', publicKeySerializer()], + ['verificationLevel', u8()], + ['priceMessage', getPriceMessageSerializer()], + ['postedSlot', u64()], + ], + { description: 'PriceUpdateV2' } + ) as Serializer; +} diff --git a/solauto-sdk/src/generated/accounts/solautoPosition.ts b/solauto-sdk/src/generated/accounts/solautoPosition.ts index 442a15d3..ff32c636 100644 --- a/solauto-sdk/src/generated/accounts/solautoPosition.ts +++ b/solauto-sdk/src/generated/accounts/solautoPosition.ts @@ -88,7 +88,7 @@ export function getSolautoPositionAccountDataSerializer(): Serializer< ['position', getPositionDataSerializer()], ['state', getPositionStateSerializer()], ['rebalance', getRebalanceDataSerializer()], - ['padding', array(u32(), { size: 32 })], + ['padding', array(u32(), { size: 20 })], ], { description: 'SolautoPositionAccountData' } ) as Serializer; @@ -188,7 +188,7 @@ export function getSolautoPositionGpaBuilder( position: [40, getPositionDataSerializer()], state: [360, getPositionStateSerializer()], rebalance: [648, getRebalanceDataSerializer()], - padding: [704, array(u32(), { size: 32 })], + padding: [752, array(u32(), { size: 20 })], }) .deserializeUsing((account) => deserializeSolautoPosition(account) diff --git a/solauto-sdk/src/generated/errors/solauto.ts b/solauto-sdk/src/generated/errors/solauto.ts index c9cc38fa..9c5f6767 100644 --- a/solauto-sdk/src/generated/errors/solauto.ts +++ b/solauto-sdk/src/generated/errors/solauto.ts @@ -15,7 +15,7 @@ type ProgramErrorConstructor = new ( const codeToErrorMap: Map = new Map(); const nameToErrorMap: Map = new Map(); -/** IncorrectAccounts: Missing or incorrect accounts provided for the given instruction */ +/** IncorrectAccounts: Missing or incorrect accounts provided for the given instructions */ export class IncorrectAccountsError extends ProgramError { override readonly name: string = 'IncorrectAccounts'; @@ -23,7 +23,7 @@ export class IncorrectAccountsError extends ProgramError { constructor(program: Program, cause?: Error) { super( - 'Missing or incorrect accounts provided for the given instruction', + 'Missing or incorrect accounts provided for the given instructions', program, cause ); @@ -48,89 +48,145 @@ nameToErrorMap.set( FailedAccountDeserializationError ); -/** InvalidPositionSettings: Invalid position settings provided */ -export class InvalidPositionSettingsError extends ProgramError { - override readonly name: string = 'InvalidPositionSettings'; +/** InvalidBoostToSetting: Invalid Boost-to param */ +export class InvalidBoostToSettingError extends ProgramError { + override readonly name: string = 'InvalidBoostToSetting'; readonly code: number = 0x2; // 2 constructor(program: Program, cause?: Error) { - super('Invalid position settings provided', program, cause); + super('Invalid Boost-to param', program, cause); } } -codeToErrorMap.set(0x2, InvalidPositionSettingsError); -nameToErrorMap.set('InvalidPositionSettings', InvalidPositionSettingsError); +codeToErrorMap.set(0x2, InvalidBoostToSettingError); +nameToErrorMap.set('InvalidBoostToSetting', InvalidBoostToSettingError); + +/** InvalidBoostGapSetting: Invalid Boost gap param */ +export class InvalidBoostGapSettingError extends ProgramError { + override readonly name: string = 'InvalidBoostGapSetting'; + + readonly code: number = 0x3; // 3 + + constructor(program: Program, cause?: Error) { + super('Invalid Boost gap param', program, cause); + } +} +codeToErrorMap.set(0x3, InvalidBoostGapSettingError); +nameToErrorMap.set('InvalidBoostGapSetting', InvalidBoostGapSettingError); + +/** InvalidRepayToSetting: Invalid repay-to param */ +export class InvalidRepayToSettingError extends ProgramError { + override readonly name: string = 'InvalidRepayToSetting'; + + readonly code: number = 0x4; // 4 + + constructor(program: Program, cause?: Error) { + super('Invalid repay-to param', program, cause); + } +} +codeToErrorMap.set(0x4, InvalidRepayToSettingError); +nameToErrorMap.set('InvalidRepayToSetting', InvalidRepayToSettingError); + +/** InvalidRepayGapSetting: Invalid repay gap param */ +export class InvalidRepayGapSettingError extends ProgramError { + override readonly name: string = 'InvalidRepayGapSetting'; + + readonly code: number = 0x5; // 5 + + constructor(program: Program, cause?: Error) { + super('Invalid repay gap param', program, cause); + } +} +codeToErrorMap.set(0x5, InvalidRepayGapSettingError); +nameToErrorMap.set('InvalidRepayGapSetting', InvalidRepayGapSettingError); + +/** InvalidRepayFromSetting: Invalid repay-from (repay-to + repay gap) */ +export class InvalidRepayFromSettingError extends ProgramError { + override readonly name: string = 'InvalidRepayFromSetting'; + + readonly code: number = 0x6; // 6 + + constructor(program: Program, cause?: Error) { + super('Invalid repay-from (repay-to + repay gap)', program, cause); + } +} +codeToErrorMap.set(0x6, InvalidRepayFromSettingError); +nameToErrorMap.set('InvalidRepayFromSetting', InvalidRepayFromSettingError); /** InvalidDCASettings: Invalid DCA configuration provided */ export class InvalidDCASettingsError extends ProgramError { override readonly name: string = 'InvalidDCASettings'; - readonly code: number = 0x3; // 3 + readonly code: number = 0x7; // 7 constructor(program: Program, cause?: Error) { super('Invalid DCA configuration provided', program, cause); } } -codeToErrorMap.set(0x3, InvalidDCASettingsError); +codeToErrorMap.set(0x7, InvalidDCASettingsError); nameToErrorMap.set('InvalidDCASettings', InvalidDCASettingsError); /** InvalidAutomationData: Invalid automation settings provided */ export class InvalidAutomationDataError extends ProgramError { override readonly name: string = 'InvalidAutomationData'; - readonly code: number = 0x4; // 4 + readonly code: number = 0x8; // 8 constructor(program: Program, cause?: Error) { super('Invalid automation settings provided', program, cause); } } -codeToErrorMap.set(0x4, InvalidAutomationDataError); +codeToErrorMap.set(0x8, InvalidAutomationDataError); nameToErrorMap.set('InvalidAutomationData', InvalidAutomationDataError); /** InvalidRebalanceCondition: Invalid position condition to rebalance */ export class InvalidRebalanceConditionError extends ProgramError { override readonly name: string = 'InvalidRebalanceCondition'; - readonly code: number = 0x5; // 5 + readonly code: number = 0x9; // 9 constructor(program: Program, cause?: Error) { super('Invalid position condition to rebalance', program, cause); } } -codeToErrorMap.set(0x5, InvalidRebalanceConditionError); +codeToErrorMap.set(0x9, InvalidRebalanceConditionError); nameToErrorMap.set('InvalidRebalanceCondition', InvalidRebalanceConditionError); /** InstructionIsCPI: Unable to invoke instruction through a CPI */ export class InstructionIsCPIError extends ProgramError { override readonly name: string = 'InstructionIsCPI'; - readonly code: number = 0x6; // 6 + readonly code: number = 0xa; // 10 constructor(program: Program, cause?: Error) { super('Unable to invoke instruction through a CPI', program, cause); } } -codeToErrorMap.set(0x6, InstructionIsCPIError); +codeToErrorMap.set(0xa, InstructionIsCPIError); nameToErrorMap.set('InstructionIsCPI', InstructionIsCPIError); -/** IncorrectInstructions: Incorrect set of instructions in the transaction */ +/** IncorrectInstructions: Incorrect set of instructions or instruction data in the transaction */ export class IncorrectInstructionsError extends ProgramError { override readonly name: string = 'IncorrectInstructions'; - readonly code: number = 0x7; // 7 + readonly code: number = 0xb; // 11 constructor(program: Program, cause?: Error) { - super('Incorrect set of instructions in the transaction', program, cause); + super( + 'Incorrect set of instructions or instruction data in the transaction', + program, + cause + ); } } -codeToErrorMap.set(0x7, IncorrectInstructionsError); +codeToErrorMap.set(0xb, IncorrectInstructionsError); nameToErrorMap.set('IncorrectInstructions', IncorrectInstructionsError); /** IncorrectDebtAdjustment: Incorrect swap amount provided. Likely due to high price volatility */ export class IncorrectDebtAdjustmentError extends ProgramError { override readonly name: string = 'IncorrectDebtAdjustment'; - readonly code: number = 0x8; // 8 + readonly code: number = 0xc; // 12 constructor(program: Program, cause?: Error) { super( @@ -140,9 +196,46 @@ export class IncorrectDebtAdjustmentError extends ProgramError { ); } } -codeToErrorMap.set(0x8, IncorrectDebtAdjustmentError); +codeToErrorMap.set(0xc, IncorrectDebtAdjustmentError); nameToErrorMap.set('IncorrectDebtAdjustment', IncorrectDebtAdjustmentError); +/** InvalidRebalanceMade: Invalid rebalance was made. Target supply USD and target debt USD was not met */ +export class InvalidRebalanceMadeError extends ProgramError { + override readonly name: string = 'InvalidRebalanceMade'; + + readonly code: number = 0xd; // 13 + + constructor(program: Program, cause?: Error) { + super( + 'Invalid rebalance was made. Target supply USD and target debt USD was not met', + program, + cause + ); + } +} +codeToErrorMap.set(0xd, InvalidRebalanceMadeError); +nameToErrorMap.set('InvalidRebalanceMade', InvalidRebalanceMadeError); + +/** NonAuthorityProvidedTargetLTV: Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority */ +export class NonAuthorityProvidedTargetLTVError extends ProgramError { + override readonly name: string = 'NonAuthorityProvidedTargetLTV'; + + readonly code: number = 0xe; // 14 + + constructor(program: Program, cause?: Error) { + super( + 'Cannot provide a target liquidation utilization rate if the instruction is not signed by the position authority', + program, + cause + ); + } +} +codeToErrorMap.set(0xe, NonAuthorityProvidedTargetLTVError); +nameToErrorMap.set( + 'NonAuthorityProvidedTargetLTV', + NonAuthorityProvidedTargetLTVError +); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/solauto-sdk/src/generated/instructions/claimReferralFees.ts b/solauto-sdk/src/generated/instructions/claimReferralFees.ts index 01bd4fd3..c4527508 100644 --- a/solauto-sdk/src/generated/instructions/claimReferralFees.ts +++ b/solauto-sdk/src/generated/instructions/claimReferralFees.ts @@ -38,7 +38,7 @@ export type ClaimReferralFeesInstructionAccounts = { referralState: PublicKey | Pda; referralFeesDestTa: PublicKey | Pda; referralFeesDestMint: PublicKey | Pda; - referralAuthority?: PublicKey | Pda; + referralAuthority: PublicKey | Pda; feesDestinationTa?: PublicKey | Pda; }; diff --git a/solauto-sdk/src/generated/instructions/closePosition.ts b/solauto-sdk/src/generated/instructions/closePosition.ts index b65ed027..260c559d 100644 --- a/solauto-sdk/src/generated/instructions/closePosition.ts +++ b/solauto-sdk/src/generated/instructions/closePosition.ts @@ -33,7 +33,7 @@ export type ClosePositionInstructionAccounts = { tokenProgram?: PublicKey | Pda; ataProgram?: PublicKey | Pda; solautoPosition: PublicKey | Pda; - protocolAccount: PublicKey | Pda; + lpUserAccount: PublicKey | Pda; positionSupplyTa: PublicKey | Pda; signerSupplyTa: PublicKey | Pda; positionDebtTa: PublicKey | Pda; @@ -102,10 +102,10 @@ export function closePosition( isWritable: true as boolean, value: input.solautoPosition ?? null, }, - protocolAccount: { + lpUserAccount: { index: 5, isWritable: true as boolean, - value: input.protocolAccount ?? null, + value: input.lpUserAccount ?? null, }, positionSupplyTa: { index: 6, diff --git a/solauto-sdk/src/generated/instructions/marginfiOpenPosition.ts b/solauto-sdk/src/generated/instructions/marginfiOpenPosition.ts index 83cd7329..8a74ef8e 100644 --- a/solauto-sdk/src/generated/instructions/marginfiOpenPosition.ts +++ b/solauto-sdk/src/generated/instructions/marginfiOpenPosition.ts @@ -8,8 +8,6 @@ import { Context, - Option, - OptionOrNullable, Pda, PublicKey, Signer, @@ -20,9 +18,7 @@ import { import { Serializer, mapSerializer, - option, struct, - u64, u8, } from '@metaplex-foundation/umi/serializers'; import { @@ -67,13 +63,11 @@ export type MarginfiOpenPositionInstructionData = { discriminator: number; positionType: PositionType; positionData: UpdatePositionData; - marginfiAccountSeedIdx: Option; }; export type MarginfiOpenPositionInstructionDataArgs = { positionType: PositionTypeArgs; positionData: UpdatePositionDataArgs; - marginfiAccountSeedIdx: OptionOrNullable; }; export function getMarginfiOpenPositionInstructionDataSerializer(): Serializer< @@ -90,7 +84,6 @@ export function getMarginfiOpenPositionInstructionDataSerializer(): Serializer< ['discriminator', u8()], ['positionType', getPositionTypeSerializer()], ['positionData', getUpdatePositionDataSerializer()], - ['marginfiAccountSeedIdx', option(u64())], ], { description: 'MarginfiOpenPositionInstructionData' } ), diff --git a/solauto-sdk/src/generated/instructions/marginfiProtocolInteraction.ts b/solauto-sdk/src/generated/instructions/marginfiProtocolInteraction.ts index 5bc2a629..459dbf6a 100644 --- a/solauto-sdk/src/generated/instructions/marginfiProtocolInteraction.ts +++ b/solauto-sdk/src/generated/instructions/marginfiProtocolInteraction.ts @@ -139,7 +139,7 @@ export function marginfiProtocolInteraction( }, marginfiGroup: { index: 7, - isWritable: false as boolean, + isWritable: true as boolean, value: input.marginfiGroup ?? null, }, marginfiAccount: { diff --git a/solauto-sdk/src/generated/instructions/marginfiRebalance.ts b/solauto-sdk/src/generated/instructions/marginfiRebalance.ts index 8a012e3c..d7e87e58 100644 --- a/solauto-sdk/src/generated/instructions/marginfiRebalance.ts +++ b/solauto-sdk/src/generated/instructions/marginfiRebalance.ts @@ -31,9 +31,15 @@ import { getAccountMetasAndSigners, } from '../shared'; import { + PriceType, + PriceTypeArgs, SolautoRebalanceType, SolautoRebalanceTypeArgs, + SwapType, + SwapTypeArgs, + getPriceTypeSerializer, getSolautoRebalanceTypeSerializer, + getSwapTypeSerializer, } from '../types'; // Accounts. @@ -69,14 +75,20 @@ export type MarginfiRebalanceInstructionAccounts = { export type MarginfiRebalanceInstructionData = { discriminator: number; rebalanceType: SolautoRebalanceType; + swapInAmountBaseUnit: Option; targetLiqUtilizationRateBps: Option; - targetInAmountBaseUnit: Option; + flashLoanFeeBps: Option; + priceType: Option; + swapType: Option; }; export type MarginfiRebalanceInstructionDataArgs = { rebalanceType: SolautoRebalanceTypeArgs; + swapInAmountBaseUnit: OptionOrNullable; targetLiqUtilizationRateBps: OptionOrNullable; - targetInAmountBaseUnit: OptionOrNullable; + flashLoanFeeBps: OptionOrNullable; + priceType: OptionOrNullable; + swapType: OptionOrNullable; }; export function getMarginfiRebalanceInstructionDataSerializer(): Serializer< @@ -92,8 +104,11 @@ export function getMarginfiRebalanceInstructionDataSerializer(): Serializer< [ ['discriminator', u8()], ['rebalanceType', getSolautoRebalanceTypeSerializer()], + ['swapInAmountBaseUnit', option(u64())], ['targetLiqUtilizationRateBps', option(u16())], - ['targetInAmountBaseUnit', option(u64())], + ['flashLoanFeeBps', option(u16())], + ['priceType', option(getPriceTypeSerializer())], + ['swapType', option(getSwapTypeSerializer())], ], { description: 'MarginfiRebalanceInstructionData' } ), @@ -173,7 +188,7 @@ export function marginfiRebalance( }, marginfiGroup: { index: 10, - isWritable: false as boolean, + isWritable: true as boolean, value: input.marginfiGroup ?? null, }, marginfiAccount: { diff --git a/solauto-sdk/src/generated/instructions/marginfiRefreshData.ts b/solauto-sdk/src/generated/instructions/marginfiRefreshData.ts index df633f2c..8994d412 100644 --- a/solauto-sdk/src/generated/instructions/marginfiRefreshData.ts +++ b/solauto-sdk/src/generated/instructions/marginfiRefreshData.ts @@ -25,6 +25,7 @@ import { ResolvedAccountsWithIndices, getAccountMetasAndSigners, } from '../shared'; +import { PriceType, PriceTypeArgs, getPriceTypeSerializer } from '../types'; // Accounts. export type MarginfiRefreshDataInstructionAccounts = { @@ -40,9 +41,14 @@ export type MarginfiRefreshDataInstructionAccounts = { }; // Data. -export type MarginfiRefreshDataInstructionData = { discriminator: number }; +export type MarginfiRefreshDataInstructionData = { + discriminator: number; + priceType: PriceType; +}; -export type MarginfiRefreshDataInstructionDataArgs = {}; +export type MarginfiRefreshDataInstructionDataArgs = { + priceType: PriceTypeArgs; +}; export function getMarginfiRefreshDataInstructionDataSerializer(): Serializer< MarginfiRefreshDataInstructionDataArgs, @@ -53,9 +59,13 @@ export function getMarginfiRefreshDataInstructionDataSerializer(): Serializer< any, MarginfiRefreshDataInstructionData >( - struct([['discriminator', u8()]], { - description: 'MarginfiRefreshDataInstructionData', - }), + struct( + [ + ['discriminator', u8()], + ['priceType', getPriceTypeSerializer()], + ], + { description: 'MarginfiRefreshDataInstructionData' } + ), (value) => ({ ...value, discriminator: 7 }) ) as Serializer< MarginfiRefreshDataInstructionDataArgs, @@ -63,10 +73,15 @@ export function getMarginfiRefreshDataInstructionDataSerializer(): Serializer< >; } +// Args. +export type MarginfiRefreshDataInstructionArgs = + MarginfiRefreshDataInstructionDataArgs; + // Instruction. export function marginfiRefreshData( context: Pick, - input: MarginfiRefreshDataInstructionAccounts + input: MarginfiRefreshDataInstructionAccounts & + MarginfiRefreshDataInstructionArgs ): TransactionBuilder { // Program ID. const programId = context.programs.getPublicKey( @@ -123,6 +138,9 @@ export function marginfiRefreshData( }, } satisfies ResolvedAccountsWithIndices; + // Arguments. + const resolvedArgs: MarginfiRefreshDataInstructionArgs = { ...input }; + // Accounts in order. const orderedAccounts: ResolvedAccount[] = Object.values( resolvedAccounts @@ -136,7 +154,9 @@ export function marginfiRefreshData( ); // Data. - const data = getMarginfiRefreshDataInstructionDataSerializer().serialize({}); + const data = getMarginfiRefreshDataInstructionDataSerializer().serialize( + resolvedArgs as MarginfiRefreshDataInstructionDataArgs + ); // Bytes Created On Chain. const bytesCreatedOnChain = 0; diff --git a/solauto-sdk/src/generated/types/index.ts b/solauto-sdk/src/generated/types/index.ts index aae7f585..c02644bb 100644 --- a/solauto-sdk/src/generated/types/index.ts +++ b/solauto-sdk/src/generated/types/index.ts @@ -14,15 +14,22 @@ export * from './lendingPlatform'; export * from './podBool'; export * from './positionData'; export * from './positionState'; -export * from './positionTokenUsage'; +export * from './positionTokenState'; export * from './positionType'; +export * from './priceType'; export * from './rebalanceData'; export * from './rebalanceDirection'; +export * from './rebalanceInstructionData'; +export * from './rebalanceStateValues'; +export * from './rebalanceStep'; export * from './solautoAction'; export * from './solautoRebalanceType'; export * from './solautoSettingsParameters'; export * from './solautoSettingsParametersInp'; +export * from './swapType'; export * from './tokenAmount'; export * from './tokenBalanceAmount'; +export * from './tokenBalanceChange'; +export * from './tokenBalanceChangeType'; export * from './tokenType'; export * from './updatePositionData'; diff --git a/solauto-sdk/src/generated/types/positionData.ts b/solauto-sdk/src/generated/types/positionData.ts index 386c5d04..b3901e31 100644 --- a/solauto-sdk/src/generated/types/positionData.ts +++ b/solauto-sdk/src/generated/types/positionData.ts @@ -16,13 +16,10 @@ import { u8, } from '@metaplex-foundation/umi/serializers'; import { - DCASettings, - DCASettingsArgs, LendingPlatform, LendingPlatformArgs, SolautoSettingsParameters, SolautoSettingsParametersArgs, - getDCASettingsSerializer, getLendingPlatformSerializer, getSolautoSettingsParametersSerializer, } from '.'; @@ -30,22 +27,22 @@ import { export type PositionData = { lendingPlatform: LendingPlatform; padding1: Array; - protocolUserAccount: PublicKey; - protocolSupplyAccount: PublicKey; - protocolDebtAccount: PublicKey; - settingParams: SolautoSettingsParameters; - dca: DCASettings; + lpUserAccount: PublicKey; + lpSupplyAccount: PublicKey; + lpDebtAccount: PublicKey; + settings: SolautoSettingsParameters; + lpPoolAccount: PublicKey; padding: Array; }; export type PositionDataArgs = { lendingPlatform: LendingPlatformArgs; padding1: Array; - protocolUserAccount: PublicKey; - protocolSupplyAccount: PublicKey; - protocolDebtAccount: PublicKey; - settingParams: SolautoSettingsParametersArgs; - dca: DCASettingsArgs; + lpUserAccount: PublicKey; + lpSupplyAccount: PublicKey; + lpDebtAccount: PublicKey; + settings: SolautoSettingsParametersArgs; + lpPoolAccount: PublicKey; padding: Array; }; @@ -57,12 +54,12 @@ export function getPositionDataSerializer(): Serializer< [ ['lendingPlatform', getLendingPlatformSerializer()], ['padding1', array(u8(), { size: 7 })], - ['protocolUserAccount', publicKeySerializer()], - ['protocolSupplyAccount', publicKeySerializer()], - ['protocolDebtAccount', publicKeySerializer()], - ['settingParams', getSolautoSettingsParametersSerializer()], - ['dca', getDCASettingsSerializer()], - ['padding', array(u32(), { size: 4 })], + ['lpUserAccount', publicKeySerializer()], + ['lpSupplyAccount', publicKeySerializer()], + ['lpDebtAccount', publicKeySerializer()], + ['settings', getSolautoSettingsParametersSerializer()], + ['lpPoolAccount', publicKeySerializer()], + ['padding', array(u32(), { size: 20 })], ], { description: 'PositionData' } ) as Serializer; diff --git a/solauto-sdk/src/generated/types/positionState.ts b/solauto-sdk/src/generated/types/positionState.ts index 684fd19a..895d1dcc 100644 --- a/solauto-sdk/src/generated/types/positionState.ts +++ b/solauto-sdk/src/generated/types/positionState.ts @@ -16,11 +16,11 @@ import { u8, } from '@metaplex-foundation/umi/serializers'; import { - PositionTokenUsage, - PositionTokenUsageArgs, + PositionTokenState, + PositionTokenStateArgs, TokenAmount, TokenAmountArgs, - getPositionTokenUsageSerializer, + getPositionTokenStateSerializer, getTokenAmountSerializer, } from '.'; @@ -28,12 +28,12 @@ export type PositionState = { liqUtilizationRateBps: number; padding1: Array; netWorth: TokenAmount; - supply: PositionTokenUsage; - debt: PositionTokenUsage; + supply: PositionTokenState; + debt: PositionTokenState; maxLtvBps: number; liqThresholdBps: number; padding2: Array; - lastUpdated: bigint; + lastRefreshed: bigint; padding: Array; }; @@ -41,12 +41,12 @@ export type PositionStateArgs = { liqUtilizationRateBps: number; padding1: Array; netWorth: TokenAmountArgs; - supply: PositionTokenUsageArgs; - debt: PositionTokenUsageArgs; + supply: PositionTokenStateArgs; + debt: PositionTokenStateArgs; maxLtvBps: number; liqThresholdBps: number; padding2: Array; - lastUpdated: number | bigint; + lastRefreshed: number | bigint; padding: Array; }; @@ -59,12 +59,12 @@ export function getPositionStateSerializer(): Serializer< ['liqUtilizationRateBps', u16()], ['padding1', array(u8(), { size: 6 })], ['netWorth', getTokenAmountSerializer()], - ['supply', getPositionTokenUsageSerializer()], - ['debt', getPositionTokenUsageSerializer()], + ['supply', getPositionTokenStateSerializer()], + ['debt', getPositionTokenStateSerializer()], ['maxLtvBps', u16()], ['liqThresholdBps', u16()], ['padding2', array(u8(), { size: 4 })], - ['lastUpdated', u64()], + ['lastRefreshed', u64()], ['padding', array(u32(), { size: 2 })], ], { description: 'PositionState' } diff --git a/solauto-sdk/src/generated/types/positionTokenUsage.ts b/solauto-sdk/src/generated/types/positionTokenState.ts similarity index 72% rename from solauto-sdk/src/generated/types/positionTokenUsage.ts rename to solauto-sdk/src/generated/types/positionTokenState.ts index d0df2a93..f7a71836 100644 --- a/solauto-sdk/src/generated/types/positionTokenUsage.ts +++ b/solauto-sdk/src/generated/types/positionTokenState.ts @@ -19,49 +19,46 @@ import { } from '@metaplex-foundation/umi/serializers'; import { TokenAmount, TokenAmountArgs, getTokenAmountSerializer } from '.'; -export type PositionTokenUsage = { +export type PositionTokenState = { mint: PublicKey; decimals: number; padding1: Array; + borrowFeeBps: number; amountUsed: TokenAmount; amountCanBeUsed: TokenAmount; baseAmountMarketPriceUsd: bigint; - flashLoanFeeBps: number; - borrowFeeBps: number; padding2: Array; padding: Uint8Array; }; -export type PositionTokenUsageArgs = { +export type PositionTokenStateArgs = { mint: PublicKey; decimals: number; padding1: Array; + borrowFeeBps: number; amountUsed: TokenAmountArgs; amountCanBeUsed: TokenAmountArgs; baseAmountMarketPriceUsd: number | bigint; - flashLoanFeeBps: number; - borrowFeeBps: number; padding2: Array; padding: Uint8Array; }; -export function getPositionTokenUsageSerializer(): Serializer< - PositionTokenUsageArgs, - PositionTokenUsage +export function getPositionTokenStateSerializer(): Serializer< + PositionTokenStateArgs, + PositionTokenState > { - return struct( + return struct( [ ['mint', publicKeySerializer()], ['decimals', u8()], - ['padding1', array(u8(), { size: 7 })], + ['padding1', array(u8(), { size: 5 })], + ['borrowFeeBps', u16()], ['amountUsed', getTokenAmountSerializer()], ['amountCanBeUsed', getTokenAmountSerializer()], ['baseAmountMarketPriceUsd', u64()], - ['flashLoanFeeBps', u16()], - ['borrowFeeBps', u16()], - ['padding2', array(u8(), { size: 4 })], + ['padding2', array(u8(), { size: 8 })], ['padding', bytes({ size: 32 })], ], - { description: 'PositionTokenUsage' } - ) as Serializer; + { description: 'PositionTokenState' } + ) as Serializer; } diff --git a/solauto-sdk/src/generated/types/priceType.ts b/solauto-sdk/src/generated/types/priceType.ts new file mode 100644 index 00000000..090f4336 --- /dev/null +++ b/solauto-sdk/src/generated/types/priceType.ts @@ -0,0 +1,22 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; + +export enum PriceType { + Realtime, + Ema, +} + +export type PriceTypeArgs = PriceType; + +export function getPriceTypeSerializer(): Serializer { + return scalarEnum(PriceType, { + description: 'PriceType', + }) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/rebalanceData.ts b/solauto-sdk/src/generated/types/rebalanceData.ts index 57dd778c..e1a5dc93 100644 --- a/solauto-sdk/src/generated/types/rebalanceData.ts +++ b/solauto-sdk/src/generated/types/rebalanceData.ts @@ -9,36 +9,28 @@ import { Serializer, array, - bytes, struct, - u64, - u8, + u32, } from '@metaplex-foundation/umi/serializers'; import { - RebalanceDirection, - RebalanceDirectionArgs, - SolautoRebalanceType, - SolautoRebalanceTypeArgs, - getRebalanceDirectionSerializer, - getSolautoRebalanceTypeSerializer, + RebalanceInstructionData, + RebalanceInstructionDataArgs, + RebalanceStateValues, + RebalanceStateValuesArgs, + getRebalanceInstructionDataSerializer, + getRebalanceStateValuesSerializer, } from '.'; export type RebalanceData = { - rebalanceType: SolautoRebalanceType; - padding1: Array; - rebalanceDirection: RebalanceDirection; - padding2: Array; - flashLoanAmount: bigint; - padding: Uint8Array; + ixs: RebalanceInstructionData; + values: RebalanceStateValues; + padding: Array; }; export type RebalanceDataArgs = { - rebalanceType: SolautoRebalanceTypeArgs; - padding1: Array; - rebalanceDirection: RebalanceDirectionArgs; - padding2: Array; - flashLoanAmount: number | bigint; - padding: Uint8Array; + ixs: RebalanceInstructionDataArgs; + values: RebalanceStateValuesArgs; + padding: Array; }; export function getRebalanceDataSerializer(): Serializer< @@ -47,12 +39,9 @@ export function getRebalanceDataSerializer(): Serializer< > { return struct( [ - ['rebalanceType', getSolautoRebalanceTypeSerializer()], - ['padding1', array(u8(), { size: 7 })], - ['rebalanceDirection', getRebalanceDirectionSerializer()], - ['padding2', array(u8(), { size: 7 })], - ['flashLoanAmount', u64()], - ['padding', bytes({ size: 32 })], + ['ixs', getRebalanceInstructionDataSerializer()], + ['values', getRebalanceStateValuesSerializer()], + ['padding', array(u32(), { size: 4 })], ], { description: 'RebalanceData' } ) as Serializer; diff --git a/solauto-sdk/src/generated/types/rebalanceDirection.ts b/solauto-sdk/src/generated/types/rebalanceDirection.ts index 94f307fd..b9779ee6 100644 --- a/solauto-sdk/src/generated/types/rebalanceDirection.ts +++ b/solauto-sdk/src/generated/types/rebalanceDirection.ts @@ -9,6 +9,7 @@ import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; export enum RebalanceDirection { + None, Boost, Repay, } diff --git a/solauto-sdk/src/generated/types/rebalanceInstructionData.ts b/solauto-sdk/src/generated/types/rebalanceInstructionData.ts new file mode 100644 index 00000000..c64f62ba --- /dev/null +++ b/solauto-sdk/src/generated/types/rebalanceInstructionData.ts @@ -0,0 +1,62 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Serializer, + array, + struct, + u32, + u64, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { + PodBool, + PodBoolArgs, + SolautoRebalanceType, + SolautoRebalanceTypeArgs, + SwapType, + SwapTypeArgs, + getPodBoolSerializer, + getSolautoRebalanceTypeSerializer, + getSwapTypeSerializer, +} from '.'; + +export type RebalanceInstructionData = { + active: PodBool; + rebalanceType: SolautoRebalanceType; + swapType: SwapType; + padding1: Array; + flashLoanAmount: bigint; + padding: Array; +}; + +export type RebalanceInstructionDataArgs = { + active: PodBoolArgs; + rebalanceType: SolautoRebalanceTypeArgs; + swapType: SwapTypeArgs; + padding1: Array; + flashLoanAmount: number | bigint; + padding: Array; +}; + +export function getRebalanceInstructionDataSerializer(): Serializer< + RebalanceInstructionDataArgs, + RebalanceInstructionData +> { + return struct( + [ + ['active', getPodBoolSerializer()], + ['rebalanceType', getSolautoRebalanceTypeSerializer()], + ['swapType', getSwapTypeSerializer()], + ['padding1', array(u8(), { size: 5 })], + ['flashLoanAmount', u64()], + ['padding', array(u32(), { size: 4 })], + ], + { description: 'RebalanceInstructionData' } + ) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/rebalanceStateValues.ts b/solauto-sdk/src/generated/types/rebalanceStateValues.ts new file mode 100644 index 00000000..98a2ffbf --- /dev/null +++ b/solauto-sdk/src/generated/types/rebalanceStateValues.ts @@ -0,0 +1,59 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Serializer, + array, + struct, + u32, + u64, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { + RebalanceDirection, + RebalanceDirectionArgs, + TokenBalanceChange, + TokenBalanceChangeArgs, + getRebalanceDirectionSerializer, + getTokenBalanceChangeSerializer, +} from '.'; + +export type RebalanceStateValues = { + rebalanceDirection: RebalanceDirection; + padding1: Array; + targetSupplyUsd: bigint; + targetDebtUsd: bigint; + tokenBalanceChange: TokenBalanceChange; + padding: Array; +}; + +export type RebalanceStateValuesArgs = { + rebalanceDirection: RebalanceDirectionArgs; + padding1: Array; + targetSupplyUsd: number | bigint; + targetDebtUsd: number | bigint; + tokenBalanceChange: TokenBalanceChangeArgs; + padding: Array; +}; + +export function getRebalanceStateValuesSerializer(): Serializer< + RebalanceStateValuesArgs, + RebalanceStateValues +> { + return struct( + [ + ['rebalanceDirection', getRebalanceDirectionSerializer()], + ['padding1', array(u8(), { size: 7 })], + ['targetSupplyUsd', u64()], + ['targetDebtUsd', u64()], + ['tokenBalanceChange', getTokenBalanceChangeSerializer()], + ['padding', array(u32(), { size: 4 })], + ], + { description: 'RebalanceStateValues' } + ) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/rebalanceStep.ts b/solauto-sdk/src/generated/types/rebalanceStep.ts new file mode 100644 index 00000000..67997c94 --- /dev/null +++ b/solauto-sdk/src/generated/types/rebalanceStep.ts @@ -0,0 +1,25 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; + +export enum RebalanceStep { + PreSwap, + PostSwap, +} + +export type RebalanceStepArgs = RebalanceStep; + +export function getRebalanceStepSerializer(): Serializer< + RebalanceStepArgs, + RebalanceStep +> { + return scalarEnum(RebalanceStep, { + description: 'RebalanceStep', + }) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/solautoAction.ts b/solauto-sdk/src/generated/types/solautoAction.ts index 15473529..9e766e89 100644 --- a/solauto-sdk/src/generated/types/solautoAction.ts +++ b/solauto-sdk/src/generated/types/solautoAction.ts @@ -90,7 +90,7 @@ export function solautoAction( data?: any ): Extract { return Array.isArray(data) - ? { __kind: kind, fields: data } + ? { __kind: kind, fields: data } as Extract : { __kind: kind, ...(data ?? {}) }; } export function isSolautoAction( diff --git a/solauto-sdk/src/generated/types/solautoRebalanceType.ts b/solauto-sdk/src/generated/types/solautoRebalanceType.ts index 7984c41b..c1fad120 100644 --- a/solauto-sdk/src/generated/types/solautoRebalanceType.ts +++ b/solauto-sdk/src/generated/types/solautoRebalanceType.ts @@ -9,10 +9,10 @@ import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; export enum SolautoRebalanceType { - None, Regular, DoubleRebalanceWithFL, - SingleRebalanceWithFL, + FLSwapThenRebalance, + FLRebalanceThenSwap, } export type SolautoRebalanceTypeArgs = SolautoRebalanceType; diff --git a/solauto-sdk/src/generated/types/solautoSettingsParameters.ts b/solauto-sdk/src/generated/types/solautoSettingsParameters.ts index dcb30534..1838d036 100644 --- a/solauto-sdk/src/generated/types/solautoSettingsParameters.ts +++ b/solauto-sdk/src/generated/types/solautoSettingsParameters.ts @@ -9,38 +9,20 @@ import { Serializer, array, - bytes, struct, u16, - u8, + u32, } from '@metaplex-foundation/umi/serializers'; -import { - AutomationSettings, - AutomationSettingsArgs, - getAutomationSettingsSerializer, -} from '.'; export type SolautoSettingsParameters = { boostToBps: number; boostGap: number; repayToBps: number; repayGap: number; - targetBoostToBps: number; - padding1: Array; - automation: AutomationSettings; - padding: Uint8Array; + padding: Array; }; -export type SolautoSettingsParametersArgs = { - boostToBps: number; - boostGap: number; - repayToBps: number; - repayGap: number; - targetBoostToBps: number; - padding1: Array; - automation: AutomationSettingsArgs; - padding: Uint8Array; -}; +export type SolautoSettingsParametersArgs = SolautoSettingsParameters; export function getSolautoSettingsParametersSerializer(): Serializer< SolautoSettingsParametersArgs, @@ -52,10 +34,7 @@ export function getSolautoSettingsParametersSerializer(): Serializer< ['boostGap', u16()], ['repayToBps', u16()], ['repayGap', u16()], - ['targetBoostToBps', u16()], - ['padding1', array(u8(), { size: 6 })], - ['automation', getAutomationSettingsSerializer()], - ['padding', bytes({ size: 32 })], + ['padding', array(u32(), { size: 24 })], ], { description: 'SolautoSettingsParameters' } ) as Serializer; diff --git a/solauto-sdk/src/generated/types/solautoSettingsParametersInp.ts b/solauto-sdk/src/generated/types/solautoSettingsParametersInp.ts index 5790bb8e..faf58089 100644 --- a/solauto-sdk/src/generated/types/solautoSettingsParametersInp.ts +++ b/solauto-sdk/src/generated/types/solautoSettingsParametersInp.ts @@ -6,36 +6,16 @@ * @see https://github.com/metaplex-foundation/kinobi */ -import { Option, OptionOrNullable } from '@metaplex-foundation/umi'; -import { - Serializer, - option, - struct, - u16, -} from '@metaplex-foundation/umi/serializers'; -import { - AutomationSettingsInp, - AutomationSettingsInpArgs, - getAutomationSettingsInpSerializer, -} from '.'; +import { Serializer, struct, u16 } from '@metaplex-foundation/umi/serializers'; export type SolautoSettingsParametersInp = { boostToBps: number; boostGap: number; repayToBps: number; repayGap: number; - targetBoostToBps: Option; - automation: Option; }; -export type SolautoSettingsParametersInpArgs = { - boostToBps: number; - boostGap: number; - repayToBps: number; - repayGap: number; - targetBoostToBps: OptionOrNullable; - automation: OptionOrNullable; -}; +export type SolautoSettingsParametersInpArgs = SolautoSettingsParametersInp; export function getSolautoSettingsParametersInpSerializer(): Serializer< SolautoSettingsParametersInpArgs, @@ -47,8 +27,6 @@ export function getSolautoSettingsParametersInpSerializer(): Serializer< ['boostGap', u16()], ['repayToBps', u16()], ['repayGap', u16()], - ['targetBoostToBps', option(u16())], - ['automation', option(getAutomationSettingsInpSerializer())], ], { description: 'SolautoSettingsParametersInp' } ) as Serializer< diff --git a/solauto-sdk/src/generated/types/swapType.ts b/solauto-sdk/src/generated/types/swapType.ts new file mode 100644 index 00000000..383c0a03 --- /dev/null +++ b/solauto-sdk/src/generated/types/swapType.ts @@ -0,0 +1,22 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; + +export enum SwapType { + ExactIn, + ExactOut, +} + +export type SwapTypeArgs = SwapType; + +export function getSwapTypeSerializer(): Serializer { + return scalarEnum(SwapType, { + description: 'SwapType', + }) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/tokenBalanceAmount.ts b/solauto-sdk/src/generated/types/tokenBalanceAmount.ts index d5c8d96b..8606e01a 100644 --- a/solauto-sdk/src/generated/types/tokenBalanceAmount.ts +++ b/solauto-sdk/src/generated/types/tokenBalanceAmount.ts @@ -56,7 +56,7 @@ export function tokenBalanceAmount( data?: any ): Extract { return Array.isArray(data) - ? { __kind: kind, fields: data } + ? { __kind: kind, fields: data } as Extract : { __kind: kind, ...(data ?? {}) }; } export function isTokenBalanceAmount( diff --git a/solauto-sdk/src/generated/types/tokenBalanceChange.ts b/solauto-sdk/src/generated/types/tokenBalanceChange.ts new file mode 100644 index 00000000..400f5e05 --- /dev/null +++ b/solauto-sdk/src/generated/types/tokenBalanceChange.ts @@ -0,0 +1,46 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { + Serializer, + array, + struct, + u64, + u8, +} from '@metaplex-foundation/umi/serializers'; +import { + TokenBalanceChangeType, + TokenBalanceChangeTypeArgs, + getTokenBalanceChangeTypeSerializer, +} from '.'; + +export type TokenBalanceChange = { + changeType: TokenBalanceChangeType; + padding1: Array; + amountUsd: bigint; +}; + +export type TokenBalanceChangeArgs = { + changeType: TokenBalanceChangeTypeArgs; + padding1: Array; + amountUsd: number | bigint; +}; + +export function getTokenBalanceChangeSerializer(): Serializer< + TokenBalanceChangeArgs, + TokenBalanceChange +> { + return struct( + [ + ['changeType', getTokenBalanceChangeTypeSerializer()], + ['padding1', array(u8(), { size: 7 })], + ['amountUsd', u64()], + ], + { description: 'TokenBalanceChange' } + ) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/tokenBalanceChangeType.ts b/solauto-sdk/src/generated/types/tokenBalanceChangeType.ts new file mode 100644 index 00000000..422ddde6 --- /dev/null +++ b/solauto-sdk/src/generated/types/tokenBalanceChangeType.ts @@ -0,0 +1,28 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/metaplex-foundation/kinobi + */ + +import { Serializer, scalarEnum } from '@metaplex-foundation/umi/serializers'; + +export enum TokenBalanceChangeType { + None, + PreSwapDeposit, + PostSwapDeposit, + PostRebalanceWithdrawSupplyToken, + PostRebalanceWithdrawDebtToken, +} + +export type TokenBalanceChangeTypeArgs = TokenBalanceChangeType; + +export function getTokenBalanceChangeTypeSerializer(): Serializer< + TokenBalanceChangeTypeArgs, + TokenBalanceChangeType +> { + return scalarEnum(TokenBalanceChangeType, { + description: 'TokenBalanceChangeType', + }) as Serializer; +} diff --git a/solauto-sdk/src/generated/types/updatePositionData.ts b/solauto-sdk/src/generated/types/updatePositionData.ts index 1d0a6bf2..8acc0805 100644 --- a/solauto-sdk/src/generated/types/updatePositionData.ts +++ b/solauto-sdk/src/generated/types/updatePositionData.ts @@ -24,13 +24,13 @@ import { export type UpdatePositionData = { positionId: number; - settingParams: Option; + settings: Option; dca: Option; }; export type UpdatePositionDataArgs = { positionId: number; - settingParams: OptionOrNullable; + settings: OptionOrNullable; dca: OptionOrNullable; }; @@ -41,7 +41,7 @@ export function getUpdatePositionDataSerializer(): Serializer< return struct( [ ['positionId', u8()], - ['settingParams', option(getSolautoSettingsParametersInpSerializer())], + ['settings', option(getSolautoSettingsParametersInpSerializer())], ['dca', option(getDCASettingsInpSerializer())], ], { description: 'UpdatePositionData' } diff --git a/solauto-sdk/src/idls/switchboard.json b/solauto-sdk/src/idls/switchboard.json deleted file mode 100644 index d4a1e928..00000000 --- a/solauto-sdk/src/idls/switchboard.json +++ /dev/null @@ -1 +0,0 @@ -{"address":"SBondMDrcV3K4kxZR1HNVT7osZxAHVHgYXL5Ze1oMUv","metadata":{"name":"sb_on_demand","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"guardian_quote_verify","discriminator":[168,36,93,156,157,150,148,45],"accounts":[{"name":"guardian","writable":true},{"name":"oracle","writable":true},{"name":"authority","signer":true,"relations":["oracle"]},{"name":"guardian_queue","writable":true,"relations":["state"]},{"name":"state"},{"name":"recent_slothashes","address":"SysvarS1otHashes111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"GuardianQuoteVerifyParams"}}}]},{"name":"guardian_register","discriminator":[159,76,53,117,219,29,116,135],"accounts":[{"name":"oracle","writable":true},{"name":"state"},{"name":"guardian_queue","relations":["state"]},{"name":"authority","signer":true,"relations":["state"]}],"args":[{"name":"params","type":{"defined":{"name":"GuardianRegisterParams"}}}]},{"name":"guardian_unregister","discriminator":[215,19,61,120,155,224,120,60],"accounts":[{"name":"oracle","writable":true},{"name":"state"},{"name":"guardian_queue","writable":true,"relations":["state"]},{"name":"authority","signer":true,"relations":["state"]}],"args":[{"name":"params","type":{"defined":{"name":"GuardianUnregisterParams"}}}]},{"name":"oracle_heartbeat","discriminator":[10,175,217,130,111,35,117,54],"accounts":[{"name":"oracle","writable":true},{"name":"oracle_stats","writable":true,"pda":{"seeds":[{"kind":"const","value":[79,114,97,99,108,101,83,116,97,116,115]},{"kind":"account","path":"oracle"}]}},{"name":"oracle_signer","signer":true},{"name":"queue","writable":true,"relations":["oracle","gc_node"]},{"name":"gc_node","writable":true},{"name":"program_state","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"native_mint","address":"So11111111111111111111111111111111111111112"},{"name":"queue_escrow","writable":true},{"name":"stake_program","address":"SBSTk6t52R89MmCdD739Rdd97HdbTQUFHe41vCX7pTt","relations":["program_state"]},{"name":"delegation_pool","writable":true},{"name":"delegation_group","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"OracleHeartbeatParams"}}}]},{"name":"oracle_init","discriminator":[21,158,66,65,60,221,148,61],"accounts":[{"name":"oracle","writable":true,"signer":true},{"name":"oracle_stats","writable":true,"pda":{"seeds":[{"kind":"const","value":[79,114,97,99,108,101,83,116,97,116,115]},{"kind":"account","path":"oracle"}]}},{"name":"program_state","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"lut_signer"},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"stake_program","relations":["program_state"]},{"name":"stake_pool","relations":["program_state"]}],"args":[{"name":"params","type":{"defined":{"name":"OracleInitParams"}}}]},{"name":"oracle_set_configs","discriminator":[129,111,223,4,191,188,70,180],"accounts":[{"name":"oracle"},{"name":"authority","signer":true,"relations":["oracle"]}],"args":[{"name":"params","type":{"defined":{"name":"OracleSetConfigsParams"}}}]},{"name":"oracle_update_delegation","discriminator":[46,198,113,223,160,189,118,90],"accounts":[{"name":"oracle","writable":true},{"name":"oracle_stats","pda":{"seeds":[{"kind":"const","value":[79,114,97,99,108,101,83,116,97,116,115]},{"kind":"account","path":"oracle"}]}},{"name":"queue","relations":["oracle"]},{"name":"authority","signer":true},{"name":"program_state","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"delegation_pool","writable":true},{"name":"lut_signer"},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"switch_mint"},{"name":"native_mint","address":"So11111111111111111111111111111111111111112"},{"name":"wsol_vault","writable":true},{"name":"switch_vault","writable":true},{"name":"stake_program","relations":["program_state"]},{"name":"stake_pool"},{"name":"delegation_group"}],"args":[{"name":"params","type":{"defined":{"name":"OracleUpdateDelegationParams"}}}]},{"name":"permission_set","discriminator":[211,122,185,120,129,182,55,103],"accounts":[{"name":"authority","signer":true},{"name":"granter"}],"args":[{"name":"params","type":{"defined":{"name":"PermissionSetParams"}}}]},{"name":"pull_feed_close","discriminator":[19,134,50,142,177,215,196,83],"accounts":[{"name":"pull_feed","writable":true},{"name":"reward_escrow","writable":true},{"name":"lut","writable":true},{"name":"lut_signer"},{"name":"payer","writable":true,"signer":true},{"name":"state"},{"name":"authority","writable":true,"signer":true,"relations":["pull_feed"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"PullFeedCloseParams"}}}]},{"name":"pull_feed_init","discriminator":[198,130,53,198,235,61,143,40],"accounts":[{"name":"pull_feed","writable":true,"signer":true},{"name":"queue"},{"name":"authority"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_state"},{"name":"reward_escrow","writable":true,"pda":{"seeds":[{"kind":"account","path":"pull_feed"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wrapped_sol_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"wrapped_sol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"lut_signer"},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"PullFeedInitParams"}}}]},{"name":"pull_feed_set_configs","discriminator":[217,45,11,246,64,26,82,168],"accounts":[{"name":"pull_feed","writable":true},{"name":"authority","signer":true,"relations":["pull_feed"]}],"args":[{"name":"params","type":{"defined":{"name":"PullFeedSetConfigsParams"}}}]},{"name":"pull_feed_submit_response","discriminator":[150,22,215,166,143,93,48,137],"accounts":[{"name":"feed","writable":true},{"name":"queue","relations":["feed"]},{"name":"program_state"},{"name":"recent_slothashes","address":"SysvarS1otHashes111111111111111111111111111"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"reward_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"}],"args":[{"name":"params","type":{"defined":{"name":"PullFeedSubmitResponseParams"}}}]},{"name":"pull_feed_submit_response_many","discriminator":[47,156,45,25,200,71,37,215],"accounts":[{"name":"queue"},{"name":"program_state"},{"name":"recent_slothashes","address":"SysvarS1otHashes111111111111111111111111111"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"reward_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"}],"args":[{"name":"params","type":{"defined":{"name":"PullFeedSubmitResponseManyParams"}}}]},{"name":"queue_add_mr_enclave","discriminator":[199,255,81,50,60,133,171,138],"accounts":[{"name":"queue","writable":true},{"name":"authority","signer":true},{"name":"program_authority"},{"name":"state"}],"args":[{"name":"params","type":{"defined":{"name":"QueueAddMrEnclaveParams"}}}]},{"name":"queue_allow_subsidies","discriminator":[94,203,82,157,188,138,202,108],"accounts":[{"name":"queue","writable":true},{"name":"authority","signer":true,"relations":["state"]},{"name":"state","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"QueueAllowSubsidiesParams"}}}]},{"name":"queue_garbage_collect","discriminator":[187,208,104,247,16,91,96,98],"accounts":[{"name":"queue","writable":true},{"name":"oracle","writable":true},{"name":"authority","signer":true},{"name":"state"}],"args":[{"name":"params","type":{"defined":{"name":"QueueGarbageCollectParams"}}}]},{"name":"queue_init","discriminator":[144,18,99,145,133,27,207,13],"accounts":[{"name":"queue","writable":true,"signer":true},{"name":"queue_escrow","writable":true,"pda":{"seeds":[{"kind":"account","path":"queue"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"native_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"authority"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"native_mint","address":"So11111111111111111111111111111111111111112"},{"name":"program_state"},{"name":"lut_signer","writable":true},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"params","type":{"defined":{"name":"QueueInitParams"}}}]},{"name":"queue_init_delegation_group","discriminator":[239,146,75,158,20,166,159,14],"accounts":[{"name":"queue","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_state"},{"name":"lut_signer"},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"delegation_group","writable":true},{"name":"stake_program","relations":["program_state"]},{"name":"stake_pool"}],"args":[{"name":"params","type":{"defined":{"name":"QueueInitDelegationGroupParams"}}}]},{"name":"queue_remove_mr_enclave","discriminator":[3,64,135,33,190,133,68,252],"accounts":[{"name":"queue","writable":true},{"name":"authority","signer":true},{"name":"program_authority"},{"name":"state"}],"args":[{"name":"params","type":{"defined":{"name":"QueueRemoveMrEnclaveParams"}}}]},{"name":"queue_set_configs","discriminator":[54,183,243,199,49,103,142,48],"accounts":[{"name":"queue","writable":true},{"name":"authority","signer":true},{"name":"state"}],"args":[{"name":"params","type":{"defined":{"name":"QueueSetConfigsParams"}}}]},{"name":"randomness_commit","discriminator":[52,170,152,201,179,133,242,141],"accounts":[{"name":"randomness","writable":true},{"name":"queue","relations":["randomness","oracle"]},{"name":"oracle","writable":true},{"name":"recent_slothashes","address":"SysvarS1otHashes111111111111111111111111111"},{"name":"authority","signer":true,"relations":["randomness"]}],"args":[{"name":"params","type":{"defined":{"name":"RandomnessCommitParams"}}}]},{"name":"randomness_init","discriminator":[9,9,204,33,50,116,113,15],"accounts":[{"name":"randomness","writable":true,"signer":true},{"name":"reward_escrow","writable":true,"pda":{"seeds":[{"kind":"account","path":"randomness"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wrapped_sol_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"authority","signer":true},{"name":"queue","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"wrapped_sol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"program_state"},{"name":"lut_signer"},{"name":"lut","writable":true},{"name":"address_lookup_table_program","address":"AddressLookupTab1e1111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"RandomnessInitParams"}}}]},{"name":"randomness_reveal","discriminator":[197,181,187,10,30,58,20,73],"accounts":[{"name":"randomness","writable":true},{"name":"oracle","relations":["randomness"]},{"name":"queue","relations":["oracle"]},{"name":"stats","writable":true,"pda":{"seeds":[{"kind":"const","value":[79,114,97,99,108,101,82,97,110,100,111,109,110,101,115,115,83,116,97,116,115]},{"kind":"account","path":"oracle"}]}},{"name":"authority","signer":true,"relations":["randomness"]},{"name":"payer","writable":true,"signer":true},{"name":"recent_slothashes","address":"SysvarS1otHashes111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"reward_escrow","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"wrapped_sol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"program_state"}],"args":[{"name":"params","type":{"defined":{"name":"RandomnessRevealParams"}}}]},{"name":"state_init","discriminator":[103,241,106,190,217,153,87,105],"accounts":[{"name":"state","writable":true,"pda":{"seeds":[{"kind":"const","value":[83,84,65,84,69]}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"StateInitParams"}}}]},{"name":"state_set_configs","discriminator":[40,98,76,37,206,9,47,144],"accounts":[{"name":"state","writable":true},{"name":"authority","signer":true,"relations":["state"]},{"name":"queue","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"StateSetConfigsParams"}}}]}],"accounts":[{"name":"OracleAccountData","discriminator":[128,30,16,241,170,73,55,54]},{"name":"OracleStatsAccountData","discriminator":[180,157,178,234,240,27,152,179]},{"name":"PullFeedAccountData","discriminator":[196,27,108,196,10,215,219,40]},{"name":"QueueAccountData","discriminator":[217,194,55,127,184,83,138,1]},{"name":"RandomnessAccountData","discriminator":[10,66,229,135,220,239,217,114]},{"name":"State","discriminator":[216,146,107,94,104,75,182,177]}],"events":[{"name":"CostWhitelistEvent","discriminator":[56,107,191,127,116,6,138,149]},{"name":"GarbageCollectionEvent","discriminator":[232,235,2,188,8,143,145,237]},{"name":"GuardianQuoteVerifyEvent","discriminator":[31,37,39,6,214,186,33,115]},{"name":"OracleHeartbeatEvent","discriminator":[52,29,166,2,94,7,188,13]},{"name":"OracleInitEvent","discriminator":[89,193,219,200,1,83,167,24]},{"name":"OracleQuoteOverrideEvent","discriminator":[78,204,191,210,164,196,244,65]},{"name":"OracleQuoteRotateEvent","discriminator":[26,189,196,192,225,127,26,228]},{"name":"OracleQuoteVerifyRequestEvent","discriminator":[203,209,79,0,20,71,226,202]},{"name":"PermissionSetEvent","discriminator":[148,86,123,0,102,20,119,206]},{"name":"PullFeedErrorValueEvent","discriminator":[225,80,192,95,14,12,83,192]},{"name":"PullFeedValueEvents","discriminator":[86,7,231,28,122,161,117,69]},{"name":"QueueAddMrEnclaveEvent","discriminator":[170,186,175,38,216,51,69,175]},{"name":"QueueInitEvent","discriminator":[44,137,99,227,107,8,30,105]},{"name":"QueueRemoveMrEnclaveEvent","discriminator":[4,105,196,60,84,122,203,196]},{"name":"RandomnessCommitEvent","discriminator":[88,60,172,90,112,10,206,147]}],"errors":[{"code":6000,"name":"GenericError"},{"code":6001,"name":"InvalidQuote"},{"code":6002,"name":"InsufficientQueue"},{"code":6003,"name":"QueueFull"},{"code":6004,"name":"InvalidEnclaveSigner"},{"code":6005,"name":"InvalidSigner"},{"code":6006,"name":"MrEnclaveAlreadyExists"},{"code":6007,"name":"MrEnclaveAtCapacity"},{"code":6008,"name":"MrEnclaveDoesntExist"},{"code":6009,"name":"PermissionDenied"},{"code":6010,"name":"InvalidQueue"},{"code":6011,"name":"IncorrectMrEnclave"},{"code":6012,"name":"InvalidAuthority"},{"code":6013,"name":"InvalidMrEnclave"},{"code":6014,"name":"InvalidTimestamp"},{"code":6015,"name":"InvalidOracleIdx"},{"code":6016,"name":"InvalidSecpSignature"},{"code":6017,"name":"InvalidGuardianQueue"},{"code":6018,"name":"InvalidIndex"},{"code":6019,"name":"InvalidOracleQueue"},{"code":6020,"name":"InvalidPermission"},{"code":6021,"name":"InvalidePermissionedAccount"},{"code":6022,"name":"InvalidEpochRotate"},{"code":6023,"name":"InvalidEpochFinalize"},{"code":6024,"name":"InvalidEscrow"},{"code":6025,"name":"IllegalOracle"},{"code":6026,"name":"IllegalExecuteAttempt"},{"code":6027,"name":"IllegalFeedValue"},{"code":6028,"name":"InvalidOracleFeedStats"},{"code":6029,"name":"InvalidStateAuthority"},{"code":6030,"name":"NotEnoughSamples"},{"code":6031,"name":"OracleIsVerified"},{"code":6032,"name":"QueueIsEmpty"},{"code":6033,"name":"SecpRecoverFailure"},{"code":6034,"name":"StaleSample"},{"code":6035,"name":"SwitchboardRandomnessTooOld"},{"code":6036,"name":"EpochIdMismatch"},{"code":6037,"name":"GuardianAlreadyVoted"},{"code":6038,"name":"RandomnessNotRequested"},{"code":6039,"name":"InvalidSlotNumber"},{"code":6040,"name":"RandomnessOracleKeyExpired"},{"code":6041,"name":"InvalidAdvisory"},{"code":6042,"name":"InvalidOracleStats"},{"code":6043,"name":"InvalidStakeProgram"},{"code":6044,"name":"InvalidStakePool"},{"code":6045,"name":"InvalidDelegationPool"},{"code":6046,"name":"UnparsableAccount"},{"code":6047,"name":"InvalidInstruction"},{"code":6048,"name":"OracleAlreadyVerified"},{"code":6049,"name":"GuardianNotVerified"},{"code":6050,"name":"InvalidConstraint"},{"code":6051,"name":"InvalidDelegationGroup"},{"code":6052,"name":"OracleKeyNotFound"},{"code":6053,"name":"GuardianReregisterAttempt"}],"types":[{"name":"CompactResult","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"std_dev","docs":["The standard deviation of the submissions needed for quorom size"],"type":"f32"},{"name":"mean","docs":["The mean of the submissions needed for quorom size"],"type":"f32"},{"name":"slot","docs":["The slot at which this value was signed."],"type":"u64"}]}},{"name":"CostWhitelistEvent","type":{"kind":"struct","fields":[{"name":"feeds","type":{"vec":"pubkey"}},{"name":"reward","type":"u32"}]}},{"name":"CurrentResult","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"value","docs":["The median value of the submissions needed for quorom size"],"type":"i128"},{"name":"std_dev","docs":["The standard deviation of the submissions needed for quorom size"],"type":"i128"},{"name":"mean","docs":["The mean of the submissions needed for quorom size"],"type":"i128"},{"name":"range","docs":["The range of the submissions needed for quorom size"],"type":"i128"},{"name":"min_value","docs":["The minimum value of the submissions needed for quorom size"],"type":"i128"},{"name":"max_value","docs":["The maximum value of the submissions needed for quorom size"],"type":"i128"},{"name":"num_samples","docs":["The number of samples used to calculate this result"],"type":"u8"},{"name":"padding1","type":{"array":["u8",7]}},{"name":"slot","docs":["The slot at which this value was signed."],"type":"u64"},{"name":"min_slot","docs":["The slot at which the first considered submission was made"],"type":"u64"},{"name":"max_slot","docs":["The slot at which the last considered submission was made"],"type":"u64"}]}},{"name":"GarbageCollectionEvent","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"queue","type":"pubkey"}]}},{"name":"GuardianQuoteVerifyEvent","type":{"kind":"struct","fields":[{"name":"quote","type":"pubkey"},{"name":"queue","type":"pubkey"},{"name":"oracle","type":"pubkey"}]}},{"name":"GuardianQuoteVerifyParams","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mr_enclave","type":{"array":["u8",32]}},{"name":"_reserved1","type":"u32"},{"name":"ed25519_key","type":"pubkey"},{"name":"secp256k1_key","type":{"array":["u8",64]}},{"name":"slot","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"},{"name":"advisories","type":{"vec":"u32"}}]}},{"name":"GuardianRegisterParams","type":{"kind":"struct","fields":[]}},{"name":"GuardianUnregisterParams","type":{"kind":"struct","fields":[]}},{"name":"MegaSlotInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reserved1","type":"u64"},{"name":"slot_end","type":"u64"},{"name":"perf_goal","type":"i64"},{"name":"current_signature_count","type":"i64"}]}},{"name":"MultiSubmission","type":{"kind":"struct","fields":[{"name":"values","type":{"vec":"i128"}},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"}]}},{"name":"OracleAccountData","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"enclave","docs":["Represents the state of the quote verifiers enclave."],"type":{"defined":{"name":"Quote"}}},{"name":"authority","docs":["The authority of the EnclaveAccount which is permitted to make account changes."],"type":"pubkey"},{"name":"queue","docs":["Queue used for attestation to verify a MRENCLAVE measurement."],"type":"pubkey"},{"name":"created_at","docs":["The unix timestamp when the quote was created."],"type":"i64"},{"name":"last_heartbeat","docs":["The last time the quote heartbeated on-chain."],"type":"i64"},{"name":"secp_authority","type":{"array":["u8",64]}},{"name":"gateway_uri","docs":["URI location of the verifier's gateway."],"type":{"array":["u8",64]}},{"name":"permissions","type":"u64"},{"name":"is_on_queue","docs":["Whether the quote is located on the AttestationQueues buffer."],"type":"u8"},{"name":"_padding1","type":{"array":["u8",7]}},{"name":"lut_slot","type":"u64"},{"name":"last_reward_epoch","type":"u64"},{"name":"_ebuf4","type":{"array":["u8",16]}},{"name":"_ebuf3","type":{"array":["u8",32]}},{"name":"_ebuf2","type":{"array":["u8",64]}},{"name":"_ebuf1","type":{"array":["u8",1024]}}]}},{"name":"OracleEpochInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"id","type":"u64"},{"name":"reserved1","type":"u64"},{"name":"slot_end","type":"u64"},{"name":"slash_score","type":"u64"},{"name":"reward_score","type":"u64"},{"name":"stake_score","type":"u64"}]}},{"name":"OracleHeartbeatEvent","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"queue","type":"pubkey"}]}},{"name":"OracleHeartbeatParams","type":{"kind":"struct","fields":[{"name":"gateway_uri","type":{"option":{"array":["u8",64]}}}]}},{"name":"OracleInitEvent","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"}]}},{"name":"OracleInitParams","type":{"kind":"struct","fields":[{"name":"recent_slot","type":"u64"},{"name":"authority","type":"pubkey"},{"name":"queue","type":"pubkey"},{"name":"secp_authority","type":{"option":{"array":["u8",64]}}}]}},{"name":"OracleQuoteOverrideEvent","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"queue","type":"pubkey"}]}},{"name":"OracleQuoteRotateEvent","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"}]}},{"name":"OracleQuoteVerifyRequestEvent","type":{"kind":"struct","fields":[{"name":"quote","type":"pubkey"},{"name":"oracle","type":"pubkey"}]}},{"name":"OracleSetConfigsParams","type":{"kind":"struct","fields":[{"name":"new_authority","type":{"option":"pubkey"}},{"name":"new_secp_authority","type":{"option":{"array":["u8",64]}}}]}},{"name":"OracleStatsAccountData","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"oracle","type":"pubkey"},{"name":"finalized_epoch","docs":["The last epoch that has completed. cleared after registered with the","staking program."],"type":{"defined":{"name":"OracleEpochInfo"}}},{"name":"current_epoch","docs":["The current epoch info being used by the oracle. for stake. Will moved","to finalized_epoch as soon as the epoch is over."],"type":{"defined":{"name":"OracleEpochInfo"}}},{"name":"mega_slot_info","type":{"defined":{"name":"MegaSlotInfo"}}},{"name":"last_transfer_slot","type":"u64"},{"name":"bump","type":"u8"},{"name":"padding1","type":{"array":["u8",7]}},{"name":"_ebuf","docs":["Reserved."],"type":{"array":["u8",1024]}}]}},{"name":"OracleSubmission","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"oracle","docs":["The public key of the oracle that submitted this value."],"type":"pubkey"},{"name":"slot","docs":["The slot at which this value was signed."],"type":"u64"},{"name":"padding1","type":{"array":["u8",8]}},{"name":"value","docs":["The value that was submitted."],"type":"i128"}]}},{"name":"OracleUpdateDelegationParams","type":{"kind":"struct","fields":[{"name":"_reserved1","type":"u64"}]}},{"name":"PermissionSetEvent","type":{"kind":"struct","fields":[{"name":"permission","type":"pubkey"}]}},{"name":"PermissionSetParams","type":{"kind":"struct","fields":[{"name":"permission","type":"u8"},{"name":"enable","type":"bool"}]}},{"name":"PullFeedAccountData","docs":["A representation of the data in a pull feed account."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"submissions","docs":["The oracle submissions for this feed."],"type":{"array":[{"defined":{"name":"OracleSubmission"}},32]}},{"name":"authority","docs":["The public key of the authority that can update the feed hash that","this account will use for registering updates."],"type":"pubkey"},{"name":"queue","docs":["The public key of the queue which oracles must be bound to in order to","submit data to this feed."],"type":"pubkey"},{"name":"feed_hash","docs":["SHA-256 hash of the job schema oracles will execute to produce data","for this feed."],"type":{"array":["u8",32]}},{"name":"initialized_at","docs":["The slot at which this account was initialized."],"type":"i64"},{"name":"permissions","type":"u64"},{"name":"max_variance","type":"u64"},{"name":"min_responses","type":"u32"},{"name":"name","type":{"array":["u8",32]}},{"name":"padding1","type":{"array":["u8",2]}},{"name":"historical_result_idx","type":"u8"},{"name":"min_sample_size","type":"u8"},{"name":"last_update_timestamp","type":"i64"},{"name":"lut_slot","type":"u64"},{"name":"_reserved1","type":{"array":["u8",32]}},{"name":"result","type":{"defined":{"name":"CurrentResult"}}},{"name":"max_staleness","type":"u32"},{"name":"padding2","type":{"array":["u8",12]}},{"name":"historical_results","type":{"array":[{"defined":{"name":"CompactResult"}},32]}},{"name":"_ebuf4","type":{"array":["u8",8]}},{"name":"_ebuf3","type":{"array":["u8",24]}},{"name":"_ebuf2","type":{"array":["u8",256]}}]}},{"name":"PullFeedCloseParams","type":{"kind":"struct","fields":[]}},{"name":"PullFeedErrorValueEvent","type":{"kind":"struct","fields":[{"name":"feed","type":"pubkey"},{"name":"oracle","type":"pubkey"}]}},{"name":"PullFeedInitParams","type":{"kind":"struct","fields":[{"name":"feed_hash","type":{"array":["u8",32]}},{"name":"max_variance","type":"u64"},{"name":"min_responses","type":"u32"},{"name":"name","type":{"array":["u8",32]}},{"name":"recent_slot","type":"u64"},{"name":"ipfs_hash","type":{"array":["u8",32]}},{"name":"min_sample_size","type":"u8"},{"name":"max_staleness","type":"u32"}]}},{"name":"PullFeedSetConfigsParams","type":{"kind":"struct","fields":[{"name":"feed_hash","type":{"option":{"array":["u8",32]}}},{"name":"authority","type":{"option":"pubkey"}},{"name":"max_variance","type":{"option":"u64"}},{"name":"min_responses","type":{"option":"u32"}},{"name":"name","type":{"option":{"array":["u8",32]}}},{"name":"ipfs_hash","type":{"option":{"array":["u8",32]}}},{"name":"min_sample_size","type":{"option":"u8"}},{"name":"max_staleness","type":{"option":"u32"}}]}},{"name":"PullFeedSubmitResponseManyParams","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"submissions","type":{"vec":{"defined":{"name":"MultiSubmission"}}}}]}},{"name":"PullFeedSubmitResponseParams","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"submissions","type":{"vec":{"defined":{"name":"Submission"}}}}]}},{"name":"PullFeedValueEvents","type":{"kind":"struct","fields":[{"name":"feeds","type":{"vec":"pubkey"}},{"name":"oracles","type":{"vec":"pubkey"}},{"name":"values","type":{"vec":{"vec":"i128"}}},{"name":"reward","type":"u32"}]}},{"name":"QueueAccountData","docs":["An Queue represents a round-robin queue of oracle oracles who attest on-chain","whether a Switchboard Function was executed within an enclave against an expected set of","enclave measurements.","","For an oracle to join the queue, the oracle must first submit their enclave quote on-chain and","wait for an existing oracle to attest their quote. If the oracle's quote matches an expected","measurement within the queues mr_enclaves config, it is granted permissions and will start","being assigned update requests."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","docs":["The address of the authority which is permitted to add/remove allowed enclave measurements."],"type":"pubkey"},{"name":"mr_enclaves","docs":["Allowed enclave measurements."],"type":{"array":[{"array":["u8",32]},32]}},{"name":"oracle_keys","docs":["The addresses of the quote oracles who have a valid","verification status and have heartbeated on-chain recently."],"type":{"array":["pubkey",128]}},{"name":"max_quote_verification_age","docs":["The maximum allowable time until a EnclaveAccount needs to be re-verified on-chain."],"type":"i64"},{"name":"last_heartbeat","docs":["The unix timestamp when the last quote oracle heartbeated on-chain."],"type":"i64"},{"name":"node_timeout","type":"i64"},{"name":"oracle_min_stake","docs":["The minimum number of lamports a quote oracle needs to lock-up in order to heartbeat and verify other quotes."],"type":"u64"},{"name":"allow_authority_override_after","type":"i64"},{"name":"mr_enclaves_len","docs":["The number of allowed enclave measurements."],"type":"u32"},{"name":"oracle_keys_len","docs":["The length of valid quote oracles for the given attestation queue."],"type":"u32"},{"name":"reward","docs":["The reward paid to quote oracles for attesting on-chain."],"type":"u32"},{"name":"curr_idx","docs":["Incrementer used to track the current quote oracle permitted to run any available functions."],"type":"u32"},{"name":"gc_idx","docs":["Incrementer used to garbage collect and remove stale quote oracles."],"type":"u32"},{"name":"require_authority_heartbeat_permission","type":"u8"},{"name":"require_authority_verify_permission","type":"u8"},{"name":"require_usage_permissions","type":"u8"},{"name":"signer_bump","type":"u8"},{"name":"mint","type":"pubkey"},{"name":"lut_slot","type":"u64"},{"name":"allow_subsidies","type":"u8"},{"name":"_ebuf6","docs":["Reserved."],"type":{"array":["u8",23]}},{"name":"_ebuf5","type":{"array":["u8",32]}},{"name":"_ebuf4","type":{"array":["u8",64]}},{"name":"_ebuf3","type":{"array":["u8",128]}},{"name":"_ebuf2","type":{"array":["u8",256]}},{"name":"_ebuf1","type":{"array":["u8",512]}}]}},{"name":"QueueAddMrEnclaveEvent","type":{"kind":"struct","fields":[{"name":"queue","type":"pubkey"},{"name":"mr_enclave","type":{"array":["u8",32]}}]}},{"name":"QueueAddMrEnclaveParams","type":{"kind":"struct","fields":[{"name":"mr_enclave","type":{"array":["u8",32]}}]}},{"name":"QueueAllowSubsidiesParams","type":{"kind":"struct","fields":[{"name":"allow_subsidies","type":"u8"}]}},{"name":"QueueGarbageCollectParams","type":{"kind":"struct","fields":[{"name":"idx","type":"u32"}]}},{"name":"QueueInitDelegationGroupParams","type":{"kind":"struct","fields":[]}},{"name":"QueueInitEvent","type":{"kind":"struct","fields":[{"name":"queue","type":"pubkey"}]}},{"name":"QueueInitParams","type":{"kind":"struct","fields":[{"name":"allow_authority_override_after","type":"u32"},{"name":"require_authority_heartbeat_permission","type":"bool"},{"name":"require_usage_permissions","type":"bool"},{"name":"max_quote_verification_age","type":"u32"},{"name":"reward","type":"u32"},{"name":"node_timeout","type":"u32"},{"name":"recent_slot","type":"u64"}]}},{"name":"QueueRemoveMrEnclaveEvent","type":{"kind":"struct","fields":[{"name":"queue","type":"pubkey"},{"name":"mr_enclave","type":{"array":["u8",32]}}]}},{"name":"QueueRemoveMrEnclaveParams","type":{"kind":"struct","fields":[{"name":"mr_enclave","type":{"array":["u8",32]}}]}},{"name":"QueueSetConfigsParams","type":{"kind":"struct","fields":[{"name":"authority","type":{"option":"pubkey"}},{"name":"reward","type":{"option":"u32"}},{"name":"node_timeout","type":{"option":"i64"}}]}},{"name":"Quote","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"enclave_signer","docs":["The address of the signer generated within an enclave."],"type":"pubkey"},{"name":"mr_enclave","docs":["The quotes MRENCLAVE measurement dictating the contents of the secure enclave."],"type":{"array":["u8",32]}},{"name":"verification_status","docs":["The VerificationStatus of the quote."],"type":"u8"},{"name":"padding1","type":{"array":["u8",7]}},{"name":"verification_timestamp","docs":["The unix timestamp when the quote was last verified."],"type":"i64"},{"name":"valid_until","docs":["The unix timestamp when the quotes verification status expires."],"type":"i64"},{"name":"quote_registry","docs":["The off-chain registry where the verifiers quote can be located."],"type":{"array":["u8",32]}},{"name":"registry_key","docs":["Key to lookup the buffer data on IPFS or an alternative decentralized storage solution."],"type":{"array":["u8",64]}},{"name":"secp256k1_signer","docs":["The secp256k1 public key of the enclave signer. Derived from the enclave_signer."],"type":{"array":["u8",64]}},{"name":"last_ed25519_signer","type":"pubkey"},{"name":"last_secp256k1_signer","type":{"array":["u8",64]}},{"name":"last_rotate_slot","type":"u64"},{"name":"guardian_approvers","type":{"array":["pubkey",64]}},{"name":"guardian_approvers_len","type":"u8"},{"name":"padding2","type":{"array":["u8",7]}},{"name":"staging_ed25519_signer","type":"pubkey"},{"name":"staging_secp256k1_signer","type":{"array":["u8",64]}},{"name":"_ebuf4","docs":["Reserved."],"type":{"array":["u8",32]}},{"name":"_ebuf3","type":{"array":["u8",128]}},{"name":"_ebuf2","type":{"array":["u8",256]}},{"name":"_ebuf1","type":{"array":["u8",512]}}]}},{"name":"RandomnessAccountData","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"queue","type":"pubkey"},{"name":"seed_slothash","type":{"array":["u8",32]}},{"name":"seed_slot","type":"u64"},{"name":"oracle","type":"pubkey"},{"name":"reveal_slot","type":"u64"},{"name":"value","type":{"array":["u8",32]}},{"name":"lut_slot","type":"u64"},{"name":"_ebuf3","type":{"array":["u8",24]}},{"name":"_ebuf2","type":{"array":["u8",64]}},{"name":"_ebuf1","type":{"array":["u8",128]}},{"name":"active_secp256k1_signer","type":{"array":["u8",64]}},{"name":"active_secp256k1_expiration","type":"i64"}]}},{"name":"RandomnessCommitEvent","type":{"kind":"struct","fields":[{"name":"randomness_account","type":"pubkey"},{"name":"oracle","type":"pubkey"},{"name":"slot","type":"u64"},{"name":"slothash","type":{"array":["u8",32]}}]}},{"name":"RandomnessCommitParams","type":{"kind":"struct","fields":[]}},{"name":"RandomnessInitParams","type":{"kind":"struct","fields":[{"name":"recent_slot","type":"u64"}]}},{"name":"RandomnessRevealParams","type":{"kind":"struct","fields":[{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"},{"name":"value","type":{"array":["u8",32]}}]}},{"name":"State","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"test_only_disable_mr_enclave_check","type":"u8"},{"name":"enable_staking","type":"u8"},{"name":"padding1","type":{"array":["u8",5]}},{"name":"authority","type":"pubkey"},{"name":"guardian_queue","type":"pubkey"},{"name":"reserved1","type":"u64"},{"name":"epoch_length","type":"u64"},{"name":"current_epoch","type":{"defined":{"name":"StateEpochInfo"}}},{"name":"next_epoch","type":{"defined":{"name":"StateEpochInfo"}}},{"name":"finalized_epoch","type":{"defined":{"name":"StateEpochInfo"}}},{"name":"stake_pool","type":"pubkey"},{"name":"stake_program","type":"pubkey"},{"name":"switch_mint","type":"pubkey"},{"name":"sgx_advisories","type":{"array":["u16",32]}},{"name":"advisories_len","type":"u8"},{"name":"padding2","type":"u8"},{"name":"flat_reward_cut_percentage","type":"u8"},{"name":"enable_slashing","type":"u8"},{"name":"subsidy_amount","type":"u32"},{"name":"lut_slot","type":"u64"},{"name":"base_reward","type":"u32"},{"name":"_ebuf6","type":{"array":["u8",28]}},{"name":"_ebuf5","type":{"array":["u8",32]}},{"name":"_ebuf4","type":{"array":["u8",64]}},{"name":"_ebuf3","type":{"array":["u8",128]}},{"name":"_ebuf2","type":{"array":["u8",512]}},{"name":"cost_whitelist","docs":["Cost whitelist by authority"],"type":{"array":["pubkey",32]}}]}},{"name":"StateEpochInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"id","type":"u64"},{"name":"_reserved1","type":"u64"},{"name":"slot_end","type":"u64"}]}},{"name":"StateInitParams","type":{"kind":"struct","fields":[]}},{"name":"StateSetConfigsParams","type":{"kind":"struct","fields":[{"name":"new_authority","type":"pubkey"},{"name":"test_only_disable_mr_enclave_check","type":"u8"},{"name":"stake_pool","type":"pubkey"},{"name":"stake_program","type":"pubkey"},{"name":"add_advisory","type":"u16"},{"name":"rm_advisory","type":"u16"},{"name":"epoch_length","type":"u32"},{"name":"reset_epochs","type":"bool"},{"name":"switch_mint","type":"pubkey"},{"name":"enable_staking","type":"u8"},{"name":"subsidy_amount","type":"u32"},{"name":"base_reward","type":"u32"},{"name":"add_cost_wl","type":"pubkey"},{"name":"rm_cost_wl","type":"pubkey"}]}},{"name":"Submission","type":{"kind":"struct","fields":[{"name":"value","type":"i128"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"},{"name":"offset","type":"u8"}]}}]} \ No newline at end of file diff --git a/solauto-sdk/src/index.ts b/solauto-sdk/src/index.ts index 6b6d8600..8d3f84bb 100644 --- a/solauto-sdk/src/index.ts +++ b/solauto-sdk/src/index.ts @@ -1,8 +1,11 @@ -export * from './clients'; -export * from './constants'; -export * from './generated'; -export * from './transactions'; -export * from './types'; -export * from './utils'; -// export * from './marginfi-sdk'; -// export * from './jupiter-sdk'; \ No newline at end of file +export * from "./constants"; +export * from "./generated/accounts"; +export * from "./generated/instructions"; +export * from "./generated/types"; +export * from "./externalSdks/marginfi/accounts"; +export * from "./externalSdks/marginfi/instructions"; +export * from "./externalSdks/marginfi/types"; +export * from "./services"; +export * from "./solautoPosition"; +export * from "./types"; +export * from "./utils"; diff --git a/solauto-sdk/src/marginfi-sdk/errors/marginfi.ts b/solauto-sdk/src/marginfi-sdk/errors/marginfi.ts deleted file mode 100644 index 5392606e..00000000 --- a/solauto-sdk/src/marginfi-sdk/errors/marginfi.ts +++ /dev/null @@ -1,650 +0,0 @@ -/** - * This code was AUTOGENERATED using the kinobi library. - * Please DO NOT EDIT THIS FILE, instead use visitors - * to add features, then rerun kinobi to update it. - * - * @see https://github.com/metaplex-foundation/kinobi - */ - -import { Program, ProgramError } from '@metaplex-foundation/umi'; - -type ProgramErrorConstructor = new ( - program: Program, - cause?: Error -) => ProgramError; -const codeToErrorMap: Map = new Map(); -const nameToErrorMap: Map = new Map(); - -/** MathError: Math error */ -export class MathErrorError extends ProgramError { - override readonly name: string = 'MathError'; - - readonly code: number = 0x1770; // 6000 - - constructor(program: Program, cause?: Error) { - super('Math error', program, cause); - } -} -codeToErrorMap.set(0x1770, MathErrorError); -nameToErrorMap.set('MathError', MathErrorError); - -/** BankNotFound: Invalid bank index */ -export class BankNotFoundError extends ProgramError { - override readonly name: string = 'BankNotFound'; - - readonly code: number = 0x1771; // 6001 - - constructor(program: Program, cause?: Error) { - super('Invalid bank index', program, cause); - } -} -codeToErrorMap.set(0x1771, BankNotFoundError); -nameToErrorMap.set('BankNotFound', BankNotFoundError); - -/** LendingAccountBalanceNotFound: Lending account balance not found */ -export class LendingAccountBalanceNotFoundError extends ProgramError { - override readonly name: string = 'LendingAccountBalanceNotFound'; - - readonly code: number = 0x1772; // 6002 - - constructor(program: Program, cause?: Error) { - super('Lending account balance not found', program, cause); - } -} -codeToErrorMap.set(0x1772, LendingAccountBalanceNotFoundError); -nameToErrorMap.set( - 'LendingAccountBalanceNotFound', - LendingAccountBalanceNotFoundError -); - -/** BankAssetCapacityExceeded: Bank deposit capacity exceeded */ -export class BankAssetCapacityExceededError extends ProgramError { - override readonly name: string = 'BankAssetCapacityExceeded'; - - readonly code: number = 0x1773; // 6003 - - constructor(program: Program, cause?: Error) { - super('Bank deposit capacity exceeded', program, cause); - } -} -codeToErrorMap.set(0x1773, BankAssetCapacityExceededError); -nameToErrorMap.set('BankAssetCapacityExceeded', BankAssetCapacityExceededError); - -/** InvalidTransfer: Invalid transfer */ -export class InvalidTransferError extends ProgramError { - override readonly name: string = 'InvalidTransfer'; - - readonly code: number = 0x1774; // 6004 - - constructor(program: Program, cause?: Error) { - super('Invalid transfer', program, cause); - } -} -codeToErrorMap.set(0x1774, InvalidTransferError); -nameToErrorMap.set('InvalidTransfer', InvalidTransferError); - -/** MissingPythOrBankAccount: Missing Pyth or Bank account */ -export class MissingPythOrBankAccountError extends ProgramError { - override readonly name: string = 'MissingPythOrBankAccount'; - - readonly code: number = 0x1775; // 6005 - - constructor(program: Program, cause?: Error) { - super('Missing Pyth or Bank account', program, cause); - } -} -codeToErrorMap.set(0x1775, MissingPythOrBankAccountError); -nameToErrorMap.set('MissingPythOrBankAccount', MissingPythOrBankAccountError); - -/** MissingPythAccount: Missing Pyth account */ -export class MissingPythAccountError extends ProgramError { - override readonly name: string = 'MissingPythAccount'; - - readonly code: number = 0x1776; // 6006 - - constructor(program: Program, cause?: Error) { - super('Missing Pyth account', program, cause); - } -} -codeToErrorMap.set(0x1776, MissingPythAccountError); -nameToErrorMap.set('MissingPythAccount', MissingPythAccountError); - -/** InvalidOracleAccount: Invalid Pyth account */ -export class InvalidOracleAccountError extends ProgramError { - override readonly name: string = 'InvalidOracleAccount'; - - readonly code: number = 0x1777; // 6007 - - constructor(program: Program, cause?: Error) { - super('Invalid Pyth account', program, cause); - } -} -codeToErrorMap.set(0x1777, InvalidOracleAccountError); -nameToErrorMap.set('InvalidOracleAccount', InvalidOracleAccountError); - -/** MissingBankAccount: Missing Bank account */ -export class MissingBankAccountError extends ProgramError { - override readonly name: string = 'MissingBankAccount'; - - readonly code: number = 0x1778; // 6008 - - constructor(program: Program, cause?: Error) { - super('Missing Bank account', program, cause); - } -} -codeToErrorMap.set(0x1778, MissingBankAccountError); -nameToErrorMap.set('MissingBankAccount', MissingBankAccountError); - -/** InvalidBankAccount: Invalid Bank account */ -export class InvalidBankAccountError extends ProgramError { - override readonly name: string = 'InvalidBankAccount'; - - readonly code: number = 0x1779; // 6009 - - constructor(program: Program, cause?: Error) { - super('Invalid Bank account', program, cause); - } -} -codeToErrorMap.set(0x1779, InvalidBankAccountError); -nameToErrorMap.set('InvalidBankAccount', InvalidBankAccountError); - -/** BadAccountHealth: Bad account health */ -export class BadAccountHealthError extends ProgramError { - override readonly name: string = 'BadAccountHealth'; - - readonly code: number = 0x177a; // 6010 - - constructor(program: Program, cause?: Error) { - super('Bad account health', program, cause); - } -} -codeToErrorMap.set(0x177a, BadAccountHealthError); -nameToErrorMap.set('BadAccountHealth', BadAccountHealthError); - -/** LendingAccountBalanceSlotsFull: Lending account balance slots are full */ -export class LendingAccountBalanceSlotsFullError extends ProgramError { - override readonly name: string = 'LendingAccountBalanceSlotsFull'; - - readonly code: number = 0x177b; // 6011 - - constructor(program: Program, cause?: Error) { - super('Lending account balance slots are full', program, cause); - } -} -codeToErrorMap.set(0x177b, LendingAccountBalanceSlotsFullError); -nameToErrorMap.set( - 'LendingAccountBalanceSlotsFull', - LendingAccountBalanceSlotsFullError -); - -/** BankAlreadyExists: Bank already exists */ -export class BankAlreadyExistsError extends ProgramError { - override readonly name: string = 'BankAlreadyExists'; - - readonly code: number = 0x177c; // 6012 - - constructor(program: Program, cause?: Error) { - super('Bank already exists', program, cause); - } -} -codeToErrorMap.set(0x177c, BankAlreadyExistsError); -nameToErrorMap.set('BankAlreadyExists', BankAlreadyExistsError); - -/** IllegalLiquidation: Illegal liquidation */ -export class IllegalLiquidationError extends ProgramError { - override readonly name: string = 'IllegalLiquidation'; - - readonly code: number = 0x177d; // 6013 - - constructor(program: Program, cause?: Error) { - super('Illegal liquidation', program, cause); - } -} -codeToErrorMap.set(0x177d, IllegalLiquidationError); -nameToErrorMap.set('IllegalLiquidation', IllegalLiquidationError); - -/** AccountNotBankrupt: Account is not bankrupt */ -export class AccountNotBankruptError extends ProgramError { - override readonly name: string = 'AccountNotBankrupt'; - - readonly code: number = 0x177e; // 6014 - - constructor(program: Program, cause?: Error) { - super('Account is not bankrupt', program, cause); - } -} -codeToErrorMap.set(0x177e, AccountNotBankruptError); -nameToErrorMap.set('AccountNotBankrupt', AccountNotBankruptError); - -/** BalanceNotBadDebt: Account balance is not bad debt */ -export class BalanceNotBadDebtError extends ProgramError { - override readonly name: string = 'BalanceNotBadDebt'; - - readonly code: number = 0x177f; // 6015 - - constructor(program: Program, cause?: Error) { - super('Account balance is not bad debt', program, cause); - } -} -codeToErrorMap.set(0x177f, BalanceNotBadDebtError); -nameToErrorMap.set('BalanceNotBadDebt', BalanceNotBadDebtError); - -/** InvalidConfig: Invalid group config */ -export class InvalidConfigError extends ProgramError { - override readonly name: string = 'InvalidConfig'; - - readonly code: number = 0x1780; // 6016 - - constructor(program: Program, cause?: Error) { - super('Invalid group config', program, cause); - } -} -codeToErrorMap.set(0x1780, InvalidConfigError); -nameToErrorMap.set('InvalidConfig', InvalidConfigError); - -/** StaleOracle: Stale oracle data */ -export class StaleOracleError extends ProgramError { - override readonly name: string = 'StaleOracle'; - - readonly code: number = 0x1781; // 6017 - - constructor(program: Program, cause?: Error) { - super('Stale oracle data', program, cause); - } -} -codeToErrorMap.set(0x1781, StaleOracleError); -nameToErrorMap.set('StaleOracle', StaleOracleError); - -/** BankPaused: Bank paused */ -export class BankPausedError extends ProgramError { - override readonly name: string = 'BankPaused'; - - readonly code: number = 0x1782; // 6018 - - constructor(program: Program, cause?: Error) { - super('Bank paused', program, cause); - } -} -codeToErrorMap.set(0x1782, BankPausedError); -nameToErrorMap.set('BankPaused', BankPausedError); - -/** BankReduceOnly: Bank is ReduceOnly mode */ -export class BankReduceOnlyError extends ProgramError { - override readonly name: string = 'BankReduceOnly'; - - readonly code: number = 0x1783; // 6019 - - constructor(program: Program, cause?: Error) { - super('Bank is ReduceOnly mode', program, cause); - } -} -codeToErrorMap.set(0x1783, BankReduceOnlyError); -nameToErrorMap.set('BankReduceOnly', BankReduceOnlyError); - -/** BankAccoutNotFound: Bank is missing */ -export class BankAccoutNotFoundError extends ProgramError { - override readonly name: string = 'BankAccoutNotFound'; - - readonly code: number = 0x1784; // 6020 - - constructor(program: Program, cause?: Error) { - super('Bank is missing', program, cause); - } -} -codeToErrorMap.set(0x1784, BankAccoutNotFoundError); -nameToErrorMap.set('BankAccoutNotFound', BankAccoutNotFoundError); - -/** OperationDepositOnly: Operation is deposit-only */ -export class OperationDepositOnlyError extends ProgramError { - override readonly name: string = 'OperationDepositOnly'; - - readonly code: number = 0x1785; // 6021 - - constructor(program: Program, cause?: Error) { - super('Operation is deposit-only', program, cause); - } -} -codeToErrorMap.set(0x1785, OperationDepositOnlyError); -nameToErrorMap.set('OperationDepositOnly', OperationDepositOnlyError); - -/** OperationWithdrawOnly: Operation is withdraw-only */ -export class OperationWithdrawOnlyError extends ProgramError { - override readonly name: string = 'OperationWithdrawOnly'; - - readonly code: number = 0x1786; // 6022 - - constructor(program: Program, cause?: Error) { - super('Operation is withdraw-only', program, cause); - } -} -codeToErrorMap.set(0x1786, OperationWithdrawOnlyError); -nameToErrorMap.set('OperationWithdrawOnly', OperationWithdrawOnlyError); - -/** OperationBorrowOnly: Operation is borrow-only */ -export class OperationBorrowOnlyError extends ProgramError { - override readonly name: string = 'OperationBorrowOnly'; - - readonly code: number = 0x1787; // 6023 - - constructor(program: Program, cause?: Error) { - super('Operation is borrow-only', program, cause); - } -} -codeToErrorMap.set(0x1787, OperationBorrowOnlyError); -nameToErrorMap.set('OperationBorrowOnly', OperationBorrowOnlyError); - -/** OperationRepayOnly: Operation is repay-only */ -export class OperationRepayOnlyError extends ProgramError { - override readonly name: string = 'OperationRepayOnly'; - - readonly code: number = 0x1788; // 6024 - - constructor(program: Program, cause?: Error) { - super('Operation is repay-only', program, cause); - } -} -codeToErrorMap.set(0x1788, OperationRepayOnlyError); -nameToErrorMap.set('OperationRepayOnly', OperationRepayOnlyError); - -/** NoAssetFound: No asset found */ -export class NoAssetFoundError extends ProgramError { - override readonly name: string = 'NoAssetFound'; - - readonly code: number = 0x1789; // 6025 - - constructor(program: Program, cause?: Error) { - super('No asset found', program, cause); - } -} -codeToErrorMap.set(0x1789, NoAssetFoundError); -nameToErrorMap.set('NoAssetFound', NoAssetFoundError); - -/** NoLiabilityFound: No liability found */ -export class NoLiabilityFoundError extends ProgramError { - override readonly name: string = 'NoLiabilityFound'; - - readonly code: number = 0x178a; // 6026 - - constructor(program: Program, cause?: Error) { - super('No liability found', program, cause); - } -} -codeToErrorMap.set(0x178a, NoLiabilityFoundError); -nameToErrorMap.set('NoLiabilityFound', NoLiabilityFoundError); - -/** InvalidOracleSetup: Invalid oracle setup */ -export class InvalidOracleSetupError extends ProgramError { - override readonly name: string = 'InvalidOracleSetup'; - - readonly code: number = 0x178b; // 6027 - - constructor(program: Program, cause?: Error) { - super('Invalid oracle setup', program, cause); - } -} -codeToErrorMap.set(0x178b, InvalidOracleSetupError); -nameToErrorMap.set('InvalidOracleSetup', InvalidOracleSetupError); - -/** IllegalUtilizationRatio: Invalid bank utilization ratio */ -export class IllegalUtilizationRatioError extends ProgramError { - override readonly name: string = 'IllegalUtilizationRatio'; - - readonly code: number = 0x178c; // 6028 - - constructor(program: Program, cause?: Error) { - super('Invalid bank utilization ratio', program, cause); - } -} -codeToErrorMap.set(0x178c, IllegalUtilizationRatioError); -nameToErrorMap.set('IllegalUtilizationRatio', IllegalUtilizationRatioError); - -/** BankLiabilityCapacityExceeded: Bank borrow cap exceeded */ -export class BankLiabilityCapacityExceededError extends ProgramError { - override readonly name: string = 'BankLiabilityCapacityExceeded'; - - readonly code: number = 0x178d; // 6029 - - constructor(program: Program, cause?: Error) { - super('Bank borrow cap exceeded', program, cause); - } -} -codeToErrorMap.set(0x178d, BankLiabilityCapacityExceededError); -nameToErrorMap.set( - 'BankLiabilityCapacityExceeded', - BankLiabilityCapacityExceededError -); - -/** InvalidPrice: Invalid Price */ -export class InvalidPriceError extends ProgramError { - override readonly name: string = 'InvalidPrice'; - - readonly code: number = 0x178e; // 6030 - - constructor(program: Program, cause?: Error) { - super('Invalid Price', program, cause); - } -} -codeToErrorMap.set(0x178e, InvalidPriceError); -nameToErrorMap.set('InvalidPrice', InvalidPriceError); - -/** IsolatedAccountIllegalState: Account can have only one liablity when account is under isolated risk */ -export class IsolatedAccountIllegalStateError extends ProgramError { - override readonly name: string = 'IsolatedAccountIllegalState'; - - readonly code: number = 0x178f; // 6031 - - constructor(program: Program, cause?: Error) { - super( - 'Account can have only one liablity when account is under isolated risk', - program, - cause - ); - } -} -codeToErrorMap.set(0x178f, IsolatedAccountIllegalStateError); -nameToErrorMap.set( - 'IsolatedAccountIllegalState', - IsolatedAccountIllegalStateError -); - -/** EmissionsAlreadySetup: Emissions already setup */ -export class EmissionsAlreadySetupError extends ProgramError { - override readonly name: string = 'EmissionsAlreadySetup'; - - readonly code: number = 0x1790; // 6032 - - constructor(program: Program, cause?: Error) { - super('Emissions already setup', program, cause); - } -} -codeToErrorMap.set(0x1790, EmissionsAlreadySetupError); -nameToErrorMap.set('EmissionsAlreadySetup', EmissionsAlreadySetupError); - -/** OracleNotSetup: Oracle is not set */ -export class OracleNotSetupError extends ProgramError { - override readonly name: string = 'OracleNotSetup'; - - readonly code: number = 0x1791; // 6033 - - constructor(program: Program, cause?: Error) { - super('Oracle is not set', program, cause); - } -} -codeToErrorMap.set(0x1791, OracleNotSetupError); -nameToErrorMap.set('OracleNotSetup', OracleNotSetupError); - -/** InvalidSwitchboardDecimalConversion: Invalid swithcboard decimal conversion */ -export class InvalidSwitchboardDecimalConversionError extends ProgramError { - override readonly name: string = 'InvalidSwitchboardDecimalConversion'; - - readonly code: number = 0x1792; // 6034 - - constructor(program: Program, cause?: Error) { - super('Invalid swithcboard decimal conversion', program, cause); - } -} -codeToErrorMap.set(0x1792, InvalidSwitchboardDecimalConversionError); -nameToErrorMap.set( - 'InvalidSwitchboardDecimalConversion', - InvalidSwitchboardDecimalConversionError -); - -/** CannotCloseOutstandingEmissions: Cannot close balance because of outstanding emissions */ -export class CannotCloseOutstandingEmissionsError extends ProgramError { - override readonly name: string = 'CannotCloseOutstandingEmissions'; - - readonly code: number = 0x1793; // 6035 - - constructor(program: Program, cause?: Error) { - super( - 'Cannot close balance because of outstanding emissions', - program, - cause - ); - } -} -codeToErrorMap.set(0x1793, CannotCloseOutstandingEmissionsError); -nameToErrorMap.set( - 'CannotCloseOutstandingEmissions', - CannotCloseOutstandingEmissionsError -); - -/** EmissionsUpdateError: Update emissions error */ -export class EmissionsUpdateErrorError extends ProgramError { - override readonly name: string = 'EmissionsUpdateError'; - - readonly code: number = 0x1794; // 6036 - - constructor(program: Program, cause?: Error) { - super('Update emissions error', program, cause); - } -} -codeToErrorMap.set(0x1794, EmissionsUpdateErrorError); -nameToErrorMap.set('EmissionsUpdateError', EmissionsUpdateErrorError); - -/** AccountDisabled: Account disabled */ -export class AccountDisabledError extends ProgramError { - override readonly name: string = 'AccountDisabled'; - - readonly code: number = 0x1795; // 6037 - - constructor(program: Program, cause?: Error) { - super('Account disabled', program, cause); - } -} -codeToErrorMap.set(0x1795, AccountDisabledError); -nameToErrorMap.set('AccountDisabled', AccountDisabledError); - -/** AccountTempActiveBalanceLimitExceeded: Account can't temporarily open 3 balances, please close a balance first */ -export class AccountTempActiveBalanceLimitExceededError extends ProgramError { - override readonly name: string = 'AccountTempActiveBalanceLimitExceeded'; - - readonly code: number = 0x1796; // 6038 - - constructor(program: Program, cause?: Error) { - super( - "Account can't temporarily open 3 balances, please close a balance first", - program, - cause - ); - } -} -codeToErrorMap.set(0x1796, AccountTempActiveBalanceLimitExceededError); -nameToErrorMap.set( - 'AccountTempActiveBalanceLimitExceeded', - AccountTempActiveBalanceLimitExceededError -); - -/** AccountInFlashloan: Illegal action during flashloan */ -export class AccountInFlashloanError extends ProgramError { - override readonly name: string = 'AccountInFlashloan'; - - readonly code: number = 0x1797; // 6039 - - constructor(program: Program, cause?: Error) { - super('Illegal action during flashloan', program, cause); - } -} -codeToErrorMap.set(0x1797, AccountInFlashloanError); -nameToErrorMap.set('AccountInFlashloan', AccountInFlashloanError); - -/** IllegalFlashloan: Illegal flashloan */ -export class IllegalFlashloanError extends ProgramError { - override readonly name: string = 'IllegalFlashloan'; - - readonly code: number = 0x1798; // 6040 - - constructor(program: Program, cause?: Error) { - super('Illegal flashloan', program, cause); - } -} -codeToErrorMap.set(0x1798, IllegalFlashloanError); -nameToErrorMap.set('IllegalFlashloan', IllegalFlashloanError); - -/** IllegalFlag: Illegal flag */ -export class IllegalFlagError extends ProgramError { - override readonly name: string = 'IllegalFlag'; - - readonly code: number = 0x1799; // 6041 - - constructor(program: Program, cause?: Error) { - super('Illegal flag', program, cause); - } -} -codeToErrorMap.set(0x1799, IllegalFlagError); -nameToErrorMap.set('IllegalFlag', IllegalFlagError); - -/** IllegalBalanceState: Illegal balance state */ -export class IllegalBalanceStateError extends ProgramError { - override readonly name: string = 'IllegalBalanceState'; - - readonly code: number = 0x179a; // 6042 - - constructor(program: Program, cause?: Error) { - super('Illegal balance state', program, cause); - } -} -codeToErrorMap.set(0x179a, IllegalBalanceStateError); -nameToErrorMap.set('IllegalBalanceState', IllegalBalanceStateError); - -/** IllegalAccountAuthorityTransfer: Illegal account authority transfer */ -export class IllegalAccountAuthorityTransferError extends ProgramError { - override readonly name: string = 'IllegalAccountAuthorityTransfer'; - - readonly code: number = 0x179b; // 6043 - - constructor(program: Program, cause?: Error) { - super('Illegal account authority transfer', program, cause); - } -} -codeToErrorMap.set(0x179b, IllegalAccountAuthorityTransferError); -nameToErrorMap.set( - 'IllegalAccountAuthorityTransfer', - IllegalAccountAuthorityTransferError -); - -/** - * Attempts to resolve a custom program error from the provided error code. - * @category Errors - */ -export function getMarginfiErrorFromCode( - code: number, - program: Program, - cause?: Error -): ProgramError | null { - const constructor = codeToErrorMap.get(code); - return constructor ? new constructor(program, cause) : null; -} - -/** - * Attempts to resolve a custom program error from the provided error name, i.e. 'Unauthorized'. - * @category Errors - */ -export function getMarginfiErrorFromName( - name: string, - program: Program, - cause?: Error -): ProgramError | null { - const constructor = nameToErrorMap.get(name); - return constructor ? new constructor(program, cause) : null; -} diff --git a/solauto-sdk/src/services/flashLoans/flProviderAggregator.ts b/solauto-sdk/src/services/flashLoans/flProviderAggregator.ts new file mode 100644 index 00000000..781850ad --- /dev/null +++ b/solauto-sdk/src/services/flashLoans/flProviderAggregator.ts @@ -0,0 +1,87 @@ +import { PublicKey } from "@solana/web3.js"; +import { Signer, TransactionBuilder, Umi } from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { FlProviderBase } from "./flProviderBase"; +import { FlashLoanDetails, ProgramEnv } from "../../types"; +import { TokenType } from "../../generated"; +import { MarginfiFlProvider } from "./marginfiFlProvider"; + +export class FlProviderAggregator extends FlProviderBase { + private marginfiFlProvider!: MarginfiFlProvider; + + constructor( + umi: Umi, + signer: Signer, + authority: PublicKey, + supplyMint: PublicKey, + debtMint: PublicKey, + programEnv?: ProgramEnv + ) { + super(umi, signer, authority, supplyMint, debtMint, programEnv); + this.marginfiFlProvider = new MarginfiFlProvider( + umi, + signer, + authority, + supplyMint, + debtMint, + programEnv + ); + } + + async initialize() { + // TODO: LP + // Once we have more than one, set the right fl provider for each liquidity source + await this.marginfiFlProvider.initialize(); + } + + async flAccountPrereqIxs(): Promise { + return await this.marginfiFlProvider.initializeIMfiAccounts(); + } + + otherSigners(): Signer[] { + // TODO: LP + return [...this.flSigners, ...this.marginfiFlProvider.otherSigners()]; + } + + lutAccountsToAdd(): PublicKey[] { + return toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority) + ? [ + ...super.lutAccountsToAdd(), + ...this.marginfiFlProvider.lutAccountsToAdd(), + ] + : []; + } + + private flProvider(source: TokenType): FlProviderBase { + // TODO: LP + return this.marginfiFlProvider; + } + + liquiditySource(source: TokenType): PublicKey { + return this.flProvider(source).liquiditySource(source); + } + + liquidityAvailable(source: TokenType): bigint { + return this.flProvider(source).liquidityAvailable(source); + } + + flFeeBps(source: TokenType, signerFlashLoan?: boolean): number { + return this.flProvider(source).flFeeBps(source, signerFlashLoan); + } + + flashBorrow( + flashLoan: FlashLoanDetails, + destTokenAccount: PublicKey + ): TransactionBuilder { + return this.flProvider(flashLoan.liquiditySource).flashBorrow( + flashLoan, + destTokenAccount + ); + } + + flashRepay(flashLoan: FlashLoanDetails): TransactionBuilder { + return this.flProvider(flashLoan.liquiditySource).flashRepay( + flashLoan + ); + } +} diff --git a/solauto-sdk/src/services/flashLoans/flProviderBase.ts b/solauto-sdk/src/services/flashLoans/flProviderBase.ts new file mode 100644 index 00000000..df7e6bef --- /dev/null +++ b/solauto-sdk/src/services/flashLoans/flProviderBase.ts @@ -0,0 +1,92 @@ +import { PublicKey } from "@solana/web3.js"; +import { + Signer, + transactionBuilder, + TransactionBuilder, + Umi, +} from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { + fromBaseUnit, + getTokenAccount, + safeGetPrice, + splTokenTransferUmiIx, + tokenInfo, +} from "../../utils"; +import { TokenType } from "../../generated"; +import { FlashLoanDetails, ProgramEnv } from "../../types"; + +export abstract class FlProviderBase { + protected flSigners: Signer[] = []; + + constructor( + protected umi: Umi, + protected signer: Signer, + protected authority: PublicKey, + protected supplyMint: PublicKey, + protected debtMint: PublicKey, + protected programEnv: ProgramEnv = "Prod" + ) {} + + abstract initialize(): Promise; + + lutAccountsToAdd(): PublicKey[] { + return []; + } + + otherSigners(): Signer[] { + return this.flSigners; + } + + mint(source: TokenType) { + return source === TokenType.Supply ? this.supplyMint : this.debtMint; + } + + abstract liquidityAvailable(source: TokenType): bigint; + liquidityAvailableUsd(source: TokenType): number { + return ( + fromBaseUnit( + this.liquidityAvailable(source), + tokenInfo(this.mint(source)).decimals + ) * (safeGetPrice(this.mint(source)) ?? 0) + ); + } + + abstract liquiditySource(source: TokenType): PublicKey; + + abstract flFeeBps(source: TokenType, signerFlashLoan?: boolean): number; + abstract flashBorrow( + flashLoan: FlashLoanDetails, + destTokenAccount: PublicKey + ): TransactionBuilder; + abstract flashRepay(flashLoan: FlashLoanDetails): TransactionBuilder; + + protected signerFlashBorrow( + flashLoan: FlashLoanDetails, + destTokenAccount: PublicKey + ): TransactionBuilder { + if ( + !destTokenAccount.equals( + getTokenAccount( + toWeb3JsPublicKey(this.signer.publicKey), + flashLoan.mint + ) + ) + ) { + return transactionBuilder().add( + splTokenTransferUmiIx( + this.signer, + getTokenAccount( + toWeb3JsPublicKey(this.signer.publicKey), + flashLoan.mint + ), + destTokenAccount, + toWeb3JsPublicKey(this.signer.publicKey), + flashLoan.baseUnitAmount + ) + ); + } else { + return transactionBuilder(); + } + } +} diff --git a/solauto-sdk/src/services/flashLoans/index.ts b/solauto-sdk/src/services/flashLoans/index.ts new file mode 100644 index 00000000..8cb35285 --- /dev/null +++ b/solauto-sdk/src/services/flashLoans/index.ts @@ -0,0 +1,3 @@ +export * from "./flProviderAggregator"; +export * from "./flProviderBase"; +export * from "./marginfiFlProvider"; diff --git a/solauto-sdk/src/services/flashLoans/marginfiFlProvider.ts b/solauto-sdk/src/services/flashLoans/marginfiFlProvider.ts new file mode 100644 index 00000000..f7fdb0a7 --- /dev/null +++ b/solauto-sdk/src/services/flashLoans/marginfiFlProvider.ts @@ -0,0 +1,364 @@ +import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + AccountMeta, + createSignerFromKeypair, + publicKey, + Signer, + transactionBuilder, + TransactionBuilder, +} from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { getMarginfiAccounts } from "../../constants"; +import { + Bank, + lendingAccountBorrow, + lendingAccountCloseBalance, + lendingAccountEndFlashloan, + lendingAccountRepay, + lendingAccountStartFlashloan, + MarginfiAccount, + marginfiAccountInitialize, + safeFetchAllBank, +} from "../../externalSdks/marginfi"; +import { FlProviderBase } from "./flProviderBase"; +import { + bytesToI80F48, + composeRemainingAccounts, + consoleLog, + fetchTokenPrices, + findMarginfiAccounts, + fromBaseUnit, + getBankLiquidityAvailableBaseUnit, + getEmptyMarginfiAccountsByAuthority, + getMarginfiPriceOracle, + getRemainingAccountsForMarginfiHealthCheck, + getTokenAccount, + rpcAccountCreated, + safeGetPrice, + toBps, + tokenInfo, +} from "../../utils"; +import { FlashLoanDetails } from "../../types"; +import { TokenType } from "../../generated"; + +interface IMFIAccount { + signer?: Signer; + accountPk: PublicKey; + accountData?: MarginfiAccount; +} + +export class MarginfiFlProvider extends FlProviderBase { + private existingMarginfiAccounts!: MarginfiAccount[]; + private supplyBankLiquiditySource!: Bank; + private debtBankLiquiditySource!: Bank; + + private supplyImfiAccount!: IMFIAccount; + private debtImfiAccount!: IMFIAccount; + private supplyRemainingAccounts!: AccountMeta[]; + private debtRemainingAccounts!: AccountMeta[]; + + async initialize() { + await this.setAvailableBanks(); + this.existingMarginfiAccounts = await getEmptyMarginfiAccountsByAuthority( + this.umi, + toWeb3JsPublicKey(this.signer.publicKey) + ); + if ( + this.liquidityBank(TokenType.Supply).group.toString() !== + this.liquidityBank(TokenType.Debt).group.toString() + ) { + await this.setIntermediaryAccount([TokenType.Supply]); + await this.setIntermediaryAccount([TokenType.Debt]); + } else { + await this.setIntermediaryAccount([TokenType.Supply, TokenType.Debt]); + } + } + + private async setAvailableBanks() { + const bankAccounts = getMarginfiAccounts(this.programEnv).bankAccounts; + + const availableBanks: string[] = []; + const checkIfUsable = (group: string, mint: string) => { + if (Object.keys(bankAccounts[group]).includes(mint)) { + availableBanks.push(bankAccounts[group][mint].bank); + } + }; + + for (const group of Object.keys(bankAccounts)) { + checkIfUsable(group, this.supplyMint.toString()); + checkIfUsable(group, this.debtMint.toString()); + } + + const banks = await safeFetchAllBank( + this.umi, + availableBanks.map((x) => publicKey(x)) + ); + + if (!safeGetPrice(this.supplyMint) || !safeGetPrice(this.debtMint)) { + await fetchTokenPrices([this.supplyMint, this.debtMint]); + } + + const mapBanksAndBalances = (mint: PublicKey) => + banks + .filter((x) => toWeb3JsPublicKey(x.mint).equals(mint)) + .map((x) => { + return [ + fromBaseUnit( + getBankLiquidityAvailableBaseUnit(x, false), + tokenInfo(mint).decimals + ) * safeGetPrice(mint)!, + x, + ] as const; + }) + .sort((a, b) => b[0] - a[0]); + + const supplyBanks = mapBanksAndBalances(this.supplyMint); + const debtBanks = mapBanksAndBalances(this.debtMint); + + this.supplyBankLiquiditySource = supplyBanks[0][1]; + this.debtBankLiquiditySource = debtBanks[0][1]; + } + + private async setIntermediaryAccount(sources: TokenType[]) { + const compatibleMarginfiAccounts = this.existingMarginfiAccounts.filter( + (x) => x.group.toString() == this.liquidityBank(sources[0]).group + ); + + const signer = + compatibleMarginfiAccounts.length > 0 + ? undefined + : createSignerFromKeypair(this.umi, this.umi.eddsa.generateKeypair()); + const accountPk = + compatibleMarginfiAccounts.length > 0 + ? toWeb3JsPublicKey(compatibleMarginfiAccounts[0].publicKey) + : toWeb3JsPublicKey(signer!.publicKey); + const accountData = + compatibleMarginfiAccounts.length > 0 + ? compatibleMarginfiAccounts[0] + : undefined; + + if (signer) { + this.flSigners.push(signer); + } + + const remainingAccounts = accountData + ? ( + await Promise.all( + accountData.lendingAccount.balances.map((balance) => + getRemainingAccountsForMarginfiHealthCheck(this.umi, balance) + ) + ) + ).flat() + : []; + + for (const s of sources) { + const data: IMFIAccount = { + signer, + accountPk, + accountData, + }; + + const supply = s === TokenType.Supply; + if (supply) { + this.supplyImfiAccount = data; + this.supplyRemainingAccounts = remainingAccounts; + } else { + this.debtImfiAccount = data; + this.debtRemainingAccounts = remainingAccounts; + } + consoleLog( + `${supply ? "Supply" : "Debt"} iMfi account:`, + accountPk.toString() + ); + } + } + + async initializeIMfiAccounts(): Promise { + const supplyImfiAccount = this.iMfiAccount(TokenType.Supply); + const debtImfiAccount = this.iMfiAccount(TokenType.Debt); + + const [supplyImfiRpcAccount, debtImfiRpcAccount] = + await this.umi.rpc.getAccounts([ + publicKey(supplyImfiAccount.accountPk), + publicKey(debtImfiAccount.accountPk), + ]); + + let tx = transactionBuilder(); + + if (!rpcAccountCreated(supplyImfiRpcAccount)) { + tx = tx.add( + marginfiAccountInitialize(this.umi, { + marginfiAccount: supplyImfiAccount.signer!, + marginfiGroup: this.supplyBankLiquiditySource.group, + authority: this.signer, + feePayer: this.signer, + }) + ); + } + + if ( + supplyImfiAccount.accountPk.toString() !== + debtImfiAccount.accountPk.toString() && + !rpcAccountCreated(debtImfiRpcAccount) + ) { + tx = tx.add( + marginfiAccountInitialize(this.umi, { + marginfiAccount: debtImfiAccount.signer!, + marginfiGroup: this.debtBankLiquiditySource.group, + authority: this.signer, + feePayer: this.signer, + }) + ); + } + + return tx; + } + + lutAccountsToAdd(): PublicKey[] { + return [ + ...super.lutAccountsToAdd(), + ...Array.from( + new Set([ + this.iMfiAccount(TokenType.Supply).accountPk.toString(), + this.iMfiAccount(TokenType.Debt).accountPk.toString(), + ]) + ).map((x) => new PublicKey(x)), + ]; + } + + liquiditySource(source: TokenType): PublicKey { + return toWeb3JsPublicKey(this.liquidityBank(source).publicKey); + } + + private liquidityBank(source: TokenType): Bank { + return source === TokenType.Supply + ? this.supplyBankLiquiditySource + : this.debtBankLiquiditySource; + } + + private iMfiAccount(source: TokenType): IMFIAccount { + return source === TokenType.Supply + ? this.supplyImfiAccount + : this.debtImfiAccount; + } + + liquidityAvailable(source: TokenType): bigint { + return getBankLiquidityAvailableBaseUnit(this.liquidityBank(source), false); + } + + flFeeBps(source: TokenType, signerFlashLoan?: boolean): number { + if (signerFlashLoan) { + return 0; + } + + return toBps( + bytesToI80F48( + this.liquidityBank(source).config.interestRateConfig + .protocolOriginationFee.value + ), + "Ceil" + ); + } + + flashBorrow( + flashLoan: FlashLoanDetails, + destTokenAccount: PublicKey + ): TransactionBuilder { + if (flashLoan.signerFlashLoan) { + return this.signerFlashBorrow(flashLoan, destTokenAccount); + } + + const bank = this.liquidityBank(flashLoan.liquiditySource); + const associatedBankAccs = findMarginfiAccounts( + toWeb3JsPublicKey(bank.publicKey) + ); + const iMfiAccount = this.iMfiAccount(flashLoan.liquiditySource)!; + + return transactionBuilder() + .add( + lendingAccountStartFlashloan(this.umi, { + endIndex: 0, // We set this after building the transaction + ixsSysvar: publicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + marginfiAccount: publicKey(iMfiAccount.accountPk), + signer: this.signer, + }) + ) + .add( + lendingAccountBorrow(this.umi, { + amount: flashLoan.baseUnitAmount, + bank: publicKey(bank), + bankLiquidityVault: publicKey(associatedBankAccs.liquidityVault), + bankLiquidityVaultAuthority: publicKey( + associatedBankAccs.vaultAuthority + ), + destinationTokenAccount: publicKey(destTokenAccount), + marginfiAccount: publicKey(iMfiAccount.accountPk), + marginfiGroup: this.liquidityBank(flashLoan.liquiditySource).group, + signer: this.signer, + }) + ); + } + + flashRepay(flashLoan: FlashLoanDetails): TransactionBuilder { + if (flashLoan.signerFlashLoan) { + return transactionBuilder(); + } + + const bank = this.liquidityBank(flashLoan.liquiditySource); + const associatedBankAccs = findMarginfiAccounts( + toWeb3JsPublicKey(bank.publicKey) + ); + const marginfiGroup = toWeb3JsPublicKey( + this.liquidityBank(flashLoan.liquiditySource).group + ); + const iMfiAccount = this.iMfiAccount(flashLoan.liquiditySource)!; + + const remainingAccounts: AccountMeta[] = + flashLoan.liquiditySource === TokenType.Supply + ? this.supplyRemainingAccounts + : this.debtRemainingAccounts; + let iMfiAccountHadPrevFlBalance = remainingAccounts.find( + (x) => x.pubkey.toString() === bank.publicKey.toString() + ); + + return transactionBuilder() + .add( + lendingAccountRepay(this.umi, { + amount: flashLoan.baseUnitAmount, + repayAll: !iMfiAccountHadPrevFlBalance, + bank: bank.publicKey, + bankLiquidityVault: publicKey(associatedBankAccs.liquidityVault), + marginfiAccount: publicKey(iMfiAccount.accountPk), + marginfiGroup: publicKey(marginfiGroup), + signer: this.signer, + signerTokenAccount: publicKey( + getTokenAccount( + toWeb3JsPublicKey(this.signer.publicKey), + flashLoan.mint + ) + ), + }) + ) + .add( + lendingAccountEndFlashloan(this.umi, { + marginfiAccount: publicKey(iMfiAccount.accountPk), + signer: this.signer, + }).addRemainingAccounts(composeRemainingAccounts(remainingAccounts)) + ); + } + + closeBalance( + marginfiAccount: PublicKey, + bank: PublicKey, + marginfiGroup: PublicKey + ) { + return transactionBuilder().add( + lendingAccountCloseBalance(this.umi, { + signer: this.signer, + marginfiAccount: publicKey(marginfiAccount), + bank: publicKey(bank), + marginfiGroup: publicKey(marginfiGroup), + }) + ); + } +} diff --git a/solauto-sdk/src/services/index.ts b/solauto-sdk/src/services/index.ts new file mode 100644 index 00000000..3f508f45 --- /dev/null +++ b/solauto-sdk/src/services/index.ts @@ -0,0 +1,5 @@ +export * from "./flashLoans"; +export * from "./rebalance"; +export * from "./solauto"; +export * from "./swap"; +export * from "./transactions"; diff --git a/solauto-sdk/src/services/rebalance/index.ts b/solauto-sdk/src/services/rebalance/index.ts new file mode 100644 index 00000000..2f84a7b2 --- /dev/null +++ b/solauto-sdk/src/services/rebalance/index.ts @@ -0,0 +1,3 @@ +export * from "./rebalanceTxBuilder"; +export * from "./rebalanceValues"; +export * from "./solautoFees"; diff --git a/solauto-sdk/src/services/rebalance/rebalanceSwapManager.ts b/solauto-sdk/src/services/rebalance/rebalanceSwapManager.ts new file mode 100644 index 00000000..a1e576d7 --- /dev/null +++ b/solauto-sdk/src/services/rebalance/rebalanceSwapManager.ts @@ -0,0 +1,274 @@ +import { QuoteResponse } from "@jup-ag/api"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { FlashLoanRequirements } from "../../types"; +import { SolautoClient } from "../solauto"; +import { JupSwapManager, SwapParams, SwapInput } from "../swap"; +import { applyDebtAdjustmentUsd, RebalanceValues } from "./rebalanceValues"; +import { PriceType, RebalanceDirection, TokenType } from "../../generated"; +import { + consoleLog, + fromBaseUnit, + fromBps, + getLiqUtilzationRateBps, + safeGetPrice, + toBaseUnit, + tokenInfo, +} from "../../utils"; +import { SolautoFeesBps } from "./solautoFees"; + +export class RebalanceSwapManager { + public swapParams!: SwapParams; + public swapQuote?: QuoteResponse; + public flBorrowAmount?: bigint; + + private jupSwapManager!: JupSwapManager; + private solautoFeeBps!: number; + + constructor( + private client: SolautoClient, + private values: RebalanceValues, + private flRequirements?: FlashLoanRequirements, + private targetLiqUtilizationRateBps?: number, + private priceType?: PriceType + ) { + this.jupSwapManager = new JupSwapManager(client.signer, true); + this.solautoFeeBps = SolautoFeesBps.create( + this.client.isReferred, + this.targetLiqUtilizationRateBps, + this.client.pos.netWorthUsd(this.priceType) + ).getSolautoFeesBps(values.rebalanceDirection).total; + } + + private isBoost() { + return this.values.rebalanceDirection === RebalanceDirection.Boost; + } + + private usdToSwap() { + return Math.abs(this.values.debtAdjustmentUsd); + } + + private swapDetails() { + const input = this.isBoost() + ? this.client.pos.state.debt + : this.client.pos.state.supply; + const output = this.isBoost() + ? this.client.pos.state.supply + : this.client.pos.state.debt; + + const inputPrice = safeGetPrice( + toWeb3JsPublicKey(input.mint), + this.priceType + )!; + const outputPrice = safeGetPrice( + toWeb3JsPublicKey(output.mint), + this.priceType + )!; + + const supplyPrice = this.client.pos.supplyPrice(this.priceType)!; + const debtPrice = this.client.pos.debtPrice(this.priceType)!; + const biasedInputPrice = this.isBoost() ? debtPrice : supplyPrice; + const biasedOutputPrice = this.isBoost() ? supplyPrice : debtPrice; + + let inputAmount = toBaseUnit( + this.usdToSwap() / biasedInputPrice!, + input.decimals + ); + + return { + inputAmount, + input, + inputPrice, + biasedInputPrice, + output, + outputPrice, + biasedOutputPrice, + }; + } + + private postRebalanceLiqUtilizationRateBps( + swapOutputAmountBaseUnit?: bigint, + swapInputAmountBaseUnit?: bigint + ) { + let supplyUsd = this.client.pos.supplyUsd(this.priceType); + let debtUsd = this.client.pos.debtUsd(this.priceType); + // TODO: add token balance change + + const { input, biasedInputPrice, output, biasedOutputPrice } = + this.swapDetails(); + + const swapInputAmount = swapInputAmountBaseUnit + ? fromBaseUnit( + swapInputAmountBaseUnit, + tokenInfo(toWeb3JsPublicKey(input.mint)).decimals + ) + : undefined; + + const swapOutputAmount = swapOutputAmountBaseUnit + ? fromBaseUnit( + swapOutputAmountBaseUnit, + tokenInfo(toWeb3JsPublicKey(output.mint)).decimals + ) + : undefined; + + const swapInputUsd = swapInputAmount + ? swapInputAmount * biasedInputPrice + : this.usdToSwap(); + + const swapOutputUsd = swapOutputAmount + ? swapOutputAmount * biasedOutputPrice + : this.usdToSwap(); + + const res = applyDebtAdjustmentUsd( + { + debtAdjustmentUsd: this.isBoost() ? swapInputUsd : swapInputUsd * -1, + debtAdjustmentUsdOutput: this.isBoost() + ? swapOutputUsd + : swapOutputUsd * -1, + }, + { supplyUsd, debtUsd }, + fromBps(this.client.pos.state.liqThresholdBps), + { + solauto: this.solautoFeeBps, + flashLoan: this.flRequirements?.flFeeBps ?? 0, + lpBorrow: this.client.pos.state.debt.borrowFeeBps, + } + ); + + return getLiqUtilzationRateBps( + res.newPos.supplyUsd, + res.newPos.debtUsd, + this.client.pos.state.liqThresholdBps ?? 0 + ); + } + + private async findSufficientQuote( + swapInput: SwapInput, + criteria: { + minOutputAmount?: bigint; + maxLiqUtilizationRateBps?: number; + } + ): Promise { + let swapQuote: QuoteResponse; + let insufficient: boolean = false; + + for (let i = 0; i < 20; i++) { + consoleLog("Finding sufficient quote..."); + swapQuote = await this.jupSwapManager.getQuote(swapInput); + + const outputAmount = parseInt(swapQuote.outAmount); + const postRebalanceRate = this.postRebalanceLiqUtilizationRateBps( + BigInt(outputAmount), + BigInt(parseInt(swapQuote.inAmount)) + ); + const exceedsMinOutput = criteria.minOutputAmount + ? outputAmount < Number(criteria.minOutputAmount) + : false; + const exceedsMaxRate = criteria.maxLiqUtilizationRateBps + ? postRebalanceRate > criteria.maxLiqUtilizationRateBps + : false; + insufficient = exceedsMinOutput || exceedsMaxRate; + + consoleLog(postRebalanceRate, criteria.maxLiqUtilizationRateBps); + if (insufficient) { + consoleLog("Insufficient swap quote:", swapQuote); + + const increment = 0.01 + i * 0.01; + swapInput.amount = this.bigIntWithIncrement( + swapInput.amount, + this.isBoost() ? increment * -1 : increment + ); + } else { + break; + } + } + + return swapQuote!; + } + + private bigIntWithIncrement(num: bigint, inc: number) { + return num + BigInt(Math.round(Number(num) * inc)); + } + + async setSwapParams(attemptNum: number) { + const rebalanceToZero = this.targetLiqUtilizationRateBps === 0; + let { input, output, biasedOutputPrice, inputAmount } = this.swapDetails(); + + const flashLoanRepayFromDebt = + !this.isBoost() && + this.flRequirements && + this.flRequirements.liquiditySource === TokenType.Debt; + + const exactOut = flashLoanRepayFromDebt; + const exactIn = !exactOut; + + const outputPaddingPct = + (exactOut ? fromBps(this.solautoFeeBps) : 0) + 0.0001; + let outputAmount = rebalanceToZero + ? output.amountUsed.baseUnit + + BigInt( + Math.round(Number(output.amountUsed.baseUnit) * outputPaddingPct) + ) + : toBaseUnit(this.usdToSwap() / biasedOutputPrice, output.decimals); + + if (exactIn && (rebalanceToZero || this.values.repayingCloseToMaxLtv)) { + inputAmount = this.bigIntWithIncrement(inputAmount, 0.005); + } + + const swapAmount = exactOut + ? this.flRequirements + ? this.bigIntWithIncrement( + outputAmount, + this.flRequirements.flFeeBps ?? 0 + ) + : outputAmount + : inputAmount; + + const inputMint = toWeb3JsPublicKey(input.mint); + const outputMint = toWeb3JsPublicKey(output.mint); + const swapInput: SwapInput = { + inputMint, + outputMint, + exactIn, + exactOut, + amount: swapAmount, + }; + consoleLog("Swap input:", swapInput); + + if (exactIn) { + this.swapQuote = await this.findSufficientQuote(swapInput, { + minOutputAmount: rebalanceToZero ? outputAmount : undefined, + maxLiqUtilizationRateBps: !rebalanceToZero + ? this.client.pos.maxBoostToBps + : undefined, + }); + } + + if (this.flRequirements) { + this.flBorrowAmount = exactOut + ? outputAmount + : this.swapQuote + ? BigInt(parseInt(this.swapQuote.inAmount)) + : inputAmount; + } + + this.swapParams = { + ...swapInput, + destinationWallet: exactOut + ? toWeb3JsPublicKey(this.client.signer.publicKey) + : this.client.pos.publicKey, + slippageIncFactor: 0.2 + attemptNum * 0.25, + }; + } + + async getSwapTxData() { + const { jupQuote, lookupTableAddresses, setupIx, swapIx } = + await this.jupSwapManager.getJupSwapTxData(this.swapParams); + + return { + swapQuote: jupQuote, + lookupTableAddresses, + setupIx, + swapIx, + }; + } +} diff --git a/solauto-sdk/src/services/rebalance/rebalanceTxBuilder.ts b/solauto-sdk/src/services/rebalance/rebalanceTxBuilder.ts new file mode 100644 index 00000000..e3be74db --- /dev/null +++ b/solauto-sdk/src/services/rebalance/rebalanceTxBuilder.ts @@ -0,0 +1,430 @@ +import { PublicKey } from "@solana/web3.js"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { transactionBuilder } from "@metaplex-foundation/umi"; +import { SolautoClient } from "../solauto"; +import { + FlashLoanRequirements, + RebalanceDetails, + TransactionItemInputs, +} from "../../types"; +import { + bytesToI80F48, + consoleLog, + fromBaseUnit, + fromBps, + getMaxLiqUtilizationRateBps, + getTokenAccount, + hasFirstRebalance, + hasLastRebalance, + isMarginfiPosition, + safeGetPrice, + tokenInfo, +} from "../../utils"; +import { + PositionTokenState, + PriceType, + RebalanceDirection, + RebalanceStep, + SolautoRebalanceType, + SwapType, + TokenBalanceChangeType, + TokenType, +} from "../../generated"; +import { + applyDebtAdjustmentUsd, + getRebalanceValues, + RebalanceValues, +} from "./rebalanceValues"; +import { SolautoFeesBps } from "./solautoFees"; +import { RebalanceSwapManager } from "./rebalanceSwapManager"; + +export class RebalanceTxBuilder { + private values!: RebalanceValues; + private rebalanceType!: SolautoRebalanceType; + private swapManager!: RebalanceSwapManager; + private flRequirements?: FlashLoanRequirements; + private priceType: PriceType = PriceType.Realtime; + + constructor( + private client: SolautoClient, + private targetLiqUtilizationRateBps?: number, + private optimizeSize?: boolean, + private bpsDistanceFromRebalance?: number + ) {} + + private shouldProceedWithRebalance() { + if (this.client.pos.selfManaged && this.targetLiqUtilizationRateBps === undefined) { + throw new Error( + "A target rate must be provided for self managed position rebalances" + ); + } + + return ( + this.client.pos.supplyUsd() > 0 && + (this.targetLiqUtilizationRateBps !== undefined || + this.client.pos.eligibleForRebalance(this.bpsDistanceFromRebalance)) + ); + } + + private getRebalanceValues() { + return getRebalanceValues( + this.client.pos, + this.priceType, + this.targetLiqUtilizationRateBps, + SolautoFeesBps.create( + this.client.isReferred, + this.targetLiqUtilizationRateBps, + this.client.pos.netWorthUsd(this.priceType) + ), + this.flRequirements?.flFeeBps ?? 0, + this.bpsDistanceFromRebalance + ); + } + + private getFlLiquiditySource( + attemptNum: number, + supplyLiquidityAvailable: bigint, + debtLiquidityAvailable: bigint + ): TokenType | undefined { + const debtAdjustmentUsd = Math.abs(this.values.debtAdjustmentUsd); + + const calcLiquidityAvailable = ( + liquidityAvailable: bigint, + tokenMint: PublicKey + ) => + fromBaseUnit(liquidityAvailable, tokenInfo(tokenMint).decimals) * + (safeGetPrice(tokenMint) ?? 0); + + const supplyLiquidityUsdAvailable = calcLiquidityAvailable( + supplyLiquidityAvailable, + this.client.pos.supplyMint + ); + const insufficientSupplyLiquidity = + debtAdjustmentUsd > supplyLiquidityUsdAvailable * 0.95; + + const debtLiquidityUsdAvailable = calcLiquidityAvailable( + debtLiquidityAvailable, + this.client.pos.debtMint + ); + const insufficientDebtLiquidity = + debtAdjustmentUsd > debtLiquidityUsdAvailable * 0.95; + + let useDebtLiquidity = + this.values.rebalanceDirection === RebalanceDirection.Boost || + insufficientSupplyLiquidity || + (attemptNum > 2 && + debtLiquidityUsdAvailable > supplyLiquidityUsdAvailable * 5); + + if (useDebtLiquidity) { + return !insufficientDebtLiquidity ? TokenType.Debt : undefined; + } else { + return !insufficientSupplyLiquidity ? TokenType.Supply : undefined; + } + } + + private intermediaryLiqUtilizationRateBps() { + if ( + this.client.pos.maxLtvPriceType !== PriceType.Ema || + this.priceType === PriceType.Ema || + this.values.rebalanceDirection === RebalanceDirection.Repay + ) { + return this.values.intermediaryLiqUtilizationRateBps; + } + + const fees = new SolautoFeesBps( + this.client.isReferred, + this.targetLiqUtilizationRateBps, + this.client.pos.netWorthUsd(PriceType.Realtime) + ); + + const { intermediaryLiqUtilizationRateBps } = applyDebtAdjustmentUsd( + { debtAdjustmentUsd: this.values.debtAdjustmentUsd }, + { + supplyUsd: this.client.pos.supplyUsd(PriceType.Ema), + debtUsd: this.client.pos.debtUsd(PriceType.Ema), + }, + fromBps(this.client.pos.state.liqThresholdBps), + { + solauto: fees.getSolautoFeesBps(this.values.rebalanceDirection).total, + lpBorrow: this.client.pos.state.debt.borrowFeeBps, + flashLoan: this.flRequirements?.flFeeBps ?? 0, + } + ); + + return intermediaryLiqUtilizationRateBps; + } + + private async flashLoanRequirements( + attemptNum: number + ): Promise { + const intermediaryLiqUtilizationRateBps = + this.intermediaryLiqUtilizationRateBps(); + const maxLtvRateBps = getMaxLiqUtilizationRateBps( + this.client.pos.state.maxLtvBps, + this.client.pos.state.liqThresholdBps, + 0.015 + ); + if (intermediaryLiqUtilizationRateBps < maxLtvRateBps) { + return undefined; + } + + const stdFlLiquiditySource = this.getFlLiquiditySource( + attemptNum, + this.client.flProvider.liquidityAvailable(TokenType.Supply), + this.client.flProvider.liquidityAvailable(TokenType.Debt) + ); + + if (stdFlLiquiditySource === undefined || this.optimizeSize) { + const { supplyBalance, debtBalance } = await this.client.signerBalances(); + const signerFlLiquiditySource = this.getFlLiquiditySource( + attemptNum, + supplyBalance, + debtBalance + ); + + if (signerFlLiquiditySource) { + return { + liquiditySource: signerFlLiquiditySource, + signerFlashLoan: true, + }; + } else { + throw new Error(`Insufficient liquidity to perform the transaction`); + } + } else { + return { + liquiditySource: stdFlLiquiditySource, + flFeeBps: this.client.flProvider.flFeeBps(stdFlLiquiditySource), + }; + } + } + + private getFlashLoanDetails() { + if (!this.flRequirements) { + throw new Error("Flash loan requirements data needed"); + } + + const boosting = + this.values.rebalanceDirection === RebalanceDirection.Boost; + const useDebtLiquidity = + this.flRequirements.liquiditySource === TokenType.Debt; + + let flashLoanToken: PositionTokenState | undefined = undefined; + if (boosting || useDebtLiquidity) { + flashLoanToken = this.client.pos.state.debt; + } else { + flashLoanToken = this.client.pos.state.supply; + } + + return { + ...this.flRequirements, + baseUnitAmount: this.swapManager.flBorrowAmount!, + mint: toWeb3JsPublicKey(flashLoanToken.mint), + }; + } + + private setRebalanceType() { + if (this.flRequirements) { + const tokenBalanceChangeType = this.values.tokenBalanceChange?.changeType; + const firstRebalanceTokenChanges = + tokenBalanceChangeType === TokenBalanceChangeType.PreSwapDeposit; + const lastRebalanceTokenChanges = [ + TokenBalanceChangeType.PostSwapDeposit, + TokenBalanceChangeType.PostRebalanceWithdrawDebtToken, + TokenBalanceChangeType.PostRebalanceWithdrawSupplyToken, + ].includes(tokenBalanceChangeType ?? TokenBalanceChangeType.None); + + const swapType = this.swapManager.swapParams.exactIn + ? SwapType.ExactIn + : SwapType.ExactOut; + + if ( + (firstRebalanceTokenChanges && swapType === SwapType.ExactIn) || + (lastRebalanceTokenChanges && swapType === SwapType.ExactOut) + ) { + this.rebalanceType = SolautoRebalanceType.DoubleRebalanceWithFL; + } else { + this.rebalanceType = + swapType === SwapType.ExactOut + ? SolautoRebalanceType.FLRebalanceThenSwap + : SolautoRebalanceType.FLSwapThenRebalance; + } + } else { + this.rebalanceType = SolautoRebalanceType.Regular; + } + } + + private getInitialRebalanceValues() { + let rebalanceValues = this.getRebalanceValues(); + if (!rebalanceValues) { + return undefined; + } + + if ( + !this.client.pos.rebalance.validRealtimePricesBoost( + rebalanceValues.debtAdjustmentUsd + ) + ) { + this.priceType = PriceType.Ema; + rebalanceValues = this.getRebalanceValues(); + if (!rebalanceValues) { + return undefined; + } + } + + return rebalanceValues; + } + + private async setRebalanceDetails(attemptNum: number): Promise { + const rebalanceValues = this.getInitialRebalanceValues(); + if (!rebalanceValues) { + return false; + } + this.values = rebalanceValues; + + this.flRequirements = await this.flashLoanRequirements(attemptNum); + if (this.flRequirements?.flFeeBps) { + this.values = this.getRebalanceValues()!; + } + + this.swapManager = new RebalanceSwapManager( + this.client, + this.values, + this.flRequirements, + this.targetLiqUtilizationRateBps, + this.priceType + ); + await this.swapManager.setSwapParams(attemptNum); + + this.setRebalanceType(); + return true; + } + + private async refreshBeforeRebalance() { + if ( + this.client.pos.selfManaged || + this.client.contextUpdates.supplyAdjustment > BigInt(0) || + this.client.contextUpdates.debtAdjustment > BigInt(0) || + !this.client.pos.exists + ) { + return false; + } + // Rebalance ix will already refresh internally if position is self managed + + const utilizationRateDiff = Math.abs( + await this.client.pos.utilizationRateBpsDrift(this.priceType) + ); + consoleLog("Liq utilization rate diff:", utilizationRateDiff); + + if (utilizationRateDiff >= 5) { + consoleLog("Refreshing before rebalance"); + return true; + } + + consoleLog("Not refreshing before rebalance"); + return false; + } + + private async assembleTransaction(): Promise { + const { swapQuote, lookupTableAddresses, setupIx, swapIx } = + await this.swapManager.getSwapTxData(); + + const flashLoanDetails = this.flRequirements + ? this.getFlashLoanDetails() + : undefined; + + let tx = transactionBuilder(); + + if (await this.refreshBeforeRebalance()) { + tx = tx.add(this.client.refreshIx(this.priceType)); + } + + const rebalanceDetails: RebalanceDetails = { + values: this.values, + rebalanceType: this.rebalanceType, + flashLoan: flashLoanDetails, + swapQuote, + targetLiqUtilizationRateBps: this.targetLiqUtilizationRateBps, + priceType: this.priceType, + }; + consoleLog("Rebalance details:", rebalanceDetails); + consoleLog( + "Prices:", + safeGetPrice(this.client.pos.supplyMint, this.priceType), + this.client.pos.supplyPrice(this.priceType), + safeGetPrice(this.client.pos.debtMint, this.priceType), + this.client.pos.debtPrice(this.priceType) + ); + + if (isMarginfiPosition(this.client.pos)) { + const supply = + this.values.endResult.supplyUsd * + bytesToI80F48(this.client.pos.supplyBank!.config.assetWeightInit.value); + const debt = + this.values.endResult.debtUsd * + bytesToI80F48( + this.client.pos.debtBank!.config.liabilityWeightInit.value + ); + consoleLog("Weighted values", supply, debt); + } + + const firstRebalance = this.client.rebalanceIx( + RebalanceStep.PreSwap, + rebalanceDetails + ); + const lastRebalance = this.client.rebalanceIx( + RebalanceStep.PostSwap, + rebalanceDetails + ); + + if (!flashLoanDetails) { + tx = tx.add([setupIx, firstRebalance, swapIx, lastRebalance]); + } else { + const exactOut = swapQuote.swapMode === "ExactOut"; + const addFirstRebalance = hasFirstRebalance(this.rebalanceType); + const addLastRebalance = hasLastRebalance(this.rebalanceType); + + const flashBorrowDest = exactOut + ? getTokenAccount( + this.client.pos.publicKey, + new PublicKey(swapQuote.outputMint) + ) + : getTokenAccount( + toWeb3JsPublicKey(this.client.signer.publicKey), + new PublicKey(swapQuote.inputMint) + ); + + consoleLog("Flash borrow dest:", flashBorrowDest.toString()); + tx = tx.add([ + setupIx, + this.client.flProvider.flashBorrow(flashLoanDetails, flashBorrowDest), + ...(addFirstRebalance ? [firstRebalance] : []), + swapIx, + ...(addLastRebalance ? [lastRebalance] : []), + this.client.flProvider.flashRepay(flashLoanDetails), + ]); + } + + return { + tx, + lookupTableAddresses, + }; + } + + public async buildRebalanceTx( + attemptNum: number + ): Promise { + await this.client.pos.refreshPositionState(); + + if (!this.shouldProceedWithRebalance()) { + this.client.log("Not eligible for a rebalance"); + return undefined; + } + + const proceed = await this.setRebalanceDetails(attemptNum); + if (!proceed) { + return undefined; + } + + return await this.assembleTransaction(); + } +} diff --git a/solauto-sdk/src/services/rebalance/rebalanceValues.ts b/solauto-sdk/src/services/rebalance/rebalanceValues.ts new file mode 100644 index 00000000..d188f20f --- /dev/null +++ b/solauto-sdk/src/services/rebalance/rebalanceValues.ts @@ -0,0 +1,253 @@ +import { + PriceType, + RebalanceDirection, + TokenBalanceChange, + TokenBalanceChangeType, +} from "../../generated"; +import { + fromBps, + getLiqUtilzationRateBps, + toBps, +} from "../../utils"; +import { SolautoPositionEx } from "../../solautoPosition"; +import { SolautoFeesBps } from "./solautoFees"; + +export interface PositionValues { + supplyUsd: number; + debtUsd: number; +} + +export interface DebtAdjustment { + debtAdjustmentUsd: number; + endResult: PositionValues; + intermediaryLiqUtilizationRateBps: number; +} + +export interface RebalanceFeesBps { + solauto: number; + lpBorrow: number; + flashLoan: number; +} + +interface ApplyDebtAdjustmentResult { + newPos: PositionValues; + intermediaryLiqUtilizationRateBps: number; +} + +export function applyDebtAdjustmentUsd( + adjustment: { debtAdjustmentUsd: number; debtAdjustmentUsdOutput?: number }, + pos: PositionValues, + liqThreshold: number, + fees?: RebalanceFeesBps +): ApplyDebtAdjustmentResult { + const newPos = { ...pos }; + const isBoost = adjustment.debtAdjustmentUsd > 0; + + if (!adjustment.debtAdjustmentUsdOutput) { + adjustment.debtAdjustmentUsdOutput = adjustment.debtAdjustmentUsd; + } + const daMinusSolautoFees = + adjustment.debtAdjustmentUsdOutput - + adjustment.debtAdjustmentUsdOutput * fromBps(fees?.solauto ?? 0); + + const daWithFlashLoan = + adjustment.debtAdjustmentUsd * (1.0 + fromBps(fees?.flashLoan ?? 0)); + + let intermediaryLiqUtilizationRateBps = 0; + if (isBoost) { + newPos.debtUsd += + daWithFlashLoan * fromBps(fees?.lpBorrow ?? 0) + daWithFlashLoan; + intermediaryLiqUtilizationRateBps = getLiqUtilzationRateBps( + newPos.supplyUsd, + newPos.debtUsd, + toBps(liqThreshold) + ); + newPos.supplyUsd += daMinusSolautoFees; + } else { + newPos.supplyUsd += daWithFlashLoan; + intermediaryLiqUtilizationRateBps = getLiqUtilzationRateBps( + newPos.supplyUsd, + newPos.debtUsd, + toBps(liqThreshold) + ); + newPos.debtUsd += daMinusSolautoFees; + } + + return { newPos, intermediaryLiqUtilizationRateBps }; +} + +export function getDebtAdjustment( + liqThresholdBps: number, + pos: PositionValues, + targetLiqUtilizationRateBps: number, + fees?: RebalanceFeesBps +): DebtAdjustment { + const isBoost = + getLiqUtilzationRateBps(pos.supplyUsd, pos.debtUsd, liqThresholdBps) < + targetLiqUtilizationRateBps; + const liqThreshold = fromBps(liqThresholdBps); + + const targetUtilizationRate = fromBps(targetLiqUtilizationRateBps); + const actualizedFee = 1.0 - fromBps(fees?.solauto ?? 0); + const flFee = fromBps(fees?.flashLoan ?? 0); + const lpBorrowFee = fromBps(fees?.lpBorrow ?? 0); + + const debtAdjustmentUsd = isBoost + ? (targetUtilizationRate * liqThreshold * pos.supplyUsd - pos.debtUsd) / + (1.0 + + lpBorrowFee + + flFee - + targetUtilizationRate * actualizedFee * liqThreshold) + : (targetUtilizationRate * liqThreshold * pos.supplyUsd - pos.debtUsd) / + (actualizedFee - targetUtilizationRate * liqThreshold * (1.0 + flFee)); + + const endResult = applyDebtAdjustmentUsd( + { debtAdjustmentUsd }, + pos, + liqThreshold, + fees + ); + + return { + debtAdjustmentUsd, + endResult: endResult.newPos, + intermediaryLiqUtilizationRateBps: endResult.intermediaryLiqUtilizationRateBps, + }; +} + +function getTokenBalanceChange(): TokenBalanceChange | undefined { + // TODO: DCA, limit orders, take profit, stop loss, etc. + return undefined; +} + +function getTargetLiqUtilizationRateBps( + solautoPosition: SolautoPositionEx, + priceType: PriceType, + targetLiqUtilizationRateBps?: number, + tokenBalanceChange?: TokenBalanceChange, + bpsDistanceFromRebalance?: number +): number | undefined { + if (targetLiqUtilizationRateBps !== undefined) { + return targetLiqUtilizationRateBps; + } + + if ( + solautoPosition.liqUtilizationRateBps(PriceType.Realtime, true) + (bpsDistanceFromRebalance ?? 0) >= + solautoPosition.repayFromBps + ) { + return solautoPosition.settings!.repayToBps; + } else if ( + solautoPosition.liqUtilizationRateBps(priceType, true) - (bpsDistanceFromRebalance ?? 0) <= + solautoPosition.boostFromBps + ) { + return solautoPosition.settings!.boostToBps; + } + // TODO: DCA, limit orders, take profit, stop loss, etc. + // else if (tokenBalanceChange !== null) { + // return currentRate; + // } + + return undefined; +} + +function getAdjustedPositionValues( + solautoPosition: SolautoPositionEx, + priceType: PriceType, + tokenBalanceChange: TokenBalanceChange | undefined +): PositionValues { + let supplyUsd = solautoPosition.supplyUsd(priceType); + const debtUsd = solautoPosition.debtUsd(priceType); + + if (tokenBalanceChange) { + const tb = tokenBalanceChange; + switch (tb.changeType) { + case TokenBalanceChangeType.PreSwapDeposit: + case TokenBalanceChangeType.PostSwapDeposit: + supplyUsd += Number(tb.amountUsd); + break; + case TokenBalanceChangeType.PostRebalanceWithdrawDebtToken: + case TokenBalanceChangeType.PostRebalanceWithdrawSupplyToken: + supplyUsd -= Number(tb.amountUsd); + break; + default: + break; + } + } + + return { + supplyUsd, + debtUsd, + }; +} + +function getRebalanceDirection( + solautoPosition: SolautoPositionEx, + targetLtvBps: number, + priceType: number +): RebalanceDirection { + return solautoPosition.liqUtilizationRateBps(priceType) < targetLtvBps + ? RebalanceDirection.Boost + : RebalanceDirection.Repay; +} + +export interface RebalanceValues extends DebtAdjustment { + rebalanceDirection: RebalanceDirection; + tokenBalanceChange?: TokenBalanceChange; + repayingCloseToMaxLtv: boolean; +} + +export function getRebalanceValues( + solautoPosition: SolautoPositionEx, + priceType: PriceType, + targetLiqUtilizationRateBps?: number, + solautoFeeBps?: SolautoFeesBps, + flFeeBps?: number, + bpsDistanceFromRebalance?: number +): RebalanceValues | undefined { + const tokenBalanceChange = getTokenBalanceChange(); + + const targetRate = getTargetLiqUtilizationRateBps( + solautoPosition, + priceType, + targetLiqUtilizationRateBps, + tokenBalanceChange, + bpsDistanceFromRebalance + ); + if (targetRate === undefined) { + return undefined; + } + + const rebalanceDirection = getRebalanceDirection(solautoPosition, targetRate, priceType); + + const position = getAdjustedPositionValues( + solautoPosition, + priceType, + tokenBalanceChange + ); + + const fees: RebalanceFeesBps = { + solauto: solautoFeeBps + ? solautoFeeBps.getSolautoFeesBps(rebalanceDirection).total + : 0, + lpBorrow: solautoPosition.state.debt.borrowFeeBps, + flashLoan: flFeeBps ?? 0, + }; + + const debtAdjustment = getDebtAdjustment( + solautoPosition.state.liqThresholdBps, + position, + targetRate, + fees + ); + + const repayingCloseToMaxLtv = + rebalanceDirection === RebalanceDirection.Repay && + targetRate >= solautoPosition.maxRepayToBps; + + return { + ...debtAdjustment, + rebalanceDirection, + tokenBalanceChange, + repayingCloseToMaxLtv, + }; +} diff --git a/solauto-sdk/src/services/rebalance/solautoFees.ts b/solauto-sdk/src/services/rebalance/solautoFees.ts new file mode 100644 index 00000000..b3a4d753 --- /dev/null +++ b/solauto-sdk/src/services/rebalance/solautoFees.ts @@ -0,0 +1,64 @@ +import { REFERRER_PERCENTAGE } from "../../constants"; +import { RebalanceDirection } from "../../generated"; + +export class SolautoFeesBps { + constructor( + private isReferred: boolean, + private targetLiqUtilizationRateBps: number | undefined, + private positionNetWorthUsd: number + ) {} + + static create( + isReferred: boolean, + targetLiqUtilizationRateBps: number | undefined, + netWorthUsd: number + ) { + return new SolautoFeesBps( + isReferred, + targetLiqUtilizationRateBps, + netWorthUsd + ); + } + + getSolautoFeesBps(rebalanceDirection: RebalanceDirection) { + const minSize = 10_000; // Minimum position size + const maxSize = 250_000; // Maximum position size + const maxFeeBps = 50; // Fee in basis points for minSize (0.5%) + const minFeeBps = 25; // Fee in basis points for maxSize (0.25%) + const k = 1.5; + + let feeBps: number = 0; + if (this.targetLiqUtilizationRateBps !== undefined) { + if (this.targetLiqUtilizationRateBps === 0) { + feeBps = 15; + } else { + feeBps = 10; + } + } else if (rebalanceDirection === RebalanceDirection.Repay) { + feeBps = 25; + } else if (this.positionNetWorthUsd <= minSize) { + feeBps = maxFeeBps; + } else if (this.positionNetWorthUsd >= maxSize) { + feeBps = minFeeBps; + } else { + const t = + (Math.log(this.positionNetWorthUsd) - Math.log(minSize)) / + (Math.log(maxSize) - Math.log(minSize)); + feeBps = Math.round( + minFeeBps + (maxFeeBps - minFeeBps) * (1 - Math.pow(t, k)) + ); + } + + let referrer = 0; + if (this.isReferred) { + feeBps *= 1.0 - REFERRER_PERCENTAGE; + referrer = Math.floor(feeBps * REFERRER_PERCENTAGE); + } + + return { + solauto: feeBps - referrer, + referrer, + total: feeBps, + }; + } +} diff --git a/solauto-sdk/src/services/solauto/index.ts b/solauto-sdk/src/services/solauto/index.ts new file mode 100644 index 00000000..9dccd0aa --- /dev/null +++ b/solauto-sdk/src/services/solauto/index.ts @@ -0,0 +1,4 @@ +export * from "./referralStateManager"; +export * from "./solautoClient"; +export * from "./solautoMarginfiClient"; +export * from "./txHandler"; diff --git a/solauto-sdk/src/clients/referralStateManager.ts b/solauto-sdk/src/services/solauto/referralStateManager.ts similarity index 78% rename from solauto-sdk/src/clients/referralStateManager.ts rename to solauto-sdk/src/services/solauto/referralStateManager.ts index 9385ddb6..65ba92ab 100644 --- a/solauto-sdk/src/clients/referralStateManager.ts +++ b/solauto-sdk/src/services/solauto/referralStateManager.ts @@ -1,30 +1,18 @@ import { PublicKey } from "@solana/web3.js"; import { NATIVE_MINT } from "@solana/spl-token"; -import { - publicKey, - Signer, - signerIdentity, - TransactionBuilder, - Umi, -} from "@metaplex-foundation/umi"; +import { publicKey, TransactionBuilder, Umi } from "@metaplex-foundation/umi"; import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; -import { - WalletAdapter, - walletAdapterIdentity, -} from "@metaplex-foundation/umi-signer-wallet-adapters"; import { claimReferralFees, ReferralState, safeFetchReferralState, updateReferralStates, -} from "../generated"; -import { getReferralState, getTokenAccount } from "../utils"; +} from "../../generated"; +import { getReferralState, getTokenAccount } from "../../utils"; import { TxHandler } from "./txHandler"; -import { SOLAUTO_LUT } from "../constants"; +import { SOLAUTO_LUT } from "../../constants"; export interface ReferralStateManagerArgs { - signer?: Signer; - wallet?: WalletAdapter; authority?: PublicKey; referralState?: PublicKey; referredByAuthority?: PublicKey; @@ -32,7 +20,6 @@ export interface ReferralStateManagerArgs { export class ReferralStateManager extends TxHandler { public umi!: Umi; - public signer!: Signer; public referralState!: PublicKey; public referralStateData!: ReferralState | null; @@ -42,32 +29,23 @@ export class ReferralStateManager extends TxHandler { public referredByState?: PublicKey; async initialize(args: ReferralStateManagerArgs) { - if (!args.signer && !args.wallet) { - throw new Error("Signer or wallet must be provided"); - } - this.umi = this.umi.use( - args.signer - ? signerIdentity(args.signer) - : walletAdapterIdentity(args.wallet!, true) - ); - this.signer = this.umi.identity; - - this.referralState = args.referralState - ? args.referralState - : getReferralState( - args.authority ?? toWeb3JsPublicKey(this.signer.publicKey), - this.programId - ); - this.referralStateData = await safeFetchReferralState( - this.umi, - publicKey(this.referralState), - { commitment: "confirmed" } - ); - this.authority = this.referralStateData + this.referralState = + args.referralState ?? + getReferralState( + args.authority ?? toWeb3JsPublicKey(this.signer.publicKey), + this.programId + ); + + await this.refetchReferralState(); + this.authority = this.referralStateData?.authority ? toWeb3JsPublicKey(this.referralStateData.authority) - : toWeb3JsPublicKey(this.signer.publicKey); + : (args.authority ?? toWeb3JsPublicKey(this.signer.publicKey)); this.setReferredBy(args.referredByAuthority); + + this.log("Authority:", this.authority.toString()); + this.log("Signer:", this.signer.publicKey.toString()); + this.log("Referral state:", this.referralState.toString()); } defaultLookupTables(): string[] { @@ -79,6 +57,14 @@ export class ReferralStateManager extends TxHandler { : [SOLAUTO_LUT]; } + async refetchReferralState() { + this.referralStateData = await safeFetchReferralState( + this.umi, + publicKey(this.referralState), + { commitment: "confirmed" } + ); + } + setReferredBy(referredBy?: PublicKey) { const hasReferredBy = this.referralStateData && @@ -101,6 +87,10 @@ export class ReferralStateManager extends TxHandler { : undefined; } + get isReferred(): boolean { + return Boolean(this.referredByState); + } + updateReferralStatesIx( destFeesMint?: PublicKey, lookupTable?: PublicKey diff --git a/solauto-sdk/src/clients/solautoClient.ts b/solauto-sdk/src/services/solauto/solautoClient.ts similarity index 52% rename from solauto-sdk/src/clients/solautoClient.ts rename to solauto-sdk/src/services/solauto/solautoClient.ts index 2eae7866..074ec385 100644 --- a/solauto-sdk/src/clients/solautoClient.ts +++ b/solauto-sdk/src/services/solauto/solautoClient.ts @@ -1,7 +1,6 @@ import "rpc-websockets/dist/lib/client"; import { AddressLookupTableProgram, PublicKey } from "@solana/web3.js"; import { - Signer, TransactionBuilder, isOption, publicKey, @@ -11,74 +10,76 @@ import { } from "@metaplex-foundation/umi"; import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; import { - DCASettings, DCASettingsInpArgs, LendingPlatform, - PositionState, + PositionType, + PriceType, + RebalanceStep, SolautoActionArgs, - SolautoPosition, - SolautoRebalanceTypeArgs, - SolautoSettingsParameters, SolautoSettingsParametersInpArgs, TokenType, UpdatePositionDataArgs, cancelDCA, - closePosition, - safeFetchSolautoPosition, updatePosition, -} from "../generated"; +} from "../../generated"; import { - getSolautoPositionAccount, getTokenAccount, -} from "../utils/accountUtils"; -import { SOLAUTO_FEES_WALLET } from "../constants/generalAccounts"; -import { + getWalletSplBalances, getWrappedInstruction, splTokenTransferUmiIx, -} from "../utils/solanaUtils"; -import { - FlashLoanDetails, - RebalanceValues, -} from "../utils/solauto/rebalanceUtils"; -import { - MIN_POSITION_STATE_FRESHNESS_SECS, - SOLAUTO_LUT, -} from "../constants/solautoConstants"; -import { currentUnixSeconds } from "../utils/generalUtils"; -import { LivePositionUpdates } from "../utils/solauto/generalUtils"; + ContextUpdates, +} from "../../utils"; +import { SOLAUTO_FEES_WALLET, SOLAUTO_LUT } from "../../constants"; +import { ProgramEnv, RebalanceDetails } from "../../types"; import { ReferralStateManager, ReferralStateManagerArgs, } from "./referralStateManager"; -import { QuoteResponse } from "@jup-ag/api"; +import { + getOrCreatePositionEx, + SolautoPositionEx, +} from "../../solautoPosition"; +import { FlProviderAggregator } from "../flashLoans"; export interface SolautoClientArgs extends ReferralStateManagerArgs { - new?: boolean; positionId?: number; supplyMint?: PublicKey; debtMint?: PublicKey; + lpPoolAccount?: PublicKey; + lpUserAccount?: PublicKey; } +export type ExistingSolautoPositionArgs = Required< + Pick +> & + Partial>; + +export type NewSolautoPositionArgs = Required< + Pick< + SolautoClientArgs, + "positionId" | "supplyMint" | "debtMint" | "lpPoolAccount" + > +>; + +export type ExistingSelfManagedPositionArgs = Required< + Pick +>; + +export type NewSelfManagedPositionArgs = Required< + Pick +>; + export abstract class SolautoClient extends ReferralStateManager { - public lendingPlatform?: LendingPlatform; + public lendingPlatform!: LendingPlatform; + public lpEnv!: ProgramEnv; public authority!: PublicKey; - public signer!: Signer; - - public positionId!: number; - public selfManaged!: boolean; - public solautoPosition!: PublicKey; - public solautoPositionData!: SolautoPosition | null; - public solautoPositionState!: PositionState | undefined; - public maxLtvBps?: number; - public liqThresholdBps?: number; + public pos!: SolautoPositionEx; - public supplyMint!: PublicKey; public positionSupplyTa!: PublicKey; public signerSupplyTa!: PublicKey; - public debtMint!: PublicKey; public positionDebtTa!: PublicKey; public signerDebtTa!: PublicKey; @@ -87,62 +88,75 @@ export abstract class SolautoClient extends ReferralStateManager { public authorityLutAddress?: PublicKey; - public livePositionUpdates: LivePositionUpdates = new LivePositionUpdates(); + public flProvider!: FlProviderAggregator; + public contextUpdates: ContextUpdates = new ContextUpdates(); + + private signerSupplyBalance: bigint | undefined; + private signerDebtBalance: bigint | undefined; + + async initializeExistingSolautoPosition(args: ExistingSolautoPositionArgs) { + await this.initialize(args); + } + + async initializeNewSolautoPosition(args: NewSolautoPositionArgs) { + await this.initialize(args); + } + + async initializeExistingSelfManagedPosition( + args: ExistingSelfManagedPositionArgs + ) { + await this.initialize(args); + } + + async initializeNewSelfManagedPosition(args: NewSelfManagedPositionArgs) { + await this.initialize(args); + } async initialize(args: SolautoClientArgs) { await super.initialize(args); - this.positionId = args.positionId ?? 0; - this.selfManaged = this.positionId === 0; - this.solautoPosition = getSolautoPositionAccount( + const positionId = args.positionId ?? 0; + this.pos = await getOrCreatePositionEx( + this.umi, this.authority, - this.positionId, - this.programId + positionId, + this.programId, + { + supplyMint: args.supplyMint, + debtMint: args.debtMint, + lpPoolAccount: args.lpPoolAccount, + lpUserAccount: args.lpUserAccount, + lendingPlatform: this.lendingPlatform, + lpEnv: this.lpEnv, + }, + this.contextUpdates ); - this.solautoPositionData = !args.new - ? await safeFetchSolautoPosition( - this.umi, - publicKey(this.solautoPosition), - { commitment: "confirmed" } - ) - : null; - this.solautoPositionState = this.solautoPositionData?.state; - - this.maxLtvBps = undefined; - this.liqThresholdBps = undefined; - this.supplyMint = - args.supplyMint ?? - (this.solautoPositionData && !this.selfManaged - ? toWeb3JsPublicKey(this.solautoPositionData!.state.supply.mint) - : PublicKey.default); this.positionSupplyTa = getTokenAccount( - this.solautoPosition, - this.supplyMint + this.pos.publicKey, + this.pos.supplyMint ); this.signerSupplyTa = getTokenAccount( toWeb3JsPublicKey(this.signer.publicKey), - this.supplyMint + this.pos.supplyMint ); - this.debtMint = - args.debtMint ?? - (this.solautoPositionData && !this.selfManaged - ? toWeb3JsPublicKey(this.solautoPositionData!.state.debt.mint) - : PublicKey.default); - this.positionDebtTa = getTokenAccount(this.solautoPosition, this.debtMint); + this.positionDebtTa = getTokenAccount( + this.pos.publicKey, + this.pos.debtMint + ); this.signerDebtTa = getTokenAccount( toWeb3JsPublicKey(this.signer.publicKey), - this.debtMint + this.pos.debtMint ); this.solautoFeesSupplyTa = getTokenAccount( SOLAUTO_FEES_WALLET, - this.supplyMint + this.pos.supplyMint ); this.solautoFeesDebtTa = getTokenAccount( SOLAUTO_FEES_WALLET, - this.debtMint + this.pos.debtMint ); this.authorityLutAddress = @@ -153,59 +167,54 @@ export abstract class SolautoClient extends ReferralStateManager { ? toWeb3JsPublicKey(this.referralStateData.lookupTable) : undefined; - this.log("Position state: ", this.solautoPositionState); - this.log( - "Position settings: ", - this.solautoPositionData?.position?.settingParams - ); - this.log( - "Position DCA: ", - (this.solautoPositionData?.position?.dca?.automation?.targetPeriods ?? - 0) > 0 - ? this.solautoPositionData?.position?.dca - : undefined + this.flProvider = new FlProviderAggregator( + this.umi, + this.signer, + this.authority, + this.pos.supplyMint, + this.pos.debtMint, + this.lpEnv ); + await this.flProvider.initialize(); + this.otherSigners.push(...this.flProvider.otherSigners()); + + this.log("Position state: ", this.pos.state); + this.log("Position settings: ", this.pos.settings); + this.log("Public key:", this.pos.publicKey.toString()); + this.log("Supply mint:", this.pos.supplyMint.toString()); + this.log("Debt mint:", this.pos.debtMint.toString()); + this.log("LP pool:", this.pos.lpPoolAccount.toString()); } referredBySupplyTa(): PublicKey | undefined { if (this.referredByState !== undefined) { - return getTokenAccount(this.referredByState, this.supplyMint); + return getTokenAccount(this.referredByState, this.pos.supplyMint); } return undefined; } referredByDebtTa(): PublicKey | undefined { if (this.referredByState !== undefined) { - return getTokenAccount(this.referredByState, this.debtMint); + return getTokenAccount(this.referredByState, this.pos.debtMint); } return undefined; } async resetLiveTxUpdates(success?: boolean) { + this.log("Resetting context updates..."); if (success) { - if (!this.solautoPositionData) { - this.solautoPositionData = await safeFetchSolautoPosition( - this.umi, - publicKey(this.solautoPosition), - { commitment: "confirmed" } - ); + if (!this.pos.exists) { + await this.pos.refetchPositionData(); } else { - if (this.livePositionUpdates.activeDca) { - this.solautoPositionData.position.dca = - this.livePositionUpdates.activeDca; + if (this.contextUpdates.settings) { + this.pos.updateSettings(this.contextUpdates.settings); } - if (this.livePositionUpdates.settings) { - this.solautoPositionData.position.settingParams = - this.livePositionUpdates.settings; - } - // All other live position updates can be derived by getting a fresh position state, so we don't need to do anything else form livePositionUpdates + // All other live position updates can be derived by getting a fresh position state, so we don't need to do anything else form contextUpdates } } - this.livePositionUpdates.reset(); + this.contextUpdates.reset(); } - abstract protocolAccount(): PublicKey; - defaultLookupTables(): string[] { return [ SOLAUTO_LUT, @@ -218,18 +227,15 @@ export abstract class SolautoClient extends ReferralStateManager { lutAccountsToAdd(): PublicKey[] { return [ this.authority, - ...(toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority) - ? [this.signerSupplyTa] - : []), - ...(toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority) - ? [this.signerDebtTa] - : []), - this.solautoPosition, + this.signerSupplyTa, + this.signerDebtTa, + this.pos.publicKey, this.positionSupplyTa, this.positionDebtTa, this.referralState, ...(this.referredBySupplyTa() ? [this.referredBySupplyTa()!] : []), ...(this.referredByDebtTa() ? [this.referredByDebtTa()!] : []), + ...this.flProvider.lutAccountsToAdd(), ]; } @@ -251,6 +257,10 @@ export abstract class SolautoClient extends ReferralStateManager { } | undefined > { + if (!toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority)) { + return undefined; + } + const existingLutAccounts = await this.fetchExistingAuthorityLutAccounts(); if ( this.lutAccountsToAdd().every((element) => @@ -272,7 +282,9 @@ export abstract class SolautoClient extends ReferralStateManager { recentSlot: await this.umi.rpc.getSlot({ commitment: "finalized" }), }); this.authorityLutAddress = lookupTableAddress; - tx = tx.add(getWrappedInstruction(this.signer, createLookupTableInst)); + tx = tx + .add(getWrappedInstruction(this.signer, createLookupTableInst)) + .add(this.updateReferralStatesIx(undefined, this.authorityLutAddress)); } const accountsToAdd: PublicKey[] = this.lutAccountsToAdd().filter( @@ -285,19 +297,22 @@ export abstract class SolautoClient extends ReferralStateManager { return undefined; } - tx = tx.add( - getWrappedInstruction( - this.signer, - AddressLookupTableProgram.extendLookupTable({ - payer: toWeb3JsPublicKey(this.signer.publicKey), - authority: this.authority, - lookupTable: this.authorityLutAddress, - addresses: accountsToAdd, - }) + tx = tx + .add( + getWrappedInstruction( + this.signer, + AddressLookupTableProgram.extendLookupTable({ + payer: toWeb3JsPublicKey(this.signer.publicKey), + authority: this.authority, + lookupTable: this.authorityLutAddress, + addresses: accountsToAdd, + }) + ) ) - ); + .add(await this.flProvider.flAccountPrereqIxs()); this.log("Requires authority LUT update..."); + this.log("Addresses to add:", accountsToAdd.join(", ")); return { tx, new: existingLutAccounts.length === 0, @@ -305,33 +320,32 @@ export abstract class SolautoClient extends ReferralStateManager { }; } - solautoPositionSettings(): SolautoSettingsParameters | undefined { - return ( - this.livePositionUpdates.settings ?? - this.solautoPositionData?.position.settingParams - ); - } - - solautoPositionActiveDca(): DCASettings | undefined { - return ( - this.livePositionUpdates.activeDca ?? - this.solautoPositionData?.position.dca - ); - } - - async maxLtvAndLiqThresholdBps(): Promise<[number, number] | undefined> { - if (this.maxLtvBps !== undefined && this.liqThresholdBps !== undefined) { - return [this.maxLtvBps, this.liqThresholdBps]; + async signerBalances(): Promise<{ + supplyBalance: bigint; + debtBalance: bigint; + }> { + if (!this.signerSupplyBalance || !this.signerDebtBalance) { + [this.signerSupplyBalance, this.signerDebtBalance] = + await getWalletSplBalances( + this.connection, + toWeb3JsPublicKey(this.signer.publicKey), + [this.pos.supplyMint, this.pos.debtMint] + ); } - return undefined; + + return { + supplyBalance: this.signerSupplyBalance, + debtBalance: this.signerDebtBalance, + }; } - openPosition( - settingParams?: SolautoSettingsParametersInpArgs, - dca?: DCASettingsInpArgs + openPositionIx( + settings?: SolautoSettingsParametersInpArgs, + dca?: DCASettingsInpArgs, + positionType?: PositionType ): TransactionBuilder { if (dca && dca.dcaInBaseUnit > 0) { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "dcaInBalance", value: { amount: BigInt(dca.dcaInBaseUnit), @@ -339,14 +353,14 @@ export abstract class SolautoClient extends ReferralStateManager { }, }); } - if (settingParams) { - this.livePositionUpdates.new({ + if (settings) { + this.contextUpdates.new({ type: "settings", - value: settingParams, + value: settings, }); } if (dca) { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "dca", value: dca, }); @@ -361,41 +375,39 @@ export abstract class SolautoClient extends ReferralStateManager { let signerDcaTa: UmiPublicKey | undefined = undefined; if (isOption(args.dca) && isSome(args.dca)) { if (args.dca.value.tokenType === TokenType.Supply) { - dcaMint = publicKey(this.supplyMint); + dcaMint = publicKey(this.pos.supplyMint); positionDcaTa = publicKey(this.positionSupplyTa); signerDcaTa = publicKey(this.signerSupplyTa); } else { - dcaMint = publicKey(this.debtMint); + dcaMint = publicKey(this.pos.debtMint); positionDcaTa = publicKey(this.positionDebtTa); signerDcaTa = publicKey(this.signerDebtTa); } - let addingToPos = false; if ( isOption(args.dca) && isSome(args.dca) && args.dca.value.dcaInBaseUnit > 0 ) { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "dcaInBalance", value: { amount: BigInt(args.dca.value.dcaInBaseUnit), tokenType: args.dca.value.tokenType, }, }); - addingToPos = true; } } - if (isOption(args.settingParams) && isSome(args.settingParams)) { - this.livePositionUpdates.new({ + if (isOption(args.settings) && isSome(args.settings)) { + this.contextUpdates.new({ type: "settings", - value: args.settingParams.value, + value: args.settings.value, }); } if (isOption(args.dca) && isSome(args.dca)) { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "dca", value: args.dca.value, }); @@ -403,7 +415,7 @@ export abstract class SolautoClient extends ReferralStateManager { return updatePosition(this.umi, { signer: this.signer, - solautoPosition: publicKey(this.solautoPosition), + solautoPosition: publicKey(this.pos.publicKey), dcaMint, positionDcaTa, signerDcaTa, @@ -411,56 +423,28 @@ export abstract class SolautoClient extends ReferralStateManager { }); } - closePositionIx(): TransactionBuilder { - return closePosition(this.umi, { - signer: this.signer, - solautoPosition: publicKey(this.solautoPosition), - signerSupplyTa: publicKey(this.signerSupplyTa), - positionSupplyTa: publicKey(this.positionSupplyTa), - positionDebtTa: publicKey(this.positionDebtTa), - signerDebtTa: publicKey(this.signerDebtTa), - protocolAccount: publicKey(this.protocolAccount()), - }); - } + abstract closePositionIx(): TransactionBuilder; cancelDCAIx(): TransactionBuilder { let dcaMint: UmiPublicKey | undefined = undefined; let positionDcaTa: UmiPublicKey | undefined = undefined; let signerDcaTa: UmiPublicKey | undefined = undefined; - const currDca = this.solautoPositionActiveDca()!; - if (currDca.dcaInBaseUnit > 0) { - if (currDca.tokenType === TokenType.Supply) { - dcaMint = publicKey(this.supplyMint); - positionDcaTa = publicKey(this.positionSupplyTa); - signerDcaTa = publicKey(this.signerSupplyTa); - } else { - dcaMint = publicKey(this.debtMint); - positionDcaTa = publicKey(this.positionDebtTa); - signerDcaTa = publicKey(this.signerDebtTa); - } - - this.livePositionUpdates.new({ - type: "cancellingDca", - value: this.solautoPositionData!.position.dca.tokenType, - }); - } - return cancelDCA(this.umi, { signer: this.signer, - solautoPosition: publicKey(this.solautoPosition), + solautoPosition: publicKey(this.pos.publicKey), dcaMint, positionDcaTa, signerDcaTa, }); } - abstract refresh(): TransactionBuilder; + abstract refreshIx(priceType?: PriceType): TransactionBuilder; - protocolInteraction(args: SolautoActionArgs): TransactionBuilder { + protocolInteractionIx(args: SolautoActionArgs): TransactionBuilder { let tx = transactionBuilder(); - if (!this.selfManaged) { + if (!this.pos.selfManaged) { if (args.__kind === "Deposit") { tx = tx.add( splTokenTransferUmiIx( @@ -491,8 +475,7 @@ export abstract class SolautoClient extends ReferralStateManager { toWeb3JsPublicKey(this.signer.publicKey), BigInt( Math.round( - Number(this.solautoPositionState!.debt.amountUsed.baseUnit) * - 1.01 + Number(this.pos.state.debt.amountUsed.baseUnit) * 1.01 ) ) ) @@ -502,41 +485,40 @@ export abstract class SolautoClient extends ReferralStateManager { } if (args.__kind === "Deposit") { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "supply", value: BigInt(args.fields[0]), }); } else if (args.__kind === "Withdraw") { if (args.fields[0].__kind === "Some") { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "supply", value: BigInt(args.fields[0].fields[0]) * BigInt(-1), }); } else { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "supply", value: - (this.solautoPositionState?.supply.amountUsed.baseUnit ?? - BigInt(0)) * BigInt(-1), + (this.pos.state.supply.amountUsed.baseUnit ?? BigInt(0)) * + BigInt(-1), }); } } else if (args.__kind === "Borrow") { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "debt", value: BigInt(args.fields[0]), }); } else { if (args.fields[0].__kind === "Some") { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "debt", value: BigInt(args.fields[0].fields[0]) * BigInt(-1), }); } else { - this.livePositionUpdates.new({ + this.contextUpdates.new({ type: "debt", value: - (this.solautoPositionState?.debt.amountUsed.baseUnit ?? BigInt(0)) * - BigInt(-1), + (this.pos.state.debt.amountUsed.baseUnit ?? BigInt(0)) * BigInt(-1), }); } } @@ -544,33 +526,8 @@ export abstract class SolautoClient extends ReferralStateManager { return tx; } - abstract flashBorrow( - flashLoanDetails: FlashLoanDetails, - destinationTokenAccount: PublicKey - ): TransactionBuilder; - - abstract flashRepay(flashLoanDetails: FlashLoanDetails): TransactionBuilder; - - abstract rebalance( - rebalanceStep: "A" | "B", - jupQuote: QuoteResponse, - rebalanceType: SolautoRebalanceTypeArgs, - rebalanceValues: RebalanceValues, - flashLoan?: FlashLoanDetails, - targetLiqUtilizationRateBps?: number + abstract rebalanceIx( + rebalanceStep: RebalanceStep, + data: RebalanceDetails ): TransactionBuilder; - - async getFreshPositionState(): Promise { - if ( - Boolean(this.solautoPositionData) && - Boolean(this.solautoPositionState) && - Number(this.solautoPositionState!.lastUpdated) > - currentUnixSeconds() - MIN_POSITION_STATE_FRESHNESS_SECS && - !this.livePositionUpdates.hasUpdates() - ) { - return this.solautoPositionState; - } - - return undefined; - } } diff --git a/solauto-sdk/src/services/solauto/solautoMarginfiClient.ts b/solauto-sdk/src/services/solauto/solautoMarginfiClient.ts new file mode 100644 index 00000000..e8960501 --- /dev/null +++ b/solauto-sdk/src/services/solauto/solautoMarginfiClient.ts @@ -0,0 +1,494 @@ +import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + Signer, + TransactionBuilder, + publicKey, + PublicKey as UmiPublicKey, + createSignerFromKeypair, + AccountMeta, +} from "@metaplex-foundation/umi"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { MarginfiAssetAccounts, RebalanceDetails } from "../../types"; +import { getMarginfiAccounts, MarginfiProgramAccounts } from "../../constants"; +import { + DCASettingsInpArgs, + LendingPlatform, + PositionType, + PriceType, + RebalanceDirection, + RebalanceStep, + SolautoActionArgs, + SolautoRebalanceType, + SolautoSettingsParametersInpArgs, + SwapType, + closePosition, + marginfiOpenPosition, + marginfiProtocolInteraction, + marginfiRebalance, + marginfiRefreshData, +} from "../../generated"; +import { + getAllMarginfiAccountsByAuthority, + marginfiAccountEmpty, + getTokenAccount, + hasFirstRebalance, + getRemainingAccountsForMarginfiHealthCheck, + getAccountMeta, + composeRemainingAccounts, +} from "../../utils"; +import { + Bank, + fetchMarginfiAccount, + lendingAccountBorrow, + lendingAccountDeposit, + lendingAccountRepay, + lendingAccountWithdraw, + marginfiAccountInitialize, + safeFetchAllMarginfiAccount, +} from "../../externalSdks/marginfi"; +import { SolautoClient, SolautoClientArgs } from "./solautoClient"; + +function isSigner(account: PublicKey | Signer): account is Signer { + return "publicKey" in account; +} + +export class SolautoMarginfiClient extends SolautoClient { + public lendingPlatform = LendingPlatform.Marginfi; + + public mfiAccounts!: MarginfiProgramAccounts; + + public marginfiAccount!: PublicKey | Signer; + public marginfiAccountPk!: PublicKey; + public healthCheckRemainingAccounts!: AccountMeta[]; + public marginfiGroup!: PublicKey; + + public marginfiSupplyAccounts!: MarginfiAssetAccounts; + public marginfiDebtAccounts!: MarginfiAssetAccounts; + + public supplyPriceOracle!: PublicKey; + public debtPriceOracle!: PublicKey; + + async initialize(args: SolautoClientArgs) { + await super.initialize(args); + + this.mfiAccounts = getMarginfiAccounts(this.lpEnv); + + this.marginfiGroup = this.pos.lpPoolAccount; + this.healthCheckRemainingAccounts = []; + + if (this.pos.selfManaged) { + this.marginfiAccount = + args.lpUserAccount ?? + createSignerFromKeypair(this.umi, this.umi.eddsa.generateKeypair()); + } else { + if (this.pos.exists) { + this.marginfiAccount = this.pos.lpUserAccount!; + } else { + const accounts = await getAllMarginfiAccountsByAuthority( + this.umi, + this.pos.publicKey, + this.marginfiGroup, + false + ); + const reusableAccounts = + accounts.length > 0 + ? ( + await safeFetchAllMarginfiAccount( + this.umi, + accounts.map((x) => publicKey(x.marginfiAccount)) + ) + ).filter((x) => marginfiAccountEmpty(x)) + : []; + this.marginfiAccount = + reusableAccounts.length > 0 + ? toWeb3JsPublicKey(reusableAccounts[0].publicKey) + : createSignerFromKeypair( + this.umi, + this.umi.eddsa.generateKeypair() + ); + } + } + + this.marginfiAccountPk = isSigner(this.marginfiAccount) + ? toWeb3JsPublicKey(this.marginfiAccount.publicKey) + : this.marginfiAccount; + + if (isSigner(this.marginfiAccount)) { + this.otherSigners.push(this.marginfiAccount); + } else if (this.pos.selfManaged) { + const accountData = await fetchMarginfiAccount( + this.umi, + fromWeb3JsPublicKey(this.marginfiAccount as PublicKey) + ); + this.healthCheckRemainingAccounts = ( + await Promise.all( + accountData.lendingAccount.balances.map((balance) => + getRemainingAccountsForMarginfiHealthCheck(this.umi, balance) + ) + ) + ).flat(); + } + + this.marginfiSupplyAccounts = + this.mfiAccounts.bankAccounts[this.marginfiGroup.toString()][ + this.pos.supplyMint.toString() + ]!; + this.marginfiDebtAccounts = + this.mfiAccounts.bankAccounts[this.marginfiGroup.toString()][ + this.pos.debtMint.toString() + ]!; + + [this.supplyPriceOracle, this.debtPriceOracle] = + await this.pos.priceOracles(); + + this.log("Marginfi account:", this.marginfiAccountPk.toString()); + this.log("Supply price oracle:", this.supplyPriceOracle.toString()); + this.log("Debt price oracle:", this.debtPriceOracle.toString()); + } + + defaultLookupTables(): string[] { + return [ + this.mfiAccounts.lookupTable.toString(), + ...super.defaultLookupTables(), + ]; + } + + lutAccountsToAdd(): PublicKey[] { + return [...super.lutAccountsToAdd(), this.marginfiAccountPk]; + } + + marginfiAccountInitialize(marginfiAccount: Signer): TransactionBuilder { + return marginfiAccountInitialize(this.umi, { + marginfiAccount: marginfiAccount, + marginfiGroup: publicKey(this.marginfiGroup), + authority: this.signer, + feePayer: this.signer, + }); + } + + openPositionIx( + settings?: SolautoSettingsParametersInpArgs, + dca?: DCASettingsInpArgs + ): TransactionBuilder { + return super + .openPositionIx(settings, dca) + .add(this.marginfiOpenPositionIx(settings, dca)); + } + + private marginfiOpenPositionIx( + settings?: SolautoSettingsParametersInpArgs, + dca?: DCASettingsInpArgs, + positionType?: PositionType + ): TransactionBuilder { + let signerDebtTa: UmiPublicKey | undefined = undefined; + if (dca) { + signerDebtTa = publicKey(this.signerDebtTa); + } + + return marginfiOpenPosition(this.umi, { + signer: this.signer, + marginfiProgram: publicKey(this.mfiAccounts.program), + signerReferralState: publicKey(this.referralState), + referredByState: this.referredByState + ? publicKey(this.referredByState) + : undefined, + referredBySupplyTa: this.referredBySupplyTa() + ? publicKey(this.referredBySupplyTa()!) + : undefined, + solautoPosition: publicKey(this.pos.publicKey), + marginfiGroup: publicKey(this.marginfiGroup), + marginfiAccount: + "publicKey" in this.marginfiAccount + ? (this.marginfiAccount as Signer) + : publicKey(this.marginfiAccount), + supplyMint: publicKey(this.pos.supplyMint), + supplyBank: publicKey(this.marginfiSupplyAccounts.bank), + positionSupplyTa: publicKey(this.positionSupplyTa), + debtMint: publicKey(this.pos.debtMint), + debtBank: publicKey(this.marginfiDebtAccounts.bank), + positionDebtTa: publicKey(this.positionDebtTa), + signerDebtTa: signerDebtTa, + positionType: positionType ?? PositionType.Leverage, + positionData: { + positionId: this.pos.positionId, + settings: settings ?? null, + dca: dca ?? null, + }, + }); + } + + closePositionIx(): TransactionBuilder { + return closePosition(this.umi, { + signer: this.signer, + solautoPosition: publicKey(this.pos.publicKey), + signerSupplyTa: publicKey(this.signerSupplyTa), + positionSupplyTa: publicKey(this.positionSupplyTa), + positionDebtTa: publicKey(this.positionDebtTa), + signerDebtTa: publicKey(this.signerDebtTa), + lpUserAccount: publicKey(this.marginfiAccountPk), + }); + } + + refreshIx(priceType?: PriceType): TransactionBuilder { + return marginfiRefreshData(this.umi, { + signer: this.signer, + marginfiProgram: publicKey(this.mfiAccounts.program), + marginfiGroup: publicKey(this.marginfiGroup), + marginfiAccount: publicKey(this.marginfiAccount), + supplyBank: publicKey(this.marginfiSupplyAccounts.bank), + supplyPriceOracle: publicKey(this.supplyPriceOracle), + debtBank: publicKey(this.marginfiDebtAccounts.bank), + debtPriceOracle: publicKey(this.debtPriceOracle), + solautoPosition: publicKey(this.pos.publicKey), + priceType: priceType ?? PriceType.Realtime, + }); + } + + protocolInteractionIx(args: SolautoActionArgs): TransactionBuilder { + let tx = super.protocolInteractionIx(args); + + if (this.pos.selfManaged) { + return tx.add(this.marginfiProtocolInteractionIx(args)); + } else { + return tx.add(this.marginfiSolautoProtocolInteractionIx(args)); + } + } + + private marginfiProtocolInteractionIx(args: SolautoActionArgs) { + switch (args.__kind) { + case "Deposit": { + if ( + !this.healthCheckRemainingAccounts + .map((x) => x.pubkey.toString()) + .includes(this.marginfiSupplyAccounts.bank) + ) { + this.healthCheckRemainingAccounts.push( + ...[ + getAccountMeta(new PublicKey(this.marginfiSupplyAccounts.bank)), + getAccountMeta(this.supplyPriceOracle), + ] + ); + } + return lendingAccountDeposit(this.umi, { + signer: this.signer, + signerTokenAccount: publicKey(this.signerSupplyTa), + marginfiAccount: publicKey(this.marginfiAccountPk), + marginfiGroup: publicKey(this.marginfiGroup), + bank: publicKey(this.marginfiSupplyAccounts.bank), + bankLiquidityVault: publicKey( + this.marginfiSupplyAccounts.liquidityVault + ), + amount: args.fields[0], + depositUpToLimit: true, + }); + } + case "Borrow": { + const remainingAccounts = this.healthCheckRemainingAccounts; + if ( + !remainingAccounts.find( + (x) => + x.pubkey.toString() === this.marginfiDebtAccounts.bank.toString() + ) + ) { + remainingAccounts.push( + ...[ + getAccountMeta(new PublicKey(this.marginfiDebtAccounts.bank)), + getAccountMeta(this.debtPriceOracle), + ] + ); + } + + return lendingAccountBorrow(this.umi, { + amount: args.fields[0], + signer: this.signer, + destinationTokenAccount: publicKey(this.signerDebtTa), + marginfiAccount: publicKey(this.marginfiAccountPk), + marginfiGroup: publicKey(this.marginfiGroup), + bank: publicKey(this.marginfiDebtAccounts.bank), + bankLiquidityVault: publicKey( + this.marginfiDebtAccounts.liquidityVault + ), + bankLiquidityVaultAuthority: publicKey( + this.marginfiDebtAccounts.vaultAuthority + ), + }).addRemainingAccounts(composeRemainingAccounts(remainingAccounts)); + } + case "Repay": { + return lendingAccountRepay(this.umi, { + amount: + args.fields[0].__kind === "Some" ? args.fields[0].fields[0] : 0, + repayAll: args.fields[0].__kind === "All" ? true : false, + signer: this.signer, + signerTokenAccount: publicKey(this.signerDebtTa), + marginfiAccount: publicKey(this.marginfiAccountPk), + marginfiGroup: publicKey(this.marginfiGroup), + bank: publicKey(this.marginfiDebtAccounts.bank), + bankLiquidityVault: publicKey( + this.marginfiDebtAccounts.liquidityVault + ), + }); + } + case "Withdraw": { + return lendingAccountWithdraw(this.umi, { + amount: + args.fields[0].__kind === "Some" ? args.fields[0].fields[0] : 0, + withdrawAll: args.fields[0].__kind === "All" ? true : false, + signer: this.signer, + destinationTokenAccount: publicKey(this.signerSupplyTa), + marginfiAccount: publicKey(this.marginfiAccountPk), + marginfiGroup: publicKey(this.marginfiGroup), + bank: publicKey(this.marginfiSupplyAccounts.bank), + bankLiquidityVault: publicKey( + this.marginfiSupplyAccounts.liquidityVault + ), + bankLiquidityVaultAuthority: publicKey( + this.marginfiSupplyAccounts.vaultAuthority + ), + }).addRemainingAccounts( + composeRemainingAccounts(this.healthCheckRemainingAccounts) + ); + } + } + } + + private marginfiSolautoProtocolInteractionIx( + args: SolautoActionArgs + ): TransactionBuilder { + let positionSupplyTa: UmiPublicKey | undefined = undefined; + let vaultSupplyTa: UmiPublicKey | undefined = undefined; + let supplyVaultAuthority: UmiPublicKey | undefined = undefined; + if (args.__kind === "Deposit" || args.__kind === "Withdraw") { + positionSupplyTa = publicKey( + args.__kind === "Withdraw" || this.pos.selfManaged + ? this.signerSupplyTa + : this.positionSupplyTa + ); + vaultSupplyTa = publicKey(this.marginfiSupplyAccounts.liquidityVault); + supplyVaultAuthority = publicKey( + this.marginfiSupplyAccounts.vaultAuthority + ); + } + + let positionDebtTa: UmiPublicKey | undefined = undefined; + let vaultDebtTa: UmiPublicKey | undefined = undefined; + let debtVaultAuthority: UmiPublicKey | undefined = undefined; + if (args.__kind === "Borrow" || args.__kind === "Repay") { + positionDebtTa = publicKey( + args.__kind === "Borrow" || this.pos.selfManaged + ? this.signerDebtTa + : this.positionDebtTa + ); + vaultDebtTa = publicKey(this.marginfiDebtAccounts.liquidityVault); + debtVaultAuthority = publicKey(this.marginfiDebtAccounts.vaultAuthority); + } + + let supplyPriceOracle: UmiPublicKey | undefined = undefined; + let debtPriceOracle: UmiPublicKey | undefined = undefined; + if (args.__kind === "Withdraw" || args.__kind === "Borrow") { + supplyPriceOracle = publicKey(this.supplyPriceOracle); + debtPriceOracle = publicKey(this.debtPriceOracle); + } + + return marginfiProtocolInteraction(this.umi, { + signer: this.signer, + marginfiProgram: publicKey(this.mfiAccounts.program), + solautoPosition: publicKey(this.pos.publicKey), + marginfiGroup: publicKey(this.marginfiGroup), + marginfiAccount: publicKey(this.marginfiAccountPk), + supplyBank: publicKey(this.marginfiSupplyAccounts.bank), + supplyPriceOracle, + positionSupplyTa, + vaultSupplyTa, + supplyVaultAuthority, + debtBank: publicKey(this.marginfiDebtAccounts.bank), + debtPriceOracle, + positionDebtTa, + vaultDebtTa, + debtVaultAuthority, + solautoAction: args, + }); + } + + rebalanceIx( + rebalanceStep: RebalanceStep, + data: RebalanceDetails + ): TransactionBuilder { + const preSwapRebalance = rebalanceStep === RebalanceStep.PreSwap; + const postSwapRebalance = rebalanceStep === RebalanceStep.PostSwap; + + const isFirstRebalance = + (preSwapRebalance && hasFirstRebalance(data.rebalanceType)) || + (postSwapRebalance && + data.rebalanceType === SolautoRebalanceType.FLSwapThenRebalance); + + const addAuthorityTas = + this.pos.selfManaged || data.values.tokenBalanceChange !== undefined; + + return marginfiRebalance(this.umi, { + signer: this.signer, + marginfiProgram: publicKey(this.mfiAccounts.program), + ixsSysvar: publicKey(SYSVAR_INSTRUCTIONS_PUBKEY), + solautoFeesTa: publicKey( + data.values.rebalanceDirection === RebalanceDirection.Boost + ? this.solautoFeesSupplyTa + : this.solautoFeesDebtTa + ), + authorityReferralState: publicKey(this.referralState), + referredByTa: this.referredByState + ? publicKey( + data.values.rebalanceDirection === RebalanceDirection.Boost + ? this.referredBySupplyTa()! + : this.referredByDebtTa()! + ) + : undefined, + positionAuthority: + data.values.tokenBalanceChange !== undefined + ? publicKey(this.authority) + : undefined, + solautoPosition: publicKey(this.pos.publicKey), + marginfiGroup: publicKey(this.marginfiGroup), + marginfiAccount: publicKey(this.marginfiAccountPk), + intermediaryTa: publicKey( + getTokenAccount( + toWeb3JsPublicKey(this.signer.publicKey), + new PublicKey(data.swapQuote.inputMint) + ) + ), + supplyBank: publicKey(this.marginfiSupplyAccounts.bank), + supplyPriceOracle: publicKey(this.supplyPriceOracle), + positionSupplyTa: publicKey(this.positionSupplyTa), + authoritySupplyTa: addAuthorityTas + ? publicKey(getTokenAccount(this.authority, this.pos.supplyMint)) + : undefined, + vaultSupplyTa: publicKey(this.marginfiSupplyAccounts.liquidityVault), + supplyVaultAuthority: publicKey( + this.marginfiSupplyAccounts.vaultAuthority + ), + debtBank: publicKey(this.marginfiDebtAccounts.bank), + debtPriceOracle: publicKey(this.debtPriceOracle), + positionDebtTa: publicKey(this.positionDebtTa), + authorityDebtTa: addAuthorityTas + ? publicKey(getTokenAccount(this.authority, this.pos.debtMint)) + : undefined, + vaultDebtTa: publicKey(this.marginfiDebtAccounts.liquidityVault), + debtVaultAuthority: publicKey(this.marginfiDebtAccounts.vaultAuthority), + rebalanceType: data.rebalanceType, + targetLiqUtilizationRateBps: data.targetLiqUtilizationRateBps ?? null, + swapInAmountBaseUnit: isFirstRebalance + ? parseInt(data.swapQuote.inAmount) + : null, + swapType: + data.swapQuote.swapMode === "ExactOut" && isFirstRebalance + ? SwapType.ExactOut + : null, + priceType: isFirstRebalance ? data.priceType : null, + flashLoanFeeBps: + data.flashLoan?.flFeeBps && isFirstRebalance + ? data.flashLoan.flFeeBps + : null, + }); + } +} diff --git a/solauto-sdk/src/services/solauto/txHandler.ts b/solauto-sdk/src/services/solauto/txHandler.ts new file mode 100644 index 00000000..35aafb1e --- /dev/null +++ b/solauto-sdk/src/services/solauto/txHandler.ts @@ -0,0 +1,72 @@ +import { Connection, PublicKey } from "@solana/web3.js"; +import { Signer, signerIdentity, Umi } from "@metaplex-foundation/umi"; +import { + WalletAdapter, + walletAdapterIdentity, +} from "@metaplex-foundation/umi-signer-wallet-adapters"; +import { consoleLog, getSolanaRpcConnection } from "../../utils"; +import { SOLAUTO_PROD_PROGRAM } from "../../constants"; +import { ProgramEnv } from "../../types"; + +export interface TxHandlerProps { + signer?: Signer; + wallet?: WalletAdapter; + rpcUrl: string; + showLogs?: boolean; + programId?: PublicKey; + lpEnv?: ProgramEnv; +} + +export class TxHandler { + public rpcUrl!: string; + public showLogs = false; + public programId!: PublicKey; + public lpEnv!: ProgramEnv; + + public connection!: Connection; + public umi!: Umi; + public signer!: Signer; + public otherSigners: Signer[] = []; + + constructor(props: TxHandlerProps) { + this.programId = props.programId ?? SOLAUTO_PROD_PROGRAM; + this.lpEnv = props.lpEnv ?? "Prod"; + + this.rpcUrl = props.rpcUrl; + const [connection, umi] = getSolanaRpcConnection( + this.rpcUrl, + this.programId, + this.lpEnv + ); + this.connection = connection; + this.umi = umi; + + if (!props.signer && !props.wallet) { + throw new Error("Signer or wallet must be provided"); + } + this.umi = this.umi.use( + props.signer + ? signerIdentity(props.signer, true) + : walletAdapterIdentity(props.wallet!, true) + ); + this.signer = this.umi.identity; + + if (props.showLogs !== undefined) { + this.showLogs = props.showLogs; + } + + if (!(globalThis as any).SHOW_LOGS && this.showLogs) { + (globalThis as any).SHOW_LOGS = Boolean(this.showLogs); + } + } + + log(...args: any[]): void { + consoleLog(...args); + } + + defaultLookupTables(): string[] { + return []; + } + + resetLiveTxUpdates(success?: boolean) {} +} diff --git a/solauto-sdk/src/services/swap/index.ts b/solauto-sdk/src/services/swap/index.ts new file mode 100644 index 00000000..36ff8444 --- /dev/null +++ b/solauto-sdk/src/services/swap/index.ts @@ -0,0 +1 @@ +export * from "./jupSwapManager"; diff --git a/solauto-sdk/src/services/swap/jupSwapManager.ts b/solauto-sdk/src/services/swap/jupSwapManager.ts new file mode 100644 index 00000000..1df49106 --- /dev/null +++ b/solauto-sdk/src/services/swap/jupSwapManager.ts @@ -0,0 +1,211 @@ +import { PublicKey } from "@solana/web3.js"; +import { + Signer, + TransactionBuilder, + transactionBuilder, +} from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { + createJupiterApiClient, + QuoteResponse, + SwapInstructionsResponse, +} from "@jup-ag/api"; +import { + consoleLog, + retryWithExponentialBackoff, + getWrappedInstruction, + fromBps, + toBps, + getTokenAccount, + jupIxToSolanaIx, + tokenInfo, +} from "../../utils"; +import { TransactionItemInputs } from "../../types"; +import { TokenInfo } from "../../constants"; + +export interface SwapInput { + inputMint: PublicKey; + outputMint: PublicKey; + amount: bigint; + exactIn?: boolean; + exactOut?: boolean; + slippageBps?: number; +} + +export interface SwapParams extends SwapInput { + destinationWallet?: PublicKey; + slippageIncFactor?: number; + wrapAndUnwrapSol?: boolean; +} + +export interface JupSwapTransactionData { + jupQuote: QuoteResponse; + setupIx: TransactionBuilder; + swapIx: TransactionBuilder; + cleanupIx: TransactionBuilder; + lookupTableAddresses: string[]; +} + +export class JupSwapManager { + jupApi = createJupiterApiClient({ + basePath: "https://lite-api.jup.ag" + }); + + public jupQuote: QuoteResponse | undefined = undefined; + + constructor( + private signer: Signer, + private limitSize?: boolean + ) {} + + public async getQuote(data: SwapInput): Promise { + const inputMintInfo: TokenInfo | undefined = tokenInfo(data.inputMint); + const outputMintInfo: TokenInfo | undefined = tokenInfo(data.outputMint); + const lowLiquidityMint = + (!inputMintInfo?.isMajor && !inputMintInfo?.isLST) || + (!outputMintInfo?.isMajor && !outputMintInfo?.isLST); + const slippageBps = data.slippageBps ?? (lowLiquidityMint ? 250 : 100); + + return await retryWithExponentialBackoff( + async (attemptNum: number) => + await this.jupApi.quoteGet({ + amount: Number(data.amount), + inputMint: data.inputMint.toString(), + outputMint: data.outputMint.toString(), + swapMode: data.exactOut + ? "ExactOut" + : data.exactIn + ? "ExactIn" + : undefined, + slippageBps, + maxAccounts: + !data.exactOut && this.limitSize + ? (lowLiquidityMint ? 25 : 15) + attemptNum * 5 + : undefined, + excludeDexes: ["Obric V2"], + }), + 6, + 250 + ); + } + + private async getJupInstructions( + data: SwapParams + ): Promise { + if (!this.jupQuote) { + throw new Error( + "Fetch a quote first before getting Jupiter instructions" + ); + } + + const instructions = await retryWithExponentialBackoff( + async () => { + const res = await this.jupApi.swapInstructionsPost({ + swapRequest: { + userPublicKey: this.signer.publicKey.toString(), + quoteResponse: this.jupQuote!, + wrapAndUnwrapSol: data.wrapAndUnwrapSol ?? false, + destinationTokenAccount: getTokenAccount( + data.destinationWallet ?? + toWeb3JsPublicKey(this.signer.publicKey), + data.outputMint + ).toString(), + }, + }); + if (!res) { + throw new Error("No instructions retrieved"); + } + return res; + }, + 4, + 200 + ); + if (!instructions.swapInstruction) { + throw new Error("No swap instruction was returned by Jupiter"); + } + return instructions; + } + + priceImpactBps() { + return Math.round(toBps(parseFloat(this.jupQuote!.priceImpactPct))) + 1; + } + + private adaptSlippageToPriceImpact(slippageIncFactor: number) { + const finalPriceSlippageBps = Math.round( + Math.max(20, this.jupQuote!.slippageBps, this.priceImpactBps()) * + (1 + slippageIncFactor) + ); + this.jupQuote!.slippageBps = finalPriceSlippageBps; + } + + private addInAmountSlippagePadding() { + consoleLog("Raw inAmount:", this.jupQuote!.inAmount); + const inc = Math.max( + fromBps(this.priceImpactBps()) * 1.1, + fromBps(this.jupQuote!.slippageBps) * 0.1 + ); + consoleLog("Inc:", inc); + this.jupQuote!.inAmount = Math.round( + parseInt(this.jupQuote!.inAmount) + + parseInt(this.jupQuote!.inAmount) * inc + ).toString(); + consoleLog("Increased inAmount:", this.jupQuote!.inAmount); + } + + async getJupSwapTxData(data: SwapParams): Promise { + if (!this.jupQuote) { + this.jupQuote = await this.getQuote(data); + } + + if (data.slippageIncFactor) { + this.adaptSlippageToPriceImpact(data.slippageIncFactor); + } + consoleLog("Quote:", this.jupQuote); + consoleLog("route plan", this.jupQuote.routePlan); + + const instructions = await this.getJupInstructions(data); + + if (data.exactOut) { + this.addInAmountSlippagePadding(); + } + + return { + jupQuote: this.jupQuote, + lookupTableAddresses: instructions.addressLookupTableAddresses, + setupIx: transactionBuilder( + (instructions.setupInstructions ?? []).map((ix) => + getWrappedInstruction(this.signer, jupIxToSolanaIx(ix)) + ) + ), + swapIx: transactionBuilder([ + getWrappedInstruction( + this.signer, + jupIxToSolanaIx(instructions.swapInstruction) + ), + ]), + cleanupIx: transactionBuilder( + instructions.cleanupInstruction + ? [ + getWrappedInstruction( + this.signer, + jupIxToSolanaIx(instructions.cleanupInstruction) + ), + ] + : [] + ), + }; + } + + async getSwapTx(data: SwapParams): Promise { + const swapData = await this.getJupSwapTxData(data); + + return { + tx: transactionBuilder().add([ + swapData.setupIx, + swapData.swapIx, + swapData.cleanupIx, + ]), + lookupTableAddresses: swapData.lookupTableAddresses, + }; + } +} diff --git a/solauto-sdk/src/services/transactions/index.ts b/solauto-sdk/src/services/transactions/index.ts new file mode 100644 index 00000000..256d07b1 --- /dev/null +++ b/solauto-sdk/src/services/transactions/index.ts @@ -0,0 +1,3 @@ +export * from "./manager"; +export * from "./types"; +export * from "./transactionUtils"; diff --git a/solauto-sdk/src/services/transactions/manager/clientTransactionsManager.ts b/solauto-sdk/src/services/transactions/manager/clientTransactionsManager.ts new file mode 100644 index 00000000..08d64af2 --- /dev/null +++ b/solauto-sdk/src/services/transactions/manager/clientTransactionsManager.ts @@ -0,0 +1,113 @@ +import { + transactionBuilder, + TransactionBuilder, +} from "@metaplex-foundation/umi"; +import { SolautoClient } from "../../solauto"; +import { TransactionsManager } from "./transactionsManager"; +import { + retryWithExponentialBackoff, +} from "../../../utils"; +import { TransactionItem } from "../types"; +import { getTransactionChores } from "../transactionUtils"; +import { CHORES_TX_NAME } from "../../../constants"; + +export class ClientTransactionsManager extends TransactionsManager { + private async updateLut(tx: TransactionBuilder, newLut: boolean) { + const lutInputs = await this.lookupTables.getLutInputs(); + const updateLutTxName = `${newLut ? "create" : "update"} lookup table`; + await retryWithExponentialBackoff( + async (attemptNum, prevError) => + await this.sendTransaction( + tx.setAddressLookupTables(lutInputs), + updateLutTxName, + attemptNum, + this.getUpdatedPriorityFeeSetting(prevError, attemptNum), + "skip-simulation" + ), + this.signableRetries, + 150, + this.errorsToThrow + ); + await this.txHandler.refetchReferralState(); + } + + private async addChoreTxs( + txs: TransactionItem[], + updateLutTx?: TransactionBuilder + ) { + let [choresBefore, choresAfter] = await getTransactionChores( + this.txHandler, + transactionBuilder().add( + txs + .filter((x) => x.tx && x.tx.getInstructions().length > 0) + .map((x) => x.tx!) + ) + ); + + if (updateLutTx) { + choresBefore = choresBefore.prepend(updateLutTx); + } + + if (choresBefore.getInstructions().length) { + const chore = new TransactionItem( + async () => ({ tx: choresBefore }), + CHORES_TX_NAME + ); + await chore.initialize(); + txs.unshift(chore); + this.txHandler.log( + "Chores before: ", + choresBefore.getInstructions().length + ); + } + + if (choresAfter.getInstructions().length) { + const chore = new TransactionItem( + async () => ({ tx: choresAfter }), + CHORES_TX_NAME + ); + await chore.initialize(); + txs.push(chore); + this.txHandler.log( + "Chores after: ", + choresAfter.getInstructions().length + ); + } + } + + public async send(transactions: TransactionItem[]) { + const items = [...transactions]; + const client = this.txHandler as SolautoClient; + + const updateLut = await client.updateLookupTable(); + + const updateLutInSepTx = + updateLut?.new || (updateLut?.accountsToAdd ?? []).length > 4; + if (updateLut && updateLutInSepTx) { + await this.updateLut(updateLut.tx, updateLut.new); + } + this.lookupTables.defaultLuts = client.defaultLookupTables(); + + // await addSwbOraclePullTxs(this.txHandler, items); + + for (const item of items) { + await item.initialize(); + } + + await this.addChoreTxs( + items, + updateLut && !updateLutInSepTx ? updateLut.tx : undefined + ); + + const result = await super.send(items).catch((e) => { + client.resetLiveTxUpdates(false); + throw e; + }); + + if (this.txRunType !== "only-simulate") { + await client.resetLiveTxUpdates(); + } + + return result; + } +} diff --git a/solauto-sdk/src/services/transactions/manager/index.ts b/solauto-sdk/src/services/transactions/manager/index.ts new file mode 100644 index 00000000..c3a4b549 --- /dev/null +++ b/solauto-sdk/src/services/transactions/manager/index.ts @@ -0,0 +1,2 @@ +export * from "./transactionsManager"; +export * from "./clientTransactionsManager"; \ No newline at end of file diff --git a/solauto-sdk/src/services/transactions/manager/transactionsManager.ts b/solauto-sdk/src/services/transactions/manager/transactionsManager.ts new file mode 100644 index 00000000..c7d9b809 --- /dev/null +++ b/solauto-sdk/src/services/transactions/manager/transactionsManager.ts @@ -0,0 +1,619 @@ +import bs58 from "bs58"; +import { TransactionExpiredBlockheightExceededError } from "@solana/web3.js"; +import { TransactionBuilder } from "@metaplex-foundation/umi"; +import { + PriorityFeeSetting, + priorityFeeSettingValues, + TransactionRunType, +} from "../../../types"; +import { + addTxOptimizations, + canSerializeTransaction, + consoleLog, + ErrorsToThrow, + getActualTxSize, + retryWithExponentialBackoff, + sendSingleOptimizedTransaction, + sendJitoBundledTransactions, +} from "../../../utils"; +import { TxHandler } from "../../solauto"; +import { getErrorInfo } from "../transactionUtils"; +import { + JITO_TIP_BUFFER_BYTES, + LookupTables, + TransactionItem, + TransactionSet, +} from "../types"; +import { UPDATE_ORACLE_TX_NAME } from "../../../constants"; + +export class TransactionTooLargeError extends Error { + constructor(message: string) { + super(message); + this.name = "TransactionTooLargeError"; + Object.setPrototypeOf(this, TransactionTooLargeError.prototype); + } +} + +export enum TransactionStatus { + Skipped = "Skipped", + Processing = "Processing", + Queued = "Queued", + Successful = "Successful", + Failed = "Failed", +} + +export interface TransactionManagerStatus { + name: string; + attemptNum: number; + status: TransactionStatus; + moreInfo?: string; + simulationSuccessful?: boolean; + txSig?: string; +} + +export type TransactionManagerStatuses = TransactionManagerStatus[]; + +interface RetryConfig { + signableRetries?: number; + totalRetries?: number; + retryDelay?: number; +} + +export interface TransactionsManagerArgs { + txHandler: T; + statusCallback?: (statuses: TransactionManagerStatuses) => void; + txRunType?: TransactionRunType; + priorityFeeSetting?: PriorityFeeSetting; + atomically?: boolean; + errorsToThrow?: ErrorsToThrow; + retryConfig?: RetryConfig; + abortController?: AbortController; +} + +export class TransactionsManager { + protected txHandler: T; + protected statusCallback?: (statuses: TransactionManagerStatuses) => void; + protected txRunType?: TransactionRunType; + protected priorityFeeSetting: PriorityFeeSetting; + protected atomically: boolean; + protected errorsToThrow?: ErrorsToThrow; + protected statuses: TransactionManagerStatuses = []; + protected lookupTables: LookupTables; + protected signableRetries: number; + protected totalRetries: number; + protected retryDelay: number; + protected abortController?: AbortController; + + constructor(args: TransactionsManagerArgs) { + this.txHandler = args.txHandler; + this.statusCallback = args.statusCallback; + this.txRunType = args.txRunType; + this.priorityFeeSetting = args.priorityFeeSetting ?? PriorityFeeSetting.Min; + this.atomically = args.atomically ?? true; + this.errorsToThrow = args.errorsToThrow; + this.abortController = args.abortController; + + this.lookupTables = new LookupTables( + this.txHandler.defaultLookupTables(), + this.txHandler.umi + ); + this.signableRetries = + args.retryConfig?.signableRetries ?? args.retryConfig?.totalRetries ?? 4; + this.totalRetries = + args.retryConfig?.totalRetries ?? args.retryConfig?.signableRetries ?? 4; + this.retryDelay = args.retryConfig?.retryDelay ?? 150; + } + + private async assembleTransactionSets( + items: TransactionItem[] + ): Promise { + let transactionSets: TransactionSet[] = []; + consoleLog(`Reassembling ${items.length} items`); + + const txItems = items.sort((a, b) => a.orderPrio - b.orderPrio); + + for (let i = txItems.length - 1; i >= 0; ) { + let item = txItems[i]; + i--; + + if (!item.tx) { + continue; + } + + const transaction = addTxOptimizations( + this.txHandler.umi, + item.tx, + 1, + 1 + ).setAddressLookupTables( + await this.lookupTables.getLutInputs(item.lookupTableAddresses) + ); + // Check if transaction can be serialized with buffer for Jito tip instruction + if ( + !canSerializeTransaction( + this.txHandler.umi, + transaction, + JITO_TIP_BUFFER_BYTES + ) + ) { + const actualSize = getActualTxSize(this.txHandler.umi, transaction); + throw new TransactionTooLargeError( + `Exceeds max transaction size (actual: ${actualSize ?? "failed to serialize"} + ~${JITO_TIP_BUFFER_BYTES} bytes for Jito tip)` + ); + } else { + let newSet = new TransactionSet(this.txHandler, this.lookupTables, [ + item, + ]); + for (let j = i; j >= 0; j--) { + const tx = txItems[j]; + if (await newSet.fitsWith(tx)) { + newSet.prepend(tx); + i--; + } else { + break; + } + } + transactionSets.unshift(newSet); + } + } + + return transactionSets; + } + + private updateStatus(args: TransactionManagerStatus, reset?: boolean) { + if (!this.statuses.filter((x) => x.name === args.name)) { + this.statuses.push(args); + } else { + const idx = this.statuses.findIndex( + (x) => x.name === args.name && x.attemptNum === args.attemptNum + ); + if (idx !== -1) { + this.statuses[idx].status = args.status; + this.statuses[idx].txSig = args.txSig; + if (args.simulationSuccessful) { + this.statuses[idx].simulationSuccessful = args.simulationSuccessful; + } + if (args.moreInfo) { + this.statuses[idx].moreInfo = args.moreInfo; + } + if (reset) { + this.statuses[idx].txSig = undefined; + this.statuses[idx].simulationSuccessful = undefined; + this.statuses[idx].moreInfo = undefined; + } + } else { + this.statuses.push(args); + } + } + consoleLog( + `${args.name} ${args.attemptNum} is ${args.status + .toString() + .toLowerCase()}` + ); + this.statusCallback?.([...this.statuses]); + } + + private async debugAccounts(itemSet: TransactionSet, tx: TransactionBuilder) { + const lutInputs = await itemSet.lookupTables.getLutInputs([]); + const lutAccounts = lutInputs.map((x) => x.addresses).flat(); + for (const ix of tx.getInstructions()) { + const ixAccounts = ix.keys.map((x) => x.pubkey); + const accountsNotInLut = ixAccounts.filter( + (x) => !lutAccounts.includes(x) + ); + consoleLog( + `Program ${ix.programId}, data len: ${ + ix.data.length + }, LUT accounts data: ${ + ix.keys.filter((x) => lutAccounts.includes(x.pubkey)).length * 3 + }` + ); + if (accountsNotInLut.length > 0) { + consoleLog(`${accountsNotInLut.length} accounts not in LUT:`); + for (const key of accountsNotInLut) { + consoleLog(key.toString()); + } + } + } + } + + protected getUpdatedPriorityFeeSetting( + prevError: Error | undefined, + attemptNum: number + ) { + if (prevError instanceof TransactionExpiredBlockheightExceededError) { + const currIdx = priorityFeeSettingValues.indexOf(this.priorityFeeSetting); + return priorityFeeSettingValues[ + Math.min( + priorityFeeSettingValues.length - 1, + currIdx + Math.floor(attemptNum / 3) + ) + ]; + } + return this.priorityFeeSetting; + } + + private updateStatusForSets( + txNames: string[], + args: Omit, + txSigs?: string[], + reset?: boolean + ) { + txNames.forEach((name, i) => { + this.updateStatus( + { + name, + txSig: txSigs && txSigs.length > i ? txSigs[i] : undefined, + ...args, + }, + reset + ); + }); + } + + public async send( + items: TransactionItem[] + ): Promise { + this.statuses = []; + this.lookupTables.reset(); + + const itemSets = await retryWithExponentialBackoff(async () => { + for (const item of items) { + if (!item.initialized) { + await item.initialize(); + } + } + consoleLog("Transaction items:", items.length); + return await this.assembleTransactionSets(items); + }, this.totalRetries); + + this.updateStatusForSets( + itemSets.map((x) => x.name()), + { + status: TransactionStatus.Queued, + attemptNum: 0, + } + ); + consoleLog("Initial item sets:", itemSets.length); + + if (this.atomically) { + await this.processTransactionsAtomically(itemSets); + } else { + let currentIndex = 0; + while (currentIndex < itemSets.length) { + await this.processTransactionSet(itemSets, currentIndex); + currentIndex++; + } + } + + return this.statuses; + } + + private shouldProceedToSend(itemSets: TransactionSet[], attemptNum: number) { + if (itemSets.length === 0) { + return false; + } + + const newItemSetNames = itemSets.flatMap((x) => + x.items.map((y) => y.name ?? "") + ); + if ( + newItemSetNames.length === 1 && + newItemSetNames[0] === UPDATE_ORACLE_TX_NAME + ) { + consoleLog("Skipping unnecessary oracle update"); + this.updateStatusForSets( + itemSets.map((x) => x.name()), + { + status: TransactionStatus.Skipped, + attemptNum, + } + ); + return false; + } + + return true; + } + + private async refreshItemSets( + itemSets: TransactionSet[], + attemptNum: number, + prevError?: Error, + currentIndex?: number + ): Promise { + if (currentIndex !== undefined) { + const itemSet = itemSets[currentIndex]; + await itemSet.reset(); + await itemSet.refetchAll(attemptNum, prevError); + } else { + await Promise.all(itemSets.map((itemSet) => itemSet.reset())); + for (const itemSet of itemSets) { + await itemSet.refetchAll(attemptNum, prevError); + } + } + + const newItemSets = await this.assembleTransactionSets( + currentIndex !== undefined + ? [ + ...itemSets[currentIndex].items, + ...itemSets.slice(currentIndex + 1).flatMap((set) => set.items), + ] + : itemSets.flatMap((set) => set.items) + ); + + if (currentIndex !== undefined && newItemSets.length > 1) { + itemSets.splice( + currentIndex, + itemSets.length - currentIndex, + ...newItemSets + ); + const startOfQueuedStatuses = this.statuses.findIndex( + (x) => x.status === TransactionStatus.Queued + ); + this.statuses.splice( + startOfQueuedStatuses, + this.statuses.length - startOfQueuedStatuses, + ...newItemSets.map((x, i) => ({ + name: x.name(), + attemptNum: i === 0 ? attemptNum : 0, + status: + i === 0 ? TransactionStatus.Processing : TransactionStatus.Queued, + })) + ); + } + + return newItemSets; + } + + private async processTransactionsAtomically(itemSets: TransactionSet[]) { + await retryWithExponentialBackoff( + async (attemptNum, prevError) => { + if ( + prevError && + this.statuses.filter((x) => x.simulationSuccessful).length > + this.signableRetries + ) { + throw prevError; + } + + this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting( + prevError, + attemptNum + ); + + if (attemptNum > 0) { + const refreshedSets = await this.refreshItemSets( + itemSets, + attemptNum, + prevError + ); + if (!refreshedSets || !refreshedSets.length) { + return; + } else { + itemSets = refreshedSets; + } + } + + if (!this.shouldProceedToSend(itemSets, attemptNum)) { + return; + } + + await this.sendJitoBundle(itemSets, attemptNum); + }, + this.totalRetries, + this.retryDelay, + this.errorsToThrow + ); + } + + private async sendJitoBundle(itemSets: TransactionSet[], attemptNum: number) { + let transactions: TransactionBuilder[] = []; + let txNames: string[] = []; + + try { + for (const set of itemSets) { + transactions.push(await set.getSingleTransaction()); + } + transactions = transactions.filter((x) => x.getInstructions().length > 0); + + txNames = itemSets.map((x) => x.name()); + if (transactions.length === 0) { + this.updateStatusForSets(txNames, { + status: TransactionStatus.Skipped, + attemptNum, + }); + return; + } + + this.updateStatusForSets( + txNames, + { + status: TransactionStatus.Processing, + attemptNum, + }, + undefined, + true + ); + for (const itemSet of itemSets) { + await this.debugAccounts(itemSet, await itemSet.getSingleTransaction()); + } + + const txSigs = await sendJitoBundledTransactions( + this.txHandler.umi, + this.txHandler.connection, + this.txHandler.signer, + this.txHandler.otherSigners, + transactions, + this.txRunType, + this.priorityFeeSetting, + () => + this.updateStatusForSets(txNames, { + status: TransactionStatus.Processing, + attemptNum, + simulationSuccessful: true, + }), + this.abortController + ); + + if ( + this.txRunType !== "only-simulate" && + (!Boolean(txSigs) || txSigs?.length === 0) && + !this.abortController?.signal.aborted + ) { + this.updateStatusForSets( + txNames, + { + status: TransactionStatus.Failed, + attemptNum, + }, + txSigs + ); + } + + this.updateStatusForSets( + txNames, + { status: TransactionStatus.Successful, attemptNum }, + txSigs + ); + } catch (e: any) { + this.captureErrorInfo(transactions, txNames, attemptNum, e); + } + } + + private async processTransactionSet( + itemSets: TransactionSet[], + currentIndex: number + ) { + let itemSet: TransactionSet | undefined = itemSets[currentIndex]; + await retryWithExponentialBackoff( + async (attemptNum, prevError) => { + if ( + prevError && + this.statuses.filter((x) => x.simulationSuccessful).length > + this.signableRetries + ) { + throw prevError; + } + + if (currentIndex > 0 || attemptNum > 0) { + const refreshedSets = await this.refreshItemSets( + itemSets, + attemptNum, + prevError, + currentIndex + ); + itemSet = refreshedSets ? refreshedSets[0] : undefined; + } + if (!itemSet || !this.shouldProceedToSend([itemSet], attemptNum)) { + return; + } + + const tx = await itemSet.getSingleTransaction(); + if (tx.getInstructions().length === 0) { + this.updateStatus({ + name: itemSet.name(), + status: TransactionStatus.Skipped, + attemptNum, + }); + } else { + await this.debugAccounts(itemSet, tx); + this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting( + prevError, + attemptNum + ); + await this.sendTransaction( + tx, + itemSet.name(), + attemptNum, + this.priorityFeeSetting + ); + } + }, + this.totalRetries, + this.retryDelay, + this.errorsToThrow + ); + } + + protected async sendTransaction( + tx: TransactionBuilder, + name: string, + attemptNum: number, + priorityFeeSetting?: PriorityFeeSetting, + txRunType?: TransactionRunType + ) { + this.updateStatus( + { + name, + status: TransactionStatus.Processing, + attemptNum, + }, + true + ); + try { + const txSig = await sendSingleOptimizedTransaction( + this.txHandler.umi, + this.txHandler.connection, + tx, + txRunType ?? this.txRunType, + priorityFeeSetting, + () => + this.updateStatus({ + name, + status: TransactionStatus.Processing, + attemptNum, + simulationSuccessful: true, + }), + this.abortController + ); + this.updateStatus({ + name, + status: TransactionStatus.Successful, + attemptNum, + txSig: txSig ? bs58.encode(txSig) : undefined, + }); + } catch (e: any) { + this.captureErrorInfo([tx], [name], attemptNum, e); + } + } + + private captureErrorInfo( + transactions: TransactionBuilder[], + txNames: string[], + attemptNum: number, + error: any + ) { + consoleLog("Capturing error info..."); + const errorDetails = getErrorInfo( + this.txHandler.umi, + transactions, + error, + txNames.filter( + (x) => + this.statuses.find((y) => x === y.name && y.attemptNum === attemptNum) + ?.simulationSuccessful + ).length === txNames.length, + this.priorityFeeSetting + ); + + const errorString = `${errorDetails.errorName ?? "Unknown error"}: ${ + errorDetails.errorInfo?.split("\n")[0] ?? "unknown" + }`; + const errorInfo = + errorDetails.errorName || errorDetails.errorInfo + ? errorString + : error.message; + this.updateStatusForSets(txNames, { + status: errorDetails.canBeIgnored + ? TransactionStatus.Skipped + : TransactionStatus.Failed, + attemptNum, + moreInfo: errorInfo, + }); + consoleLog(errorString); + + if (!errorDetails.canBeIgnored) { + throw errorDetails.errorName ? new Error(errorString) : error; + } + } +} diff --git a/solauto-sdk/src/transactions/transactionUtils.ts b/solauto-sdk/src/services/transactions/transactionUtils.ts similarity index 63% rename from solauto-sdk/src/transactions/transactionUtils.ts rename to solauto-sdk/src/services/transactions/transactionUtils.ts index 8dcb9aa8..c34be856 100644 --- a/solauto-sdk/src/transactions/transactionUtils.ts +++ b/solauto-sdk/src/services/transactions/transactionUtils.ts @@ -1,3 +1,8 @@ +import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; +import { + ACCOUNT_SIZE as TOKEN_ACCOUNT_SIZE, + NATIVE_MINT, +} from "@solana/spl-token"; import { Instruction, ProgramError, @@ -8,16 +13,12 @@ import { transactionBuilder, } from "@metaplex-foundation/umi"; import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; -import { PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY } from "@solana/web3.js"; -import { - ACCOUNT_SIZE as TOKEN_ACCOUNT_SIZE, - NATIVE_MINT, -} from "@solana/spl-token"; import { InvalidRebalanceConditionError, - LendingPlatform, + PriceType, SolautoAction, SolautoRebalanceType, + SwapType, TokenType, convertReferralFees, createSolautoProgram, @@ -26,34 +27,21 @@ import { getSolautoErrorFromCode, isSolautoAction, solautoAction, -} from "../generated"; -import { SolautoClient } from "../clients/solautoClient"; +} from "../../generated"; +import { SolautoClient, ReferralStateManager } from "../solauto"; import { closeTokenAccountUmiIx, createAssociatedTokenAccountUmiIx, systemTransferUmiIx, -} from "../utils/solanaUtils"; -import { getJupSwapTransaction } from "../utils/jupiterUtils"; -import { - getFlashLoanDetails, - getJupSwapRebalanceDetails, - getRebalanceValues, -} from "../utils/solauto/rebalanceUtils"; -import { - currentUnixSeconds, + consoleLog, getSolanaAccountCreated, rpcAccountCreated, -} from "../utils/generalUtils"; -import { SolautoMarginfiClient } from "../clients/solautoMarginfiClient"; -import { - getMaxLiqUtilizationRateBps, uint8ArrayToBigInt, -} from "../utils/numberUtils"; -import { - eligibleForRebalance, - positionStateWithLatestPrices, -} from "../utils/solauto/generalUtils"; -import { getTokenAccount, getTokenAccountData } from "../utils/accountUtils"; + getTokenAccount, + getTokenAccountData, + isMarginfiClient, + addTxOptimizations, +} from "../../utils"; import { createMarginfiProgram, getLendingAccountBorrowInstructionDataSerializer, @@ -61,17 +49,19 @@ import { getLendingAccountRepayInstructionDataSerializer, getLendingAccountWithdrawInstructionDataSerializer, getMarginfiErrorFromCode, - MARGINFI_PROGRAM_ID, -} from "../marginfi-sdk"; -import { ReferralStateManager } from "../clients"; +} from "../../externalSdks/marginfi"; +import { JupSwapManager } from "../swap"; import { createJupiterProgram, getJupiterErrorFromCode, JUPITER_PROGRAM_ID, -} from "../jupiter-sdk"; -import { PRICES } from "../constants"; -import { TransactionItemInputs } from "../types"; -import { safeGetPrice } from "../utils"; +} from "../../externalSdks/jupiter"; +import { + TransactionItemInputs, + BundleSimulationError, + PriorityFeeSetting, +} from "../../types"; +import { isMarginfiProgram } from "../../constants"; interface wSolTokenUsage { wSolTokenAccount: PublicKey; @@ -87,8 +77,8 @@ function getWSolUsage( }, cancellingDcaIn?: TokenType ): wSolTokenUsage | undefined { - const supplyIsWsol = client.supplyMint.equals(NATIVE_MINT); - const debtIsWsol = client.debtMint.equals(NATIVE_MINT); + const supplyIsWsol = client.pos.supplyMint.equals(NATIVE_MINT); + const debtIsWsol = client.pos.debtMint.equals(NATIVE_MINT); if (!supplyIsWsol && !debtIsWsol) { return undefined; } @@ -151,23 +141,20 @@ async function transactionChoresBefore( ); } - if (client.selfManaged) { - if (client.solautoPositionData === null) { - chores = chores.add(client.openPosition()); - } else if ( - client.lendingPlatform === LendingPlatform.Marginfi && - !(await getSolanaAccountCreated( - client.umi, - (client as SolautoMarginfiClient).marginfiAccountPk - )) + if (client.pos.selfManaged) { + if ( + isMarginfiClient(client) && + !(await getSolanaAccountCreated(client.umi, client.marginfiAccountPk)) ) { chores = chores.add( - (client as SolautoMarginfiClient).marginfiAccountInitialize( - (client as SolautoMarginfiClient).marginfiAccount as Signer - ) + client.marginfiAccountInitialize(client.marginfiAccount as Signer) ); } - // TODO: PF + // TODO: LP + + if (!client.pos.exists) { + chores = chores.add(client.openPositionIx()); + } } const wSolUsage = getWSolUsage( @@ -236,18 +223,19 @@ async function transactionChoresBefore( const tokenAccount = isSolautoAction("Withdraw", solautoAction) ? client.signerSupplyTa : client.signerDebtTa; + if (accountsGettingCreated.includes(tokenAccount.toString())) { continue; } - if (!getSolanaAccountCreated(client.umi, tokenAccount)) { + if (!(await getSolanaAccountCreated(client.umi, tokenAccount))) { chores = chores.add( createAssociatedTokenAccountUmiIx( client.signer, toWeb3JsPublicKey(client.signer.publicKey), isSolautoAction("Withdraw", solautoAction) - ? client.supplyMint - : client.debtMint + ? client.pos.supplyMint + : client.pos.debtMint ) ); accountsGettingCreated.push(tokenAccount.toString()); @@ -276,11 +264,6 @@ export async function rebalanceChoresBefore( client.referredBySupplyTa() && usesAccount(client.referredBySupplyTa()!); const checkReferralDebtTa = client.referredByDebtTa() && usesAccount(client.referredByDebtTa()!); - const checkIntermediaryMfiAccount = - client.lendingPlatform === LendingPlatform.Marginfi && - usesAccount( - (client as SolautoMarginfiClient).intermediaryMarginfiAccountPk - ); const checkSignerSupplyTa = usesAccount(client.signerSupplyTa); const checkSignerDebtTa = usesAccount(client.signerDebtTa); @@ -289,24 +272,14 @@ export async function rebalanceChoresBefore( checkReferralSupplyTa ? client.referredBySupplyTa() : PublicKey.default, ], ...[checkReferralDebtTa ? client.referredByDebtTa() : PublicKey.default], - ...[ - checkIntermediaryMfiAccount - ? (client as SolautoMarginfiClient).intermediaryMarginfiAccountPk - : PublicKey.default, - ], ...[checkSignerSupplyTa ? client.signerSupplyTa : PublicKey.default], ...[checkSignerDebtTa ? client.signerDebtTa : PublicKey.default], ]; - const [ - referredBySupplyTa, - referredByDebtTa, - intermediaryMarginfiAccount, - signerSupplyTa, - signerDebtTa, - ] = await client.umi.rpc.getAccounts( - accountsNeeded.map((x) => publicKey(x ?? PublicKey.default)) - ); + const [referredBySupplyTa, referredByDebtTa, signerSupplyTa, signerDebtTa] = + await client.umi.rpc.getAccounts( + accountsNeeded.map((x) => publicKey(x ?? PublicKey.default)) + ); let chores = transactionBuilder(); @@ -316,7 +289,7 @@ export async function rebalanceChoresBefore( createAssociatedTokenAccountUmiIx( client.signer, client.referredByState!, - client.supplyMint + client.pos.supplyMint ) ); } @@ -327,19 +300,7 @@ export async function rebalanceChoresBefore( createAssociatedTokenAccountUmiIx( client.signer, client.referredByState!, - client.debtMint - ) - ); - } - - if ( - checkIntermediaryMfiAccount && - !rpcAccountCreated(intermediaryMarginfiAccount) - ) { - client.log("Creating intermediary marginfi account"); - chores = chores.add( - (client as SolautoMarginfiClient).marginfiAccountInitialize( - (client as SolautoMarginfiClient).intermediaryMarginfiAccountSigner! + client.pos.debtMint ) ); } @@ -354,7 +315,7 @@ export async function rebalanceChoresBefore( createAssociatedTokenAccountUmiIx( client.signer, toWeb3JsPublicKey(client.signer.publicKey), - client.supplyMint + client.pos.supplyMint ) ); accountsGettingCreated.push(signerSupplyTa.publicKey.toString()); @@ -370,7 +331,7 @@ export async function rebalanceChoresBefore( createAssociatedTokenAccountUmiIx( client.signer, toWeb3JsPublicKey(client.signer.publicKey), - client.debtMint + client.pos.debtMint ) ); accountsGettingCreated.push(signerDebtTa.publicKey.toString()); @@ -417,9 +378,12 @@ function getRebalanceInstructions( try { const serializer = getMarginfiRebalanceInstructionDataSerializer(); const discriminator = serializer.serialize({ - targetInAmountBaseUnit: 0, - rebalanceType: SolautoRebalanceType.None, + swapInAmountBaseUnit: 0, + rebalanceType: SolautoRebalanceType.Regular, + swapType: SwapType.ExactIn, targetLiqUtilizationRateBps: 0, + priceType: PriceType.Realtime, + flashLoanFeeBps: null, })[0]; const [data, _] = serializer.deserialize(x.data); if (data.discriminator === discriminator) { @@ -452,13 +416,14 @@ function getSolautoActions(umi: Umi, tx: TransactionBuilder): SolautoAction[] { } catch {} } - if (x.programId === MARGINFI_PROGRAM_ID) { + if (isMarginfiProgram(toWeb3JsPublicKey(x.programId))) { try { const serializer = getLendingAccountDepositInstructionDataSerializer(); const discriminator = uint8ArrayToBigInt( serializer .serialize({ amount: 0, + depositUpToLimit: true, }) .slice(0, 8) ); @@ -558,7 +523,7 @@ function getSolautoActions(umi: Umi, tx: TransactionBuilder): SolautoAction[] { } catch {} } - // TODO: PF + // TODO: LP }); return solautoActions; @@ -579,7 +544,7 @@ export async function getTransactionChores( client, accountsGettingCreated, solautoActions, - client.livePositionUpdates.dcaInBalance + client.contextUpdates.dcaInBalance ), await rebalanceChoresBefore(client, tx, accountsGettingCreated), ]); @@ -588,187 +553,13 @@ export async function getTransactionChores( transactionChoresAfter( client, solautoActions, - client.livePositionUpdates.cancellingDca + client.contextUpdates.cancellingDca ) ); return [choresBefore, choresAfter]; } -export async function requiresRefreshBeforeRebalance(client: SolautoClient) { - if ( - client.solautoPositionState!.liqUtilizationRateBps > - getMaxLiqUtilizationRateBps( - client.solautoPositionState!.maxLtvBps, - client.solautoPositionState!.liqThresholdBps, - 0.01 - ) - ) { - return true; - } else if (client.solautoPositionData && !client.selfManaged) { - if ( - client.livePositionUpdates.supplyAdjustment > BigInt(0) || - client.livePositionUpdates.debtAdjustment > BigInt(0) - ) { - return false; - } - - const oldStateWithLatestPrices = await positionStateWithLatestPrices( - client.solautoPositionData.state, - PRICES[client.supplyMint.toString()].price, - PRICES[client.debtMint.toString()].price - ); - const utilizationRateDiff = Math.abs( - (client.solautoPositionState?.liqUtilizationRateBps ?? 0) - - oldStateWithLatestPrices.liqUtilizationRateBps - ); - - client.log("Liq utilization rate diff:", utilizationRateDiff); - if ( - client.livePositionUpdates.supplyAdjustment === BigInt(0) && - client.livePositionUpdates.debtAdjustment === BigInt(0) && - utilizationRateDiff >= 10 - ) { - client.log("Choosing to refresh before rebalance"); - return true; - } - } - - // Rebalance ix will already refresh internally if position is self managed, has automation to update, or position state last updated >= 1 day ago - - client.log("Not refreshing before rebalance"); - return false; -} - -export async function buildSolautoRebalanceTransaction( - client: SolautoClient, - targetLiqUtilizationRateBps?: number, - attemptNum?: number -): Promise { - client.solautoPositionState = await client.getFreshPositionState(); - const supplyPrice = safeGetPrice(client.supplyMint) ?? 0; - const debtPrice = safeGetPrice(client.debtMint) ?? 0; - - if ( - (client.solautoPositionState?.supply.amountUsed.baseUnit === BigInt(0) && - client.livePositionUpdates.supplyAdjustment === BigInt(0)) || - (targetLiqUtilizationRateBps === undefined && - !eligibleForRebalance( - client.solautoPositionState!, - client.solautoPositionSettings(), - client.solautoPositionActiveDca(), - currentUnixSeconds(), - supplyPrice, - debtPrice - )) - ) { - client.log("Not eligible for a rebalance"); - return undefined; - } - - const values = getRebalanceValues( - client.solautoPositionState!, - client.solautoPositionSettings(), - client.solautoPositionActiveDca(), - currentUnixSeconds(), - supplyPrice, - debtPrice, - targetLiqUtilizationRateBps - ); - client.log("Rebalance values: ", values); - - const swapDetails = getJupSwapRebalanceDetails( - client, - values, - targetLiqUtilizationRateBps, - attemptNum - ); - const { - jupQuote, - lookupTableAddresses, - setupInstructions, - tokenLedgerIx, - swapIx, - } = await getJupSwapTransaction(client.signer, swapDetails, attemptNum); - const flashLoan = getFlashLoanDetails(client, values, jupQuote); - - let tx = transactionBuilder(); - - if (await requiresRefreshBeforeRebalance(client)) { - tx = tx.add(client.refresh()); - } - - if (flashLoan) { - client.log("Flash loan details: ", flashLoan); - const addFirstRebalance = values.amountUsdToDcaIn > 0; - const rebalanceType = addFirstRebalance - ? SolautoRebalanceType.DoubleRebalanceWithFL - : SolautoRebalanceType.SingleRebalanceWithFL; - - tx = tx.add([ - setupInstructions, - tokenLedgerIx, - client.flashBorrow( - flashLoan, - getTokenAccount( - toWeb3JsPublicKey(client.signer.publicKey), - swapDetails.inputMint - ) - ), - ...(addFirstRebalance - ? [ - client.rebalance( - "A", - jupQuote, - rebalanceType, - values, - flashLoan, - targetLiqUtilizationRateBps - ), - ] - : []), - swapIx, - client.rebalance( - "B", - jupQuote, - rebalanceType, - values, - flashLoan, - targetLiqUtilizationRateBps - ), - client.flashRepay(flashLoan), - ]); - } else { - const rebalanceType = SolautoRebalanceType.Regular; - tx = tx.add([ - setupInstructions, - tokenLedgerIx, - client.rebalance( - "A", - jupQuote, - rebalanceType, - values, - undefined, - targetLiqUtilizationRateBps - ), - swapIx, - client.rebalance( - "B", - jupQuote, - rebalanceType, - values, - undefined, - targetLiqUtilizationRateBps - ), - ]); - } - - return { - tx, - lookupTableAddresses, - }; -} - export async function convertReferralFeesToDestination( referralManager: ReferralStateManager, tokenAccount: PublicKey, @@ -782,8 +573,9 @@ export async function convertReferralFeesToDestination( return undefined; } - const { lookupTableAddresses, setupInstructions, swapIx } = - await getJupSwapTransaction(referralManager.umi.identity, { + const jupSwapManager = new JupSwapManager(referralManager.umi.identity); + const { lookupTableAddresses, setupIx, swapIx, cleanupIx } = + await jupSwapManager.getJupSwapTxData({ amount: tokenAccountData.amount, destinationWallet: referralManager.referralState, inputMint: tokenAccountData.mint, @@ -793,10 +585,10 @@ export async function convertReferralFeesToDestination( }); let tx = transactionBuilder() - .add(setupInstructions) + .add(setupIx) .add( convertReferralFees(referralManager.umi, { - signer: referralManager.umi.identity, + signer: referralManager.signer, intermediaryTa: publicKey( getTokenAccount( toWeb3JsPublicKey(referralManager.umi.identity.publicKey), @@ -813,62 +605,154 @@ export async function convertReferralFeesToDestination( return { tx, lookupTableAddresses }; } -export function getErrorInfo(umi: Umi, tx: TransactionBuilder, error: any) { +export function getErrorInfo( + umi: Umi, + txs: TransactionBuilder[], + error: Error, + simulationSuccessful?: boolean, + priorityFeeSetting?: PriorityFeeSetting +) { let canBeIgnored = false; let errorName: string | undefined = undefined; let errorInfo: string | undefined = undefined; - try { - let programError: ProgramError | null = null; + let errTxIdx: number = 0; + let errIxIdx: number | undefined; + let errCode: number | undefined; + let errName: string | undefined; + + // sub ixs to account for computeUnitLimit and computeUnitPrice that get added + const getComputeIxs = (txIdx: number) => + addTxOptimizations( + umi, + txs[txIdx], + simulationSuccessful && usePriorityFee(priorityFeeSetting) + ? 1 + : undefined, + 1 + ).getInstructions().length - + txs[txIdx].getInstructions().length - + (txs.length > 1 && txIdx === 0 ? 1 : 0); // Account for jito tip IX - if (typeof error === "object" && (error as any)["InstructionError"]) { + try { + if (error instanceof BundleSimulationError) { + errTxIdx = error.details.transactionIdx; + errIxIdx = error.details.instructionIdx - getComputeIxs(errTxIdx); + errCode = error.details.errorCode; + } else if ( + typeof error === "object" && + (error as any)["InstructionError"] + ) { const err = (error as any)["InstructionError"]; - const errIx = tx.getInstructions()[Math.max(0, err[0])]; - const errCode = + + errIxIdx = err[0] - getComputeIxs(0); + errCode = typeof err[1] === "object" && "Custom" in err[1] ? err[1]["Custom"] : undefined; - const errName = errCode === undefined ? (err[1] as string) : undefined; - let programName = ""; + errName = errCode === undefined ? (err[1] as string) : undefined; + } - if ( - errIx.programId.toString() === + consoleLog( + "Transaction instructions:", + txs.map((x) => + x + .getInstructions() + .map((y) => y.programId.toString()) + .join(",") + ) + ); + + let programError: ProgramError | null = null; + let programName = ""; + const errIx = + errTxIdx !== undefined && errIxIdx !== undefined + ? txs[errTxIdx].getInstructions()[Math.max(0, errIxIdx)] + : undefined; + + consoleLog("Error transaction index:", errTxIdx); + consoleLog("Error instruction index:", errIxIdx); + consoleLog("Error code:", errCode); + consoleLog("Error instruction program:", errIx?.programId.toString()); + if (errIx) { + consoleLog("Error ix data length:", errIx.data.length); + consoleLog("Error ix num keys:", errIx.keys.length); + consoleLog( + "Error ix data (first 64 bytes hex):", + Buffer.from(errIx.data.slice(0, 64)).toString("hex") + ); + } + consoleLog( + "All ixs in failing tx:", + txs[errTxIdx] + ?.getInstructions() + .map( + (ix, i) => + `[${i}] ${ix.programId.toString()} (data: ${ix.data.length}B, keys: ${ix.keys.length})` + ) + ); + + const solautoError = getSolautoErrorFromCode( + errCode ?? -1, + createSolautoProgram() + ); + const marginfiError = getMarginfiErrorFromCode( + errCode ?? -1, + createMarginfiProgram() + ); + + if ( + errCode !== undefined && + errIx?.programId.toString() === umi.programs.get("solauto").publicKey.toString() + ) { + programError = solautoError ?? marginfiError; + programName = "Haven"; + if ( + programError?.name === + new InvalidRebalanceConditionError(createSolautoProgram()).name ) { - programError = getSolautoErrorFromCode(errCode, createSolautoProgram()); - programName = "Haven"; - if ( - programError?.name === - new InvalidRebalanceConditionError(createSolautoProgram()).name - ) { - canBeIgnored = true; - } - } else if (errIx.programId === MARGINFI_PROGRAM_ID) { - programName = "Marginfi"; - programError = getMarginfiErrorFromCode( - errCode, - createMarginfiProgram() - ); - } else if (errIx.programId === JUPITER_PROGRAM_ID) { - programName = "Jupiter"; - programError = getJupiterErrorFromCode(errCode, createJupiterProgram()); - } - - if (errName && errCode === undefined) { - errorName = `${programName ?? "Program"} error`; - errorInfo = errName; + canBeIgnored = true; } + } else if ( + errCode !== undefined && + errIx && + isMarginfiProgram(toWeb3JsPublicKey(errIx.programId)) + ) { + programName = "Marginfi"; + programError = marginfiError; + } else if ( + errCode !== undefined && + errIx?.programId === JUPITER_PROGRAM_ID + ) { + programName = "Jupiter"; + programError = getJupiterErrorFromCode(errCode, createJupiterProgram()); } if (programError) { errorName = programError?.name; errorInfo = programError?.message; + } else if (errName) { + errorName = `${programName ?? "Program"} error`; + errorInfo = errName; } - } catch {} + } catch (e) { + consoleLog(e); + } - return { - errorName: errorName, - errorInfo: errorInfo, + const errData = { + errorName, + errorInfo, canBeIgnored, }; + consoleLog(errData); + + return errData; +} + +export function usePriorityFee(priorityFeeSetting?: PriorityFeeSetting) { + return ( + priorityFeeSetting !== undefined && + priorityFeeSetting !== PriorityFeeSetting.None + ); } diff --git a/solauto-sdk/src/services/transactions/types/index.ts b/solauto-sdk/src/services/transactions/types/index.ts new file mode 100644 index 00000000..1026d52d --- /dev/null +++ b/solauto-sdk/src/services/transactions/types/index.ts @@ -0,0 +1,3 @@ +export * from "./lookupTables"; +export * from "./transactionItem"; +export * from "./transactionSet"; \ No newline at end of file diff --git a/solauto-sdk/src/services/transactions/types/lookupTables.ts b/solauto-sdk/src/services/transactions/types/lookupTables.ts new file mode 100644 index 00000000..aa6481da --- /dev/null +++ b/solauto-sdk/src/services/transactions/types/lookupTables.ts @@ -0,0 +1,37 @@ +import { AddressLookupTableInput, Umi } from "@metaplex-foundation/umi"; +import { getAddressLookupInputs } from "../../../utils"; + +export class LookupTables { + cache: AddressLookupTableInput[] = []; + + constructor( + public defaultLuts: string[], + private umi: Umi + ) {} + + async getLutInputs( + additionalAddresses?: string[] + ): Promise { + const addresses = [...this.defaultLuts, ...(additionalAddresses ?? [])]; + const currentCacheAddresses = this.cache.map((x) => x.publicKey.toString()); + + const missingAddresses = addresses.filter( + (x) => !currentCacheAddresses.includes(x) + ); + if (missingAddresses) { + const additionalInputs = await getAddressLookupInputs( + this.umi, + missingAddresses + ); + this.cache.push(...additionalInputs); + } + + return this.cache; + } + + reset() { + this.cache = this.cache.filter((x) => + this.defaultLuts.includes(x.publicKey.toString()) + ); + } +} diff --git a/solauto-sdk/src/services/transactions/types/transactionItem.ts b/solauto-sdk/src/services/transactions/types/transactionItem.ts new file mode 100644 index 00000000..362037a8 --- /dev/null +++ b/solauto-sdk/src/services/transactions/types/transactionItem.ts @@ -0,0 +1,43 @@ +import { TransactionBuilder } from "@metaplex-foundation/umi"; +import { TransactionItemInputs } from "../../../types"; + +export class TransactionItem { + lookupTableAddresses!: string[]; + tx?: TransactionBuilder; + initialized: boolean = false; + orderPrio: number = 0; + + constructor( + public fetchTx: ( + attemptNum: number, + prevError?: Error + ) => Promise, + public name?: string, + public oracleInteractor?: boolean + ) {} + + async initialize() { + await this.refetch(0); + this.initialized = true; + } + + async refetch(attemptNum: number, prevError?: Error) { + const resp = await this.fetchTx(attemptNum, prevError); + this.tx = resp?.tx; + this.lookupTableAddresses = resp?.lookupTableAddresses ?? []; + this.orderPrio = resp?.orderPrio ?? 0; + } + + uniqueAccounts(): string[] { + return Array.from( + new Set( + this.tx!.getInstructions() + .map((x) => [ + x.programId.toString(), + ...x.keys.map((y) => y.pubkey.toString()), + ]) + .flat() + ) + ); + } +} diff --git a/solauto-sdk/src/services/transactions/types/transactionSet.ts b/solauto-sdk/src/services/transactions/types/transactionSet.ts new file mode 100644 index 00000000..d11c83b0 --- /dev/null +++ b/solauto-sdk/src/services/transactions/types/transactionSet.ts @@ -0,0 +1,124 @@ +import { + AddressLookupTableInput, + transactionBuilder, + TransactionBuilder, + Umi, +} from "@metaplex-foundation/umi"; +import { TxHandler } from "../../solauto"; +import { LookupTables } from "./lookupTables"; +import { TransactionItem } from "./transactionItem"; +import { addTxOptimizations, canSerializeTransaction } from "../../../utils"; +import { CHORES_TX_NAME } from "../../../constants"; + +const MAX_SUPPORTED_ACCOUNT_LOCKS = 64; + +// Buffer for Jito tip instruction (~44 bytes) + potential new accounts in message +// This accounts for: System Transfer instruction data, Jito tip account (if new), etc. +export const JITO_TIP_BUFFER_BYTES = 75; + +export class TransactionSet { + constructor( + private txHandler: TxHandler, + public lookupTables: LookupTables, + public items: TransactionItem[] = [] + ) {} + + async lutInputs(): Promise { + const lutInputs = await this.lookupTables.getLutInputs(this.lutAddresses()); + + return lutInputs.filter( + (lut, index, self) => + index === + self.findIndex( + (item) => item.publicKey.toString() === lut.publicKey.toString() + ) + ); + } + + async fitsWith(item: TransactionItem): Promise { + if (!item.tx) { + return true; + } + + const accountLocks = Array.from( + new Set([ + ...this.items.map((x) => x.uniqueAccounts()), + ...item.uniqueAccounts(), + ]) + ).length; + if (accountLocks > MAX_SUPPORTED_ACCOUNT_LOCKS) { + return false; + } + + const singleTx = await this.getSingleTransaction(); + const tx = addTxOptimizations(this.txHandler.umi, singleTx, 1, 1) + .add(item.tx) + .setAddressLookupTables( + await this.lookupTables.getLutInputs([ + ...this.lutAddresses(), + ...item.lookupTableAddresses, + ]) + ); + + // Use actual serialization check with buffer for Jito tip instruction + return canSerializeTransaction( + this.txHandler.umi, + tx, + JITO_TIP_BUFFER_BYTES + ); + } + + add(...items: TransactionItem[]) { + this.items.push( + ...items.filter((x) => x.tx && x.tx.getInstructions().length > 0) + ); + } + + prepend(...items: TransactionItem[]) { + this.items.unshift( + ...items.filter((x) => x.tx && x.tx.getInstructions().length > 0) + ); + } + + async reset() { + await this.txHandler.resetLiveTxUpdates(); + } + + async refetchAll(attemptNum: number, prevError?: Error) { + for (const item of this.items) { + await item.refetch(attemptNum, prevError); + } + } + + async getSingleTransaction(): Promise { + const transactions = this.items + .filter((x) => x.tx && x.tx.getInstructions().length > 0) + .map((x) => x.tx!); + + return transactionBuilder() + .add(transactions) + .setAddressLookupTables(await this.lutInputs()); + } + + lutAddresses(): string[] { + return Array.from( + new Set(this.items.map((x) => x.lookupTableAddresses).flat()) + ); + } + + name(): string { + let names = this.items + .filter((x) => x.tx && Boolean(x.name)) + .map((x) => x.name!.toLowerCase()); + if (names.length > 1) { + names = names.filter((x) => x !== CHORES_TX_NAME); + } + if (names.length >= 3) { + return [names.slice(0, -1).join(", "), names[names.length - 1]].join( + ", and " + ); + } else { + return names.join(" & "); + } + } +} diff --git a/solauto-sdk/src/solautoPosition/index.ts b/solauto-sdk/src/solautoPosition/index.ts new file mode 100644 index 00000000..7767fc41 --- /dev/null +++ b/solauto-sdk/src/solautoPosition/index.ts @@ -0,0 +1,3 @@ +export * from "./solautoPositionEx"; +export * from "./marginfiSolautoPositionEx"; +export * from "./positionUtils"; diff --git a/solauto-sdk/src/solautoPosition/marginfiSolautoPositionEx.ts b/solauto-sdk/src/solautoPosition/marginfiSolautoPositionEx.ts new file mode 100644 index 00000000..417b0ed9 --- /dev/null +++ b/solauto-sdk/src/solautoPosition/marginfiSolautoPositionEx.ts @@ -0,0 +1,183 @@ +import { PublicKey } from "@solana/web3.js"; +import { Bank, PriceBias, safeFetchAllBank } from "../externalSdks/marginfi"; +import { publicKey } from "@metaplex-foundation/umi"; +import { + calcMarginfiMaxLtvAndLiqThresholdBps, + fetchTokenPrices, + fromBaseUnit, + getBankLiquidityAvailableBaseUnit, + getBankLiquidityUsedBaseUnit, + getMarginfiAccountPositionState, + getMarginfiPriceOracle, + safeGetPrice, + tokenInfo, + toRoundedUsdValue, + validPubkey, +} from "../utils"; +import { getMarginfiAccounts } from "../constants"; +import { SolautoPositionEx } from "./solautoPositionEx"; +import { LendingPlatform, PriceType } from "../generated"; + +export class MarginfiSolautoPositionEx extends SolautoPositionEx { + lendingPlatform = LendingPlatform.Marginfi; + maxLtvPriceType = PriceType.Ema; + + public supplyBank: Bank | null = null; + public debtBank: Bank | null = null; + + supplyPrice( + priceType?: PriceType, + withoutBias?: boolean + ): number | undefined { + return ( + this._supplyPrice ?? + safeGetPrice( + this.supplyMint, + priceType, + !withoutBias ? PriceBias.Low : undefined + ) + ); + } + + debtPrice(priceType?: PriceType, withoutBias?: boolean): number | undefined { + return ( + this._debtPrice ?? + safeGetPrice( + this.debtMint, + priceType, + !withoutBias ? PriceBias.High : undefined + ) + ); + } + + private getBankAccounts(mint: PublicKey) { + const group = this.lpPoolAccount.toString(); + const bankAccounts = getMarginfiAccounts(this.lpEnv).bankAccounts; + return bankAccounts[group][mint.toString()]; + } + + async getBanks(): Promise { + if (!this.supplyBank || !this.debtBank) { + [this.supplyBank, this.debtBank] = await safeFetchAllBank(this.umi, [ + publicKey(this.lpSupplyAccount), + publicKey(this.lpDebtAccount), + ]); + } + + return [this.supplyBank!, this.debtBank!]; + } + + async priceOracles(): Promise { + const [supplyBank, debtBank] = await this.getBanks(); + + return await Promise.all([ + getMarginfiPriceOracle(this.umi, { data: supplyBank }), + getMarginfiPriceOracle(this.umi, { data: debtBank }), + ]); + } + + async maxLtvAndLiqThresholdBps(): Promise<[number, number]> { + const [supplyBank, debtBank] = await this.getBanks(); + + const [supplyPrice] = await fetchTokenPrices([this.supplyMint]); + const [maxLtvBps, liqThresholdBps] = calcMarginfiMaxLtvAndLiqThresholdBps( + supplyBank, + debtBank, + supplyPrice + ); + + return [maxLtvBps, liqThresholdBps]; + } + + private getUpToDateLiquidityAvailable( + banks: Bank[], + mint: PublicKey, + availableToDeposit: boolean + ) { + const bank = banks.find( + (x) => + x.group.toString() === this.lpPoolAccount.toString() && + x.mint.toString() === mint.toString() + ); + + const baseUnit = getBankLiquidityAvailableBaseUnit( + bank!, + availableToDeposit + ); + return { + baseUnit: baseUnit, + baseAmountUsdValue: toRoundedUsdValue( + fromBaseUnit(baseUnit, tokenInfo(mint).decimals) * + (safeGetPrice(mint) ?? 0) + ), + }; + } + + updateSupplyLiquidityDepositable(banks: Bank[]) { + this._data.state.supply.amountCanBeUsed = + this.getUpToDateLiquidityAvailable(banks, this.supplyMint, true); + } + + updateDebtLiquidityAvailable(banks: Bank[]) { + this._data.state.debt.amountCanBeUsed = this.getUpToDateLiquidityAvailable( + banks, + this.debtMint, + false + ); + } + + get lpSupplyAccount() { + return new PublicKey(this.getBankAccounts(this.supplyMint).bank); + } + + get lpDebtAccount() { + return new PublicKey(this.getBankAccounts(this.debtMint).bank); + } + + get supplyLiquidityAvailable(): number { + return fromBaseUnit( + getBankLiquidityAvailableBaseUnit(this.supplyBank, false), + this.supplyMintInfo.decimals + ); + } + + get supplyUsdWithdrawable(): number { + const deposits = fromBaseUnit( + getBankLiquidityUsedBaseUnit(this.supplyBank, true), + this.supplyMintInfo.decimals + ); + const borrows = fromBaseUnit( + getBankLiquidityUsedBaseUnit(this.supplyBank, false), + this.supplyMintInfo.decimals + ); + return Math.min(deposits - borrows, this.totalSupply) * this.supplyPrice()!; + } + + async refreshPositionState(priceType?: PriceType): Promise { + if (!this.canRefreshPositionState()) { + return; + } + + const useDesignatedMint = + !this.exists || + !this.selfManaged || + (this.selfManaged && !validPubkey(this.lpUserAccount)); + + const resp = await getMarginfiAccountPositionState( + this.umi, + { pk: this.lpUserAccount }, + this._lpPoolAccount, + useDesignatedMint ? { mint: this.supplyMint } : undefined, + useDesignatedMint ? { mint: this.debtMint } : undefined, + this.contextUpdates, + priceType + ); + + if (resp) { + this.supplyBank = resp.supplyBank; + this.debtBank = resp.debtBank; + this._lpPoolAccount = resp.marginfiGroup; + this._data.state = resp.state; + } + } +} diff --git a/solauto-sdk/src/solautoPosition/positionUtils.ts b/solauto-sdk/src/solautoPosition/positionUtils.ts new file mode 100644 index 00000000..9fcaeca5 --- /dev/null +++ b/solauto-sdk/src/solautoPosition/positionUtils.ts @@ -0,0 +1,206 @@ +import { PublicKey } from "@solana/web3.js"; +import { Umi } from "@metaplex-foundation/umi"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { + LendingPlatform, + PositionState, + safeFetchAllSolautoPosition, + safeFetchSolautoPosition, + SolautoSettingsParameters, + SolautoSettingsParametersInpArgs, +} from "../generated"; +import { + ContextUpdates, + currentUnixSeconds, + getBatches, + getLiqUtilzationRateBps, + getSolautoPositionAccount, + retryWithExponentialBackoff, + toBaseUnit, + tokenInfo, + toRoundedUsdValue, +} from "../utils"; +import { + PositionCustomArgs, + PositionExArgs, + SolautoPositionEx, +} from "./solautoPositionEx"; +import { MarginfiSolautoPositionEx } from "./marginfiSolautoPositionEx"; + +export function createSolautoSettings( + settings: SolautoSettingsParametersInpArgs +): SolautoSettingsParameters { + return { + boostGap: settings.boostGap, + boostToBps: settings.boostToBps, + repayGap: settings.repayGap, + repayToBps: settings.repayToBps, + padding: new Array(24).fill(0), + }; +} + +export async function getPositionExBulk( + umi: Umi, + publicKeys: PublicKey[] +): Promise { + const batches = getBatches(publicKeys, 100); + + const data = ( + await Promise.all( + batches.map(async (pubkeys) => { + return retryWithExponentialBackoff( + async () => + await safeFetchAllSolautoPosition( + umi, + pubkeys.map((x) => fromWeb3JsPublicKey(x)) + ) + ); + }) + ) + ).flat(); + + return data.map((x) => { + switch (x.position.lendingPlatform) { + case LendingPlatform.Marginfi: + return new MarginfiSolautoPositionEx({ + umi, + publicKey: toWeb3JsPublicKey(x.publicKey), + data: x, + }); + // TODO: LP + } + }); +} + +export async function getOrCreatePositionEx( + umi: Umi, + authority: PublicKey, + positionId: number, + programId: PublicKey, + customArgs?: PositionCustomArgs, + contextUpdates?: ContextUpdates +): Promise { + const publicKey = getSolautoPositionAccount(authority, positionId, programId); + const data = await retryWithExponentialBackoff( + async () => + await safeFetchSolautoPosition(umi, fromWeb3JsPublicKey(publicKey)) + ); + + const lendingPlatform = data + ? data.position.lendingPlatform + : customArgs!.lendingPlatform; + + const args: PositionExArgs = { + umi, + publicKey, + authority, + positionId, + programId, + data: data ?? { + state: createFakePositionState( + { + mint: customArgs?.supplyMint ?? PublicKey.default, + }, + { mint: customArgs?.debtMint ?? PublicKey.default }, + 0, + 0 + ), + }, + customArgs, + contextUpdates, + }; + + let position: SolautoPositionEx; + switch (lendingPlatform) { + case LendingPlatform.Marginfi: + position = new MarginfiSolautoPositionEx(args); + // TODO: LP + } + + if (position.selfManaged) { + await position.refreshPositionState(); + } + + return position; +} + +interface AssetProps { + mint: PublicKey; + price?: number; + amountUsed?: number; + amountCanBeUsed?: number; +} + +export function createFakePositionState( + supply: AssetProps, + debt: AssetProps, + maxLtvBps: number, + liqThresholdBps: number +): PositionState { + const supplyDecimals = tokenInfo(supply.mint).decimals; + const debtDecimals = tokenInfo(debt.mint).decimals; + + const supplyUsd = (supply.amountUsed ?? 0) * (supply.price ?? 0); + const debtUsd = (debt.amountUsed ?? 0) * (debt.price ?? 0); + + return { + liqUtilizationRateBps: getLiqUtilzationRateBps( + supplyUsd, + debtUsd, + liqThresholdBps + ), + supply: { + amountUsed: { + baseUnit: toBaseUnit(supply.amountUsed ?? 0, supplyDecimals), + baseAmountUsdValue: toRoundedUsdValue(supplyUsd), + }, + amountCanBeUsed: { + baseUnit: toBaseUnit(supply.amountCanBeUsed ?? 0, supplyDecimals), + baseAmountUsdValue: toRoundedUsdValue( + (supply.amountCanBeUsed ?? 0) * (supply.price ?? 0) + ), + }, + baseAmountMarketPriceUsd: toRoundedUsdValue(supply.price ?? 0), + borrowFeeBps: 0, + decimals: supplyDecimals, + mint: fromWeb3JsPublicKey(supply.mint), + padding1: new Array(5).fill(0), + padding2: new Array(8).fill(0), + padding: new Uint8Array(32), + }, + debt: { + amountUsed: { + baseUnit: toBaseUnit(debt.amountUsed ?? 0, debtDecimals), + baseAmountUsdValue: toRoundedUsdValue(debtUsd), + }, + amountCanBeUsed: { + baseUnit: toBaseUnit(debt.amountCanBeUsed ?? 0, debtDecimals), + baseAmountUsdValue: toRoundedUsdValue( + (debt.amountCanBeUsed ?? 0) * (debt.price ?? 0) + ), + }, + baseAmountMarketPriceUsd: toRoundedUsdValue(debt.price ?? 0), + borrowFeeBps: 0, + decimals: debtDecimals, + mint: fromWeb3JsPublicKey(debt.mint), + padding1: new Array(5).fill(0), + padding2: new Array(8).fill(0), + padding: new Uint8Array(32), + }, + netWorth: { + baseUnit: supply.price + ? toBaseUnit((supplyUsd - debtUsd) / supply.price, supplyDecimals) + : BigInt(0), + baseAmountUsdValue: toRoundedUsdValue(supplyUsd - debtUsd), + }, + maxLtvBps, + liqThresholdBps, + lastRefreshed: BigInt(currentUnixSeconds()), + padding1: new Array(6).fill(0), + padding2: new Array(4).fill(0), + padding: new Array(2).fill(0), + }; +} diff --git a/solauto-sdk/src/solautoPosition/solautoPositionEx.ts b/solauto-sdk/src/solautoPosition/solautoPositionEx.ts new file mode 100644 index 00000000..cbc33a38 --- /dev/null +++ b/solauto-sdk/src/solautoPosition/solautoPositionEx.ts @@ -0,0 +1,533 @@ +import { PublicKey } from "@solana/web3.js"; +import { Umi } from "@metaplex-foundation/umi"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { + fetchSolautoPosition, + LendingPlatform, + PositionState, + PriceType, + SolautoPosition, + SolautoSettingsParameters, +} from "../generated"; +import { + calcDebtUsd, + calcNetWorth, + calcSupplyUsd, + calcTotalDebt, + calcTotalSupply, + consoleLog, + ContextUpdates, + currentUnixSeconds, + debtLiquidityAvailable, + debtLiquidityUsdAvailable, + getLiqUtilzationRateBps, + getSolautoPositionAccount, + maxBoostToBps, + maxRepayFromBps, + maxRepayToBps, + positionStateWithLatestPrices, + realtimeUsdToEmaUsd, + safeGetPrice, + solautoStrategyName, + supplyLiquidityDepositable, + supplyLiquidityUsdDepositable, + toBaseUnit, + tokenInfo, + toRoundedUsdValue, +} from "../utils"; +import { ProgramEnv, RebalanceAction } from "../types"; +import { + getDebtAdjustment, + getRebalanceValues, + SolautoFeesBps, +} from "../services/rebalance"; +import { TokenInfo } from "../constants"; + +export interface PositionCustomArgs { + lendingPlatform: LendingPlatform; + supplyMint?: PublicKey; + debtMint?: PublicKey; + lpPoolAccount?: PublicKey; + lpUserAccount?: PublicKey; + lpEnv?: ProgramEnv; +} + +interface SolautoPositionExData extends Partial { + state: PositionState; +} + +export interface PositionExArgs { + umi: Umi; + publicKey?: PublicKey; + programId?: PublicKey; + authority?: PublicKey; + positionId?: number; + data: SolautoPositionExData; + customArgs?: PositionCustomArgs; + contextUpdates?: ContextUpdates; +} + +export abstract class SolautoPositionEx { + public umi!: Umi; + protected contextUpdates?: ContextUpdates; + + public publicKey!: PublicKey; + public lendingPlatform!: LendingPlatform; + public positionId!: number; + public authority!: PublicKey; + protected _lpPoolAccount?: PublicKey; + public lpUserAccount?: PublicKey = undefined; + protected lpEnv!: ProgramEnv; + private _supplyMint?: PublicKey; + private _debtMint?: PublicKey; + protected _data!: SolautoPositionExData; + + private readonly firstState!: PositionState; + + protected _supplyPrice?: number; + protected _debtPrice?: number; + + public rebalance!: PositionRebalanceHelper; + public maxLtvPriceType!: PriceType; + + constructor(args: PositionExArgs) { + this.umi = args.umi; + this.contextUpdates = args.contextUpdates; + + this.publicKey = + args.publicKey ?? + getSolautoPositionAccount( + args.authority!, + args.positionId!, + args.programId! + ); + this.positionId = args.positionId ?? args.data.positionId![0]; + this.authority = args.authority ?? toWeb3JsPublicKey(args.data.authority!); + + this._lpPoolAccount = args.customArgs?.lpPoolAccount; + this.lpUserAccount = + args.customArgs?.lpUserAccount ?? + (args.data.position + ? toWeb3JsPublicKey(args.data.position!.lpUserAccount) + : undefined); + this.lpEnv = args.customArgs?.lpEnv ?? "Prod"; + this._supplyMint = args.customArgs?.supplyMint; + this._debtMint = args.customArgs?.debtMint; + + this._data = args.data; + this.firstState = { ...args.data.state }; + + this.rebalance = new PositionRebalanceHelper(this); + } + + get exists() { + return this._data.position !== undefined; + } + + get selfManaged() { + return this.positionId === 0; + } + + get positionType() { + return this._data.positionType; + } + + get strategyName() { + return solautoStrategyName(this.supplyMint, this.debtMint); + } + + get lpPoolAccount() { + return ( + this._lpPoolAccount ?? + toWeb3JsPublicKey(this.data.position!.lpPoolAccount) + ); + } + + liqUtilizationRateBps(priceType?: PriceType, withoutBias?: boolean): number { + return getLiqUtilzationRateBps( + this.supplyUsd(priceType, withoutBias), + this.debtUsd(priceType, withoutBias), + this.state.liqThresholdBps + ); + } + + protected get data(): SolautoPositionExData { + return this._data; + } + + get state(): PositionState { + return this.data.state; + } + + get settings(): SolautoSettingsParameters | undefined { + return this.contextUpdates?.settings ?? this.data.position?.settings; + } + + updateSettings(settings: SolautoSettingsParameters) { + this.data.position!.settings = settings; + } + + get supplyMint(): PublicKey { + return this._supplyMint ?? toWeb3JsPublicKey(this.state.supply.mint); + } + + get supplyMintInfo(): TokenInfo { + return tokenInfo(this.supplyMint); + } + + get debtMint(): PublicKey { + return this._debtMint ?? toWeb3JsPublicKey(this.state.debt.mint); + } + + get debtMintInfo(): TokenInfo { + return tokenInfo(this.debtMint); + } + + get boostToBps() { + return Math.min(this.settings?.boostToBps ?? 0, this.maxBoostToBps); + } + + get maxBoostToBps() { + return maxBoostToBps(this.state.maxLtvBps, this.state.liqThresholdBps); + } + + get boostFromBps() { + return this.boostToBps - (this.settings?.boostGap ?? 0); + } + + get repayToBps() { + return Math.min(this.settings?.repayToBps ?? 0, this.maxRepayToBps); + } + + get maxRepayToBps() { + return maxRepayToBps(this.state.maxLtvBps, this.state.liqThresholdBps); + } + + get repayFromBps() { + return Math.min( + (this.settings?.repayToBps ?? 0) + (this.settings?.repayGap ?? 0), + this.maxRepayFromBps + ); + } + + get maxRepayFromBps() { + return maxRepayFromBps(this.state.maxLtvBps, this.state.liqThresholdBps); + } + + get netWorth() { + return calcNetWorth(this.state); + } + + netWorthUsd(priceType?: PriceType) { + return this.supplyUsd(priceType) - this.debtUsd(priceType); + } + + get totalSupply() { + return calcTotalSupply(this.state); + } + + supplyUsd(priceType?: PriceType, withoutBias?: boolean) { + const supplyPrice = this.supplyPrice(priceType, withoutBias); + return supplyPrice + ? calcTotalSupply(this.state) * supplyPrice + : calcSupplyUsd(this.state); + } + + supplyPrice(priceType?: PriceType, withoutBias?: boolean) { + return this._supplyPrice ?? safeGetPrice(this.supplyMint, priceType); + } + + get totalDebt() { + return calcTotalDebt(this.state); + } + + debtUsd(priceType?: PriceType, withoutBias?: boolean) { + const debtPrice = this.debtPrice(priceType, withoutBias); + return debtPrice + ? calcTotalDebt(this.state) * debtPrice + : calcDebtUsd(this.state); + } + + debtPrice(priceType?: PriceType, withoutBias?: boolean) { + return this._debtPrice ?? safeGetPrice(this.debtMint, priceType); + } + + get supplyLiquidityDepositable() { + return supplyLiquidityDepositable(this.state); + } + + get supplyLiquidityUsdDepositable() { + return supplyLiquidityUsdDepositable(this.state); + } + + get supplyLiquidityUsdAvailable() { + return this.supplyLiquidityAvailable * (this.supplyPrice() ?? 0); + } + + get debtLiquidityAvailable() { + return debtLiquidityAvailable(this.state); + } + + get debtLiquidityUsdAvailable() { + return debtLiquidityUsdAvailable(this.state); + } + + abstract get lpSupplyAccount(): PublicKey; + abstract get lpDebtAccount(): PublicKey; + abstract get supplyLiquidityAvailable(): number; + abstract get supplyUsdWithdrawable(): number; + + abstract maxLtvAndLiqThresholdBps(): Promise<[number, number]>; + abstract priceOracles(): Promise; + + get memecoinPosition() { + return tokenInfo(this.supplyMint).isMeme || tokenInfo(this.debtMint).isMeme; + } + + eligibleForRebalance( + bpsDistanceThreshold: number = 0, + skipExtraChecks?: boolean + ): RebalanceAction | undefined { + return this.rebalance.eligibleForRebalance( + bpsDistanceThreshold, + skipExtraChecks + ); + } + + eligibleForRefresh(): boolean { + if (this.selfManaged) return false; + + return ( + currentUnixSeconds() - Number(this.state.lastRefreshed) > 60 * 60 * 24 * 7 + ); + } + + protected canRefreshPositionState() { + if ( + this.state.maxLtvBps === 0 || + currentUnixSeconds() - Number(this.state.lastRefreshed) > 5 || + this.contextUpdates?.positionUpdates() + ) { + return true; + } + } + + abstract refreshPositionState(priceType?: PriceType): Promise; + + async utilizationRateBpsDrift(priceType?: PriceType) { + const supplyPrice = this.supplyPrice(priceType) ?? 0; + const debtPrice = this.debtPrice(priceType) ?? 0; + const oldState = await positionStateWithLatestPrices( + this.firstState, + supplyPrice, + debtPrice + ); + const newState = await positionStateWithLatestPrices( + this.state, + supplyPrice, + debtPrice + ); + + return newState.liqUtilizationRateBps - oldState.liqUtilizationRateBps; + } + + updateSupply(newSupplyUsd: number, supplyPrice?: number) { + this._data.state.supply.amountUsed.baseAmountUsdValue = + toRoundedUsdValue(newSupplyUsd); + this._data.state.supply.amountUsed.baseUnit = toBaseUnit( + newSupplyUsd / (supplyPrice ?? this.supplyPrice() ?? 0), + this.supplyMintInfo.decimals + ); + } + + updateSupplyPrice(price: number) { + this._supplyPrice = price; + } + + updateDebt(newDebtUsd: number, debtPrice?: number) { + this._data.state.debt.amountUsed.baseAmountUsdValue = + toRoundedUsdValue(newDebtUsd); + this._data.state.debt.amountUsed.baseUnit = toBaseUnit( + newDebtUsd / (debtPrice ?? this.debtPrice() ?? 0), + this.debtMintInfo.decimals + ); + } + + updateDebtPrice(price: number) { + this._debtPrice = price; + } + + updateNetWorth(supplyPrice?: number) { + const netWorthUsd = this.supplyUsd() - this.debtUsd(); + this._data.state.netWorth.baseAmountUsdValue = + toRoundedUsdValue(netWorthUsd); + this._data.state.netWorth.baseUnit = toBaseUnit( + netWorthUsd / (supplyPrice ?? this.supplyPrice()!), + this.supplyMintInfo.decimals + ); + } + + updateLiqUtilizationRate( + supplyUsd?: number, + debtUsd?: number, + priceType?: PriceType + ) { + this._data.state.liqUtilizationRateBps = getLiqUtilzationRateBps( + supplyUsd ?? this.supplyUsd(priceType), + debtUsd ?? this.debtUsd(priceType), + this.state.liqThresholdBps + ); + } + + simulateRebalance( + unixTime: number, + supplyPrice: number, + debtPrice: number, + targetLiqUtilizationRateBps?: number, + withSolautoFee?: boolean + ) { + this.updateSupplyPrice(supplyPrice); + this.updateDebtPrice(debtPrice); + + this._data.state.lastRefreshed = BigInt(unixTime); + const rebalance = getRebalanceValues( + this, + PriceType.Realtime, + targetLiqUtilizationRateBps, + withSolautoFee + ? SolautoFeesBps.create( + true, + targetLiqUtilizationRateBps, + this.netWorthUsd() + ) + : undefined + ); + if (!rebalance) { + return undefined; + } + + this.updateSupply(rebalance.endResult.supplyUsd, supplyPrice); + this.updateDebt(rebalance.endResult.debtUsd, debtPrice); + this.updateNetWorth(supplyPrice); + this.updateLiqUtilizationRate( + rebalance.endResult.supplyUsd, + rebalance.endResult.debtUsd + ); + } + + async refetchPositionData() { + this._data = await fetchSolautoPosition( + this.umi, + fromWeb3JsPublicKey(this.publicKey) + ); + } +} + +class PositionRebalanceHelper { + constructor(private pos: SolautoPositionEx) {} + + private sufficientLiquidityToBoost() { + const limitsUpToDate = + this.pos.debtLiquidityUsdAvailable !== 0 || + this.pos.supplyLiquidityUsdDepositable !== 0; + + if (limitsUpToDate) { + const { debtAdjustmentUsd } = getDebtAdjustment( + this.pos.state.liqThresholdBps, + { supplyUsd: this.pos.supplyUsd(), debtUsd: this.pos.debtUsd() }, + this.pos.boostToBps, + { solauto: 50, lpBorrow: 50, flashLoan: 50 } // Overshoot fees + ); + + const sufficientLiquidity = + this.pos.debtLiquidityUsdAvailable * 0.95 > debtAdjustmentUsd && + this.pos.supplyLiquidityUsdDepositable * 0.95 > debtAdjustmentUsd; + + if (!sufficientLiquidity) { + consoleLog("Insufficient liquidity to further boost"); + } + return sufficientLiquidity; + } + + return true; + } + + validRealtimePricesBoost(debtAdjustmentUsd: number) { + const postRebalanceLiqUtilRate = getLiqUtilzationRateBps( + realtimeUsdToEmaUsd( + this.pos.supplyUsd() + debtAdjustmentUsd, + this.pos.supplyMint + ), + realtimeUsdToEmaUsd( + this.pos.debtUsd() + debtAdjustmentUsd, + this.pos.debtMint + ), + this.pos.state.liqThresholdBps + ); + + return postRebalanceLiqUtilRate <= this.pos.maxBoostToBps; + } + + private validBoostFromHere(bpsDistanceThreshold: number = 0) { + const realtimeSupplyUsd = this.pos.supplyUsd(PriceType.Realtime); + const realtimeDebtUsd = this.pos.debtUsd(PriceType.Realtime); + + if ( + (realtimeSupplyUsd === this.pos.supplyUsd(PriceType.Ema) && + realtimeDebtUsd === this.pos.debtUsd(PriceType.Ema)) || + this.pos.maxLtvPriceType !== PriceType.Ema + ) { + return true; + } + + const { debtAdjustmentUsd } = getDebtAdjustment( + this.pos.state.liqThresholdBps, + { + supplyUsd: realtimeSupplyUsd, + debtUsd: realtimeDebtUsd, + }, + this.pos.boostToBps, + { solauto: 25, lpBorrow: 0, flashLoan: 0 } // Undershoot fees + ); + + return ( + this.validRealtimePricesBoost(debtAdjustmentUsd) || + this.pos.liqUtilizationRateBps(PriceType.Ema, true) - + this.pos.boostFromBps <= + bpsDistanceThreshold + ); + } + + eligibleForRebalance( + bpsDistanceThreshold: number, + skipExtraChecks?: boolean + ): RebalanceAction | undefined { + if (this.pos.selfManaged || !this.pos.supplyUsd()) { + return undefined; + } + + const realtimeLiqUtilRateBps = this.pos.liqUtilizationRateBps( + PriceType.Realtime, + true + ); + + if ( + this.pos.repayFromBps - realtimeLiqUtilRateBps <= + bpsDistanceThreshold + ) { + return "repay"; + } else if ( + realtimeLiqUtilRateBps - this.pos.boostFromBps <= bpsDistanceThreshold && + (skipExtraChecks || + (this.validBoostFromHere(bpsDistanceThreshold) && + this.sufficientLiquidityToBoost())) + ) { + return "boost"; + } + + return undefined; + } +} diff --git a/solauto-sdk/src/transactions/index.ts b/solauto-sdk/src/transactions/index.ts deleted file mode 100644 index cd06c8ee..00000000 --- a/solauto-sdk/src/transactions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './transactionsManager'; -export * from './transactionUtils'; diff --git a/solauto-sdk/src/transactions/transactionsManager.ts b/solauto-sdk/src/transactions/transactionsManager.ts deleted file mode 100644 index 10a108e6..00000000 --- a/solauto-sdk/src/transactions/transactionsManager.ts +++ /dev/null @@ -1,760 +0,0 @@ -import bs58 from "bs58"; -import { - AddressLookupTableInput, - transactionBuilder, - TransactionBuilder, - Umi, -} from "@metaplex-foundation/umi"; -import { SolautoClient } from "../clients/solautoClient"; -import { - addTxOptimizations, - getAddressLookupInputs, - sendSingleOptimizedTransaction, -} from "../utils/solanaUtils"; -import { - ErrorsToThrow, - retryWithExponentialBackoff, -} from "../utils/generalUtils"; -import { getErrorInfo, getTransactionChores } from "./transactionUtils"; -import { - PriorityFeeSetting, - priorityFeeSettingValues, - TransactionItemInputs, - TransactionRunType, -} from "../types"; -import { ReferralStateManager, TxHandler } from "../clients"; -import { - PublicKey, - TransactionExpiredBlockheightExceededError, -} from "@solana/web3.js"; -import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants"; -import { buildSwbSubmitResponseTx, getSwitchboardFeedData } from "../utils"; -import { sendJitoBundledTransactions } from "../utils/jitoUtils"; - -const CHORES_TX_NAME = "account chores"; - -export class TransactionTooLargeError extends Error { - constructor(message: string) { - super(message); - this.name = "TransactionTooLargeError"; - Object.setPrototypeOf(this, TransactionTooLargeError.prototype); - } -} - -class LookupTables { - cache: AddressLookupTableInput[] = []; - - constructor( - public defaultLuts: string[], - private umi: Umi - ) {} - - async getLutInputs( - additionalAddresses: string[] - ): Promise { - const addresses = [...this.defaultLuts, ...additionalAddresses]; - const currentCacheAddresses = this.cache.map((x) => x.publicKey.toString()); - - const missingAddresses = addresses.filter( - (x) => !currentCacheAddresses.includes(x) - ); - if (missingAddresses) { - const additionalInputs = await getAddressLookupInputs( - this.umi, - missingAddresses - ); - this.cache.push(...additionalInputs); - } - - return this.cache; - } - - reset() { - this.cache = this.cache.filter((x) => - this.defaultLuts.includes(x.publicKey.toString()) - ); - } -} - -export class TransactionItem { - lookupTableAddresses!: string[]; - tx?: TransactionBuilder; - public initialized: boolean = false; - - constructor( - public fetchTx: ( - attemptNum: number - ) => Promise, - public name?: string - ) {} - - async initialize() { - await this.refetch(0); - this.initialized = true; - } - - async refetch(attemptNum: number) { - const resp = await this.fetchTx(attemptNum); - this.tx = resp?.tx; - this.lookupTableAddresses = resp?.lookupTableAddresses ?? []; - } - - uniqueAccounts(): string[] { - return Array.from( - new Set( - this.tx!.getInstructions() - .map((x) => [ - x.programId.toString(), - ...x.keys.map((y) => y.pubkey.toString()), - ]) - .flat() - ) - ); - } -} - -class TransactionSet { - constructor( - private txHandler: TxHandler, - public lookupTables: LookupTables, - public items: TransactionItem[] = [] - ) {} - - async fitsWith(item: TransactionItem): Promise { - if (!item.tx) { - return true; - } - - const accountLocks = Array.from( - new Set([ - ...this.items.map((x) => x.uniqueAccounts()), - ...item.uniqueAccounts(), - ]) - ).length; - if (accountLocks > 128) { - return false; - } - - const singleTx = await this.getSingleTransaction(); - const tx = addTxOptimizations(this.txHandler.umi.identity, singleTx, 1, 1) - .add(item.tx) - .setAddressLookupTables( - await this.lookupTables.getLutInputs([ - ...this.lutAddresses(), - ...item.lookupTableAddresses, - ]) - ); - - return tx.fitsInOneTransaction(this.txHandler.umi); - } - - add(...items: TransactionItem[]) { - this.items.push( - ...items.filter((x) => x.tx && x.tx.getInstructions().length > 0) - ); - } - - prepend(...items: TransactionItem[]) { - this.items.unshift( - ...items.filter((x) => x.tx && x.tx.getInstructions().length > 0) - ); - } - - async refetchAll(attemptNum: number) { - await this.txHandler.resetLiveTxUpdates(); - for (const item of this.items) { - await item.refetch(attemptNum); - } - } - - async getSingleTransaction(): Promise { - const transactions = this.items - .filter((x) => x.tx && x.tx.getInstructions().length > 0) - .map((x) => x.tx!); - - const lutInputs = await this.lookupTables.getLutInputs(this.lutAddresses()); - return transactionBuilder() - .add(transactions) - .setAddressLookupTables(lutInputs); - } - - lutAddresses(): string[] { - return Array.from( - new Set(this.items.map((x) => x.lookupTableAddresses).flat()) - ); - } - - name(): string { - let names = this.items - .filter((x) => x.tx && Boolean(x.name)) - .map((x) => x.name!.toLowerCase()); - if (names.length > 1) { - names = names.filter((x) => x !== CHORES_TX_NAME); - } - if (names.length >= 3) { - return [names.slice(0, -1).join(", "), names[names.length - 1]].join( - ", and " - ); - } else { - return names.join(" & "); - } - } -} - -export enum TransactionStatus { - Skipped = "Skipped", - Processing = "Processing", - Queued = "Queued", - Successful = "Successful", - Failed = "Failed", -} - -export type TransactionManagerStatuses = { - name: string; - attemptNum: number; - status: TransactionStatus; - moreInfo?: string; - simulationSuccessful?: boolean; - txSig?: string; -}[]; - -export class TransactionsManager { - private statuses: TransactionManagerStatuses = []; - private lookupTables: LookupTables; - - constructor( - private txHandler: SolautoClient | ReferralStateManager, - private statusCallback?: (statuses: TransactionManagerStatuses) => void, - private txType?: TransactionRunType, - private priorityFeeSetting: PriorityFeeSetting = PriorityFeeSetting.Min, - private atomically: boolean = false, - private errorsToThrow?: ErrorsToThrow, - private retries: number = 4, - private retryDelay: number = 150 - ) { - this.lookupTables = new LookupTables( - this.txHandler.defaultLookupTables(), - this.txHandler.umi - ); - } - - private async assembleTransactionSets( - items: TransactionItem[] - ): Promise { - let transactionSets: TransactionSet[] = []; - this.txHandler.log(`Reassembling ${items.length} items`); - - for (let i = items.length - 1; i >= 0; ) { - let item = items[i]; - i--; - - if (!item.tx) { - continue; - } - - const transaction = item.tx.setAddressLookupTables( - await this.lookupTables.getLutInputs(item.lookupTableAddresses) - ); - if (!transaction.fitsInOneTransaction(this.txHandler.umi)) { - throw new TransactionTooLargeError( - `Exceeds max transaction size (${transaction.getTransactionSize(this.txHandler.umi)})` - ); - } else { - let newSet = new TransactionSet(this.txHandler, this.lookupTables, [ - item, - ]); - for (let j = i; j >= 0; j--) { - if (await newSet.fitsWith(items[j])) { - newSet.prepend(items[j]); - i--; - } else { - break; - } - } - transactionSets.unshift(newSet); - } - } - - return transactionSets; - } - - private updateStatus( - name: string, - status: TransactionStatus, - attemptNum: number, - txSig?: string, - simulationSuccessful?: boolean, - moreInfo?: string - ) { - if (!this.statuses.filter((x) => x.name === name)) { - this.statuses.push({ - name, - status, - txSig, - attemptNum, - simulationSuccessful, - moreInfo, - }); - } else { - const idx = this.statuses.findIndex( - (x) => x.name === name && x.attemptNum === attemptNum - ); - if (idx !== -1) { - this.statuses[idx].status = status; - this.statuses[idx].txSig = txSig; - if (simulationSuccessful) { - this.statuses[idx].simulationSuccessful = simulationSuccessful; - } - if (moreInfo) { - this.statuses[idx].moreInfo = moreInfo; - } - } else { - this.statuses.push({ - name, - status, - txSig, - attemptNum, - simulationSuccessful, - moreInfo, - }); - } - } - this.txHandler.log(`${name} is ${status.toString().toLowerCase()}`); - this.statusCallback?.([...this.statuses]); - } - - private async debugAccounts(itemSet: TransactionSet, tx: TransactionBuilder) { - const lutInputs = await itemSet.lookupTables.getLutInputs([]); - const lutAccounts = lutInputs.map((x) => x.addresses).flat(); - for (const ix of tx.getInstructions()) { - const ixAccounts = ix.keys.map((x) => x.pubkey); - const accountsNotInLut = ixAccounts.filter( - (x) => !lutAccounts.includes(x) - ); - this.txHandler.log( - `Program ${ix.programId}, data len: ${ix.data.length}, LUT accounts data: ${ix.keys.filter((x) => lutAccounts.includes(x.pubkey)).length * 3}` - ); - if (accountsNotInLut.length > 0) { - this.txHandler.log(`${accountsNotInLut.length} accounts not in LUT:`); - for (const key of accountsNotInLut) { - this.txHandler.log(key.toString()); - } - } - } - } - - private getUpdatedPriorityFeeSetting( - prevError: Error | undefined, - attemptNum: number - ) { - if (prevError instanceof TransactionExpiredBlockheightExceededError) { - const currIdx = priorityFeeSettingValues.indexOf(this.priorityFeeSetting); - return priorityFeeSettingValues[ - Math.min( - priorityFeeSettingValues.length - 1, - currIdx + Math.floor(attemptNum / 2) - ) - ]; - } - return this.priorityFeeSetting; - } - - private updateStatusForSets( - itemSets: TransactionSet[], - status: TransactionStatus, - attemptNum: number, - txSigs?: string[], - simulationSuccessful?: boolean, - moreInfo?: string - ) { - itemSets.forEach((itemSet, i) => { - this.updateStatus( - itemSet.name(), - status, - attemptNum, - txSigs !== undefined ? txSigs[i] : undefined, - simulationSuccessful, - moreInfo - ); - }); - } - - private async updateLut(tx: TransactionBuilder, newLut: boolean) { - const updateLutTxName = `${newLut ? "create" : "update"} lookup table`; - await retryWithExponentialBackoff( - async (attemptNum, prevError) => - await this.sendTransaction( - tx, - updateLutTxName, - attemptNum, - this.getUpdatedPriorityFeeSetting(prevError, attemptNum) - ), - 3, - 150, - this.errorsToThrow - ); - } - - public async clientSend( - transactions: TransactionItem[] - ): Promise { - const items = [...transactions]; - const client = this.txHandler as SolautoClient; - - const updateLookupTable = await client.updateLookupTable(); - - if (updateLookupTable && updateLookupTable?.new) { - await this.updateLut(updateLookupTable.tx, updateLookupTable.new); - } - this.lookupTables.defaultLuts = client.defaultLookupTables(); - - for (const item of items) { - await item.initialize(); - } - - const allAccounts = items.flatMap((x) => - x.tx - ?.getInstructions() - .flatMap((x) => x.keys.map((x) => x.pubkey.toString())) - ); - const swbOracle = allAccounts.find((x) => - Object.values(SWITCHBOARD_PRICE_FEED_IDS).includes(x ?? "") - ); - if (swbOracle) { - const mint = new PublicKey( - Object.keys(SWITCHBOARD_PRICE_FEED_IDS).find( - (x) => SWITCHBOARD_PRICE_FEED_IDS[x] === swbOracle - )! - ); - const stale = (await getSwitchboardFeedData(client.connection, [mint]))[0] - .stale; - - if (stale) { - this.txHandler.log("Requires oracle update..."); - const swbTx = new TransactionItem( - async () => - buildSwbSubmitResponseTx(client.connection, client.signer, mint), - "Update oracle" - ); - await swbTx.initialize(); - items.unshift(swbTx); - } - } - - let [choresBefore, choresAfter] = await getTransactionChores( - client, - transactionBuilder().add( - items - .filter((x) => x.tx && x.tx.getInstructions().length > 0) - .map((x) => x.tx!) - ) - ); - if (updateLookupTable && !updateLookupTable?.new) { - choresBefore = choresBefore.prepend(updateLookupTable.tx); - } - if (choresBefore.getInstructions().length > 0) { - const chore = new TransactionItem( - async () => ({ tx: choresBefore }), - CHORES_TX_NAME - ); - await chore.initialize(); - items.unshift(chore); - this.txHandler.log( - "Chores before: ", - choresBefore.getInstructions().length - ); - } - if (choresAfter.getInstructions().length > 0) { - const chore = new TransactionItem( - async () => ({ tx: choresAfter }), - CHORES_TX_NAME - ); - await chore.initialize(); - items.push(chore); - this.txHandler.log( - "Chores after: ", - choresAfter.getInstructions().length - ); - } - - const result = await this.send(items).catch((e) => { - client.resetLiveTxUpdates(false); - throw e; - }); - - if (this.txType !== "only-simulate") { - await client.resetLiveTxUpdates(); - } - - return result; - } - - public async send( - items: TransactionItem[] - ): Promise { - this.statuses = []; - this.lookupTables.reset(); - - if (!items[0].initialized) { - for (const item of items) { - await item.initialize(); - } - } - - this.txHandler.log("Transaction items:", items.length); - const itemSets = await this.assembleTransactionSets(items); - this.updateStatusForSets(itemSets, TransactionStatus.Queued, 0); - this.txHandler.log("Initial item sets:", itemSets.length); - - if (this.txType === "only-simulate" && itemSets.length > 1) { - this.txHandler.log( - "Only simulate and more than 1 transaction. Skipping..." - ); - return []; - } - - if (itemSets.length > 1 && this.atomically) { - await this.processTransactionsAtomically(itemSets); - } else { - let currentIndex = 0; - while (currentIndex < itemSets.length) { - await this.processTransactionSet(itemSets, currentIndex); - currentIndex++; - } - } - - return this.statuses; - } - - private async processTransactionsAtomically(itemSets: TransactionSet[]) { - let num = 0; - - await retryWithExponentialBackoff( - async (attemptNum, prevError) => { - num = attemptNum; - - if (attemptNum > 0) { - for (let i = 0; i < itemSets.length; i++) { - await itemSets[i].refetchAll(attemptNum); - } - itemSets = await this.assembleTransactionSets( - itemSets.flatMap((x) => x.items) - ); - } - - let transactions = []; - for (const set of itemSets) { - transactions.push(await set.getSingleTransaction()); - } - transactions = transactions.filter( - (x) => x.getInstructions().length > 0 - ); - if (transactions.length === 0) { - this.updateStatusForSets( - itemSets, - TransactionStatus.Skipped, - attemptNum - ); - return; - } - - this.updateStatusForSets( - itemSets, - TransactionStatus.Processing, - attemptNum - ); - - let txSigs: string[] | undefined; - let error: Error | undefined; - try { - txSigs = await sendJitoBundledTransactions( - this.txHandler.umi, - this.txHandler.connection, - this.txHandler.signer, - transactions, - this.txType, - this.getUpdatedPriorityFeeSetting(prevError, attemptNum) - ); - } catch (e: any) { - error = e as Error; - } - - if (error || !Boolean(txSigs) || txSigs?.length === 0) { - this.updateStatusForSets( - itemSets, - TransactionStatus.Failed, - attemptNum, - txSigs, - true, - error?.message - ); - throw error ? error : new Error("Unknown error"); - } - - this.updateStatusForSets( - itemSets, - TransactionStatus.Successful, - attemptNum, - txSigs - ); - }, - this.retries, - this.retryDelay, - this.errorsToThrow - ).catch((e: Error) => { - this.updateStatusForSets( - itemSets, - TransactionStatus.Failed, - num, - undefined, - true, - e.message - ); - throw e; - }); - } - - private async processTransactionSet( - itemSets: TransactionSet[], - currentIndex: number - ) { - let itemSet: TransactionSet | undefined = itemSets[currentIndex]; - let num = 0; - - await retryWithExponentialBackoff( - async (attemptNum, prevError) => { - num = attemptNum; - - if (currentIndex > 0 || attemptNum > 0) { - itemSet = await this.refreshItemSet( - itemSets, - currentIndex, - attemptNum - ); - } - if (!itemSet) return; - - const tx = await itemSet.getSingleTransaction(); - if (tx.getInstructions().length === 0) { - this.updateStatus( - itemSet.name(), - TransactionStatus.Skipped, - attemptNum - ); - } else { - await this.debugAccounts(itemSet, tx); - await this.sendTransaction( - tx, - itemSet.name(), - attemptNum, - this.getUpdatedPriorityFeeSetting(prevError, attemptNum) - ); - } - }, - this.retries, - this.retryDelay, - this.errorsToThrow - ).catch((e: Error) => { - if (itemSet) { - this.updateStatus( - itemSet.name(), - TransactionStatus.Failed, - num, - undefined, - undefined, - e.message - ); - } - throw e; - }); - } - - private async refreshItemSet( - itemSets: TransactionSet[], - currentIndex: number, - attemptNum: number - ): Promise { - const itemSet = itemSets[currentIndex]; - await itemSet.refetchAll(attemptNum); - - const newItemSets = await this.assembleTransactionSets([ - ...itemSet.items, - ...itemSets.slice(currentIndex + 1).flatMap((set) => set.items), - ]); - - if (newItemSets.length > 1) { - itemSets.splice( - currentIndex, - itemSets.length - currentIndex, - ...newItemSets - ); - const startOfQueuedStatuses = this.statuses.findIndex( - (x) => x.status === TransactionStatus.Queued - ); - this.statuses.splice( - startOfQueuedStatuses, - this.statuses.length - startOfQueuedStatuses, - ...newItemSets.map((x, i) => ({ - name: x.name(), - attemptNum: i === 0 ? attemptNum : 0, - status: - i === 0 ? TransactionStatus.Processing : TransactionStatus.Queued, - })) - ); - } - - return newItemSets[0]; - } - - private async sendTransaction( - tx: TransactionBuilder, - txName: string, - attemptNum: number, - priorityFeeSetting?: PriorityFeeSetting - ) { - this.updateStatus(txName, TransactionStatus.Processing, attemptNum); - try { - const txSig = await sendSingleOptimizedTransaction( - this.txHandler.umi, - this.txHandler.connection, - tx, - this.txType, - priorityFeeSetting, - () => - this.updateStatus( - txName, - TransactionStatus.Processing, - attemptNum, - undefined, - true - ) - ); - this.updateStatus( - txName, - TransactionStatus.Successful, - attemptNum, - txSig ? bs58.encode(txSig) : undefined - ); - } catch (e: any) { - const errorDetails = getErrorInfo(this.txHandler.umi, tx, e); - - const errorString = `${errorDetails.errorName ?? "Unknown error"}: ${errorDetails.errorInfo ?? "unknown"}`; - this.updateStatus( - txName, - errorDetails.canBeIgnored - ? TransactionStatus.Skipped - : TransactionStatus.Failed, - attemptNum, - undefined, - undefined, - errorDetails.errorName || errorDetails.errorInfo - ? errorString - : e.message - ); - - if (!errorDetails.canBeIgnored) { - throw e; - } - } - } -} diff --git a/solauto-sdk/src/types/accounts.ts b/solauto-sdk/src/types/accounts.ts index 35da13a3..9dfafb91 100644 --- a/solauto-sdk/src/types/accounts.ts +++ b/solauto-sdk/src/types/accounts.ts @@ -2,5 +2,4 @@ export interface MarginfiAssetAccounts { bank: string; liquidityVault: string; vaultAuthority: string; - priceOracle: string; } diff --git a/solauto-sdk/src/types/index.ts b/solauto-sdk/src/types/index.ts index 5670a221..634ddf08 100644 --- a/solauto-sdk/src/types/index.ts +++ b/solauto-sdk/src/types/index.ts @@ -1,2 +1,3 @@ export * from './accounts'; export * from './solauto'; +export * from './transactions'; diff --git a/solauto-sdk/src/types/solauto.ts b/solauto-sdk/src/types/solauto.ts index 0de25c33..ca85750c 100644 --- a/solauto-sdk/src/types/solauto.ts +++ b/solauto-sdk/src/types/solauto.ts @@ -1,6 +1,14 @@ import { PublicKey } from "@solana/web3.js"; -import { LendingPlatform, PositionType } from "../generated"; +import { + LendingPlatform, + PositionType, + PriceType, + SolautoRebalanceType, + TokenType, +} from "../generated"; import { TransactionBuilder } from "@metaplex-foundation/umi"; +import { RebalanceValues } from "../services/rebalance"; +import { QuoteResponse } from "@jup-ag/api"; export interface SolautoPositionDetails { publicKey?: PublicKey; @@ -8,7 +16,7 @@ export interface SolautoPositionDetails { positionId: number; positionType: PositionType; lendingPlatform: LendingPlatform; - protocolAccount?: PublicKey; + lpUserAccount?: PublicKey; supplyMint?: PublicKey; debtMint?: PublicKey; } @@ -19,9 +27,12 @@ export enum PriorityFeeSetting { Low = "Low", Default = "Medium", High = "High", + VeryHigh = "VeryHigh", } -export const priorityFeeSettingValues = Object.values(PriorityFeeSetting) as PriorityFeeSetting[]; +export const priorityFeeSettingValues = Object.values( + PriorityFeeSetting +) as PriorityFeeSetting[]; export type RebalanceAction = "boost" | "repay" | "dca"; @@ -30,4 +41,29 @@ export type TransactionRunType = "skip-simulation" | "only-simulate" | "normal"; export interface TransactionItemInputs { tx: TransactionBuilder; lookupTableAddresses?: string[]; + orderPrio?: number; } + +export interface FlashLoanRequirements { + liquiditySource: TokenType; + signerFlashLoan?: boolean; + flFeeBps?: number; +} + +export interface FlashLoanDetails extends FlashLoanRequirements { + baseUnitAmount: bigint; + mint: PublicKey; +} + +export interface RebalanceDetails { + values: RebalanceValues; + rebalanceType: SolautoRebalanceType; + flashLoan?: FlashLoanDetails; + swapQuote: QuoteResponse; + targetLiqUtilizationRateBps?: number; + priceType: PriceType; +} + +export type ProgramEnv = "Prod" | "Staging"; + +export type RoundAction = "Floor" | "Round" | "Ceil"; diff --git a/solauto-sdk/src/types/transactions.ts b/solauto-sdk/src/types/transactions.ts new file mode 100644 index 00000000..e8b1ddb2 --- /dev/null +++ b/solauto-sdk/src/types/transactions.ts @@ -0,0 +1,23 @@ +export interface BundleInstructionFailure { + transactionIdx: number; + instructionIdx: number; + errorCode: number; +} + +export class BundleSimulationError extends Error { + public statusCode: number; + public details: BundleInstructionFailure; + + constructor( + message: string, + statusCode: number, + details: BundleInstructionFailure + ) { + super(message); + + this.statusCode = statusCode; + this.details = details; + + Object.setPrototypeOf(this, BundleSimulationError.prototype); + } +} diff --git a/solauto-sdk/src/utils/accountUtils.ts b/solauto-sdk/src/utils/accountUtils.ts index 3d9f1286..9ad08fc7 100644 --- a/solauto-sdk/src/utils/accountUtils.ts +++ b/solauto-sdk/src/utils/accountUtils.ts @@ -1,5 +1,8 @@ import { PublicKey } from "@solana/web3.js"; -import { AccountLayout as SplTokenAccountLayout, getAssociatedTokenAddressSync } from "@solana/spl-token"; +import { + AccountLayout as SplTokenAccountLayout, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; import { publicKey, Umi } from "@metaplex-foundation/umi"; export function bufferFromU8(num: number): Buffer { @@ -14,20 +17,24 @@ export function bufferFromU64(num: bigint): Buffer { return buffer; } -export function getTokenAccount(wallet: PublicKey, tokenMint: PublicKey): PublicKey { - return getAssociatedTokenAddressSync( - tokenMint, - wallet, - true - ); +export function getTokenAccount( + wallet: PublicKey, + tokenMint: PublicKey +): PublicKey { + return getAssociatedTokenAddressSync(tokenMint, wallet, true); } -export function getTokenAccounts(wallet: PublicKey, tokenMints: PublicKey[]): PublicKey[] { - return tokenMints.map(x => getTokenAccount(wallet, x)); +export function getTokenAccounts( + wallet: PublicKey, + tokenMints: PublicKey[] +): PublicKey[] { + return tokenMints.map((x) => getTokenAccount(wallet, x)); } export async function getTokenAccountData(umi: Umi, tokenAccount: PublicKey) { - const resp = await umi.rpc.getAccount(publicKey(tokenAccount), { commitment: "confirmed" }); + const resp = await umi.rpc.getAccount(publicKey(tokenAccount), { + commitment: "confirmed", + }); if (resp.exists) { return SplTokenAccountLayout.decode(resp.data); } else { @@ -40,8 +47,14 @@ export function getSolautoPositionAccount( positionId: number, programId: PublicKey ) { + const fakePosition = positionId >= 256; const [positionAccount, _] = PublicKey.findProgramAddressSync( - [bufferFromU8(positionId), authority.toBuffer()], + [ + fakePosition + ? bufferFromU64(BigInt(positionId)) + : bufferFromU8(positionId), + authority.toBuffer(), + ], programId ); @@ -59,21 +72,3 @@ export function getReferralState(authority: PublicKey, programId: PublicKey) { return ReferralState; } - -export function getMarginfiAccountPDA( - solautoPositionAccount: PublicKey, - marginfiAccountSeedIdx: bigint, - programId: PublicKey -) { - const seeds = [ - solautoPositionAccount.toBuffer(), - bufferFromU64(marginfiAccountSeedIdx), - ]; - - const [marginfiAccount, _] = PublicKey.findProgramAddressSync( - seeds, - programId - ); - - return marginfiAccount; -} \ No newline at end of file diff --git a/solauto-sdk/src/utils/generalUtils.ts b/solauto-sdk/src/utils/generalUtils.ts index 373c7787..8623faca 100644 --- a/solauto-sdk/src/utils/generalUtils.ts +++ b/solauto-sdk/src/utils/generalUtils.ts @@ -1,11 +1,62 @@ +import axios from "axios"; import { PublicKey } from "@solana/web3.js"; -import { MaybeRpcAccount, publicKey, Umi } from "@metaplex-foundation/umi"; +import { + MaybeRpcAccount, + publicKey, + Umi, + PublicKey as UmiPublicKey, +} from "@metaplex-foundation/umi"; +import { TOKEN_INFO, TokenInfo } from "../constants"; + +export function buildHeliusApiUrl(heliusApiKey: string) { + return `https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`; +} + +export function buildIronforgeApiUrl(ironforgeApiKey: string) { + return `https://rpc.ironforge.network/mainnet?apiKey=${ironforgeApiKey}`; +} export function consoleLog(...args: any[]): void { - if ((globalThis as any).LOCAL_TEST) { + if ((globalThis as any).SHOW_LOGS) { console.log(...args); } } + +export function tokenInfo(mint?: PublicKey): TokenInfo { + return TOKEN_INFO[mint ? mint.toString() : PublicKey.default.toString()]; +} + +export function findMintByTicker(ticker: string): PublicKey { + for (const key in TOKEN_INFO) { + const account = TOKEN_INFO[key]; + if ( + account.ticker.toString().toLowerCase() === + ticker.toString().toLowerCase() + ) { + return new PublicKey(key); + } + } + throw new Error(`Token mint not found by the ticker: ${ticker}`); +} + +export function tokenInfoByTicker(ticker: string) { + for (const key in TOKEN_INFO) { + const token = TOKEN_INFO[key]; + if (token.ticker.toLowerCase() === ticker.toLowerCase()) { + return token; + } + } + return undefined; +} + +export function getBatches(items: T[], batchSize: number): T[][] { + const batches: T[][] = []; + for (let i = 0; i < items.length; i += batchSize) { + batches.push(items.slice(i, i + batchSize)); + } + return batches; +} + export function generateRandomU8(): number { return Math.floor(Math.random() * 255 + 1); } @@ -76,7 +127,7 @@ export function retryWithExponentialBackoff( attemptNum++; if ( - errorsToThrow && + errorsToThrow?.length && errorsToThrow.some((errorType) => error instanceof errorType) ) { reject(error); @@ -98,3 +149,71 @@ export function retryWithExponentialBackoff( return attempt(0); }); } + +export function toEnumValue( + enumObj: E, + value: number +): E[keyof E] | undefined { + const numericValues = Object.values(enumObj).filter( + (v) => typeof v === "number" + ) as number[]; + + if (numericValues.includes(value)) { + return value as E[keyof E]; + } + + return undefined; +} + +export async function customRpcCall(umi: Umi, method: string, params?: any) { + const data = ( + await axios.post( + umi.rpc.getEndpoint(), + { + jsonrpc: "2.0", + id: 1, + method, + params, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ) + ).data; + + if ("result" in data) { + return data.result; + } else { + if ("error" in data) { + console.log(JSON.stringify(data.error)); + } + return data; + } +} + +export function u16ToArrayBufferLE(value: number): Uint8Array { + // Create a buffer of 2 bytes + const buffer = new ArrayBuffer(2); + const dataView = new DataView(buffer); + + // Set the Uint16 value in little-endian order + dataView.setUint16(0, value, true); + + // Return the buffer + return new Uint8Array(buffer); +} + +export function validPubkey(pubkey?: PublicKey | UmiPublicKey | string) { + return Boolean(pubkey) && pubkey!.toString() !== PublicKey.default.toString(); +} + +export function createRecord( + keys: string[], + values: T[] +): Record { + return Object.fromEntries( + zip(keys, values).map(([k, v]) => [k.toString(), v]) + ); +} diff --git a/solauto-sdk/src/utils/index.ts b/solauto-sdk/src/utils/index.ts index 52a1506e..6e70de82 100644 --- a/solauto-sdk/src/utils/index.ts +++ b/solauto-sdk/src/utils/index.ts @@ -1,9 +1,13 @@ -export * from './solauto/index'; -export * from './accountUtils'; -export * from './generalUtils'; -export * from './jupiterUtils'; -export * from './marginfiUtils'; -export * from './numberUtils'; -export * from './solanaUtils'; -export * from './priceUtils'; -export * from './switchboardUtils'; \ No newline at end of file +export * from "./accountUtils"; +export * from "./generalUtils"; +export * from "./instructionUtils"; +export * from "./jitoUtils"; +export * from "./jupiterUtils"; +export * from "./marginfi"; +export * from "./numberUtils"; +export * from "./solautoUtils"; +export * from "./solanaUtils"; +export * from "./stringUtils"; +export * from "./priceUtils"; +export * from "./pythUtils"; +export * from "./switchboardUtils"; diff --git a/solauto-sdk/src/utils/instructionUtils.ts b/solauto-sdk/src/utils/instructionUtils.ts new file mode 100644 index 00000000..3e3c7d8f --- /dev/null +++ b/solauto-sdk/src/utils/instructionUtils.ts @@ -0,0 +1,187 @@ +import { OptionOrNullable, transactionBuilder } from "@metaplex-foundation/umi"; +import { + DCASettingsInpArgs, + PositionType, + SolautoSettingsParametersInpArgs, +} from "../generated"; +import { + JupSwapManager, + RebalanceTxBuilder, + SolautoClient, + SwapInput, + TransactionItem, + TransactionTooLargeError, +} from "../services"; +import { PublicKey } from "@solana/web3.js"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; + +export function openSolautoPosition( + client: SolautoClient, + settingParams: SolautoSettingsParametersInpArgs, + dca?: DCASettingsInpArgs, + positionType?: PositionType +) { + return new TransactionItem( + async () => ({ + tx: client!.openPositionIx(settingParams, dca, positionType), + }), + "open position" + ); +} + +export function closeSolautoPosition(client: SolautoClient) { + return new TransactionItem( + async () => ({ + tx: client.closePositionIx(), + }), + "close position" + ); +} + +export function updateSolautoPosition( + client: SolautoClient, + settings: OptionOrNullable, + dca: OptionOrNullable +) { + return new TransactionItem( + async () => ({ + tx: client.updatePositionIx({ + positionId: client.pos.positionId, + settings, + dca, + }), + }), + "update position" + ); +} + +export function cancelSolautoDca(client: SolautoClient) { + return new TransactionItem( + async () => ({ + tx: client.cancelDCAIx(), + }), + "cancel DCA" + ); +} + +export function deposit(client: SolautoClient, baseUnitAmount: bigint) { + return new TransactionItem( + async () => ({ + tx: client.protocolInteractionIx({ + __kind: "Deposit", + fields: [baseUnitAmount], + }), + }), + "deposit" + ); +} + +export function borrow(client: SolautoClient, baseUnitAmount: bigint) { + return new TransactionItem( + async () => ({ + tx: client.protocolInteractionIx({ + __kind: "Borrow", + fields: [baseUnitAmount], + }), + }), + "borrow", + true + ); +} + +export function withdraw(client: SolautoClient, amount: "All" | bigint) { + return new TransactionItem( + async () => ({ + tx: client.protocolInteractionIx({ + __kind: "Withdraw", + fields: [ + amount === "All" + ? { __kind: "All" } + : { __kind: "Some", fields: [amount] }, + ], + }), + }), + "withdraw", + true + ); +} + +export function repay(client: SolautoClient, amount: "All" | bigint) { + return new TransactionItem( + async () => ({ + tx: client.protocolInteractionIx({ + __kind: "Repay", + fields: [ + amount === "All" + ? { __kind: "All" } + : { __kind: "Some", fields: [amount] }, + ], + }), + }), + "repay" + ); +} + +export function rebalance( + client: SolautoClient, + targetLiqUtilizationRateBps?: number, + bpsDistanceFromRebalance?: number +) { + return new TransactionItem( + async (attemptNum, prevError) => + await new RebalanceTxBuilder( + client, + targetLiqUtilizationRateBps, + attemptNum > 2 && prevError instanceof TransactionTooLargeError, + bpsDistanceFromRebalance + ).buildRebalanceTx(attemptNum), + "rebalance", + true + ); +} + +export function swapThenDeposit( + client: SolautoClient, + depositMint: PublicKey, + depositAmountBaseUnit: bigint +) { + return [ + new TransactionItem(async () => { + const swapInput: SwapInput = { + inputMint: depositMint, + outputMint: client.pos.supplyMint, + amount: depositAmountBaseUnit, + exactIn: true, + }; + const jupSwapManager = new JupSwapManager(client.signer); + const { setupIx, swapIx, cleanupIx, lookupTableAddresses } = + await jupSwapManager.getJupSwapTxData({ + ...swapInput, + destinationWallet: toWeb3JsPublicKey(client.signer.publicKey), + wrapAndUnwrapSol: true, + }); + + client.contextUpdates.new({ + type: "jupSwap", + value: jupSwapManager.jupQuote!, + }); + + return { + tx: transactionBuilder().add([setupIx, swapIx, cleanupIx]), + lookupTableAddresses, + orderPrio: -1, + }; + }, "swap"), + new TransactionItem(async () => { + const quoteOutAmount = client.contextUpdates.jupSwap?.outAmount; + return { + tx: transactionBuilder().add( + client.protocolInteractionIx({ + __kind: "Deposit", + fields: [BigInt(Math.round(parseInt(quoteOutAmount!) * 0.995))], + }) + ), + }; + }, "deposit"), + ]; +} diff --git a/solauto-sdk/src/utils/jitoUtils.ts b/solauto-sdk/src/utils/jitoUtils.ts index 3f03e854..d1031f7b 100644 --- a/solauto-sdk/src/utils/jitoUtils.ts +++ b/solauto-sdk/src/utils/jitoUtils.ts @@ -1,114 +1,206 @@ -import { Connection, PublicKey, TransactionExpiredBlockheightExceededError, VersionedTransaction } from "@solana/web3.js"; -import { toWeb3JsTransaction } from "@metaplex-foundation/umi-web3js-adapters"; -import { JITO_BLOCK_ENGINE } from "../constants/solautoConstants"; +import { + Connection, + PublicKey, + SimulatedTransactionResponse, + TransactionExpiredBlockheightExceededError, + VersionedTransaction, +} from "@solana/web3.js"; import { Signer, TransactionBuilder, Umi, WrappedInstruction, + TransactionMessage, } from "@metaplex-foundation/umi"; +import { toWeb3JsTransaction } from "@metaplex-foundation/umi-web3js-adapters"; +import { JITO_TIP_ACCOUNTS } from "../constants"; +import { PriorityFeeSetting, TransactionRunType } from "../types"; +import { BundleSimulationError } from "../types"; import { assembleFinalTransaction, - buildIronforgeApiUrl, getComputeUnitPriceEstimate, + prependTx, sendSingleOptimizedTransaction, systemTransferUmiIx, } from "./solanaUtils"; -import { consoleLog } from "./generalUtils"; -import { PriorityFeeSetting, TransactionRunType } from "../types"; -import axios from "axios"; +import { + consoleLog, + customRpcCall, + retryWithExponentialBackoff, +} from "./generalUtils"; import base58 from "bs58"; -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; - -export async function getRandomTipAccount(): Promise { - const tipAccounts = [ - "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5", - "HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe", - "Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY", - "ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49", - "DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh", - "ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt", - "DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL", - "3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT", - ]; - const randomInt = Math.floor(Math.random() * tipAccounts.length); - return new PublicKey(tipAccounts[randomInt]); +import { usePriorityFee } from "../services"; + +export function getRandomTipAccount(): PublicKey { + const randomInt = Math.floor(Math.random() * JITO_TIP_ACCOUNTS.length); + return new PublicKey(JITO_TIP_ACCOUNTS[randomInt]); } -async function getTipInstruction( +function getTipInstruction( signer: Signer, tipLamports: number -): Promise { +): WrappedInstruction { return systemTransferUmiIx( signer, - await getRandomTipAccount(), + getRandomTipAccount(), BigInt(tipLamports) ); } -// TODO: fix -// async function simulateJitoBundle(umi: Umi, txs: VersionedTransaction[]) { -// const simulationResult = await axios.post( -// `${JITO_BLOCK_ENGINE}/api/v1/bundles`, -// { -// method: "simulateBundle", -// id: 1, -// jsonrpc: "2.0", -// params: [ -// { -// encodedTransactions: txs.map((x) => bs58.encode(x.serialize())), -// preExecutionAccountsConfigs: txs.map((_) => ""), -// postExecutionAccountsConfigs: txs.map((_) => ""), -// skipSigVerify: true, -// }, -// ], -// } -// ); -// } +function parseJitoErrorMessage(message: string) { + const regex = + /Error processing Instruction (\d+): custom program error: (0x[0-9A-Fa-f]+|\d+)/; + const match = message.match(regex); + + if (match) { + const instructionIndex = parseInt(match[1], 10); + + let errorCode: number; + if (match[2].toLowerCase().startsWith("0x")) { + errorCode = parseInt(match[2], 16); + } else { + errorCode = parseInt(match[2], 10); + } + + return { + instructionIndex, + errorCode, + }; + } else { + return null; + } +} + +async function simulateJitoBundle(umi: Umi, txs: VersionedTransaction[]) { + const simulationResult = await retryWithExponentialBackoff(async () => { + const res = await customRpcCall(umi, "simulateBundle", [ + { + encodedTransactions: txs.map((x) => + Buffer.from(x.serialize()).toString("base64") + ), + }, + { + encoding: "base64", + commitment: "confirmed", + preExecutionAccountsConfigs: txs.map((_) => { }), + postExecutionAccountsConfigs: txs.map((_) => { }), + skipSigVerify: true, + }, + ]); + + if (res.value && res.value.summary.failed) { + const transactionResults = res.value + .transactionResults as SimulatedTransactionResponse[]; + transactionResults.forEach((x) => { + x.logs?.forEach((y) => { + consoleLog(y); + }); + }); + + const failedTxIdx = transactionResults.length; + const txFailure = res.value.summary.failed.error.TransactionFailure; + + if (txFailure) { + const info = parseJitoErrorMessage(txFailure[1] as string); + if (info) { + throw new BundleSimulationError( + `Failed to simulate transaction: TX: ${failedTxIdx}, IX: ${info.instructionIndex}, Error: ${info.errorCode}`, + 400, + { + transactionIdx: failedTxIdx, + instructionIdx: info.instructionIndex, + errorCode: info.errorCode, + } + ); + } + } + + throw new Error( + txFailure ? txFailure[1] : res.value.summary.failed.toString() + ); + } else if (res.error && res.error.message) { + throw new Error(res.error.message); + } + + return res.value; + }, 2); + + const transactionResults = + simulationResult.transactionResults as SimulatedTransactionResponse[]; + + return transactionResults; +} + +export function getAdditionalSigners(message: TransactionMessage) { + const { numRequiredSignatures, numReadonlySignedAccounts } = message.header; + + const numWritableSigners = numRequiredSignatures - numReadonlySignedAccounts; + + const signersInfo = []; + for (let i = 0; i < numRequiredSignatures; i++) { + const publicKey = message.accounts[i].toString(); + const isWritable = i < numWritableSigners; + + signersInfo.push({ + index: i, + publicKey, + isWritable, + }); + } + + return signersInfo; +} async function umiToVersionedTransactions( umi: Umi, - signer: Signer, + blockhash: string, + userSigner: Signer, + otherSigners: Signer[], txs: TransactionBuilder[], sign: boolean, feeEstimates?: number[], computeUnitLimits?: number[] ): Promise { - let builtTxs = await Promise.all( - txs.map(async (tx, i) => { - return ( - await assembleFinalTransaction( - signer, - tx, - feeEstimates ? feeEstimates[i] : undefined, - computeUnitLimits ? computeUnitLimits[i] : undefined - ).setLatestBlockhash(umi) - ).build(umi); - }) + let builtTxs = txs.map((tx, i) => + assembleFinalTransaction( + umi, + tx, + feeEstimates ? feeEstimates[i] : undefined, + computeUnitLimits ? computeUnitLimits[i] : undefined + ) + .setBlockhash(blockhash) + .build(umi) ); if (sign) { - builtTxs = await signer.signAllTransactions(builtTxs); + builtTxs = await userSigner.signAllTransactions(builtTxs); + for (const signer of otherSigners) { + for (let i = 0; i < builtTxs.length; i++) { + const requiredSigners = getAdditionalSigners(builtTxs[i].message); + if ( + requiredSigners + .map((x) => x.publicKey) + .includes(signer.publicKey.toString()) + ) { + builtTxs[i] = await signer.signTransaction(builtTxs[i]); + } + } + } } return builtTxs.map((x) => toWeb3JsTransaction(x)); } -async function getBundleStatus(bundleId: string) { - const res = await axios.post(`${JITO_BLOCK_ENGINE}/api/v1/bundles`, { - jsonrpc: "2.0", - id: 1, - method: "getBundleStatuses", - params: [[bundleId]], - }); - if (res.data.error) { - throw new Error(`Failed to get bundle status: ${res.data.error}`); +async function getBundleStatus(umi: Umi, bundleId: string) { + const res = await customRpcCall(umi, "getBundleStatuses", [[bundleId]]); + if (res.error) { + throw new Error(`Failed to get bundle status: ${res.error}`); } - - return res.data.result; + return res; } async function pollBundleStatus( + umi: Umi, bundleId: string, interval = 1000, timeout = 40000 @@ -116,30 +208,44 @@ async function pollBundleStatus( const endTime = Date.now() + timeout; while (Date.now() < endTime) { await new Promise((resolve) => setTimeout(resolve, interval)); - const statuses = await getBundleStatus(bundleId); + + const statuses = await retryWithExponentialBackoff( + async () => { + const resp = await getBundleStatus(umi, bundleId); + if (resp?.value?.length > 0 && resp.value[0] === null) { + throw new Error("No confirmation status"); + } + return resp; + }, + 3, + 250 + ); + if (statuses?.value?.length > 0) { consoleLog("Statuses:", statuses); const status = statuses.value[0].confirmation_status; if (status === "confirmed") { - return statuses?.value[0].transactions as string[]; + return statuses.value[0].transactions as string[]; + } + const err = statuses.value[0].err; + if (err) { + consoleLog("Jito bundle err:", JSON.stringify(err, null, 2)); + throw new Error(err); } } } - throw new TransactionExpiredBlockheightExceededError("Unable to confirm transaction. Try a higher priority fee."); + throw new TransactionExpiredBlockheightExceededError( + "Unable to confirm transaction. Try a higher priority fee." + ); } -async function sendJitoBundle(transactions: string[]): Promise { +async function sendJitoBundle( + umi: Umi, + transactions: string[] +): Promise { let resp: any; try { - resp = await axios.post<{ result: string }>( - `${JITO_BLOCK_ENGINE}/api/v1/bundles`, - { - jsonrpc: "2.0", - id: 1, - method: "sendBundle", - params: [transactions], - } - ); + resp = await customRpcCall(umi, "sendBundle", [transactions]); } catch (e: any) { if (e.response.data.error) { console.error("Jito send bundle error:", e.response.data.error); @@ -149,70 +255,131 @@ async function sendJitoBundle(transactions: string[]): Promise { } } - const bundleId = resp.data.result; + if (resp.error?.message === "All providers failed") { + throw new Error(resp.error.responses[0].response.error.message); + } else if (resp.error) { + throw new Error(resp.error); + } + + const bundleId = resp as string; consoleLog("Bundle ID:", bundleId); - return bundleId ? await pollBundleStatus(bundleId) : []; + return bundleId ? await pollBundleStatus(umi, bundleId) : []; } export async function sendJitoBundledTransactions( umi: Umi, connection: Connection, - signer: Signer, - txs: TransactionBuilder[], + userSigner: Signer, + otherSigners: Signer[], + transactions: TransactionBuilder[], txType?: TransactionRunType, - priorityFeeSetting: PriorityFeeSetting = PriorityFeeSetting.Min + priorityFeeSetting: PriorityFeeSetting = PriorityFeeSetting.Min, + onAwaitingSign?: () => void, + abortController?: AbortController ): Promise { + const txs = [...transactions]; + if (txs.length === 1) { - const res = await sendSingleOptimizedTransaction( + const resp = await sendSingleOptimizedTransaction( umi, connection, txs[0], txType, - priorityFeeSetting + priorityFeeSetting, + onAwaitingSign, + abortController ); - return res ? [bs58.encode(res)] : undefined; + return resp ? [base58.encode(resp)] : undefined; } consoleLog("Sending Jito bundle..."); consoleLog("Transactions: ", txs.length); + consoleLog( + txs.map((tx) => tx.getInstructions().map((x) => x.programId.toString())) + ); consoleLog( "Transaction sizes: ", txs.map((x) => x.getTransactionSize(umi)) ); - txs[0] = txs[0].prepend(await getTipInstruction(signer, 150_000)); - const feeEstimates = - priorityFeeSetting !== PriorityFeeSetting.None - ? await Promise.all( - txs.map( - async (x) => - (await getComputeUnitPriceEstimate(umi, x, priorityFeeSetting)) ?? - 1000000 - ) - ) - : undefined; + txs[0] = prependTx(txs[0], [getTipInstruction(userSigner, 250_000)]); - let builtTxs = await umiToVersionedTransactions( - umi, - signer, - txs, - true, // false if simulating first and rebuilding later - feeEstimates - ); + const latestBlockhash = ( + await retryWithExponentialBackoff( + async () => await umi.rpc.getLatestBlockhash({ commitment: "confirmed" }) + ) + ).blockhash; + + if (abortController?.signal.aborted) { + return; + } + + let builtTxs: VersionedTransaction[] = []; + let simulationResults: SimulatedTransactionResponse[] | undefined; + if (txType !== "skip-simulation") { + builtTxs = await umiToVersionedTransactions( + umi, + latestBlockhash, + userSigner, + otherSigners, + txs, + false, + undefined, + Array(txs.length) + .fill(null) + .map((_) => 1_400_000) + ); + simulationResults = await simulateJitoBundle(umi, builtTxs); + } - // const simulationResults = await simulateJitoBundle(umi, builtTxs); + const feeEstimates = usePriorityFee(priorityFeeSetting) + ? await Promise.all( + txs.map( + async (x) => + (await getComputeUnitPriceEstimate( + umi, + x, + priorityFeeSetting, + true + )) ?? 1000000 + ) + ) + : undefined; + if (abortController?.signal.aborted) { + return; + } if (txType !== "only-simulate") { - // let builtTxs = await umiToVersionedTransactions( - // client.signer, - // txs, - // true, - // feeEstimates, - // simulationResults.map((x) => x.unitsConsumed! * 1.15) - // ); + onAwaitingSign?.(); + + builtTxs = await umiToVersionedTransactions( + umi, + latestBlockhash, + userSigner, + otherSigners, + txs, + true, + feeEstimates, + simulationResults + ? simulationResults.map((x) => x.unitsConsumed! * 1.15) + : undefined + ); + consoleLog( + builtTxs.map((x) => + x.message.compiledInstructions.map((y) => + x.message.staticAccountKeys[y.programIdIndex].toString() + ) + ) + ); + + const serializedTxs = builtTxs.map((x) => x.serialize()); + if (serializedTxs.find((x) => x.length > 1232)) { + throw new Error("A transaction is too large"); + } const txSigs = await sendJitoBundle( - builtTxs.map((x) => base58.encode(x.serialize())) + umi, + serializedTxs.map((x) => base58.encode(x)) ); return txSigs.length > 0 ? txSigs : undefined; } diff --git a/solauto-sdk/src/utils/jupiterUtils.ts b/solauto-sdk/src/utils/jupiterUtils.ts index 1f755a27..90327adc 100644 --- a/solauto-sdk/src/utils/jupiterUtils.ts +++ b/solauto-sdk/src/utils/jupiterUtils.ts @@ -1,33 +1,8 @@ -import { - Signer, - TransactionBuilder, - transactionBuilder, -} from "@metaplex-foundation/umi"; import { PublicKey, TransactionInstruction } from "@solana/web3.js"; -import { getWrappedInstruction } from "./solanaUtils"; -import { fromBps, toBps } from "./numberUtils"; -import { - createJupiterApiClient, - Instruction, - QuoteResponse, -} from "@jup-ag/api"; -import { getTokenAccount } from "./accountUtils"; -import { consoleLog, retryWithExponentialBackoff } from "./generalUtils"; -import { TOKEN_INFO } from "../constants"; +import { Instruction } from "@jup-ag/api"; +import { getBatches, retryWithExponentialBackoff } from "./generalUtils"; -const jupApi = createJupiterApiClient(); - -export interface JupSwapDetails { - inputMint: PublicKey; - outputMint: PublicKey; - destinationWallet: PublicKey; - amount: bigint; - slippageIncFactor?: number; - exactOut?: boolean; - exactIn?: boolean; -} - -function createTransactionInstruction( +export function jupIxToSolanaIx( instruction: Instruction ): TransactionInstruction { return new TransactionInstruction({ @@ -41,110 +16,29 @@ function createTransactionInstruction( }); } -export interface JupSwapTransaction { - jupQuote: QuoteResponse; - priceImpactBps: number; - lookupTableAddresses: string[]; - setupInstructions: TransactionBuilder; - tokenLedgerIx: TransactionBuilder; - swapIx: TransactionBuilder; -} - -export async function getJupSwapTransaction( - signer: Signer, - swapDetails: JupSwapDetails, - attemptNum?: number -): Promise { - const memecoinSwap = - TOKEN_INFO[swapDetails.inputMint.toString()].isMeme || - TOKEN_INFO[swapDetails.outputMint.toString()].isMeme; - - const quoteResponse = await retryWithExponentialBackoff( - async () => - await jupApi.quoteGet({ - amount: Number(swapDetails.amount), - inputMint: swapDetails.inputMint.toString(), - outputMint: swapDetails.outputMint.toString(), - swapMode: swapDetails.exactOut - ? "ExactOut" - : swapDetails.exactIn - ? "ExactIn" - : undefined, - slippageBps: memecoinSwap ? 150 : 50, - maxAccounts: !swapDetails.exactOut ? 60 : undefined, - }), - 4, - 200 - ); +export async function getJupPriceData(mints: PublicKey[]) { + const batches = getBatches(mints, 50); - const priceImpactBps = - Math.round(toBps(parseFloat(quoteResponse.priceImpactPct))) + 1; - const finalPriceSlippageBps = Math.round( - Math.max(50, quoteResponse.slippageBps, priceImpactBps) * - (1 + (swapDetails.slippageIncFactor ?? 0)) - ); - quoteResponse.slippageBps = finalPriceSlippageBps; - consoleLog(quoteResponse); + const results = await Promise.all( + batches.map((batch) => + retryWithExponentialBackoff(async () => { + const res = await ( + await fetch( + "https://lite-api.jup.ag/price/v3?ids=" + + batch.map((x) => x.toString()).join(",") + ) + ).json(); - if (swapDetails.exactOut) { - quoteResponse.inAmount = ( - parseInt(quoteResponse.inAmount) + - Math.ceil( - parseInt(quoteResponse.inAmount) * fromBps(finalPriceSlippageBps) - ) - ).toString(); - } + if (!res || typeof res !== "object") { + throw new Error("Failed to get token prices using Jupiter"); + } - consoleLog("Getting jup instructions..."); - const instructions = await retryWithExponentialBackoff( - async () => - await jupApi.swapInstructionsPost({ - swapRequest: { - userPublicKey: signer.publicKey.toString(), - quoteResponse, - wrapAndUnwrapSol: false, - useTokenLedger: !swapDetails.exactOut && !swapDetails.exactIn, - destinationTokenAccount: getTokenAccount( - swapDetails.destinationWallet, - swapDetails.outputMint - ).toString(), - }, - }), - 4, - 200 + return res; + }, 6, 250) + ) ); - if (!instructions.swapInstruction) { - throw new Error("No swap instruction was returned by Jupiter"); - } + const mergedResults = Object.assign({}, ...results); - consoleLog("Raw price impact bps:", priceImpactBps); - const finalPriceImpactBps = - priceImpactBps * (1 + (swapDetails.slippageIncFactor ?? 0)); - consoleLog("Increased price impact bps:", finalPriceImpactBps); - - return { - jupQuote: quoteResponse, - priceImpactBps: finalPriceImpactBps, - lookupTableAddresses: instructions.addressLookupTableAddresses, - setupInstructions: transactionBuilder().add( - instructions.setupInstructions.map((ix) => - getWrappedInstruction(signer, createTransactionInstruction(ix)) - ) - ), - tokenLedgerIx: transactionBuilder().add( - instructions.tokenLedgerInstruction !== undefined - ? getWrappedInstruction( - signer, - createTransactionInstruction(instructions.tokenLedgerInstruction) - ) - : transactionBuilder() - ), - swapIx: transactionBuilder().add( - getWrappedInstruction( - signer, - createTransactionInstruction(instructions.swapInstruction) - ) - ), - }; -} \ No newline at end of file + return mergedResults; +} diff --git a/solauto-sdk/src/utils/marginfiUtils.ts b/solauto-sdk/src/utils/marginfi/data.ts similarity index 58% rename from solauto-sdk/src/utils/marginfiUtils.ts rename to solauto-sdk/src/utils/marginfi/data.ts index ea199afb..c4b41327 100644 --- a/solauto-sdk/src/utils/marginfiUtils.ts +++ b/solauto-sdk/src/utils/marginfi/data.ts @@ -1,89 +1,39 @@ -import { Connection, PublicKey } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { publicKey, Umi } from "@metaplex-foundation/umi"; import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { PositionState, PositionTokenState, PriceType } from "../../generated"; +import { + ALL_SUPPORTED_TOKENS, + getMarginfiAccounts, + TOKEN_INFO, + USD_DECIMALS, +} from "../../constants"; import { Bank, + deserializeMarginfiAccount, getMarginfiAccountSize, - MARGINFI_PROGRAM_ID, MarginfiAccount, + OracleSetup, safeFetchBank, safeFetchMarginfiAccount, -} from "../marginfi-sdk"; -import { currentUnixSeconds } from "./generalUtils"; +} from "../../externalSdks/marginfi"; +import { ContextUpdates } from "../solautoUtils"; +import { fetchTokenPrices, safeGetPrice } from "../priceUtils"; +import { currentUnixSeconds, validPubkey } from "../generalUtils"; import { bytesToI80F48, + calcNetWorthUsd, fromBaseUnit, getLiqUtilzationRateBps, toBaseUnit, toBps, -} from "./numberUtils"; +} from "../numberUtils"; import { - DEFAULT_MARGINFI_GROUP, - MARGINFI_ACCOUNTS, -} from "../constants/marginfiAccounts"; -import { MarginfiAssetAccounts } from "../types/accounts"; -import { PositionState, PositionTokenUsage } from "../generated"; -import { USD_DECIMALS } from "../constants/generalAccounts"; -import { LivePositionUpdates } from "./solauto/generalUtils"; -import { TOKEN_INFO } from "../constants"; -import { fetchTokenPrices, safeGetPrice } from "./priceUtils"; - -interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts { - mint: PublicKey; -} - -export function findMarginfiAccounts( - bank: PublicKey -): AllMarginfiAssetAccounts { - for (const group in MARGINFI_ACCOUNTS) { - for (const key in MARGINFI_ACCOUNTS[group]) { - const account = MARGINFI_ACCOUNTS[group][key]; - if ( - account.bank.toString().toLowerCase() === bank.toString().toLowerCase() - ) { - return { ...account, mint: new PublicKey(key) }; - } - } - } - throw new Error(`Marginfi accounts not found by the bank: ${bank}`); -} - -export function calcMaxLtvAndLiqThreshold( - supplyBank: Bank, - debtBank: Bank, - supplyPrice: number -): [number, number] { - let maxLtv = - bytesToI80F48(supplyBank.config.assetWeightInit.value) / - bytesToI80F48(debtBank.config.liabilityWeightInit.value); - const liqThreshold = - bytesToI80F48(supplyBank.config.assetWeightMaint.value) / - bytesToI80F48(debtBank.config.liabilityWeightMaint.value); - - const totalDepositedUsdValue = - fromBaseUnit( - BigInt( - Math.round( - bytesToI80F48(supplyBank.totalAssetShares.value) * - bytesToI80F48(supplyBank.assetShareValue.value) - ) - ), - supplyBank.mintDecimals - ) * supplyPrice!; - if ( - supplyBank.config.totalAssetValueInitLimit !== BigInt(0) && - totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit - ) { - const discount = - Number(supplyBank.config.totalAssetValueInitLimit) / - totalDepositedUsdValue; - maxLtv = maxLtv * Number(discount); - } + calcMarginfiMaxLtvAndLiqThresholdBps, + marginfiAccountEmpty, +} from "./general"; - return [maxLtv, liqThreshold]; -} - -export async function getMaxLtvAndLiqThreshold( +export async function getMarginfiMaxLtvAndLiqThresholdBps( umi: Umi, marginfiGroup: PublicKey, supply: { @@ -96,28 +46,30 @@ export async function getMaxLtvAndLiqThreshold( }, supplyPrice?: number ): Promise<[number, number]> { - if (!supply.bank && supply.mint.equals(PublicKey.default)) { + if (!supply.bank && !validPubkey(supply.mint)) { return [0, 0]; } + const bankAccounts = getMarginfiAccounts( + undefined, + marginfiGroup + ).bankAccounts; + if (!supply.bank || supply.bank === null) { supply.bank = await safeFetchBank( umi, publicKey( - MARGINFI_ACCOUNTS[marginfiGroup.toString()][supply.mint.toString()].bank + bankAccounts[marginfiGroup.toString()][supply.mint.toString()].bank ), { commitment: "confirmed" } ); } - if ( - (!debt.bank || debt.bank === null) && - !debt.mint.equals(PublicKey.default) - ) { + if ((!debt.bank || debt.bank === null) && !validPubkey(debt.mint)) { debt.bank = await safeFetchBank( umi, publicKey( - MARGINFI_ACCOUNTS[marginfiGroup.toString()][debt.mint.toString()].bank + bankAccounts[marginfiGroup.toString()][debt.mint.toString()].bank ), { commitment: "confirmed" } ); @@ -134,7 +86,45 @@ export async function getMaxLtvAndLiqThreshold( return [0, 0]; } - return calcMaxLtvAndLiqThreshold(supply.bank!, debt.bank, supplyPrice); + return calcMarginfiMaxLtvAndLiqThresholdBps( + supply.bank!, + debt.bank, + supplyPrice + ); +} + +export async function getEmptyMarginfiAccountsByAuthority( + umi: Umi, + authority: PublicKey +): Promise { + const marginfiAccounts = await umi.rpc.getProgramAccounts( + umi.programs.get("marginfi").publicKey, + { + commitment: "confirmed", + filters: [ + { + dataSize: getMarginfiAccountSize(), + }, + { + memcmp: { + bytes: new Uint8Array(authority.toBuffer()), + offset: 8 + 32, // Anchor account discriminator + group pubkey + }, + }, + { + // First balance is not active + memcmp: { + bytes: new Uint8Array([0]), + offset: 8 + 32 + 32, + }, + }, + ], + } + ); + + return marginfiAccounts + .map((x) => deserializeMarginfiAccount(x)) + .filter((x) => marginfiAccountEmpty(x)); } export async function getAllMarginfiAccountsByAuthority( @@ -146,7 +136,7 @@ export async function getAllMarginfiAccountsByAuthority( { marginfiAccount: PublicKey; supplyMint?: PublicKey; debtMint?: PublicKey }[] > { const marginfiAccounts = await umi.rpc.getProgramAccounts( - MARGINFI_PROGRAM_ID, + umi.programs.get("marginfi").publicKey, { commitment: "confirmed", dataSlice: { @@ -181,23 +171,15 @@ export async function getAllMarginfiAccountsByAuthority( const positionStates = await Promise.all( marginfiAccounts.map(async (x) => ({ publicKey: x.publicKey, - state: await getMarginfiAccountPositionState(umi, { - pk: toWeb3JsPublicKey(x.publicKey), - }), + state: ( + await getMarginfiAccountPositionState(umi, { + pk: toWeb3JsPublicKey(x.publicKey), + }) + )?.state, })) ); return positionStates - .sort( - (a, b) => - fromBaseUnit( - b.state?.netWorth.baseAmountUsdValue ?? BigInt(0), - USD_DECIMALS - ) - - fromBaseUnit( - a.state?.netWorth.baseAmountUsdValue ?? BigInt(0), - USD_DECIMALS - ) - ) + .sort((a, b) => calcNetWorthUsd(b.state) - calcNetWorthUsd(a.state)) .filter((x) => x.state !== undefined) .map((x) => ({ marginfiAccount: toWeb3JsPublicKey(x.publicKey), @@ -213,39 +195,73 @@ export async function getAllMarginfiAccountsByAuthority( export function getBankLiquidityAvailableBaseUnit( bank: Bank | null, - isAsset: boolean + availableToDeposit: boolean ) { let amountCanBeUsed = 0; if (bank !== null) { const [assetShareValue, liabilityShareValue] = getUpToDateShareValues(bank); + const totalDeposited = bytesToI80F48(bank.totalAssetShares.value) * assetShareValue; - amountCanBeUsed = isAsset + const totalBorrowed = + bytesToI80F48(bank.totalLiabilityShares.value) * liabilityShareValue; + + amountCanBeUsed = availableToDeposit ? Number(bank.config.depositLimit) - totalDeposited - : totalDeposited - - bytesToI80F48(bank.totalLiabilityShares.value) * liabilityShareValue; + : Math.min( + totalDeposited - totalBorrowed, + Math.max(0, Number(bank.config.borrowLimit) - totalBorrowed) + ); + } + + return BigInt(Math.floor(amountCanBeUsed)); +} + +export function getBankLiquidityUsedBaseUnit( + bank: Bank | null, + asset: boolean +) { + let amountUsed = 0; + + if (bank !== null) { + const [assetShareValue, liabilityShareValue] = getUpToDateShareValues(bank); + + const totalDeposited = + bytesToI80F48(bank.totalAssetShares.value) * assetShareValue; + const totalBorrowed = + bytesToI80F48(bank.totalLiabilityShares.value) * liabilityShareValue; + + amountUsed = asset ? totalDeposited : totalBorrowed; } - return amountCanBeUsed; + return BigInt(Math.floor(amountUsed)); } async function getTokenUsage( bank: Bank | null, isAsset: boolean, shares: number, - amountUsedAdjustment?: bigint -): Promise { + amountUsedAdjustment?: bigint, + priceType?: PriceType +): Promise { let amountUsed = 0; - let amountCanBeUsed = 0; + let amountCanBeUsed = BigInt(0); let marketPrice = 0; + let originationFee = 0; if (bank !== null) { - [marketPrice] = await fetchTokenPrices([toWeb3JsPublicKey(bank.mint)]); + [marketPrice] = await fetchTokenPrices( + [toWeb3JsPublicKey(bank.mint)], + priceType + ); const [assetShareValue, liabilityShareValue] = getUpToDateShareValues(bank); const shareValue = isAsset ? assetShareValue : liabilityShareValue; amountUsed = shares * shareValue + Number(amountUsedAdjustment ?? 0); amountCanBeUsed = getBankLiquidityAvailableBaseUnit(bank, isAsset); + originationFee = bytesToI80F48( + bank?.config.interestRateConfig.protocolOriginationFee.value + ); } return { @@ -262,23 +278,19 @@ async function getTokenUsage( : BigInt(0), }, amountCanBeUsed: { - baseUnit: BigInt(Math.round(amountCanBeUsed)), + baseUnit: amountCanBeUsed, baseAmountUsdValue: bank ? toBaseUnit( - fromBaseUnit( - BigInt(Math.round(amountCanBeUsed)), - bank.mintDecimals - ) * marketPrice, + fromBaseUnit(amountCanBeUsed, bank.mintDecimals) * marketPrice, USD_DECIMALS ) : BigInt(0), }, baseAmountMarketPriceUsd: toBaseUnit(marketPrice, USD_DECIMALS), - flashLoanFeeBps: 0, - borrowFeeBps: 0, - padding1: [], - padding2: [], - padding: new Uint8Array([]), + borrowFeeBps: isAsset ? 0 : toBps(originationFee), + padding1: new Array(5).fill(0), + padding2: new Array(8).fill(0), + padding: new Uint8Array(32), }; } @@ -289,19 +301,52 @@ interface BankSelection { type BanksCache = { [group: string]: { [mint: string]: Bank } }; +async function getBank( + umi: Umi, + data: BankSelection, + marginfiGroup: PublicKey +) { + const mint = validPubkey(data.mint) ? data.mint!.toString() : undefined; + + return data?.banksCache && mint + ? data.banksCache[marginfiGroup.toString()][mint] + : mint && mint !== PublicKey.default.toString() + ? await safeFetchBank( + umi, + publicKey( + getMarginfiAccounts(undefined, marginfiGroup).bankAccounts[ + marginfiGroup.toString() + ][mint].bank + ), + { commitment: "confirmed" } + ) + : null; +} + export async function getMarginfiAccountPositionState( umi: Umi, - protocolAccount: { pk: PublicKey; data?: MarginfiAccount }, + lpUserAccount: { pk?: PublicKey; data?: MarginfiAccount | null }, marginfiGroup?: PublicKey, supply?: BankSelection, debt?: BankSelection, - livePositionUpdates?: LivePositionUpdates -): Promise { + contextUpdates?: ContextUpdates, + priceType?: PriceType +): Promise< + | { + supplyBank: Bank | null; + debtBank: Bank | null; + marginfiGroup: PublicKey; + state: PositionState; + } + | undefined +> { let marginfiAccount = - protocolAccount.data ?? - (await safeFetchMarginfiAccount(umi, publicKey(protocolAccount.pk), { - commitment: "confirmed", - })); + lpUserAccount.data ?? + (validPubkey(lpUserAccount.pk) + ? await safeFetchMarginfiAccount(umi, publicKey(lpUserAccount.pk!), { + commitment: "confirmed", + }) + : null); if (!supply) { supply = {}; @@ -314,37 +359,15 @@ export async function getMarginfiAccountPositionState( marginfiGroup = toWeb3JsPublicKey(marginfiAccount.group); } - let supplyBank: Bank | null = - supply?.banksCache && supply.mint && marginfiGroup - ? supply.banksCache[marginfiGroup!.toString()][supply?.mint?.toString()] - : supply?.mint && supply?.mint !== PublicKey.default - ? await safeFetchBank( - umi, - publicKey( - MARGINFI_ACCOUNTS[marginfiGroup?.toString() ?? ""][ - supply?.mint.toString() - ].bank - ), - { commitment: "confirmed" } - ) - : null; - let debtBank: Bank | null = - debt?.banksCache && debt.mint && marginfiGroup - ? debt.banksCache[marginfiGroup!.toString()][debt?.mint?.toString()] - : debt?.mint && debt?.mint !== PublicKey.default - ? await safeFetchBank( - umi, - publicKey( - MARGINFI_ACCOUNTS[marginfiGroup?.toString() ?? ""][ - debt?.mint.toString() - ].bank - ), - { commitment: "confirmed" } - ) - : null; + let supplyBank: Bank | null = await getBank(umi, supply, marginfiGroup!); + let debtBank: Bank | null = await getBank(umi, debt, marginfiGroup!); + + let supplyUsage: PositionTokenState | undefined = undefined; + let debtUsage: PositionTokenState | undefined = undefined; - let supplyUsage: PositionTokenUsage | undefined = undefined; - let debtUsage: PositionTokenUsage | undefined = undefined; + if (supply.mint && debt.mint) { + await fetchTokenPrices([supply.mint, debt.mint]); + } if ( marginfiAccount !== null && @@ -377,7 +400,8 @@ export async function getMarginfiAccountPositionState( supplyBank!, true, bytesToI80F48(supplyBalances[0].assetShares.value), - livePositionUpdates?.supplyAdjustment + contextUpdates?.supplyAdjustment, + priceType ); } @@ -394,7 +418,8 @@ export async function getMarginfiAccountPositionState( debtBank!, false, bytesToI80F48(debtBalances[0].liabilityShares.value), - livePositionUpdates?.debtAdjustment + contextUpdates?.debtAdjustment, + priceType ); } } @@ -408,13 +433,25 @@ export async function getMarginfiAccountPositionState( supplyBank, true, 0, - livePositionUpdates?.supplyAdjustment + contextUpdates?.supplyAdjustment ); } + if (debtBank === null) { + return undefined; + } + + const supplyMint = TOKEN_INFO[supplyBank.mint.toString()]; + const debtMint = TOKEN_INFO[debtBank.mint.toString()]; + if ( - TOKEN_INFO[supplyBank.mint.toString()].isStableCoin && - (debtBank === null || TOKEN_INFO[debtBank.mint.toString()].isStableCoin) + supplyMint === undefined || + debtMint === undefined || + (supplyMint.isStableCoin && debtMint.isStableCoin) || + !ALL_SUPPORTED_TOKENS.includes(supplyBank.mint.toString()) || + !ALL_SUPPORTED_TOKENS.includes(debtBank.mint.toString()) || + supplyBank.config.oracleSetup === OracleSetup.StakedWithPythPush || + debtBank.config.oracleSetup === OracleSetup.StakedWithPythPush ) { return undefined; } @@ -424,14 +461,17 @@ export async function getMarginfiAccountPositionState( debtBank, false, 0, - livePositionUpdates?.debtAdjustment + contextUpdates?.debtAdjustment ); } - const supplyPrice = safeGetPrice(supply.mint!)!; - let [maxLtv, liqThreshold] = await getMaxLtvAndLiqThreshold( + if (!marginfiGroup) { + marginfiGroup = toWeb3JsPublicKey(supplyBank.group); + } + const supplyPrice = safeGetPrice(toWeb3JsPublicKey(supplyBank.mint))!; + let [maxLtvBps, liqThresholdBps] = await getMarginfiMaxLtvAndLiqThresholdBps( umi, - marginfiGroup ?? new PublicKey(DEFAULT_MARGINFI_GROUP), + marginfiGroup, { mint: toWeb3JsPublicKey(supplyBank.mint), bank: supplyBank, @@ -452,26 +492,31 @@ export async function getMarginfiAccountPositionState( ); return { - liqUtilizationRateBps: getLiqUtilzationRateBps( - supplyUsd, - debtUsd, - toBps(liqThreshold) - ), - netWorth: { - baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS), - baseUnit: toBaseUnit( - (supplyUsd - debtUsd) / supplyPrice, - supplyUsage!.decimals + supplyBank, + debtBank, + marginfiGroup, + state: { + liqUtilizationRateBps: getLiqUtilzationRateBps( + supplyUsd, + debtUsd, + liqThresholdBps ), + netWorth: { + baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS), + baseUnit: toBaseUnit( + (supplyUsd - debtUsd) / supplyPrice, + supplyUsage!.decimals + ), + }, + supply: supplyUsage!, + debt: debtUsage!, + maxLtvBps, + liqThresholdBps, + lastRefreshed: BigInt(currentUnixSeconds()), + padding1: new Array(6).fill(0), + padding2: new Array(4).fill(0), + padding: new Array(2).fill(0), }, - supply: supplyUsage!, - debt: debtUsage!, - maxLtvBps: toBps(maxLtv), - liqThresholdBps: toBps(liqThreshold), - lastUpdated: BigInt(currentUnixSeconds()), - padding1: [], - padding2: [], - padding: [], }; } diff --git a/solauto-sdk/src/utils/marginfi/general.ts b/solauto-sdk/src/utils/marginfi/general.ts new file mode 100644 index 00000000..1d6866b2 --- /dev/null +++ b/solauto-sdk/src/utils/marginfi/general.ts @@ -0,0 +1,234 @@ +import { PublicKey } from "@solana/web3.js"; +import { AccountMeta, Program, publicKey, Umi } from "@metaplex-foundation/umi"; +import { + fromWeb3JsPublicKey, + toWeb3JsPublicKey, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { ProgramEnv, MarginfiAssetAccounts } from "../../types"; +import { + getMarginfiAccounts, + MarginfiBankAccountsMap, +} from "../../constants"; +import { + Balance, + Bank, + fetchBank, + getMarginfiErrorFromCode, + getMarginfiErrorFromName, + MarginfiAccount, + OracleSetup, + safeFetchAllBank, +} from "../../externalSdks/marginfi"; +import { bytesToI80F48, fromBaseUnit, toBps } from "../numberUtils"; +import { getTokenAccountData } from "../accountUtils"; +import { + getMostUpToDatePythOracle, +} from "../pythUtils"; +import { getAccountMeta } from "../solanaUtils"; +import { validPubkey } from "../generalUtils"; + +export function createDynamicMarginfiProgram(env?: ProgramEnv): Program { + return { + name: "marginfi", + publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program), + getErrorFromCode(code: number, cause?: Error) { + return getMarginfiErrorFromCode(code, this, cause); + }, + getErrorFromName(name: string, cause?: Error) { + return getMarginfiErrorFromName(name, this, cause); + }, + isOnCluster() { + return true; + }, + }; +} + +export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) { + return umi.use({ + install(umi) { + umi.programs.add( + createDynamicMarginfiProgram(marginfiEnv ?? "Prod"), + false + ); + }, + }); +} + +export async function getAllBankRelatedAccounts( + umi: Umi, + bankAccountsMap: MarginfiBankAccountsMap +): Promise { + const banks = Object.values(bankAccountsMap).flatMap((group) => + Object.values(group).map((accounts) => accounts.bank) + ); + const banksData = await safeFetchAllBank( + umi, + banks.map((x) => publicKey(x)) + ); + + const oracles = banksData + .map((bank) => + bank.config.oracleKeys + .map((x) => toWeb3JsPublicKey(x)) + .filter((x) => validPubkey(x)) + ) + .flat() + .map((x) => x.toString()); + + const otherAccounts = Object.entries(bankAccountsMap).flatMap( + ([groupName, tokenMap]) => + Object.values(tokenMap).flatMap((accounts) => [ + groupName, + accounts.liquidityVault, + accounts.vaultAuthority, + ]) + ); + + return Array.from(new Set([...banks, ...oracles, ...otherAccounts])) + .filter((x) => x !== PublicKey.default.toString()) + .map((x) => new PublicKey(x)); +} + +export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) { + const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk)); + const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault); + const vaultAuthority = (await getTokenAccountData(umi, liquidityVault)) + ?.owner; + const priceOracle = await getMarginfiPriceOracle(umi, { data: bank }); + + return { + bank: bankPk, + liquidityVault, + vaultAuthority, + priceOracle, + }; +} + +export async function getMarginfiPriceOracle( + umi: Umi, + bank: { pk?: PublicKey; data?: Bank } +) { + if (!bank.data) { + bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!)); + } + + const priceOracle = + bank.data.config.oracleSetup === OracleSetup.PythPushOracle + ? await getMostUpToDatePythOracle( + umi, + bank.data.config.oracleKeys + .map((x) => toWeb3JsPublicKey(x)) + .filter((x) => validPubkey(x)) + ) + : toWeb3JsPublicKey(bank.data.config.oracleKeys[0]); + + return priceOracle; +} + +interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts { + mint: PublicKey; +} + +export function findMarginfiAccounts( + bank: PublicKey +): AllMarginfiAssetAccounts { + const search = (bankAccounts: MarginfiBankAccountsMap) => { + for (const group in bankAccounts) { + for (const key in bankAccounts[group]) { + const account = bankAccounts[group][key]; + if ( + account.bank.toString().toLowerCase() === + bank.toString().toLowerCase() + ) { + return { ...account, mint: new PublicKey(key) }; + } + } + } + }; + + let res = search(getMarginfiAccounts("Prod").bankAccounts); + if (res) { + return res; + } + res = search(getMarginfiAccounts("Staging").bankAccounts); + if (res) { + return res; + } + + throw new Error(`Marginfi accounts not found by the bank: ${bank}`); +} + +export async function getRemainingAccountsForMarginfiHealthCheck( + umi: Umi, + balance: Balance +): Promise { + if (!balance.active) { + return []; + } + const priceOracle = await getMarginfiPriceOracle(umi, { + pk: toWeb3JsPublicKey(balance.bankPk), + }); + return [ + getAccountMeta(toWeb3JsPublicKey(balance.bankPk)), + getAccountMeta(priceOracle), + ]; +} + +export function calcMarginfiMaxLtvAndLiqThresholdBps( + supplyBank: Bank, + debtBank: Bank, + supplyPrice: number +): [number, number] { + let maxLtv = + bytesToI80F48(supplyBank.config.assetWeightInit.value) / + bytesToI80F48(debtBank.config.liabilityWeightInit.value); + const liqThreshold = + bytesToI80F48(supplyBank.config.assetWeightMaint.value) / + bytesToI80F48(debtBank.config.liabilityWeightMaint.value); + + const totalDepositedUsdValue = + fromBaseUnit( + BigInt( + Math.round( + bytesToI80F48(supplyBank.totalAssetShares.value) * + bytesToI80F48(supplyBank.assetShareValue.value) + ) + ), + supplyBank.mintDecimals + ) * supplyPrice!; + if ( + supplyBank.config.totalAssetValueInitLimit !== BigInt(0) && + totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit + ) { + const discount = + Number(supplyBank.config.totalAssetValueInitLimit) / + totalDepositedUsdValue; + maxLtv = maxLtv * Number(discount); + } + + return [toBps(maxLtv, "Floor"), toBps(liqThreshold, "Floor")]; +} + +export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) { + return ( + marginfiAccount.lendingAccount.balances.find( + (x) => + x.bankPk.toString() !== PublicKey.default.toString() && + (bytesToI80F48(x.assetShares.value) > 0.000001 || + bytesToI80F48(x.liabilityShares.value) > 0.000001) + ) === undefined + ); +} + +export function composeRemainingAccounts(accs: AccountMeta[]): AccountMeta[] { + const banksAndOracles: [AccountMeta, AccountMeta][] = accs.reduce( + (acc: [AccountMeta, AccountMeta][], _, i) => + i % 2 === 0 ? [...acc, [accs[i], accs[i + 1]]] : acc, + [] + ); + return banksAndOracles + .sort((a, b) => + b[0].pubkey.toString().localeCompare(a[0].pubkey.toString()) + ) + .flat(); +} diff --git a/solauto-sdk/src/utils/marginfi/index.ts b/solauto-sdk/src/utils/marginfi/index.ts new file mode 100644 index 00000000..28a4ebcf --- /dev/null +++ b/solauto-sdk/src/utils/marginfi/index.ts @@ -0,0 +1,2 @@ +export * from "./data"; +export * from "./general"; \ No newline at end of file diff --git a/solauto-sdk/src/utils/numberUtils.ts b/solauto-sdk/src/utils/numberUtils.ts index 4dced0a5..b772da96 100644 --- a/solauto-sdk/src/utils/numberUtils.ts +++ b/solauto-sdk/src/utils/numberUtils.ts @@ -1,5 +1,86 @@ -import { BASIS_POINTS, MIN_REPAY_GAP_BPS } from "../constants"; -import { RebalanceDirection } from "../generated"; +import { PublicKey } from "@solana/web3.js"; +import { + BASIS_POINTS, + MIN_REPAY_GAP_BPS, + OFFSET_FROM_MAX_LTV, + USD_DECIMALS, +} from "../constants"; +import { PositionState, PriceType } from "../generated"; +import { RoundAction } from "../types"; +import { safeGetPrice } from "./priceUtils"; +import { StrategyType, strategyType } from "./stringUtils"; +import { getDebtAdjustment } from "../services"; + +export function calcNetWorthUsd(state?: PositionState) { + return fromRoundedUsdValue(state?.netWorth.baseAmountUsdValue ?? BigInt(0)); +} + +export function calcSupplyUsd(state?: PositionState) { + return fromRoundedUsdValue( + state?.supply.amountUsed.baseAmountUsdValue ?? BigInt(0) + ); +} + +export function calcDebtUsd(state?: PositionState) { + return fromRoundedUsdValue( + state?.debt.amountUsed.baseAmountUsdValue ?? BigInt(0) + ); +} + +export function calcNetWorth(state?: PositionState) { + return fromBaseUnit( + state?.netWorth.baseUnit ?? BigInt(0), + state?.supply.decimals ?? 1 + ); +} + +export function calcTotalSupply(state?: PositionState) { + return fromBaseUnit( + state?.supply.amountUsed.baseUnit ?? BigInt(0), + state?.supply.decimals ?? 1 + ); +} + +export function calcTotalDebt(state?: PositionState) { + return fromBaseUnit( + state?.debt.amountUsed.baseUnit ?? BigInt(0), + state?.debt.decimals ?? 1 + ); +} + +export function debtLiquidityAvailable(state?: PositionState) { + return fromBaseUnit( + state?.debt.amountCanBeUsed.baseUnit ?? BigInt(0), + state?.debt.decimals ?? 1 + ); +} + +export function debtLiquidityUsdAvailable(state?: PositionState) { + return fromRoundedUsdValue( + state?.debt.amountCanBeUsed.baseAmountUsdValue ?? BigInt(0) + ); +} + +export function supplyLiquidityDepositable(state?: PositionState) { + return fromBaseUnit( + state?.supply.amountCanBeUsed.baseUnit ?? BigInt(0), + state?.supply.decimals ?? 1 + ); +} + +export function supplyLiquidityUsdDepositable(state?: PositionState) { + return fromRoundedUsdValue( + state?.supply.amountCanBeUsed.baseAmountUsdValue ?? BigInt(0) + ); +} + +export function fromRoundedUsdValue(number: bigint) { + return fromBaseUnit(number, USD_DECIMALS); +} + +export function toRoundedUsdValue(number: number) { + return toBaseUnit(number, USD_DECIMALS); +} export function getLiqUtilzationRateBps( supplyUsd: number, @@ -13,11 +94,21 @@ export function getLiqUtilzationRateBps( return toBps(debtUsd / (supplyUsd * fromBps(liqThresholdBps))); } -export function toBaseUnit(value: number, decimals: number): bigint { - return BigInt(Math.round(value * Math.pow(10, decimals))); +export function toBaseUnit( + value: number, + decimals: number, + roundAction: RoundAction = "Round" +): bigint { + if (!decimals) { + return BigInt(Math.floor(value)); + } + return BigInt(roundNumber(value * Math.pow(10, decimals), roundAction)); } export function fromBaseUnit(value: bigint, decimals: number): number { + if (!decimals) { + return Number(value); + } return Number(value) / Math.pow(10, decimals); } @@ -25,8 +116,20 @@ export function fromBps(value: number): number { return value / BASIS_POINTS; } -export function toBps(value: number): number { - return Math.round(value * BASIS_POINTS); +export function toBps( + value: number, + roundAction: RoundAction = "Round" +): number { + const bps = value * BASIS_POINTS; + return roundNumber(bps, roundAction); +} + +function roundNumber(number: number, roundAction: RoundAction = "Round") { + return roundAction === "Round" + ? Math.round(number) + : roundAction === "Floor" + ? Math.floor(number) + : Math.ceil(number); } export function bytesToI80F48(bytes: number[]): number { @@ -69,84 +172,6 @@ export function uint8ArrayToBigInt(uint8Array: Uint8Array): bigint { return (BigInt(high) << 32n) | BigInt(low); } -export function getDebtAdjustmentUsd( - liqThresholdBps: number, - supplyUsd: number, - debtUsd: number, - targetLiqUtilizationRateBps: number, - adjustmentFeeBps?: number -) { - const adjustmentFee = - adjustmentFeeBps && adjustmentFeeBps > 0 ? fromBps(adjustmentFeeBps) : 0; - const liqThreshold = fromBps(liqThresholdBps); - const targetLiqUtilizationRate = fromBps(targetLiqUtilizationRateBps); - - const debtAdjustmentUsd = - (targetLiqUtilizationRate * supplyUsd * liqThreshold - debtUsd) / - (1 - targetLiqUtilizationRate * (1 - adjustmentFee) * liqThreshold); - return debtAdjustmentUsd; -} - -export function getSolautoFeesBps( - isReferred: boolean, - targetLiqUtilizationRateBps: number | undefined, - positionNetWorthUsd: number, - rebalanceDirection: RebalanceDirection -): { - solauto: number; - referrer: number; - total: number; -} { - const minSize = 10_000; // Minimum position size - const maxSize = 500_000; // Maximum position size - const maxFeeBps = 200; // Fee in basis points for minSize (2%) - const minFeeBps = 50; // Fee in basis points for maxSize (0.5%) - const k = 1.5; - - if ( - targetLiqUtilizationRateBps !== undefined && - targetLiqUtilizationRateBps === 0 - ) { - return { - solauto: 0, - referrer: 0, - total: 0, - }; - } - - let feeBps: number = 0; - - if ( - targetLiqUtilizationRateBps !== undefined || - rebalanceDirection === RebalanceDirection.Repay - ) { - feeBps = 25; - } else if (positionNetWorthUsd <= minSize) { - feeBps = maxFeeBps; - } else if (positionNetWorthUsd >= maxSize) { - feeBps = minFeeBps; - } else { - const t = - (Math.log(positionNetWorthUsd) - Math.log(minSize)) / - (Math.log(maxSize) - Math.log(minSize)); - feeBps = Math.round( - minFeeBps + (maxFeeBps - minFeeBps) * (1 - Math.pow(t, k)) - ); - } - - let referrer = 0; - if (isReferred) { - feeBps *= 0.9; - referrer = Math.floor(feeBps * 0.15); - } - - return { - solauto: feeBps - referrer, - referrer, - total: feeBps, - }; -} - export function getMaxLiqUtilizationRateBps( maxLtvBps: number, liqThresholdBps: number, @@ -161,20 +186,63 @@ export function getMaxLiqUtilizationRateBps( export function maxRepayFromBps(maxLtvBps: number, liqThresholdBps: number) { return Math.min( 9000, - getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps - 1000, 0.01) + getMaxLiqUtilizationRateBps( + maxLtvBps, + liqThresholdBps - 1000, + OFFSET_FROM_MAX_LTV + ) ); } export function maxRepayToBps(maxLtvBps: number, liqThresholdBps: number) { return Math.min( maxRepayFromBps(maxLtvBps, liqThresholdBps) - MIN_REPAY_GAP_BPS, - getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps, 0.01) + getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps, OFFSET_FROM_MAX_LTV) ); } export function maxBoostToBps(maxLtvBps: number, liqThresholdBps: number) { return Math.min( maxRepayToBps(maxLtvBps, liqThresholdBps), - getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps, 0.01) + getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps, OFFSET_FROM_MAX_LTV) + ); +} + +export function realtimeUsdToEmaUsd( + realtimeAmountUsd: number, + mint: PublicKey +) { + return ( + (realtimeAmountUsd / safeGetPrice(mint, PriceType.Realtime)!) * + safeGetPrice(mint, PriceType.Ema)! + ); +} + +export function boostSettingToLeverageFactor( + supplyMint: PublicKey, + debtMint: PublicKey, + boostToBps: number, + liqThresholdBps: number +) { + const strategy = strategyType(supplyMint, debtMint); + const supplyUsd = 100; + const debtUsd = getDebtAdjustment( + liqThresholdBps, + { supplyUsd: 100, debtUsd: 0 }, + boostToBps + ).debtAdjustmentUsd; + return getLeverageFactor(strategy, supplyUsd + debtUsd, debtUsd); +} + +export function getLeverageFactor( + strategyType: StrategyType, + supplyUsd: number, + debtUsd: number +): number { + return ( + (strategyType === "Long" || strategyType === "Ratio" + ? supplyUsd + : debtUsd) / + (supplyUsd - debtUsd) ); } diff --git a/solauto-sdk/src/utils/priceUtils.ts b/solauto-sdk/src/utils/priceUtils.ts index a6cfe8e0..cc3f4eb3 100644 --- a/solauto-sdk/src/utils/priceUtils.ts +++ b/solauto-sdk/src/utils/priceUtils.ts @@ -1,59 +1,87 @@ import { PublicKey } from "@solana/web3.js"; import { PublicKey as UmiPublicKey } from "@metaplex-foundation/umi"; -import { PYTH_PRICE_FEED_IDS } from "../constants/pythConstants"; +import * as SwbCommon from "@switchboard-xyz/common"; +import { + PYTH_PRICE_FEED_IDS, + PRICES, + SWITCHBOARD_PRICE_FEED_IDS, +} from "../constants"; import { fromBaseUnit, toBaseUnit } from "./numberUtils"; -import { PRICES } from "../constants/solautoConstants"; -import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants"; import { consoleLog, + createRecord, currentUnixSeconds, retryWithExponentialBackoff, - zip, + tokenInfo, } from "./generalUtils"; -import { CrossbarClient } from "@switchboard-xyz/on-demand"; +import { getJupPriceData } from "./jupiterUtils"; +import { PriceType } from "../generated"; +import { PriceBias } from "../externalSdks/marginfi"; + +interface PriceResult { + realtimePrice: number; + confInterval?: number; + emaPrice?: number; + emaConfInterval?: number; +} -export async function fetchTokenPrices(mints: PublicKey[]): Promise { +export async function fetchTokenPrices( + mints: PublicKey[], + priceType: PriceType = PriceType.Realtime, + priceBias?: PriceBias +): Promise { const currentTime = currentUnixSeconds(); - if ( - !mints.some( - (mint) => - !(mint.toString() in PRICES) || - currentTime - PRICES[mint.toString()].time > 3 + const mintStrs = mints.map((x) => x.toString()); + const cachedPrices: Record = Object.fromEntries( + Object.entries(PRICES).filter( + ([mint, price]) => + mintStrs.includes(mint) && currentTime - price.time <= 3 ) - ) { - return mints.map((mint) => PRICES[mint.toString()].price); - } - - const pythMints = mints.filter((x) => x.toString() in PYTH_PRICE_FEED_IDS); - const switchboardMints = mints.filter( - (x) => x.toString() in SWITCHBOARD_PRICE_FEED_IDS ); - const [pythData, switchboardData] = await Promise.all([ - zip(pythMints, await getPythPrices(pythMints)), - zip(switchboardMints, await getSwitchboardPrices(switchboardMints)), - ]); - - const prices = mints.map((mint) => { - const item = [...pythData, ...switchboardData].find((data) => - data[0].equals(mint) - ); - return item ? item[1] : 0; - }); + const newMints = mintStrs + .filter((x) => !Object.keys(cachedPrices).includes(x)) + .map((x) => new PublicKey(x)); + const pythMints = newMints.filter((x) => + Object.keys(PYTH_PRICE_FEED_IDS).includes(x.toString()) + ); + const switchboardMints = newMints.filter( + (x) => + Object.keys(SWITCHBOARD_PRICE_FEED_IDS).includes(x.toString()) && + !pythMints.map((y) => y.toString()).includes(x.toString()) + ); + const otherMints = newMints.filter( + (x) => !pythMints.includes(x) && !switchboardMints.includes(x) + ); + const newPrices: Record = Object.assign( + {}, + ...(await Promise.all([ + getPythPrices(pythMints), + getSwitchboardPrices(switchboardMints), + getJupTokenPrices(otherMints), + ])) + ); - for (var i = 0; i < mints.length; i++) { - PRICES[mints[i].toString()] = { - price: prices[i], + for (const mint of newMints) { + const data = newPrices[mint.toString()]; + const realtimePrice = data.realtimePrice; + PRICES[mint.toString()] = { + realtimePrice, + confInterval: data.confInterval ?? 0, + emaPrice: data.emaPrice ?? realtimePrice, + emaConfInterval: data.emaConfInterval ?? 0, time: currentUnixSeconds(), }; } - return prices; + return mints.map((x) => safeGetPrice(x, priceType, priceBias)!); } -export async function getPythPrices(mints: PublicKey[]) { +export async function getPythPrices( + mints: PublicKey[] +): Promise> { if (mints.length === 0) { - return []; + return {}; } const priceFeedIds = mints.map( @@ -62,10 +90,22 @@ export async function getPythPrices(mints: PublicKey[]) { const getReq = async () => await fetch( - `https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}` + `https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds + .map((x) => `ids%5B%5D=${x}`) + .join("&")}` ); - const prices: number[] = await retryWithExponentialBackoff( + const deriveValue = (price: number, exponent: number) => { + if (exponent > 0) { + return Number(toBaseUnit(Number(price), exponent)); + } else if (exponent < 0) { + return fromBaseUnit(BigInt(price), Math.abs(exponent)); + } else { + return Number(price); + } + }; + + const prices: PriceResult[] = await retryWithExponentialBackoff( async () => { let resp = await getReq(); let status = resp.status; @@ -75,90 +115,150 @@ export async function getPythPrices(mints: PublicKey[]) { const json = await resp.json(); const prices = json.parsed.map((x: any) => { - if (x.price.expo > 0) { - return Number(toBaseUnit(Number(x.price.price), x.price.expo)); - } else if (x.price.expo < 0) { - return fromBaseUnit(BigInt(x.price.price), Math.abs(x.price.expo)); - } else { - return Number(x.price.price); - } + return { + realtimePrice: deriveValue(x.price.price, x.price.expo), + confInterval: deriveValue(x.price.conf, x.price.expo) * 2.12, + emaPrice: deriveValue(x.ema_price.price, x.ema_price.expo), + emaConfInterval: + deriveValue(x.ema_price.conf, x.ema_price.expo) * 2.12, + }; }); return prices; }, 5, - 200 + 250 ); - return prices; + return createRecord( + mints.map((x) => x.toString()), + prices + ); +} + +function getSortedPriceData( + prices: Record, + mints: PublicKey[] +) { + const sortedPrices: { [key: string]: any } = {}; + + for (const mint of mints) { + const key = mint.toString(); + if (prices.hasOwnProperty(key)) { + sortedPrices[key] = prices[key]; + } + } + + return sortedPrices; } export async function getSwitchboardPrices( mints: PublicKey[] -): Promise { +): Promise> { if (mints.length === 0) { - return []; + return {}; } - const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz"); - let prices = await retryWithExponentialBackoff( - async () => { - const res = await crossbar.simulateSolanaFeeds( - "mainnet", - mints.map((x) => SWITCHBOARD_PRICE_FEED_IDS[x.toString()]) - ); - - const prices = res.flatMap((x) => x.results[0]); - if (prices.find((x) => !x || x === -Infinity)) { - throw new Error("Unable to fetch Switchboard prices"); - } + const { CrossbarClient } = SwbCommon; + const crossbar = new CrossbarClient("https://integrator-crossbar.mrgn.app/"); - return prices; - }, - 8, - 250 - ); + let prices: Record = {}; + try { + prices = await retryWithExponentialBackoff( + async () => { + const resp = await crossbar.simulateFeeds( + mints.map((x) => SWITCHBOARD_PRICE_FEED_IDS[x.toString()].feedHash) + ); - const missingPrices = zip(mints, prices).filter((x) => !x[1]); - const jupPrices = zip( - missingPrices.map((x) => x[0]), - await getJupTokenPrices(missingPrices.map((x) => x[0])) - ); + const data = resp.flatMap((x) => x.results[0]); + if ( + data.filter((x) => !x || isNaN(Number(x)) || Number(x) <= 0).length > + 0 + ) { + throw new Error("Unable to fetch Switchboard prices"); + } - prices = prices.map((x, i) => - x ? x : jupPrices.find((y) => y[0].toString() === mints[i].toString())![1] + const finalMap: Record = {}; + for (const item of resp) { + for (const [k, v] of Object.entries(SWITCHBOARD_PRICE_FEED_IDS)) { + if (item.feedHash === v.feedHash) { + const price = Number(item.results[0]); + finalMap[k] = { + realtimePrice: price, + }; + } + } + } + return finalMap; + }, + 2, + 350 + ); + } catch { + consoleLog("Failed to fetch Switchboard prices after multiple retries"); + } + + const missingMints = mints.filter((x) => !prices[x.toString()]); + const jupPrices = await getJupTokenPrices( + missingMints.map((x) => new PublicKey(x)) ); - return prices; + return { ...prices, ...jupPrices }; } -export async function getJupTokenPrices(mints: PublicKey[]) { +export async function getJupTokenPrices( + mints: PublicKey[] +): Promise> { if (mints.length == 0) { - return []; + return {}; } - const data = await retryWithExponentialBackoff(async () => { - const res = ( - await fetch( - "https://api.jup.ag/price/v2?ids=" + - mints.map((x) => x.toString()).join(",") - ) - ).json(); - return res; - }, 6); - - const prices = Object.values(data.data as { [key: string]: any }).map( - (x) => parseFloat(x.price as string) as number + const data = getSortedPriceData(await getJupPriceData(mints), mints); + + const prices: Record = Object.fromEntries( + mints.map((mint) => [ + mint, + data !== null && + typeof data === "object" && + typeof data[mint.toString()] === "object" && + "usdPrice" in data[mint.toString()] + ? { + realtimePrice: parseFloat(data[mint.toString()].usdPrice as string), + } + : { realtimePrice: 0 }, + ]) ); return prices; } export function safeGetPrice( - mint: PublicKey | UmiPublicKey | undefined + mint: PublicKey | UmiPublicKey | string | undefined, + priceType: PriceType = PriceType.Realtime, + priceBias?: PriceBias ): number | undefined { if (mint && mint?.toString() in PRICES) { - return PRICES[mint!.toString()].price; + const priceData = PRICES[mint!.toString()]; + let price = + priceType === PriceType.Ema + ? priceData.emaPrice + : priceData.realtimePrice; + + if (priceBias !== undefined) { + const confInterval = + priceType === PriceType.Ema + ? priceData.emaConfInterval + : priceData.confInterval; + const conf = Math.min(confInterval, price * 0.05); + + if (priceBias === PriceBias.Low) { + price -= conf; + } else { + price += conf; + } + } + + return price; } return undefined; } diff --git a/solauto-sdk/src/utils/pythUtils.ts b/solauto-sdk/src/utils/pythUtils.ts new file mode 100644 index 00000000..8ca99a49 --- /dev/null +++ b/solauto-sdk/src/utils/pythUtils.ts @@ -0,0 +1,25 @@ +import { PublicKey } from "@solana/web3.js"; +import { publicKey, Umi } from "@metaplex-foundation/umi"; +import { PYTH_PUSH_PROGRAM } from "../constants"; +import { u16ToArrayBufferLE, zip } from "./generalUtils"; +import { safeFetchAllPriceUpdateV2Account } from "../externalSdks/pyth"; + +export async function getMostUpToDatePythOracle( + umi: Umi, + oracleKeys: PublicKey[] +) { + const oracles = zip( + oracleKeys, + await safeFetchAllPriceUpdateV2Account( + umi, + oracleKeys.map((x) => publicKey(x)), + { commitment: "confirmed" } + ) + ).sort( + (a, b) => + Number(b[1]?.priceMessage.publishTime ?? 0) - + Number(a[1]?.priceMessage.publishTime ?? 0) + ); + + return oracles[0][0]; +} diff --git a/solauto-sdk/src/utils/solanaUtils.ts b/solauto-sdk/src/utils/solanaUtils.ts index a564a23e..067f7c51 100644 --- a/solauto-sdk/src/utils/solanaUtils.ts +++ b/solauto-sdk/src/utils/solanaUtils.ts @@ -1,19 +1,4 @@ import bs58 from "bs58"; -import { - AddressLookupTableInput, - Signer, - TransactionBuilder, - Umi, - WrappedInstruction, - publicKey, - transactionBuilder, -} from "@metaplex-foundation/umi"; -import { - fromWeb3JsInstruction, - toWeb3JsPublicKey, - toWeb3JsTransaction, -} from "@metaplex-foundation/umi-web3js-adapters"; -import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; import { AddressLookupTableAccount, BlockhashWithExpiryBlockHeight, @@ -32,36 +17,51 @@ import { createCloseAccountInstruction, createTransferInstruction, } from "@solana/spl-token"; +import { + AccountMeta, + AddressLookupTableInput, + Signer, + TransactionBuilder, + Umi, + WrappedInstruction, + publicKey, + transactionBuilder, +} from "@metaplex-foundation/umi"; +import { + fromWeb3JsInstruction, + fromWeb3JsPublicKey, + toWeb3JsPublicKey, + toWeb3JsTransaction, +} from "@metaplex-foundation/umi-web3js-adapters"; +import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; +import { PriorityFeeSetting, ProgramEnv, TransactionRunType } from "../types"; +import { + getLendingAccountEndFlashloanInstructionDataSerializer, + getLendingAccountStartFlashloanInstructionDataSerializer, +} from "../externalSdks/marginfi"; import { getTokenAccount } from "./accountUtils"; import { arraysAreEqual, consoleLog, + customRpcCall, retryWithExponentialBackoff, } from "./generalUtils"; -import { - getLendingAccountEndFlashloanInstructionDataSerializer, - getLendingAccountStartFlashloanInstructionDataSerializer, -} from "../marginfi-sdk"; -import { PriorityFeeSetting, TransactionRunType } from "../types"; -import { createDynamicSolautoProgram } from "./solauto"; -import { SOLAUTO_PROD_PROGRAM } from "../constants"; - -export function buildHeliusApiUrl(heliusApiKey: string) { - return `https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`; -} - -export function buildIronforgeApiUrl(ironforgeApiKey: string) { - return `https://rpc.ironforge.network/mainnet?apiKey=${ironforgeApiKey}`; -} +import { createDynamicSolautoProgram } from "./solautoUtils"; +import { createDynamicMarginfiProgram } from "./marginfi"; +import { usePriorityFee } from "../services"; export function getSolanaRpcConnection( rpcUrl: string, - programId: PublicKey = SOLAUTO_PROD_PROGRAM + programId?: PublicKey, + lpEnv?: ProgramEnv ): [Connection, Umi] { - const connection = new Connection(rpcUrl, "confirmed"); + const connection = new Connection(rpcUrl, { + commitment: "confirmed", + }); const umi = createUmi(connection).use({ install(umi) { umi.programs.add(createDynamicSolautoProgram(programId), false); + umi.programs.add(createDynamicMarginfiProgram(lpEnv), false); }, }); return [connection, umi]; @@ -157,12 +157,41 @@ export function splTokenTransferUmiIx( ); } +export function getAccountMeta( + pubkey: PublicKey, + isSigner: boolean = false, + isWritable: boolean = false +): AccountMeta { + return { pubkey: fromWeb3JsPublicKey(pubkey), isSigner, isWritable }; +} + +export async function getWalletSplBalances( + conn: Connection, + wallet: PublicKey, + tokenMints: PublicKey[] +): Promise { + return await Promise.all( + tokenMints.map(async (mint) => { + try { + const data = await conn.getTokenAccountBalance( + getTokenAccount(wallet, mint), + "confirmed" + ); + return BigInt(data.value.amount); + } catch { + return 0n; + } + }) + ); +} + export async function getAddressLookupInputs( umi: Umi, lookupTableAddresses: string[] ): Promise { const addressLookupTableAccountInfos = await umi.rpc.getAccounts( - lookupTableAddresses.map((key) => publicKey(key)) + lookupTableAddresses.map((key) => publicKey(key)), + { commitment: "confirmed" } ); return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => { @@ -180,33 +209,117 @@ export async function getAddressLookupInputs( }, new Array()); } +export function prependTx( + tx: TransactionBuilder, + txsToAdd: (TransactionBuilder | WrappedInstruction)[] +) { + const instructions = tx.getInstructions(); + const keccakIdx = instructions.findIndex( + (x) => + x.programId.toString() === "KeccakSecp256k11111111111111111111111111111" + ); + if (keccakIdx !== -1) { + const [beforeKeccak, afterKeccak] = tx.splitByIndex(keccakIdx + 1); + // IMPORTANT: Preserve lookup tables from original transaction + const lookupTables = tx.options.addressLookupTables ?? []; + let finalTx = transactionBuilder() + .setAddressLookupTables(lookupTables) + .add(beforeKeccak); + for (const txToAdd of txsToAdd) { + finalTx = finalTx.append(txToAdd); + } + return finalTx.add(afterKeccak); + } else { + let finalTx = tx; + for (const txToAdd of txsToAdd) { + finalTx = finalTx.prepend(txToAdd); + } + return finalTx; + } +} + +const MAX_TX_SIZE = 1232; + +// Dummy blockhash for size checking (all zeros, 32 bytes base58 encoded) +const DUMMY_BLOCKHASH = "11111111111111111111111111111111"; + +/** + * Safely checks if a transaction can be serialized and returns its actual size. + * Returns undefined if serialization fails. + */ +export function getActualTxSize( + umi: Umi, + tx: TransactionBuilder +): number | undefined { + try { + // Set a dummy blockhash if not already set, just for size checking + const txWithBlockhash = tx.setBlockhash(DUMMY_BLOCKHASH); + // Build the transaction and convert to web3.js format + const builtTx = txWithBlockhash.build(umi); + const web3Tx = toWeb3JsTransaction(builtTx); + // Actually serialize to get the real size + const serialized = web3Tx.serialize(); + return serialized.length; + } catch (e) { + // Serialization failed - transaction is too large or malformed + return undefined; + } +} + +/** + * Checks if a transaction can fit in a single transaction by actually serializing it. + * More accurate than getTransactionSize() estimation. + */ +export function canSerializeTransaction( + umi: Umi, + tx: TransactionBuilder, + buffer: number = 0 +): boolean { + const size = getActualTxSize(umi, tx); + if (size === undefined) { + return false; + } + return size + buffer <= MAX_TX_SIZE; +} + export function addTxOptimizations( - signer: Signer, - transaction: TransactionBuilder, + umi: Umi, + tx: TransactionBuilder, computeUnitPrice?: number, computeUnitLimit?: number ) { - return transaction - .prepend( - computeUnitPrice !== undefined - ? setComputeUnitPriceUmiIx(signer, computeUnitPrice) - : transactionBuilder() - ) - .prepend( - computeUnitLimit - ? setComputeUnitLimitUmiIx(signer, computeUnitLimit) - : transactionBuilder() - ); + const computePriceIx = + computeUnitPrice !== undefined + ? setComputeUnitPriceUmiIx(umi.identity, computeUnitPrice) + : transactionBuilder(); + const computeLimitIx = computeUnitLimit + ? setComputeUnitLimitUmiIx(umi.identity, computeUnitLimit) + : transactionBuilder(); + + const allOptimizations = tx.prepend(computePriceIx).prepend(computeLimitIx); + const withCuPrice = tx.prepend(computePriceIx); + const withCuLimit = tx.prepend(computeLimitIx); + + // Use actual serialization check instead of estimate + if (canSerializeTransaction(umi, allOptimizations)) { + return prependTx(tx, [computePriceIx, computeLimitIx]); + } else if (canSerializeTransaction(umi, withCuPrice)) { + return prependTx(tx, [computePriceIx]); + } else if (canSerializeTransaction(umi, withCuLimit)) { + return prependTx(tx, [computeLimitIx]); + } else { + return tx; + } } export function assembleFinalTransaction( - signer: Signer, + umi: Umi, transaction: TransactionBuilder, computeUnitPrice?: number, computeUnitLimit?: number ) { const tx = addTxOptimizations( - signer, + umi, transaction, computeUnitPrice, computeUnitLimit @@ -285,20 +398,40 @@ async function simulateTransaction( return simulationResult; } +export async function getQnComputeUnitPriceEstimate( + umi: Umi, + programId: PublicKey, + blockheight: number = 50 +): Promise { + return await customRpcCall(umi, "qn_estimatePriorityFees", { + last_n_blocks: blockheight, + account: programId.toString(), + api_version: 2, + }); +} + export async function getComputeUnitPriceEstimate( umi: Umi, tx: TransactionBuilder, - prioritySetting: PriorityFeeSetting + prioritySetting: PriorityFeeSetting, + useAccounts?: boolean ): Promise { const web3Transaction = toWeb3JsTransaction( (await tx.setLatestBlockhash(umi, { commitment: "finalized" })).build(umi) ); + const accountKeys = tx + .getInstructions() + .flatMap((x) => x.keys.flatMap((x) => x.pubkey.toString())); + let feeEstimate: number | undefined; try { - const resp = await umi.rpc.call("getPriorityFeeEstimate", [ + const resp = await customRpcCall(umi, "getPriorityFeeEstimate", [ { - transaction: bs58.encode(web3Transaction.serialize()), + transaction: !useAccounts + ? bs58.encode(web3Transaction.serialize()) + : undefined, + accountKeys: useAccounts ? accountKeys : undefined, options: { priorityLevel: prioritySetting.toString(), }, @@ -307,11 +440,9 @@ export async function getComputeUnitPriceEstimate( feeEstimate = Math.round((resp as any).priorityFeeEstimate as number); } catch (e) { try { - const resp = await umi.rpc.call("getPriorityFeeEstimate", [ + const resp = await customRpcCall(umi, "getPriorityFeeEstimate", [ { - accountKeys: tx - .getInstructions() - .flatMap((x) => x.keys.flatMap((x) => x.pubkey.toString())), + accountKeys, options: { priorityLevel: prioritySetting.toString(), }, @@ -319,7 +450,7 @@ export async function getComputeUnitPriceEstimate( ]); feeEstimate = Math.round((resp as any).priorityFeeEstimate as number); } catch (e) { - console.error(e); + // console.error(e); } } @@ -332,29 +463,29 @@ async function spamSendTransactionUntilConfirmed( blockhash: BlockhashWithExpiryBlockHeight, spamInterval: number = 1500 ): Promise { - let transactionSignature: string | null = null; + let transactionSignature: string | undefined; const sendTx = async () => { try { const txSignature = await connection.sendRawTransaction( Buffer.from(transaction.serialize()), - { skipPreflight: true, maxRetries: 0 } + { skipPreflight: true, maxRetries: 3 } ); - transactionSignature = txSignature; + if (!transactionSignature) { + transactionSignature = txSignature; + } consoleLog(`Transaction sent`); - } catch (error) { - consoleLog("Error sending transaction:", error); - } + } catch (e) {} }; - await sendTx(); - const sendIntervalId = setInterval(async () => { await sendTx(); }, spamInterval); + await new Promise((resolve) => setTimeout(resolve, spamInterval * 4)); + if (!transactionSignature) { - throw new Error("Failed to send"); + throw new Error("No transaction signature found"); } const resp = await connection @@ -379,56 +510,62 @@ export async function sendSingleOptimizedTransaction( tx: TransactionBuilder, txType?: TransactionRunType, prioritySetting: PriorityFeeSetting = PriorityFeeSetting.Min, - onAwaitingSign?: () => void + onAwaitingSign?: () => void, + abortController?: AbortController ): Promise { consoleLog("Sending single optimized transaction..."); consoleLog("Instructions: ", tx.getInstructions().length); consoleLog("Serialized transaction size: ", tx.getTransactionSize(umi)); + consoleLog( + "Programs: ", + tx.getInstructions().map((x) => x.programId) + ); - let cuPrice: number | undefined; - if (prioritySetting !== PriorityFeeSetting.None) { - cuPrice = await getComputeUnitPriceEstimate( - umi, - tx, - prioritySetting, - ); - if (!cuPrice) { - cuPrice = 1000000; - } - cuPrice = Math.min(cuPrice, 100 * 1_000_000); - consoleLog("Compute unit price: ", cuPrice); - } + const accounts = tx + .getInstructions() + .flatMap((x) => [ + x.programId.toString(), + ...x.keys.map((y) => y.pubkey.toString()), + ]); + consoleLog("Unique account locks: ", Array.from(new Set(accounts)).length); - let computeUnitLimit = undefined; + const blockhash = await retryWithExponentialBackoff( + async () => await connection.getLatestBlockhash("confirmed") + ); + + if (abortController?.signal.aborted) { + return; + } + let cuLimit = undefined; if (txType !== "skip-simulation") { const simulationResult = await retryWithExponentialBackoff( async () => await simulateTransaction( umi, connection, - await assembleFinalTransaction( - umi.identity, - tx, - cuPrice, - 1_400_000 - ).setLatestBlockhash(umi) + assembleFinalTransaction(umi, tx, undefined, 1_400_000).setBlockhash( + blockhash + ) ), - 3 + 2 ); - simulationResult.value.err; - computeUnitLimit = Math.round(simulationResult.value.unitsConsumed! * 1.15); - consoleLog("Compute unit limit: ", computeUnitLimit); + cuLimit = Math.round(simulationResult.value.unitsConsumed! * 1.15); + consoleLog("Compute unit limit: ", cuLimit); } + let cuPrice: number | undefined; + if (usePriorityFee(prioritySetting)) { + cuPrice = await getComputeUnitPriceEstimate(umi, tx, prioritySetting); + cuPrice = Math.min(cuPrice ?? 0, 100_000_000); + consoleLog("Compute unit price: ", cuPrice); + } + + if (abortController?.signal.aborted) { + return; + } if (txType !== "only-simulate") { onAwaitingSign?.(); - const blockhash = await connection.getLatestBlockhash("confirmed"); - const signedTx = await assembleFinalTransaction( - umi.identity, - tx, - cuPrice, - computeUnitLimit - ) + const signedTx = await assembleFinalTransaction(umi, tx, cuPrice, cuLimit) .setBlockhash(blockhash) .buildAndSign(umi); const txSig = await spamSendTransactionUntilConfirmed( diff --git a/solauto-sdk/src/utils/solauto/index.ts b/solauto-sdk/src/utils/solauto/index.ts deleted file mode 100644 index 80df64d0..00000000 --- a/solauto-sdk/src/utils/solauto/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './generalUtils'; -export * from './rebalanceUtils'; \ No newline at end of file diff --git a/solauto-sdk/src/utils/solauto/rebalanceUtils.ts b/solauto-sdk/src/utils/solauto/rebalanceUtils.ts deleted file mode 100644 index 13cd66b7..00000000 --- a/solauto-sdk/src/utils/solauto/rebalanceUtils.ts +++ /dev/null @@ -1,360 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; -import { SolautoClient } from "../../clients/solautoClient"; -import { - DCASettings, - PositionState, - PositionTokenUsage, - RebalanceDirection, - SolautoSettingsParameters, - TokenType, -} from "../../generated"; -import { - eligibleForNextAutomationPeriod, - getAdjustedSettingsFromAutomation, - getUpdatedValueFromAutomation, -} from "./generalUtils"; -import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; -import { QuoteResponse } from "@jup-ag/api"; -import { JupSwapDetails } from "../jupiterUtils"; -import { currentUnixSeconds } from "../generalUtils"; -import { - fromBaseUnit, - fromBps, - getDebtAdjustmentUsd, - getLiqUtilzationRateBps, - getMaxLiqUtilizationRateBps, - getSolautoFeesBps, - maxRepayToBps, - toBaseUnit, -} from "../numberUtils"; -import { USD_DECIMALS } from "../../constants/generalAccounts"; -import { RebalanceAction } from "../../types"; -import { safeGetPrice } from "../priceUtils"; - -function getAdditionalAmountToDcaIn(dca: DCASettings): number { - if (dca.dcaInBaseUnit === BigInt(0)) { - return 0; - } - - const debtBalance = Number(dca.dcaInBaseUnit); - const updatedDebtBalance = getUpdatedValueFromAutomation( - debtBalance, - 0, - dca.automation, - currentUnixSeconds() - ); - - return debtBalance - updatedDebtBalance; -} - -function getStandardTargetLiqUtilizationRateBps( - state: PositionState, - settings: SolautoSettingsParameters -): number { - const adjustedSettings = getAdjustedSettingsFromAutomation( - settings, - currentUnixSeconds() - ); - - const repayFrom = settings.repayToBps + settings.repayGap; - const boostFrom = adjustedSettings.boostToBps - settings.boostGap; - - if (state.liqUtilizationRateBps <= boostFrom) { - return adjustedSettings.boostToBps; - } else if (state.liqUtilizationRateBps >= repayFrom) { - return adjustedSettings.repayToBps; - } else { - throw new Error("Invalid rebalance condition"); - } -} - -function targetLiqUtilizationRateBpsFromDCA( - state: PositionState, - settings: SolautoSettingsParameters, - dca: DCASettings, - currentUnixTime: number -) { - const adjustedSettings = getAdjustedSettingsFromAutomation( - settings, - currentUnixTime - ); - - let targetRateBps = 0; - if (dca.dcaInBaseUnit > BigInt(0)) { - targetRateBps = Math.max( - state.liqUtilizationRateBps, - adjustedSettings.boostToBps - ); - } else { - targetRateBps = adjustedSettings.boostToBps; - } - return targetRateBps; -} - -function isDcaRebalance( - state: PositionState, - settings: SolautoSettingsParameters, - dca: DCASettings | undefined, - currentUnixTime: number -): boolean { - if (dca === undefined || dca.automation.targetPeriods === 0) { - return false; - } - - const adjustedSettings = getAdjustedSettingsFromAutomation( - settings, - currentUnixTime - ); - - if ( - state.liqUtilizationRateBps > - adjustedSettings.repayToBps + adjustedSettings.repayGap - ) { - return false; - } - - if (!eligibleForNextAutomationPeriod(dca.automation, currentUnixTime)) { - return false; - } - - return true; -} - -function getTargetRateAndDcaAmount( - state: PositionState, - settings: SolautoSettingsParameters | undefined, - dca: DCASettings | undefined, - currentUnixTime: number, - targetLiqUtilizationRateBps?: number -): { targetRateBps: number; amountToDcaIn?: number } { - if (targetLiqUtilizationRateBps !== undefined) { - return { - targetRateBps: targetLiqUtilizationRateBps, - }; - } - - if (settings === undefined) { - throw new Error( - "If rebalancing a self-managed position, settings and DCA should be provided" - ); - } - - if (isDcaRebalance(state, settings, dca, currentUnixTime)) { - const targetLiqUtilizationRateBps = targetLiqUtilizationRateBpsFromDCA( - state, - settings, - dca!, - currentUnixTime - ); - const amountToDcaIn = getAdditionalAmountToDcaIn(dca!); - - return { - targetRateBps: targetLiqUtilizationRateBps, - amountToDcaIn, - }; - } else { - return { - targetRateBps: getStandardTargetLiqUtilizationRateBps(state, settings), - }; - } -} - -export interface RebalanceValues { - debtAdjustmentUsd: number; - repayingCloseToMaxLtv: boolean; - amountToDcaIn: number; - amountUsdToDcaIn: number; - dcaTokenType?: TokenType; - rebalanceAction: RebalanceAction; - rebalanceDirection: RebalanceDirection; - feesUsd: number; -} - -export function getRebalanceValues( - state: PositionState, - settings: SolautoSettingsParameters | undefined, - dca: DCASettings | undefined, - currentUnixTime: number, - supplyPrice: number, - debtPrice: number, - targetLiqUtilizationRateBps?: number -): RebalanceValues { - const { targetRateBps, amountToDcaIn } = getTargetRateAndDcaAmount( - state, - settings, - dca, - currentUnixTime, - targetLiqUtilizationRateBps - ); - - const amountUsdToDcaIn = - fromBaseUnit(BigInt(Math.round(amountToDcaIn ?? 0)), state.debt.decimals) * - (dca?.tokenType === TokenType.Debt ? debtPrice : supplyPrice); - - const rebalanceDirection = - amountUsdToDcaIn > 0 || state.liqUtilizationRateBps <= targetRateBps - ? RebalanceDirection.Boost - : RebalanceDirection.Repay; - const adjustmentFeeBps = getSolautoFeesBps( - false, - targetLiqUtilizationRateBps, - fromBaseUnit(state.netWorth.baseAmountUsdValue, USD_DECIMALS), - rebalanceDirection - ).total; - - const supplyUsd = - fromBaseUnit(state.supply.amountUsed.baseAmountUsdValue, USD_DECIMALS) + - amountUsdToDcaIn; - const debtUsd = fromBaseUnit( - state.debt.amountUsed.baseAmountUsdValue, - USD_DECIMALS - ); - let debtAdjustmentUsd = getDebtAdjustmentUsd( - state.liqThresholdBps, - supplyUsd, - debtUsd, - targetRateBps, - adjustmentFeeBps - ); - - const maxRepayTo = maxRepayToBps(state.maxLtvBps, state.liqThresholdBps); - return { - debtAdjustmentUsd, - repayingCloseToMaxLtv: - state.liqUtilizationRateBps > maxRepayTo && targetRateBps >= maxRepayTo, - amountToDcaIn: amountToDcaIn ?? 0, - amountUsdToDcaIn, - dcaTokenType: dca?.tokenType, - rebalanceAction: - (amountToDcaIn ?? 0) > 0 - ? "dca" - : rebalanceDirection === RebalanceDirection.Boost - ? "boost" - : "repay", - rebalanceDirection, - feesUsd: debtAdjustmentUsd * fromBps(adjustmentFeeBps), - }; -} - -export interface FlashLoanDetails { - baseUnitAmount: bigint; - mint: PublicKey; -} - -export function getFlashLoanDetails( - client: SolautoClient, - values: RebalanceValues, - jupQuote: QuoteResponse -): FlashLoanDetails | undefined { - let supplyUsd = - fromBaseUnit( - client.solautoPositionState!.supply.amountUsed.baseAmountUsdValue, - USD_DECIMALS - ) + - (values.dcaTokenType === TokenType.Supply ? values.amountUsdToDcaIn : 0); - let debtUsd = fromBaseUnit( - client.solautoPositionState!.debt.amountUsed.baseAmountUsdValue, - USD_DECIMALS - ); - - const debtAdjustmentUsdAbs = Math.abs(values.debtAdjustmentUsd); - supplyUsd = - values.rebalanceDirection === RebalanceDirection.Repay - ? supplyUsd - debtAdjustmentUsdAbs - : supplyUsd; - debtUsd = - values.rebalanceDirection === RebalanceDirection.Boost - ? debtUsd + debtAdjustmentUsdAbs - : debtUsd; - - const tempLiqUtilizationRateBps = getLiqUtilzationRateBps( - supplyUsd, - debtUsd, - client.solautoPositionState!.liqThresholdBps - ); - const requiresFlashLoan = - supplyUsd <= 0 || - tempLiqUtilizationRateBps > - getMaxLiqUtilizationRateBps( - client.solautoPositionState!.maxLtvBps, - client.solautoPositionState!.liqThresholdBps, - 0.015 - ); - - let flashLoanToken: PositionTokenUsage | undefined = undefined; - let flashLoanTokenPrice = 0; - if (values.rebalanceDirection === RebalanceDirection.Boost) { - flashLoanToken = client.solautoPositionState!.debt; - flashLoanTokenPrice = safeGetPrice(client.debtMint)!; - } else { - flashLoanToken = client.solautoPositionState!.supply; - flashLoanTokenPrice = safeGetPrice(client.supplyMint)!; - } - - const exactAmountBaseUnit = - jupQuote.swapMode === "ExactOut" || jupQuote.swapMode === "ExactIn" - ? BigInt(parseInt(jupQuote.inAmount)) - : undefined; - - return requiresFlashLoan - ? { - baseUnitAmount: exactAmountBaseUnit - ? exactAmountBaseUnit - : toBaseUnit( - debtAdjustmentUsdAbs / flashLoanTokenPrice, - flashLoanToken.decimals - ), - mint: toWeb3JsPublicKey(flashLoanToken.mint), - } - : undefined; -} - -export function getJupSwapRebalanceDetails( - client: SolautoClient, - values: RebalanceValues, - targetLiqUtilizationRateBps?: number, - attemptNum?: number -): JupSwapDetails { - const input = - values.rebalanceDirection === RebalanceDirection.Boost - ? client.solautoPositionState!.debt - : client.solautoPositionState!.supply; - const output = - values.rebalanceDirection === RebalanceDirection.Boost - ? client.solautoPositionState!.supply - : client.solautoPositionState!.debt; - - const usdToSwap = - Math.abs(values.debtAdjustmentUsd) + - (values.dcaTokenType === TokenType.Debt ? values.amountUsdToDcaIn : 0); - - const inputAmount = toBaseUnit( - usdToSwap / safeGetPrice(input.mint)!, - input.decimals - ); - const outputAmount = - targetLiqUtilizationRateBps === 0 - ? output.amountUsed.baseUnit + - BigInt( - Math.round( - Number(output.amountUsed.baseUnit) * - // Add this small percentage to account for the APR on the debt between now and the transaction - 0.0001 - ) - ) - : toBaseUnit(usdToSwap / safeGetPrice(output.mint)!, output.decimals); - - const exactOut = - targetLiqUtilizationRateBps === 0 || values.repayingCloseToMaxLtv; - const exactIn = !exactOut; - - return { - inputMint: toWeb3JsPublicKey(input.mint), - outputMint: toWeb3JsPublicKey(output.mint), - destinationWallet: client.solautoPosition, - slippageIncFactor: 0.5 + (attemptNum ?? 0) * 0.2, - amount: exactOut ? outputAmount : inputAmount, - exactIn: exactIn, - exactOut: exactOut, - }; -} diff --git a/solauto-sdk/src/utils/solauto/generalUtils.ts b/solauto-sdk/src/utils/solautoUtils.ts similarity index 53% rename from solauto-sdk/src/utils/solauto/generalUtils.ts rename to solauto-sdk/src/utils/solautoUtils.ts index 271eb697..651c5201 100644 --- a/solauto-sdk/src/utils/solauto/generalUtils.ts +++ b/solauto-sdk/src/utils/solautoUtils.ts @@ -1,11 +1,7 @@ -import { Connection, PublicKey } from "@solana/web3.js"; -import { - isOption, - isSome, - Program, - publicKey, - Umi, -} from "@metaplex-foundation/umi"; +import { PublicKey } from "@solana/web3.js"; +import { Program, publicKey, Umi } from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { QuoteResponse } from "@jup-ag/api"; import { AutomationSettings, DCASettings, @@ -13,6 +9,7 @@ import { LendingPlatform, PositionState, PositionType, + SolautoRebalanceType, SolautoSettingsParameters, SolautoSettingsParametersInpArgs, TokenType, @@ -21,32 +18,39 @@ import { getSolautoErrorFromName, getSolautoPositionAccountDataSerializer, getSolautoPositionSize, -} from "../../generated"; -import { consoleLog, currentUnixSeconds } from "../generalUtils"; +} from "../generated"; +import { SOLAUTO_PROD_PROGRAM } from "../constants"; +import { SolautoPositionDetails } from "../types"; +import { + SolautoClient, + SolautoMarginfiClient, + TxHandlerProps, +} from "../services"; import { + createSolautoSettings, + MarginfiSolautoPositionEx, + SolautoPositionEx, +} from "../solautoPosition"; +import { getReferralState } from "./accountUtils"; +import { + calcTotalDebt, + calcTotalSupply, fromBaseUnit, getLiqUtilzationRateBps, toBaseUnit, -} from "../numberUtils"; -import { getReferralState } from "../accountUtils"; -import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; -import { - ALL_SUPPORTED_TOKENS, - TOKEN_INFO, - USD_DECIMALS, -} from "../../constants"; + toRoundedUsdValue, +} from "./numberUtils"; +import { fetchTokenPrices } from "./priceUtils"; +import { validPubkey } from "./generalUtils"; import { findMarginfiAccounts, getAllMarginfiAccountsByAuthority, -} from "../marginfiUtils"; -import { RebalanceAction, SolautoPositionDetails } from "../../types/solauto"; -import { fetchTokenPrices } from "../priceUtils"; -import { getRebalanceValues } from "./rebalanceUtils"; +} from "./marginfi"; -export function createDynamicSolautoProgram(programId: PublicKey): Program { +export function createDynamicSolautoProgram(programId?: PublicKey): Program { return { name: "solauto", - publicKey: publicKey(programId), + publicKey: publicKey(programId ?? SOLAUTO_PROD_PROGRAM), getErrorFromCode(code: number, cause?: Error) { return getSolautoErrorFromCode(code, this, cause); }, @@ -107,117 +111,6 @@ export function getUpdatedValueFromAutomation( return newValue; } -export function getAdjustedSettingsFromAutomation( - settings: SolautoSettingsParameters, - currentUnixTime: number -): SolautoSettingsParameters { - const boostToBps = - settings.automation.targetPeriods > 0 && - eligibleForNextAutomationPeriod(settings.automation, currentUnixTime) - ? getUpdatedValueFromAutomation( - settings.boostToBps, - settings.targetBoostToBps, - settings.automation, - currentUnixTime - ) - : settings.boostToBps; - - return { - ...settings, - boostToBps, - }; -} - -export function eligibleForRebalance( - positionState: PositionState, - positionSettings: SolautoSettingsParameters | undefined, - positionDca: DCASettings | undefined, - currentUnixTime: number, - supplyMintPrice: number, - debtMintPrice: number, - bpsDistanceThreshold = 0 -): RebalanceAction | undefined { - if (!positionSettings) { - return undefined; - } - - if ( - positionDca && - positionDca.automation.targetPeriods > 0 && - eligibleForNextAutomationPeriod(positionDca.automation, currentUnixTime) - ) { - return "dca"; - } - - if (positionState.supply.amountUsed.baseUnit === BigInt(0)) { - return undefined; - } - - const boostToBps = - eligibleForRefresh(positionState, positionSettings, currentUnixTime) && - positionSettings.automation.targetPeriods > 0 - ? getUpdatedValueFromAutomation( - positionSettings.boostToBps, - positionSettings.targetBoostToBps, - positionSettings.automation, - currentUnixTime - ) - : positionSettings.boostToBps; - const repayFrom = positionSettings.repayToBps + positionSettings.repayGap; - const boostFrom = boostToBps - positionSettings.boostGap; - - if (positionState.liqUtilizationRateBps - boostFrom <= bpsDistanceThreshold) { - if (positionState.liqUtilizationRateBps < boostFrom) { - const values = getRebalanceValues( - positionState!, - positionSettings, - positionDca, - currentUnixSeconds(), - supplyMintPrice, - debtMintPrice - ); - const sufficientLiquidity = - fromBaseUnit( - positionState.debt.amountCanBeUsed.baseAmountUsdValue, - USD_DECIMALS - ) * - 0.95 > - values.debtAdjustmentUsd; - if (!sufficientLiquidity) { - consoleLog("Insufficient debt liquidity to further boost"); - } - return sufficientLiquidity ? "boost" : undefined; - } - - return "boost"; - } else if ( - repayFrom - positionState.liqUtilizationRateBps <= - bpsDistanceThreshold - ) { - return "repay"; - } - - return undefined; -} - -export function eligibleForRefresh( - positionState: PositionState, - positionSettings: SolautoSettingsParameters, - currentUnixTime: number -): boolean { - if (positionSettings.automation.targetPeriods > 0) { - return eligibleForNextAutomationPeriod( - positionSettings.automation, - currentUnixTime - ); - } else { - return ( - currentUnixSeconds() - Number(positionState.lastUpdated) > - 60 * 60 * 24 * 7 - ); - } -} - export async function getSolautoManagedPositions( umi: Umi, authority?: PublicKey, @@ -267,7 +160,7 @@ export async function getSolautoManagedPositions( ? [ { memcmp: { - bytes: new Uint8Array(positionTypeFilter), + bytes: new Uint8Array([positionTypeFilter]), offset: 3, }, }, @@ -289,14 +182,13 @@ export async function getSolautoManagedPositions( if (position.position.lendingPlatform === LendingPlatform.Marginfi) { tokens = [ findMarginfiAccounts( - toWeb3JsPublicKey(position.position.protocolSupplyAccount) - ).mint, - findMarginfiAccounts( - toWeb3JsPublicKey(position.position.protocolDebtAccount) + toWeb3JsPublicKey(position.position.lpSupplyAccount) ).mint, + findMarginfiAccounts(toWeb3JsPublicKey(position.position.lpDebtAccount)) + .mint, ]; } - // TODO: PF + // TODO: LP return { publicKey: toWeb3JsPublicKey(x.publicKey), @@ -304,7 +196,7 @@ export async function getSolautoManagedPositions( positionId: position.positionId[0], lendingPlatform: position.position.lendingPlatform, positionType: position.positionType, - protocolAccount: toWeb3JsPublicKey(position.position.protocolUserAccount), + lpUserAccount: toWeb3JsPublicKey(position.position.lpUserAccount), supplyMint: tokens![0], debtMint: tokens![1], }; @@ -397,10 +289,7 @@ export async function getAllPositionsByAuthority( true ); marginfiPositions = marginfiPositions.filter( - (x) => - x.supplyMint && - (x.debtMint!.equals(PublicKey.default) || - ALL_SUPPORTED_TOKENS.includes(x.debtMint!.toString())) + (x) => validPubkey(x.supplyMint) && validPubkey(x.debtMint) ); return marginfiPositions.map((x) => ({ publicKey: x.marginfiAccount, @@ -408,7 +297,7 @@ export async function getAllPositionsByAuthority( positionId: 0, positionType: PositionType.Leverage, lendingPlatform: LendingPlatform.Marginfi, - protocolAccount: x.marginfiAccount, + lpUserAccount: x.marginfiAccount, supplyMint: x.supplyMint, debtMint: x.debtMint, })); @@ -430,12 +319,8 @@ export async function positionStateWithLatestPrices( ]); } - const supplyUsd = - fromBaseUnit(state.supply.amountUsed.baseUnit, state.supply.decimals) * - supplyPrice; - const debtUsd = - fromBaseUnit(state.debt.amountUsed.baseUnit, state.debt.decimals) * - debtPrice; + const supplyUsd = calcTotalSupply(state) * supplyPrice; + const debtUsd = calcTotalDebt(state) * debtPrice; return { ...state, liqUtilizationRateBps: getLiqUtilzationRateBps( @@ -445,161 +330,65 @@ export async function positionStateWithLatestPrices( ), netWorth: { baseUnit: toBaseUnit( - (supplyUsd - debtUsd) / supplyPrice, + supplyUsd > 0 ? (supplyUsd - debtUsd) / supplyPrice : 0, state.supply.decimals ), - baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS), + baseAmountUsdValue: toRoundedUsdValue(supplyUsd - debtUsd), }, supply: { ...state.supply, + amountCanBeUsed: { + ...state.supply.amountCanBeUsed, + baseAmountUsdValue: toRoundedUsdValue( + fromBaseUnit( + state.supply.amountCanBeUsed.baseUnit, + state.supply.decimals + ) * supplyPrice + ), + }, amountUsed: { ...state.supply.amountUsed, - baseAmountUsdValue: toBaseUnit(supplyUsd, USD_DECIMALS), + baseAmountUsdValue: toRoundedUsdValue(supplyUsd), }, }, debt: { ...state.debt, - amountUsed: { - ...state.debt.amountUsed, - baseAmountUsdValue: toBaseUnit(debtUsd, USD_DECIMALS), - }, - }, - }; -} - -interface AssetProps { - mint: PublicKey; - price?: number; - amountUsed?: number; - amountCanBeUsed?: number; -} - -export function createFakePositionState( - supply: AssetProps, - debt: AssetProps, - maxLtvBps: number, - liqThresholdBps: number -): PositionState { - const supplyDecimals = TOKEN_INFO[supply.mint.toString()].decimals; - const debtDecimals = TOKEN_INFO[debt.mint.toString()].decimals; - - const supplyUsd = (supply.amountUsed ?? 0) * (supply.price ?? 0); - const debtUsd = (debt.amountUsed ?? 0) * (debt.price ?? 0); - - return { - liqUtilizationRateBps: getLiqUtilzationRateBps( - supplyUsd, - debtUsd, - liqThresholdBps - ), - supply: { - amountUsed: { - baseUnit: toBaseUnit(supply.amountUsed ?? 0, supplyDecimals), - baseAmountUsdValue: toBaseUnit(supplyUsd, USD_DECIMALS), - }, amountCanBeUsed: { - baseUnit: toBaseUnit(supply.amountCanBeUsed ?? 0, supplyDecimals), - baseAmountUsdValue: toBaseUnit( - (supply.amountCanBeUsed ?? 0) * (supply.price ?? 0), - USD_DECIMALS + ...state.debt.amountCanBeUsed, + baseAmountUsdValue: toRoundedUsdValue( + fromBaseUnit( + state.debt.amountCanBeUsed.baseUnit, + state.debt.decimals + ) * debtPrice ), }, - baseAmountMarketPriceUsd: toBaseUnit(supply.price ?? 0, USD_DECIMALS), - borrowFeeBps: 0, - decimals: supplyDecimals, - flashLoanFeeBps: 0, - mint: publicKey(supply.mint), - padding1: [], - padding2: [], - padding: new Uint8Array([]), - }, - debt: { amountUsed: { - baseUnit: toBaseUnit(debt.amountUsed ?? 0, debtDecimals), - baseAmountUsdValue: toBaseUnit(debtUsd, USD_DECIMALS), - }, - amountCanBeUsed: { - baseUnit: toBaseUnit(debt.amountCanBeUsed ?? 0, debtDecimals), - baseAmountUsdValue: toBaseUnit( - (debt.amountCanBeUsed ?? 0) * (debt.price ?? 0), - USD_DECIMALS - ), + ...state.debt.amountUsed, + baseAmountUsdValue: toRoundedUsdValue(debtUsd), }, - baseAmountMarketPriceUsd: toBaseUnit(debt.price ?? 0, USD_DECIMALS), - borrowFeeBps: 0, - decimals: debtDecimals, - flashLoanFeeBps: 0, - mint: publicKey(debt.mint), - padding1: [], - padding2: [], - padding: new Uint8Array([]), - }, - netWorth: { - baseUnit: supply.price - ? toBaseUnit((supplyUsd - debtUsd) / supply.price, supplyDecimals) - : BigInt(0), - baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS), }, - maxLtvBps, - liqThresholdBps, - lastUpdated: BigInt(currentUnixSeconds()), - padding1: [], - padding2: [], - padding: [], }; } -export function createSolautoSettings( - settings: SolautoSettingsParametersInpArgs -): SolautoSettingsParameters { - return { - automation: - isOption(settings.automation) && isSome(settings.automation) - ? { - ...settings.automation.value, - intervalSeconds: BigInt(settings.automation.value.intervalSeconds), - unixStartDate: BigInt(settings.automation.value.unixStartDate), - padding: new Uint8Array([]), - padding1: [], - } - : { - targetPeriods: 0, - periodsPassed: 0, - intervalSeconds: BigInt(0), - unixStartDate: BigInt(0), - padding: new Uint8Array([]), - padding1: [], - }, - targetBoostToBps: - isOption(settings.targetBoostToBps) && isSome(settings.targetBoostToBps) - ? settings.targetBoostToBps.value - : 0, - boostGap: settings.boostGap, - boostToBps: settings.boostToBps, - repayGap: settings.repayGap, - repayToBps: settings.repayToBps, - padding: new Uint8Array([]), - padding1: [], - }; -} - -type PositionAdjustment = +type ContextAdjustment = | { type: "supply"; value: bigint } | { type: "debt"; value: bigint } | { type: "settings"; value: SolautoSettingsParametersInpArgs } | { type: "dca"; value: DCASettingsInpArgs } | { type: "dcaInBalance"; value: { amount: bigint; tokenType: TokenType } } - | { type: "cancellingDca"; value: TokenType }; + | { type: "cancellingDca"; value: TokenType } + | { type: "jupSwap"; value: QuoteResponse }; -export class LivePositionUpdates { +export class ContextUpdates { public supplyAdjustment = BigInt(0); public debtAdjustment = BigInt(0); public settings: SolautoSettingsParameters | undefined = undefined; - public activeDca: DCASettings | undefined = undefined; + public dca: DCASettings | undefined = undefined; public dcaInBalance?: { amount: bigint; tokenType: TokenType } = undefined; public cancellingDca: TokenType | undefined = undefined; + public jupSwap: QuoteResponse | undefined = undefined; - new(update: PositionAdjustment) { + new(update: ContextAdjustment) { if (update.type === "supply") { this.supplyAdjustment += update.value; } else if (update.type === "debt") { @@ -609,22 +398,24 @@ export class LivePositionUpdates { this.settings = createSolautoSettings(settings); } else if (update.type === "dca") { const dca = update.value; - this.activeDca = { + this.dca = { automation: { ...dca.automation, intervalSeconds: BigInt(dca.automation.intervalSeconds), unixStartDate: BigInt(dca.automation.unixStartDate), - padding: new Uint8Array([]), - padding1: [], + padding: new Uint8Array(32), + padding1: new Array(4).fill(0), }, dcaInBaseUnit: BigInt(dca.dcaInBaseUnit), tokenType: dca.tokenType, - padding: [], + padding: new Array(31).fill(0), }; } else if (update.type === "cancellingDca") { this.cancellingDca = update.value; } else if (update.type === "dcaInBalance") { this.dcaInBalance = update.value; + } else if (update.type === "jupSwap") { + this.jupSwap = update.value; } } @@ -632,12 +423,12 @@ export class LivePositionUpdates { this.supplyAdjustment = BigInt(0); this.debtAdjustment = BigInt(0); this.settings = undefined; - this.activeDca = undefined; + this.dca = undefined; this.dcaInBalance = undefined; this.cancellingDca = undefined; } - hasUpdates(): boolean { + positionUpdates(): boolean { return ( this.supplyAdjustment !== BigInt(0) || this.debtAdjustment !== BigInt(0) || @@ -647,3 +438,45 @@ export class LivePositionUpdates { ); } } + +export function getClient( + lendingPlatform: LendingPlatform, + txHandlerProps: TxHandlerProps +) { + if (lendingPlatform === LendingPlatform.Marginfi) { + return new SolautoMarginfiClient(txHandlerProps); + } else { + throw new Error("Lending platform not yet supported"); + // TODO: LP + } +} + +// TODO: LP +export function isMarginfiClient( + client: SolautoClient +): client is SolautoMarginfiClient { + return client.lendingPlatform === LendingPlatform.Marginfi; +} + +// TODO: LP +export function isMarginfiPosition( + pos: SolautoPositionEx +): pos is MarginfiSolautoPositionEx { + return pos.lendingPlatform === LendingPlatform.Marginfi; +} + +export function hasFirstRebalance(rebalanceType: SolautoRebalanceType) { + return [ + SolautoRebalanceType.Regular, + SolautoRebalanceType.DoubleRebalanceWithFL, + SolautoRebalanceType.FLRebalanceThenSwap, + ].includes(rebalanceType); +} + +export function hasLastRebalance(rebalanceType: SolautoRebalanceType) { + return [ + SolautoRebalanceType.Regular, + SolautoRebalanceType.DoubleRebalanceWithFL, + SolautoRebalanceType.FLSwapThenRebalance, + ].includes(rebalanceType); +} diff --git a/solauto-sdk/src/utils/stringUtils.ts b/solauto-sdk/src/utils/stringUtils.ts new file mode 100644 index 00000000..baf85233 --- /dev/null +++ b/solauto-sdk/src/utils/stringUtils.ts @@ -0,0 +1,72 @@ +import { PublicKey } from "@solana/web3.js"; +import { NATIVE_MINT } from "@solana/spl-token"; +import { MAJORS_PRIO } from "../constants"; +import { tokenInfo } from "./generalUtils"; + +export const StrategyTypes = ["Long", "Short", "Ratio"] as const; +export type StrategyType = (typeof StrategyTypes)[number]; + +export function adjustedTicker(mint?: PublicKey) { + const info = tokenInfo(mint); + + if (info.ticker.toLowerCase().includes("btc")) { + return "BTC"; + } else if (info.ticker.toLowerCase() === "weth") { + return "ETH"; + } else { + return info.ticker; + } +} + +export function ratioMintDetails(supplyMint?: PublicKey, debtMint?: PublicKey) { + if ( + (tokenInfo(supplyMint).isLST && debtMint?.equals(NATIVE_MINT)) || + (supplyMint && + debtMint && + MAJORS_PRIO[supplyMint?.toString() ?? ""] > + MAJORS_PRIO[debtMint?.toString() ?? ""]) + ) { + return { order: [supplyMint, debtMint], strategyName: "Long" }; + } else { + return { order: [debtMint, supplyMint], strategyName: "Short" }; + } +} + +export function ratioName(supplyMint?: PublicKey, debtMint?: PublicKey) { + const { order, strategyName } = ratioMintDetails(supplyMint, debtMint); + return `${adjustedTicker(order[0])}/${adjustedTicker(order[1])} ${strategyName}`; +} + +export function solautoStrategyName( + supplyMint?: PublicKey, + debtMint?: PublicKey +) { + const strat = strategyType( + supplyMint ?? PublicKey.default, + debtMint ?? PublicKey.default + ); + + if (strat === "Long") { + return `${adjustedTicker(supplyMint)} Long`; + } else if (strat === "Ratio") { + return ratioName(supplyMint, debtMint); + } else { + return `${adjustedTicker(debtMint)} Short`; + } +} + +export function strategyType( + supplyMint: PublicKey, + debtMint: PublicKey +): StrategyType { + const supplyInfo = tokenInfo(supplyMint); + const debtInfo = tokenInfo(debtMint); + + if (!supplyInfo.isStableCoin && !debtInfo.isStableCoin) { + return "Ratio"; + } else if (debtInfo.isStableCoin) { + return "Long"; + } else { + return "Short"; + } +} diff --git a/solauto-sdk/src/utils/switchboardUtils.ts b/solauto-sdk/src/utils/switchboardUtils.ts index 3cca6c85..270f74ae 100644 --- a/solauto-sdk/src/utils/switchboardUtils.ts +++ b/solauto-sdk/src/utils/switchboardUtils.ts @@ -1,22 +1,30 @@ -import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor"; -import switchboardIdl from "../idls/switchboard.json"; import { Connection, PublicKey, Transaction, VersionedTransaction, } from "@solana/web3.js"; -import { CrossbarClient, PullFeed } from "@switchboard-xyz/on-demand"; -import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants"; -import { TransactionItemInputs } from "../types"; import { Signer, transactionBuilder } from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; +import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor"; +import * as OnDemand from "@switchboard-xyz/on-demand"; +import Big from "big.js"; import { - fromWeb3JsInstruction, - toWeb3JsPublicKey, -} from "@metaplex-foundation/umi-web3js-adapters"; -import { retryWithExponentialBackoff, zip } from "./generalUtils"; + PRICES, + SWITCHBOARD_PRICE_FEED_IDS, + UPDATE_ORACLE_TX_NAME, +} from "../constants"; +import { TransactionItemInputs } from "../types"; +import { + consoleLog, + currentUnixSeconds, + retryWithExponentialBackoff, +} from "./generalUtils"; +import { getWrappedInstruction } from "./solanaUtils"; +import { CrossbarClient } from "@switchboard-xyz/common"; +import { SolautoClient, TransactionItem } from "../services"; -export function getPullFeed( +export async function getPullFeed( conn: Connection, mint: PublicKey, wallet?: PublicKey @@ -35,12 +43,24 @@ export function getPullFeed( dummyWallet, AnchorProvider.defaultOptions() ); - const program = new Program(switchboardIdl as Idl, provider); - return new PullFeed( - program, - new PublicKey(SWITCHBOARD_PRICE_FEED_IDS[mint.toString()]) - ); + consoleLog("Pulling SWB program..."); + const { PullFeed, Queue, ON_DEMAND_MAINNET_PID } = OnDemand; + const sbProgram = await Program.at(ON_DEMAND_MAINNET_PID, provider); + + const crossbar = new CrossbarClient("https://integrator-crossbar.mrgn.app/"); + const queue = await Queue.loadDefault(sbProgram); + const gateway = await queue.fetchGatewayFromCrossbar(crossbar as any); + + consoleLog("Pulled SWB program!"); + consoleLog("Feed id:", SWITCHBOARD_PRICE_FEED_IDS[mint.toString()].feedId); + return { + gateway, + feed: new PullFeed( + sbProgram, + new PublicKey(SWITCHBOARD_PRICE_FEED_IDS[mint.toString()].feedId) + ), + }; } export async function buildSwbSubmitResponseTx( @@ -48,26 +68,74 @@ export async function buildSwbSubmitResponseTx( signer: Signer, mint: PublicKey ): Promise { - const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz"); - const feed = getPullFeed(conn, mint, toWeb3JsPublicKey(signer.publicKey)); - const [pullIx, responses] = await retryWithExponentialBackoff( - async () => - await feed.fetchUpdateIx({ - crossbarClient: crossbar, - }), - 8, + const { feed, gateway } = await getPullFeed( + conn, + mint, + toWeb3JsPublicKey(signer.publicKey) + ); + + consoleLog("Fetching crank IX..."); + const [pullIxs, responses] = await retryWithExponentialBackoff( + async () => { + const res = await feed.fetchUpdateIx({ + gateway: gateway.endpoint(), + chain: "solana", + network: "mainnet-beta", + }); + if (!res[1] || !res[1][0].value) { + throw new Error("Unable to fetch Switchboard pull IX"); + } + return res; + }, + 3, 200 ); + if (!pullIxs || !pullIxs.length) { + throw new Error("Unable to fetch SWB crank IX"); + } + + consoleLog("SWB pullIxs count:", pullIxs.length); + pullIxs.forEach((ix, i) => { + consoleLog(`SWB pullIx[${i}] programId: ${ix.programId.toString()}`); + consoleLog(`SWB pullIx[${i}] data length: ${ix.data.length}`); + consoleLog(`SWB pullIx[${i}] keys count: ${ix.keys.length}`); + if (ix.data.length > 0) { + consoleLog( + `SWB pullIx[${i}] data (first 128 bytes hex): ${Buffer.from(ix.data.slice(0, 128)).toString("hex")}` + ); + } + }); + consoleLog("SWB oracle responses:", responses.length); + consoleLog( + "SWB oracle response details:", + responses.map((r) => ({ + value: r.value?.toString(), + error: r.error, + oracle: r.oracle.pubkey.toString(), + })) + ); + + consoleLog("Setting price locally..."); + const price = (responses[0].value as Big).toNumber(); + consoleLog(price); + PRICES[mint.toString()] = { + realtimePrice: price, + confInterval: 0, + emaPrice: price, + emaConfInterval: 0, + time: currentUnixSeconds(), + }; + + consoleLog("Returning SWB transaction data..."); return { - tx: transactionBuilder().add({ - bytesCreatedOnChain: 0, - instruction: fromWeb3JsInstruction(pullIx!), - signers: [signer], - }), + tx: transactionBuilder( + pullIxs.map((x) => getWrappedInstruction(signer, x)) + ), lookupTableAddresses: responses .filter((x) => Boolean(x.oracle.lut?.key)) .map((x) => x.oracle.lut!.key.toString()), + orderPrio: -2, }; } @@ -86,7 +154,7 @@ export async function getSwitchboardFeedData( const results = await Promise.all( mints.map(async (mint) => { - const feed = getPullFeed(conn, mint); + const { feed } = await getPullFeed(conn, mint); const result = await feed.loadData(); const price = Number(result.result.value) / Math.pow(10, 18); const stale = @@ -97,4 +165,46 @@ export async function getSwitchboardFeedData( ); return results; -} \ No newline at end of file +} + +export function isSwitchboardMint(mint: PublicKey | string) { + return Object.keys(SWITCHBOARD_PRICE_FEED_IDS).includes(mint.toString()); +} + +export async function addSwbOraclePullTxs( + client: SolautoClient, + txs: TransactionItem[] +) { + const switchboardMints = [ + ...(isSwitchboardMint(client.pos.supplyMint) + ? [client.pos.supplyMint] + : []), + ...(isSwitchboardMint(client.pos.debtMint) ? [client.pos.debtMint] : []), + ]; + + if (txs.find((x) => x.oracleInteractor) && switchboardMints.length) { + consoleLog("Checking if oracle update(s) needed..."); + const staleOracles = + ( + await getSwitchboardFeedData(client.connection, switchboardMints) + ).filter((x) => x.stale).length > 0; + + if (staleOracles) { + consoleLog("Requires oracle update(s)..."); + const oracleTxs = switchboardMints.map( + (x) => + new TransactionItem( + async () => + await buildSwbSubmitResponseTx( + client.connection, + client.signer, + x + ), + UPDATE_ORACLE_TX_NAME + ) + ); + consoleLog("Set crank IXs in TX"); + txs.unshift(...oracleTxs); + } + } +} diff --git a/solauto-sdk/tests/transactions/shared.ts b/solauto-sdk/tests/transactions/shared.ts new file mode 100644 index 00000000..7310e18c --- /dev/null +++ b/solauto-sdk/tests/transactions/shared.ts @@ -0,0 +1,92 @@ +import { PublicKey } from "@solana/web3.js"; +import { NATIVE_MINT } from "@solana/spl-token"; +import { Signer } from "@metaplex-foundation/umi"; +import { + consoleLog, + fetchTokenPrices, + getClient, + LendingPlatform, + LOCAL_IRONFORGE_API_URL, + maxBoostToBps, + maxRepayToBps, + SOLAUTO_PROD_PROGRAM, + SOLAUTO_TEST_PROGRAM, + SolautoSettingsParametersInpArgs, + toBaseUnit, + USDC, + deposit, + openSolautoPosition, + borrow, + rebalance, + withdraw, + closeSolautoPosition, + getMarginfiAccounts, + ClientTransactionsManager, + ProgramEnv, +} from "../../src"; + +export async function e2eTransactionTest( + signer: Signer, + testProgram: boolean, + lendingPlatform: LendingPlatform, + withFlashLoan: boolean, + showLogs?: boolean, + lpEnv?: ProgramEnv +) { + const client = getClient(lendingPlatform, { + signer, + showLogs, + rpcUrl: LOCAL_IRONFORGE_API_URL, + programId: testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM, + lpEnv, + }); + + const supplyMint = new PublicKey(NATIVE_MINT); + const debtMint = new PublicKey(USDC); + + await client.initializeNewSolautoPosition({ + positionId: 1, + lpPoolAccount: getMarginfiAccounts(lpEnv).defaultGroup, + supplyMint, + debtMint, + }); + + const [maxLtvBps, liqThresholdBps] = + await client.pos.maxLtvAndLiqThresholdBps(); + const settings: SolautoSettingsParametersInpArgs = { + boostToBps: maxBoostToBps(maxLtvBps, liqThresholdBps), + boostGap: 50, + repayToBps: maxRepayToBps(maxLtvBps, liqThresholdBps), + repayGap: 50, + }; + + const supplyUsd = 100; + const debtUsd = withFlashLoan ? 60 : 3; + const [supplyPrice, debtPrice] = await fetchTokenPrices([ + supplyMint, + debtMint, + ]); + + const transactionItems = [ + openSolautoPosition(client, settings), + deposit( + client, + toBaseUnit(supplyUsd / supplyPrice, client.pos.supplyMintInfo.decimals) + ), + borrow( + client, + toBaseUnit(debtUsd / debtPrice, client.pos.debtMintInfo.decimals) + ), + rebalance(client, 0), + withdraw(client, "All"), + closeSolautoPosition(client), + ]; + + const txManager = new ClientTransactionsManager({ + txHandler: client, + txRunType: "only-simulate", + }); + const statuses = await txManager.send(transactionItems); + + consoleLog(statuses); +} diff --git a/solauto-sdk/tests/transactions/solautoMarginfi.ts b/solauto-sdk/tests/transactions/solautoMarginfi.ts index ee5f2062..b8b02e3a 100644 --- a/solauto-sdk/tests/transactions/solautoMarginfi.ts +++ b/solauto-sdk/tests/transactions/solautoMarginfi.ts @@ -1,168 +1,33 @@ import { describe, it } from "mocha"; -import { none, publicKey, some } from "@metaplex-foundation/umi"; +import { LendingPlatform, ProgramEnv } from "../../src"; import { setupTest } from "../shared"; -import { SolautoMarginfiClient } from "../../src/clients/solautoMarginfiClient"; -import { - safeFetchSolautoPosition, - solautoAction, - SolautoSettingsParametersInpArgs, -} from "../../src/generated"; -import { buildSolautoRebalanceTransaction } from "../../src/transactions/transactionUtils"; -import { - getLiqUtilzationRateBps, - maxBoostToBps, - maxRepayFromBps, - maxRepayToBps, - toBaseUnit, -} from "../../src/utils/numberUtils"; -import { NATIVE_MINT } from "@solana/spl-token"; -import { - TransactionItem, - TransactionsManager, -} from "../../src/transactions/transactionsManager"; -import { PublicKey } from "@solana/web3.js"; -import { - SOLAUTO_PROD_PROGRAM, - SOLAUTO_TEST_PROGRAM, - USDC, -} from "../../src/constants"; -import { buildHeliusApiUrl } from "../../src/utils"; -import { PriorityFeeSetting } from "../../src/types"; +import { e2eTransactionTest } from "./shared"; describe("Solauto Marginfi tests", async () => { const signer = setupTest(); - // const signer = setupTest("solauto-manager"); - - const payForTransactions = false; - const testProgram = true; - const positionId = 1; + const testProgram = false; + const showLogs = false; + const lpEnv: ProgramEnv = "Prod"; it("open - deposit - borrow - rebalance to 0 - withdraw - close", async () => { - const client = new SolautoMarginfiClient( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!), - true, - testProgram ? SOLAUTO_TEST_PROGRAM : SOLAUTO_PROD_PROGRAM + await e2eTransactionTest( + signer, + testProgram, + LendingPlatform.Marginfi, + false, + showLogs, + lpEnv ); + }); - const supply = NATIVE_MINT; - const supplyDecimals = 6; - const debtDecimals = 6; - - await client.initialize({ + it("open - deposit - borrow - fl rebalance to 0 - withdraw - close", async () => { + await e2eTransactionTest( signer, - positionId, - authority: new PublicKey("rC5dMP5dmSsfQ66rynzfFzuc122Eex9h1RJHVDkeH6D"), - // new: true, - // marginfiAccount: new PublicKey( - // "4nNvUXF5YqHFcH2nGweSiuvy1ct7V5FXfoCLKFYUN36z" - // ), - // marginfiGroup: new PublicKey("G1rt3EpQ43K3bY457rhukQGRAo2QxydFAGRKqnjKzyr5"), - // supplyMint: new PublicKey("3B5wuUrMEi5yATD7on46hKfej3pfmd7t1RKgrsN3pump"), - // debtMint: new PublicKey(USDC), - }); - - const transactionItems: TransactionItem[] = []; - // const settingParams: SolautoSettingsParametersInpArgs = { - // boostToBps: maxBoostToBps( - // client.solautoPositionState?.maxLtvBps ?? 0, - // client.solautoPositionState?.liqThresholdBps ?? 0 - // ), - // boostGap: 50, - // repayToBps: maxRepayToBps( - // client.solautoPositionState?.maxLtvBps ?? 0, - // client.solautoPositionState?.liqThresholdBps ?? 0 - // ), - // repayGap: 50, - // automation: none(), - // targetBoostToBps: none(), - // }; - - // if (client.solautoPositionData === null) { - // transactionItems.push( - // new TransactionItem(async () => { - // return { - // tx: client.openPosition(settingParams), - // }; - // }, "open position") - // ); - - // const initialSupplyUsd = 150; - // transactionItems.push( - // new TransactionItem(async () => { - // // const [supplyPrice] = await fetchTokenPrices([supply]); - // return { - // tx: client.protocolInteraction( - // solautoAction("Deposit", [toBaseUnit(300, supplyDecimals)]) - // ), - // }; - // }, "deposit") - // ); - // } - - // const maxLtvBps = client.solautoPositionState!.maxLtvBps; - // const liqThresholdBps = client.solautoPositionState!.liqThresholdBps; - // const maxRepayFrom = maxRepayFromBps(maxLtvBps, liqThresholdBps); - // const maxRepayTo = maxRepayToBps(maxLtvBps, liqThresholdBps); - // const maxBoostTo = maxBoostToBps(maxLtvBps, liqThresholdBps); - // transactionItems.push( - // new TransactionItem( - // async () => ({ - // tx: client.updatePositionIx({ - // positionId: client.positionId, - // settingParams: some({ - // ...settingParams, - // }), - // dca: null, - // }), - // }), - // "update position" - // ) - // ); - - transactionItems.push( - new TransactionItem( - async (attemptNum) => - await buildSolautoRebalanceTransaction(client, undefined, attemptNum), - "rebalance" - ) + testProgram, + LendingPlatform.Marginfi, + true, + showLogs, + lpEnv ); - - // transactionItems.push( - // new TransactionItem( - // async (attemptNum) => - // await buildSolautoRebalanceTransaction(client, settingParams.boostToBps, attemptNum), - // "rebalance" - // ) - // ); - - // transactionItems.push( - // new TransactionItem( - // async () => ({ - // tx: client.protocolInteraction( - // solautoAction("Withdraw", [{ __kind: "All" }]) - // ), - // }), - // "withdraw" - // ) - // ); - - // transactionItems.push( - // new TransactionItem( - // async () => ({ - // tx: client.closePositionIx(), - // }), - // "close position" - // ) - // ); - - const statuses = await new TransactionsManager( - client, - undefined, - !payForTransactions ? "only-simulate" : "normal", - PriorityFeeSetting.Low, - true - ).clientSend(transactionItems); - - console.log(statuses); }); }); diff --git a/solauto-sdk/tests/unit/accounts.ts b/solauto-sdk/tests/unit/accounts.ts index 4bad2151..0eac09c7 100644 --- a/solauto-sdk/tests/unit/accounts.ts +++ b/solauto-sdk/tests/unit/accounts.ts @@ -1,31 +1,22 @@ import { describe, it } from "mocha"; +import { assert } from "chai"; +import { PublicKey } from "@solana/web3.js"; +import { publicKey } from "@metaplex-foundation/umi"; +import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; import { ALL_SUPPORTED_TOKENS, TOKEN_INFO, -} from "../../src/constants/tokenConstants"; -import { - buildHeliusApiUrl, - getSolanaRpcConnection, -} from "../../src/utils/solanaUtils"; -import { publicKey } from "@metaplex-foundation/umi"; -import { assert } from "chai"; -import { - getAllMarginfiAccountsByAuthority, - getTokenAccount, -} from "../../src/utils"; -import { - MARGINFI_ACCOUNTS, SOLAUTO_FEES_WALLET, SOLAUTO_MANAGER, -} from "../../src/constants"; -import { PublicKey } from "@solana/web3.js"; -import { safeFetchAllMarginfiAccount } from "../../src/marginfi-sdk"; -import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; + LOCAL_IRONFORGE_API_URL, + getMarginfiAccounts, + getSolanaRpcConnection, + getEmptyMarginfiAccountsByAuthority, + getTokenAccount, +} from "../../src"; async function hasTokenAccounts(wallet: PublicKey) { - let [_, umi] = getSolanaRpcConnection( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!) - ); + let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); const tokenAccounts = await umi.rpc.getAccounts( ALL_SUPPORTED_TOKENS.map((x) => @@ -33,7 +24,6 @@ async function hasTokenAccounts(wallet: PublicKey) { ) ); for (let i = 0; i < tokenAccounts.length; i++) { - console.log(tokenAccounts[i].publicKey.toString()); if (!tokenAccounts[i].exists) { console.log( `Missing ${wallet.toString()} TA for `, @@ -50,28 +40,23 @@ describe("Assert Solauto fee token accounts are created", async () => { }); it("ISM accounts for every supported Marginfi group", async () => { - let [_, umi] = getSolanaRpcConnection( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!) - ); + let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); - const ismAccounts = await getAllMarginfiAccountsByAuthority( - umi, - SOLAUTO_MANAGER, - undefined, - false - ); - const ismData = await safeFetchAllMarginfiAccount( + const ismAccounts = await getEmptyMarginfiAccountsByAuthority( umi, - ismAccounts.map((x) => publicKey(x.marginfiAccount)) - ); - const supportedMarginfiGroups = Object.keys(MARGINFI_ACCOUNTS).map( - (x) => new PublicKey(x) + SOLAUTO_MANAGER ); + const supportedMarginfiGroups = Object.keys( + getMarginfiAccounts("Prod").bankAccounts + ).map((x) => new PublicKey(x)); const missingIsmAccounts = supportedMarginfiGroups.filter( - (group) => !ismData.find((x) => group.equals(toWeb3JsPublicKey(x.group))) + (group) => + !ismAccounts.find((x) => group.equals(toWeb3JsPublicKey(x.group))) ); - console.log("Missing ISM accounts", missingIsmAccounts); + if (missingIsmAccounts.length > 0) { + console.log("Missing ISM accounts", missingIsmAccounts); + } assert(missingIsmAccounts.length === 0); }); }); diff --git a/solauto-sdk/tests/unit/lookupTables.ts b/solauto-sdk/tests/unit/lookupTables.ts index 2db90657..4a835d35 100644 --- a/solauto-sdk/tests/unit/lookupTables.ts +++ b/solauto-sdk/tests/unit/lookupTables.ts @@ -1,43 +1,43 @@ import { describe, it } from "mocha"; -import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js"; import { - MARGINFI_ACCOUNTS, - MARGINFI_ACCOUNTS_LOOKUP_TABLE, -} from "../../src/constants/marginfiAccounts"; + getMarginfiAccounts, + LOCAL_IRONFORGE_API_URL, + SOLAUTO_MANAGER, + getAllBankRelatedAccounts, + getEmptyMarginfiAccountsByAuthority, + getSolanaRpcConnection, + ProgramEnv, +} from "../../src"; -const conn = new Connection(clusterApiUrl("mainnet-beta"), "confirmed"); +const [conn, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL); + +async function checkLookupTableAccounts(programEnv: ProgramEnv) { + const data = getMarginfiAccounts(programEnv); + const lookupTable = await conn.getAddressLookupTable(data.lookupTable); + if (lookupTable === null) { + throw new Error("Lookup table not found"); + } + + const ismAccounts = ( + await getEmptyMarginfiAccountsByAuthority(umi, SOLAUTO_MANAGER) + ).map((x) => x.publicKey.toString()); + + const bankAccounts = ( + await getAllBankRelatedAccounts(umi, data.bankAccounts) + ).map((x) => x.toString()); + + const accountsRequired = [...ismAccounts, ...bankAccounts]; + + const existingAccounts = + lookupTable.value?.state.addresses.map((x) => x.toString()) ?? []; + + if (accountsRequired.find((x) => !existingAccounts.includes(x.toString()))) { + throw new Error("Marginfi accounts lookup table missing an account"); + } +} describe("Assert lookup tables up-to-date", async () => { it("marginfi accounts LUT should have everything", async () => { - const lookupTable = await conn.getAddressLookupTable( - new PublicKey(MARGINFI_ACCOUNTS_LOOKUP_TABLE) - ); - if (lookupTable === null) { - throw new Error("Lookup table not found"); - } - - const existingAccounts = - lookupTable.value?.state.addresses.map((x) => x.toString()) ?? []; - - for (const group in MARGINFI_ACCOUNTS) { - for (const key in MARGINFI_ACCOUNTS[group]) { - if (key === PublicKey.default.toString()) { - continue; - } - - const accounts = MARGINFI_ACCOUNTS[group][key]; - const addresses = [ - group, - accounts.bank, - accounts.liquidityVault, - accounts.vaultAuthority, - accounts.priceOracle, - ]; - - if (addresses.find((x) => !existingAccounts.includes(x.toString()))) { - throw new Error("Marginfi accounts lookup table missing an account"); - } - } - } + await checkLookupTableAccounts("Prod"); }); }); diff --git a/solauto-sdk/tests/unit/rebalanceCalculations.ts b/solauto-sdk/tests/unit/rebalanceCalculations.ts index 28a5bb3e..55cc712d 100644 --- a/solauto-sdk/tests/unit/rebalanceCalculations.ts +++ b/solauto-sdk/tests/unit/rebalanceCalculations.ts @@ -1,102 +1,51 @@ import { describe, it, before } from "mocha"; +import { assert } from "chai"; import { PublicKey } from "@solana/web3.js"; import { NATIVE_MINT } from "@solana/spl-token"; -import { assert } from "chai"; -import { SolautoMarginfiClient } from "../../src/clients/solautoMarginfiClient"; -import { setupTest } from "../shared"; -import { getRebalanceValues } from "../../src/utils/solauto/rebalanceUtils"; import { publicKey } from "@metaplex-foundation/umi"; -import { SolautoClient } from "../../src/clients/solautoClient"; -import { - DCASettings, - LendingPlatform, - PositionType, - RebalanceDirection, - SolautoRebalanceType, - SolautoSettingsParameters, - TokenType, -} from "../../src/generated"; +import { setupTest } from "../shared"; import { - fromBaseUnit, + LOCAL_IRONFORGE_API_URL, + USDC, fromBps, getLiqUtilzationRateBps, - getSolautoFeesBps, - toBaseUnit, -} from "../../src/utils/numberUtils"; -import { USD_DECIMALS } from "../../src/constants/generalAccounts"; -import { - createFakePositionState, - eligibleForNextAutomationPeriod, - getAdjustedSettingsFromAutomation, - getUpdatedValueFromAutomation, - positionStateWithLatestPrices, -} from "../../src/utils/solauto/generalUtils"; -import { currentUnixSeconds } from "../../src/utils/generalUtils"; -import { USDC } from "../../src/constants/tokenConstants"; -import { - buildHeliusApiUrl, + getClient, fetchTokenPrices, - getSolanaRpcConnection, safeGetPrice, -} from "../../src/utils"; + LendingPlatform, + PriceType, + SolautoSettingsParameters, + getRebalanceValues, + SolautoClient, + SolautoFeesBps, + createFakePositionState, + MarginfiSolautoPositionEx, + getMarginfiAccounts, +} from "../../src"; const signer = setupTest(undefined, true); -const [conn, _] = getSolanaRpcConnection( - buildHeliusApiUrl(process.env.HELIUS_API_URL!) -); function assertAccurateRebalance( client: SolautoClient, expectedLiqUtilizationRateBps: number, - targetLiqUtilizationRateBps?: number, - expectedUsdToDcaIn?: number + targetLiqUtilizationRateBps?: number ) { - const { rebalanceDirection, debtAdjustmentUsd, amountUsdToDcaIn } = - getRebalanceValues( - client.solautoPositionState!, - client.solautoPositionSettings(), - client.solautoPositionActiveDca(), - currentUnixSeconds(), - safeGetPrice(client.supplyMint)!, - safeGetPrice(client.debtMint)!, - targetLiqUtilizationRateBps - ); - - let adjustmentFeeBps = 0; - adjustmentFeeBps = getSolautoFeesBps( - client.referredBy !== undefined, + const { endResult } = getRebalanceValues( + client.pos, + PriceType.Realtime, targetLiqUtilizationRateBps, - fromBaseUnit( - client.solautoPositionState?.netWorth.baseAmountUsdValue ?? BigInt(0), - USD_DECIMALS + SolautoFeesBps.create( + false, + targetLiqUtilizationRateBps, + client.pos.netWorthUsd() ), - rebalanceDirection - ).total; - - assert( - Math.round(amountUsdToDcaIn) === Math.round(expectedUsdToDcaIn ?? 0), - `Expected DCA-in amount does not match ${Math.round( - amountUsdToDcaIn - )}, ${Math.round(expectedUsdToDcaIn ?? 0)}` + 50 ); - const newSupply = - fromBaseUnit( - client.solautoPositionState!.supply.amountUsed.baseAmountUsdValue, - USD_DECIMALS - ) + - (debtAdjustmentUsd - debtAdjustmentUsd * fromBps(adjustmentFeeBps)) + - amountUsdToDcaIn; - const newDebt = - fromBaseUnit( - client.solautoPositionState!.debt.amountUsed.baseAmountUsdValue, - USD_DECIMALS - ) + debtAdjustmentUsd; - const newLiqUtilizationRateBps = getLiqUtilzationRateBps( - newSupply, - newDebt, - client.solautoPositionState!.liqThresholdBps + endResult.supplyUsd, + endResult.debtUsd, + client.pos.state.liqThresholdBps ); assert( Math.round(newLiqUtilizationRateBps) === @@ -109,16 +58,15 @@ async function getFakePosition( supplyPrice: number, debtPrice: number, fakeLiqUtilizationRateBps: number, - settings: SolautoSettingsParameters, - dca?: DCASettings + settings: SolautoSettingsParameters ): Promise { - const client = new SolautoMarginfiClient( - buildHeliusApiUrl(process.env.HELIUS_API_KEY!), - true - ); + const client = getClient(LendingPlatform.Marginfi, { + signer, + rpcUrl: LOCAL_IRONFORGE_API_URL, + }); await client.initialize({ positionId: 1, - signer, + lpPoolAccount: getMarginfiAccounts().defaultGroup, supplyMint: new PublicKey(NATIVE_MINT), debtMint: new PublicKey(USDC), }); @@ -126,81 +74,46 @@ async function getFakePosition( const supplyUsd = 1000; const maxLtvBps = 6400; const liqThresholdBps = 8181; - client.solautoPositionState = await positionStateWithLatestPrices( - createFakePositionState( - { - amountUsed: supplyUsd / supplyPrice, - price: safeGetPrice(NATIVE_MINT)!, - mint: NATIVE_MINT, - }, - { - amountUsed: - (supplyUsd * - fromBps(liqThresholdBps) * - fromBps(fakeLiqUtilizationRateBps)) / - debtPrice, - price: 1, - mint: new PublicKey(USDC), - }, - maxLtvBps, - liqThresholdBps - ) - ); - client.solautoPositionData = { - positionId: [1], - bump: [0], - selfManaged: { - val: false, + const fakeState = createFakePositionState( + { + amountUsed: supplyUsd / supplyPrice, + price: safeGetPrice(NATIVE_MINT)!, + mint: NATIVE_MINT, }, - authority: client.signer.publicKey, - position: { - dca: dca ?? { - automation: { - targetPeriods: 0, - periodsPassed: 0, - unixStartDate: BigInt(0), - intervalSeconds: BigInt(0), - padding1: [], - padding: new Uint8Array([]), - }, - dcaInBaseUnit: BigInt(0), - tokenType: TokenType.Debt, - padding: [], - }, - lendingPlatform: LendingPlatform.Marginfi, - protocolSupplyAccount: publicKey(PublicKey.default), - protocolDebtAccount: publicKey(PublicKey.default), - protocolUserAccount: publicKey(PublicKey.default), - settingParams: settings, - padding1: [], - padding: [], - }, - state: client.solautoPositionState!, - rebalance: { - rebalanceType: SolautoRebalanceType.Regular, - rebalanceDirection: RebalanceDirection.Boost, - flashLoanAmount: BigInt(0), - padding1: [], - padding2: [], - padding: new Uint8Array([]), + { + amountUsed: + (supplyUsd * + fromBps(liqThresholdBps) * + fromBps(fakeLiqUtilizationRateBps)) / + debtPrice, + price: 1, + mint: new PublicKey(USDC), }, - positionType: PositionType.Leverage, - padding1: [], - padding: [], - publicKey: publicKey(PublicKey.default), - header: { - executable: false, - lamports: { - basisPoints: BigInt(0), - decimals: 9, - identifier: "SOL", + maxLtvBps, + liqThresholdBps + ); + + const defaultPk = publicKey(PublicKey.default); + client.pos = new MarginfiSolautoPositionEx({ + umi: client.umi, + publicKey: PublicKey.default, + data: { + state: fakeState, + positionId: [1], + authority: defaultPk, + position: { + lendingPlatform: LendingPlatform.Marginfi, + lpUserAccount: defaultPk, + lpSupplyAccount: defaultPk, + lpDebtAccount: defaultPk, + lpPoolAccount: defaultPk, + settings, + padding: [], + padding1: [], }, - owner: publicKey(PublicKey.default), }, - }; - - client.solautoPositionState!.lastUpdated = BigInt(currentUnixSeconds()); + }); return client; } @@ -218,71 +131,13 @@ async function rebalanceFromFakePosition( settings ); - const adjustedSettings = getAdjustedSettingsFromAutomation( - settings, - currentUnixSeconds() - ); const expectedLiqUtilizationRateBps = - fakeLiqUtilizationRateBps < - adjustedSettings.boostToBps - adjustedSettings.boostGap - ? adjustedSettings.boostToBps - : adjustedSettings.repayToBps; + fakeLiqUtilizationRateBps < settings.boostToBps - settings.boostGap + ? settings.boostToBps + : settings.repayToBps; assertAccurateRebalance(client, expectedLiqUtilizationRateBps); } -async function dcaRebalanceFromFakePosition( - supplyPrice: number, - debtPrice: number, - fakeLiqUtilizationRateBps: number, - settings: SolautoSettingsParameters, - dca: DCASettings -) { - const client = await getFakePosition( - supplyPrice, - debtPrice, - fakeLiqUtilizationRateBps, - settings, - dca - ); - - const adjustedSettings = getAdjustedSettingsFromAutomation( - settings, - currentUnixSeconds() - ); - const expectedLiqUtilizationRateBps = - dca.dcaInBaseUnit > BigInt(0) - ? Math.max(fakeLiqUtilizationRateBps, adjustedSettings.boostToBps) - : adjustedSettings.boostToBps; - - const expectedDcaInAmount = - dca.dcaInBaseUnit > BigInt(0) && - eligibleForNextAutomationPeriod(dca.automation, currentUnixSeconds()) - ? dca.dcaInBaseUnit - - BigInt( - Math.round( - getUpdatedValueFromAutomation( - Number(dca.dcaInBaseUnit), - 0, - dca.automation, - currentUnixSeconds() - ) - ) - ) - : BigInt(0); - const expectedUsdToDcaIn = - fromBaseUnit( - BigInt(Math.round(Number(expectedDcaInAmount))), - client.solautoPositionState!.debt.decimals - ) * debtPrice; - - assertAccurateRebalance( - client, - expectedLiqUtilizationRateBps, - undefined, - expectedUsdToDcaIn - ); -} - describe("Rebalance tests", async () => { let supplyPrice: number, debtPrice: number; @@ -299,17 +154,7 @@ describe("Rebalance tests", async () => { boostGap: 100, repayToBps: 7000, repayGap: 250, - automation: { - targetPeriods: 0, - periodsPassed: 0, - unixStartDate: BigInt(0), - intervalSeconds: BigInt(0), - padding1: [], - padding: new Uint8Array([]), - }, - targetBoostToBps: 0, - padding1: [], - padding: new Uint8Array([]), + padding: [], }); assertAccurateRebalance(client, 5000, 5000); @@ -318,145 +163,14 @@ describe("Rebalance tests", async () => { it("Standard boost or repay", async () => { const settings: SolautoSettingsParameters = { - automation: { - targetPeriods: 0, - periodsPassed: 0, - unixStartDate: BigInt(0), - intervalSeconds: BigInt(0), - padding1: [], - padding: new Uint8Array([]), - }, - targetBoostToBps: 0, boostGap: 1000, boostToBps: 4000, repayGap: 1000, repayToBps: 7500, - padding1: [], - padding: new Uint8Array([]), + padding: [], }; await rebalanceFromFakePosition(supplyPrice, debtPrice, 1000, settings); await rebalanceFromFakePosition(supplyPrice, debtPrice, 9000, settings); }); - - it("Rebalance with settings automation", async () => { - const settings: SolautoSettingsParameters = { - automation: { - targetPeriods: 2, - periodsPassed: 1, - intervalSeconds: BigInt(5), - unixStartDate: BigInt(currentUnixSeconds() - 5), - padding1: [], - padding: new Uint8Array([]), - }, - targetBoostToBps: 5000, - boostGap: 1000, - boostToBps: 4000, - repayGap: 1000, - repayToBps: 7500, - padding1: [], - padding: new Uint8Array([]), - }; - await rebalanceFromFakePosition(supplyPrice, debtPrice, 3500, settings); - - settings.automation.targetPeriods = 5; - await rebalanceFromFakePosition(supplyPrice, debtPrice, 3100, settings); - - settings.automation.periodsPassed = 0; - settings.automation.unixStartDate = BigInt(currentUnixSeconds()); - await rebalanceFromFakePosition(supplyPrice, debtPrice, 3100, settings); - }); - - it("Rebalance DCA out", async () => { - const settings: SolautoSettingsParameters = { - automation: { - targetPeriods: 4, - periodsPassed: 0, - intervalSeconds: BigInt(5), - unixStartDate: BigInt(currentUnixSeconds()), - padding1: [], - padding: new Uint8Array([]), - }, - targetBoostToBps: 0, - boostGap: 1000, - boostToBps: 4000, - repayGap: 1000, - repayToBps: 7500, - padding1: [], - padding: new Uint8Array([]), - }; - const dca: DCASettings = { - automation: { - targetPeriods: 4, - periodsPassed: 0, - intervalSeconds: BigInt(5), - unixStartDate: BigInt(currentUnixSeconds()), - padding1: [], - padding: new Uint8Array([]), - }, - dcaInBaseUnit: BigInt(0), - tokenType: TokenType.Debt, - padding: [], - }; - await dcaRebalanceFromFakePosition( - supplyPrice, - debtPrice, - 3500, - settings, - dca - ); - - settings.boostToBps = 1500; - settings.automation.periodsPassed = 3; - settings.automation.unixStartDate = BigInt(currentUnixSeconds() - 3 * 5); - dca.automation.periodsPassed = 3; - dca.automation.unixStartDate = BigInt(currentUnixSeconds() - 3 * 5); - await dcaRebalanceFromFakePosition( - supplyPrice, - debtPrice, - 3500, - settings, - dca - ); - }); - - it("Rebalance DCA in", async () => { - const settings: SolautoSettingsParameters = { - automation: { - targetPeriods: 3, - periodsPassed: 0, - intervalSeconds: BigInt(5), - unixStartDate: BigInt(currentUnixSeconds()), - padding1: [], - padding: new Uint8Array([]), - }, - targetBoostToBps: 5000, - boostGap: 1000, - boostToBps: 4000, - repayGap: 1000, - repayToBps: 7500, - padding1: [], - padding: new Uint8Array([]), - }; - const dca: DCASettings = { - automation: { - targetPeriods: 4, - periodsPassed: 0, - intervalSeconds: BigInt(5), - unixStartDate: BigInt(currentUnixSeconds()), - padding1: [], - padding: new Uint8Array([]), - }, - dcaInBaseUnit: toBaseUnit(debtPrice * 300, 6), - tokenType: TokenType.Debt, - padding: [], - }; - await dcaRebalanceFromFakePosition( - supplyPrice, - debtPrice, - 3500, - settings, - dca - ); - }); });