Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a helper function for creating splash pools #198

Merged
merged 7 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion legacy-sdk/whirlpool/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@orca-so/whirlpools-sdk",
"version": "0.13.3",
"version": "0.13.4",
"description": "Typescript SDK to interact with Orca's Whirlpool program.",
"license": "Apache-2.0",
"main": "dist/index.js",
Expand Down
125 changes: 124 additions & 1 deletion legacy-sdk/whirlpool/src/impl/whirlpool-client-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import {
WhirlpoolAccountFetcherInterface,
} from "../network/public/fetcher";
import { WhirlpoolRouter, WhirlpoolRouterBuilder } from "../router/public";
import { WhirlpoolData } from "../types/public";
import { MAX_TICK_INDEX, MIN_TICK_INDEX, SPLASH_POOL_TICK_SPACING, WhirlpoolData } from "../types/public";
import { getTickArrayDataForPosition } from "../utils/builder/position-builder-util";
import { PDAUtil, PoolUtil, PriceMath, TickUtil } from "../utils/public";
import { Position, Whirlpool, WhirlpoolClient } from "../whirlpool-client";
import { PositionImpl } from "./position-impl";
import { getRewardInfos, getTokenMintInfos, getTokenVaultAccountInfos } from "./util";
import { WhirlpoolImpl } from "./whirlpool-impl";
import { NO_TOKEN_EXTENSION_CONTEXT, TokenExtensionContextForPool, TokenExtensionUtil } from "../utils/public/token-extension-util";
import Decimal from "decimal.js";

export class WhirlpoolClientImpl implements WhirlpoolClient {
constructor(readonly ctx: WhirlpoolContext) {}
Expand Down Expand Up @@ -187,6 +188,128 @@ export class WhirlpoolClientImpl implements WhirlpoolClient {
return Object.fromEntries(results);
}

public async createSplashPool(
whirlpoolsConfig: Address,
tokenMintA: Address,
tokenMintB: Address,
initialPrice: Decimal,
funder: Address,
opts = PREFER_CACHE
): Promise<{ poolKey: PublicKey; tx: TransactionBuilder }> {
const correctTokenOrder = PoolUtil.orderMints(tokenMintA, tokenMintB).map((addr) =>
addr.toString()
);

invariant(
correctTokenOrder[0] === tokenMintA.toString(),
"Token order needs to be flipped to match the canonical ordering (i.e. sorted on the byte repr. of the mint pubkeys)"
);

const mintInfos = await this.getFetcher().getMintInfos([tokenMintA, tokenMintB], opts);
invariant(mintInfos.size === 2, "At least one of the token mints cannot be found.");

const tokenExtensionCtx: TokenExtensionContextForPool = {
...NO_TOKEN_EXTENSION_CONTEXT,
tokenMintWithProgramA: mintInfos.get(tokenMintA.toString())!,
tokenMintWithProgramB: mintInfos.get(tokenMintB.toString())!,
};

whirlpoolsConfig = AddressUtil.toPubKey(whirlpoolsConfig);

const feeTierKey = PDAUtil.getFeeTier(
this.ctx.program.programId,
whirlpoolsConfig,
SPLASH_POOL_TICK_SPACING
).publicKey;

const whirlpoolPda = PDAUtil.getWhirlpool(
this.ctx.program.programId,
whirlpoolsConfig,
new PublicKey(tokenMintA),
new PublicKey(tokenMintB),
SPLASH_POOL_TICK_SPACING
);

const tokenDecimalsA = mintInfos.get(tokenMintA.toString())?.decimals ?? 0;
const tokenDecimalsB = mintInfos.get(tokenMintB.toString())?.decimals ?? 0;
const initSqrtPrice = PriceMath.priceToSqrtPriceX64(initialPrice, tokenDecimalsA, tokenDecimalsB);
const tokenVaultAKeypair = Keypair.generate();
const tokenVaultBKeypair = Keypair.generate();

const txBuilder = new TransactionBuilder(
this.ctx.provider.connection,
this.ctx.provider.wallet,
this.ctx.txBuilderOpts
);

const tokenBadgeA = PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, AddressUtil.toPubKey(tokenMintA)).publicKey;
const tokenBadgeB = PDAUtil.getTokenBadge(this.ctx.program.programId, whirlpoolsConfig, AddressUtil.toPubKey(tokenMintB)).publicKey;

const baseParams = {
initSqrtPrice,
whirlpoolsConfig,
whirlpoolPda,
tokenMintA: new PublicKey(tokenMintA),
tokenMintB: new PublicKey(tokenMintB),
tokenVaultAKeypair,
tokenVaultBKeypair,
feeTierKey,
tickSpacing: SPLASH_POOL_TICK_SPACING,
funder: new PublicKey(funder),
};

const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
? WhirlpoolIx.initializePoolIx(this.ctx.program, baseParams)
: WhirlpoolIx.initializePoolV2Ix(this.ctx.program, {
...baseParams,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
tokenBadgeA,
tokenBadgeB,
});

txBuilder.addInstruction(initPoolIx);

const [startTickIndex, endTickIndex] = TickUtil.getFullRangeTickIndex(SPLASH_POOL_TICK_SPACING);
const startInitializableTickIndex = TickUtil.getStartTickIndex(startTickIndex, SPLASH_POOL_TICK_SPACING);
const endInitializableTickIndex = TickUtil.getStartTickIndex(endTickIndex, SPLASH_POOL_TICK_SPACING);

const startTickArrayPda = PDAUtil.getTickArray(
this.ctx.program.programId,
whirlpoolPda.publicKey,
startInitializableTickIndex
);

const endTickArrayPda = PDAUtil.getTickArray(
this.ctx.program.programId,
whirlpoolPda.publicKey,
endInitializableTickIndex
);

txBuilder.addInstruction(
initTickArrayIx(this.ctx.program, {
startTick: startInitializableTickIndex,
tickArrayPda: startTickArrayPda,
whirlpool: whirlpoolPda.publicKey,
funder: AddressUtil.toPubKey(funder),
})
);

txBuilder.addInstruction(
initTickArrayIx(this.ctx.program, {
startTick: endInitializableTickIndex,
tickArrayPda: endTickArrayPda,
whirlpool: whirlpoolPda.publicKey,
funder: AddressUtil.toPubKey(funder),
})
);

return {
poolKey: whirlpoolPda.publicKey,
tx: txBuilder,
};
}

public async createPool(
whirlpoolsConfig: Address,
tokenMintA: Address,
Expand Down
7 changes: 7 additions & 0 deletions legacy-sdk/whirlpool/src/types/public/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,10 @@ export const WHIRLPOOL_NFT_UPDATE_AUTH = new PublicKey(
* @category Constants
*/
export const FULL_RANGE_ONLY_TICK_SPACING_THRESHOLD = 32768;


/**
* The tick spacing for splash pools.
* @category Constants
*/
export const SPLASH_POOL_TICK_SPACING = 32896;
19 changes: 19 additions & 0 deletions legacy-sdk/whirlpool/src/whirlpool-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
WhirlpoolData,
} from "./types/public";
import { TokenAccountInfo, TokenInfo, WhirlpoolRewardInfo } from "./types/public/client-types";
import Decimal from "decimal.js";

/**
* Helper class to help interact with Whirlpool Accounts with a simpler interface.
Expand Down Expand Up @@ -92,6 +93,24 @@ export interface WhirlpoolClient {
opts?: WhirlpoolAccountFetchOptions
) => Promise<TransactionBuilder[]>;

/**
* Create a Whirlpool account for a group of token A, token B and tick spacing
* @param whirlpoolConfig the address of the whirlpool config
* @param tokenMintA the address of the token A
* @param tokenMintB the address of the token B
* @param initialPrice the initial price of the pool (as x token B per 1 token A)
* @param funder the account to debit SOL from to fund the creation of the account(s)
* @return `poolKey`: The public key of the newly created whirlpool account. `tx`: The transaction containing instructions for the on-chain operations.
* @throws error when the tokens are not in the canonical byte-based ordering. To resolve this, invert the token order and the initialTick (see `TickUtil.invertTick()`, `PriceMath.invertSqrtPriceX64()`, or `PriceMath.invertPrice()`).
*/
createSplashPool: (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to match CPMM, we need to hide the "tick" & "tick-array" concepts from the dev.

  • this method sets the initial price to some default (ex $1)
  • creates both tick-arrays in a splash-pool in the same call (createPool only creates 1 of them)

Copy link
Member Author

@wjthieme wjthieme Aug 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I assume it would be better to still be able to set initial price though instead of some default

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have it as an optional?

Copy link
Member Author

@wjthieme wjthieme Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 B/A as the default?

whirlpoolsConfig: Address,
tokenMintA: Address,
tokenMintB: Address,
initialPrice: Decimal,
funder: Address
) => Promise<{ poolKey: PublicKey; tx: TransactionBuilder }>;

/**
* Create a Whirlpool account for a group of token A, token B and tick spacing
* @param whirlpoolConfig the address of the whirlpool config
Expand Down
Loading