@drift-labs/sdk 0.1.23-master.0 → 0.1.23-master.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RetryTxSender = void 0;
16
+ const assert_1 = __importDefault(require("assert"));
17
+ const bs58_1 = __importDefault(require("bs58"));
18
+ const DEFAULT_TIMEOUT = 35000;
19
+ const DEFAULT_RETRY = 8000;
20
+ class RetryTxSender {
21
+ constructor(provider, timeout, retrySleep) {
22
+ this.provider = provider;
23
+ this.timeout = timeout !== null && timeout !== void 0 ? timeout : DEFAULT_TIMEOUT;
24
+ this.retrySleep = retrySleep !== null && retrySleep !== void 0 ? retrySleep : DEFAULT_RETRY;
25
+ }
26
+ send(tx, additionalSigners, opts) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ if (additionalSigners === undefined) {
29
+ additionalSigners = [];
30
+ }
31
+ if (opts === undefined) {
32
+ opts = this.provider.opts;
33
+ }
34
+ yield this.prepareTx(tx, additionalSigners, opts);
35
+ const rawTransaction = tx.serialize();
36
+ const startTime = this.getTimestamp();
37
+ const txid = yield this.provider.connection.sendRawTransaction(rawTransaction, opts);
38
+ let done = false;
39
+ const resolveReference = {
40
+ resolve: undefined,
41
+ };
42
+ const stopWaiting = () => {
43
+ done = true;
44
+ if (resolveReference.resolve) {
45
+ resolveReference.resolve();
46
+ }
47
+ };
48
+ (() => __awaiter(this, void 0, void 0, function* () {
49
+ while (!done && this.getTimestamp() - startTime < this.timeout) {
50
+ yield this.sleep(resolveReference);
51
+ if (!done) {
52
+ this.provider.connection
53
+ .sendRawTransaction(rawTransaction, opts)
54
+ .catch((e) => {
55
+ console.error(e);
56
+ stopWaiting();
57
+ });
58
+ }
59
+ }
60
+ }))();
61
+ try {
62
+ yield this.confirmTransaction(txid, opts.commitment);
63
+ }
64
+ catch (e) {
65
+ console.error(e);
66
+ throw e;
67
+ }
68
+ finally {
69
+ stopWaiting();
70
+ }
71
+ return txid;
72
+ });
73
+ }
74
+ prepareTx(tx, additionalSigners, opts) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ tx.feePayer = this.provider.wallet.publicKey;
77
+ tx.recentBlockhash = (yield this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
78
+ yield this.provider.wallet.signTransaction(tx);
79
+ additionalSigners
80
+ .filter((s) => s !== undefined)
81
+ .forEach((kp) => {
82
+ tx.partialSign(kp);
83
+ });
84
+ return tx;
85
+ });
86
+ }
87
+ confirmTransaction(signature, commitment) {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ let decodedSignature;
90
+ try {
91
+ decodedSignature = bs58_1.default.decode(signature);
92
+ }
93
+ catch (err) {
94
+ throw new Error('signature must be base58 encoded: ' + signature);
95
+ }
96
+ (0, assert_1.default)(decodedSignature.length === 64, 'signature has invalid length');
97
+ const start = Date.now();
98
+ const subscriptionCommitment = commitment || this.provider.opts.commitment;
99
+ let subscriptionId;
100
+ let response = null;
101
+ const confirmPromise = new Promise((resolve, reject) => {
102
+ try {
103
+ subscriptionId = this.provider.connection.onSignature(signature, (result, context) => {
104
+ subscriptionId = undefined;
105
+ response = {
106
+ context,
107
+ value: result,
108
+ };
109
+ resolve(null);
110
+ }, subscriptionCommitment);
111
+ }
112
+ catch (err) {
113
+ reject(err);
114
+ }
115
+ });
116
+ try {
117
+ yield this.promiseTimeout(confirmPromise, this.timeout);
118
+ }
119
+ finally {
120
+ if (subscriptionId) {
121
+ this.provider.connection.removeSignatureListener(subscriptionId);
122
+ }
123
+ }
124
+ if (response === null) {
125
+ const duration = (Date.now() - start) / 1000;
126
+ throw new Error(`Transaction was not confirmed in ${duration.toFixed(2)} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`);
127
+ }
128
+ return response;
129
+ });
130
+ }
131
+ getTimestamp() {
132
+ return new Date().getTime();
133
+ }
134
+ sleep(reference) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ return new Promise((resolve) => {
137
+ reference.resolve = resolve;
138
+ setTimeout(resolve, this.retrySleep);
139
+ });
140
+ });
141
+ }
142
+ promiseTimeout(promise, timeoutMs) {
143
+ let timeoutId;
144
+ const timeoutPromise = new Promise((resolve) => {
145
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
146
+ });
147
+ return Promise.race([promise, timeoutPromise]).then((result) => {
148
+ clearTimeout(timeoutId);
149
+ return result;
150
+ });
151
+ }
152
+ }
153
+ exports.RetryTxSender = RetryTxSender;
package/lib/tx/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { Provider } from '@project-serum/anchor';
1
2
  import { ConfirmOptions, Signer, Transaction, TransactionSignature } from '@solana/web3.js';
