From 7488419f527db9ddf893ead68c9600bd16bc693e Mon Sep 17 00:00:00 2001 From: Nick Caradonna Date: Mon, 20 Oct 2025 23:00:53 -0400 Subject: [PATCH 1/3] sdk: allow other authority signer for deposits --- CHANGELOG.md | 1 + sdk/src/driftClient.ts | 97 +++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9cc3fb8a..c46ed3813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Features +- sdk: allow deposit from external authority directly to drift account ### Fixes diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 4c71a49ab..e91baae5b 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -349,8 +349,8 @@ export class DriftClient { this.authoritySubAccountMap = config.authoritySubAccountMap ? config.authoritySubAccountMap : config.subAccountIds - ? new Map([[this.authority.toString(), config.subAccountIds]]) - : new Map(); + ? new Map([[this.authority.toString(), config.subAccountIds]]) + : new Map(); this.includeDelegates = config.includeDelegates ?? false; if (config.accountSubscription?.type === 'polling') { @@ -840,8 +840,8 @@ export class DriftClient { this.authoritySubAccountMap = authoritySubaccountMap ? authoritySubaccountMap : subAccountIds - ? new Map([[this.authority.toString(), subAccountIds]]) - : new Map(); + ? new Map([[this.authority.toString(), subAccountIds]]) + : new Map(); /* Reset user stats account */ if (this.userStats?.isSubscribed) { @@ -2843,23 +2843,25 @@ export class DriftClient { marketIndex: number, associatedTokenAccount: PublicKey, subAccountId?: number, - reduceOnly = false + reduceOnly = false, + signerAuthority?: PublicKey ): Promise { const spotMarketAccount = this.getSpotMarketAccount(marketIndex); const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT); - const signerAuthority = this.wallet.publicKey; + const signer = signerAuthority ?? this.wallet.publicKey; const createWSOLTokenAccount = - isSolMarket && associatedTokenAccount.equals(signerAuthority); + isSolMarket && associatedTokenAccount.equals(signer); const instructions = []; if (createWSOLTokenAccount) { const { ixs, pubkey } = await this.getWrappedSolAccountCreationIxs( amount, - true + true, + signerAuthority ); associatedTokenAccount = pubkey; @@ -2873,7 +2875,8 @@ export class DriftClient { associatedTokenAccount, subAccountId, reduceOnly, - true + true, + signerAuthority ); instructions.push(depositCollateralIx); @@ -2883,8 +2886,8 @@ export class DriftClient { instructions.push( createCloseAccountInstruction( associatedTokenAccount, - signerAuthority, - signerAuthority, + signer, + signer, [] ) ); @@ -2953,14 +2956,16 @@ export class DriftClient { subAccountId?: number, reduceOnly = false, txParams?: TxParams, - initSwiftAccount = false + initSwiftAccount = false, + signerAuthority?: PublicKey ): Promise { const instructions = await this.getDepositTxnIx( amount, marketIndex, associatedTokenAccount, subAccountId, - reduceOnly + reduceOnly, + signerAuthority ); if (initSwiftAccount) { @@ -2995,6 +3000,9 @@ export class DriftClient { * @param associatedTokenAccount can be the wallet public key if using native sol * @param subAccountId subaccountId to deposit * @param reduceOnly if true, deposit must not increase account risk + * @param txParams transaction parameters + * @param initSwiftAccount if true, initialize a swift account for the user + * @param signerAuthority the authority to sign the transaction, allowing for any authority to deposit directly to a Drift account */ public async deposit( amount: BN, @@ -3003,7 +3011,8 @@ export class DriftClient { subAccountId?: number, reduceOnly = false, txParams?: TxParams, - initSwiftAccount = false + initSwiftAccount = false, + signerAuthority?: PublicKey ): Promise { const tx = await this.createDepositTxn( amount, @@ -3012,7 +3021,8 @@ export class DriftClient { subAccountId, reduceOnly, txParams, - initSwiftAccount + initSwiftAccount, + signerAuthority ); const { txSig, slot } = await this.sendTransaction(tx, [], this.opts); @@ -3026,7 +3036,8 @@ export class DriftClient { userTokenAccount: PublicKey, subAccountId?: number, reduceOnly = false, - userInitialized = true + userInitialized = true, + signerAuthority?: PublicKey ): Promise { const userAccountPublicKey = await getUserAccountPublicKey( this.program.programId, @@ -3071,7 +3082,7 @@ export class DriftClient { user: userAccountPublicKey, userStats: this.getUserStatsAccountPublicKey(), userTokenAccount: userTokenAccount, - authority: this.wallet.publicKey, + authority: signerAuthority ?? this.wallet.publicKey, tokenProgram, }, remainingAccounts, @@ -3091,14 +3102,15 @@ export class DriftClient { public async getWrappedSolAccountCreationIxs( amount: BN, - includeRent?: boolean + includeRent?: boolean, + signerAuthority?: PublicKey ): Promise<{ ixs: anchor.web3.TransactionInstruction[]; /** @deprecated - this array is always going to be empty, in the current implementation */ signers: Signer[]; pubkey: PublicKey; }> { - const authority = this.wallet.publicKey; + const authority = signerAuthority ?? this.wallet.publicKey; // Generate a random seed for wrappedSolAccount. const seed = Keypair.generate().publicKey.toBase58().slice(0, 32); @@ -3316,7 +3328,7 @@ export class DriftClient { marketIndex, fromSubAccountId, subAccountId - ) + ) : await this.getDepositInstruction( amount, marketIndex, @@ -3324,7 +3336,7 @@ export class DriftClient { subAccountId, false, false - ); + ); if (subAccountId === 0) { if ( @@ -4367,11 +4379,11 @@ export class DriftClient { [orderParams, ...bracketOrdersParams], positionMaxLev, userAccount.subAccountId - ) + ) : this.getPlaceOrdersIx( [orderParams, ...bracketOrdersParams], userAccount.subAccountId - ); + ); ixPromisesForTxs.marketOrderTx = marketOrderTxIxs; @@ -4522,7 +4534,7 @@ export class DriftClient { this.program.programId, this.authority, subAccountId - ) + ) : await this.getUserAccountPublicKey(subAccountId); const remainingAccounts = this.getRemainingAccounts({ @@ -5160,13 +5172,13 @@ export class DriftClient { ? order.marketIndex : userAccount.orders.find( (order) => order.orderId === userAccount.nextOrderId - 1 - ).marketIndex; + ).marketIndex; makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [userAccount]; for (const maker of makerInfo) { @@ -5305,9 +5317,8 @@ export class DriftClient { subAccountId?: number ): Promise { orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT }); - const userAccountPublicKey = await this.getUserAccountPublicKey( - subAccountId - ); + const userAccountPublicKey = + await this.getUserAccountPublicKey(subAccountId); const remainingAccounts = this.getRemainingAccounts({ userAccounts: [this.getUserAccount(subAccountId)], @@ -5383,13 +5394,13 @@ export class DriftClient { ? order.marketIndex : userAccount.orders.find( (order) => order.orderId === userAccount.nextOrderId - 1 - ).marketIndex; + ).marketIndex; makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [userAccount]; for (const maker of makerInfo) { @@ -6617,9 +6628,8 @@ export class DriftClient { const prepSettlePnlTx = async () => { if (settlePnl && isVariant(orderParams.marketType, 'perp')) { - const userAccountPublicKey = await this.getUserAccountPublicKey( - subAccountId - ); + const userAccountPublicKey = + await this.getUserAccountPublicKey(subAccountId); const settlePnlIx = await this.settlePNLIx( userAccountPublicKey, @@ -6728,8 +6738,8 @@ export class DriftClient { makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [this.getUserAccount(subAccountId)]; for (const maker of makerInfo) { @@ -6977,11 +6987,11 @@ export class DriftClient { ? this.program.coder.types.encode( 'SignedMsgOrderParamsDelegateMessage', withBuilderDefaults as SignedMsgOrderParamsDelegateMessage - ) + ) : this.program.coder.types.encode( 'SignedMsgOrderParamsMessage', withBuilderDefaults as SignedMsgOrderParamsMessage - ), + ), ]); return buf; } @@ -9651,9 +9661,8 @@ export class DriftClient { } if (initializeStakeAccount) { - const initializeIx = await this.getInitializeInsuranceFundStakeIx( - marketIndex - ); + const initializeIx = + await this.getInitializeInsuranceFundStakeIx(marketIndex); addIfStakeIxs.push(initializeIx); } @@ -10701,7 +10710,7 @@ export class DriftClient { const remainingAccounts = userAccount ? this.getRemainingAccounts({ userAccounts: [userAccount], - }) + }) : undefined; const ix = await this.program.instruction.disableUserHighLeverageMode( From b1b9c4f7fc5bd8d24242dc60973c69cbb9337ebb Mon Sep 17 00:00:00 2001 From: Nick Caradonna Date: Mon, 20 Oct 2025 23:10:17 -0400 Subject: [PATCH 2/3] fix prettier --- sdk/src/driftClient.ts | 55 ++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index e91baae5b..04f29bea1 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -349,8 +349,8 @@ export class DriftClient { this.authoritySubAccountMap = config.authoritySubAccountMap ? config.authoritySubAccountMap : config.subAccountIds - ? new Map([[this.authority.toString(), config.subAccountIds]]) - : new Map(); + ? new Map([[this.authority.toString(), config.subAccountIds]]) + : new Map(); this.includeDelegates = config.includeDelegates ?? false; if (config.accountSubscription?.type === 'polling') { @@ -840,8 +840,8 @@ export class DriftClient { this.authoritySubAccountMap = authoritySubaccountMap ? authoritySubaccountMap : subAccountIds - ? new Map([[this.authority.toString(), subAccountIds]]) - : new Map(); + ? new Map([[this.authority.toString(), subAccountIds]]) + : new Map(); /* Reset user stats account */ if (this.userStats?.isSubscribed) { @@ -3328,7 +3328,7 @@ export class DriftClient { marketIndex, fromSubAccountId, subAccountId - ) + ) : await this.getDepositInstruction( amount, marketIndex, @@ -3336,7 +3336,7 @@ export class DriftClient { subAccountId, false, false - ); + ); if (subAccountId === 0) { if ( @@ -4379,11 +4379,11 @@ export class DriftClient { [orderParams, ...bracketOrdersParams], positionMaxLev, userAccount.subAccountId - ) + ) : this.getPlaceOrdersIx( [orderParams, ...bracketOrdersParams], userAccount.subAccountId - ); + ); ixPromisesForTxs.marketOrderTx = marketOrderTxIxs; @@ -4534,7 +4534,7 @@ export class DriftClient { this.program.programId, this.authority, subAccountId - ) + ) : await this.getUserAccountPublicKey(subAccountId); const remainingAccounts = this.getRemainingAccounts({ @@ -5172,13 +5172,13 @@ export class DriftClient { ? order.marketIndex : userAccount.orders.find( (order) => order.orderId === userAccount.nextOrderId - 1 - ).marketIndex; + ).marketIndex; makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [userAccount]; for (const maker of makerInfo) { @@ -5317,8 +5317,9 @@ export class DriftClient { subAccountId?: number ): Promise { orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT }); - const userAccountPublicKey = - await this.getUserAccountPublicKey(subAccountId); + const userAccountPublicKey = await this.getUserAccountPublicKey( + subAccountId + ); const remainingAccounts = this.getRemainingAccounts({ userAccounts: [this.getUserAccount(subAccountId)], @@ -5394,13 +5395,13 @@ export class DriftClient { ? order.marketIndex : userAccount.orders.find( (order) => order.orderId === userAccount.nextOrderId - 1 - ).marketIndex; + ).marketIndex; makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [userAccount]; for (const maker of makerInfo) { @@ -6628,8 +6629,9 @@ export class DriftClient { const prepSettlePnlTx = async () => { if (settlePnl && isVariant(orderParams.marketType, 'perp')) { - const userAccountPublicKey = - await this.getUserAccountPublicKey(subAccountId); + const userAccountPublicKey = await this.getUserAccountPublicKey( + subAccountId + ); const settlePnlIx = await this.settlePNLIx( userAccountPublicKey, @@ -6738,8 +6740,8 @@ export class DriftClient { makerInfo = Array.isArray(makerInfo) ? makerInfo : makerInfo - ? [makerInfo] - : []; + ? [makerInfo] + : []; const userAccounts = [this.getUserAccount(subAccountId)]; for (const maker of makerInfo) { @@ -6987,11 +6989,11 @@ export class DriftClient { ? this.program.coder.types.encode( 'SignedMsgOrderParamsDelegateMessage', withBuilderDefaults as SignedMsgOrderParamsDelegateMessage - ) + ) : this.program.coder.types.encode( 'SignedMsgOrderParamsMessage', withBuilderDefaults as SignedMsgOrderParamsMessage - ), + ), ]); return buf; } @@ -9661,8 +9663,9 @@ export class DriftClient { } if (initializeStakeAccount) { - const initializeIx = - await this.getInitializeInsuranceFundStakeIx(marketIndex); + const initializeIx = await this.getInitializeInsuranceFundStakeIx( + marketIndex + ); addIfStakeIxs.push(initializeIx); } @@ -10710,7 +10713,7 @@ export class DriftClient { const remainingAccounts = userAccount ? this.getRemainingAccounts({ userAccounts: [userAccount], - }) + }) : undefined; const ix = await this.program.instruction.disableUserHighLeverageMode( From c8f18ce146620a4f92b3d0cbf5305050ce5622b8 Mon Sep 17 00:00:00 2001 From: Nick Caradonna Date: Tue, 21 Oct 2025 15:17:33 -0400 Subject: [PATCH 3/3] use overrides convention --- sdk/src/driftClient.ts | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 04f29bea1..96c04b88d 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -2844,13 +2844,15 @@ export class DriftClient { associatedTokenAccount: PublicKey, subAccountId?: number, reduceOnly = false, - signerAuthority?: PublicKey + overrides?: { + authority?: PublicKey; + } ): Promise { const spotMarketAccount = this.getSpotMarketAccount(marketIndex); const isSolMarket = spotMarketAccount.mint.equals(WRAPPED_SOL_MINT); - const signer = signerAuthority ?? this.wallet.publicKey; + const signer = overrides?.authority ?? this.wallet.publicKey; const createWSOLTokenAccount = isSolMarket && associatedTokenAccount.equals(signer); @@ -2861,7 +2863,7 @@ export class DriftClient { const { ixs, pubkey } = await this.getWrappedSolAccountCreationIxs( amount, true, - signerAuthority + overrides ); associatedTokenAccount = pubkey; @@ -2876,7 +2878,7 @@ export class DriftClient { subAccountId, reduceOnly, true, - signerAuthority + overrides ); instructions.push(depositCollateralIx); @@ -2957,7 +2959,9 @@ export class DriftClient { reduceOnly = false, txParams?: TxParams, initSwiftAccount = false, - signerAuthority?: PublicKey + overrides?: { + authority?: PublicKey; + } ): Promise { const instructions = await this.getDepositTxnIx( amount, @@ -2965,7 +2969,7 @@ export class DriftClient { associatedTokenAccount, subAccountId, reduceOnly, - signerAuthority + overrides ); if (initSwiftAccount) { @@ -3002,7 +3006,7 @@ export class DriftClient { * @param reduceOnly if true, deposit must not increase account risk * @param txParams transaction parameters * @param initSwiftAccount if true, initialize a swift account for the user - * @param signerAuthority the authority to sign the transaction, allowing for any authority to deposit directly to a Drift account + * @param overrides allows overriding authority for the deposit transaction */ public async deposit( amount: BN, @@ -3012,7 +3016,9 @@ export class DriftClient { reduceOnly = false, txParams?: TxParams, initSwiftAccount = false, - signerAuthority?: PublicKey + overrides?: { + authority?: PublicKey; + } ): Promise { const tx = await this.createDepositTxn( amount, @@ -3022,7 +3028,7 @@ export class DriftClient { reduceOnly, txParams, initSwiftAccount, - signerAuthority + overrides ); const { txSig, slot } = await this.sendTransaction(tx, [], this.opts); @@ -3037,7 +3043,9 @@ export class DriftClient { subAccountId?: number, reduceOnly = false, userInitialized = true, - signerAuthority?: PublicKey + overrides?: { + authority?: PublicKey; + } ): Promise { const userAccountPublicKey = await getUserAccountPublicKey( this.program.programId, @@ -3069,6 +3077,7 @@ export class DriftClient { ); } + const authority = overrides?.authority ?? this.wallet.publicKey; const tokenProgram = this.getTokenProgramForSpotMarket(spotMarketAccount); return await this.program.instruction.deposit( marketIndex, @@ -3082,7 +3091,7 @@ export class DriftClient { user: userAccountPublicKey, userStats: this.getUserStatsAccountPublicKey(), userTokenAccount: userTokenAccount, - authority: signerAuthority ?? this.wallet.publicKey, + authority, tokenProgram, }, remainingAccounts, @@ -3103,14 +3112,16 @@ export class DriftClient { public async getWrappedSolAccountCreationIxs( amount: BN, includeRent?: boolean, - signerAuthority?: PublicKey + overrides?: { + authority?: PublicKey; + } ): Promise<{ ixs: anchor.web3.TransactionInstruction[]; /** @deprecated - this array is always going to be empty, in the current implementation */ signers: Signer[]; pubkey: PublicKey; }> { - const authority = signerAuthority ?? this.wallet.publicKey; + const authority = overrides?.authority ?? this.wallet.publicKey; // Generate a random seed for wrappedSolAccount. const seed = Keypair.generate().publicKey.toBase58().slice(0, 32);