@symmetry-hq/temp-v3-sdk 0.0.62 → 0.0.63

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.
@@ -796,4 +796,4 @@ export { FormattedRebalanceType, FormattedRebalanceAction, FormattedOraclePrice,
796
796
  export { FormattedCreatorSettings, FormattedManagersSettings, FormattedFeeSettings, FormattedScheduleSettings, FormattedAutomationSettings, FormattedLpSettings, FormattedMetadataSettings, FormattedDepositsSettings, FormattedForceRebalanceSettings, FormattedCustomRebalanceSettings, FormattedAddTokenSettings, FormattedUpdateWeightsSettings, FormattedMakeDirectSwapSettings, FormattedAccumulatedFees, FormattedLookupTables, FormattedAsset, FormattedOracleSettings, FormattedOracleAggregator, FormattedOracle, FormattedOracleType, FormattedBasket, };
797
797
  export { EditCreatorSettings, EditManagerSettings, EditFeeSettings, EditScheduleSettings, EditAutomationSettings, EditLpSettings, EditMetadataSettings, EditDepositsSettings, EditForceRebalanceSettings, EditCustomRebalanceSettings, EditAddTokenSettings, EditUpdateWeightsSettings, EditMakeDirectSwapSettings, AddOrEditTokenInput, OracleInput, UpdateWeightsInput, MakeDirectSwapInput, Settings, TaskContext, TaskType, };
798
798
  export { getJupTokenLedgerAndSwapInstructions, };
799
- export { KeeperMonitor } from './keeperMonitor';
799
+ export { KeeperMonitor, RebalanceHandler, } from './keeperMonitor';
package/dist/src/index.js CHANGED
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.KeeperMonitor = exports.getJupTokenLedgerAndSwapInstructions = exports.TaskType = exports.SymmetryCore = void 0;
15
+ exports.RebalanceHandler = exports.KeeperMonitor = exports.getJupTokenLedgerAndSwapInstructions = exports.TaskType = exports.SymmetryCore = void 0;
16
16
  const decimal_js_1 = __importDefault(require("decimal.js"));
17
17
  const bn_js_1 = __importDefault(require("bn.js"));
18
18
  const spl_token_1 = require("@solana/spl-token");
@@ -2087,3 +2087,4 @@ class SymmetryCore {
2087
2087
  exports.SymmetryCore = SymmetryCore;
2088
2088
  var keeperMonitor_1 = require("./keeperMonitor");
2089
2089
  Object.defineProperty(exports, "KeeperMonitor", { enumerable: true, get: function () { return keeperMonitor_1.KeeperMonitor; } });
2090
+ Object.defineProperty(exports, "RebalanceHandler", { enumerable: true, get: function () { return keeperMonitor_1.RebalanceHandler; } });
@@ -1,5 +1,6 @@
1
- import { Connection } from "@solana/web3.js";
1
+ import { Connection, PublicKey } from "@solana/web3.js";
2
2
  import { Wallet } from "./txUtils";
3
+ import { Basket, UIRebalanceIntent } from ".";
3
4
  export declare class KeeperMonitor {
4
5
  private params;
5
6
  private intents;
@@ -20,3 +21,32 @@ export declare class KeeperMonitor {
20
21
  monitorIntent(pubkey: string): Promise<void>;
21
22
  monitorRebalanceIntent(pubkey: string): Promise<void>;
22
23
  }
24
+ export declare class RebalanceHandler {
25
+ private params;
26
+ private intent;
27
+ private basket;
28
+ constructor(params: {
29
+ intent: UIRebalanceIntent;
30
+ basket: Basket;
31
+ wallet: Wallet;
32
+ connection: Connection;
33
+ network: "devnet" | "mainnet";
34
+ jupiterApiKey: string;
35
+ maxAllowedAccounts: number;
36
+ priorityFee?: number;
37
+ simulateTransactions?: boolean;
38
+ });
39
+ delay: (ms: number) => Promise<unknown>;
40
+ refresh(): Promise<void>;
41
+ static run(params: {
42
+ intentPubkey: PublicKey;
43
+ wallet: Wallet;
44
+ connection: Connection;
45
+ network: "devnet" | "mainnet";
46
+ jupiterApiKey: string;
47
+ maxAllowedAccounts: number;
48
+ priorityFee?: number;
49
+ simulateTransactions?: boolean;
50
+ }): Promise<void>;
51
+ private execute;
52
+ }
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.KeeperMonitor = void 0;
12
+ exports.RebalanceHandler = exports.KeeperMonitor = void 0;
13
13
  const web3_js_1 = require("@solana/web3.js");
14
14
  const _1 = require(".");
15
15
  const rebalanceIntent_1 = require("./states/intents/rebalanceIntent");
@@ -156,7 +156,7 @@ class KeeperMonitor {
156
156
  intent: intent.ownAddress.toBase58(),
157
157
  });
158
158
  let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
159
- console.log("Execute Basket Intent - ", pubkey, " : ", res);
159
+ console.log("Execute Basket Intent -", pubkey, " : ", res);
160
160
  nextCheckTime = now + 60;
161
161
  }
162
162
  catch (_a) { }
@@ -172,12 +172,12 @@ class KeeperMonitor {
172
172
  intent: intent.ownAddress.toBase58(),
173
173
  });
174
174
  let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
175
- console.log("Cancel Basket Intent - ", pubkey, " : ", res);
175
+ console.log("Cancel Basket Intent -", pubkey, " : ", res);
176
176
  nextCheckTime = now + 60;
177
177
  }
178
178
  catch (e) {
179
179
  if (numTries == 4) {
180
- console.log("Stop monitoring - ", pubkey, " : ", e);
180
+ console.log("Stop monitoring -", pubkey, " : ", e);
181
181
  }
182
182
  }
183
183
  continue;
@@ -186,12 +186,14 @@ class KeeperMonitor {
186
186
  }
