Implement batch getPools method (#42)
* Adding getPools method in SDK client * Adding fetching of token vault infos to whirlpool retrieval * Adding retrieval of reward infos * Adding comment about empty pubkey constant * Updating comments * Using PoolUtil to check for initialized rewards * Updating getRewardInfos comment
This commit is contained in:
parent
8fc92ea1af
commit
16bca0ea1f
|
@ -0,0 +1,76 @@
|
|||
import BN from "bn.js";
|
||||
import { AccountFetcher, PoolUtil, TokenInfo } from "..";
|
||||
import {
|
||||
WhirlpoolData,
|
||||
WhirlpoolRewardInfo,
|
||||
WhirlpoolRewardInfoData,
|
||||
TokenAccountInfo,
|
||||
} from "../types/public";
|
||||
|
||||
export async function getTokenMintInfos(
|
||||
fetcher: AccountFetcher,
|
||||
data: WhirlpoolData,
|
||||
refresh: boolean
|
||||
): Promise<TokenInfo[]> {
|
||||
const mintA = data.tokenMintA;
|
||||
const infoA = await fetcher.getMintInfo(mintA, refresh);
|
||||
if (!infoA) {
|
||||
throw new Error(`Unable to fetch MintInfo for mint - ${mintA}`);
|
||||
}
|
||||
const mintB = data.tokenMintB;
|
||||
const infoB = await fetcher.getMintInfo(mintB, refresh);
|
||||
if (!infoB) {
|
||||
throw new Error(`Unable to fetch MintInfo for mint - ${mintB}`);
|
||||
}
|
||||
return [
|
||||
{ mint: mintA, ...infoA },
|
||||
{ mint: mintB, ...infoB },
|
||||
];
|
||||
}
|
||||
|
||||
export async function getRewardInfos(
|
||||
fetcher: AccountFetcher,
|
||||
data: WhirlpoolData,
|
||||
refresh: boolean
|
||||
): Promise<WhirlpoolRewardInfo[]> {
|
||||
const rewardInfos: WhirlpoolRewardInfo[] = [];
|
||||
for (const rewardInfo of data.rewardInfos) {
|
||||
rewardInfos.push(await getRewardInfo(fetcher, rewardInfo, refresh));
|
||||
}
|
||||
return rewardInfos;
|
||||
}
|
||||
|
||||
async function getRewardInfo(
|
||||
fetcher: AccountFetcher,
|
||||
data: WhirlpoolRewardInfoData,
|
||||
refresh: boolean
|
||||
): Promise<WhirlpoolRewardInfo> {
|
||||
const rewardInfo = { ...data, initialized: false, vaultAmount: new BN(0) };
|
||||
if (PoolUtil.isRewardInitialized(data)) {
|
||||
const vaultInfo = await fetcher.getTokenInfo(data.vault, refresh);
|
||||
if (!vaultInfo) {
|
||||
throw new Error(`Unable to fetch TokenAccountInfo for vault - ${data.vault}`);
|
||||
}
|
||||
rewardInfo.initialized = true;
|
||||
rewardInfo.vaultAmount = vaultInfo.amount;
|
||||
}
|
||||
return rewardInfo;
|
||||
}
|
||||
|
||||
export async function getTokenVaultAccountInfos(
|
||||
fetcher: AccountFetcher,
|
||||
data: WhirlpoolData,
|
||||
refresh: boolean
|
||||
): Promise<TokenAccountInfo[]> {
|
||||
const vaultA = data.tokenVaultA;
|
||||
const vaultInfoA = await fetcher.getTokenInfo(vaultA, refresh);
|
||||
if (!vaultInfoA) {
|
||||
throw new Error(`Unable to fetch TokenAccountInfo for vault - ${vaultA}`);
|
||||
}
|
||||
const vaultB = data.tokenVaultB;
|
||||
const vaultInfoB = await fetcher.getTokenInfo(vaultB, refresh);
|
||||
if (!vaultInfoB) {
|
||||
throw new Error(`Unable to fetch TokenAccountInfo for vault - ${vaultB}`);
|
||||
}
|
||||
return [vaultInfoA, vaultInfoB];
|
||||
}
|
|
@ -2,9 +2,11 @@ import { AddressUtil } from "@orca-so/common-sdk";
|
|||
import { Address } from "@project-serum/anchor";
|
||||
import { WhirlpoolContext } from "../context";
|
||||
import { AccountFetcher } from "../network/public";
|
||||
import { WhirlpoolData, TokenInfo } from "../types/public";
|
||||
import { WhirlpoolData } from "../types/public";
|
||||
import { PoolUtil } from "../utils/public";
|
||||
import { WhirlpoolClient, Whirlpool, Position } from "../whirlpool-client";
|
||||
import { PositionImpl } from "./position-impl";
|
||||
import { getRewardInfos, getTokenMintInfos, getTokenVaultAccountInfos } from "./util";
|
||||
import { WhirlpoolImpl } from "./whirlpool-impl";
|
||||
|
||||
export class WhirlpoolClientImpl implements WhirlpoolClient {
|
||||
|
@ -23,17 +25,69 @@ export class WhirlpoolClientImpl implements WhirlpoolClient {
|
|||
if (!account) {
|
||||
throw new Error(`Unable to fetch Whirlpool at address at ${poolAddress}`);
|
||||
}
|
||||
const tokenInfos = await getTokenInfos(this.ctx.fetcher, account, false);
|
||||
const tokenInfos = await getTokenMintInfos(this.ctx.fetcher, account, refresh);
|
||||
const vaultInfos = await getTokenVaultAccountInfos(this.ctx.fetcher, account, refresh);
|
||||
const rewardInfos = await getRewardInfos(this.ctx.fetcher, account, refresh);
|
||||
return new WhirlpoolImpl(
|
||||
this.ctx,
|
||||
this.ctx.fetcher,
|
||||
AddressUtil.toPubKey(poolAddress),
|
||||
tokenInfos[0],
|
||||
tokenInfos[1],
|
||||
vaultInfos[0],
|
||||
vaultInfos[1],
|
||||
rewardInfos,
|
||||
account
|
||||
);
|
||||
}
|
||||
|
||||
public async getPools(poolAddresses: Address[], refresh = false): Promise<Whirlpool[]> {
|
||||
const accounts = (await this.ctx.fetcher.listPools(poolAddresses, refresh)).filter(
|
||||
(account): account is WhirlpoolData => !!account
|
||||
);
|
||||
if (accounts.length !== poolAddresses.length) {
|
||||
throw new Error(`Unable to fetch all Whirlpools at addresses ${poolAddresses}`);
|
||||
}
|
||||
const tokenMints = new Set<string>();
|
||||
const tokenAccounts = new Set<string>();
|
||||
accounts.forEach((account) => {
|
||||
tokenMints.add(account.tokenMintA.toBase58());
|
||||
tokenMints.add(account.tokenMintB.toBase58());
|
||||
tokenAccounts.add(account.tokenVaultA.toBase58());
|
||||
tokenAccounts.add(account.tokenVaultB.toBase58());
|
||||
account.rewardInfos.forEach((rewardInfo) => {
|
||||
if (PoolUtil.isRewardInitialized(rewardInfo)) {
|
||||
tokenAccounts.add(rewardInfo.vault.toBase58());
|
||||
}
|
||||
});
|
||||
});
|
||||
await this.ctx.fetcher.listMintInfos(Array.from(tokenMints), refresh);
|
||||
await this.ctx.fetcher.listTokenInfos(Array.from(tokenAccounts), refresh);
|
||||
|
||||
const whirlpools: Whirlpool[] = [];
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const account = accounts[i];
|
||||
const poolAddress = poolAddresses[i];
|
||||
const tokenInfos = await getTokenMintInfos(this.ctx.fetcher, account, false);
|
||||
const vaultInfos = await getTokenVaultAccountInfos(this.ctx.fetcher, account, false);
|
||||
const rewardInfos = await getRewardInfos(this.ctx.fetcher, account, false);
|
||||
whirlpools.push(
|
||||
new WhirlpoolImpl(
|
||||
this.ctx,
|
||||
this.ctx.fetcher,
|
||||
AddressUtil.toPubKey(poolAddress),
|
||||
tokenInfos[0],
|
||||
tokenInfos[1],
|
||||
vaultInfos[0],
|
||||
vaultInfos[1],
|
||||
rewardInfos,
|
||||
account
|
||||
)
|
||||
);
|
||||
}
|
||||
return whirlpools;
|
||||
}
|
||||
|
||||
public async getPosition(positionAddress: Address, refresh = false): Promise<Position> {
|
||||
const account = await this.ctx.fetcher.getPosition(positionAddress, refresh);
|
||||
if (!account) {
|
||||
|
@ -47,24 +101,3 @@ export class WhirlpoolClientImpl implements WhirlpoolClient {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function getTokenInfos(
|
||||
fetcher: AccountFetcher,
|
||||
data: WhirlpoolData,
|
||||
refresh: boolean
|
||||
): Promise<TokenInfo[]> {
|
||||
const mintA = data.tokenMintA;
|
||||
const infoA = await fetcher.getMintInfo(mintA, refresh);
|
||||
if (!infoA) {
|
||||
throw new Error(`Unable to fetch MintInfo for mint - ${mintA}`);
|
||||
}
|
||||
const mintB = data.tokenMintB;
|
||||
const infoB = await fetcher.getMintInfo(mintB, refresh);
|
||||
if (!infoB) {
|
||||
throw new Error(`Unable to fetch MintInfo for mint - ${mintB}`);
|
||||
}
|
||||
return [
|
||||
{ mint: mintA, ...infoA },
|
||||
{ mint: mintB, ...infoB },
|
||||
];
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@ import {
|
|||
swapIx,
|
||||
SwapInput,
|
||||
} from "../instructions";
|
||||
import { TokenInfo, WhirlpoolData } from "../types/public";
|
||||
import { TokenAccountInfo, TokenInfo, WhirlpoolData, WhirlpoolRewardInfo } from "../types/public";
|
||||
import { Whirlpool } from "../whirlpool-client";
|
||||
import { PublicKey, Keypair } from "@solana/web3.js";
|
||||
import { AccountFetcher } from "../network/public";
|
||||
import invariant from "tiny-invariant";
|
||||
import { PDAUtil, PriceMath, TickArrayUtil, TickUtil } from "../utils/public";
|
||||
import { PDAUtil, TickArrayUtil, TickUtil } from "../utils/public";
|
||||
import { decreaseLiquidityQuoteByLiquidityWithParams, SwapQuote } from "../quotes/public";
|
||||
import { getRewardInfos, getTokenVaultAccountInfos } from "./util";
|
||||
|
||||
export class WhirlpoolImpl implements Whirlpool {
|
||||
private data: WhirlpoolData;
|
||||
|
@ -35,6 +36,9 @@ export class WhirlpoolImpl implements Whirlpool {
|
|||
readonly address: PublicKey,
|
||||
readonly tokenAInfo: TokenInfo,
|
||||
readonly tokenBInfo: TokenInfo,
|
||||
private tokenVaultAInfo: TokenAccountInfo,
|
||||
private tokenVaultBInfo: TokenAccountInfo,
|
||||
private rewardInfos: WhirlpoolRewardInfo[],
|
||||
data: WhirlpoolData
|
||||
) {
|
||||
this.data = data;
|
||||
|
@ -56,6 +60,18 @@ export class WhirlpoolImpl implements Whirlpool {
|
|||
return this.tokenBInfo;
|
||||
}
|
||||
|
||||
getTokenVaultAInfo(): TokenAccountInfo {
|
||||
return this.tokenVaultAInfo;
|
||||
}
|
||||
|
||||
getTokenVaultBInfo(): TokenAccountInfo {
|
||||
return this.tokenVaultBInfo;
|
||||
}
|
||||
|
||||
getRewardInfos(): WhirlpoolRewardInfo[] {
|
||||
return this.rewardInfos;
|
||||
}
|
||||
|
||||
async refreshData() {
|
||||
await this.refresh();
|
||||
return this.data;
|
||||
|
@ -422,7 +438,16 @@ export class WhirlpoolImpl implements Whirlpool {
|
|||
private async refresh() {
|
||||
const account = await this.fetcher.getPool(this.address, true);
|
||||
if (!!account) {
|
||||
const rewardInfos = await getRewardInfos(this.fetcher, account, true);
|
||||
const [tokenVaultAInfo, tokenVaultBInfo] = await getTokenVaultAccountInfos(
|
||||
this.fetcher,
|
||||
account,
|
||||
true
|
||||
);
|
||||
this.data = account;
|
||||
this.tokenVaultAInfo = tokenVaultAInfo;
|
||||
this.tokenVaultBInfo = tokenVaultBInfo;
|
||||
this.rewardInfos = rewardInfos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { MintInfo } from "@solana/spl-token";
|
||||
import { TickArrayData } from "./anchor-types";
|
||||
import { AccountInfo, MintInfo, u64 } from "@solana/spl-token";
|
||||
import { TickArrayData, WhirlpoolRewardInfoData } from "./anchor-types";
|
||||
|
||||
/**
|
||||
* Extended MintInfo class to host token info.
|
||||
|
@ -8,6 +8,13 @@ import { TickArrayData } from "./anchor-types";
|
|||
*/
|
||||
export type TokenInfo = MintInfo & { mint: PublicKey };
|
||||
|
||||
export type TokenAccountInfo = AccountInfo;
|
||||
|
||||
export type WhirlpoolRewardInfo = WhirlpoolRewardInfoData & {
|
||||
initialized: boolean;
|
||||
vaultAmount: u64;
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper class of a TickArray on a Whirlpool
|
||||
* @category WhirlpoolClient
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
PositionData,
|
||||
WhirlpoolData,
|
||||
} from "./types/public";
|
||||
import { TokenInfo } from "./types/public/client-types";
|
||||
import { TokenAccountInfo, TokenInfo, WhirlpoolRewardInfo } from "./types/public/client-types";
|
||||
|
||||
/**
|
||||
* Helper class to help interact with Whirlpool Accounts with a simpler interface.
|
||||
|
@ -38,6 +38,13 @@ export interface WhirlpoolClient {
|
|||
*/
|
||||
getPool: (poolAddress: Address, refresh?: boolean) => Promise<Whirlpool>;
|
||||
|
||||
/**
|
||||
* Get a list of Whirlpool objects matching the provided list of addresses.
|
||||
* @param poolAddresses the addresses of the Whirlpool accounts
|
||||
* @return a list of Whirlpool objects to interact with
|
||||
*/
|
||||
getPools: (poolAddresses: Address[], refresh?: boolean) => Promise<Whirlpool[]>;
|
||||
|
||||
/**
|
||||
* Get a Position object to interact with the Position account at the given address.
|
||||
* @param positionAddress the address of the Position account
|
||||
|
@ -92,6 +99,24 @@ export interface Whirlpool {
|
|||
*/
|
||||
getTokenBInfo: () => TokenInfo;
|
||||
|
||||
/**
|
||||
* Get the TokenAccountInfo for token vault A of this pool.
|
||||
* @return TokenAccountInfo for token vault A
|
||||
*/
|
||||
getTokenVaultAInfo: () => TokenAccountInfo;
|
||||
|
||||
/**
|
||||
* Get the TokenAccountInfo for token vault B of this pool.
|
||||
* @return TokenAccountInfo for token vault B
|
||||
*/
|
||||
getTokenVaultBInfo: () => TokenAccountInfo;
|
||||
|
||||
/**
|
||||
* Get the WhirlpoolRewardInfos for this pool.
|
||||
* @return Array of 3 WhirlpoolRewardInfos. However, not all of them may be initialized. Use the initialized field on WhirlpoolRewardInfo to check if the reward is active.
|
||||
*/
|
||||
getRewardInfos: () => WhirlpoolRewardInfo[];
|
||||
|
||||
/**
|
||||
* Initialize a set of tick-arrays that encompasses the provided ticks.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue