@drift-labs/sdk 2.54.0-beta.0 → 2.54.0-beta.10
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.
- package/VERSION +1 -1
- package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +0 -1
- package/lib/constants/perpMarkets.js +20 -0
- package/lib/driftClient.d.ts +8 -2
- package/lib/driftClient.js +95 -20
- package/lib/events/webSocketLogProvider.js +3 -0
- package/lib/idl/drift.json +7 -1
- package/lib/jupiter/jupiterClient.d.ts +6 -0
- package/lib/jupiter/jupiterClient.js +2 -2
- package/lib/math/funding.js +24 -1
- package/lib/math/oracles.js +2 -2
- package/lib/math/superStake.d.ts +51 -0
- package/lib/math/superStake.js +10 -2
- package/lib/priorityFee/averageOverSlotsStrategy.d.ts +0 -5
- package/lib/priorityFee/averageOverSlotsStrategy.js +1 -13
- package/lib/priorityFee/maxOverSlotsStrategy.d.ts +0 -5
- package/lib/priorityFee/maxOverSlotsStrategy.js +1 -13
- package/lib/priorityFee/priorityFeeSubscriber.d.ts +5 -4
- package/lib/priorityFee/priorityFeeSubscriber.js +15 -21
- package/lib/tx/baseTxSender.d.ts +6 -2
- package/lib/tx/baseTxSender.js +47 -2
- package/lib/tx/fastSingleTxSender.d.ts +3 -2
- package/lib/tx/fastSingleTxSender.js +10 -2
- package/lib/tx/retryTxSender.d.ts +3 -2
- package/lib/tx/retryTxSender.js +14 -23
- package/lib/tx/types.d.ts +5 -0
- package/lib/tx/types.js +7 -0
- package/lib/tx/utils.d.ts +5 -1
- package/lib/tx/utils.js +20 -1
- package/lib/userMap/userMap.js +4 -0
- package/package.json +1 -1
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +0 -1
- package/src/constants/perpMarkets.ts +20 -0
- package/src/driftClient.ts +197 -39
- package/src/events/webSocketLogProvider.ts +11 -2
- package/src/idl/drift.json +7 -1
- package/src/jupiter/jupiterClient.ts +8 -2
- package/src/math/funding.ts +28 -1
- package/src/math/oracles.ts +2 -2
- package/src/math/superStake.ts +60 -1
- package/src/priorityFee/averageOverSlotsStrategy.ts +1 -16
- package/src/priorityFee/maxOverSlotsStrategy.ts +1 -16
- package/src/priorityFee/priorityFeeSubscriber.ts +22 -26
- package/src/tx/baseTxSender.ts +64 -2
- package/src/tx/fastSingleTxSender.ts +11 -2
- package/src/tx/retryTxSender.ts +16 -25
- package/src/tx/types.ts +6 -0
- package/src/tx/utils.ts +32 -0
- package/src/userMap/userMap.ts +3 -0
- package/tests/amm/test.ts +275 -2
- package/tests/dlob/test.ts +2 -2
- package/tests/tx/priorityFeeStrategy.ts +2 -2
|
@@ -10,16 +10,20 @@ export class PriorityFeeSubscriber {
|
|
|
10
10
|
customStrategy?: PriorityFeeStrategy;
|
|
11
11
|
averageStrategy = new AverageOverSlotsStrategy();
|
|
12
12
|
maxStrategy = new MaxOverSlotsStrategy();
|
|
13
|
+
lookbackDistance: number;
|
|
13
14
|
|
|
14
15
|
intervalId?: ReturnType<typeof setTimeout>;
|
|
15
16
|
|
|
16
17
|
latestPriorityFee = 0;
|
|
17
|
-
lastStrategyResult = 0;
|
|
18
18
|
lastCustomStrategyResult = 0;
|
|
19
19
|
lastAvgStrategyResult = 0;
|
|
20
20
|
lastMaxStrategyResult = 0;
|
|
21
21
|
lastSlotSeen = 0;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param props
|
|
25
|
+
* customStrategy : strategy to return the priority fee to use based on recent samples. defaults to AVERAGE.
|
|
26
|
+
*/
|
|
23
27
|
public constructor({
|
|
24
28
|
connection,
|
|
25
29
|
frequencyMs,
|
|
@@ -36,28 +40,12 @@ export class PriorityFeeSubscriber {
|
|
|
36
40
|
this.connection = connection;
|
|
37
41
|
this.frequencyMs = frequencyMs;
|
|
38
42
|
this.addresses = addresses;
|
|
39
|
-
if (
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
if (customStrategy) {
|
|
43
|
+
if (!customStrategy) {
|
|
44
|
+
this.customStrategy = new AverageOverSlotsStrategy();
|
|
45
|
+
} else {
|
|
44
46
|
this.customStrategy = customStrategy;
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
public get avgPriorityFee(): number {
|
|
49
|
-
return Math.floor(this.lastAvgStrategyResult);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public get maxPriorityFee(): number {
|
|
53
|
-
return Math.floor(this.lastMaxStrategyResult);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public get customPriorityFee(): number {
|
|
57
|
-
if (!this.customStrategy) {
|
|
58
|
-
console.error('Custom strategy not set');
|
|
59
|
-
}
|
|
60
|
-
return Math.floor(this.lastCustomStrategyResult);
|
|
48
|
+
this.lookbackDistance = slotsToCheck;
|
|
61
49
|
}
|
|
62
50
|
|
|
63
51
|
public async subscribe(): Promise<void> {
|
|
@@ -75,21 +63,29 @@ export class PriorityFeeSubscriber {
|
|
|
75
63
|
[this.addresses]
|
|
76
64
|
);
|
|
77
65
|
|
|
78
|
-
// getRecentPrioritizationFees returns results unsorted
|
|
79
66
|
const results: { slot: number; prioritizationFee: number }[] =
|
|
80
67
|
rpcJSONResponse?.result;
|
|
68
|
+
|
|
81
69
|
if (!results.length) return;
|
|
82
|
-
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
83
70
|
|
|
71
|
+
// # Sort and filter results based on the slot lookback setting
|
|
72
|
+
const descResults = results.sort((a, b) => b.slot - a.slot);
|
|
84
73
|
const mostRecentResult = descResults[0];
|
|
74
|
+
const cutoffSlot = mostRecentResult.slot - this.lookbackDistance;
|
|
75
|
+
|
|
76
|
+
const resultsToUse = descResults.filter(
|
|
77
|
+
(result) => result.slot >= cutoffSlot
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// # Handle results
|
|
85
81
|
this.latestPriorityFee = mostRecentResult.prioritizationFee;
|
|
86
82
|
this.lastSlotSeen = mostRecentResult.slot;
|
|
87
83
|
|
|
88
|
-
this.lastAvgStrategyResult = this.averageStrategy.calculate(
|
|
89
|
-
this.lastMaxStrategyResult = this.maxStrategy.calculate(
|
|
84
|
+
this.lastAvgStrategyResult = this.averageStrategy.calculate(resultsToUse);
|
|
85
|
+
this.lastMaxStrategyResult = this.maxStrategy.calculate(resultsToUse);
|
|
90
86
|
if (this.customStrategy) {
|
|
91
87
|
this.lastCustomStrategyResult =
|
|
92
|
-
this.customStrategy.calculate(
|
|
88
|
+
this.customStrategy.calculate(resultsToUse);
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
|
package/src/tx/baseTxSender.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TxSender, TxSigAndSlot } from './types';
|
|
1
|
+
import { ConfirmationStrategy, TxSender, TxSigAndSlot } from './types';
|
|
2
2
|
import {
|
|
3
3
|
Commitment,
|
|
4
4
|
ConfirmOptions,
|
|
@@ -28,6 +28,7 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
28
28
|
timeout: number;
|
|
29
29
|
additionalConnections: Connection[];
|
|
30
30
|
timeoutCount = 0;
|
|
31
|
+
confirmationStrategy: ConfirmationStrategy;
|
|
31
32
|
|
|
32
33
|
public constructor({
|
|
33
34
|
connection,
|
|
@@ -35,18 +36,21 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
35
36
|
opts = AnchorProvider.defaultOptions(),
|
|
36
37
|
timeout = DEFAULT_TIMEOUT,
|
|
37
38
|
additionalConnections = new Array<Connection>(),
|
|
39
|
+
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
38
40
|
}: {
|
|
39
41
|
connection: Connection;
|
|
40
42
|
wallet: IWallet;
|
|
41
43
|
opts?: ConfirmOptions;
|
|
42
44
|
timeout?: number;
|
|
43
45
|
additionalConnections?;
|
|
46
|
+
confirmationStrategy?: ConfirmationStrategy;
|
|
44
47
|
}) {
|
|
45
48
|
this.connection = connection;
|
|
46
49
|
this.wallet = wallet;
|
|
47
50
|
this.opts = opts;
|
|
48
51
|
this.timeout = timeout;
|
|
49
52
|
this.additionalConnections = additionalConnections;
|
|
53
|
+
this.confirmationStrategy = confirmationStrategy;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
async send(
|
|
@@ -156,7 +160,7 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
156
160
|
throw new Error('Must be implemented by subclass');
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
async
|
|
163
|
+
async confirmTransactionWebSocket(
|
|
160
164
|
signature: TransactionSignature,
|
|
161
165
|
commitment?: Commitment
|
|
162
166
|
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
@@ -210,6 +214,22 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
210
214
|
}
|
|
211
215
|
|
|
212
216
|
if (response === null) {
|
|
217
|
+
if (this.confirmationStrategy === ConfirmationStrategy.Combo) {
|
|
218
|
+
try {
|
|
219
|
+
const rpcResponse = await this.connection.getSignatureStatus(
|
|
220
|
+
signature
|
|
221
|
+
);
|
|
222
|
+
if (rpcResponse?.value?.confirmationStatus) {
|
|
223
|
+
response = {
|
|
224
|
+
context: rpcResponse.context,
|
|
225
|
+
value: { err: rpcResponse.value.err },
|
|
226
|
+
};
|
|
227
|
+
return response;
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
// Ignore error to pass through to timeout error
|
|
231
|
+
}
|
|
232
|
+
}
|
|
213
233
|
this.timeoutCount += 1;
|
|
214
234
|
const duration = (Date.now() - start) / 1000;
|
|
215
235
|
throw new Error(
|
|
@@ -222,6 +242,48 @@ export abstract class BaseTxSender implements TxSender {
|
|
|
222
242
|
return response;
|
|
223
243
|
}
|
|
224
244
|
|
|
245
|
+
async confirmTransactionPolling(
|
|
246
|
+
signature: TransactionSignature,
|
|
247
|
+
commitment: Commitment = 'finalized'
|
|
248
|
+
): Promise<RpcResponseAndContext<SignatureResult> | undefined> {
|
|
249
|
+
let totalTime = 0;
|
|
250
|
+
let backoffTime = 400; // approx block time
|
|
251
|
+
|
|
252
|
+
while (totalTime < this.timeout) {
|
|
253
|
+
await new Promise((resolve) => setTimeout(resolve, backoffTime));
|
|
254
|
+
|
|
255
|
+
const response = await this.connection.getSignatureStatus(signature);
|
|
256
|
+
const result = response && response.value?.[0];
|
|
257
|
+
|
|
258
|
+
if (result && result.confirmationStatus === commitment) {
|
|
259
|
+
return { context: result.context, value: { err: null } };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
totalTime += backoffTime;
|
|
263
|
+
backoffTime = Math.min(backoffTime * 2, 5000);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Transaction not confirmed within 30 seconds
|
|
267
|
+
this.timeoutCount += 1;
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Transaction was not confirmed in 30 seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async confirmTransaction(
|
|
274
|
+
signature: TransactionSignature,
|
|
275
|
+
commitment?: Commitment
|
|
276
|
+
): Promise<RpcResponseAndContext<SignatureResult>> {
|
|
277
|
+
if (
|
|
278
|
+
this.confirmationStrategy === ConfirmationStrategy.WebSocket ||
|
|
279
|
+
this.confirmationStrategy === ConfirmationStrategy.Combo
|
|
280
|
+
) {
|
|
281
|
+
return await this.confirmTransactionWebSocket(signature, commitment);
|
|
282
|
+
} else if (this.confirmationStrategy === ConfirmationStrategy.Polling) {
|
|
283
|
+
return await this.confirmTransactionPolling(signature, commitment);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
225
287
|
getTimestamp(): number {
|
|
226
288
|
return new Date().getTime();
|
|
227
289
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TxSigAndSlot } from './types';
|
|
1
|
+
import { ConfirmationStrategy, TxSigAndSlot } from './types';
|
|
2
2
|
import {
|
|
3
3
|
ConfirmOptions,
|
|
4
4
|
Signer,
|
|
@@ -39,6 +39,7 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
39
39
|
additionalConnections = new Array<Connection>(),
|
|
40
40
|
skipConfirmation = false,
|
|
41
41
|
blockhashCommitment = 'finalized',
|
|
42
|
+
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
42
43
|
}: {
|
|
43
44
|
connection: Connection;
|
|
44
45
|
wallet: IWallet;
|
|
@@ -48,8 +49,16 @@ export class FastSingleTxSender extends BaseTxSender {
|
|
|
48
49
|
additionalConnections?;
|
|
49
50
|
skipConfirmation?: boolean;
|
|
50
51
|
blockhashCommitment?: Commitment;
|
|
52
|
+
confirmationStrategy?: ConfirmationStrategy;
|
|
51
53
|
}) {
|
|
52
|
-
super({
|
|
54
|
+
super({
|
|
55
|
+
connection,
|
|
56
|
+
wallet,
|
|
57
|
+
opts,
|
|
58
|
+
timeout,
|
|
59
|
+
additionalConnections,
|
|
60
|
+
confirmationStrategy,
|
|
61
|
+
});
|
|
53
62
|
this.connection = connection;
|
|
54
63
|
this.wallet = wallet;
|
|
55
64
|
this.opts = opts;
|
package/src/tx/retryTxSender.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { TxSigAndSlot } from './types';
|
|
2
|
-
import {
|
|
3
|
-
ConfirmOptions,
|
|
4
|
-
TransactionSignature,
|
|
5
|
-
Connection,
|
|
6
|
-
} from '@solana/web3.js';
|
|
1
|
+
import { ConfirmationStrategy, TxSigAndSlot } from './types';
|
|
2
|
+
import { ConfirmOptions, Connection } from '@solana/web3.js';
|
|
7
3
|
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
8
4
|
import { IWallet } from '../types';
|
|
9
5
|
import { BaseTxSender } from './baseTxSender';
|
|
@@ -31,6 +27,7 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
31
27
|
timeout = DEFAULT_TIMEOUT,
|
|
32
28
|
retrySleep = DEFAULT_RETRY,
|
|
33
29
|
additionalConnections = new Array<Connection>(),
|
|
30
|
+
confirmationStrategy = ConfirmationStrategy.Combo,
|
|
34
31
|
}: {
|
|
35
32
|
connection: Connection;
|
|
36
33
|
wallet: IWallet;
|
|
@@ -38,8 +35,16 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
38
35
|
timeout?: number;
|
|
39
36
|
retrySleep?: number;
|
|
40
37
|
additionalConnections?;
|
|
38
|
+
confirmationStrategy?: ConfirmationStrategy;
|
|
41
39
|
}) {
|
|
42
|
-
super({
|
|
40
|
+
super({
|
|
41
|
+
connection,
|
|
42
|
+
wallet,
|
|
43
|
+
opts,
|
|
44
|
+
timeout,
|
|
45
|
+
additionalConnections,
|
|
46
|
+
confirmationStrategy,
|
|
47
|
+
});
|
|
43
48
|
this.connection = connection;
|
|
44
49
|
this.wallet = wallet;
|
|
45
50
|
this.opts = opts;
|
|
@@ -61,14 +66,8 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
61
66
|
): Promise<TxSigAndSlot> {
|
|
62
67
|
const startTime = this.getTimestamp();
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
txid = await this.connection.sendRawTransaction(rawTransaction, opts);
|
|
67
|
-
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.error(e);
|
|
70
|
-
throw e;
|
|
71
|
-
}
|
|
69
|
+
const txid = await this.connection.sendRawTransaction(rawTransaction, opts);
|
|
70
|
+
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
72
71
|
|
|
73
72
|
let done = false;
|
|
74
73
|
const resolveReference: ResolveReference = {
|
|
@@ -96,16 +95,8 @@ export class RetryTxSender extends BaseTxSender {
|
|
|
96
95
|
}
|
|
97
96
|
})();
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
102
|
-
slot = result.context.slot;
|
|
103
|
-
} catch (e) {
|
|
104
|
-
console.error(e);
|
|
105
|
-
throw e;
|
|
106
|
-
} finally {
|
|
107
|
-
stopWaiting();
|
|
108
|
-
}
|
|
98
|
+
const result = await this.confirmTransaction(txid, opts.commitment);
|
|
99
|
+
const slot = result.context.slot;
|
|
109
100
|
|
|
110
101
|
return { txSig: txid, slot };
|
|
111
102
|
}
|
package/src/tx/types.ts
CHANGED
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
} from '@solana/web3.js';
|
|
10
10
|
import { IWallet } from '../types';
|
|
11
11
|
|
|
12
|
+
export enum ConfirmationStrategy {
|
|
13
|
+
WebSocket = 'websocket',
|
|
14
|
+
Polling = 'polling',
|
|
15
|
+
Combo = 'combo',
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
export type TxSigAndSlot = {
|
|
13
19
|
txSig: TransactionSignature;
|
|
14
20
|
slot: number;
|
package/src/tx/utils.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { Wallet } from '@coral-xyz/anchor';
|
|
1
2
|
import {
|
|
2
3
|
Transaction,
|
|
3
4
|
TransactionInstruction,
|
|
4
5
|
ComputeBudgetProgram,
|
|
6
|
+
VersionedTransaction,
|
|
5
7
|
} from '@solana/web3.js';
|
|
6
8
|
|
|
7
9
|
const COMPUTE_UNITS_DEFAULT = 200_000;
|
|
@@ -30,3 +32,33 @@ export function wrapInTx(
|
|
|
30
32
|
|
|
31
33
|
return tx.add(instruction);
|
|
32
34
|
}
|
|
35
|
+
|
|
36
|
+
/* Helper function for signing multiple transactions where some may be undefined and mapping the output */
|
|
37
|
+
export async function getSignedTransactionMap(
|
|
38
|
+
wallet: Wallet,
|
|
39
|
+
txsToSign: (Transaction | VersionedTransaction | undefined)[],
|
|
40
|
+
keys: string[]
|
|
41
|
+
): Promise<{ [key: string]: Transaction | VersionedTransaction | undefined }> {
|
|
42
|
+
const signedTxMap: {
|
|
43
|
+
[key: string]: Transaction | VersionedTransaction | undefined;
|
|
44
|
+
} = {};
|
|
45
|
+
|
|
46
|
+
const keysWithTx = [];
|
|
47
|
+
txsToSign.forEach((tx, index) => {
|
|
48
|
+
if (tx == undefined) {
|
|
49
|
+
signedTxMap[keys[index]] = undefined;
|
|
50
|
+
} else {
|
|
51
|
+
keysWithTx.push(keys[index]);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const signedTxs = await wallet.signAllTransactions(
|
|
56
|
+
txsToSign.filter((tx) => tx !== undefined)
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
signedTxs.forEach((signedTx, index) => {
|
|
60
|
+
signedTxMap[keysWithTx[index]] = signedTx;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return signedTxMap;
|
|
64
|
+
}
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -337,6 +337,9 @@ export class UserMap implements UserMapInterface {
|
|
|
337
337
|
const userAccount = this.decode('User', buffer);
|
|
338
338
|
await this.addPubkey(new PublicKey(key), userAccount);
|
|
339
339
|
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
340
|
+
} else {
|
|
341
|
+
const userAccount = this.decode('User', buffer);
|
|
342
|
+
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
340
343
|
}
|
|
341
344
|
// give event loop a chance to breathe
|
|
342
345
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
package/tests/amm/test.ts
CHANGED
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
squareRootBN,
|
|
29
29
|
calculateReferencePriceOffset,
|
|
30
30
|
calculateInventoryLiquidityRatio,
|
|
31
|
+
ContractTier,
|
|
32
|
+
isOracleValid,
|
|
33
|
+
OracleGuardRails,
|
|
31
34
|
} from '../../src';
|
|
32
35
|
import { mockPerpMarkets } from '../dlob/helpers';
|
|
33
36
|
|
|
@@ -875,11 +878,12 @@ describe('AMM Tests', () => {
|
|
|
875
878
|
const mockMarket1 = myMockPerpMarkets[0];
|
|
876
879
|
const mockAmm = mockMarket1.amm;
|
|
877
880
|
const now = new BN(new Date().getTime() / 1000); //todo
|
|
881
|
+
const slot = 999999999;
|
|
878
882
|
|
|
879
883
|
const oraclePriceData = {
|
|
880
884
|
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
881
|
-
slot: new BN(
|
|
882
|
-
confidence: new BN(
|
|
885
|
+
slot: new BN(slot),
|
|
886
|
+
confidence: new BN(1000),
|
|
883
887
|
hasSufficientNumberOfDataPoints: true,
|
|
884
888
|
};
|
|
885
889
|
mockAmm.oracleStd = new BN(0.18 * PRICE_PRECISION.toNumber());
|
|
@@ -901,6 +905,127 @@ describe('AMM Tests', () => {
|
|
|
901
905
|
const liveOracleStd = calculateLiveOracleStd(mockAmm, oraclePriceData, now);
|
|
902
906
|
console.log('liveOracleStd:', liveOracleStd.toNumber());
|
|
903
907
|
assert(liveOracleStd.eq(new BN(192962)));
|
|
908
|
+
|
|
909
|
+
const oracleGuardRails: OracleGuardRails = {
|
|
910
|
+
priceDivergence: {
|
|
911
|
+
markOraclePercentDivergence: PERCENTAGE_PRECISION.divn(10),
|
|
912
|
+
oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.divn(10),
|
|
913
|
+
},
|
|
914
|
+
validity: {
|
|
915
|
+
slotsBeforeStaleForAmm: new BN(10),
|
|
916
|
+
slotsBeforeStaleForMargin: new BN(60),
|
|
917
|
+
confidenceIntervalMaxSize: new BN(20000),
|
|
918
|
+
tooVolatileRatio: new BN(5),
|
|
919
|
+
},
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
// good oracle
|
|
923
|
+
assert(isOracleValid(mockAmm, oraclePriceData, oracleGuardRails, slot + 5));
|
|
924
|
+
|
|
925
|
+
// conf too high
|
|
926
|
+
assert(
|
|
927
|
+
!isOracleValid(
|
|
928
|
+
mockAmm,
|
|
929
|
+
{
|
|
930
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
931
|
+
slot: new BN(slot),
|
|
932
|
+
confidence: new BN(13.553 * PRICE_PRECISION.toNumber() * 0.021),
|
|
933
|
+
hasSufficientNumberOfDataPoints: true,
|
|
934
|
+
},
|
|
935
|
+
oracleGuardRails,
|
|
936
|
+
slot
|
|
937
|
+
)
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
// not hasSufficientNumberOfDataPoints
|
|
941
|
+
assert(
|
|
942
|
+
!isOracleValid(
|
|
943
|
+
mockAmm,
|
|
944
|
+
{
|
|
945
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
946
|
+
slot: new BN(slot),
|
|
947
|
+
confidence: new BN(1),
|
|
948
|
+
hasSufficientNumberOfDataPoints: false,
|
|
949
|
+
},
|
|
950
|
+
oracleGuardRails,
|
|
951
|
+
slot
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
// negative oracle price
|
|
956
|
+
assert(
|
|
957
|
+
!isOracleValid(
|
|
958
|
+
mockAmm,
|
|
959
|
+
{
|
|
960
|
+
price: new BN(-1 * PRICE_PRECISION.toNumber()),
|
|
961
|
+
slot: new BN(slot),
|
|
962
|
+
confidence: new BN(1),
|
|
963
|
+
hasSufficientNumberOfDataPoints: true,
|
|
964
|
+
},
|
|
965
|
+
oracleGuardRails,
|
|
966
|
+
slot
|
|
967
|
+
)
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
// too delayed for amm
|
|
971
|
+
assert(
|
|
972
|
+
!isOracleValid(
|
|
973
|
+
mockAmm,
|
|
974
|
+
{
|
|
975
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
976
|
+
slot: new BN(slot),
|
|
977
|
+
confidence: new BN(1),
|
|
978
|
+
hasSufficientNumberOfDataPoints: true,
|
|
979
|
+
},
|
|
980
|
+
oracleGuardRails,
|
|
981
|
+
slot + 100
|
|
982
|
+
)
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
// im passing stale slot (should not call oracle invalid)
|
|
986
|
+
assert(
|
|
987
|
+
isOracleValid(
|
|
988
|
+
mockAmm,
|
|
989
|
+
{
|
|
990
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
991
|
+
slot: new BN(slot + 100),
|
|
992
|
+
confidence: new BN(1),
|
|
993
|
+
hasSufficientNumberOfDataPoints: true,
|
|
994
|
+
},
|
|
995
|
+
oracleGuardRails,
|
|
996
|
+
slot
|
|
997
|
+
)
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
// too volatile (more than 5x higher)
|
|
1001
|
+
assert(
|
|
1002
|
+
!isOracleValid(
|
|
1003
|
+
mockAmm,
|
|
1004
|
+
{
|
|
1005
|
+
price: new BN(113.553 * PRICE_PRECISION.toNumber()),
|
|
1006
|
+
slot: new BN(slot + 5),
|
|
1007
|
+
confidence: new BN(1),
|
|
1008
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1009
|
+
},
|
|
1010
|
+
oracleGuardRails,
|
|
1011
|
+
slot
|
|
1012
|
+
)
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
// too volatile (more than 1/5 lower)
|
|
1016
|
+
assert(
|
|
1017
|
+
!isOracleValid(
|
|
1018
|
+
mockAmm,
|
|
1019
|
+
{
|
|
1020
|
+
price: new BN(0.553 * PRICE_PRECISION.toNumber()),
|
|
1021
|
+
slot: new BN(slot + 5),
|
|
1022
|
+
confidence: new BN(1),
|
|
1023
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1024
|
+
},
|
|
1025
|
+
oracleGuardRails,
|
|
1026
|
+
slot
|
|
1027
|
+
)
|
|
1028
|
+
);
|
|
904
1029
|
});
|
|
905
1030
|
|
|
906
1031
|
it('predicted funding rate mock1', async () => {
|
|
@@ -1065,6 +1190,154 @@ describe('AMM Tests', () => {
|
|
|
1065
1190
|
assert(est2.eq(new BN('-719')));
|
|
1066
1191
|
});
|
|
1067
1192
|
|
|
1193
|
+
it('predicted funding rate mock clamp', async () => {
|
|
1194
|
+
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
1195
|
+
const mockMarket1 = myMockPerpMarkets[0];
|
|
1196
|
+
|
|
1197
|
+
// make it like OP
|
|
1198
|
+
const now = new BN(1688881915);
|
|
1199
|
+
|
|
1200
|
+
mockMarket1.amm.fundingPeriod = new BN(3600);
|
|
1201
|
+
mockMarket1.amm.lastFundingRateTs = new BN(1688864415);
|
|
1202
|
+
|
|
1203
|
+
const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium
|
|
1204
|
+
const oraclePriceData: OraclePriceData = {
|
|
1205
|
+
price: new BN(1.924 * PRICE_PRECISION.toNumber()),
|
|
1206
|
+
slot: new BN(0),
|
|
1207
|
+
confidence: new BN(1),
|
|
1208
|
+
hasSufficientNumberOfDataPoints: true,
|
|
1209
|
+
};
|
|
1210
|
+
mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN(
|
|
1211
|
+
1.9535 * PRICE_PRECISION.toNumber()
|
|
1212
|
+
);
|
|
1213
|
+
|
|
1214
|
+
// mockMarket1.amm.pegMultiplier = new BN(1.897573 * 1e3);
|
|
1215
|
+
|
|
1216
|
+
mockMarket1.amm.lastMarkPriceTwap = new BN(
|
|
1217
|
+
1.218363 * PRICE_PRECISION.toNumber()
|
|
1218
|
+
);
|
|
1219
|
+
mockMarket1.amm.lastBidPriceTwap = new BN(
|
|
1220
|
+
1.218363 * PRICE_PRECISION.toNumber()
|
|
1221
|
+
);
|
|
1222
|
+
mockMarket1.amm.lastAskPriceTwap = new BN(
|
|
1223
|
+
1.218364 * PRICE_PRECISION.toNumber()
|
|
1224
|
+
);
|
|
1225
|
+
mockMarket1.amm.lastMarkPriceTwapTs = new BN(1688878815);
|
|
1226
|
+
|
|
1227
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap = new BN(
|
|
1228
|
+
1.820964 * PRICE_PRECISION.toNumber()
|
|
1229
|
+
);
|
|
1230
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwapTs = new BN(
|
|
1231
|
+
1688879991
|
|
1232
|
+
);
|
|
1233
|
+
mockMarket1.contractTier = ContractTier.A;
|
|
1234
|
+
|
|
1235
|
+
const [
|
|
1236
|
+
_markTwapLive,
|
|
1237
|
+
_oracleTwapLive,
|
|
1238
|
+
_lowerboundEst,
|
|
1239
|
+
_cappedAltEst,
|
|
1240
|
+
_interpEst,
|
|
1241
|
+
] = await calculateAllEstimatedFundingRate(
|
|
1242
|
+
mockMarket1,
|
|
1243
|
+
oraclePriceData,
|
|
1244
|
+
currentMarkPrice,
|
|
1245
|
+
now
|
|
1246
|
+
);
|
|
1247
|
+
|
|
1248
|
+
// console.log(_markTwapLive.toString());
|
|
1249
|
+
// console.log(_oracleTwapLive.toString());
|
|
1250
|
+
// console.log(_lowerboundEst.toString());
|
|
1251
|
+
// console.log(_cappedAltEst.toString());
|
|
1252
|
+
// console.log(_interpEst.toString());
|
|
1253
|
+
// console.log('-----');
|
|
1254
|
+
|
|
1255
|
+
let [markTwapLive, oracleTwapLive, est1, est2] =
|
|
1256
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1257
|
+
mockMarket1,
|
|
1258
|
+
oraclePriceData,
|
|
1259
|
+
currentMarkPrice,
|
|
1260
|
+
now
|
|
1261
|
+
);
|
|
1262
|
+
|
|
1263
|
+
console.log(
|
|
1264
|
+
'markTwapLive:',
|
|
1265
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1266
|
+
'->',
|
|
1267
|
+
markTwapLive.toString()
|
|
1268
|
+
);
|
|
1269
|
+
console.log(
|
|
1270
|
+
'oracTwapLive:',
|
|
1271
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1272
|
+
'->',
|
|
1273
|
+
oracleTwapLive.toString()
|
|
1274
|
+
);
|
|
1275
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1276
|
+
|
|
1277
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1278
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1279
|
+
assert(est1.eq(est2));
|
|
1280
|
+
assert(est2.eq(new BN('-126261')));
|
|
1281
|
+
|
|
1282
|
+
mockMarket1.contractTier = ContractTier.C;
|
|
1283
|
+
|
|
1284
|
+
[markTwapLive, oracleTwapLive, est1, est2] =
|
|
1285
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1286
|
+
mockMarket1,
|
|
1287
|
+
oraclePriceData,
|
|
1288
|
+
currentMarkPrice,
|
|
1289
|
+
now
|
|
1290
|
+
);
|
|
1291
|
+
|
|
1292
|
+
console.log(
|
|
1293
|
+
'markTwapLive:',
|
|
1294
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1295
|
+
'->',
|
|
1296
|
+
markTwapLive.toString()
|
|
1297
|
+
);
|
|
1298
|
+
console.log(
|
|
1299
|
+
'oracTwapLive:',
|
|
1300
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1301
|
+
'->',
|
|
1302
|
+
oracleTwapLive.toString()
|
|
1303
|
+
);
|
|
1304
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1305
|
+
|
|
1306
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1307
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1308
|
+
assert(est1.eq(est2));
|
|
1309
|
+
assert(est2.eq(new BN('-208332')));
|
|
1310
|
+
|
|
1311
|
+
mockMarket1.contractTier = ContractTier.SPECULATIVE;
|
|
1312
|
+
|
|
1313
|
+
[markTwapLive, oracleTwapLive, est1, est2] =
|
|
1314
|
+
await calculateLongShortFundingRateAndLiveTwaps(
|
|
1315
|
+
mockMarket1,
|
|
1316
|
+
oraclePriceData,
|
|
1317
|
+
currentMarkPrice,
|
|
1318
|
+
now
|
|
1319
|
+
);
|
|
1320
|
+
|
|
1321
|
+
console.log(
|
|
1322
|
+
'markTwapLive:',
|
|
1323
|
+
mockMarket1.amm.lastMarkPriceTwap.toString(),
|
|
1324
|
+
'->',
|
|
1325
|
+
markTwapLive.toString()
|
|
1326
|
+
);
|
|
1327
|
+
console.log(
|
|
1328
|
+
'oracTwapLive:',
|
|
1329
|
+
mockMarket1.amm.historicalOracleData.lastOraclePriceTwap.toString(),
|
|
1330
|
+
'->',
|
|
1331
|
+
oracleTwapLive.toString()
|
|
1332
|
+
);
|
|
1333
|
+
console.log('pred funding:', est1.toString(), est2.toString());
|
|
1334
|
+
|
|
1335
|
+
assert(markTwapLive.eq(new BN('1680634')));
|
|
1336
|
+
assert(oracleTwapLive.eq(new BN('1876031')));
|
|
1337
|
+
assert(est1.eq(est2));
|
|
1338
|
+
assert(est2.eq(new BN('-416666')));
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1068
1341
|
it('orderbook L2 gen (no topOfBookQuoteAmounts, 10 numOrders, low liquidity)', async () => {
|
|
1069
1342
|
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
1070
1343
|
|
package/tests/dlob/test.ts
CHANGED
|
@@ -76,7 +76,7 @@ function insertOrderToDLOB(
|
|
|
76
76
|
auctionEndPrice,
|
|
77
77
|
maxTs,
|
|
78
78
|
},
|
|
79
|
-
userAccount,
|
|
79
|
+
userAccount.toString(),
|
|
80
80
|
slot.toNumber()
|
|
81
81
|
);
|
|
82
82
|
}
|
|
@@ -127,7 +127,7 @@ function insertTriggerOrderToDLOB(
|
|
|
127
127
|
auctionEndPrice,
|
|
128
128
|
maxTs,
|
|
129
129
|
},
|
|
130
|
-
userAccount,
|
|
130
|
+
userAccount.toString(),
|
|
131
131
|
slot.toNumber()
|
|
132
132
|
);
|
|
133
133
|
}
|