187
187
  monitorRebalanceIntent(pubkey) {
188
188
  return __awaiter(this, void 0, void 0, function* () {
189
- var _a, _b;
189
+ var _a, _b, _c, _d, _e;
190
190
  let nextCheckTime = 0;
191
191
  let numTriesUpdatePrices = 0;
192
192
  let numTriesMint = 0;
193
193
  let numTriesRedeemTokens = 0;
194
194
  let numTriesClaimBounty = 0;
195
+ let lastJupQuotesUpdate = 0;
196
+ let jupQuotes = [];
195
197
  while (true) {
196
198
  let uiIntent = this.rebalanceIntents.get(pubkey);
197
199
  if (!uiIntent)
@@ -213,7 +215,7 @@ class KeeperMonitor {
213
215
  continue;
214
216
  }
215
217
  if (intent.current_action == "update_prices") {
216
- if (numTriesUpdatePrices >= 3)
218
+ if (numTriesUpdatePrices >= 5)
217
219
  break;
218
220
  numTriesUpdatePrices += 1;
219
221
  try {
@@ -226,86 +228,90 @@ class KeeperMonitor {
226
228
  console.log("Update Prices - ", pubkey, " : ", res);
227
229
  }
228
230
  catch (e) {
229
- if (numTriesUpdatePrices == 3) {
231
+ if (numTriesUpdatePrices == 4) {
230
232
  console.log("Stop monitoring - ", pubkey, " : ", e);
231
233
  }
232
234
  }
235
+ nextCheckTime += 60;
233
236
  continue;
234
237
  }
235
238
  if (intent.auctions[2].end_time > now) {
236
239
  let basket = this.baskets.get(intent.basket);
237
240
  if (!basket)
238
241
  continue;
239
- let auction0StartTime = intent.auctions[0].start_time;
240
- let auction0EndTime = intent.auctions[0].end_time;
241
- let auction0MidTime = auction0StartTime + (auction0EndTime - auction0StartTime) / 2;
242
- let auction1StartTime = intent.auctions[1].start_time;
243
- let auction1EndTime = intent.auctions[1].end_time;
244
- let auction1MidTime = auction1StartTime + (auction1EndTime - auction1StartTime) / 2;
245
- let auction2StartTime = intent.auctions[2].start_time;
246
- let auction2EndTime = intent.auctions[2].end_time;
247
- let auction2MidTime = auction2StartTime + (auction2EndTime - auction2StartTime) / 2;
248
- if (now < auction0MidTime + 30) {
249
- nextCheckTime = auction0MidTime + 30;
250
- continue;
251
- }
252
- if (now >= auction0EndTime) {
253
- if (now < auction1MidTime + 10) {
254
- nextCheckTime = auction1MidTime + 10;
255
- continue;
256
- }
257
- if (now >= auction1EndTime)
258
- if (now < auction2MidTime + 5) {
259
- nextCheckTime = auction2MidTime + 5;
260
- continue;
261
- }
262
- }
263
242
  let pairs = (0, rebalanceIntent_1.getSwapPairs)(chainData, basket);
264
- for (let pair of pairs)
265
- try {
243
+ if (Date.now() / 1000 > lastJupQuotesUpdate + 60) {
244
+ lastJupQuotesUpdate = Date.now() / 1000;
245
+ jupQuotes = [];
246
+ for (let pair of pairs) {
266
247
  if (pair.value < 0.005)
267
248
  continue;
268
- yield this.delay(1000);
269
- let { tokenLedgerInstruction, swapInstruction, addressLookupTableAddresses, quoteResponse } = this.params.network == "mainnet" ?
270
- yield (0, _1.getJupTokenLedgerAndSwapInstructions)({
271
- keeper: this.params.wallet.publicKey,
272
- basketMintIn: new web3_js_1.PublicKey(pair.inMint),
273
- basketMintOut: new web3_js_1.PublicKey(pair.outMint),
274
- basketAmountIn: pair.inAmount,
275
- basketAmountOut: pair.outAmount,
276
- swapMode: "ioc",
277
- apiKey: this.params.jupiterApiKey,
278
- maxJupAccounts: this.params.maxAllowedAccounts,
279
- }) : { tokenLedgerInstruction: undefined, swapInstruction: undefined, addressLookupTableAddresses: [], quoteResponse: undefined };
280
- console.log(pair, "Jup Quote:", parseFloat((_a = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.outAmount) !== null && _a !== void 0 ? _a : 0), "Requested In:", pair.inAmount);
281
- if (this.params.network == "devnet" || parseFloat((_b = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.outAmount) !== null && _b !== void 0 ? _b : 0) > pair.inAmount) {
249
+ if (this.params.network == "mainnet")
282
250
  try {
283
- let tx = yield this.params.symmetryCore.flashSwapTx({
284
- keeper: this.params.wallet.publicKey.toBase58(),
285
- basket: basket.ownAddress.toBase58(),
286
- rebalance_intent: intent.pubkey,
287
- mint_in: pair.inMint,
288
- mint_out: pair.outMint,
289
- amount_in: pair.inAmount,
290
- amount_out: pair.outAmount,
291
- mode: 2,
292
- jup_token_ledger_ix: tokenLedgerInstruction,
293
- jup_swap_ix: swapInstruction,
294
- jup_address_lookup_table_addresses: addressLookupTableAddresses,
251
+ let res = yield (0, _1.getJupTokenLedgerAndSwapInstructions)({
252
+ keeper: this.params.wallet.publicKey,
253
+ basketMintIn: new web3_js_1.PublicKey(pair.inMint),
254
+ basketMintOut: new web3_js_1.PublicKey(pair.outMint),
255
+ basketAmountIn: pair.inAmount,
256
+ basketAmountOut: pair.outAmount,
257
+ swapMode: "ioc",
258
+ apiKey: this.params.jupiterApiKey,
259
+ maxJupAccounts: this.params.maxAllowedAccounts,
295
260
  });
296
- let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
297
- console.log("Flash Swap - ", pubkey, " : ", res);
261
+ jupQuotes.push(Object.assign(Object.assign({}, res), { inMint: pair.inMint, outMint: pair.outMint }));
262
+ console.log("Fetch new Jup Quote:", pair.inMint, pair.outMint);
263
+ console.log(pair, "Jup Quote:", parseFloat((_b = (_a = res.quoteResponse) === null || _a === void 0 ? void 0 : _a.outAmount) !== null && _b !== void 0 ? _b : 0), "Requested In:", pair.inAmount);
298
264
  }
299
- catch (_c) { }
265
+ catch (_f) { }
266
+ }
267
+ }
268
+ for (let index = 0; index < pairs.length; index++)
269
+ try {
270
+ let pair = pairs[index];
271
+ let jupIndex = jupQuotes.findIndex(q => q && q.inMint == pair.inMint && q.outMint == pair.outMint);
272
+ let quote = jupIndex >= 0 ? jupQuotes[jupIndex] : undefined;
273
+ if (!quote && this.params.network == "mainnet")
274
+ continue;
275
+ if (pair.value < 0.005)
276
+ continue;
277
+ let tokenLedgerInstruction = quote === null || quote === void 0 ? void 0 : quote.tokenLedgerInstruction;
278
+ let swapInstruction = quote === null || quote === void 0 ? void 0 : quote.swapInstruction;
279
+ let addressLookupTableAddresses = (_c = quote === null || quote === void 0 ? void 0 : quote.addressLookupTableAddresses) !== null && _c !== void 0 ? _c : [];
280
+ let quoteResponse = quote === null || quote === void 0 ? void 0 : quote.quoteResponse;
281
+ if (this.params.network == "mainnet" && !quoteResponse)
282
+ continue;
283
+ console.log(pair, "Jup Quote:", parseFloat((_d = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.outAmount) !== null && _d !== void 0 ? _d : 0), "Requested In:", pair.inAmount);
284
+ let quoteResponseAmount = parseFloat((_e = quoteResponse === null || quoteResponse === void 0 ? void 0 : quoteResponse.outAmount) !== null && _e !== void 0 ? _e : 0);
285
+ if (this.params.network == "mainnet" && quoteResponseAmount * 1.005 <= pair.inAmount)
286
+ continue;
287
+ try {
288
+ let tx = yield this.params.symmetryCore.flashSwapTx({
289
+ keeper: this.params.wallet.publicKey.toBase58(),
290
+ basket: basket.ownAddress.toBase58(),
291
+ rebalance_intent: intent.pubkey,
292
+ mint_in: pair.inMint,
293
+ mint_out: pair.outMint,
294
+ amount_in: pair.inAmount,
295
+ amount_out: pair.outAmount,
296
+ mode: 2,
297
+ jup_token_ledger_ix: tokenLedgerInstruction,
298
+ jup_swap_ix: swapInstruction,
299
+ jup_address_lookup_table_addresses: addressLookupTableAddresses,
300
+ });
301
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
302
+ console.log("Flash Swap - ", pubkey, " : ", res);
303
+ jupQuotes = jupQuotes.filter(q => q && (q.inMint != pair.inMint || q.outMint != pair.outMint));
300
304
  }
305
+ catch (_g) { }
301
306
  }
302
- catch (_d) { }
307
+ catch (_h) { }
303
308
  nextCheckTime = (Date.now() / 1000) + 8;
304
309
  continue;
305
310
  }
306
311
  if (intent.rebalance_type == "deposit") {
307
312
  if (numTriesMint >= 3)
308
313
  break;
314
+ lastJupQuotesUpdate = 0;
309
315
  numTriesMint += 1;
310
316
  try {
311
317
  let tx = yield this.params.symmetryCore.mintTx({
@@ -365,3 +371,254 @@ class KeeperMonitor {
365
371
  }
366
372
  }
367
373
  exports.KeeperMonitor = KeeperMonitor;
374
+ class RebalanceHandler {
375
+ constructor(params) {
376
+ var _a, _b;
377
+ this.delay = (ms) => __awaiter(this, void 0, void 0, function* () { return new Promise(resolve => setTimeout(resolve, ms)); });
378
+ this.params = {
379
+ wallet: params.wallet,
380
+ connection: params.connection,
381
+ symmetryCore: new _1.SymmetryCore({
382
+ connection: params.connection,
383
+ network: params.network,
384
+ priorityFee: (_a = params.priorityFee) !== null && _a !== void 0 ? _a : constants_1.PRIORITY_FEE,
385
+ }),
386
+ network: params.network,
387
+ jupiterApiKey: params.jupiterApiKey,
388
+ maxAllowedAccounts: params.maxAllowedAccounts,
389
+ simulateTransactions: (_b = params.simulateTransactions) !== null && _b !== void 0 ? _b : false,
390
+ };
391
+ this.intent = params.intent;
392
+ this.basket = params.basket;
393
+ }
394
+ refresh() {
395
+ return __awaiter(this, void 0, void 0, function* () {
396
+ this.intent = yield this.params.symmetryCore.fetchRebalanceIntent(this.intent.formatted_data.pubkey);
397
+ this.basket = yield this.params.symmetryCore.fetchBasket(this.intent.formatted_data.basket);
398
+ });
399
+ }
400
+ static run(params) {
401
+ return __awaiter(this, void 0, void 0, function* () {
402
+ var _a;
403
+ let symmetryCore = new _1.SymmetryCore({
404
+ connection: params.connection,
405
+ network: params.network,
406
+ priorityFee: (_a = params.priorityFee) !== null && _a !== void 0 ? _a : constants_1.PRIORITY_FEE,
407
+ });
408
+ let intent = yield symmetryCore.fetchRebalanceIntent(params.intentPubkey.toBase58());
409
+ let basket = yield symmetryCore.fetchBasket(intent.formatted_data.basket);
410
+ let handler = new RebalanceHandler({
411
+ intent, basket,
412
+ wallet: params.wallet,
413
+ connection: params.connection,
414
+ network: params.network,
415
+ jupiterApiKey: params.jupiterApiKey,
416
+ maxAllowedAccounts: params.maxAllowedAccounts,
417
+ simulateTransactions: params.simulateTransactions,
418
+ });
419
+ handler.execute();
420
+ for (let i = 0; i < 20; i++) {
421
+ yield handler.delay(15 * 1000);
422
+ try {
423
+ yield handler.refresh();
424
+ }
425
+ catch (e) {
426
+ handler.intent = undefined;
427
+ break;
428
+ }
429
+ }
430
+ });
431
+ }
432
+ execute() {
433
+ return __awaiter(this, void 0, void 0, function* () {
434
+ var _a, _b;
435
+ console.log("Starting rebalance handler for intent:", this.intent.formatted_data.pubkey);
436
+ let nextCheckTime = 0;
437
+ let numTriesUpdatePrices = 0;
438
+ let numTriesMint = 0;
439
+ let numTriesRedeemTokens = 0;
440
+ let numTriesClaimBounty = 0;
441
+ let rebalancePairs = [];
442
+ let lastJupQuotesUpdate = 0;
443
+ let jupQuotes = [];
444
+ while (true) {
445
+ if (!this.intent)
446
+ break;
447
+ let intent = this.intent.formatted_data;
448
+ let chainData = this.intent.chain_data;
449
+ let now = Date.now() / 1000;
450
+ if (now < nextCheckTime) {
451
+ yield this.delay(Math.min(30 * 1000, Math.max(0, nextCheckTime - now + 0.2) * 1000));
452
+ continue;
453
+ }
454
+ nextCheckTime = now + 5;
455
+ if (intent.current_action == "not_active") {
456
+ console.log("Intent not active, stopping");
457
+ break;
458
+ }
459
+ if (intent.current_action == "deposit_tokens") {
460
+ console.log("Waiting for deposit...");
461
+ continue;
462
+ }
463
+ if (intent.current_action == "update_prices" && intent.last_action_timestamp > now) {
464
+ nextCheckTime = intent.last_action_timestamp;
465
+ continue;
466
+ }
467
+ if (intent.current_action == "update_prices") {
468
+ if (numTriesUpdatePrices >= 3) {
469
+ console.log("Max retries for update_prices");
470
+ break;
471
+ }
472
+ numTriesUpdatePrices += 1;
473
+ try {
474
+ let tx = yield this.params.symmetryCore.updateTokenPricesTx({
475
+ keeper: this.params.wallet.publicKey.toBase58(),
476
+ basket: intent.basket,
477
+ rebalance_intent: intent.pubkey,
478
+ });
479
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
480
+ console.log("Update Prices:", res);
481
+ }
482
+ catch (e) {
483
+ if (numTriesUpdatePrices == 3) {
484
+ console.log("Stop - update prices failed:", e);
485
+ }
486
+ }
487
+ continue;
488
+ }
489
+ if (intent.auctions[2].end_time > now) {
490
+ rebalancePairs = (0, rebalanceIntent_1.getSwapPairs)(chainData, this.basket);
491
+ if (Date.now() / 1000 > lastJupQuotesUpdate + 60) {
492
+ let usedValue = new Map();
493
+ jupQuotes = [];
494
+ for (let pair of rebalancePairs) {
495
+ let inValue = (_a = usedValue.get(pair.inMint)) !== null && _a !== void 0 ? _a : 0;
496
+ let outValue = (_b = usedValue.get(pair.outMint)) !== null && _b !== void 0 ? _b : 0;
497
+ let swapValue = Math.min(pair.value - inValue, pair.value - outValue);
498
+ if (swapValue < 0.005) {
499
+ jupQuotes.push(undefined);
500
+ continue;
501
+ }
502
+ usedValue.set(pair.inMint, inValue + swapValue);
503
+ usedValue.set(pair.outMint, outValue + swapValue);
504
+ let res = undefined;
505
+ if (this.params.network == "mainnet")
506
+ try {
507
+ res = Object.assign(Object.assign({}, (yield (0, _1.getJupTokenLedgerAndSwapInstructions)({
508
+ keeper: this.params.wallet.publicKey,
509
+ basketMintIn: new web3_js_1.PublicKey(pair.inMint),
510
+ basketMintOut: new web3_js_1.PublicKey(pair.outMint),
511
+ basketAmountIn: pair.inAmount,
512
+ basketAmountOut: pair.outAmount,
513
+ swapMode: "ioc",
514
+ apiKey: this.params.jupiterApiKey,
515
+ maxJupAccounts: this.params.maxAllowedAccounts,
516
+ }))), { inMint: pair.inMint, outMint: pair.outMint });
517
+ }
518
+ catch (_c) { }
519
+ ;
520
+ jupQuotes.push(res);
521
+ }
522
+ }
523
+ for (let index = 0; index < rebalancePairs.length; index++)
524
+ try {
525
+ let pair = rebalancePairs[index];
526
+ let quote = jupQuotes.find(q => q && q.inMint == pair.inMint && q.outMint == pair.outMint);
527
+ if (!quote)
528
+ continue;
529
+ if (pair.value < 0.005)
530
+ continue;
531
+ let { tokenLedgerInstruction, swapInstruction, addressLookupTableAddresses, quoteResponse } = quote;
532
+ if (!quoteResponse)
533
+ continue;
534
+ console.log(pair, "Jup Quote:", parseFloat(quoteResponse.outAmount), "Requested In:", pair.inAmount);
535
+ if (parseFloat(quoteResponse.outAmount) <= pair.inAmount && this.params.network == "mainnet")
536
+ continue;
537
+ try {
538
+ let tx = yield this.params.symmetryCore.flashSwapTx({
539
+ keeper: this.params.wallet.publicKey.toBase58(),
540
+ basket: this.basket.ownAddress.toBase58(),
541
+ rebalance_intent: intent.pubkey,
542
+ mint_in: pair.inMint,
543
+ mint_out: pair.outMint,
544
+ amount_in: pair.inAmount,
545
+ amount_out: pair.outAmount,
546
+ mode: 2,
547
+ jup_token_ledger_ix: tokenLedgerInstruction,
548
+ jup_swap_ix: swapInstruction,
549
+ jup_address_lookup_table_addresses: addressLookupTableAddresses,
550
+ });
551
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
552
+ console.log("Flash Swap:", res);
553
+ rebalancePairs = rebalancePairs.splice(index, 1);
554
+ index -= 1;
555
+ }
556
+ catch (_d) { }
557
+ }
558
+ catch (_e) { }
559
+ nextCheckTime = (Date.now() / 1000) + 10;
560
+ continue;
561
+ }
562
+ if (intent.rebalance_type == "deposit") {
563
+ if (numTriesMint >= 3)
564
+ break;
565
+ lastJupQuotesUpdate = 0;
566
+ numTriesMint += 1;
567
+ try {
568
+ let tx = yield this.params.symmetryCore.mintTx({
569
+ keeper: this.params.wallet.publicKey.toBase58(),
570
+ rebalance_intent: intent.pubkey,
571
+ });
572
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
573
+ console.log("Mint -", res);
574
+ }
575
+ catch (e) {
576
+ if (numTriesMint == 3) {
577
+ console.log("Stop monitoring -", e);
578
+ }
579
+ }
580
+ continue;
581
+ }
582
+ let hasTokens = intent.tokens.find(token => token.amount > 0);
583
+ if (hasTokens && intent.rebalance_type == "withdraw") {
584
+ if (numTriesRedeemTokens >= 3)
585
+ break;
586
+ numTriesRedeemTokens += 1;
587
+ try {
588
+ let tx = yield this.params.symmetryCore.redeemTokensTx({
589
+ keeper: this.params.wallet.publicKey.toBase58(),
590
+ rebalance_intent: intent.pubkey,
591
+ });
592
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
593
+ console.log("Redeem Tokens -", res);
594
+ }
595
+ catch (e) {
596
+ if (numTriesRedeemTokens == 3) {
597
+ console.log("Stop monitoring -", e);
598
+ }
599
+ }
600
+ continue;
601
+ }
602
+ if (numTriesClaimBounty >= 3)
603
+ break;
604
+ numTriesClaimBounty += 1;
605
+ try {
606
+ let tx = yield this.params.symmetryCore.claimBountyTx({
607
+ keeper: this.params.wallet.publicKey.toBase58(),
608
+ rebalance_intent: intent.pubkey,
609
+ });
610
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
611
+ console.log("Claim Bounty -", res);
612
+ nextCheckTime = now + 60;
613
+ }
614
+ catch (e) {
615
+ if (numTriesClaimBounty == 3) {
616
+ console.log("Stop monitoring -", e);
617
+ }
618
+ }
619
+ continue;
620
+ }
621
+ });
622
+ }
623
+ }
624
+ exports.RebalanceHandler = RebalanceHandler;
@@ -0,0 +1,32 @@
1
+ import { Connection, PublicKey } from "@solana/web3.js";
2
+ import { Wallet } from "./txUtils";
3
+ import { Basket, UIRebalanceIntent } from ".";
4
+ export declare class RebalanceHandler {
5
+ private params;
6
+ private intent;
7
+ private basket;
8
+ constructor(params: {
9
+ intent: UIRebalanceIntent;
10
+ basket: Basket;
11
+ wallet: Wallet;
12
+ connection: Connection;
13
+ network: "devnet" | "mainnet";
14
+ jupiterApiKey: string;
15
+ maxAllowedAccounts: number;
16
+ priorityFee?: number;
17
+ simulateTransactions?: boolean;
18
+ });
19
+ delay: (ms: number) => Promise<unknown>;
20
+ refresh(): Promise<void>;
21
+ static run(params: {
22
+ intentPubkey: PublicKey;
23
+ wallet: Wallet;
24
+ connection: Connection;
25
+ network: "devnet" | "mainnet";
26
+ jupiterApiKey: string;
27
+ maxAllowedAccounts: number;
28
+ priorityFee?: number;
29
+ simulateTransactions?: boolean;
30
+ }): Promise<void>;
31
+ private execute;
32
+ }
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RebalanceHandler = void 0;
13
+ const web3_js_1 = require("@solana/web3.js");
14
+ const _1 = require(".");
15
+ const rebalanceIntent_1 = require("./states/intents/rebalanceIntent");
16
+ const constants_1 = require("./constants");
17
+ class RebalanceHandler {
18
+ constructor(params) {
19
+ var _a, _b;
20
+ this.delay = (ms) => __awaiter(this, void 0, void 0, function* () { return new Promise(resolve => setTimeout(resolve, ms)); });
21
+ this.params = {
22
+ wallet: params.wallet,
23
+ connection: params.connection,
24
+ symmetryCore: new _1.SymmetryCore({
25
+ connection: params.connection,
26
+ network: params.network,
27
+ priorityFee: (_a = params.priorityFee) !== null && _a !== void 0 ? _a : constants_1.PRIORITY_FEE,
28
+ }),
29
+ network: params.network,
30
+ jupiterApiKey: params.jupiterApiKey,
31
+ maxAllowedAccounts: params.maxAllowedAccounts,
32
+ simulateTransactions: (_b = params.simulateTransactions) !== null && _b !== void 0 ? _b : false,
33
+ };
34
+ this.intent = params.intent;
35
+ this.basket = params.basket;
36
+ }
37
+ refresh() {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ this.intent = yield this.params.symmetryCore.fetchRebalanceIntent(this.intent.formatted_data.pubkey);
40
+ this.basket = yield this.params.symmetryCore.fetchBasket(this.intent.formatted_data.basket);
41
+ });
42
+ }
43
+ static run(params) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ var _a;
46
+ let symmetryCore = new _1.SymmetryCore({
47
+ connection: params.connection,
48
+ network: params.network,
49
+ priorityFee: (_a = params.priorityFee) !== null && _a !== void 0 ? _a : constants_1.PRIORITY_FEE,
50
+ });
51
+ let intent = yield symmetryCore.fetchRebalanceIntent(params.intentPubkey.toBase58());
52
+ let basket = yield symmetryCore.fetchBasket(intent.formatted_data.basket);
53
+ let handler = new RebalanceHandler({
54
+ intent, basket,
55
+ wallet: params.wallet,
56
+ connection: params.connection,
57
+ network: params.network,
58
+ jupiterApiKey: params.jupiterApiKey,
59
+ maxAllowedAccounts: params.maxAllowedAccounts,
60
+ simulateTransactions: params.simulateTransactions,
61
+ });
62
+ handler.execute();
63
+ for (let i = 0; i < 20; i++) {
64
+ yield handler.delay(15 * 1000);
65
+ try {
66
+ yield handler.refresh();
67
+ }
68
+ catch (e) {
69
+ break;
70
+ }
71
+ }
72
+ });
73
+ }
74
+ execute() {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ var _a, _b;
77
+ console.log("Starting rebalance handler for intent:", this.intent.formatted_data.pubkey);
78
+ let nextCheckTime = 0;
79
+ let numTriesUpdatePrices = 0;
80
+ let numTriesMint = 0;
81
+ let numTriesRedeemTokens = 0;
82
+ let numTriesClaimBounty = 0;
83
+ let rebalancePairs = [];
84
+ let lastJupQuotesUpdate = 0;
85
+ let jupQuotes = [];
86
+ while (true) {
87
+ if (!this.intent)
88
+ break;
89
+ let intent = this.intent.formatted_data;
90
+ let chainData = this.intent.chain_data;
91
+ let now = Date.now() / 1000;
92
+ if (now < nextCheckTime) {
93
+ yield this.delay(Math.min(30 * 1000, Math.max(0, nextCheckTime - now + 0.2) * 1000));
94
+ continue;
95
+ }
96
+ nextCheckTime = now + 35;
97
+ if (intent.current_action == "not_active") {
98
+ console.log("Intent not active, stopping");
99
+ break;
100
+ }
101
+ if (intent.current_action == "deposit_tokens") {
102
+ console.log("Waiting for deposit...");
103
+ continue;
104
+ }
105
+ if (intent.current_action == "update_prices" && intent.last_action_timestamp > now) {
106
+ nextCheckTime = intent.last_action_timestamp;
107
+ continue;
108
+ }
109
+ if (intent.current_action == "update_prices") {
110
+ if (numTriesUpdatePrices >= 3) {
111
+ console.log("Max retries for update_prices");
112
+ break;
113
+ }
114
+ numTriesUpdatePrices += 1;
115
+ try {
116
+ let tx = yield this.params.symmetryCore.updateTokenPricesTx({
117
+ keeper: this.params.wallet.publicKey.toBase58(),
118
+ basket: intent.basket,
119
+ rebalance_intent: intent.pubkey,
120
+ });
121
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
122
+ console.log("Update Prices:", res);
123
+ }
124
+ catch (e) {
125
+ if (numTriesUpdatePrices == 3) {
126
+ console.log("Stop - update prices failed:", e);
127
+ }
128
+ }
129
+ continue;
130
+ }
131
+ if (intent.auctions[2].end_time > now) {
132
+ let auction0StartTime = intent.auctions[0].start_time;
133
+ let auction0EndTime = intent.auctions[0].end_time;
134
+ let auction1StartTime = intent.auctions[1].start_time;
135
+ let auction1EndTime = intent.auctions[1].end_time;
136
+ let auction2StartTime = intent.auctions[2].start_time;
137
+ let auction2EndTime = intent.auctions[2].end_time;
138
+ rebalancePairs = (0, rebalanceIntent_1.getSwapPairs)(chainData, this.basket);
139
+ if (Date.now() / 1000 > lastJupQuotesUpdate + 60) {
140
+ let usedValue = new Map();
141
+ jupQuotes = [];
142
+ for (let pair of rebalancePairs) {
143
+ let inValue = (_a = usedValue.get(pair.inMint)) !== null && _a !== void 0 ? _a : 0;
144
+ let outValue = (_b = usedValue.get(pair.outMint)) !== null && _b !== void 0 ? _b : 0;
145
+ let swapValue = Math.min(pair.value - inValue, pair.value - outValue);
146
+ if (swapValue < 0.005) {
147
+ jupQuotes.push(undefined);
148
+ continue;
149
+ }
150
+ usedValue.set(pair.inMint, inValue + swapValue);
151
+ usedValue.set(pair.outMint, outValue + swapValue);
152
+ let res = undefined;
153
+ if (this.params.network == "mainnet")
154
+ try {
155
+ res = Object.assign(Object.assign({}, (yield (0, _1.getJupTokenLedgerAndSwapInstructions)({
156
+ keeper: this.params.wallet.publicKey,
157
+ basketMintIn: new web3_js_1.PublicKey(pair.inMint),
158
+ basketMintOut: new web3_js_1.PublicKey(pair.outMint),
159
+ basketAmountIn: pair.inAmount,
160
+ basketAmountOut: pair.outAmount,
161
+ swapMode: "ioc",
162
+ apiKey: this.params.jupiterApiKey,
163
+ maxJupAccounts: this.params.maxAllowedAccounts,
164
+ }))), { inMint: pair.inMint, outMint: pair.outMint });
165
+ }
166
+ catch (_c) { }
167
+ ;
168
+ jupQuotes.push(res);
169
+ }
170
+ }
171
+ for (let index = 0; index < rebalancePairs.length; index++)
172
+ try {
173
+ let pair = rebalancePairs[index];
174
+ let quote = jupQuotes.find(q => q && q.inMint == pair.inMint && q.outMint == pair.outMint);
175
+ if (!quote)
176
+ continue;
177
+ if (pair.value < 0.005)
178
+ continue;
179
+ let { tokenLedgerInstruction, swapInstruction, addressLookupTableAddresses, quoteResponse } = quote;
180
+ if (!quoteResponse)
181
+ continue;
182
+ console.log(pair, "Jup Quote:", parseFloat(quoteResponse.outAmount), "Requested In:", pair.inAmount);
183
+ if (parseFloat(quoteResponse.outAmount) <= pair.inAmount && this.params.network == "mainnet")
184
+ continue;
185
+ try {
186
+ let tx = yield this.params.symmetryCore.flashSwapTx({
187
+ keeper: this.params.wallet.publicKey.toBase58(),
188
+ basket: this.basket.ownAddress.toBase58(),
189
+ rebalance_intent: intent.pubkey,
190
+ mint_in: pair.inMint,
191
+ mint_out: pair.outMint,
192
+ amount_in: pair.inAmount,
193
+ amount_out: pair.outAmount,
194
+ mode: 2,
195
+ jup_token_ledger_ix: tokenLedgerInstruction,
196
+ jup_swap_ix: swapInstruction,
197
+ jup_address_lookup_table_addresses: addressLookupTableAddresses,
198
+ });
199
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
200
+ console.log("Flash Swap:", res);
201
+ rebalancePairs = rebalancePairs.splice(index, 1);
202
+ index -= 1;
203
+ }
204
+ catch (_d) { }
205
+ }
206
+ catch (_e) { }
207
+ nextCheckTime = (Date.now() / 1000) + 10;
208
+ continue;
209
+ }
210
+ if (intent.rebalance_type == "deposit") {
211
+ lastJupQuotesUpdate = 0;
212
+ if (numTriesMint >= 3) {
213
+ console.log("Max retries for mint");
214
+ break;
215
+ }
216
+ numTriesMint += 1;
217
+ try {
218
+ let tx = yield this.params.symmetryCore.mintTx({
219
+ keeper: this.params.wallet.publicKey.toBase58(),
220
+ rebalance_intent: intent.pubkey,
221
+ });
222
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
223
+ console.log("Mint:", res);
224
+ }
225
+ catch (e) {
226
+ if (numTriesMint == 3) {
227
+ console.log("Stop - mint failed:", e);
228
+ }
229
+ }
230
+ continue;
231
+ }
232
+ let hasTokens = intent.tokens.find((token) => token.amount > 0);
233
+ if (hasTokens && intent.rebalance_type == "withdraw") {
234
+ if (numTriesRedeemTokens >= 3) {
235
+ console.log("Max retries for redeem");
236
+ break;
237
+ }
238
+ numTriesRedeemTokens += 1;
239
+ try {
240
+ let tx = yield this.params.symmetryCore.redeemTokensTx({
241
+ keeper: this.params.wallet.publicKey.toBase58(),
242
+ rebalance_intent: intent.pubkey,
243
+ });
244
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
245
+ console.log("Redeem Tokens:", res);
246
+ }
247
+ catch (e) {
248
+ if (numTriesRedeemTokens == 3) {
249
+ console.log("Stop - redeem failed:", e);
250
+ }
251
+ }
252
+ continue;
253
+ }
254
+ if (numTriesClaimBounty >= 3) {
255
+ console.log("Max retries for claim bounty");
256
+ break;
257
+ }
258
+ numTriesClaimBounty += 1;
259
+ try {
260
+ let tx = yield this.params.symmetryCore.claimBountyTx({
261
+ keeper: this.params.wallet.publicKey.toBase58(),
262
+ rebalance_intent: intent.pubkey,
263
+ });
264
+ let res = yield this.params.symmetryCore.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions });
265
+ console.log("Claim Bounty:", res);
266
+ nextCheckTime = now + 60;
267
+ }
268
+ catch (e) {
269
+ if (numTriesClaimBounty == 3) {
270
+ console.log("Stop - claim bounty failed:", e);
271
+ }
272
+ }
273
+ }
274
+ console.log("Rebalance handler finished for intent:", this.intent.formatted_data.pubkey);
275
+ });
276
+ }
277
+ }
278
+ exports.RebalanceHandler = RebalanceHandler;
package/dist/test.js CHANGED
@@ -28,7 +28,7 @@ function testStates() {
28
28
  let sdk = new src_1.SymmetryCore({
29
29
  connection: connection,
30
30
  network: "mainnet",
31
- priorityFee: 237,
31
+ priorityFee: 5237,
32
32
  });
33
33
  let keeperMonitor = new src_1.KeeperMonitor({
34
34
  wallet: wallet,
@@ -37,7 +37,7 @@ function testStates() {
37
37
  jupiterApiKey: jupApiKey,
38
38
  maxAllowedAccounts: 40,
39
39
  simulateTransactions: false,
40
- priorityFee: 237,
40
+ priorityFee: 5237,
41
41
  });
42
42
  // keeperMonitor.run();
43
43
  // return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symmetry-hq/temp-v3-sdk",
3
- "version": "0.0.62",
3
+ "version": "0.0.63",
4
4
  "description": "Symmetry V3 SDK",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
package/src/index.ts CHANGED
@@ -2417,4 +2417,7 @@ export {
2417
2417
  getJupTokenLedgerAndSwapInstructions,
2418
2418
  }
2419
2419
 
2420
- export { KeeperMonitor } from './keeperMonitor';
2420
+ export {
2421
+ KeeperMonitor,
2422
+ RebalanceHandler,
2423
+ } from './keeperMonitor';
@@ -1,4 +1,4 @@
1
- import { Connection, PublicKey } from "@solana/web3.js";
1
+ import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
2
2
  import { Wallet } from "./txUtils";
3
3
  import { Basket, FormattedRebalanceIntent, getJupTokenLedgerAndSwapInstructions, Intent, SymmetryCore, UIRebalanceIntent } from ".";
4
4
  import { getSwapPairs } from "./states/intents/rebalanceIntent";
@@ -150,7 +150,7 @@ export class KeeperMonitor {
150
150
  intent: intent.ownAddress!.toBase58(),
151
151
  });
152
152
  let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
153
- console.log("Execute Basket Intent - ", pubkey, " : ", res);
153
+ console.log("Execute Basket Intent -", pubkey, " : ", res);
154
154
  nextCheckTime = now + 60;
155
155
  } catch {}
156
156
  continue;
@@ -163,9 +163,9 @@ export class KeeperMonitor {
163
163
  intent: intent.ownAddress!.toBase58(),
164
164
  });
165
165
  let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
166
- console.log("Cancel Basket Intent - ", pubkey, " : ", res);
166
+ console.log("Cancel Basket Intent -", pubkey, " : ", res);
167
167
  nextCheckTime = now + 60;
168
- } catch (e) { if (numTries == 4) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
168
+ } catch (e) { if (numTries == 4) { console.log("Stop monitoring -", pubkey, " : ", e); } }
169
169
  continue;
170
170
  }
171
171
  }