2
3
  export interface TxSender {
4
+ provider: Provider;
3
5
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
4
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.23-master.0",
3
+ "version": "0.1.23-master.2",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -52,7 +52,6 @@ import {
52
52
  ClearingHouseAccountTypes,
53
53
  } from './accounts/types';
54
54
  import { TxSender } from './tx/types';
55
- import { DefaultTxSender } from './tx/defaultTxSender';
56
55
  import { wrapInTx } from './tx/utils';
57
56
  import {
58
57
  getClearingHouse,
@@ -250,12 +249,13 @@ export class ClearingHouse {
250
249
  this.program.programId,
251
250
  newProvider
252
251
  );
253
- const newTxSender = new DefaultTxSender(newProvider);
252
+
253
+ // Update provider for txSender with new wallet details
254
+ this.txSender.provider = newProvider;
254
255
 
255
256
  this.wallet = newWallet;
256
257
  this.provider = newProvider;
257
258
  this.program = newProgram;
258
- this.txSender = newTxSender;
259
259
  this.userAccountPublicKey = undefined;
260
260
  this.userAccount = undefined;
261
261
  }
@@ -1321,6 +1321,52 @@
1321
1321
  }
1322
1322
  ]
1323
1323
  },
1324
+ {
1325
+ "name": "initializeUserOrdersWithExplicitPayer",
1326
+ "accounts": [
1327
+ {
1328
+ "name": "user",
1329
+ "isMut": false,
1330
+ "isSigner": false
1331
+ },
1332
+ {
1333
+ "name": "userOrders",
1334
+ "isMut": true,
1335
+ "isSigner": false
1336
+ },
1337
+ {
1338
+ "name": "state",
1339
+ "isMut": false,
1340
+ "isSigner": false
1341
+ },
1342
+ {
1343
+ "name": "authority",
1344
+ "isMut": false,
1345
+ "isSigner": true
1346
+ },
1347
+ {
1348
+ "name": "payer",
1349
+ "isMut": true,
1350
+ "isSigner": true
1351
+ },
1352
+ {
1353
+ "name": "rent",
1354
+ "isMut": false,
1355
+ "isSigner": false
1356
+ },
1357
+ {
1358
+ "name": "systemProgram",
1359
+ "isMut": false,
1360
+ "isSigner": false
1361
+ }
1362
+ ],
1363
+ "args": [
1364
+ {
1365
+ "name": "userOrdersNonce",
1366
+ "type": "u8"
1367
+ }
1368
+ ]
1369
+ },
1324
1370
  {
1325
1371
  "name": "deleteUser",
1326
1372
  "accounts": [
@@ -3667,6 +3713,23 @@
3667
3713
  ]
3668
3714
  }
3669
3715
  },
3716
+ {
3717
+ "name": "LiquidationType",
3718
+ "type": {
3719
+ "kind": "enum",
3720
+ "variants": [
3721
+ {
3722
+ "name": "NONE"
3723
+ },
3724
+ {
3725
+ "name": "PARTIAL"
3726
+ },
3727
+ {
3728
+ "name": "FULL"
3729
+ }
3730
+ ]
3731
+ }
3732
+ },
3670
3733
  {
3671
3734
  "name": "OracleSource",
3672
3735
  "type": {
@@ -4064,6 +4127,11 @@
4064
4127
  "code": 6055,
4065
4128
  "name": "UserOrderIdAlreadyInUse",
4066
4129
  "msg": "User Order Id Already In Use"
4130
+ },
4131
+ {
4132
+ "code": 6056,
4133
+ "name": "NoPositionsLiquidatable",
4134
+ "msg": "No positions liquidatable"
4067
4135
  }
4068
4136
  ]
4069
4137
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './types';
7
7
  export * from './constants/markets';
8
8
  export * from './accounts/webSocketClearingHouseAccountSubscriber';
9
9
  export * from './accounts/bulkAccountLoader';
10
+ export * from './accounts/bulkUserSubscription';
10
11
  export * from './accounts/pollingClearingHouseAccountSubscriber';
11
12
  export * from './accounts/pollingTokenAccountSubscriber';
12
13
  export * from './accounts/types';
@@ -31,6 +32,7 @@ export * from './types';
31
32
  export * from './math/utils';
32
33
  export * from './config';
33
34
  export * from './constants/numericConstants';
