707 lines
20 KiB
TypeScript
707 lines
20 KiB
TypeScript
import {
|
|
LISTING_PRESETS,
|
|
LISTING_PRESETS_KEY,
|
|
tierSwitchboardSettings,
|
|
tierToSwitchboardJobSwapValue,
|
|
} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools';
|
|
import {
|
|
Cluster,
|
|
Commitment,
|
|
Connection,
|
|
Keypair,
|
|
PublicKey,
|
|
} from '@solana/web3.js';
|
|
import fs from 'fs';
|
|
import * as toml from '@iarna/toml';
|
|
import { option, publicKey, struct, u64, u8 } from '@raydium-io/raydium-sdk';
|
|
import { decodeString } from '@switchboard-xyz/common';
|
|
import {
|
|
asV0Tx,
|
|
CrossbarClient,
|
|
OracleJob,
|
|
PullFeed,
|
|
Queue,
|
|
SB_ON_DEMAND_PID,
|
|
} from '@switchboard-xyz/on-demand';
|
|
import {
|
|
Program as Anchor30Program,
|
|
AnchorProvider,
|
|
Wallet,
|
|
} from 'switchboard-anchor';
|
|
import { sendSignAndConfirmTransactions } from '@blockworks-foundation/mangolana/lib/transactions';
|
|
import { SequenceType } from '@blockworks-foundation/mangolana/lib/globalTypes';
|
|
import { createComputeBudgetIx } from '../src/utils/rpc';
|
|
|
|
// Configuration
|
|
const TIER: LISTING_PRESETS_KEY = 'asset_10';
|
|
const TOKEN_MINT = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac';
|
|
|
|
// Tier based variables
|
|
const swapValue = tierToSwitchboardJobSwapValue[TIER];
|
|
const settingFromLib = tierSwitchboardSettings[TIER];
|
|
const maxVariance = LISTING_PRESETS[TIER].oracleConfFilter * 100;
|
|
const minResponses = settingFromLib!.minRequiredOracleResults;
|
|
const numSignatures = settingFromLib!.minRequiredOracleResults + 1;
|
|
const minSampleSize = settingFromLib!.minRequiredOracleResults;
|
|
const maxStaleness =
|
|
LISTING_PRESETS[TIER].maxStalenessSlots === -1
|
|
? 10000
|
|
: LISTING_PRESETS[TIER].maxStalenessSlots;
|
|
|
|
// Constants
|
|
const JUPITER_PRICE_API_MAINNET = 'https://price.jup.ag/v4/';
|
|
const JUPITER_TOKEN_API_MAINNET = 'https://token.jup.ag/all';
|
|
const WRAPPED_SOL_MINT = 'So11111111111111111111111111111111111111112';
|
|
const PYTH_SOL_ORACLE =
|
|
'ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d';
|
|
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
const PYTH_USDC_ORACLE =
|
|
'eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a';
|
|
const SWITCHBOARD_USDC_ORACLE = 'FwYfsmj5x8YZXtQBNo2Cz8TE7WRCMFqA6UTffK4xQKMH';
|
|
const CLUSTER: Cluster =
|
|
(process.env.CLUSTER_OVERRIDE as Cluster) || 'mainnet-beta';
|
|
const CLUSTER_URL =
|
|
process.env.CLUSTER_URL_OVERRIDE || process.env.MB_CLUSTER_URL;
|
|
|
|
const USER_KEYPAIR =
|
|
process.env.USER_KEYPAIR_OVERRIDE || process.env.MB_PAYER_KEYPAIR;
|
|
|
|
async function setupAnchor() {
|
|
const options = AnchorProvider.defaultOptions();
|
|
const connection = new Connection(CLUSTER_URL!, options);
|
|
const user = Keypair.fromSecretKey(
|
|
Buffer.from(
|
|
JSON.parse(
|
|
fs.readFileSync(USER_KEYPAIR!, {
|
|
encoding: 'utf-8',
|
|
}),
|
|
),
|
|
),
|
|
);
|
|
//@ts-ignore
|
|
const userWallet = new Wallet(user);
|
|
//@ts-ignore
|
|
const userProvider = new AnchorProvider(connection, userWallet, options);
|
|
|
|
return { userProvider, connection, user };
|
|
}
|
|
|
|
async function getTokenPrice(mint: string): Promise<number> {
|
|
const priceInfo = await (
|
|
await fetch(`${JUPITER_PRICE_API_MAINNET}price?ids=${mint}`)
|
|
).json();
|
|
//Note: if listing asset that don't have price on jupiter remember to edit this 0 to real price
|
|
//in case of using 0 openbook market can be wrongly configured ignore if openbook market is existing
|
|
const price = priceInfo.data[mint]?.price || 0;
|
|
if (!price) {
|
|
console.log('Token price not found');
|
|
throw 'Token price not found';
|
|
}
|
|
return price;
|
|
}
|
|
|
|
async function getTokenInfo(mint: string): Promise<Token | undefined> {
|
|
const response = await fetch(JUPITER_TOKEN_API_MAINNET);
|
|
const data: Token[] = await response.json();
|
|
const tokenInfo = data.find((x) => x.address === mint);
|
|
if (!tokenInfo) {
|
|
console.log('Token info not found');
|
|
throw 'Token info not found';
|
|
}
|
|
return data.find((x) => x.address === mint);
|
|
}
|
|
|
|
async function getPool(mint: string): Promise<
|
|
| {
|
|
pool: string;
|
|
poolSource: 'raydium' | 'orca';
|
|
isSolPool: boolean;
|
|
isReveredSolPool: boolean;
|
|
}
|
|
| undefined
|
|
> {
|
|
const dex = await fetch(
|
|
`https://api.dexscreener.com/latest/dex/search?q=${mint}`,
|
|
);
|
|
const resp = await dex.json();
|
|
|
|
if (!resp?.pairs?.length) {
|
|
return;
|
|
}
|
|
|
|
const pairs = resp.pairs.filter(
|
|
(x) => x.dexId.includes('raydium') || x.dexId.includes('orca'),
|
|
);
|
|
|
|
const bestUsdcPool = pairs.find(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(x: any) => x.quoteToken.address === USDC_MINT,
|
|
);
|
|
|
|
const bestSolPool = pairs.find(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(x: any) => x.quoteToken.address === WRAPPED_SOL_MINT,
|
|
);
|
|
|
|
const bestReversedSolPool = pairs.find(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(x: any) => x.baseToken.address === WRAPPED_SOL_MINT,
|
|
);
|
|
|
|
if (bestUsdcPool) {
|
|
return {
|
|
pool: bestUsdcPool.pairAddress,
|
|
poolSource: bestUsdcPool.dexId.includes('raydium') ? 'raydium' : 'orca',
|
|
isSolPool: false,
|
|
isReveredSolPool: false,
|
|
};
|
|
}
|
|
|
|
if (bestSolPool) {
|
|
return {
|
|
pool: bestSolPool.pairAddress,
|
|
poolSource: bestSolPool.dexId.includes('raydium') ? 'raydium' : 'orca',
|
|
isSolPool: true,
|
|
isReveredSolPool: false,
|
|
};
|
|
}
|
|
|
|
if (bestSolPool) {
|
|
return {
|
|
pool: bestReversedSolPool.pairAddress,
|
|
poolSource: bestReversedSolPool.dexId.includes('raydium')
|
|
? 'raydium'
|
|
: 'orca',
|
|
isSolPool: true,
|
|
isReveredSolPool: true,
|
|
};
|
|
}
|
|
|
|
console.log('No orca or raydium pool found');
|
|
throw 'No orca or raydium pool found';
|
|
}
|
|
|
|
const getLstStakePool = async (
|
|
connection: Connection,
|
|
mint: string,
|
|
): Promise<string> => {
|
|
try {
|
|
let poolAddress = '';
|
|
let addresses: string[] = [];
|
|
try {
|
|
const tomlFile = await fetch(
|
|
`https://raw.githubusercontent.com/${'igneous-labs'}/${'sanctum-lst-list'}/master/sanctum-lst-list.toml`,
|
|
);
|
|
|
|
const tomlText = await tomlFile.text();
|
|
const tomlData = toml.parse(tomlText) as unknown as {
|
|
sanctum_lst_list: { pool: { pool: string } }[];
|
|
};
|
|
addresses = [
|
|
...tomlData.sanctum_lst_list
|
|
.map((x) => tryGetPubKey(x.pool.pool)?.toBase58())
|
|
.filter((x) => x),
|
|
] as string[];
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
|
|
//remove duplicates
|
|
const possibleStakePoolsAddresses = [...new Set(addresses)].map(
|
|
(x) => new PublicKey(x),
|
|
);
|
|
|
|
const accounts = await connection.getMultipleAccountsInfo(
|
|
possibleStakePoolsAddresses,
|
|
);
|
|
for (const idx in accounts) {
|
|
try {
|
|
const acc = accounts[idx];
|
|
const stakeAddressPk = possibleStakePoolsAddresses[idx];
|
|
if (acc?.data) {
|
|
const decoded = StakePoolLayout.decode(acc?.data);
|
|
if (decoded.poolMint.toBase58() === mint && stakeAddressPk) {
|
|
poolAddress = stakeAddressPk?.toBase58();
|
|
break;
|
|
}
|
|
}
|
|
// eslint-disable-next-line no-empty
|
|
} catch (e) {}
|
|
}
|
|
|
|
return poolAddress;
|
|
} catch (e) {
|
|
console.log(e);
|
|
return '';
|
|
}
|
|
};
|
|
|
|
const LSTExactIn = (inMint: string, uiAmountIn: string): string => {
|
|
const template = `tasks:
|
|
- conditionalTask:
|
|
attempt:
|
|
- sanctumLstPriceTask:
|
|
lstMint: ${inMint}
|
|
- conditionalTask:
|
|
attempt:
|
|
- valueTask:
|
|
big: ${uiAmountIn}
|
|
- divideTask:
|
|
job:
|
|
tasks:
|
|
- jupiterSwapTask:
|
|
inTokenAddress: So11111111111111111111111111111111111111112
|
|
outTokenAddress: ${inMint}
|
|
baseAmountString: ${uiAmountIn}
|
|
- conditionalTask:
|
|
attempt:
|
|
- multiplyTask:
|
|
job:
|
|
tasks:
|
|
- oracleTask:
|
|
pythAddress: ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d
|
|
pythAllowedConfidenceInterval: 10
|
|
onFailure:
|
|
- multiplyTask:
|
|
job:
|
|
tasks:
|
|
- oracleTask:
|
|
switchboardAddress: AEcJSgRBkU9WnKCBELj66TPFfzhKWBWa4tL7JugnonUa`;
|
|
return template;
|
|
};
|
|
|
|
const LSTExactOut = (inMint: string, uiOutSolAmount: string): string => {
|
|
const template = `tasks:
|
|
- conditionalTask:
|
|
attempt:
|
|
- sanctumLstPriceTask:
|
|
lstMint: ${inMint}
|
|
- conditionalTask:
|
|
attempt:
|
|
- cacheTask:
|
|
cacheItems:
|
|
- variableName: QTY
|
|
job:
|
|
tasks:
|
|
- jupiterSwapTask:
|
|
inTokenAddress: So11111111111111111111111111111111111111112
|
|
outTokenAddress: ${inMint}
|
|
baseAmountString: ${uiOutSolAmount}
|
|
- jupiterSwapTask:
|
|
inTokenAddress: ${inMint}
|
|
outTokenAddress: So11111111111111111111111111111111111111112
|
|
baseAmountString: \${QTY}
|
|
- divideTask:
|
|
big: \${QTY}
|
|
- conditionalTask:
|
|
attempt:
|
|
- multiplyTask:
|
|
job:
|
|
tasks:
|
|
- oracleTask:
|
|
pythAddress: ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d
|
|
pythAllowedConfidenceInterval: 10
|
|
onFailure:
|
|
- multiplyTask:
|
|
job:
|
|
tasks:
|
|
- oracleTask:
|
|
switchboardAddress: AEcJSgRBkU9WnKCBELj66TPFfzhKWBWa4tL7JugnonUa`;
|
|
return template;
|
|
};
|
|
|
|
async function setupSwitchboard(userProvider: AnchorProvider) {
|
|
const idl = await Anchor30Program.fetchIdl(SB_ON_DEMAND_PID, userProvider);
|
|
const sbOnDemandProgram = new Anchor30Program(idl!, userProvider);
|
|
let queue = new PublicKey('A43DyUGA7s8eXPxqEjJY6EBu1KKbNgfxF8h17VAHn13w');
|
|
if (CLUSTER == 'devnet') {
|
|
queue = new PublicKey('FfD96yeXs4cxZshoPPSKhSPgVQxLAJUT3gefgh84m1Di');
|
|
}
|
|
const crossbarClient = new CrossbarClient(
|
|
'https://crossbar.switchboard.xyz',
|
|
true,
|
|
);
|
|
return { sbOnDemandProgram, crossbarClient, queue };
|
|
}
|
|
|
|
(async function main(): Promise<void> {
|
|
const { userProvider, connection, user } = await setupAnchor();
|
|
const [
|
|
{ sbOnDemandProgram, crossbarClient, queue },
|
|
poolInfo,
|
|
price,
|
|
tokeninfo,
|
|
lstPool,
|
|
] = await Promise.all([
|
|
setupSwitchboard(userProvider),
|
|
getPool(TOKEN_MINT),
|
|
getTokenPrice(TOKEN_MINT),
|
|
getTokenInfo(TOKEN_MINT),
|
|
getLstStakePool(connection, TOKEN_MINT),
|
|
]);
|
|
|
|
const FALLBACK_POOL_NAME: 'orcaPoolAddress' | 'raydiumPoolAddress' = `${
|
|
poolInfo?.poolSource || 'raydium'
|
|
}PoolAddress`;
|
|
const FALLBACK_POOL = poolInfo?.pool;
|
|
const TOKEN_SYMBOL = tokeninfo!.symbol.toUpperCase();
|
|
|
|
const queueAccount = new Queue(sbOnDemandProgram, queue);
|
|
try {
|
|
await queueAccount.loadData();
|
|
} catch (err) {
|
|
console.error('Queue not found, ensure you are using devnet in your env');
|
|
return;
|
|
}
|
|
|
|
let onFailureTaskDesc: { [key: string]: any }[];
|
|
if (!poolInfo?.isReveredSolPool) {
|
|
onFailureTaskDesc = [
|
|
{
|
|
lpExchangeRateTask: {
|
|
[FALLBACK_POOL_NAME]: FALLBACK_POOL,
|
|
},
|
|
},
|
|
];
|
|
if (poolInfo?.isSolPool) {
|
|
onFailureTaskDesc.push({
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
pythAddress: PYTH_SOL_ORACLE,
|
|
pythAllowedConfidenceInterval: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
}
|
|
} else {
|
|
onFailureTaskDesc = [
|
|
{
|
|
valueTask: {
|
|
big: 1,
|
|
},
|
|
},
|
|
{
|
|
divideTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
lpExchangeRateTask: {
|
|
[FALLBACK_POOL_NAME]: FALLBACK_POOL,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
];
|
|
if (poolInfo.isSolPool) {
|
|
onFailureTaskDesc.push({
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
pythAddress: PYTH_SOL_ORACLE,
|
|
pythAllowedConfidenceInterval: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
const txOpts = {
|
|
commitment: 'finalized' as Commitment,
|
|
skipPreflight: true,
|
|
maxRetries: 0,
|
|
};
|
|
|
|
const conf = {
|
|
name: `${TOKEN_SYMBOL}/USD`, // the feed name (max 32 bytes)
|
|
queue, // the queue of oracles to bind to
|
|
maxVariance: maxVariance!, // allow 1% variance between submissions and jobs
|
|
minResponses: minResponses!, // minimum number of responses of jobs to allow
|
|
numSignatures: numSignatures!, // number of signatures to fetch per update
|
|
minSampleSize: minSampleSize!, // minimum number of responses to sample
|
|
maxStaleness: maxStaleness!, // maximum staleness of responses in seconds to sample
|
|
};
|
|
|
|
console.log('Initializing new data feed');
|
|
// Generate the feed keypair
|
|
const [pullFeed, feedKp] = PullFeed.generate(sbOnDemandProgram);
|
|
const jobs = [
|
|
lstPool
|
|
? OracleJob.fromYaml(
|
|
LSTExactIn(
|
|
TOKEN_MINT,
|
|
Math.ceil(Number(swapValue) / price).toString(),
|
|
),
|
|
)
|
|
: OracleJob.fromObject({
|
|
tasks: [
|
|
{
|
|
conditionalTask: {
|
|
attempt: [
|
|
{
|
|
valueTask: {
|
|
big: swapValue,
|
|
},
|
|
},
|
|
{
|
|
divideTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
jupiterSwapTask: {
|
|
inTokenAddress: USDC_MINT,
|
|
outTokenAddress: TOKEN_MINT,
|
|
baseAmountString: swapValue,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
onFailure: onFailureTaskDesc,
|
|
},
|
|
},
|
|
{
|
|
conditionalTask: {
|
|
attempt: [
|
|
{
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
pythAddress: PYTH_USDC_ORACLE,
|
|
pythAllowedConfidenceInterval: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
onFailure: [
|
|
{
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
switchboardAddress: SWITCHBOARD_USDC_ORACLE,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
lstPool
|
|
? OracleJob.fromYaml(
|
|
LSTExactOut(
|
|
TOKEN_MINT,
|
|
Math.ceil(Number(swapValue) / price).toString(),
|
|
),
|
|
)
|
|
: OracleJob.fromObject({
|
|
tasks: [
|
|
{
|
|
conditionalTask: {
|
|
attempt: [
|
|
{
|
|
cacheTask: {
|
|
cacheItems: [
|
|
{
|
|
variableName: 'QTY',
|
|
job: {
|
|
tasks: [
|
|
{
|
|
jupiterSwapTask: {
|
|
inTokenAddress: USDC_MINT,
|
|
outTokenAddress: TOKEN_MINT,
|
|
baseAmountString: swapValue,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
jupiterSwapTask: {
|
|
inTokenAddress: TOKEN_MINT,
|
|
outTokenAddress: USDC_MINT,
|
|
baseAmountString: '${QTY}',
|
|
},
|
|
},
|
|
{
|
|
divideTask: {
|
|
big: '${QTY}',
|
|
},
|
|
},
|
|
],
|
|
onFailure: onFailureTaskDesc,
|
|
},
|
|
},
|
|
{
|
|
conditionalTask: {
|
|
attempt: [
|
|
{
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
pythAddress: PYTH_USDC_ORACLE,
|
|
pythAllowedConfidenceInterval: 10,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
onFailure: [
|
|
{
|
|
multiplyTask: {
|
|
job: {
|
|
tasks: [
|
|
{
|
|
oracleTask: {
|
|
switchboardAddress: SWITCHBOARD_USDC_ORACLE,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
];
|
|
const decodedFeedHash = await crossbarClient
|
|
.store(queue.toBase58(), jobs)
|
|
.then((resp) => decodeString(resp.feedHash));
|
|
console.log('Feed hash:', decodedFeedHash);
|
|
|
|
const tx = await asV0Tx({
|
|
//@ts-ignore
|
|
connection: sbOnDemandProgram.provider.connection,
|
|
ixs: [await pullFeed.initIx({ ...conf, feedHash: decodedFeedHash! })],
|
|
payer: user.publicKey,
|
|
signers: [user, feedKp],
|
|
computeUnitPrice: 75_000,
|
|
computeUnitLimitMultiple: 1.3,
|
|
});
|
|
console.log('Sending initialize transaction');
|
|
const sim = await connection.simulateTransaction(tx, txOpts);
|
|
|
|
sendSignAndConfirmTransactions({
|
|
connection,
|
|
//@ts-ignore
|
|
wallet: new Wallet(user),
|
|
backupConnections: [],
|
|
transactionInstructions: [
|
|
{
|
|
instructionsSet: [
|
|
{
|
|
signers: [],
|
|
transactionInstruction: createComputeBudgetIx(500000),
|
|
},
|
|
...[
|
|
await pullFeed.initIx({ ...conf, feedHash: decodedFeedHash! }),
|
|
].map((tx) => ({
|
|
signers: [user, feedKp],
|
|
transactionInstruction: tx,
|
|
})),
|
|
],
|
|
sequenceType: SequenceType.Sequential,
|
|
},
|
|
],
|
|
config: {
|
|
maxTxesInBatch: 10,
|
|
maxRetries: 1,
|
|
autoRetry: true,
|
|
logFlowInfo: false,
|
|
useVersionedTransactions: true,
|
|
},
|
|
});
|
|
console.log(`Feed ${feedKp.publicKey}`);
|
|
})();
|
|
|
|
export type Token = {
|
|
address: string;
|
|
chainId: number;
|
|
decimals: number;
|
|
name: string;
|
|
symbol: string;
|
|
logoURI: string;
|
|
extensions: {
|
|
coingeckoId?: string;
|
|
};
|
|
tags: string[];
|
|
};
|
|
|
|
const feeFields = [u64('denominator'), u64('numerator')];
|
|
const StakePoolLayout = struct([
|
|
u8('accountType'),
|
|
publicKey('manager'),
|
|
publicKey('staker'),
|
|
publicKey('stakeDepositAuthority'),
|
|
u8('stakeWithdrawBumpSeed'),
|
|
publicKey('validatorList'),
|
|
publicKey('reserveStake'),
|
|
publicKey('poolMint'),
|
|
publicKey('managerFeeAccount'),
|
|
publicKey('tokenProgramId'),
|
|
u64('totalLamports'),
|
|
u64('poolTokenSupply'),
|
|
u64('lastUpdateEpoch'),
|
|
struct(
|
|
[u64('unixTimestamp'), u64('epoch'), publicKey('custodian')],
|
|
'lockup',
|
|
),
|
|
struct(feeFields, 'epochFee'),
|
|
option(struct(feeFields), 'nextEpochFee'),
|
|
option(publicKey(), 'preferredDepositValidatorVoteAddress'),
|
|
option(publicKey(), 'preferredWithdrawValidatorVoteAddress'),
|
|
struct(feeFields, 'stakeDepositFee'),
|
|
struct(feeFields, 'stakeWithdrawalFee'),
|
|
option(struct(feeFields), 'nextStakeWithdrawalFee'),
|
|
u8('stakeReferralFee'),
|
|
option(publicKey(), 'solDepositAuthority'),
|
|
struct(feeFields, 'solDepositFee'),
|
|
u8('solReferralFee'),
|
|
option(publicKey(), 'solWithdrawAuthority'),
|
|
struct(feeFields, 'solWithdrawalFee'),
|
|
option(struct(feeFields), 'nextSolWithdrawalFee'),
|
|
u64('lastEpochPoolTokenSupply'),
|
|
u64('lastEpochTotalLamports'),
|
|
]);
|
|
|
|
const tryGetPubKey = (pubkey: string | string[]) => {
|
|
try {
|
|
return new PublicKey(pubkey);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
};
|