@drift-labs/sdk 2.52.0-beta.9 → 2.53.0-beta.0
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/examples/phoenix.ts +45 -26
- package/lib/constants/perpMarkets.js +20 -0
- package/lib/driftClient.d.ts +3 -1
- package/lib/driftClient.js +23 -6
- package/lib/idl/drift.json +37 -1
- package/lib/math/amm.js +13 -8
- package/lib/math/orders.d.ts +1 -1
- package/lib/math/orders.js +2 -2
- package/lib/math/state.js +3 -0
- package/lib/phoenix/phoenixSubscriber.js +18 -16
- package/lib/priorityFee/priorityFeeSubscriber.js +3 -3
- package/package.json +1 -1
- package/src/constants/perpMarkets.ts +20 -0
- package/src/driftClient.ts +39 -6
- package/src/idl/drift.json +37 -1
- package/src/math/amm.ts +37 -27
- package/src/math/orders.ts +8 -3
- package/src/math/state.ts +3 -0
- package/src/phoenix/phoenixSubscriber.ts +19 -20
- package/src/priorityFee/priorityFeeSubscriber.ts +3 -3
- package/tests/amm/test.ts +313 -18
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.53.0-beta.0
|
package/examples/phoenix.ts
CHANGED
|
@@ -1,38 +1,57 @@
|
|
|
1
1
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BASE_PRECISION,
|
|
4
|
+
L2Level,
|
|
5
|
+
PRICE_PRECISION,
|
|
6
|
+
PhoenixSubscriber,
|
|
7
|
+
} from '../src';
|
|
3
8
|
import { PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk';
|
|
4
9
|
|
|
5
10
|
export async function listenToBook(): Promise<void> {
|
|
6
11
|
const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
7
12
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
for (const market of [
|
|
14
|
+
'4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg', // SOL/USDC
|
|
15
|
+
'Ew3vFDdtdGrknJAVVfraxCA37uNJtimXYPY4QjnfhFHH', // ETH/USDC
|
|
16
|
+
'2sTMN9A1D1qeZLF95XQgJCUPiKe5DiV52jLfZGqMP46m', // PYTH/USDC
|
|
17
|
+
'BRLLmdtPGuuFn3BU6orYw4KHaohAEptBToi3dwRUnHQZ', // JTO/USDC
|
|
18
|
+
]) {
|
|
19
|
+
const phoenixSubscriber = new PhoenixSubscriber({
|
|
20
|
+
connection,
|
|
21
|
+
programId: PROGRAM_ID,
|
|
22
|
+
marketAddress: new PublicKey(market),
|
|
23
|
+
accountSubscription: {
|
|
24
|
+
type: 'websocket',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
await phoenixSubscriber.subscribe();
|
|
29
|
+
|
|
30
|
+
const bids = phoenixSubscriber.getL2Levels('bids');
|
|
31
|
+
const asks = phoenixSubscriber.getL2Levels('asks');
|
|
32
|
+
let bid: L2Level | null = null;
|
|
33
|
+
for (const b of bids) {
|
|
34
|
+
bid = b;
|
|
35
|
+
break;
|
|
27
36
|
}
|
|
28
|
-
|
|
29
|
-
for (const
|
|
30
|
-
|
|
37
|
+
let ask: L2Level | null = null;
|
|
38
|
+
for (const a of asks) {
|
|
39
|
+
ask = a;
|
|
40
|
+
break;
|
|
31
41
|
}
|
|
32
|
-
await new Promise((r) => setTimeout(r, 2000));
|
|
33
|
-
}
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
console.log('market', market);
|
|
44
|
+
console.log(
|
|
45
|
+
(bid?.size.toNumber() || 0) / BASE_PRECISION.toNumber(),
|
|
46
|
+
(bid?.price.toNumber() || 0) / PRICE_PRECISION.toNumber(),
|
|
47
|
+
'@',
|
|
48
|
+
(ask?.price.toNumber() || (1 << 53) - 1) / PRICE_PRECISION.toNumber(),
|
|
49
|
+
(ask?.size.toNumber() || 0) / BASE_PRECISION.toNumber()
|
|
50
|
+
);
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
await phoenixSubscriber.unsubscribe();
|
|
54
|
+
}
|
|
36
55
|
}
|
|
37
56
|
|
|
38
57
|
(async function () {
|
|
@@ -214,6 +214,16 @@ exports.DevnetPerpMarkets = [
|
|
|
214
214
|
launchTs: 1701967240000,
|
|
215
215
|
oracleSource: __1.OracleSource.PYTH,
|
|
216
216
|
},
|
|
217
|
+
{
|
|
218
|
+
fullName: 'SEI',
|
|
219
|
+
category: ['L1'],
|
|
220
|
+
symbol: 'SEI-PERP',
|
|
221
|
+
baseAssetSymbol: 'SEI',
|
|
222
|
+
marketIndex: 21,
|
|
223
|
+
oracle: new web3_js_1.PublicKey('B6KVbgqTRY33yDgjAnc1mWw4ATS4W5544xghayQscdt7'),
|
|
224
|
+
launchTs: 1703173331000,
|
|
225
|
+
oracleSource: __1.OracleSource.PYTH,
|
|
226
|
+
},
|
|
217
227
|
];
|
|
218
228
|
exports.MainnetPerpMarkets = [
|
|
219
229
|
{
|
|
@@ -426,6 +436,16 @@ exports.MainnetPerpMarkets = [
|
|
|
426
436
|
launchTs: 1701967240000,
|
|
427
437
|
oracleSource: __1.OracleSource.PYTH,
|
|
428
438
|
},
|
|
439
|
+
{
|
|
440
|
+
fullName: 'SEI',
|
|
441
|
+
category: ['L1'],
|
|
442
|
+
symbol: 'SEI-PERP',
|
|
443
|
+
baseAssetSymbol: 'SEI',
|
|
444
|
+
marketIndex: 21,
|
|
445
|
+
oracle: new web3_js_1.PublicKey('6cUuAyAX3eXoiWkjFF77RQBEUF15AAMQ7d1hm4EPd3tv'),
|
|
446
|
+
launchTs: 1703173331000,
|
|
447
|
+
oracleSource: __1.OracleSource.PYTH,
|
|
448
|
+
},
|
|
429
449
|
];
|
|
430
450
|
exports.PerpMarkets = {
|
|
431
451
|
devnet: exports.DevnetPerpMarkets,
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -133,8 +133,10 @@ export declare class DriftClient {
|
|
|
133
133
|
getUserAccountsForAuthority(authority: PublicKey): Promise<UserAccount[]>;
|
|
134
134
|
getReferredUserStatsAccountsByReferrer(referrer: PublicKey): Promise<UserStatsAccount[]>;
|
|
135
135
|
getReferrerNameAccountsForAuthority(authority: PublicKey): Promise<ReferrerNameAccount[]>;
|
|
136
|
-
getUserDeletionIx(userAccountPublicKey: PublicKey): Promise<anchor.web3.TransactionInstruction>;
|
|
137
136
|
deleteUser(subAccountId?: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
137
|
+
getUserDeletionIx(userAccountPublicKey: PublicKey): Promise<anchor.web3.TransactionInstruction>;
|
|
138
|
+
reclaimRent(subAccountId?: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
139
|
+
getReclaimRentIx(userAccountPublicKey: PublicKey): Promise<anchor.web3.TransactionInstruction>;
|
|
138
140
|
getUser(subAccountId?: number, authority?: PublicKey): User;
|
|
139
141
|
hasUser(subAccountId?: number, authority?: PublicKey): boolean;
|
|
140
142
|
getUsers(): User[];
|
package/lib/driftClient.js
CHANGED
|
@@ -665,6 +665,16 @@ class DriftClient {
|
|
|
665
665
|
]);
|
|
666
666
|
return programAccounts.map((programAccount) => programAccount.account);
|
|
667
667
|
}
|
|
668
|
+
async deleteUser(subAccountId = 0, txParams) {
|
|
669
|
+
var _a;
|
|
670
|
+
const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId);
|
|
671
|
+
const ix = await this.getUserDeletionIx(userAccountPublicKey);
|
|
672
|
+
const { txSig } = await this.sendTransaction(await this.buildTransaction(ix, txParams), [], this.opts);
|
|
673
|
+
const userMapKey = this.getUserMapKey(subAccountId, this.wallet.publicKey);
|
|
674
|
+
await ((_a = this.users.get(userMapKey)) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
675
|
+
this.users.delete(userMapKey);
|
|
676
|
+
return txSig;
|
|
677
|
+
}
|
|
668
678
|
async getUserDeletionIx(userAccountPublicKey) {
|
|
669
679
|
const ix = await this.program.instruction.deleteUser({
|
|
670
680
|
accounts: {
|
|
@@ -676,16 +686,23 @@ class DriftClient {
|
|
|
676
686
|
});
|
|
677
687
|
return ix;
|
|
678
688
|
}
|
|
679
|
-
async
|
|
680
|
-
var _a;
|
|
689
|
+
async reclaimRent(subAccountId = 0, txParams) {
|
|
681
690
|
const userAccountPublicKey = (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.wallet.publicKey, subAccountId);
|
|
682
|
-
const ix = await this.
|
|
691
|
+
const ix = await this.getReclaimRentIx(userAccountPublicKey);
|
|
683
692
|
const { txSig } = await this.sendTransaction(await this.buildTransaction(ix, txParams), [], this.opts);
|
|
684
|
-
const userMapKey = this.getUserMapKey(subAccountId, this.wallet.publicKey);
|
|
685
|
-
await ((_a = this.users.get(userMapKey)) === null || _a === void 0 ? void 0 : _a.unsubscribe());
|
|
686
|
-
this.users.delete(userMapKey);
|
|
687
693
|
return txSig;
|
|
688
694
|
}
|
|
695
|
+
async getReclaimRentIx(userAccountPublicKey) {
|
|
696
|
+
return await this.program.instruction.reclaimRent({
|
|
697
|
+
accounts: {
|
|
698
|
+
user: userAccountPublicKey,
|
|
699
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
700
|
+
authority: this.wallet.publicKey,
|
|
701
|
+
state: await this.getStatePublicKey(),
|
|
702
|
+
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
}
|
|
689
706
|
getUser(subAccountId, authority) {
|
|
690
707
|
subAccountId = subAccountId !== null && subAccountId !== void 0 ? subAccountId : this.activeSubAccountId;
|
|
691
708
|
authority = authority !== null && authority !== void 0 ? authority : this.authority;
|
package/lib/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.52.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -1173,6 +1173,37 @@
|
|
|
1173
1173
|
],
|
|
1174
1174
|
"args": []
|
|
1175
1175
|
},
|
|
1176
|
+
{
|
|
1177
|
+
"name": "reclaimRent",
|
|
1178
|
+
"accounts": [
|
|
1179
|
+
{
|
|
1180
|
+
"name": "user",
|
|
1181
|
+
"isMut": true,
|
|
1182
|
+
"isSigner": false
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
"name": "userStats",
|
|
1186
|
+
"isMut": true,
|
|
1187
|
+
"isSigner": false
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
"name": "state",
|
|
1191
|
+
"isMut": false,
|
|
1192
|
+
"isSigner": false
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
"name": "authority",
|
|
1196
|
+
"isMut": false,
|
|
1197
|
+
"isSigner": true
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
"name": "rent",
|
|
1201
|
+
"isMut": false,
|
|
1202
|
+
"isSigner": false
|
|
1203
|
+
}
|
|
1204
|
+
],
|
|
1205
|
+
"args": []
|
|
1206
|
+
},
|
|
1176
1207
|
{
|
|
1177
1208
|
"name": "fillPerpOrder",
|
|
1178
1209
|
"accounts": [
|
|
@@ -11139,6 +11170,11 @@
|
|
|
11139
11170
|
"code": 6256,
|
|
11140
11171
|
"name": "CantPayUserInitFee",
|
|
11141
11172
|
"msg": "CantPayUserInitFee"
|
|
11173
|
+
},
|
|
11174
|
+
{
|
|
11175
|
+
"code": 6257,
|
|
11176
|
+
"name": "CantReclaimRent",
|
|
11177
|
+
"msg": "CantReclaimRent"
|
|
11142
11178
|
}
|
|
11143
11179
|
]
|
|
11144
11180
|
}
|
package/lib/math/amm.js
CHANGED
|
@@ -456,12 +456,11 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
|
|
|
456
456
|
}
|
|
457
457
|
const quoteAssetReserveDelta = amm.quoteAssetReserve.div(numericConstants_1.BID_ASK_SPREAD_PRECISION.div(spreadFraction));
|
|
458
458
|
let quoteAssetReserve;
|
|
459
|
-
if ((
|
|
460
|
-
|
|
461
|
-
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
|
|
459
|
+
if (quoteAssetReserveDelta.gte(numericConstants_1.ZERO)) {
|
|
460
|
+
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta.abs());
|
|
462
461
|
}
|
|
463
462
|
else {
|
|
464
|
-
quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAssetReserveDelta);
|
|
463
|
+
quoteAssetReserve = amm.quoteAssetReserve.sub(quoteAssetReserveDelta.abs());
|
|
465
464
|
}
|
|
466
465
|
const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
|
|
467
466
|
return {
|
|
@@ -471,12 +470,18 @@ function calculateSpreadReserves(amm, oraclePriceData, now) {
|
|
|
471
470
|
}
|
|
472
471
|
const reservePrice = calculatePrice(amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier);
|
|
473
472
|
// always allow 10 bps of price offset, up to a fifth of the market's max_spread
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
473
|
+
let maxOffset = 0;
|
|
474
|
+
let referencePriceOffset = numericConstants_1.ZERO;
|
|
475
|
+
if (amm.curveUpdateIntensity > 100) {
|
|
476
|
+
maxOffset = Math.max(amm.maxSpread / 5, (numericConstants_1.PERCENTAGE_PRECISION.toNumber() / 10000) *
|
|
477
|
+
(amm.curveUpdateIntensity - 100));
|
|
478
|
+
const liquidityFraction = calculateInventoryLiquidityRatio(amm.baseAssetAmountWithAmm, amm.baseAssetReserve, amm.minBaseAssetReserve, amm.maxBaseAssetReserve);
|
|
479
|
+
const liquidityFractionSigned = liquidityFraction.mul((0, __1.sigNum)(amm.baseAssetAmountWithAmm.add(amm.baseAssetAmountWithUnsettledLp)));
|
|
480
|
+
referencePriceOffset = calculateReferencePriceOffset(reservePrice, amm.last24HAvgFundingRate, liquidityFractionSigned, amm.historicalOracleData.lastOraclePriceTwap5Min, amm.lastMarkPriceTwap5Min, amm.historicalOracleData.lastOraclePriceTwap, amm.lastMarkPriceTwap, maxOffset);
|
|
481
|
+
}
|
|
477
482
|
const [longSpread, shortSpread] = calculateSpread(amm, oraclePriceData, now, reservePrice);
|
|
478
483
|
const askReserves = calculateSpreadReserve(longSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.LONG, amm);
|
|
479
|
-
const bidReserves = calculateSpreadReserve(shortSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.SHORT, amm);
|
|
484
|
+
const bidReserves = calculateSpreadReserve(-shortSpread + referencePriceOffset.toNumber(), types_1.PositionDirection.SHORT, amm);
|
|
480
485
|
return [bidReserves, askReserves];
|
|
481
486
|
}
|
|
482
487
|
exports.calculateSpreadReserves = calculateSpreadReserves;
|
package/lib/math/orders.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare function standardizePrice(price: BN, tickSize: BN, direction: Pos
|
|
|
11
11
|
export declare function getLimitPrice(order: Order, oraclePriceData: OraclePriceData, slot: number, fallbackPrice?: BN): BN | undefined;
|
|
12
12
|
export declare function hasLimitPrice(order: Order, slot: number): boolean;
|
|
13
13
|
export declare function hasAuctionPrice(order: Order, slot: number): boolean;
|
|
14
|
-
export declare function isFillableByVAMM(order: Order, market: PerpMarketAccount, oraclePriceData: OraclePriceData, slot: number, ts: number): boolean;
|
|
14
|
+
export declare function isFillableByVAMM(order: Order, market: PerpMarketAccount, oraclePriceData: OraclePriceData, slot: number, ts: number, minAuctionDuration: number): boolean;
|
|
15
15
|
export declare function calculateBaseAssetAmountForAmmToFulfill(order: Order, market: PerpMarketAccount, oraclePriceData: OraclePriceData, slot: number): BN;
|
|
16
16
|
export declare function calculateBaseAssetAmountToFillUpToLimitPrice(order: Order, amm: AMM, limitPrice: BN, oraclePriceData: OraclePriceData): BN;
|
|
17
17
|
export declare function isOrderExpired(order: Order, ts: number, enforceBuffer?: boolean): boolean;
|
package/lib/math/orders.js
CHANGED
|
@@ -124,8 +124,8 @@ function hasAuctionPrice(order, slot) {
|
|
|
124
124
|
(!order.auctionStartPrice.eq(numericConstants_1.ZERO) || !order.auctionEndPrice.eq(numericConstants_1.ZERO)));
|
|
125
125
|
}
|
|
126
126
|
exports.hasAuctionPrice = hasAuctionPrice;
|
|
127
|
-
function isFillableByVAMM(order, market, oraclePriceData, slot, ts) {
|
|
128
|
-
return (((0, auction_1.
|
|
127
|
+
function isFillableByVAMM(order, market, oraclePriceData, slot, ts, minAuctionDuration) {
|
|
128
|
+
return (((0, auction_1.isFallbackAvailableLiquiditySource)(order, minAuctionDuration, slot) &&
|
|
129
129
|
calculateBaseAssetAmountForAmmToFulfill(order, market, oraclePriceData, slot).gte(market.amm.minOrderSize)) ||
|
|
130
130
|
isOrderExpired(order, ts));
|
|
131
131
|
}
|
package/lib/math/state.js
CHANGED
|
@@ -22,6 +22,9 @@ function calculateInitUserFee(stateAccount) {
|
|
|
22
22
|
}
|
|
23
23
|
exports.calculateInitUserFee = calculateInitUserFee;
|
|
24
24
|
function getMaxNumberOfSubAccounts(stateAccount) {
|
|
25
|
+
if (stateAccount.maxNumberOfSubAccounts <= 5) {
|
|
26
|
+
return new __1.BN(stateAccount.maxNumberOfSubAccounts);
|
|
27
|
+
}
|
|
25
28
|
return new __1.BN(stateAccount.maxNumberOfSubAccounts).muln(100);
|
|
26
29
|
}
|
|
27
30
|
exports.getMaxNumberOfSubAccounts = getMaxNumberOfSubAccounts;
|
|
@@ -99,23 +99,25 @@ class PhoenixSubscriber {
|
|
|
99
99
|
return this.getL2Levels('asks');
|
|
100
100
|
}
|
|
101
101
|
*getL2Levels(side) {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
const basePrecision = new anchor_1.BN(Math.pow(10, this.market.data.header.baseParams.decimals) *
|
|
106
|
-
baseLotsToRawBaseUnits);
|
|
107
|
-
const pricePrecision = numericConstants_1.PRICE_PRECISION.div(tickSize);
|
|
108
|
-
const ladder = (0, phoenix_sdk_1.getMarketLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
|
|
102
|
+
const basePrecision = Math.pow(10, this.market.data.header.baseParams.decimals);
|
|
103
|
+
const pricePrecision = numericConstants_1.PRICE_PRECISION.toNumber();
|
|
104
|
+
const ladder = (0, phoenix_sdk_1.getMarketUiLadder)(this.market, this.lastSlot, this.lastUnixTimestamp, 20);
|
|
109
105
|
for (let i = 0; i < ladder[side].length; i++) {
|
|
110
|
-
const {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
106
|
+
const { price, quantity } = ladder[side][i];
|
|
107
|
+
try {
|
|
108
|
+
const size = new anchor_1.BN(quantity * basePrecision);
|
|
109
|
+
const updatedPrice = new anchor_1.BN(price * pricePrecision);
|
|
110
|
+
yield {
|
|
111
|
+
price: updatedPrice,
|
|
112
|
+
size,
|
|
113
|
+
sources: {
|
|
114
|
+
phoenix: size,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
async unsubscribe() {
|
|
@@ -25,16 +25,16 @@ class PriorityFeeSubscriber {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
get avgPriorityFee() {
|
|
28
|
-
return this.lastAvgStrategyResult;
|
|
28
|
+
return Math.floor(this.lastAvgStrategyResult);
|
|
29
29
|
}
|
|
30
30
|
get maxPriorityFee() {
|
|
31
|
-
return this.lastMaxStrategyResult;
|
|
31
|
+
return Math.floor(this.lastMaxStrategyResult);
|
|
32
32
|
}
|
|
33
33
|
get customPriorityFee() {
|
|
34
34
|
if (!this.customStrategy) {
|
|
35
35
|
console.error('Custom strategy not set');
|
|
36
36
|
}
|
|
37
|
-
return this.lastCustomStrategyResult;
|
|
37
|
+
return Math.floor(this.lastCustomStrategyResult);
|
|
38
38
|
}
|
|
39
39
|
async subscribe() {
|
|
40
40
|
if (this.intervalId) {
|
package/package.json
CHANGED
|
@@ -224,6 +224,16 @@ export const DevnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
224
224
|
launchTs: 1701967240000,
|
|
225
225
|
oracleSource: OracleSource.PYTH,
|
|
226
226
|
},
|
|
227
|
+
{
|
|
228
|
+
fullName: 'SEI',
|
|
229
|
+
category: ['L1'],
|
|
230
|
+
symbol: 'SEI-PERP',
|
|
231
|
+
baseAssetSymbol: 'SEI',
|
|
232
|
+
marketIndex: 21,
|
|
233
|
+
oracle: new PublicKey('B6KVbgqTRY33yDgjAnc1mWw4ATS4W5544xghayQscdt7'),
|
|
234
|
+
launchTs: 1703173331000,
|
|
235
|
+
oracleSource: OracleSource.PYTH,
|
|
236
|
+
},
|
|
227
237
|
];
|
|
228
238
|
|
|
229
239
|
export const MainnetPerpMarkets: PerpMarketConfig[] = [
|
|
@@ -437,6 +447,16 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [
|
|
|
437
447
|
launchTs: 1701967240000,
|
|
438
448
|
oracleSource: OracleSource.PYTH,
|
|
439
449
|
},
|
|
450
|
+
{
|
|
451
|
+
fullName: 'SEI',
|
|
452
|
+
category: ['L1'],
|
|
453
|
+
symbol: 'SEI-PERP',
|
|
454
|
+
baseAssetSymbol: 'SEI',
|
|
455
|
+
marketIndex: 21,
|
|
456
|
+
oracle: new PublicKey('6cUuAyAX3eXoiWkjFF77RQBEUF15AAMQ7d1hm4EPd3tv'),
|
|
457
|
+
launchTs: 1703173331000,
|
|
458
|
+
oracleSource: OracleSource.PYTH,
|
|
459
|
+
},
|
|
440
460
|
];
|
|
441
461
|
|
|
442
462
|
export const PerpMarkets: { [key in DriftEnv]: PerpMarketConfig[] } = {
|
package/src/driftClient.ts
CHANGED
|
@@ -1112,6 +1112,31 @@ export class DriftClient {
|
|
|
1112
1112
|
);
|
|
1113
1113
|
}
|
|
1114
1114
|
|
|
1115
|
+
public async deleteUser(
|
|
1116
|
+
subAccountId = 0,
|
|
1117
|
+
txParams?: TxParams
|
|
1118
|
+
): Promise<TransactionSignature> {
|
|
1119
|
+
const userAccountPublicKey = getUserAccountPublicKeySync(
|
|
1120
|
+
this.program.programId,
|
|
1121
|
+
this.wallet.publicKey,
|
|
1122
|
+
subAccountId
|
|
1123
|
+
);
|
|
1124
|
+
|
|
1125
|
+
const ix = await this.getUserDeletionIx(userAccountPublicKey);
|
|
1126
|
+
|
|
1127
|
+
const { txSig } = await this.sendTransaction(
|
|
1128
|
+
await this.buildTransaction(ix, txParams),
|
|
1129
|
+
[],
|
|
1130
|
+
this.opts
|
|
1131
|
+
);
|
|
1132
|
+
|
|
1133
|
+
const userMapKey = this.getUserMapKey(subAccountId, this.wallet.publicKey);
|
|
1134
|
+
await this.users.get(userMapKey)?.unsubscribe();
|
|
1135
|
+
this.users.delete(userMapKey);
|
|
1136
|
+
|
|
1137
|
+
return txSig;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1115
1140
|
public async getUserDeletionIx(userAccountPublicKey: PublicKey) {
|
|
1116
1141
|
const ix = await this.program.instruction.deleteUser({
|
|
1117
1142
|
accounts: {
|
|
@@ -1125,7 +1150,7 @@ export class DriftClient {
|
|
|
1125
1150
|
return ix;
|
|
1126
1151
|
}
|
|
1127
1152
|
|
|
1128
|
-
public async
|
|
1153
|
+
public async reclaimRent(
|
|
1129
1154
|
subAccountId = 0,
|
|
1130
1155
|
txParams?: TxParams
|
|
1131
1156
|
): Promise<TransactionSignature> {
|
|
@@ -1135,7 +1160,7 @@ export class DriftClient {
|
|
|
1135
1160
|
subAccountId
|
|
1136
1161
|
);
|
|
1137
1162
|
|
|
1138
|
-
const ix = await this.
|
|
1163
|
+
const ix = await this.getReclaimRentIx(userAccountPublicKey);
|
|
1139
1164
|
|
|
1140
1165
|
const { txSig } = await this.sendTransaction(
|
|
1141
1166
|
await this.buildTransaction(ix, txParams),
|
|
@@ -1143,13 +1168,21 @@ export class DriftClient {
|
|
|
1143
1168
|
this.opts
|
|
1144
1169
|
);
|
|
1145
1170
|
|
|
1146
|
-
const userMapKey = this.getUserMapKey(subAccountId, this.wallet.publicKey);
|
|
1147
|
-
await this.users.get(userMapKey)?.unsubscribe();
|
|
1148
|
-
this.users.delete(userMapKey);
|
|
1149
|
-
|
|
1150
1171
|
return txSig;
|
|
1151
1172
|
}
|
|
1152
1173
|
|
|
1174
|
+
public async getReclaimRentIx(userAccountPublicKey: PublicKey) {
|
|
1175
|
+
return await this.program.instruction.reclaimRent({
|
|
1176
|
+
accounts: {
|
|
1177
|
+
user: userAccountPublicKey,
|
|
1178
|
+
userStats: this.getUserStatsAccountPublicKey(),
|
|
1179
|
+
authority: this.wallet.publicKey,
|
|
1180
|
+
state: await this.getStatePublicKey(),
|
|
1181
|
+
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
|
1182
|
+
},
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1153
1186
|
public getUser(subAccountId?: number, authority?: PublicKey): User {
|
|
1154
1187
|
subAccountId = subAccountId ?? this.activeSubAccountId;
|
|
1155
1188
|
authority = authority ?? this.authority;
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.52.0",
|
|
3
3
|
"name": "drift",
|
|
4
4
|
"instructions": [
|
|
5
5
|
{
|
|
@@ -1173,6 +1173,37 @@
|
|
|
1173
1173
|
],
|
|
1174
1174
|
"args": []
|
|
1175
1175
|
},
|
|
1176
|
+
{
|
|
1177
|
+
"name": "reclaimRent",
|
|
1178
|
+
"accounts": [
|
|
1179
|
+
{
|
|
1180
|
+
"name": "user",
|
|
1181
|
+
"isMut": true,
|
|
1182
|
+
"isSigner": false
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
"name": "userStats",
|
|
1186
|
+
"isMut": true,
|
|
1187
|
+
"isSigner": false
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
"name": "state",
|
|
1191
|
+
"isMut": false,
|
|
1192
|
+
"isSigner": false
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
"name": "authority",
|
|
1196
|
+
"isMut": false,
|
|
1197
|
+
"isSigner": true
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
"name": "rent",
|
|
1201
|
+
"isMut": false,
|
|
1202
|
+
"isSigner": false
|
|
1203
|
+
}
|
|
1204
|
+
],
|
|
1205
|
+
"args": []
|
|
1206
|
+
},
|
|
1176
1207
|
{
|
|
1177
1208
|
"name": "fillPerpOrder",
|
|
1178
1209
|
"accounts": [
|
|
@@ -11139,6 +11170,11 @@
|
|
|
11139
11170
|
"code": 6256,
|
|
11140
11171
|
"name": "CantPayUserInitFee",
|
|
11141
11172
|
"msg": "CantPayUserInitFee"
|
|
11173
|
+
},
|
|
11174
|
+
{
|
|
11175
|
+
"code": 6257,
|
|
11176
|
+
"name": "CantReclaimRent",
|
|
11177
|
+
"msg": "CantReclaimRent"
|
|
11142
11178
|
}
|
|
11143
11179
|
]
|
|
11144
11180
|
}
|
package/src/math/amm.ts
CHANGED
|
@@ -863,13 +863,14 @@ export function calculateSpreadReserves(
|
|
|
863
863
|
);
|
|
864
864
|
|
|
865
865
|
let quoteAssetReserve;
|
|
866
|
-
if (
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
quoteAssetReserve = amm.quoteAssetReserve.add(quoteAssetReserveDelta);
|
|
866
|
+
if (quoteAssetReserveDelta.gte(ZERO)) {
|
|
867
|
+
quoteAssetReserve = amm.quoteAssetReserve.add(
|
|
868
|
+
quoteAssetReserveDelta.abs()
|
|
869
|
+
);
|
|
871
870
|
} else {
|
|
872
|
-
quoteAssetReserve = amm.quoteAssetReserve.sub(
|
|
871
|
+
quoteAssetReserve = amm.quoteAssetReserve.sub(
|
|
872
|
+
quoteAssetReserveDelta.abs()
|
|
873
|
+
);
|
|
873
874
|
}
|
|
874
875
|
|
|
875
876
|
const baseAssetReserve = amm.sqrtK.mul(amm.sqrtK).div(quoteAssetReserve);
|
|
@@ -886,26 +887,35 @@ export function calculateSpreadReserves(
|
|
|
886
887
|
);
|
|
887
888
|
|
|
888
889
|
// always allow 10 bps of price offset, up to a fifth of the market's max_spread
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
890
|
+
let maxOffset = 0;
|
|
891
|
+
let referencePriceOffset = ZERO;
|
|
892
|
+
if (amm.curveUpdateIntensity > 100) {
|
|
893
|
+
maxOffset = Math.max(
|
|
894
|
+
amm.maxSpread / 5,
|
|
895
|
+
(PERCENTAGE_PRECISION.toNumber() / 10000) *
|
|
896
|
+
(amm.curveUpdateIntensity - 100)
|
|
897
|
+
);
|
|
898
|
+
|
|
899
|
+
const liquidityFraction = calculateInventoryLiquidityRatio(
|
|
900
|
+
amm.baseAssetAmountWithAmm,
|
|
901
|
+
amm.baseAssetReserve,
|
|
902
|
+
amm.minBaseAssetReserve,
|
|
903
|
+
amm.maxBaseAssetReserve
|
|
904
|
+
);
|
|
905
|
+
const liquidityFractionSigned = liquidityFraction.mul(
|
|
906
|
+
sigNum(amm.baseAssetAmountWithAmm.add(amm.baseAssetAmountWithUnsettledLp))
|
|
907
|
+
);
|
|
908
|
+
referencePriceOffset = calculateReferencePriceOffset(
|
|
909
|
+
reservePrice,
|
|
910
|
+
amm.last24HAvgFundingRate,
|
|
911
|
+
liquidityFractionSigned,
|
|
912
|
+
amm.historicalOracleData.lastOraclePriceTwap5Min,
|
|
913
|
+
amm.lastMarkPriceTwap5Min,
|
|
914
|
+
amm.historicalOracleData.lastOraclePriceTwap,
|
|
915
|
+
amm.lastMarkPriceTwap,
|
|
916
|
+
maxOffset
|
|
917
|
+
);
|
|
918
|
+
}
|
|
909
919
|
|
|
910
920
|
const [longSpread, shortSpread] = calculateSpread(
|
|
911
921
|
amm,
|
|
@@ -920,7 +930,7 @@ export function calculateSpreadReserves(
|
|
|
920
930
|
amm
|
|
921
931
|
);
|
|
922
932
|
const bidReserves = calculateSpreadReserve(
|
|
923
|
-
shortSpread + referencePriceOffset.toNumber(),
|
|
933
|
+
-shortSpread + referencePriceOffset.toNumber(),
|
|
924
934
|
PositionDirection.SHORT,
|
|
925
935
|
amm
|
|
926
936
|
);
|
package/src/math/orders.ts
CHANGED
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
import { ZERO, TWO } from '../constants/numericConstants';
|
|
11
11
|
import { BN } from '@coral-xyz/anchor';
|
|
12
12
|
import { OraclePriceData } from '../oracles/types';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getAuctionPrice,
|
|
15
|
+
isAuctionComplete,
|
|
16
|
+
isFallbackAvailableLiquiditySource,
|
|
17
|
+
} from './auction';
|
|
14
18
|
import {
|
|
15
19
|
calculateMaxBaseAssetAmountFillable,
|
|
16
20
|
calculateMaxBaseAssetAmountToTrade,
|
|
@@ -186,10 +190,11 @@ export function isFillableByVAMM(
|
|
|
186
190
|
market: PerpMarketAccount,
|
|
187
191
|
oraclePriceData: OraclePriceData,
|
|
188
192
|
slot: number,
|
|
189
|
-
ts: number
|
|
193
|
+
ts: number,
|
|
194
|
+
minAuctionDuration: number
|
|
190
195
|
): boolean {
|
|
191
196
|
return (
|
|
192
|
-
(
|
|
197
|
+
(isFallbackAvailableLiquiditySource(order, minAuctionDuration, slot) &&
|
|
193
198
|
calculateBaseAssetAmountForAmmToFulfill(
|
|
194
199
|
order,
|
|
195
200
|
market,
|
package/src/math/state.ts
CHANGED
|
@@ -22,5 +22,8 @@ export function calculateInitUserFee(stateAccount: StateAccount): BN {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function getMaxNumberOfSubAccounts(stateAccount: StateAccount): BN {
|
|
25
|
+
if (stateAccount.maxNumberOfSubAccounts <= 5) {
|
|
26
|
+
return new BN(stateAccount.maxNumberOfSubAccounts);
|
|
27
|
+
}
|
|
25
28
|
return new BN(stateAccount.maxNumberOfSubAccounts).muln(100);
|
|
26
29
|
}
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
toNum,
|
|
7
7
|
getMarketUiLadder,
|
|
8
8
|
Market,
|
|
9
|
-
getMarketLadder,
|
|
10
9
|
} from '@ellipsis-labs/phoenix-sdk';
|
|
11
10
|
import { PRICE_PRECISION } from '../constants/numericConstants';
|
|
12
11
|
import { BN } from '@coral-xyz/anchor';
|
|
@@ -164,18 +163,14 @@ export class PhoenixSubscriber implements L2OrderBookGenerator {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
*getL2Levels(side: 'bids' | 'asks'): Generator<L2Level> {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const basePrecision = new BN(
|
|
172
|
-
Math.pow(10, this.market.data.header.baseParams.decimals) *
|
|
173
|
-
baseLotsToRawBaseUnits
|
|
166
|
+
const basePrecision = Math.pow(
|
|
167
|
+
10,
|
|
168
|
+
this.market.data.header.baseParams.decimals
|
|
174
169
|
);
|
|
175
170
|
|
|
176
|
-
const pricePrecision = PRICE_PRECISION.
|
|
171
|
+
const pricePrecision = PRICE_PRECISION.toNumber();
|
|
177
172
|
|
|
178
|
-
const ladder =
|
|
173
|
+
const ladder = getMarketUiLadder(
|
|
179
174
|
this.market,
|
|
180
175
|
this.lastSlot,
|
|
181
176
|
this.lastUnixTimestamp,
|
|
@@ -183,18 +178,22 @@ export class PhoenixSubscriber implements L2OrderBookGenerator {
|
|
|
183
178
|
);
|
|
184
179
|
|
|
185
180
|
for (let i = 0; i < ladder[side].length; i++) {
|
|
186
|
-
const {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
181
|
+
const { price, quantity } = ladder[side][i];
|
|
182
|
+
try {
|
|
183
|
+
const size = new BN(quantity * basePrecision);
|
|
184
|
+
const updatedPrice = new BN(price * pricePrecision);
|
|
185
|
+
yield {
|
|
186
|
+
price: updatedPrice,
|
|
187
|
+
size,
|
|
188
|
+
sources: {
|
|
189
|
+
phoenix: size,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
} catch {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
-
|
|
198
197
|
public async unsubscribe(): Promise<void> {
|
|
199
198
|
if (!this.subscribed) {
|
|
200
199
|
return;
|
|
@@ -46,18 +46,18 @@ export class PriorityFeeSubscriber {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
public get avgPriorityFee(): number {
|
|
49
|
-
return this.lastAvgStrategyResult;
|
|
49
|
+
return Math.floor(this.lastAvgStrategyResult);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
public get maxPriorityFee(): number {
|
|
53
|
-
return this.lastMaxStrategyResult;
|
|
53
|
+
return Math.floor(this.lastMaxStrategyResult);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
public get customPriorityFee(): number {
|
|
57
57
|
if (!this.customStrategy) {
|
|
58
58
|
console.error('Custom strategy not set');
|
|
59
59
|
}
|
|
60
|
-
return this.lastCustomStrategyResult;
|
|
60
|
+
return Math.floor(this.lastCustomStrategyResult);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
public async subscribe(): Promise<void> {
|
package/tests/amm/test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
calculateSpread,
|
|
9
9
|
calculateSpreadBN,
|
|
10
10
|
ZERO,
|
|
11
|
+
sigNum,
|
|
11
12
|
ONE,
|
|
12
13
|
calculateLiveOracleStd,
|
|
13
14
|
calculateLiveOracleTwap,
|
|
@@ -465,7 +466,7 @@ describe('AMM Tests', () => {
|
|
|
465
466
|
|
|
466
467
|
mockAmm.baseAssetAmountWithAmm = new BN(0);
|
|
467
468
|
mockAmm.pegMultiplier = new BN(13.553 * PEG_PRECISION.toNumber());
|
|
468
|
-
mockAmm.ammJitIntensity =
|
|
469
|
+
mockAmm.ammJitIntensity = 200;
|
|
469
470
|
mockAmm.curveUpdateIntensity = 200;
|
|
470
471
|
mockAmm.baseSpread = 2500;
|
|
471
472
|
mockAmm.maxSpread = 25000;
|
|
@@ -521,13 +522,13 @@ describe('AMM Tests', () => {
|
|
|
521
522
|
console.log('amm.baseAssetReserve:', mockAmm.baseAssetReserve.toString());
|
|
522
523
|
assert(mockAmm.baseAssetReserve.eq(new BN('1000000000')));
|
|
523
524
|
const reserves2 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
524
|
-
console.log(reserves2[
|
|
525
|
-
console.log(reserves2[
|
|
525
|
+
console.log(reserves2[0].baseAssetReserve.toString());
|
|
526
|
+
console.log(reserves2[0].quoteAssetReserve.toString());
|
|
526
527
|
|
|
527
|
-
assert(reserves2[0].baseAssetReserve.eq(new BN('
|
|
528
|
-
assert(reserves2[0].quoteAssetReserve.eq(new BN('
|
|
529
|
-
assert(reserves2[1].baseAssetReserve.eq(new BN('
|
|
530
|
-
assert(reserves2[1].quoteAssetReserve.eq(new BN('
|
|
528
|
+
assert(reserves2[0].baseAssetReserve.eq(new BN('1005050504')));
|
|
529
|
+
assert(reserves2[0].quoteAssetReserve.eq(new BN('994974875')));
|
|
530
|
+
assert(reserves2[1].baseAssetReserve.eq(new BN('992537314')));
|
|
531
|
+
assert(reserves2[1].quoteAssetReserve.eq(new BN('1007518796')));
|
|
531
532
|
|
|
532
533
|
// create imbalance for reference price offset
|
|
533
534
|
mockAmm.baseAssetReserve = new BN(1000000000 * 1.1);
|
|
@@ -536,11 +537,12 @@ describe('AMM Tests', () => {
|
|
|
536
537
|
mockAmm.baseAssetReserve.mul(mockAmm.quoteAssetReserve)
|
|
537
538
|
);
|
|
538
539
|
|
|
539
|
-
mockAmm.baseAssetAmountWithAmm = new BN(
|
|
540
|
+
mockAmm.baseAssetAmountWithAmm = new BN(1000000000 * 0.1);
|
|
540
541
|
|
|
541
542
|
const maxOffset = Math.max(
|
|
542
543
|
mockAmm.maxSpread / 5,
|
|
543
|
-
PERCENTAGE_PRECISION.toNumber() /
|
|
544
|
+
(PERCENTAGE_PRECISION.toNumber() / 10000) *
|
|
545
|
+
(mockAmm.curveUpdateIntensity - 100)
|
|
544
546
|
);
|
|
545
547
|
const liquidityFraction = calculateInventoryLiquidityRatio(
|
|
546
548
|
mockAmm.baseAssetAmountWithAmm,
|
|
@@ -550,11 +552,17 @@ describe('AMM Tests', () => {
|
|
|
550
552
|
);
|
|
551
553
|
console.log('liquidityFraction:', liquidityFraction.toString());
|
|
552
554
|
assert(liquidityFraction.eq(new BN(1000000))); // full
|
|
553
|
-
|
|
555
|
+
const liquidityFractionSigned = liquidityFraction.mul(
|
|
556
|
+
sigNum(
|
|
557
|
+
mockAmm.baseAssetAmountWithAmm.add(
|
|
558
|
+
mockAmm.baseAssetAmountWithUnsettledLp
|
|
559
|
+
)
|
|
560
|
+
)
|
|
561
|
+
);
|
|
554
562
|
const referencePriceOffset = calculateReferencePriceOffset(
|
|
555
563
|
reservePrice,
|
|
556
564
|
mockAmm.last24HAvgFundingRate,
|
|
557
|
-
|
|
565
|
+
liquidityFractionSigned,
|
|
558
566
|
mockAmm.historicalOracleData.lastOraclePriceTwap5Min,
|
|
559
567
|
mockAmm.lastMarkPriceTwap5Min,
|
|
560
568
|
mockAmm.historicalOracleData.lastOraclePriceTwap,
|
|
@@ -562,17 +570,304 @@ describe('AMM Tests', () => {
|
|
|
562
570
|
maxOffset
|
|
563
571
|
);
|
|
564
572
|
console.log('referencePriceOffset:', referencePriceOffset.toString());
|
|
565
|
-
assert(referencePriceOffset.eq(new BN(
|
|
573
|
+
assert(referencePriceOffset.eq(new BN(10000)));
|
|
566
574
|
assert(referencePriceOffset.eq(new BN(maxOffset)));
|
|
567
575
|
|
|
576
|
+
// mockAmm.curveUpdateIntensity = 100;
|
|
577
|
+
const reserves3 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
578
|
+
console.log(reserves3[0].baseAssetReserve.toString());
|
|
579
|
+
console.log(reserves3[0].quoteAssetReserve.toString());
|
|
580
|
+
|
|
581
|
+
assert(reserves3[0].baseAssetReserve.eq(new BN('1094581278')));
|
|
582
|
+
assert(reserves3[0].quoteAssetReserve.eq(new BN('913591359')));
|
|
583
|
+
assert(reserves3[1].baseAssetReserve.eq(new BN('989999998')));
|
|
584
|
+
assert(reserves3[1].quoteAssetReserve.eq(new BN('1010101010')));
|
|
585
|
+
|
|
586
|
+
const p1 = calculatePrice(
|
|
587
|
+
reserves3[0].baseAssetReserve,
|
|
588
|
+
reserves3[0].quoteAssetReserve,
|
|
589
|
+
mockAmm.pegMultiplier
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
const p2 = calculatePrice(
|
|
593
|
+
reserves3[1].baseAssetReserve,
|
|
594
|
+
reserves3[1].quoteAssetReserve,
|
|
595
|
+
mockAmm.pegMultiplier
|
|
596
|
+
);
|
|
597
|
+
console.log(p1.toString(), p2.toString());
|
|
598
|
+
|
|
599
|
+
assert(p1.eq(new BN(11312000)));
|
|
600
|
+
assert(p2.eq(new BN(13828180)));
|
|
601
|
+
|
|
602
|
+
mockAmm.curveUpdateIntensity = 110;
|
|
603
|
+
const reserves4 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
604
|
+
console.log(reserves4[1].baseAssetReserve.toString());
|
|
605
|
+
console.log(reserves4[1].quoteAssetReserve.toString());
|
|
606
|
+
|
|
607
|
+
assert(reserves4[0].baseAssetReserve.eq(new BN('1097323599')));
|
|
608
|
+
assert(reserves4[0].quoteAssetReserve.eq(new BN('911308203')));
|
|
609
|
+
assert(reserves4[1].baseAssetReserve.eq(new BN('989999998')));
|
|
610
|
+
assert(reserves4[1].quoteAssetReserve.eq(new BN('1010101010')));
|
|
611
|
+
|
|
612
|
+
const p1RF = calculatePrice(
|
|
613
|
+
reserves4[0].baseAssetReserve,
|
|
614
|
+
reserves4[0].quoteAssetReserve,
|
|
615
|
+
mockAmm.pegMultiplier
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
const p2RF = calculatePrice(
|
|
619
|
+
reserves4[1].baseAssetReserve,
|
|
620
|
+
reserves4[1].quoteAssetReserve,
|
|
621
|
+
mockAmm.pegMultiplier
|
|
622
|
+
);
|
|
623
|
+
console.log(p1RF.toString(), p2RF.toString());
|
|
624
|
+
|
|
625
|
+
assert(p1RF.eq(new BN(11255531)));
|
|
626
|
+
assert(p2RF.eq(new BN(13828180)));
|
|
627
|
+
// no ref price offset at 100
|
|
628
|
+
mockAmm.curveUpdateIntensity = 100;
|
|
629
|
+
const reserves5 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
630
|
+
console.log(reserves5[0].baseAssetReserve.toString());
|
|
631
|
+
console.log(reserves5[0].quoteAssetReserve.toString());
|
|
632
|
+
|
|
633
|
+
assert(reserves5[0].baseAssetReserve.eq(new BN('1100068201')));
|
|
634
|
+
assert(reserves5[0].quoteAssetReserve.eq(new BN('909034546')));
|
|
635
|
+
assert(reserves5[1].baseAssetReserve.eq(new BN('989999998')));
|
|
636
|
+
assert(reserves5[1].quoteAssetReserve.eq(new BN('1010101010')));
|
|
637
|
+
|
|
638
|
+
const p1RFNone = calculatePrice(
|
|
639
|
+
reserves5[0].baseAssetReserve,
|
|
640
|
+
reserves5[0].quoteAssetReserve,
|
|
641
|
+
mockAmm.pegMultiplier
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
const p2RFNone = calculatePrice(
|
|
645
|
+
reserves5[1].baseAssetReserve,
|
|
646
|
+
reserves5[1].quoteAssetReserve,
|
|
647
|
+
mockAmm.pegMultiplier
|
|
648
|
+
);
|
|
649
|
+
console.log(p1RFNone.toString(), p2RFNone.toString());
|
|
650
|
+
|
|
651
|
+
assert(p1RFNone.eq(new BN(11199437)));
|
|
652
|
+
assert(p2RFNone.eq(new BN(13828180)));
|
|
653
|
+
assert(p1RF.sub(p1RFNone).eq(new BN(56094)));
|
|
654
|
+
assert(p2RF.sub(p2RFNone).eq(new BN(0))); // todo?
|
|
655
|
+
});
|
|
656
|
+
it('Spread Reserves (with negative offset)', () => {
|
|
657
|
+
const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
|
|
658
|
+
const mockMarket1 = myMockPerpMarkets[0];
|
|
659
|
+
const mockAmm = mockMarket1.amm;
|
|
660
|
+
const now = new BN(new Date().getTime() / 1000); //todo
|
|
661
|
+
|
|
662
|
+
const oraclePriceData = {
|
|
663
|
+
price: new BN(13.553 * PRICE_PRECISION.toNumber()),
|
|
664
|
+
slot: new BN(68 + 1),
|
|
665
|
+
confidence: new BN(1),
|
|
666
|
+
hasSufficientNumberOfDataPoints: true,
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const reserves = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
670
|
+
assert(reserves[0].baseAssetReserve.eq(new BN('1000000000')));
|
|
671
|
+
assert(reserves[0].quoteAssetReserve.eq(new BN('12000000000')));
|
|
672
|
+
assert(reserves[1].baseAssetReserve.eq(new BN('1000000000')));
|
|
673
|
+
assert(reserves[1].quoteAssetReserve.eq(new BN('12000000000')));
|
|
674
|
+
|
|
675
|
+
mockAmm.baseAssetReserve = new BN(1000000000);
|
|
676
|
+
mockAmm.quoteAssetReserve = new BN(1000000000);
|
|
677
|
+
mockAmm.sqrtK = new BN(1000000000);
|
|
678
|
+
|
|
679
|
+
mockAmm.baseAssetAmountWithAmm = new BN(0);
|
|
680
|
+
mockAmm.pegMultiplier = new BN(13.553 * PEG_PRECISION.toNumber());
|
|
681
|
+
mockAmm.ammJitIntensity = 200;
|
|
682
|
+
mockAmm.curveUpdateIntensity = 200;
|
|
683
|
+
mockAmm.baseSpread = 2500;
|
|
684
|
+
mockAmm.maxSpread = 25000;
|
|
685
|
+
|
|
686
|
+
mockAmm.last24HAvgFundingRate = new BN(-7590328523);
|
|
687
|
+
|
|
688
|
+
mockAmm.lastMarkPriceTwap = new BN(
|
|
689
|
+
(oraclePriceData.price.toNumber() / 1e6 + 0.01) * 1e6
|
|
690
|
+
);
|
|
691
|
+
mockAmm.historicalOracleData.lastOraclePriceTwap = new BN(
|
|
692
|
+
(oraclePriceData.price.toNumber() / 1e6 - 0.015) * 1e6
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
mockAmm.historicalOracleData.lastOraclePriceTwap5Min = new BN(
|
|
696
|
+
(oraclePriceData.price.toNumber() / 1e6 + 0.005) * 1e6
|
|
697
|
+
);
|
|
698
|
+
mockAmm.lastMarkPriceTwap5Min = new BN(
|
|
699
|
+
(oraclePriceData.price.toNumber() / 1e6 - 0.005) * 1e6
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
console.log('starting rr:');
|
|
703
|
+
let reservePrice = undefined;
|
|
704
|
+
if (!reservePrice) {
|
|
705
|
+
reservePrice = calculatePrice(
|
|
706
|
+
mockAmm.baseAssetReserve,
|
|
707
|
+
mockAmm.quoteAssetReserve,
|
|
708
|
+
mockAmm.pegMultiplier
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const targetPrice = oraclePriceData?.price || reservePrice;
|
|
713
|
+
const confInterval = oraclePriceData.confidence || ZERO;
|
|
714
|
+
const targetMarkSpreadPct = reservePrice
|
|
715
|
+
.sub(targetPrice)
|
|
716
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
717
|
+
.div(reservePrice);
|
|
718
|
+
|
|
719
|
+
const confIntervalPct = confInterval
|
|
720
|
+
.mul(BID_ASK_SPREAD_PRECISION)
|
|
721
|
+
.div(reservePrice);
|
|
722
|
+
|
|
723
|
+
// now = now || new BN(new Date().getTime() / 1000); //todo
|
|
724
|
+
const liveOracleStd = calculateLiveOracleStd(mockAmm, oraclePriceData, now);
|
|
725
|
+
console.log('reservePrice:', reservePrice.toString());
|
|
726
|
+
console.log('targetMarkSpreadPct:', targetMarkSpreadPct.toString());
|
|
727
|
+
console.log('confIntervalPct:', confIntervalPct.toString());
|
|
728
|
+
|
|
729
|
+
console.log('liveOracleStd:', liveOracleStd.toString());
|
|
730
|
+
|
|
731
|
+
const tt = calculateSpread(mockAmm, oraclePriceData, now);
|
|
732
|
+
console.log(tt);
|
|
733
|
+
|
|
734
|
+
console.log('amm.baseAssetReserve:', mockAmm.baseAssetReserve.toString());
|
|
735
|
+
assert(mockAmm.baseAssetReserve.eq(new BN('1000000000')));
|
|
736
|
+
const reserves2 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
737
|
+
console.log(reserves2[1].baseAssetReserve.toString());
|
|
738
|
+
console.log(reserves2[1].quoteAssetReserve.toString());
|
|
739
|
+
|
|
740
|
+
assert(reserves2[0].baseAssetReserve.eq(new BN('1006289308')));
|
|
741
|
+
assert(reserves2[0].quoteAssetReserve.eq(new BN('993750000')));
|
|
742
|
+
assert(reserves2[1].baseAssetReserve.eq(new BN('993788819')));
|
|
743
|
+
assert(reserves2[1].quoteAssetReserve.eq(new BN('1006250000')));
|
|
744
|
+
|
|
745
|
+
// create imbalance for reference price offset
|
|
746
|
+
mockAmm.baseAssetReserve = new BN(1000000000 / 1.1);
|
|
747
|
+
mockAmm.quoteAssetReserve = new BN(1000000000 * 1.1);
|
|
748
|
+
mockAmm.sqrtK = squareRootBN(
|
|
749
|
+
mockAmm.baseAssetReserve.mul(mockAmm.quoteAssetReserve)
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
mockAmm.baseAssetAmountWithAmm = new BN(-1000000000 * 0.1);
|
|
753
|
+
|
|
754
|
+
const maxOffset = Math.max(
|
|
755
|
+
mockAmm.maxSpread / 5,
|
|
756
|
+
(PERCENTAGE_PRECISION.toNumber() / 10000) *
|
|
757
|
+
(mockAmm.curveUpdateIntensity - 100)
|
|
758
|
+
);
|
|
759
|
+
const liquidityFraction = calculateInventoryLiquidityRatio(
|
|
760
|
+
mockAmm.baseAssetAmountWithAmm,
|
|
761
|
+
mockAmm.baseAssetReserve,
|
|
762
|
+
mockAmm.minBaseAssetReserve,
|
|
763
|
+
mockAmm.maxBaseAssetReserve
|
|
764
|
+
);
|
|
765
|
+
console.log('liquidityFraction:', liquidityFraction.toString());
|
|
766
|
+
assert(liquidityFraction.eq(new BN(1000000))); // full
|
|
767
|
+
const liquidityFractionSigned = liquidityFraction.mul(
|
|
768
|
+
sigNum(
|
|
769
|
+
mockAmm.baseAssetAmountWithAmm.add(
|
|
770
|
+
mockAmm.baseAssetAmountWithUnsettledLp
|
|
771
|
+
)
|
|
772
|
+
)
|
|
773
|
+
);
|
|
774
|
+
const referencePriceOffset = calculateReferencePriceOffset(
|
|
775
|
+
reservePrice,
|
|
776
|
+
mockAmm.last24HAvgFundingRate,
|
|
777
|
+
liquidityFractionSigned,
|
|
778
|
+
mockAmm.historicalOracleData.lastOraclePriceTwap5Min,
|
|
779
|
+
mockAmm.lastMarkPriceTwap5Min,
|
|
780
|
+
mockAmm.historicalOracleData.lastOraclePriceTwap,
|
|
781
|
+
mockAmm.lastMarkPriceTwap,
|
|
782
|
+
maxOffset
|
|
783
|
+
);
|
|
784
|
+
console.log('referencePriceOffset:', referencePriceOffset.toString());
|
|
785
|
+
assert(referencePriceOffset.eq(new BN(-10000))); // neg
|
|
786
|
+
|
|
787
|
+
// assert(referencePriceOffset.eq(new BN(maxOffset)));
|
|
788
|
+
|
|
789
|
+
// mockAmm.curveUpdateIntensity = 100;
|
|
568
790
|
const reserves3 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
569
|
-
console.log(reserves3[
|
|
570
|
-
console.log(reserves3[
|
|
791
|
+
console.log(reserves3[0].baseAssetReserve.toString());
|
|
792
|
+
console.log(reserves3[0].quoteAssetReserve.toString());
|
|
793
|
+
|
|
794
|
+
assert(reserves3[0].baseAssetReserve.eq(new BN('1010101008')));
|
|
795
|
+
assert(reserves3[0].quoteAssetReserve.eq(new BN('990000000')));
|
|
796
|
+
assert(reserves3[1].baseAssetReserve.eq(new BN('913613747')));
|
|
797
|
+
assert(reserves3[1].quoteAssetReserve.eq(new BN('1094554456')));
|
|
798
|
+
|
|
799
|
+
const p1 = calculatePrice(
|
|
800
|
+
reserves3[0].baseAssetReserve,
|
|
801
|
+
reserves3[0].quoteAssetReserve,
|
|
802
|
+
mockAmm.pegMultiplier
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
const p2 = calculatePrice(
|
|
806
|
+
reserves3[1].baseAssetReserve,
|
|
807
|
+
reserves3[1].quoteAssetReserve,
|
|
808
|
+
mockAmm.pegMultiplier
|
|
809
|
+
);
|
|
810
|
+
console.log(p1.toString(), p2.toString());
|
|
811
|
+
|
|
812
|
+
assert(p1.eq(new BN(13283295)));
|
|
813
|
+
assert(p2.eq(new BN(16237164)));
|
|
814
|
+
|
|
815
|
+
mockAmm.curveUpdateIntensity = 110;
|
|
816
|
+
const reserves4 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
817
|
+
console.log(reserves4[1].baseAssetReserve.toString());
|
|
818
|
+
console.log(reserves4[1].quoteAssetReserve.toString());
|
|
819
|
+
|
|
820
|
+
assert(reserves4[0].baseAssetReserve.eq(new BN('999999998')));
|
|
821
|
+
assert(reserves4[0].quoteAssetReserve.eq(new BN('1000000000')));
|
|
822
|
+
assert(reserves4[1].baseAssetReserve.eq(new BN('911313622')));
|
|
823
|
+
assert(reserves4[1].quoteAssetReserve.eq(new BN('1097317074')));
|
|
824
|
+
|
|
825
|
+
const p1RF = calculatePrice(
|
|
826
|
+
reserves4[0].baseAssetReserve,
|
|
827
|
+
reserves4[0].quoteAssetReserve,
|
|
828
|
+
mockAmm.pegMultiplier
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
const p2RF = calculatePrice(
|
|
832
|
+
reserves4[1].baseAssetReserve,
|
|
833
|
+
reserves4[1].quoteAssetReserve,
|
|
834
|
+
mockAmm.pegMultiplier
|
|
835
|
+
);
|
|
836
|
+
console.log(p1RF.toString(), p2RF.toString());
|
|
837
|
+
|
|
838
|
+
assert(p1RF.eq(new BN(13553000)));
|
|
839
|
+
assert(p2RF.eq(new BN(16319231)));
|
|
840
|
+
|
|
841
|
+
// no ref price offset at 100
|
|
842
|
+
mockAmm.curveUpdateIntensity = 100;
|
|
843
|
+
const reserves5 = calculateSpreadReserves(mockAmm, oraclePriceData, now);
|
|
844
|
+
console.log(reserves5[0].baseAssetReserve.toString());
|
|
845
|
+
console.log(reserves5[0].quoteAssetReserve.toString());
|
|
846
|
+
|
|
847
|
+
assert(reserves5[0].baseAssetReserve.eq(new BN('999999998')));
|
|
848
|
+
assert(reserves5[0].quoteAssetReserve.eq(new BN('1000000000')));
|
|
849
|
+
assert(reserves5[1].baseAssetReserve.eq(new BN('909034547')));
|
|
850
|
+
assert(reserves5[1].quoteAssetReserve.eq(new BN('1100068200')));
|
|
851
|
+
|
|
852
|
+
const p1RFNone = calculatePrice(
|
|
853
|
+
reserves5[0].baseAssetReserve,
|
|
854
|
+
reserves5[0].quoteAssetReserve,
|
|
855
|
+
mockAmm.pegMultiplier
|
|
856
|
+
);
|
|
857
|
+
|
|
858
|
+
const p2RFNone = calculatePrice(
|
|
859
|
+
reserves5[1].baseAssetReserve,
|
|
860
|
+
reserves5[1].quoteAssetReserve,
|
|
861
|
+
mockAmm.pegMultiplier
|
|
862
|
+
);
|
|
863
|
+
console.log(p1RFNone.toString(), p2RFNone.toString());
|
|
571
864
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
assert(
|
|
575
|
-
assert(
|
|
865
|
+
const rr = p2RF.sub(p2RFNone).mul(PERCENTAGE_PRECISION).div(p2RF);
|
|
866
|
+
console.log(rr.toNumber());
|
|
867
|
+
assert(p1RFNone.eq(new BN(13553000)));
|
|
868
|
+
assert(p2RFNone.eq(new BN(16401163)));
|
|
869
|
+
assert(p1RF.sub(p1RFNone).eq(new BN(0))); // todo?
|
|
870
|
+
assert(rr.eq(new BN(-5020)));
|
|
576
871
|
});
|
|
577
872
|
|
|
578
873
|
it('live update functions', () => {
|