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
121 changes: 90 additions & 31 deletions sdk/src/driftClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ export class DriftClient {

if (!this.marketLookupTables) {
console.log('Market lookup table address not set');
return;
return [];
}

const lookupTableAccountResults = await Promise.all(
Expand All @@ -785,9 +785,12 @@ export class DriftClient {
)
);

const lookupTableAccounts = lookupTableAccountResults.map(
(result) => result.value
);
// Filter out null values - lookup tables may not exist on-chain
const lookupTableAccounts = lookupTableAccountResults
.map((result) => result.value)
.filter(
(account): account is AddressLookupTableAccount => account !== null
);
this.lookupTableAccounts = lookupTableAccounts;

return lookupTableAccounts;
Expand Down Expand Up @@ -1129,15 +1132,23 @@ export class DriftClient {
return [txSig, userAccountPublicKey];
}

async getInitializeUserStatsIx(): Promise<TransactionInstruction> {
async getInitializeUserStatsIx(overrides?: {
/**
* Optional external wallet to use as payer. If provided, this wallet will pay
* for the account creation instead of the default wallet.
*/
externalWallet?: PublicKey;
}): Promise<TransactionInstruction> {
const payer = overrides?.externalWallet ?? this.wallet.publicKey;
const authority = this.authority;
return await this.program.instruction.initializeUserStats({
accounts: {
userStats: getUserStatsAccountPublicKey(
this.program.programId,
this.wallet.publicKey // only allow payer to initialize own user stats account
authority
),
authority: this.wallet.publicKey,
payer: this.wallet.publicKey,
authority,
payer,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
systemProgram: anchor.web3.SystemProgram.programId,
state: await this.getStatePublicKey(),
Expand Down Expand Up @@ -1166,8 +1177,16 @@ export class DriftClient {

async getInitializeSignedMsgUserOrdersAccountIx(
authority: PublicKey,
numOrders: number
numOrders: number,
overrides?: {
/**
* Optional external wallet to use as payer. If provided, this wallet will pay
* for the account creation instead of the default wallet.
*/
externalWallet?: PublicKey;
}
): Promise<[PublicKey, TransactionInstruction]> {
const payer = overrides?.externalWallet ?? this.wallet.publicKey;
const signedMsgUserAccountPublicKey = getSignedMsgUserAccountPublicKey(
this.program.programId,
authority
Expand All @@ -1177,7 +1196,7 @@ export class DriftClient {
accounts: {
signedMsgUserOrders: signedMsgUserAccountPublicKey,
authority,
payer: this.wallet.publicKey,
payer,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
systemProgram: anchor.web3.SystemProgram.programId,
},
Expand Down Expand Up @@ -1592,11 +1611,19 @@ export class DriftClient {
private async getInitializeUserInstructions(
subAccountId = 0,
name?: string,
referrerInfo?: ReferrerInfo
referrerInfo?: ReferrerInfo,
overrides?: {
externalWallet?: PublicKey;
}
): Promise<[PublicKey, TransactionInstruction]> {
// Use external wallet as payer if provided, otherwise use the wallet
const payer = overrides?.externalWallet ?? this.wallet.publicKey;
// The authority is the account owner (this.authority), not the payer
const accountAuthority = this.authority;

const userAccountPublicKey = await getUserAccountPublicKey(
this.program.programId,
this.wallet.publicKey,
accountAuthority,
subAccountId
);

Expand All @@ -1618,7 +1645,7 @@ export class DriftClient {
if (!state.whitelistMint.equals(PublicKey.default)) {
const associatedTokenPublicKey = await getAssociatedTokenAddress(
state.whitelistMint,
this.wallet.publicKey
payer
);
remainingAccounts.push({
pubkey: associatedTokenPublicKey,
Expand All @@ -1641,8 +1668,8 @@ export class DriftClient {
accounts: {
user: userAccountPublicKey,
userStats: this.getUserStatsAccountPublicKey(),
authority: this.wallet.publicKey,
payer: this.wallet.publicKey,
authority: accountAuthority,
payer: payer,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
systemProgram: anchor.web3.SystemProgram.programId,
state: await this.getStatePublicKey(),
Expand Down Expand Up @@ -3315,7 +3342,14 @@ export class DriftClient {
referrerInfo?: ReferrerInfo,
donateAmount?: BN,
customMaxMarginRatio?: number,
poolId?: number
poolId?: number,
overrides?: {
/**
* Optional external wallet to deposit from. If provided, the deposit will be made
* from this wallet instead of the user's authority wallet.
*/
externalWallet?: PublicKey;
}
): Promise<{
ixs: TransactionInstruction[];
userAccountPublicKey: PublicKey;
Expand All @@ -3326,17 +3360,20 @@ export class DriftClient {
await this.getInitializeUserInstructions(
subAccountId,
name,
referrerInfo
referrerInfo,
overrides
);

// Check signed message orders account for the actual authority (account owner)
const isSignedMsgUserOrdersAccountInitialized =
await this.isSignedMsgUserOrdersAccountInitialized(this.wallet.publicKey);
await this.isSignedMsgUserOrdersAccountInitialized(this.authority);

if (!isSignedMsgUserOrdersAccountInitialized) {
const [, initializeSignedMsgUserOrdersAccountIx] =
await this.getInitializeSignedMsgUserOrdersAccountIx(
this.wallet.publicKey,
8
this.authority,
8,
overrides
);
ixs.push(initializeSignedMsgUserOrdersAccountIx);
}
Expand All @@ -3345,7 +3382,8 @@ export class DriftClient {

const isSolMarket = spotMarket.mint.equals(WRAPPED_SOL_MINT);

const authority = this.wallet.publicKey;
// Use external wallet for deposit source if provided, otherwise use the wallet
const depositSource = overrides?.externalWallet ?? this.wallet.publicKey;

const isFromSubaccount =
fromSubAccountId !== null &&
Expand All @@ -3356,7 +3394,7 @@ export class DriftClient {

const createWSOLTokenAccount =
(isSolMarket &&
userTokenAccount.equals(authority) &&
userTokenAccount.equals(depositSource) &&
!isFromSubaccount) ||
!donateAmount.eq(ZERO);

Expand All @@ -3365,7 +3403,13 @@ export class DriftClient {
let wsolTokenAccount: PublicKey;
if (createWSOLTokenAccount) {
const { ixs: startIxs, pubkey } =
await this.getWrappedSolAccountCreationIxs(wSolAmount, true);
await this.getWrappedSolAccountCreationIxs(
wSolAmount,
true,
overrides?.externalWallet
? { authority: overrides.externalWallet }
: undefined
);

wsolTokenAccount = pubkey;

Expand Down Expand Up @@ -3408,14 +3452,17 @@ export class DriftClient {
userTokenAccount,
subAccountId,
false,
false
false,
overrides?.externalWallet
? { authority: overrides.externalWallet }
: undefined
);

if (subAccountId === 0) {
if (
!(await this.checkIfAccountExists(this.getUserStatsAccountPublicKey()))
) {
ixs.push(await this.getInitializeUserStatsIx());
ixs.push(await this.getInitializeUserStatsIx(overrides));
}
}
ixs.push(initializeUserAccountIx);
Expand Down Expand Up @@ -3446,12 +3493,13 @@ export class DriftClient {
}

// Close the wrapped sol account at the end of the transaction
// Return funds to the deposit source (external wallet if provided)
if (createWSOLTokenAccount) {
ixs.push(
createCloseAccountInstruction(
wsolTokenAccount,
authority,
authority,
depositSource,
depositSource,
[]
)
);
Expand All @@ -3474,7 +3522,10 @@ export class DriftClient {
donateAmount?: BN,
txParams?: TxParams,
customMaxMarginRatio?: number,
poolId?: number
poolId?: number,
overrides?: {
externalWallet?: PublicKey;
}
): Promise<[Transaction | VersionedTransaction, PublicKey]> {
const { ixs, userAccountPublicKey } =
await this.createInitializeUserAccountAndDepositCollateralIxs(
Expand All @@ -3487,7 +3538,8 @@ export class DriftClient {
referrerInfo,
donateAmount,
customMaxMarginRatio,
poolId
poolId,
overrides
);

const tx = await this.buildTransaction(ixs, txParams);
Expand All @@ -3506,6 +3558,9 @@ export class DriftClient {
* @param referrerInfo
* @param donateAmount
* @param txParams
* @param customMaxMarginRatio
* @param poolId
* @param overrides - Optional overrides including externalWallet for depositing from a different wallet
* @returns
*/
public async initializeUserAccountAndDepositCollateral(
Expand All @@ -3519,7 +3574,10 @@ export class DriftClient {
donateAmount?: BN,
txParams?: TxParams,
customMaxMarginRatio?: number,
poolId?: number
poolId?: number,
overrides?: {
externalWallet?: PublicKey;
}
): Promise<[TransactionSignature, PublicKey]> {
const [tx, userAccountPublicKey] =
await this.createInitializeUserAccountAndDepositCollateral(
Expand All @@ -3533,7 +3591,8 @@ export class DriftClient {
donateAmount,
txParams,
customMaxMarginRatio,
poolId
poolId,
overrides
);
const additionalSigners: Array<Signer> = [];

Expand Down
7 changes: 6 additions & 1 deletion sdk/src/tx/txHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,14 @@ export class TxHandler {

const marketLookupTables = await fetchAllMarketLookupTableAccounts();

lookupTables = lookupTables
// Combine and filter out any null/undefined lookup tables
const combinedLookupTables = lookupTables
? [...lookupTables, ...marketLookupTables]
: marketLookupTables;
lookupTables = combinedLookupTables.filter(
(table): table is AddressLookupTableAccount =>
table !== null && table !== undefined
);

// # Collect and process Tx Params
let baseTxParams: BaseTxParams = {
Expand Down
9 changes: 7 additions & 2 deletions sdk/src/tx/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ export const getSizeOfTransaction = (
.reduce((a, b) => a + b, 0);

let numberOfAddressLookups = 0;
if (addressLookupTables.length > 0) {
const lookupTableAddresses = addressLookupTables
// Filter out null/undefined lookup tables before accessing .state
const validLookupTables = addressLookupTables.filter(
(table): table is AddressLookupTableAccount =>
table !== null && table !== undefined
);
if (validLookupTables.length > 0) {
const lookupTableAddresses = validLookupTables
.map((addressLookupTable) =>
addressLookupTable.state.addresses.map((address) => address.toBase58())
)
Expand Down
Loading