UNPKG

@robertprp/intents-sdk

Version:

Shogun Network Intent-based cross-chain swaps SDK

143 lines (119 loc) 4.75 kB
import { address, addSignersToTransactionMessage, appendTransactionMessageInstructions, createNoopSigner, createSolanaRpc, createTransactionMessage, fetchEncodedAccount, generateKeyPairSigner, getAddressEncoder, getProgramDerivedAddress, getTransactionCodec, partiallySignTransactionMessageWithSigners, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, type Address, type IInstruction, } from '@solana/kit'; import type { DcaSingleChainOrder } from '../../orders/dca-single-chain.js'; import type { SolanaOrderInstructionResult } from '../order-instructions.js'; import { getDefaultSolanaRPC } from '../client.js'; import { genSecretHashAndNumber } from '../utils.js'; import { NATIVE_SOLANA_TOKEN_ADDRESS, SINGLE_CHAIN_GUARD_ADDRESSES, WRAPPED_SOL_MINT_ADDRESS, } from '../../../constants.js'; import { ASSOCIATED_TOKEN_PROGRAM_ADDRESS, fetchMint, getCreateAssociatedTokenInstructionAsync, getSyncNativeInstruction, } from '@solana-program/token'; import { ChainID } from '../../../chains.js'; import { getTransferSolInstruction } from '@solana-program/system'; import { getCreateDcaOrderInstructionAsync } from '../generated/single-chain/index.js'; export async function getSolanaDcaSingleChainOrderInstructions( order: DcaSingleChainOrder, options?: { rpcUrl?: string }, ): Promise<SolanaOrderInstructionResult & { secretNumber: string }> { const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC(); const orderSigner = await generateKeyPairSigner(); const signer = createNoopSigner(order.user as Address); let tokenInMint = address(order.tokenIn); const { secretHash, secretNumber } = genSecretHashAndNumber(order); const orderUserAddress = address(order.user); const spendingNative = tokenInMint === NATIVE_SOLANA_TOKEN_ADDRESS; if (spendingNative) { tokenInMint = WRAPPED_SOL_MINT_ADDRESS; } const tokenMintProgram = await fetchMint(rpc, tokenInMint); const guardAddress = address(SINGLE_CHAIN_GUARD_ADDRESSES[ChainID.Solana]); // Assuming DCA uses same guard const addressEncoder = getAddressEncoder(); const instructions: Array<IInstruction> = []; const [tokenInProgramAccount] = await getProgramDerivedAddress({ programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS, seeds: [ addressEncoder.encode(orderUserAddress), addressEncoder.encode(tokenMintProgram.programAddress), addressEncoder.encode(tokenInMint), ], }); const userTokenInAccount = await fetchEncodedAccount(rpc, tokenInProgramAccount); if (!userTokenInAccount.exists) { const createAccountIx = await getCreateAssociatedTokenInstructionAsync({ payer: signer, ata: tokenInProgramAccount, owner: orderUserAddress, mint: tokenMintProgram.address, tokenProgram: tokenMintProgram.programAddress, }); instructions.push(createAccountIx); } const totalAmountIn = BigInt(order.amountInPerInterval) * BigInt(order.totalIntervals); if (spendingNative) { const transferIx = getTransferSolInstruction({ amount: totalAmountIn, destination: userTokenInAccount.address, source: signer, }); instructions.push(transferIx); const syncNativeIx = getSyncNativeInstruction({ account: userTokenInAccount.address, }); instructions.push(syncNativeIx); } const createDcaOrderIx = await getCreateDcaOrderInstructionAsync({ user: signer, order: orderSigner, guard: guardAddress, tokenInMint: tokenInMint, userTokenInAccount: userTokenInAccount.address, tokenInProgram: tokenMintProgram.programAddress, amountInPerInterval: order.amountInPerInterval, totalIntervals: order.totalIntervals, intervalDuration: order.intervalDuration, deadline: order.deadline, amountOutMin: order.amountOutMin, secretHash: Uint8Array.from(secretHash), extraTransfersAmounts: [], // TODO: Implement extra transfers }); instructions.push(createDcaOrderIx); const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send(); const txMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx), (tx) => addSignersToTransactionMessage([orderSigner], tx), ); const partiallySignedTransaction = await partiallySignTransactionMessageWithSigners(txMessage); const txBytes = getTransactionCodec().encode(partiallySignedTransaction); return { orderAddress: orderSigner.address, txBytes, secretNumber, }; }