35
+ export * from './tx/retryTxSender';
34
36
  export * from './util/computeUnits';
35
37
  export * from './util/tps';
36
38
 
@@ -47,7 +47,8 @@ export function calculateBaseAssetValue(
47
47
  return newQuoteAssetReserve
48
48
  .sub(market.amm.quoteAssetReserve)
49
49
  .mul(market.amm.pegMultiplier)
50
- .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
50
+ .div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
51
+ .add(ONE);
51
52
  }
52
53
  }
53
54
 
@@ -74,7 +75,7 @@ export function calculatePositionPNL(
74
75
  if (marketPosition.baseAssetAmount.gt(ZERO)) {
75
76
  pnl = baseAssetValue.sub(marketPosition.quoteAssetAmount);
76
77
  } else {
77
- pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue).sub(ONE);
78
+ pnl = marketPosition.quoteAssetAmount.sub(baseAssetValue);
78
79
  }
79
80
 
80
81
  if (withFunding) {
package/src/math/trade.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Market, PositionDirection } from '../types';
1
+ import { isVariant, Market, PositionDirection } from '../types';
2
2
  import { BN } from '@project-serum/anchor';
3
3
  import { assert } from '../assert/assert';
4
4
  import {
@@ -6,6 +6,7 @@ import {
6
6
  PEG_PRECISION,
7
7
  AMM_TO_QUOTE_PRECISION_RATIO,
8
8
  ZERO,
9
+ ONE,
9
10
  } from '../constants/numericConstants';
10
11
  import { calculateMarkPrice } from './market';
11
12
  import {
@@ -114,16 +115,20 @@ export function calculateTradeAcquiredAmounts(
114
115
  return [ZERO, ZERO];
115
116
  }
116
117
 
118
+ const swapDirection = getSwapDirection(inputAssetType, direction);
117
119
  const [newQuoteAssetReserve, newBaseAssetReserve] =
118
120
  calculateAmmReservesAfterSwap(
119
121
  market.amm,
120
122
  inputAssetType,
121
123
  amount,
122
- getSwapDirection(inputAssetType, direction)
124
+ swapDirection
123
125
  );
124
126
 
125
127
  const acquiredBase = market.amm.baseAssetReserve.sub(newBaseAssetReserve);
126
- const acquiredQuote = market.amm.quoteAssetReserve.sub(newQuoteAssetReserve);
128
+ let acquiredQuote = market.amm.quoteAssetReserve.sub(newQuoteAssetReserve);
129
+ if (inputAssetType === 'base' && isVariant(swapDirection, 'remove')) {
130
+ acquiredQuote = acquiredQuote.sub(ONE);
131
+ }
127
132
 
128
133
  return [acquiredBase, acquiredQuote];
129
134
  }
@@ -0,0 +1,196 @@
1
+ import { TxSender } from './types';
2
+ import {
3
+ Commitment,
4
+ ConfirmOptions,
5
+ Context,
6
+ RpcResponseAndContext,
7
+ Signer,
8
+ SignatureResult,
9
+ Transaction,
10
+ TransactionSignature,
11
+ } from '@solana/web3.js';
12
+ import { Provider } from '@project-serum/anchor';
13
+ import assert from 'assert';
14
+ import bs58 from 'bs58';
15
+
16
+ const DEFAULT_TIMEOUT = 35000;
17
+ const DEFAULT_RETRY = 8000;
18
+
19
+ type ResolveReference = {
20
+ resolve?: () => void;
21
+ };
22
+
23
+ export class RetryTxSender implements TxSender {
24
+ provider: Provider;
25
+ timeout: number;
26
+ retrySleep: number;
27
+
28
+ public constructor(
29
+ provider: Provider,
30
+ timeout?: number,
31
+ retrySleep?: number
32
+ ) {
33
+ this.provider = provider;
34
+ this.timeout = timeout ?? DEFAULT_TIMEOUT;
35
+ this.retrySleep = retrySleep ?? DEFAULT_RETRY;
36
+ }
37
+
38
+ async send(
39
+ tx: Transaction,
40
+ additionalSigners?: Array<Signer>,
41
+ opts?: ConfirmOptions
42
+ ): Promise<TransactionSignature> {
43
+ if (additionalSigners === undefined) {
44
+ additionalSigners = [];
45
+ }
46
+ if (opts === undefined) {
47
+ opts = this.provider.opts;
48
+ }
49
+
50
+ await this.prepareTx(tx, additionalSigners, opts);
51
+
52
+ const rawTransaction = tx.serialize();
53
+ const startTime = this.getTimestamp();
54
+
55
+ const txid: TransactionSignature =
56
+ await this.provider.connection.sendRawTransaction(rawTransaction, opts);
57
+
58
+ let done = false;
59
+ const resolveReference: ResolveReference = {
60
+ resolve: undefined,
61
+ };
62
+ const stopWaiting = () => {
63
+ done = true;
64
+ if (resolveReference.resolve) {
65
+ resolveReference.resolve();
66
+ }
67
+ };
68
+
69
+ (async () => {
70
+ while (!done && this.getTimestamp() - startTime < this.timeout) {
71
+ await this.sleep(resolveReference);
72
+ if (!done) {
73
+ this.provider.connection
74
+ .sendRawTransaction(rawTransaction, opts)
75
+ .catch((e) => {
76
+ console.error(e);
77
+ stopWaiting();
78
+ });
79
+ }
80
+ }
81
+ })();
82
+
83
+ try {
84
+ await this.confirmTransaction(txid, opts.commitment);
85
+ } catch (e) {
86
+ console.error(e);
87
+ throw e;
88
+ } finally {
89
+ stopWaiting();
90
+ }
91
+
92
+ return txid;
93
+ }
94
+
95
+ async prepareTx(
96
+ tx: Transaction,
97
+ additionalSigners: Array<Signer>,
98
+ opts: ConfirmOptions
99
+ ): Promise<Transaction> {
100
+ tx.feePayer = this.provider.wallet.publicKey;
101
+ tx.recentBlockhash = (
102
+ await this.provider.connection.getRecentBlockhash(
103
+ opts.preflightCommitment
104
+ )
105
+ ).blockhash;
106
+
107
+ await this.provider.wallet.signTransaction(tx);
108
+ additionalSigners
109
+ .filter((s): s is Signer => s !== undefined)
110
+ .forEach((kp) => {
111
+ tx.partialSign(kp);
112
+ });
113
+
114
+ return tx;
115
+ }
116
+
117
+ async confirmTransaction(
118
+ signature: TransactionSignature,
119
+ commitment?: Commitment
120
+ ): Promise<RpcResponseAndContext<SignatureResult>> {
121
+ let decodedSignature;
122
+ try {
123
+ decodedSignature = bs58.decode(signature);
124
+ } catch (err) {
125
+ throw new Error('signature must be base58 encoded: ' + signature);
126
+ }
127
+
128
+ assert(decodedSignature.length === 64, 'signature has invalid length');
129
+
130
+ const start = Date.now();
131
+ const subscriptionCommitment = commitment || this.provider.opts.commitment;
132
+
133
+ let subscriptionId;
134
+ let response: RpcResponseAndContext<SignatureResult> | null = null;
135
+ const confirmPromise = new Promise((resolve, reject) => {
136
+ try {
137
+ subscriptionId = this.provider.connection.onSignature(
138
+ signature,
139
+ (result: SignatureResult, context: Context) => {
140
+ subscriptionId = undefined;
141
+ response = {
142
+ context,
143
+ value: result,
144
+ };
145
+ resolve(null);
146
+ },
147
+ subscriptionCommitment
148
+ );
149
+ } catch (err) {
150
+ reject(err);
151
+ }
152
+ });
153
+
154
+ try {
155
+ await this.promiseTimeout(confirmPromise, this.timeout);
156
+ } finally {
157
+ if (subscriptionId) {
158
+ this.provider.connection.removeSignatureListener(subscriptionId);
159
+ }
160
+ }
161
+
162
+ if (response === null) {
163
+ const duration = (Date.now() - start) / 1000;
164
+ throw new Error(
165
+ `Transaction was not confirmed in ${duration.toFixed(
166
+ 2
167
+ )} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`
168
+ );
169
+ }
170
+
171
+ return response;
172
+ }
173
+
174
+ getTimestamp(): number {
175
+ return new Date().getTime();
176
+ }
177
+
178
+ async sleep(reference: ResolveReference): Promise<void> {
179
+ return new Promise((resolve) => {
180
+ reference.resolve = resolve;
181
+ setTimeout(resolve, this.retrySleep);
182
+ });
183
+ }
184
+
185
+ promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null> {
186
+ let timeoutId: ReturnType<typeof setTimeout>;
187
+ const timeoutPromise: Promise<null> = new Promise((resolve) => {
188
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
189
+ });
190
+
191
+ return Promise.race([promise, timeoutPromise]).then((result: T | null) => {
192
+ clearTimeout(timeoutId);
193
+ return result;
194
+ });
195
+ }
196
+ }
package/src/tx/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Provider } from '@project-serum/anchor';
1
2
  import {
2
3
  ConfirmOptions,
3
4
  Signer,
@@ -6,6 +7,8 @@ import {
6
7
  } from '@solana/web3.js';
7
8
 
8
9
  export interface TxSender {
10
+ provider: Provider;
11
+
9
12
  send(
10
13
  tx: Transaction,
11
14
  additionalSigners?: Array<Signer>,