@drift-labs/sdk 0.1.29 → 0.1.30-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.
- package/lib/accounts/bulkAccountLoader.d.ts +2 -1
- package/lib/accounts/bulkAccountLoader.js +44 -32
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +15 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +50 -23
- package/lib/clearingHouse.d.ts +0 -1
- package/lib/clearingHouse.js +0 -14
- package/lib/constants/accounts.d.ts +15 -0
- package/lib/constants/accounts.js +18 -0
- package/lib/factory/clearingHouse.d.ts +14 -4
- package/lib/factory/clearingHouse.js +23 -6
- package/lib/idl/clearing_house.json +2 -33
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/math/amm.d.ts +20 -0
- package/lib/math/amm.js +151 -1
- package/lib/tx/retryTxSender.d.ts +5 -2
- package/lib/tx/retryTxSender.js +14 -1
- package/lib/util/promiseTimeout.d.ts +1 -0
- package/lib/util/promiseTimeout.js +14 -0
- package/package.json +1 -1
- package/src/accounts/bulkAccountLoader.ts +51 -40
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +73 -30
- package/src/clearingHouse.ts +0 -13
- package/src/constants/accounts.ts +26 -0
- package/src/factory/clearingHouse.ts +47 -7
- package/src/idl/clearing_house.json +2 -33
- package/src/index.ts +0 -1
- package/src/math/amm.ts +212 -1
- package/src/tx/retryTxSender.ts +19 -1
- package/src/util/promiseTimeout.ts +14 -0
- package/lib/math/repeg.d.ts +0 -32
- package/lib/math/repeg.js +0 -178
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -210
- package/src/accounts/pollingClearingHouseAccountSubscriber.js.map +0 -1
- package/src/accounts/pollingOracleSubscriber.js +0 -65
- package/src/accounts/pollingOracleSubscriber.js.map +0 -1
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -65
- package/src/accounts/pollingTokenAccountSubscriber.js.map +0 -1
- package/src/accounts/utils.js +0 -8
- package/src/accounts/utils.js.map +0 -1
- package/src/accounts/webSocketAccountSubscriber.js +0 -64
- package/src/accounts/webSocketAccountSubscriber.js.map +0 -1
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -212
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js.map +0 -1
- package/src/assert/assert.js +0 -10
- package/src/assert/assert.js.map +0 -1
- package/src/constants/markets.js +0 -167
- package/src/constants/markets.js.map +0 -1
- package/src/constants/numericConstants.js +0 -22
- package/src/constants/numericConstants.js.map +0 -1
- package/src/math/conversion.js +0 -16
- package/src/math/conversion.js.map +0 -1
- package/src/math/funding.js +0 -223
- package/src/math/funding.js.map +0 -1
- package/src/math/insuranceFund.js +0 -23
- package/src/math/insuranceFund.js.map +0 -1
- package/src/math/market.js +0 -30
- package/src/math/market.js.map +0 -1
- package/src/math/orders.js +0 -73
- package/src/math/orders.js.map +0 -1
- package/src/math/position.js +0 -121
- package/src/math/position.js.map +0 -1
- package/src/math/repeg.ts +0 -253
- package/src/math/trade.js +0 -182
- package/src/math/trade.js.map +0 -1
- package/src/math/utils.js +0 -27
- package/src/math/utils.js.map +0 -1
- package/src/oracles/switchboardClient.js +0 -60
- package/src/oracles/switchboardClient.js.map +0 -1
- package/src/oracles/types.js +0 -3
- package/src/oracles/types.js.map +0 -1
- package/src/token/index.js +0 -39
- package/src/token/index.js.map +0 -1
- package/src/tx/defaultTxSender.js +0 -13
- package/src/tx/defaultTxSender.js.map +0 -1
- package/src/tx/retryTxSender.js +0 -137
- package/src/tx/retryTxSender.js.map +0 -1
- package/src/tx/types.js +0 -3
- package/src/tx/types.js.map +0 -1
- package/src/tx/utils.js +0 -9
- package/src/tx/utils.js.map +0 -1
- package/src/util/computeUnits.js +0 -17
- package/src/util/computeUnits.js.map +0 -1
- package/src/util/tps.js +0 -17
- package/src/util/tps.js.map +0 -1
package/lib/math/amm.d.ts
CHANGED
|
@@ -38,6 +38,24 @@ export declare function calculateSwapOutput(inputAssetReserve: BN, swapAmount: B
|
|
|
38
38
|
* @param positionDirection
|
|
39
39
|
*/
|
|
40
40
|
export declare function getSwapDirection(inputAssetType: AssetType, positionDirection: PositionDirection): SwapDirection;
|
|
41
|
+
/**
|
|
42
|
+
* Helper function calculating adjust k cost
|
|
43
|
+
* @param market
|
|
44
|
+
* @param marketIndex
|
|
45
|
+
* @param numerator
|
|
46
|
+
* @param denomenator
|
|
47
|
+
* @returns cost : Precision QUOTE_ASSET_PRECISION
|
|
48
|
+
*/
|
|
49
|
+
export declare function calculateAdjustKCost(market: Market, marketIndex: BN, numerator: BN, denomenator: BN): BN;
|
|
50
|
+
/**
|
|
51
|
+
* Helper function calculating adjust pegMultiplier (repeg) cost
|
|
52
|
+
*
|
|
53
|
+
* @param market
|
|
54
|
+
* @param marketIndex
|
|
55
|
+
* @param newPeg
|
|
56
|
+
* @returns cost : Precision QUOTE_ASSET_PRECISION
|
|
57
|
+
*/
|
|
58
|
+
export declare function calculateRepegCost(market: Market, marketIndex: BN, newPeg: BN): BN;
|
|
41
59
|
/**
|
|
42
60
|
* Helper function calculating terminal price of amm
|
|
43
61
|
*
|
|
@@ -46,3 +64,5 @@ export declare function getSwapDirection(inputAssetType: AssetType, positionDire
|
|
|
46
64
|
*/
|
|
47
65
|
export declare function calculateTerminalPrice(market: Market): BN;
|
|
48
66
|
export declare function calculateMaxBaseAssetAmountToTrade(amm: AMM, limit_price: BN): [BN, PositionDirection];
|
|
67
|
+
export declare function calculateBudgetedK(market: Market, cost: BN): [BN, BN];
|
|
68
|
+
export declare function calculateBudgetedPeg(market: Market, cost: BN): BN;
|
package/lib/math/amm.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
|
|
3
|
+
exports.calculateBudgetedPeg = exports.calculateBudgetedK = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.calculateRepegCost = exports.calculateAdjustKCost = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
|
|
4
4
|
const anchor_1 = require("@project-serum/anchor");
|
|
5
5
|
const numericConstants_1 = require("../constants/numericConstants");
|
|
6
|
+
const position_1 = require("./position");
|
|
6
7
|
const types_1 = require("../types");
|
|
7
8
|
const assert_1 = require("../assert/assert");
|
|
8
9
|
const __1 = require("..");
|
|
@@ -87,6 +88,92 @@ function getSwapDirection(inputAssetType, positionDirection) {
|
|
|
87
88
|
return types_1.SwapDirection.ADD;
|
|
88
89
|
}
|
|
89
90
|
exports.getSwapDirection = getSwapDirection;
|
|
91
|
+
/**
|
|
92
|
+
* Helper function calculating adjust k cost
|
|
93
|
+
* @param market
|
|
94
|
+
* @param marketIndex
|
|
95
|
+
* @param numerator
|
|
96
|
+
* @param denomenator
|
|
97
|
+
* @returns cost : Precision QUOTE_ASSET_PRECISION
|
|
98
|
+
*/
|
|
99
|
+
function calculateAdjustKCost(market, marketIndex, numerator, denomenator) {
|
|
100
|
+
const netUserPosition = {
|
|
101
|
+
baseAssetAmount: market.baseAssetAmount,
|
|
102
|
+
lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
|
|
103
|
+
marketIndex: new anchor_1.BN(marketIndex),
|
|
104
|
+
quoteAssetAmount: new anchor_1.BN(0),
|
|
105
|
+
openOrders: new anchor_1.BN(0),
|
|
106
|
+
};
|
|
107
|
+
const currentValue = (0, position_1.calculateBaseAssetValue)(market, netUserPosition);
|
|
108
|
+
const marketNewK = Object.assign({}, market);
|
|
109
|
+
marketNewK.amm = Object.assign({}, market.amm);
|
|
110
|
+
marketNewK.amm.baseAssetReserve = market.amm.baseAssetReserve
|
|
111
|
+
.mul(numerator)
|
|
112
|
+
.div(denomenator);
|
|
113
|
+
marketNewK.amm.quoteAssetReserve = market.amm.quoteAssetReserve
|
|
114
|
+
.mul(numerator)
|
|
115
|
+
.div(denomenator);
|
|
116
|
+
marketNewK.amm.sqrtK = market.amm.sqrtK.mul(numerator).div(denomenator);
|
|
117
|
+
netUserPosition.quoteAssetAmount = currentValue;
|
|
118
|
+
const cost = (0, __1.calculatePositionPNL)(marketNewK, netUserPosition);
|
|
119
|
+
const p = numericConstants_1.PEG_PRECISION.mul(numerator).div(denomenator);
|
|
120
|
+
const x = market.amm.baseAssetReserve;
|
|
121
|
+
const y = market.amm.quoteAssetReserve;
|
|
122
|
+
const delta = market.baseAssetAmount;
|
|
123
|
+
const k = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
124
|
+
const numer1 = numericConstants_1.PEG_PRECISION.sub(p).mul(y).div(numericConstants_1.PEG_PRECISION);
|
|
125
|
+
const numer20 = k
|
|
126
|
+
.mul(p)
|
|
127
|
+
.mul(p)
|
|
128
|
+
.div(numericConstants_1.PEG_PRECISION)
|
|
129
|
+
.div(numericConstants_1.PEG_PRECISION)
|
|
130
|
+
.div(x.mul(p).div(numericConstants_1.PEG_PRECISION).add(delta));
|
|
131
|
+
const numer21 = k.div(x.add(delta));
|
|
132
|
+
const formulaCost = numer21
|
|
133
|
+
.sub(numer20)
|
|
134
|
+
.sub(numer1)
|
|
135
|
+
.mul(market.amm.pegMultiplier)
|
|
136
|
+
.div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
|
|
137
|
+
console.log((0, __1.convertToNumber)(formulaCost, numericConstants_1.QUOTE_PRECISION));
|
|
138
|
+
// p.div(p.mul(x).add(delta)).sub()
|
|
139
|
+
return cost;
|
|
140
|
+
}
|
|
141
|
+
exports.calculateAdjustKCost = calculateAdjustKCost;
|
|
142
|
+
/**
|
|
143
|
+
* Helper function calculating adjust pegMultiplier (repeg) cost
|
|
144
|
+
*
|
|
145
|
+
* @param market
|
|
146
|
+
* @param marketIndex
|
|
147
|
+
* @param newPeg
|
|
148
|
+
* @returns cost : Precision QUOTE_ASSET_PRECISION
|
|
149
|
+
*/
|
|
150
|
+
function calculateRepegCost(market, marketIndex, newPeg) {
|
|
151
|
+
const netUserPosition = {
|
|
152
|
+
baseAssetAmount: market.baseAssetAmount,
|
|
153
|
+
lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
|
|
154
|
+
marketIndex: new anchor_1.BN(marketIndex),
|
|
155
|
+
quoteAssetAmount: new anchor_1.BN(0),
|
|
156
|
+
openOrders: new anchor_1.BN(0),
|
|
157
|
+
};
|
|
158
|
+
const currentValue = (0, position_1.calculateBaseAssetValue)(market, netUserPosition);
|
|
159
|
+
netUserPosition.quoteAssetAmount = currentValue;
|
|
160
|
+
const prevMarketPrice = (0, __1.calculateMarkPrice)(market);
|
|
161
|
+
const marketNewPeg = Object.assign({}, market);
|
|
162
|
+
marketNewPeg.amm = Object.assign({}, market.amm);
|
|
163
|
+
// const marketNewPeg = JSON.parse(JSON.stringify(market));
|
|
164
|
+
marketNewPeg.amm.pegMultiplier = newPeg;
|
|
165
|
+
console.log('Price moves from', (0, __1.convertToNumber)(prevMarketPrice), 'to', (0, __1.convertToNumber)((0, __1.calculateMarkPrice)(marketNewPeg)));
|
|
166
|
+
const cost = (0, __1.calculatePositionPNL)(marketNewPeg, netUserPosition);
|
|
167
|
+
const k = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
168
|
+
const newQuoteAssetReserve = k.div(market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount));
|
|
169
|
+
const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(market.amm.quoteAssetReserve);
|
|
170
|
+
const cost2 = deltaQuoteAssetReserves
|
|
171
|
+
.mul(market.amm.pegMultiplier.sub(newPeg))
|
|
172
|
+
.div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
|
|
173
|
+
console.log((0, __1.convertToNumber)(cost2, numericConstants_1.QUOTE_PRECISION));
|
|
174
|
+
return cost;
|
|
175
|
+
}
|
|
176
|
+
exports.calculateRepegCost = calculateRepegCost;
|
|
90
177
|
/**
|
|
91
178
|
* Helper function calculating terminal price of amm
|
|
92
179
|
*
|
|
@@ -132,3 +219,66 @@ function calculateMaxBaseAssetAmountToTrade(amm, limit_price) {
|
|
|
132
219
|
}
|
|
133
220
|
}
|
|
134
221
|
exports.calculateMaxBaseAssetAmountToTrade = calculateMaxBaseAssetAmountToTrade;
|
|
222
|
+
function calculateBudgetedK(market, cost) {
|
|
223
|
+
// wolframalpha.com
|
|
224
|
+
// (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
|
|
225
|
+
// p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
|
|
226
|
+
// todo: assumes k = x * y
|
|
227
|
+
// otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
|
|
228
|
+
// const k = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
229
|
+
const x = market.amm.baseAssetReserve;
|
|
230
|
+
const y = market.amm.quoteAssetReserve;
|
|
231
|
+
const d = market.baseAssetAmount;
|
|
232
|
+
const Q = market.amm.pegMultiplier;
|
|
233
|
+
const C = cost.mul(new anchor_1.BN(-1));
|
|
234
|
+
const numer1 = y.mul(d).mul(Q).div(numericConstants_1.AMM_RESERVE_PRECISION).div(numericConstants_1.PEG_PRECISION);
|
|
235
|
+
const numer2 = C.mul(x.add(d)).div(numericConstants_1.QUOTE_PRECISION);
|
|
236
|
+
const denom1 = C.mul(x)
|
|
237
|
+
.mul(x.add(d))
|
|
238
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
239
|
+
.div(numericConstants_1.QUOTE_PRECISION);
|
|
240
|
+
const denom2 = y
|
|
241
|
+
.mul(d)
|
|
242
|
+
.mul(d)
|
|
243
|
+
.mul(Q)
|
|
244
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
245
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
246
|
+
.div(numericConstants_1.PEG_PRECISION);
|
|
247
|
+
const numerator = d
|
|
248
|
+
.mul(numer1.add(numer2))
|
|
249
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
250
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
251
|
+
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
|
|
252
|
+
const denominator = denom1
|
|
253
|
+
.add(denom2)
|
|
254
|
+
.div(numericConstants_1.AMM_RESERVE_PRECISION)
|
|
255
|
+
.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO);
|
|
256
|
+
console.log(numerator, denominator);
|
|
257
|
+
// const p = (numerator).div(denominator);
|
|
258
|
+
// const formulaCost = (numer21.sub(numer20).sub(numer1)).mul(market.amm.pegMultiplier).div(AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO)
|
|
259
|
+
// console.log(convertToNumber(formulaCost, QUOTE_PRECISION))
|
|
260
|
+
return [numerator, denominator];
|
|
261
|
+
}
|
|
262
|
+
exports.calculateBudgetedK = calculateBudgetedK;
|
|
263
|
+
function calculateBudgetedPeg(market, cost) {
|
|
264
|
+
// wolframalpha.com
|
|
265
|
+
// (1/(x+d) - p/(x*p+d))*y*d*Q = C solve for p
|
|
266
|
+
// p = (d(y*d*Q - C(x+d))) / (C*x(x+d) + y*y*d*Q)
|
|
267
|
+
// todo: assumes k = x * y
|
|
268
|
+
// otherwise use: (y(1-p) + (kp^2/(x*p+d)) - k/(x+d)) * Q = C solve for p
|
|
269
|
+
const k = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
270
|
+
const x = market.amm.baseAssetReserve;
|
|
271
|
+
const y = market.amm.quoteAssetReserve;
|
|
272
|
+
const d = market.baseAssetAmount;
|
|
273
|
+
const Q = market.amm.pegMultiplier;
|
|
274
|
+
const C = cost.mul(new anchor_1.BN(-1));
|
|
275
|
+
const deltaQuoteAssetReserves = y.sub(k.div(x.add(d)));
|
|
276
|
+
const deltaPegMultiplier = C.mul(numericConstants_1.MARK_PRICE_PRECISION)
|
|
277
|
+
.div(deltaQuoteAssetReserves.div(numericConstants_1.AMM_TO_QUOTE_PRECISION_RATIO))
|
|
278
|
+
.mul(numericConstants_1.PEG_PRECISION)
|
|
279
|
+
.div(numericConstants_1.QUOTE_PRECISION);
|
|
280
|
+
console.log(Q.toNumber(), 'change by', deltaPegMultiplier.toNumber() / numericConstants_1.MARK_PRICE_PRECISION.toNumber());
|
|
281
|
+
const newPeg = Q.sub(deltaPegMultiplier.mul(numericConstants_1.PEG_PRECISION).div(numericConstants_1.MARK_PRICE_PRECISION));
|
|
282
|
+
return newPeg;
|
|
283
|
+
}
|
|
284
|
+
exports.calculateBudgetedPeg = calculateBudgetedPeg;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import { TxSender } from './types';
|
|
2
|
-
import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature } from '@solana/web3.js';
|
|
3
|
+
import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection } from '@solana/web3.js';
|
|
3
4
|
import { Provider } from '@project-serum/anchor';
|
|
4
5
|
declare type ResolveReference = {
|
|
5
6
|
resolve?: () => void;
|
|
@@ -8,12 +9,14 @@ export declare class RetryTxSender implements TxSender {
|
|
|
8
9
|
provider: Provider;
|
|
9
10
|
timeout: number;
|
|
10
11
|
retrySleep: number;
|
|
11
|
-
|
|
12
|
+
additionalConnections: Connection[];
|
|
13
|
+
constructor(provider: Provider, timeout?: number, retrySleep?: number, additionalConnections?: Connection[]);
|
|
12
14
|
send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
|
|
13
15
|
prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
|
|
14
16
|
confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
|
|
15
17
|
getTimestamp(): number;
|
|
16
18
|
sleep(reference: ResolveReference): Promise<void>;
|
|
17
19
|
promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null>;
|
|
20
|
+
sendToAdditionalConnections(rawTx: Buffer, opts: ConfirmOptions): void;
|
|
18
21
|
}
|
|
19
22
|
export {};
|
package/lib/tx/retryTxSender.js
CHANGED
|
@@ -18,10 +18,11 @@ const bs58_1 = __importDefault(require("bs58"));
|
|
|
18
18
|
const DEFAULT_TIMEOUT = 35000;
|
|
19
19
|
const DEFAULT_RETRY = 8000;
|
|
20
20
|
class RetryTxSender {
|
|
21
|
-
constructor(provider, timeout, retrySleep) {
|
|
21
|
+
constructor(provider, timeout, retrySleep, additionalConnections = new Array()) {
|
|
22
22
|
this.provider = provider;
|
|
23
23
|
this.timeout = timeout !== null && timeout !== void 0 ? timeout : DEFAULT_TIMEOUT;
|
|
24
24
|
this.retrySleep = retrySleep !== null && retrySleep !== void 0 ? retrySleep : DEFAULT_RETRY;
|
|
25
|
+
this.additionalConnections = additionalConnections;
|
|
25
26
|
}
|
|
26
27
|
send(tx, additionalSigners, opts) {
|
|
27
28
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -35,6 +36,7 @@ class RetryTxSender {
|
|
|
35
36
|
const rawTransaction = tx.serialize();
|
|
36
37
|
const startTime = this.getTimestamp();
|
|
37
38
|
const txid = yield this.provider.connection.sendRawTransaction(rawTransaction, opts);
|
|
39
|
+
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
38
40
|
let done = false;
|
|
39
41
|
const resolveReference = {
|
|
40
42
|
resolve: undefined,
|
|
@@ -55,6 +57,7 @@ class RetryTxSender {
|
|
|
55
57
|
console.error(e);
|
|
56
58
|
stopWaiting();
|
|
57
59
|
});
|
|
60
|
+
this.sendToAdditionalConnections(rawTransaction, opts);
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
}))();
|
|
@@ -149,5 +152,15 @@ class RetryTxSender {
|
|
|
149
152
|
return result;
|
|
150
153
|
});
|
|
151
154
|
}
|
|
155
|
+
sendToAdditionalConnections(rawTx, opts) {
|
|
156
|
+
this.additionalConnections.map((connection) => {
|
|
157
|
+
connection.sendRawTransaction(rawTx, opts).catch((e) => {
|
|
158
|
+
console.error(
|
|
159
|
+
// @ts-ignore
|
|
160
|
+
`error sending tx to additional connection ${connection._rpcEndpoint}`);
|
|
161
|
+
console.error(e);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
152
165
|
}
|
|
153
166
|
exports.RetryTxSender = RetryTxSender;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promiseTimeout = void 0;
|
|
4
|
+
function promiseTimeout(promise, timeoutMs) {
|
|
5
|
+
let timeoutId;
|
|
6
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
7
|
+
timeoutId = setTimeout(() => resolve(null), timeoutMs);
|
|
8
|
+
});
|
|
9
|
+
return Promise.race([promise, timeoutPromise]).then((result) => {
|
|
10
|
+
clearTimeout(timeoutId);
|
|
11
|
+
return result;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
exports.promiseTimeout = promiseTimeout;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
|
2
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
3
3
|
import { AccountData } from './types';
|
|
4
|
+
import { promiseTimeout } from '../util/promiseTimeout';
|
|
4
5
|
|
|
5
6
|
type AccountToLoad = {
|
|
6
7
|
publicKey: PublicKey;
|
|
@@ -10,7 +11,6 @@ type AccountToLoad = {
|
|
|
10
11
|
const GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE = 99;
|
|
11
12
|
|
|
12
13
|
const oneMinute = 60 * 1000;
|
|
13
|
-
const fiveMinutes = 5 * 60 * 1000;
|
|
14
14
|
|
|
15
15
|
export class BulkAccountLoader {
|
|
16
16
|
connection: Connection;
|
|
@@ -24,7 +24,7 @@ export class BulkAccountLoader {
|
|
|
24
24
|
loadPromise?: Promise<void>;
|
|
25
25
|
loadPromiseResolver: () => void;
|
|
26
26
|
loggingEnabled = false;
|
|
27
|
-
|
|
27
|
+
lastTimeLoadingPromiseCleared = Date.now();
|
|
28
28
|
|
|
29
29
|
public constructor(
|
|
30
30
|
connection: Connection,
|
|
@@ -43,10 +43,15 @@ export class BulkAccountLoader {
|
|
|
43
43
|
const existingSize = this.accountsToLoad.size;
|
|
44
44
|
|
|
45
45
|
const callbackId = uuidv4();
|
|
46
|
+
this.log(
|
|
47
|
+
`Adding account ${publicKey.toString()} callback id ${callbackId}`
|
|
48
|
+
);
|
|
46
49
|
const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
|
|
47
50
|
if (existingAccountToLoad) {
|
|
51
|
+
this.log(`account already exists`);
|
|
48
52
|
existingAccountToLoad.callbacks.set(callbackId, callback);
|
|
49
53
|
} else {
|
|
54
|
+
this.log(`account doesn't already exist. creating new callback map`);
|
|
50
55
|
const callbacks = new Map<string, (buffer: Buffer) => void>();
|
|
51
56
|
callbacks.set(callbackId, callback);
|
|
52
57
|
const newAccountToLoad = {
|
|
@@ -67,6 +72,9 @@ export class BulkAccountLoader {
|
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
public removeAccount(publicKey: PublicKey, callbackId: string): void {
|
|
75
|
+
this.log(
|
|
76
|
+
`Removing account ${publicKey.toString()} callback id ${callbackId}`
|
|
77
|
+
);
|
|
70
78
|
const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
|
|
71
79
|
if (existingAccountToLoad) {
|
|
72
80
|
existingAccountToLoad.callbacks.delete(callbackId);
|
|
@@ -99,17 +107,29 @@ export class BulkAccountLoader {
|
|
|
99
107
|
|
|
100
108
|
public async load(): Promise<void> {
|
|
101
109
|
if (this.loadPromise) {
|
|
102
|
-
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
if (now - this.lastTimeLoadingPromiseCleared > oneMinute) {
|
|
112
|
+
this.log(`Load promise hasnt been clearing for one minute. Clearing.`);
|
|
113
|
+
this.loadPromise = undefined;
|
|
114
|
+
} else {
|
|
115
|
+
this.log(`Load promise exists. Returning early`);
|
|
116
|
+
return this.loadPromise;
|
|
117
|
+
}
|
|
103
118
|
}
|
|
119
|
+
|
|
104
120
|
this.loadPromise = new Promise((resolver) => {
|
|
105
121
|
this.loadPromiseResolver = resolver;
|
|
106
122
|
});
|
|
123
|
+
this.lastTimeLoadingPromiseCleared = Date.now();
|
|
124
|
+
|
|
125
|
+
this.log(`Loading`);
|
|
107
126
|
|
|
108
127
|
try {
|
|
109
128
|
const chunks = this.chunks(
|
|
110
129
|
Array.from(this.accountsToLoad.values()),
|
|
111
130
|
GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE
|
|
112
131
|
);
|
|
132
|
+
this.log(`${chunks.length} chunks`);
|
|
113
133
|
|
|
114
134
|
await Promise.all(
|
|
115
135
|
chunks.map((chunk) => {
|
|
@@ -122,28 +142,17 @@ export class BulkAccountLoader {
|
|
|
122
142
|
for (const [_, callback] of this.errorCallbacks) {
|
|
123
143
|
callback(e);
|
|
124
144
|
}
|
|
145
|
+
this.log('finished error callbacks');
|
|
125
146
|
} finally {
|
|
147
|
+
this.log(`resetting load promise`);
|
|
126
148
|
this.loadPromiseResolver();
|
|
127
149
|
this.loadPromise = undefined;
|
|
128
|
-
|
|
129
|
-
const now = Date.now();
|
|
130
|
-
if (now - this.lastUpdate > fiveMinutes) {
|
|
131
|
-
if (this.loggingEnabled) {
|
|
132
|
-
console.log(
|
|
133
|
-
"Haven't seen updated account in five minutes. Bulk account loader creating new Connection Object"
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
this.connection = new Connection(
|
|
137
|
-
// @ts-ignore
|
|
138
|
-
this.connection._rpcEndpoint,
|
|
139
|
-
this.connection.commitment
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
|
|
145
153
|
async loadChunk(accountsToLoad: AccountToLoad[]): Promise<void> {
|
|
146
154
|
if (accountsToLoad.length === 0) {
|
|
155
|
+
this.log(`no accounts in chunk`);
|
|
147
156
|
return;
|
|
148
157
|
}
|
|
149
158
|
|
|
@@ -154,15 +163,15 @@ export class BulkAccountLoader {
|
|
|
154
163
|
{ commitment: this.commitment },
|
|
155
164
|
];
|
|
156
165
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
'getMultipleAccounts',
|
|
160
|
-
|
|
166
|
+
const rpcResponse: any | null = await promiseTimeout(
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
this.connection._rpcRequest('getMultipleAccounts', args),
|
|
169
|
+
10 * 1000 // 30 second timeout
|
|
161
170
|
);
|
|
162
171
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
if (rpcResponse === null) {
|
|
173
|
+
this.log('request to rpc timed out', true);
|
|
174
|
+
return;
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
const newSlot = rpcResponse.result.context.slot;
|
|
@@ -179,44 +188,44 @@ export class BulkAccountLoader {
|
|
|
179
188
|
newBuffer = Buffer.from(raw, dataType);
|
|
180
189
|
}
|
|
181
190
|
|
|
182
|
-
if (this.loggingEnabled && oneMinuteSinceLastUpdate) {
|
|
183
|
-
console.log('oldRPCResponse', oldRPCResponse);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
191
|
if (!oldRPCResponse) {
|
|
192
|
+
this.log('No old rpc response, updating account data');
|
|
187
193
|
this.accountData.set(key, {
|
|
188
194
|
slot: newSlot,
|
|
189
195
|
buffer: newBuffer,
|
|
190
196
|
});
|
|
191
197
|
this.handleAccountCallbacks(accountToLoad, newBuffer);
|
|
192
|
-
this.lastUpdate = Date.now();
|
|
193
198
|
continue;
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
if (newSlot <= oldRPCResponse.slot) {
|
|
202
|
+
this.log(`new slot ${newSlot} old slot ${oldRPCResponse.slot}`);
|
|
197
203
|
continue;
|
|
198
204
|
}
|
|
199
205
|
|
|
200
206
|
const oldBuffer = oldRPCResponse.buffer;
|
|
201
207
|
if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
|
|
208
|
+
this.log('new buffer, updating account data');
|
|
202
209
|
this.accountData.set(key, {
|
|
203
210
|
slot: newSlot,
|
|
204
211
|
buffer: newBuffer,
|
|
205
212
|
});
|
|
206
213
|
this.handleAccountCallbacks(accountToLoad, newBuffer);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
214
|
+
} else {
|
|
215
|
+
this.log('unable to update account for newest slot');
|
|
216
|
+
this.log('oldBuffer ' + oldBuffer);
|
|
217
|
+
this.log('newBuffer ' + newBuffer);
|
|
218
|
+
this.log('buffers equal ' + newBuffer?.equals(oldBuffer));
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
|
|
216
223
|
handleAccountCallbacks(accountToLoad: AccountToLoad, buffer: Buffer): void {
|
|
224
|
+
this.log('handling account callbacks');
|
|
217
225
|
for (const [_, callback] of accountToLoad.callbacks) {
|
|
218
226
|
callback(buffer);
|
|
219
227
|
}
|
|
228
|
+
this.log('finished account callbacks');
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
public getAccountData(publicKey: PublicKey): Buffer | undefined {
|
|
@@ -229,9 +238,7 @@ export class BulkAccountLoader {
|
|
|
229
238
|
return;
|
|
230
239
|
}
|
|
231
240
|
|
|
232
|
-
|
|
233
|
-
console.log(`startPolling`);
|
|
234
|
-
}
|
|
241
|
+
this.log('startPolling');
|
|
235
242
|
|
|
236
243
|
this.intervalId = setInterval(this.load.bind(this), this.pollingFrequency);
|
|
237
244
|
}
|
|
@@ -241,9 +248,13 @@ export class BulkAccountLoader {
|
|
|
241
248
|
clearInterval(this.intervalId);
|
|
242
249
|
this.intervalId = undefined;
|
|
243
250
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
251
|
+
this.log('stopPolling');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
public log(msg: string, force = false): void {
|
|
256
|
+
if (this.loggingEnabled || force) {
|
|
257
|
+
console.log(msg);
|
|
247
258
|
}
|
|
248
259
|
}
|
|
249
260
|
}
|