Skip to content

Commit 188fad0

Browse files
authored
LL (#768)
* port PR31 on dev repo * changeset * update changeset
1 parent e528dd2 commit 188fad0

30 files changed

+2351
-9
lines changed

.changeset/purple-buses-move.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@orca-so/whirlpools-sdk": patch
3+
"@orca-so/whirlpools-program": patch
4+
"@orca-so/whirlpools-rust-client": patch
5+
"@orca-so/whirlpools-client": patch
6+
---
7+
8+
Add liquidity locking feature

legacy-sdk/whirlpool/src/impl/position-impl.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ import {
2727
decreaseLiquidityV2Ix,
2828
increaseLiquidityIx,
2929
increaseLiquidityV2Ix,
30+
lockPositionIx,
3031
updateFeesAndRewardsIx,
3132
} from "../instructions";
3233
import type { WhirlpoolAccountFetchOptions } from "../network/public/fetcher";
3334
import { IGNORE_CACHE, PREFER_CACHE } from "../network/public/fetcher";
3435
import type {
36+
LockConfigData,
37+
LockTypeData,
3538
PositionData,
3639
TickArrayData,
3740
TickData,
@@ -652,6 +655,56 @@ export class PositionImpl implements Position {
652655
return builder.build();
653656
}
654657

658+
async lock(
659+
lockType: LockTypeData,
660+
positionWallet?: Address,
661+
funder?: Address,
662+
): Promise<TransactionBuilder> {
663+
const [positionWalletKey, funderKey] = AddressUtil.toPubKeys([
664+
positionWallet ?? this.ctx.wallet.publicKey,
665+
funder ?? this.ctx.wallet.publicKey,
666+
]);
667+
668+
let txBuilder = new TransactionBuilder(
669+
this.ctx.provider.connection,
670+
this.ctx.provider.wallet,
671+
this.ctx.txBuilderOpts,
672+
);
673+
674+
const positionTokenAccount = getAssociatedTokenAddressSync(
675+
this.data.positionMint,
676+
positionWalletKey,
677+
this.ctx.accountResolverOpts.allowPDAOwnerAddress,
678+
this.positionMintTokenProgramId,
679+
);
680+
681+
const ix = lockPositionIx(this.ctx.program, {
682+
funder: funderKey,
683+
positionAuthority: positionWalletKey,
684+
position: this.address,
685+
positionMint: this.data.positionMint,
686+
positionTokenAccount,
687+
whirlpool: this.data.whirlpool,
688+
lockConfigPda: PDAUtil.getLockConfig(
689+
this.ctx.program.programId,
690+
this.address,
691+
),
692+
lockType,
693+
});
694+
695+
txBuilder.addInstruction(ix);
696+
697+
return txBuilder;
698+
}
699+
700+
async getLockConfigData(): Promise<LockConfigData | null> {
701+
const lockConfig = await this.ctx.fetcher.getLockConfig(
702+
PDAUtil.getLockConfig(this.ctx.program.programId, this.address).publicKey,
703+
IGNORE_CACHE,
704+
);
705+
return lockConfig;
706+
}
707+
655708
private async refresh() {
656709
const positionAccount = await this.ctx.fetcher.getPosition(
657710
this.address,

legacy-sdk/whirlpool/src/instructions/close-position-with-token-extensions-ix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export type ClosePositionWithTokenExtensionsParams = {
2727
* Mint and TokenAccount are based on Token-2022. And Mint accout will be also closed.
2828
*
2929
* @category Instructions
30-
* @param context - Context object containing services required to generate the instruction
30+
* @param program - program object containing services required to generate the instruction
3131
* @param params - ClosePositionWithTokenExtensionsParams object
3232
* @returns - Instruction to perform the action.
3333
*/

legacy-sdk/whirlpool/src/instructions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from "./initialize-pool-ix";
1414
export * from "./initialize-position-bundle-ix";
1515
export * from "./initialize-reward-ix";
1616
export * from "./initialize-tick-array-ix";
17+
export * from "./lock-position";
1718
export * from "./open-bundled-position-ix";
1819
export * from "./open-position-ix";
1920
export * from "./open-position-with-token-extensions-ix";
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { Program } from "@coral-xyz/anchor";
2+
import type { Instruction, PDA } from "@orca-so/common-sdk";
3+
import type { PublicKey } from "@solana/web3.js";
4+
import type { LockTypeData } from "..";
5+
import type { Whirlpool } from "../artifacts/whirlpool";
6+
import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
7+
import { SystemProgram } from "@solana/web3.js";
8+
9+
/**
10+
* Parameters to lock a position (TokenExtensions based position only).
11+
*
12+
* @category Instruction Types
13+
* @param lockType - The type of lock to apply to the position.
14+
* @param funder - The account that would fund the creation of LockConfig account
15+
* @param positionAuthority - authority that owns the token corresponding to this desired position.
16+
* @param position - PublicKey for the position which will be locked.
17+
* @param positionMint - PublicKey for the mint token for the Position token.
18+
* @param positionTokenAccount - The associated token address for the position token in the owners wallet.
19+
* @param lockConfigPda - PDA for the LockConfig account that will be created to manage lock state.
20+
* @param whirlpool - PublicKey for the whirlpool that the position belongs to.
21+
*/
22+
export type LockPositionParams = {
23+
lockType: LockTypeData;
24+
funder: PublicKey;
25+
positionAuthority: PublicKey;
26+
position: PublicKey;
27+
positionMint: PublicKey;
28+
positionTokenAccount: PublicKey;
29+
lockConfigPda: PDA;
30+
whirlpool: PublicKey;
31+
};
32+
33+
/**
34+
* Lock the position to prevent any liquidity changes.
35+
*
36+
* #### Special Errors
37+
* `PositionAlreadyLocked` - The provided position is already locked.
38+
* `PositionNotLockable` - The provided position is not lockable (e.g. An empty position).
39+
*
40+
* @category Instructions
41+
* @param program - program object containing services required to generate the instruction
42+
* @param params - LockPositionParams object.
43+
* @returns - Instruction to perform the action.
44+
*/
45+
export function lockPositionIx(
46+
program: Program<Whirlpool>,
47+
params: LockPositionParams,
48+
): Instruction {
49+
const ix = program.instruction.lockPosition(params.lockType, {
50+
accounts: {
51+
funder: params.funder,
52+
positionAuthority: params.positionAuthority,
53+
position: params.position,
54+
positionMint: params.positionMint,
55+
positionTokenAccount: params.positionTokenAccount,
56+
lockConfig: params.lockConfigPda.publicKey,
57+
whirlpool: params.whirlpool,
58+
token2022Program: TOKEN_2022_PROGRAM_ID,
59+
systemProgram: SystemProgram.programId,
60+
},
61+
});
62+
63+
return {
64+
instructions: [ix],
65+
cleanupInstructions: [],
66+
signers: [],
67+
};
68+
}

legacy-sdk/whirlpool/src/instructions/open-position-with-token-extensions-ix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export type OpenPositionWithTokenExtensionsParams = {
4141
* `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of the tick-spacing in this pool.
4242
*
4343
* @category Instructions
44-
* @param context - Context object containing services required to generate the instruction
44+
* @param program - program object containing services required to generate the instruction
4545
* @param params - OpenPositionWithTokenExtensionsParams object and a derived PDA that hosts the position's metadata.
4646
* @returns - Instruction to perform the action.
4747
*/

legacy-sdk/whirlpool/src/ix.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ export class WhirlpoolIx {
576576
* #### Special Errors
577577
* `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of the tick-spacing in this pool.
578578
*
579-
* @param context - Context object containing services required to generate the instruction
579+
* @param program - program object containing services required to generate the instruction
580580
* @param params - OpenPositionWithTokenExtensionsParams object and a derived PDA that hosts the position's metadata.
581581
* @returns - Instruction to perform the action.
582582
*/
@@ -592,7 +592,7 @@ export class WhirlpoolIx {
592592
* Mint and TokenAccount are based on Token-2022. And Mint accout will be also closed.
593593
*
594594
* @category Instructions
595-
* @param context - Context object containing services required to generate the instruction
595+
* @param program - program object containing services required to generate the instruction
596596
* @param params - ClosePositionWithTokenExtensionsParams object
597597
* @returns - Instruction to perform the action.
598598
*/
@@ -603,6 +603,25 @@ export class WhirlpoolIx {
603603
return ix.closePositionWithTokenExtensionsIx(program, params);
604604
}
605605

606+
/**
607+
* Lock the position to prevent any liquidity changes.
608+
*
609+
* #### Special Errors
610+
* `PositionAlreadyLocked` - The provided position is already locked.
611+
* `PositionNotLockable` - The provided position is not lockable (e.g. An empty position).
612+
*
613+
* @category Instructions
614+
* @param program - program object containing services required to generate the instruction
615+
* @param params - LockPositionParams object.
616+
* @returns - Instruction to perform the action.
617+
*/
618+
public static lockPositionIx(
619+
program: Program<Whirlpool>,
620+
params: ix.LockPositionParams,
621+
) {
622+
return ix.lockPositionIx(program, params);
623+
}
624+
606625
// V2 instructions
607626
// TODO: comments
608627
public static collectFeesV2Ix(

legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-impl.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {
2020
import { DEFAULT_WHIRLPOOL_RETENTION_POLICY } from "..";
2121
import type {
2222
FeeTierData,
23+
LockConfigData,
2324
PositionBundleData,
2425
PositionData,
2526
TickArrayData,
@@ -30,6 +31,7 @@ import type {
3031
} from "../../../types/public";
3132
import {
3233
ParsableFeeTier,
34+
ParsableLockConfig,
3335
ParsablePosition,
3436
ParsablePositionBundle,
3537
ParsableTickArray,
@@ -237,6 +239,19 @@ export class WhirlpoolAccountFetcher
237239
return this.fetcher.getAccounts(addresses, ParsableTokenBadge, opts);
238240
}
239241

242+
getLockConfig(
243+
address: Address,
244+
opts?: WhirlpoolAccountFetchOptions,
245+
): Promise<LockConfigData | null> {
246+
return this.fetcher.getAccount(address, ParsableLockConfig, opts);
247+
}
248+
getLockConfigs(
249+
addresses: Address[],
250+
opts?: WhirlpoolAccountFetchOptions,
251+
): Promise<ReadonlyMap<string, LockConfigData | null>> {
252+
return this.fetcher.getAccounts(addresses, ParsableLockConfig, opts);
253+
}
254+
240255
populateCache<T extends WhirlpoolSupportedTypes>(
241256
accounts: ReadonlyMap<string, T>,
242257
parser: ParsableEntity<T>,

legacy-sdk/whirlpool/src/network/public/fetcher/fetcher-types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
} from "@orca-so/common-sdk";
99
import type {
1010
FeeTierData,
11+
LockConfigData,
1112
PositionBundleData,
1213
PositionData,
1314
TickArrayData,
@@ -30,6 +31,7 @@ export type WhirlpoolSupportedTypes =
3031
| PositionBundleData
3132
| WhirlpoolsConfigExtensionData
3233
| TokenBadgeData
34+
| LockConfigData
3335
| BasicSupportedTypes;
3436

3537
/**
@@ -278,6 +280,26 @@ export interface WhirlpoolAccountFetcherInterface {
278280
opts?: WhirlpoolAccountFetchOptions,
279281
): Promise<ReadonlyMap<string, TokenBadgeData | null>>;
280282

283+
/**
284+
* Fetch and cache the account for a given LockConfig address
285+
* @param address The address of the LockConfig account
286+
* @param opts {@link WhirlpoolAccountFetchOptions} instance to dictate fetch behavior
287+
*/
288+
getLockConfig(
289+
address: Address,
290+
opts?: WhirlpoolAccountFetchOptions,
291+
): Promise<LockConfigData | null>;
292+
293+
/**
294+
* Fetch and cache the accounts for a given array of LockConfig addresses
295+
* @param addresses The array of LockConfig account addresses
296+
* @param opts {@link WhirlpoolAccountFetchOptions} instance to dictate fetch behavior
297+
*/
298+
getLockConfigs(
299+
addresses: Address[],
300+
opts?: WhirlpoolAccountFetchOptions,
301+
): Promise<ReadonlyMap<string, LockConfigData | null>>;
302+
281303
/**
282304
* Populate the fetcher's cache with the given {@link WhirlpoolsData} accounts
283305
* @param accounts The map of addresses to on-chain account data

legacy-sdk/whirlpool/src/network/public/parsing.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { AccountInfo, PublicKey } from "@solana/web3.js";
66
import * as WhirlpoolIDL from "../../artifacts/whirlpool.json";
77
import type {
88
FeeTierData,
9+
LockConfigData,
910
PositionBundleData,
1011
PositionData,
1112
TickArrayData,
@@ -195,6 +196,28 @@ export class ParsableTokenBadge {
195196
}
196197
}
197198

199+
/**
200+
* @category Network
201+
*/
202+
@staticImplements<ParsableEntity<LockConfigData>>()
203+
export class ParsableLockConfig {
204+
public static parse(
205+
address: PublicKey,
206+
accountData: AccountInfo<Buffer> | undefined | null,
207+
): LockConfigData | null {
208+
if (!accountData?.data) {
209+
return null;
210+
}
211+
212+
try {
213+
return parseAnchorAccount(AccountName.LockConfig, accountData);
214+
} catch (e) {
215+
console.error(`error while parsing LockConfig: ${e}`);
216+
return null;
217+
}
218+
}
219+
}
220+
198221
const WhirlpoolCoder = new BorshAccountsCoder(WhirlpoolIDL as Idl);
199222

200223
function parseAnchorAccount(

0 commit comments

Comments
 (0)