Skip to content
Merged
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
13 changes: 12 additions & 1 deletion common-ts/src/drift/Drift/clients/CentralServerDrift/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ export class CentralServerDrift {
fromSubAccountId?: number;
customMaxMarginRatio?: number;
txParams?: TxParams;
/**
* Optional external wallet to deposit from. If provided, the deposit will be made
* from this wallet instead of the authority wallet.
*/
externalWallet?: PublicKey;
}
): Promise<{
transaction: VersionedTransaction | Transaction;
Expand All @@ -381,8 +386,10 @@ export class CentralServerDrift {
const originalWallet = this._driftClient.wallet;
const originalAuthority = this._driftClient.authority;

// Use external wallet for transaction signing context if provided
const walletPublicKey = options?.externalWallet ?? authority;
const authorityWallet = {
publicKey: authority,
publicKey: walletPublicKey,
signTransaction: () =>
Promise.reject('This is a placeholder - do not sign with this wallet'),
signAllTransactions: () =>
Expand All @@ -395,6 +402,9 @@ export class CentralServerDrift {
this._driftClient.provider.wallet = authorityWallet;
this._driftClient.txHandler.updateWallet(authorityWallet);
this._driftClient.authority = authority;
// Clear userStatsAccountPublicKey cache so it's recalculated for the new authority
// @ts-ignore - accessing private property for cache invalidation
this._driftClient.userStatsAccountPublicKey = undefined;

return await createUserAndDepositCollateralBaseTxn({
driftClient: this._driftClient,
Expand All @@ -408,6 +418,7 @@ export class CentralServerDrift {
fromSubAccountId: options?.fromSubAccountId,
customMaxMarginRatio: options?.customMaxMarginRatio,
txParams: options?.txParams ?? this.getTxParams(),
externalWallet: options?.externalWallet,
});
} finally {
this._driftClient.wallet = originalWallet;
Expand Down
20 changes: 16 additions & 4 deletions common-ts/src/drift/base/actions/user/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ interface CreateUserAndDepositCollateralBaseIxsParams {
fromSubAccountId?: number;
customMaxMarginRatio?: number;
delegate?: PublicKey;
/**
* Optional external wallet to deposit from. If provided, the deposit will be made
* from this wallet instead of the authority wallet.
*/
externalWallet?: PublicKey;
}

/**
Expand All @@ -53,6 +58,7 @@ interface CreateUserAndDepositCollateralBaseIxsParams {
* @param fromSubAccountId - Optional sub-account ID to transfer funds from
* @param customMaxMarginRatio - Optional custom maximum margin ratio for the account
* @param delegate - Optional delegate public key for the account. Immediately assigns this as the delegate of the account.
* @param externalWallet - Optional external wallet to deposit from (instead of authority wallet)
*
* @returns Promise resolving to an object containing:
* - subAccountId: The ID of the newly created sub-account
Expand All @@ -71,6 +77,7 @@ export const createUserAndDepositCollateralBaseIxs = async ({
fromSubAccountId,
customMaxMarginRatio,
delegate,
externalWallet,
}: CreateUserAndDepositCollateralBaseIxsParams): Promise<{
subAccountId: number;
userAccountPublicKey: PublicKey;
Expand All @@ -86,10 +93,12 @@ export const createUserAndDepositCollateralBaseIxs = async ({
? getTokenProgramForSpotMarket(spotMarketAccount)
: undefined;

// Use external wallet for token address if provided, otherwise use authority
const depositSourceWallet = externalWallet ?? authority;
const associatedDepositTokenAddressPromise =
getTokenAddressForDepositAndWithdraw(
spotMarketConfig.mint,
authority,
depositSourceWallet,
tokenProgram
);
const referrerNameAccountPromise: Promise<ReferrerNameAccount | undefined> =
Expand Down Expand Up @@ -129,7 +138,6 @@ export const createUserAndDepositCollateralBaseIxs = async ({
}
: undefined;

const ixs = [];
const { ixs: createAndDepositIxs, userAccountPublicKey } =
await driftClient.createInitializeUserAccountAndDepositCollateralIxs(
amount,
Expand All @@ -141,9 +149,10 @@ export const createUserAndDepositCollateralBaseIxs = async ({
referrerInfo,
ZERO,
customMaxMarginRatio,
poolId
poolId,
externalWallet ? { externalWallet } : undefined
);
ixs.push(...createAndDepositIxs);
const ixs: TransactionInstruction[] = [...createAndDepositIxs];

const nextSubAccountPublicKey = getUserAccountPublicKeySync(
driftClient.program.programId,
Expand Down Expand Up @@ -192,6 +201,7 @@ interface CreateUserAndDepositCollateralBaseTxnParams
* @param fromSubAccountId - Optional sub-account ID to transfer funds from
* @param customMaxMarginRatio - Optional custom maximum margin ratio for the account
* @param txParams - Transaction parameters for building the transaction (compute units, priority fees, etc.)
* @param externalWallet - Optional external wallet to deposit from (instead of authority wallet)
*
* @returns Promise resolving to an object containing:
* - transaction: The built transaction ready for signing (Transaction or VersionedTransaction)
Expand All @@ -210,6 +220,7 @@ export const createUserAndDepositCollateralBaseTxn = async ({
fromSubAccountId,
customMaxMarginRatio,
txParams,
externalWallet,
}: CreateUserAndDepositCollateralBaseTxnParams): Promise<{
transaction: Transaction | VersionedTransaction;
userAccountPublicKey: PublicKey;
Expand All @@ -227,6 +238,7 @@ export const createUserAndDepositCollateralBaseTxn = async ({
poolId,
fromSubAccountId,
customMaxMarginRatio,
externalWallet,
});

const tx = await driftClient.buildTransaction(ixs, txParams);
Expand Down
41 changes: 35 additions & 6 deletions common-ts/src/drift/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
PRICE_PRECISION,
MainnetSpotMarkets,
DevnetSpotMarkets,
DriftEnv,
} from '@drift-labs/sdk';
import { sign } from 'tweetnacl';
import { CentralServerDrift } from './Drift/clients/CentralServerDrift';
Expand Down Expand Up @@ -56,6 +57,7 @@ dotenv.config({ path: path.resolve(__dirname, '.env') });
* ts-node cli.ts swap --userAccount=11111111111111111111111111111111 --fromMarketIndex=1 --toMarketIndex=0 --fromAmount=1.5 --slippage=100 --swapMode=ExactIn
* ts-node cli.ts swap --userAccount=11111111111111111111111111111111 --fromMarketIndex=1 --toMarketIndex=0 --toAmount=150 --slippage=100 --swapMode=ExactOut
* ts-node cli.ts createUserAndDeposit --marketIndex=0 --amount=100 --accountName="Primary"
* ts-node cli.ts createUserAndDeposit --marketIndex=0 --amount=100 --fromWallet=22222222222222222222222222222222 --forAuthority=33333333333333333333333333333333
*/

// Shared configuration
Expand Down Expand Up @@ -242,10 +244,12 @@ async function initializeCentralServerDrift(): Promise<void> {
console.log(`✅ RPC Endpoint: ${process.env.ENDPOINT}\n`);

// Initialize CentralServerDrift
console.log('🏗️ Initializing CentralServerDrift...');
const driftEnv = (process.env.DRIFT_ENV as DriftEnv) ?? 'devnet';
console.log(`🏗️ Initializing CentralServerDrift... (${driftEnv})`);
const rpcEndpoint = process.env.ENDPOINT as string;
centralServerDrift = new CentralServerDrift({
solanaRpcEndpoint: process.env.ENDPOINT as string,
driftEnv: 'mainnet-beta', // Change to 'devnet' for devnet testing
solanaRpcEndpoint: rpcEndpoint,
driftEnv,
supportedPerpMarkets: [0, 1, 2], // SOL, BTC, ETH
supportedSpotMarkets: [0, 1], // USDC, SOL
additionalDriftClientConfig: {
Expand Down Expand Up @@ -380,11 +384,20 @@ async function createUserAndDepositCommand(args: CliArgs): Promise<void> {
const customMaxMarginRatioArg = args.customMaxMarginRatio as
| string
| undefined;
const fromWallet = args.fromWallet as string | undefined;
const forAuthority = args.forAuthority as string | undefined;

if (!marketIndexArg || !amountArg) {
throw new Error('Required arguments: --marketIndex, --amount');
}

// If external wallet is provided, authority must also be specified
if (fromWallet && !forAuthority) {
throw new Error(
'When using --fromWallet, you must also specify --forAuthority (the account owner)'
);
}

const marketIndex = parseInt(marketIndexArg, 10);
if (isNaN(marketIndex)) {
throw new Error(`Invalid marketIndex: ${marketIndexArg}`);
Expand All @@ -399,6 +412,7 @@ async function createUserAndDepositCommand(args: CliArgs): Promise<void> {
poolId?: number;
fromSubAccountId?: number;
customMaxMarginRatio?: number;
externalWallet?: PublicKey;
} = {};

if (referrerName) {
Expand Down Expand Up @@ -435,8 +449,17 @@ async function createUserAndDepositCommand(args: CliArgs): Promise<void> {
options.customMaxMarginRatio = customMaxMarginRatio;
}

// Parse external wallet and authority if provided
const externalWallet = fromWallet ? new PublicKey(fromWallet) : undefined;
const authority = forAuthority
? new PublicKey(forAuthority)
: wallet.publicKey;
if (externalWallet) {
options.externalWallet = externalWallet;
}

console.log('--- 🆕 Create User & Deposit Transaction ---');
console.log(`🔑 Authority (wallet): ${wallet.publicKey.toString()}`);
console.log(`🔑 Authority (account owner): ${authority.toString()}`);
console.log(`🏪 Spot Market Index: ${marketIndex}`);
console.log(
`💰 Initial Deposit: ${amountArg} (${amountBN.toString()} raw units)`
Expand All @@ -456,11 +479,14 @@ async function createUserAndDepositCommand(args: CliArgs): Promise<void> {
if (options.customMaxMarginRatio !== undefined) {
console.log(`📐 Custom Max Margin Ratio: ${options.customMaxMarginRatio}`);
}
if (externalWallet) {
console.log(`💼 From External Wallet: ${externalWallet.toBase58()}`);
}

const hasOptions = Object.keys(options).length > 0;
const { transaction, userAccountPublicKey, subAccountId } =
await centralServerDrift.getCreateAndDepositTxn(
wallet.publicKey,
authority,
amountBN,
marketIndex,
hasOptions ? options : undefined
Expand Down Expand Up @@ -938,11 +964,14 @@ function showUsage(): void {

console.log('🆕 createUserAndDeposit');
console.log(
' ts-node cli.ts createUserAndDeposit --marketIndex=<num> --amount=<num> [--accountName=<string>] [--referrerName=<string>] [--poolId=<num>] [--fromSubAccountId=<num>] [--customMaxMarginRatio=<num>]'
' ts-node cli.ts createUserAndDeposit --marketIndex=<num> --amount=<num> [--accountName=<string>] [--referrerName=<string>] [--poolId=<num>] [--fromSubAccountId=<num>] [--customMaxMarginRatio=<num>] [--fromWallet=<pubkey> --forAuthority=<pubkey>]'
);
console.log(
' Example: ts-node cli.ts createUserAndDeposit --marketIndex=0 --amount=100 --accountName="Primary"'
);
console.log(
' Example (external wallet): ts-node cli.ts createUserAndDeposit --marketIndex=0 --amount=100 --fromWallet=22222222222222222222222222222222 --forAuthority=33333333333333333333333333333333'
);
console.log('');

console.log('💸 withdraw');
Expand Down
Loading