@@ -176,6 +176,16 @@ export class KeeperMonitor {
176
176
  let numTriesMint = 0;
177
177
  let numTriesRedeemTokens = 0;
178
178
  let numTriesClaimBounty = 0;
179
+ let lastJupQuotesUpdate = 0;
180
+ let jupQuotes: ({
181
+ inMint: string,
182
+ outMint: string,
183
+ tokenLedgerInstruction: TransactionInstruction,
184
+ swapInstruction: TransactionInstruction,
185
+ addressLookupTableAddresses: PublicKey[],
186
+ quoteResponse: any,
187
+ } | undefined)[] = [];
188
+
179
189
  while (true) {
180
190
  let uiIntent = this.rebalanceIntents.get(pubkey);
181
191
  if (!uiIntent) break;
@@ -192,7 +202,7 @@ export class KeeperMonitor {
192
202
  if (intent.current_action == "update_prices" && intent.last_action_timestamp > now)
193
203
  { nextCheckTime = intent.last_action_timestamp; continue; }
194
204
  if (intent.current_action == "update_prices") {
195
- if (numTriesUpdatePrices >= 3) break;
205
+ if (numTriesUpdatePrices >= 5) break;
196
206
  numTriesUpdatePrices += 1; try {
197
207
  let tx = await this.params.symmetryCore.updateTokenPricesTx({
198
208
  keeper: this.params.wallet.publicKey.toBase58(),
@@ -201,47 +211,62 @@ export class KeeperMonitor {
201
211
  });
202
212
  let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
203
213
  console.log("Update Prices - ", pubkey, " : ", res);
204
- } catch (e) { if (numTriesUpdatePrices == 3) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
214
+ } catch (e) { if (numTriesUpdatePrices == 4) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
215
+ nextCheckTime += 60;
205
216
  continue;
206
217
  }
207
218
  if (intent.auctions[2].end_time > now) {
208
219
  let basket = this.baskets.get(intent.basket);
209
220
  if (!basket) continue;
210
- let auction0StartTime = intent.auctions[0].start_time;
211
- let auction0EndTime = intent.auctions[0].end_time;
212
- let auction0MidTime = auction0StartTime + (auction0EndTime - auction0StartTime) / 2;
213
-
214
- let auction1StartTime = intent.auctions[1].start_time;
215
- let auction1EndTime = intent.auctions[1].end_time;
216
- let auction1MidTime = auction1StartTime + (auction1EndTime - auction1StartTime) / 2;
217
-
218
- let auction2StartTime = intent.auctions[2].start_time;
219
- let auction2EndTime = intent.auctions[2].end_time;
220
- let auction2MidTime = auction2StartTime + (auction2EndTime - auction2StartTime) / 2;
221
-
222
- if (now < auction0MidTime + 30) { nextCheckTime = auction0MidTime + 30; continue; }
223
- if (now >= auction0EndTime) {
224
- if (now < auction1MidTime + 10) { nextCheckTime = auction1MidTime + 10; continue; }
225
- if (now >= auction1EndTime)
226
- if (now < auction2MidTime + 5) { nextCheckTime = auction2MidTime + 5; continue; }
227
- }
221
+
228
222
  let pairs = getSwapPairs(chainData, basket!);
229
- for (let pair of pairs) try {
223
+
224
+ if (Date.now() / 1000 > lastJupQuotesUpdate + 60) {
225
+ lastJupQuotesUpdate = Date.now() / 1000;
226
+ jupQuotes = [];
227
+ for (let pair of pairs) {
228
+ if (pair.value < 0.005) continue;
229
+ if (this.params.network == "mainnet") try {
230
+ let res = await getJupTokenLedgerAndSwapInstructions({
231
+ keeper: this.params.wallet.publicKey,
232
+ basketMintIn: new PublicKey(pair.inMint),
233
+ basketMintOut: new PublicKey(pair.outMint),
234
+ basketAmountIn: pair.inAmount,
235
+ basketAmountOut: pair.outAmount,
236
+ swapMode: "ioc",
237
+ apiKey: this.params.jupiterApiKey,
238
+ maxJupAccounts: this.params.maxAllowedAccounts,
239
+ });
240
+ jupQuotes.push({
241
+ ...res,
242
+ inMint: pair.inMint,
243
+ outMint: pair.outMint,
244
+ });
245
+ console.log("Fetch new Jup Quote:", pair.inMint, pair.outMint);
246
+ console.log(pair, "Jup Quote:", parseFloat(res.quoteResponse?.outAmount ?? 0), "Requested In:", pair.inAmount);
247
+ } catch {}
248
+ }
249
+ }
250
+
251
+ for (let index = 0; index < pairs.length; index++) try {
252
+ let pair = pairs[index];
253
+ let jupIndex = jupQuotes.findIndex(q => q && q.inMint == pair.inMint && q.outMint == pair.outMint);
254
+ let quote = jupIndex >= 0 ? jupQuotes[jupIndex] : undefined;
255
+ if (!quote && this.params.network == "mainnet") continue;
230
256
  if (pair.value < 0.005) continue;
231
- await this.delay(1000);
232
- let { tokenLedgerInstruction, swapInstruction, addressLookupTableAddresses , quoteResponse} = this.params.network == "mainnet" ?
233
- await getJupTokenLedgerAndSwapInstructions({
234
- keeper: this.params.wallet.publicKey,
235
- basketMintIn: new PublicKey(pair.inMint),
236
- basketMintOut: new PublicKey(pair.outMint),
237
- basketAmountIn: pair.inAmount,
238
- basketAmountOut: pair.outAmount,
239
- swapMode: "ioc",
240
- apiKey: this.params.jupiterApiKey,
241
- maxJupAccounts: this.params.maxAllowedAccounts,
242
- }) : { tokenLedgerInstruction: undefined, swapInstruction: undefined, addressLookupTableAddresses: [], quoteResponse: undefined };
257
+
258
+ let tokenLedgerInstruction = quote?.tokenLedgerInstruction;
259
+ let swapInstruction = quote?.swapInstruction;
260
+ let addressLookupTableAddresses = quote?.addressLookupTableAddresses ?? [];
261
+ let quoteResponse = quote?.quoteResponse;
262
+
263
+ if (this.params.network == "mainnet" && !quoteResponse) continue;
243
264
  console.log(pair, "Jup Quote:", parseFloat(quoteResponse?.outAmount ?? 0), "Requested In:", pair.inAmount);
244
- if (this.params.network == "devnet" || parseFloat(quoteResponse?.outAmount ?? 0) > pair.inAmount) {
265
+
266
+ let quoteResponseAmount = parseFloat(quoteResponse?.outAmount ?? 0);
267
+ if (this.params.network == "mainnet" && quoteResponseAmount * 1.005 <= pair.inAmount)
268
+ continue;
269
+
245
270
  try {
246
271
  let tx = await this.params.symmetryCore.flashSwapTx({
247
272
  keeper: this.params.wallet.publicKey.toBase58(),
@@ -258,14 +283,15 @@ export class KeeperMonitor {
258
283
  });
259
284
  let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
260
285
  console.log("Flash Swap - ", pubkey, " : ", res);
286
+ jupQuotes = jupQuotes.filter(q => q && (q.inMint != pair.inMint || q.outMint != pair.outMint));
261
287
  } catch {}
262
- }
263
288
  } catch { }
264
289
  nextCheckTime = (Date.now() / 1000) + 8;
265
290
  continue;
266
291
  }
267
292
  if (intent.rebalance_type == "deposit") {
268
293
  if (numTriesMint >= 3) break;
294
+ lastJupQuotesUpdate = 0;
269
295
  numTriesMint += 1; try {
270
296
  let tx = await this.params.symmetryCore.mintTx({
271
297
  keeper: this.params.wallet.publicKey.toBase58(),
@@ -303,3 +329,257 @@ export class KeeperMonitor {
303
329
  }
304
330
  }
305
331
  }
332
+
333
+ export class RebalanceHandler {
334
+ private params: {
335
+ wallet: Wallet,
336
+ connection: Connection,
337
+ symmetryCore: SymmetryCore,
338
+ network: "devnet" | "mainnet",
339
+ jupiterApiKey: string;
340
+ maxAllowedAccounts: number;
341
+ simulateTransactions: boolean;
342
+ }
343
+
344
+ private intent: UIRebalanceIntent;
345
+ private basket: Basket;
346
+
347
+ constructor(params: {
348
+ intent: UIRebalanceIntent,
349
+ basket: Basket,
350
+ wallet: Wallet,
351
+ connection: Connection,
352
+ network: "devnet" | "mainnet",
353
+ jupiterApiKey: string,
354
+ maxAllowedAccounts: number,
355
+ priorityFee?: number,
356
+ simulateTransactions?: boolean,
357
+ }) {
358
+ this.params = {
359
+ wallet: params.wallet,
360
+ connection: params.connection,
361
+ symmetryCore: new SymmetryCore({
362
+ connection: params.connection,
363
+ network: params.network,
364
+ priorityFee: params.priorityFee ?? PRIORITY_FEE,
365
+ }),
366
+ network: params.network,
367
+ jupiterApiKey: params.jupiterApiKey,
368
+ maxAllowedAccounts: params.maxAllowedAccounts,
369
+ simulateTransactions: params.simulateTransactions ?? false,
370
+ };
371
+ this.intent = params.intent;
372
+ this.basket = params.basket;
373
+ }
374
+
375
+ delay = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
376
+
377
+ async refresh() {
378
+ this.intent = await this.params.symmetryCore.fetchRebalanceIntent(this.intent.formatted_data.pubkey);
379
+ this.basket = await this.params.symmetryCore.fetchBasket(this.intent.formatted_data.basket);
380
+ }
381
+
382
+ static async run(params: {
383
+ intentPubkey: PublicKey,
384
+ wallet: Wallet,
385
+ connection: Connection,
386
+ network: "devnet" | "mainnet",
387
+ jupiterApiKey: string,
388
+ maxAllowedAccounts: number,
389
+ priorityFee?: number,
390
+ simulateTransactions?: boolean,
391
+ }) {
392
+ let symmetryCore = new SymmetryCore({
393
+ connection: params.connection,
394
+ network: params.network,
395
+ priorityFee: params.priorityFee ?? PRIORITY_FEE,
396
+ });
397
+ let intent = await symmetryCore.fetchRebalanceIntent(params.intentPubkey.toBase58());
398
+ let basket = await symmetryCore.fetchBasket(intent.formatted_data.basket);
399
+ let handler = new RebalanceHandler({
400
+ intent, basket,
401
+ wallet: params.wallet,
402
+ connection: params.connection,
403
+ network: params.network,
404
+ jupiterApiKey: params.jupiterApiKey,
405
+ maxAllowedAccounts: params.maxAllowedAccounts,
406
+ simulateTransactions: params.simulateTransactions,
407
+ });
408
+ handler.execute();
409
+ for (let i = 0; i < 20; i++) {
410
+ await handler.delay(15 * 1000);
411
+ try { await handler.refresh(); }
412
+ catch (e) { handler.intent = undefined as any; break;}
413
+ }
414
+ }
415
+
416
+ private async execute() {
417
+ console.log("Starting rebalance handler for intent:", this.intent.formatted_data.pubkey);
418
+ let nextCheckTime = 0;
419
+ let numTriesUpdatePrices = 0;
420
+ let numTriesMint = 0;
421
+ let numTriesRedeemTokens = 0;
422
+ let numTriesClaimBounty = 0;
423
+ let rebalancePairs: {
424
+ inMint: string,
425
+ outMint: string,
426
+ inAmount: number,
427
+ outAmount: number,
428
+ value: number,
429
+ }[] = [];
430
+ let lastJupQuotesUpdate = 0;
431
+ let jupQuotes: ({
432
+ inMint: string,
433
+ outMint: string,
434
+ tokenLedgerInstruction: TransactionInstruction,
435
+ swapInstruction: TransactionInstruction,
436
+ addressLookupTableAddresses: PublicKey[],
437
+ quoteResponse: any,
438
+ }|undefined)[] = [];
439
+
440
+ while (true) {
441
+ if (!this.intent) break;
442
+ let intent = this.intent.formatted_data;
443
+ let chainData = this.intent.chain_data;
444
+ let now = Date.now() / 1000;
445
+
446
+ if (now < nextCheckTime) {
447
+ await this.delay(Math.min(30 * 1000, Math.max(0, nextCheckTime - now + 0.2) * 1000));
448
+ continue;
449
+ }
450
+ nextCheckTime = now + 5;
451
+
452
+ if (intent.current_action == "not_active") { console.log("Intent not active, stopping"); break; }
453
+ if (intent.current_action == "deposit_tokens") { console.log("Waiting for deposit..."); continue; }
454
+
455
+ if (intent.current_action == "update_prices" && intent.last_action_timestamp > now)
456
+ { nextCheckTime = intent.last_action_timestamp; continue; }
457
+
458
+ if (intent.current_action == "update_prices") {
459
+ if (numTriesUpdatePrices >= 3) { console.log("Max retries for update_prices"); break; }
460
+ numTriesUpdatePrices += 1; try {
461
+ let tx = await this.params.symmetryCore.updateTokenPricesTx({
462
+ keeper: this.params.wallet.publicKey.toBase58(),
463
+ basket: intent.basket,
464
+ rebalance_intent: intent.pubkey,
465
+ });
466
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
467
+ console.log("Update Prices:", res);
468
+ } catch (e) { if (numTriesUpdatePrices == 3) { console.log("Stop - update prices failed:", e); } }
469
+ continue;
470
+ }
471
+
472
+ if (intent.auctions[2].end_time > now) {
473
+
474
+ rebalancePairs = getSwapPairs(chainData, this.basket);
475
+
476
+ if (Date.now() / 1000 > lastJupQuotesUpdate + 60) {
477
+ let usedValue: Map<string, number> = new Map();
478
+ jupQuotes = [];
479
+ for (let pair of rebalancePairs) {
480
+ let inValue = usedValue.get(pair.inMint) ?? 0;
481
+ let outValue = usedValue.get(pair.outMint) ?? 0;
482
+ let swapValue = Math.min(pair.value - inValue, pair.value - outValue);
483
+ if (swapValue < 0.005) {
484
+ jupQuotes.push(undefined);
485
+ continue;
486
+ }
487
+ usedValue.set(pair.inMint, inValue + swapValue);
488
+ usedValue.set(pair.outMint, outValue + swapValue);
489
+ let res = undefined;
490
+ if (this.params.network == "mainnet") try {
491
+ res = {
492
+ ...(await getJupTokenLedgerAndSwapInstructions({
493
+ keeper: this.params.wallet.publicKey,
494
+ basketMintIn: new PublicKey(pair.inMint),
495
+ basketMintOut: new PublicKey(pair.outMint),
496
+ basketAmountIn: pair.inAmount,
497
+ basketAmountOut: pair.outAmount,
498
+ swapMode: "ioc",
499
+ apiKey: this.params.jupiterApiKey,
500
+ maxJupAccounts: this.params.maxAllowedAccounts,
501
+ })),
502
+ inMint: pair.inMint,
503
+ outMint: pair.outMint,
504
+ };
505
+ } catch {};
506
+ jupQuotes.push(res);
507
+ }
508
+ }
509
+
510
+ for (let index = 0; index < rebalancePairs.length; index++) try {
511
+ let pair = rebalancePairs[index];
512
+ let quote = jupQuotes.find(q => q && q.inMint == pair.inMint && q.outMint == pair.outMint);
513
+ if (!quote) continue;
514
+ if (pair.value < 0.005) continue;
515
+ let { tokenLedgerInstruction, swapInstruction, addressLookupTableAddresses, quoteResponse } = quote;
516
+ if (!quoteResponse) continue;
517
+ console.log(pair, "Jup Quote:", parseFloat(quoteResponse.outAmount), "Requested In:", pair.inAmount);
518
+ if (parseFloat(quoteResponse.outAmount) <= pair.inAmount && this.params.network == "mainnet")
519
+ continue;
520
+ try {
521
+ let tx = await this.params.symmetryCore.flashSwapTx({
522
+ keeper: this.params.wallet.publicKey.toBase58(),
523
+ basket: this.basket.ownAddress.toBase58(),
524
+ rebalance_intent: intent.pubkey,
525
+ mint_in: pair.inMint,
526
+ mint_out: pair.outMint,
527
+ amount_in: pair.inAmount,
528
+ amount_out: pair.outAmount,
529
+ mode: 2,
530
+ jup_token_ledger_ix: tokenLedgerInstruction,
531
+ jup_swap_ix: swapInstruction,
532
+ jup_address_lookup_table_addresses: addressLookupTableAddresses,
533
+ });
534
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
535
+ console.log("Flash Swap:", res);
536
+ rebalancePairs = rebalancePairs.splice(index, 1);
537
+ index -= 1;
538
+ } catch {}
539
+ } catch { }
540
+ nextCheckTime = (Date.now() / 1000) + 10;
541
+ continue;
542
+ }
543
+ if (intent.rebalance_type == "deposit") {
544
+ if (numTriesMint >= 3) break;
545
+ lastJupQuotesUpdate = 0;
546
+ numTriesMint += 1; try {
547
+ let tx = await this.params.symmetryCore.mintTx({
548
+ keeper: this.params.wallet.publicKey.toBase58(),
549
+ rebalance_intent: intent.pubkey,
550
+ });
551
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
552
+ console.log("Mint -", res);
553
+ } catch (e) { if (numTriesMint == 3) { console.log("Stop monitoring -", e); } }
554
+ continue;
555
+ }
556
+ let hasTokens = intent.tokens.find(token => token.amount > 0);
557
+ if (hasTokens && intent.rebalance_type == "withdraw") {
558
+ if (numTriesRedeemTokens >= 3) break;
559
+ numTriesRedeemTokens += 1; try {
560
+ let tx = await this.params.symmetryCore.redeemTokensTx({
561
+ keeper: this.params.wallet.publicKey.toBase58(),
562
+ rebalance_intent: intent.pubkey,
563
+ });
564
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
565
+ console.log("Redeem Tokens -", res);
566
+ } catch (e) { if (numTriesRedeemTokens == 3) { console.log("Stop monitoring -", e); } }
567
+ continue;
568
+ }
569
+ if (numTriesClaimBounty >= 3) break;
570
+ numTriesClaimBounty += 1; try {
571
+ let tx = await this.params.symmetryCore.claimBountyTx({
572
+ keeper: this.params.wallet.publicKey.toBase58(),
573
+ rebalance_intent: intent.pubkey,
574
+ });
575
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
576
+ console.log("Claim Bounty -", res);
577
+ nextCheckTime = now + 60;
578
+ } catch (e) { if (numTriesClaimBounty == 3) { console.log("Stop monitoring -", e); } }
579
+ continue;
580
+ }
581
+
582
+ }
583
+ }
584
+
585
+
package/test.ts CHANGED
@@ -13,7 +13,6 @@ import { getSwapPairs } from "./src/states/intents/rebalanceIntent";
13
13
  let kp = Array.from(Keypair.generate().secretKey);
14
14
 
15
15
  let jupApiKey = "";
16
-
17
16
  let connection = new Connection("https://api.devnet.solana.com");
18
17
 
19
18
  async function testStates() {
@@ -24,7 +23,7 @@ async function testStates() {
24
23
  let sdk = new SymmetryCore({
25
24
  connection: connection,
26
25
  network: "mainnet",
27
- priorityFee: 237,
26
+ priorityFee: 5237,
28
27
  });
29
28
  let keeperMonitor = new KeeperMonitor({
30
29
  wallet: wallet,
@@ -33,7 +32,7 @@ async function testStates() {
33
32
  jupiterApiKey: jupApiKey,
34
33
  maxAllowedAccounts: 40,
35
34
  simulateTransactions: false,
36
- priorityFee: 237,
35
+ priorityFee: 5237,
37
36
  });
38
37
  // keeperMonitor.run();
39
38
  // return;