Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/neat-groups-slide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@orca-so/whirlpools": patch
---

Implements transfer_locked_position_instruction in ts-sdk
120 changes: 120 additions & 0 deletions ts-sdk/whirlpool/src/transferLockedPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import {
fetchMaybeLockConfig,
fetchPosition,
getPositionAddress,
getTransferLockedPositionInstruction,
WHIRLPOOL_PROGRAM_ADDRESS,
} from "@orca-so/whirlpools-client";
import {
fetchMaybeMint,
TOKEN_2022_PROGRAM_ADDRESS,
} from "@solana-program/token-2022";
import {
type Address,
type GetAccountInfoApi,
type IInstruction,
type Rpc,
type TransactionSigner,
getProgramDerivedAddress,
getAddressCodec,
} from "@solana/kit";
import { FUNDER } from "./config";
import { wrapFunctionWithExecution } from "./actionHelpers";
import assert from "assert";
import { findAssociatedTokenPda } from "@solana-program/token";

/**
* Instructions for transferring a locked position.
*/
export type TransferLockedPositionInstructions = {
/** The instructions for transferring a locked position. */
instructions: IInstruction[];
};

/**
* Generates instructions to transfer a locked position.
*
* @param {SolanaRpc} rpc - The Solana RPC client.
* @param {Address} positionMintAddress - The address of the position mint.
* @param {Address} receiver - The address of the receiver.
* @param {TransactionSigner} [authority=FUNDER] - The authority for the transfer.
* @returns {Promise<TransferLockedPositionInstructions>} - A promise that resolves to an object containing instructions.
*
* @example
* import { transferLockedPositionInstructions, setWhirlpoolsConfig } from '@orca-so/whirlpools';
* import { createSolanaRpc, devnet, address } from '@solana/kit';
* import { loadWallet } from './utils';
*
* await setWhirlpoolsConfig('solanaDevnet');
* const wallet = await loadWallet();
* const positionMint = address("HqoV7Qv27REUtmd9UKSJGGmCRNx3531t33bDG1BUfo9K");
*
* const { instructions } = await transferLockedPositionInstructions(
* devnetRpc,
* positionMint,
* receiverAddress,
* wallet
* );
*
* console.log(`Instructions: ${instructions}`);
*/
export async function transferLockedPositionInstructions(
rpc: Rpc<GetAccountInfoApi>,
positionMintAddress: Address,
receiver: Address,
authority: TransactionSigner = FUNDER,
): Promise<TransferLockedPositionInstructions> {
const instructions: IInstruction[] = [];

const positionAddress = await getPositionAddress(positionMintAddress);
const position = await fetchPosition(rpc, positionAddress[0]);
const positionMint = await fetchMaybeMint(rpc, position.data.positionMint);

assert(positionMint.exists, "Position mint not found");

const positionMintTokenAccount = await findAssociatedTokenPda({
owner: authority.address,
mint: positionMintAddress,
tokenProgram: positionMint.programAddress,
});

const destinationTokenAccount = await findAssociatedTokenPda({
owner: receiver,
mint: positionMintAddress,
tokenProgram: positionMint.programAddress,
});

const lockConfigPda = await getProgramDerivedAddress({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@wjthieme Could we use this with @orca-so/whirlpool-client? I think it is not exported. Or, I will move this to utils.ts when I fix lockPosition.ts

Copy link
Member

Choose a reason for hiding this comment

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

Yeah if the function is not yet in @orca-so/whirlpool-client (or not exported) feel free to add/change it there!

seeds: [
Buffer.from("lock_config"),
getAddressCodec().encode(positionAddress[0]),
],
programAddress: WHIRLPOOL_PROGRAM_ADDRESS,
});

const lockConfig = await fetchMaybeLockConfig(rpc, lockConfigPda[0]);
assert(lockConfig.exists, "Lock config not found");

instructions.push(
getTransferLockedPositionInstruction({
positionAuthority: authority,
receiver: receiver,
position: position.address,
positionTokenAccount: positionMintTokenAccount[0],
positionMint: positionMintAddress,
destinationTokenAccount: destinationTokenAccount[0],
lockConfig: lockConfig.address,
token2022Program: TOKEN_2022_PROGRAM_ADDRESS,
}),
);

return {
instructions,
};
}

// -------- ACTIONS --------

export const transferLockedPosition = wrapFunctionWithExecution(
transferLockedPositionInstructions,
);
Loading