import { createFundCollateralInstruction } from '@convergence-rfq/rfq'; import { PublicKey } from '@solana/web3.js'; import { SendAndConfirmTransactionResponse } from '../../rpcModule'; import { Operation, OperationHandler, OperationScope, useOperation, Signer, makeConfirmOptionsFinalizedOnMainnet, } from '../../../types'; import { TransactionBuilder, TransactionBuilderOptions, } from '../../../utils/TransactionBuilder'; import { addDecimals } from '../../../utils/conversions'; import { Convergence } from '../../../Convergence'; import { protocolCache } from '../../protocolModule/cache'; import { collateralMintCache } from '../cache'; import { addComputeBudgetIxsIfNeeded } from '@/utils/helpers'; const Key = 'FundCollateralOperation' as const; /** * Funds a collateral account. * * ```ts * const rfq = await convergence.collateral().fundCollateral({ amount: 100 }; * ``` * * @group Operations * @category Constructors */ export const fundCollateralOperation = useOperation(Key); /** * @group Operations * @category Types */ export type FundCollateralOperation = Operation< typeof Key, FundCollateralInput, FundCollateralOutput >; /** * @group Operations * @category Inputs */ export type FundCollateralInput = { /** * The user for whom collateral is funded. * * @defaultValue `convergence.identity()` */ user?: Signer; /** * The address of the protocol. * * @defaultValue `convergence.protocol().pdas().protocol()` */ protocol?: PublicKey; /** User token account. */ userTokens?: PublicKey; /** The amount to fund. */ amount: number; }; /** * @group Operations * @category Outputs */ export type FundCollateralOutput = { response: SendAndConfirmTransactionResponse; }; /** * @group Operations * @category Handlers */ export const fundCollateralOperationHandler: OperationHandler = { handle: async ( operation: FundCollateralOperation, convergence: Convergence, scope: OperationScope ) => { scope.throwIfCanceled(); const builder = await fundCollateralBuilder( convergence, { ...operation.input, }, scope ); scope.throwIfCanceled(); const confirmOptions = makeConfirmOptionsFinalizedOnMainnet( convergence, scope.confirmOptions ); const output = await builder.sendAndConfirm(convergence, confirmOptions); scope.throwIfCanceled(); return output; }, }; export type FundCollateralBuilderParams = FundCollateralInput; /** * Funds a collateral account. * * ```ts * const transactionBuilder = await convergence * .rfqs() * .builders() * .fundCollateral(); * ``` * * @group Transaction Builders * @category Constructors */ export const fundCollateralBuilder = async ( convergence: Convergence, params: FundCollateralBuilderParams, options: TransactionBuilderOptions = {} ): Promise => { const { programs, payer = convergence.rpc().getDefaultFeePayer() } = options; const { user = convergence.identity() } = params; const protocolModel = await protocolCache.get(convergence); const { protocol = convergence.protocol().pdas().protocol(), userTokens = convergence.tokens().pdas().associatedTokenAccount({ mint: protocolModel.collateralMint, owner: user.publicKey, programs, }), } = params; const { amount } = params; const collateralToken = convergence .collateral() .pdas() .collateralToken({ user: user.publicKey }); const collateralInfo = convergence .collateral() .pdas() .collateralInfo({ user: user.publicKey }); const collateralMint = await collateralMintCache.get(convergence); const collateralDecimals = collateralMint.decimals; const txBuilder = TransactionBuilder.make() .setFeePayer(payer) .add({ instruction: createFundCollateralInstruction( { user: user.publicKey, userTokens, protocol, collateralInfo, collateralToken, }, { amount: addDecimals(amount, collateralDecimals), }, convergence.programs().getRfq(programs).address ), signers: [user], key: 'fundCollateral', }); await addComputeBudgetIxsIfNeeded(txBuilder, convergence); return txBuilder; };