Skip to content

Commit

Permalink
sdk: add option to get signed settlepnl tix from a market order (#813)
Browse files Browse the repository at this point in the history
* sdk: add option to get signed settlepnl tix from a market order

* fix lint
  • Loading branch information
lowkeynicc authored Jan 9, 2024
1 parent ec3a1de commit ac2f561
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- sdk: move bracket orders into single instruction
- sdk: add ability to do placeAndTake order with bracket orders attached
- sdk: add option to cancel existing orders in market for place and take order
- sdk: add option to get signed settlePnl tx back from a market order

- program: increase full perp liquidation threshold ([#807](https://github.com/drift-labs/protocol-v2/pull/807))
- program: remove spot fee pool transfer ([#800](https://github.com/drift-labs/protocol-v2/pull/800))
Expand Down
165 changes: 136 additions & 29 deletions sdk/src/driftClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import {
DataAndSlot,
} from './accounts/types';
import { TxSender, TxSigAndSlot } from './tx/types';
import { wrapInTx } from './tx/utils';
import { getSignedTransactionMap, wrapInTx } from './tx/utils';
import {
BASE_PRECISION,
PRICE_PRECISION,
Expand Down Expand Up @@ -2565,11 +2565,13 @@ export class DriftClient {
txParams?: TxParams,
bracketOrdersParams = new Array<OptionalOrderParams>(),
referrerInfo?: ReferrerInfo,
cancelExistingOrders?: boolean
cancelExistingOrders?: boolean,
settlePnl?: boolean
): Promise<{
txSig: TransactionSignature;
signedFillTx?: Transaction;
signedCancelExistingOrdersTx?: Transaction;
signedSettlePnlTx?: Transaction;
}> {
const marketIndex = orderParams.marketIndex;
const orderId = userAccount.nextOrderId;
Expand All @@ -2579,10 +2581,10 @@ export class DriftClient {
userAccount.subAccountId
);

let cancelOrdersIx: TransactionInstruction;
let cancelExistingOrdersTx: Transaction;
/* Cancel open orders in market if requested */
let cancelExistingOrdersTx;
if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
cancelOrdersIx = await this.getCancelOrdersIx(
const cancelOrdersIx = await this.getCancelOrdersIx(
orderParams.marketType,
orderParams.marketIndex,
null,
Expand All @@ -2597,6 +2599,23 @@ export class DriftClient {
);
}

/* Settle PnL after fill if requested */
let settlePnlTx;
if (settlePnl && isVariant(orderParams.marketType, 'perp')) {
const settlePnlIx = await this.settlePNLIx(
userAccountPublicKey,
userAccount,
marketIndex
);

//@ts-ignore
settlePnlTx = await this.buildTransaction(
[settlePnlIx],
txParams,
this.txVersion
);
}

// use versioned transactions if there is a lookup table account and wallet is compatible
if (this.txVersion === 0) {
const versionedMarketOrderTx = await this.buildTransaction(
Expand All @@ -2623,17 +2642,31 @@ export class DriftClient {
0
);

const [
const allPossibleTxs = [
versionedMarketOrderTx,
versionedFillTx,
cancelExistingOrdersTx,
settlePnlTx,
];
const txKeys = [
'signedVersionedMarketOrderTx',
'signedVersionedFillTx',
'signedCancelExistingOrdersTx',
'signedSettlePnlTx',
];

const {
signedVersionedMarketOrderTx,
signedVersionedFillTx,
signedCancelExistingOrdersTx,
] = await this.provider.wallet.signAllTransactions(
[
versionedMarketOrderTx,
versionedFillTx,
cancelExistingOrdersTx,
].filter((tx) => tx !== undefined)
signedSettlePnlTx,
} = await getSignedTransactionMap(
//@ts-ignore
this.provider.wallet,
allPossibleTxs,
txKeys
);

const { txSig, slot } = await this.txSender.sendRawTransaction(
signedVersionedMarketOrderTx.serialize(),
this.opts
Expand All @@ -2646,6 +2679,8 @@ export class DriftClient {
signedFillTx: signedVersionedFillTx,
// @ts-ignore
signedCancelExistingOrdersTx,
// @ts-ignore
signedSettlePnlTx,
};
} else {
const marketOrderTx = wrapInTx(
Expand All @@ -2667,12 +2702,33 @@ export class DriftClient {
cancelExistingOrdersTx.feePayer = userAccount.authority;
}

const [signedMarketOrderTx, signedCancelExistingOrdersTx] =
await this.provider.wallet.signAllTransactions(
[marketOrderTx, cancelExistingOrdersTx].filter(
(tx) => tx !== undefined
)
);
if (settlePnlTx) {
settlePnlTx.recentBlockhash = currentBlockHash;
settlePnlTx.feePayer = userAccount.authority;
}

const allPossibleTxs = [
marketOrderTx,
cancelExistingOrdersTx,
settlePnlTx,
];
const txKeys = [
'signedMarketOrderTx',
'signedCancelExistingOrdersTx',
'signedSettlePnlTx',
];

const {
signedMarketOrderTx,
signedCancelExistingOrdersTx,
signedSettlePnlTx,
} = await getSignedTransactionMap(
//@ts-ignore
this.provider.wallet,
allPossibleTxs,
txKeys
);

const { txSig, slot } = await this.sendTransaction(
signedMarketOrderTx,
[],
Expand All @@ -2681,7 +2737,14 @@ export class DriftClient {
);
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);

return { txSig, signedFillTx: undefined, signedCancelExistingOrdersTx };
return {
txSig,
signedFillTx: undefined,
//@ts-ignore
signedCancelExistingOrdersTx,
//@ts-ignore
signedSettlePnlTx,
};
}
}

Expand Down Expand Up @@ -4330,13 +4393,14 @@ export class DriftClient {
bracketOrdersParams = new Array<OptionalOrderParams>(),
txParams?: TxParams,
subAccountId?: number,
cancelExistingOrders?: boolean
cancelExistingOrders?: boolean,
settlePnl?: boolean
): Promise<{
txSig: TransactionSignature;
signedCancelExistingOrdersTx?: Transaction;
signedSettlePnlTx?: Transaction;
}> {
let signedCancelExistingOrdersTx: Transaction;

let cancelExistingOrdersTx: Transaction;
if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
const cancelOrdersIx = await this.getCancelOrdersIx(
orderParams.marketType,
Expand All @@ -4345,15 +4409,32 @@ export class DriftClient {
subAccountId
);

const cancelExistingOrdersTx = await this.buildTransaction(
//@ts-ignore
cancelExistingOrdersTx = await this.buildTransaction(
[cancelOrdersIx],
txParams,
this.txVersion
);
}

// @ts-ignore
signedCancelExistingOrdersTx = await this.provider.wallet.signTransaction(
cancelExistingOrdersTx
/* Settle PnL after fill if requested */
let settlePnlTx: Transaction;
if (settlePnl && isVariant(orderParams.marketType, 'perp')) {
const userAccountPublicKey = await this.getUserAccountPublicKey(
subAccountId
);

const settlePnlIx = await this.settlePNLIx(
userAccountPublicKey,
this.getUserAccount(subAccountId),
orderParams.marketIndex
);

//@ts-ignore
settlePnlTx = await this.buildTransaction(
[settlePnlIx],
txParams,
this.txVersion
);
}

Expand All @@ -4376,14 +4457,40 @@ export class DriftClient {
ixs.push(bracketOrdersIx);
}

const placeAndTakeTx = await this.buildTransaction(ixs, txParams);

const allPossibleTxs = [
placeAndTakeTx,
cancelExistingOrdersTx,
settlePnlTx,
];
const txKeys = [
'signedPlaceAndTakeTx',
'signedCancelExistingOrdersTx',
'signedSettlePnlTx',
];

const {
signedPlaceAndTakeTx,
signedCancelExistingOrdersTx,
signedSettlePnlTx,
} = await getSignedTransactionMap(
//@ts-ignore
this.provider.wallet,
allPossibleTxs,
txKeys
);

const { txSig, slot } = await this.sendTransaction(
await this.buildTransaction(ixs, txParams),
signedPlaceAndTakeTx,
[],
this.opts
this.opts,
true
);
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);

return { txSig, signedCancelExistingOrdersTx };
//@ts-ignore
return { txSig, signedCancelExistingOrdersTx, signedSettlePnlTx };
}

public async getPlaceAndTakePerpOrderIx(
Expand Down
32 changes: 32 additions & 0 deletions sdk/src/tx/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Wallet } from '@coral-xyz/anchor';
import {
Transaction,
TransactionInstruction,
ComputeBudgetProgram,
VersionedTransaction,
} from '@solana/web3.js';

const COMPUTE_UNITS_DEFAULT = 200_000;
Expand Down Expand Up @@ -30,3 +32,33 @@ export function wrapInTx(

return tx.add(instruction);
}

/* Helper function for signing multiple transactions where some may be undefined and mapping the output */
export async function getSignedTransactionMap(
wallet: Wallet,
txsToSign: (Transaction | VersionedTransaction | undefined)[],
keys: string[]
): Promise<{ [key: string]: Transaction | VersionedTransaction | undefined }> {
const signedTxMap: {
[key: string]: Transaction | VersionedTransaction | undefined;
} = {};

const keysWithTx = [];
txsToSign.forEach((tx, index) => {
if (tx == undefined) {
signedTxMap[keys[index]] = undefined;
} else {
keysWithTx.push(keys[index]);
}
});

const signedTxs = await wallet.signAllTransactions(
txsToSign.filter((tx) => tx !== undefined)
);

signedTxs.forEach((signedTx, index) => {
signedTxMap[keysWithTx[index]] = signedTx;
});

return signedTxMap;
}

0 comments on commit ac2f561

Please sign in to comment.