@symmetry-hq/sdk 1.0.1

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.
Files changed (121) hide show
  1. package/dist/src/constants.d.ts +23 -0
  2. package/dist/src/constants.js +38 -0
  3. package/dist/src/index.d.ts +804 -0
  4. package/dist/src/index.js +2097 -0
  5. package/dist/src/instructions/automation/auction.d.ts +6 -0
  6. package/dist/src/instructions/automation/auction.js +40 -0
  7. package/dist/src/instructions/automation/claimBounty.d.ts +12 -0
  8. package/dist/src/instructions/automation/claimBounty.js +44 -0
  9. package/dist/src/instructions/automation/flashSwap.d.ts +21 -0
  10. package/dist/src/instructions/automation/flashSwap.js +74 -0
  11. package/dist/src/instructions/automation/priceUpdate.d.ts +19 -0
  12. package/dist/src/instructions/automation/priceUpdate.js +89 -0
  13. package/dist/src/instructions/automation/rebalanceIntent.d.ts +32 -0
  14. package/dist/src/instructions/automation/rebalanceIntent.js +117 -0
  15. package/dist/src/instructions/automation/rebalanceSwap.d.ts +11 -0
  16. package/dist/src/instructions/automation/rebalanceSwap.js +42 -0
  17. package/dist/src/instructions/management/addBounty.d.ts +7 -0
  18. package/dist/src/instructions/management/addBounty.js +41 -0
  19. package/dist/src/instructions/management/admin.d.ts +9 -0
  20. package/dist/src/instructions/management/admin.js +53 -0
  21. package/dist/src/instructions/management/claimFees.d.ts +15 -0
  22. package/dist/src/instructions/management/claimFees.js +95 -0
  23. package/dist/src/instructions/management/createBasket.d.ts +21 -0
  24. package/dist/src/instructions/management/createBasket.js +98 -0
  25. package/dist/src/instructions/management/edit.d.ts +51 -0
  26. package/dist/src/instructions/management/edit.js +477 -0
  27. package/dist/src/instructions/management/luts.d.ts +30 -0
  28. package/dist/src/instructions/management/luts.js +99 -0
  29. package/dist/src/instructions/pda.d.ts +25 -0
  30. package/dist/src/instructions/pda.js +128 -0
  31. package/dist/src/instructions/user/deposit.d.ts +20 -0
  32. package/dist/src/instructions/user/deposit.js +100 -0
  33. package/dist/src/instructions/user/withdraw.d.ts +8 -0
  34. package/dist/src/instructions/user/withdraw.js +36 -0
  35. package/dist/src/jup.d.ts +49 -0
  36. package/dist/src/jup.js +80 -0
  37. package/dist/src/keeperMonitor.d.ts +52 -0
  38. package/dist/src/keeperMonitor.js +624 -0
  39. package/dist/src/layouts/basket.d.ts +191 -0
  40. package/dist/src/layouts/basket.js +51 -0
  41. package/dist/src/layouts/config.d.ts +281 -0
  42. package/dist/src/layouts/config.js +237 -0
  43. package/dist/src/layouts/fraction.d.ts +20 -0
  44. package/dist/src/layouts/fraction.js +164 -0
  45. package/dist/src/layouts/intents/bounty.d.ts +18 -0
  46. package/dist/src/layouts/intents/bounty.js +19 -0
  47. package/dist/src/layouts/intents/intent.d.ts +209 -0
  48. package/dist/src/layouts/intents/intent.js +97 -0
  49. package/dist/src/layouts/intents/rebalanceIntent.d.ts +212 -0
  50. package/dist/src/layouts/intents/rebalanceIntent.js +94 -0
  51. package/dist/src/layouts/lookupTable.d.ts +7 -0
  52. package/dist/src/layouts/lookupTable.js +10 -0
  53. package/dist/src/layouts/oracle.d.ts +63 -0
  54. package/dist/src/layouts/oracle.js +96 -0
  55. package/dist/src/states/basket.d.ts +14 -0
  56. package/dist/src/states/basket.js +479 -0
  57. package/dist/src/states/config.d.ts +3 -0
  58. package/dist/src/states/config.js +71 -0
  59. package/dist/src/states/intents/intent.d.ts +10 -0
  60. package/dist/src/states/intents/intent.js +316 -0
  61. package/dist/src/states/intents/rebalanceIntent.d.ts +42 -0
  62. package/dist/src/states/intents/rebalanceIntent.js +680 -0
  63. package/dist/src/states/oracles/constants.d.ts +9 -0
  64. package/dist/src/states/oracles/constants.js +15 -0
  65. package/dist/src/states/oracles/oracle.d.ts +24 -0
  66. package/dist/src/states/oracles/oracle.js +168 -0
  67. package/dist/src/states/oracles/pythOracle.d.ts +132 -0
  68. package/dist/src/states/oracles/pythOracle.js +609 -0
  69. package/dist/src/states/oracles/raydiumClmmOracle.d.ts +184 -0
  70. package/dist/src/states/oracles/raydiumClmmOracle.js +843 -0
  71. package/dist/src/states/oracles/raydiumCpmmOracle.d.ts +120 -0
  72. package/dist/src/states/oracles/raydiumCpmmOracle.js +540 -0
  73. package/dist/src/states/oracles/switchboardOracle.d.ts +0 -0
  74. package/dist/src/states/oracles/switchboardOracle.js +1 -0
  75. package/dist/src/states/withdrawBasketFees.d.ts +10 -0
  76. package/dist/src/states/withdrawBasketFees.js +154 -0
  77. package/dist/src/txUtils.d.ts +65 -0
  78. package/dist/src/txUtils.js +306 -0
  79. package/dist/test.d.ts +1 -0
  80. package/dist/test.js +561 -0
  81. package/package.json +31 -0
  82. package/src/constants.ts +40 -0
  83. package/src/index.ts +2431 -0
  84. package/src/instructions/automation/auction.ts +55 -0
  85. package/src/instructions/automation/claimBounty.ts +69 -0
  86. package/src/instructions/automation/flashSwap.ts +104 -0
  87. package/src/instructions/automation/priceUpdate.ts +117 -0
  88. package/src/instructions/automation/rebalanceIntent.ts +181 -0
  89. package/src/instructions/management/addBounty.ts +55 -0
  90. package/src/instructions/management/admin.ts +72 -0
  91. package/src/instructions/management/claimFees.ts +129 -0
  92. package/src/instructions/management/createBasket.ts +138 -0
  93. package/src/instructions/management/edit.ts +602 -0
  94. package/src/instructions/management/luts.ts +157 -0
  95. package/src/instructions/pda.ts +151 -0
  96. package/src/instructions/user/deposit.ts +143 -0
  97. package/src/instructions/user/withdraw.ts +53 -0
  98. package/src/jup.ts +113 -0
  99. package/src/keeperMonitor.ts +585 -0
  100. package/src/layouts/basket.ts +233 -0
  101. package/src/layouts/config.ts +576 -0
  102. package/src/layouts/fraction.ts +164 -0
  103. package/src/layouts/intents/bounty.ts +35 -0
  104. package/src/layouts/intents/intent.ts +324 -0
  105. package/src/layouts/intents/rebalanceIntent.ts +306 -0
  106. package/src/layouts/lookupTable.ts +14 -0
  107. package/src/layouts/oracle.ts +157 -0
  108. package/src/states/basket.ts +527 -0
  109. package/src/states/config.ts +62 -0
  110. package/src/states/intents/intent.ts +311 -0
  111. package/src/states/intents/rebalanceIntent.ts +751 -0
  112. package/src/states/oracles/constants.ts +13 -0
  113. package/src/states/oracles/oracle.ts +212 -0
  114. package/src/states/oracles/pythOracle.ts +874 -0
  115. package/src/states/oracles/raydiumClmmOracle.ts +1193 -0
  116. package/src/states/oracles/raydiumCpmmOracle.ts +784 -0
  117. package/src/states/oracles/switchboardOracle.ts +0 -0
  118. package/src/states/withdrawBasketFees.ts +160 -0
  119. package/src/txUtils.ts +424 -0
  120. package/test.ts +609 -0
  121. package/tsconfig.json +101 -0
@@ -0,0 +1,585 @@
1
+ import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ import { Wallet } from "./txUtils";
3
+ import { Basket, FormattedRebalanceIntent, getJupTokenLedgerAndSwapInstructions, Intent, SymmetryCore, UIRebalanceIntent } from ".";
4
+ import { getSwapPairs } from "./states/intents/rebalanceIntent";
5
+ import { PRIORITY_FEE } from "./constants";
6
+
7
+ export class KeeperMonitor {
8
+ private params: {
9
+ wallet: Wallet,
10
+ connection: Connection,
11
+ symmetryCore: SymmetryCore,
12
+ network: "devnet" | "mainnet",
13
+ jupiterApiKey: string;
14
+ maxAllowedAccounts: number;
15
+ simulateTransactions: boolean;
16
+ }
17
+
18
+ private intents: Map<string, Intent>;
19
+ private rebalanceIntents: Map<string, UIRebalanceIntent>;
20
+ private baskets: Map<string, Basket>;
21
+
22
+ constructor(params: {
23
+ wallet: Wallet,
24
+ connection: Connection,
25
+ network: "devnet" | "mainnet",
26
+ jupiterApiKey: string,
27
+ maxAllowedAccounts: number,
28
+ priorityFee?: number,
29
+ simulateTransactions?: boolean,
30
+ }) {
31
+ this.params = {
32
+ wallet: params.wallet,
33
+ connection: params.connection,
34
+ symmetryCore: new SymmetryCore({
35
+ connection: params.connection,
36
+ network: params.network,
37
+ priorityFee: params.priorityFee ?? PRIORITY_FEE,
38
+ }),
39
+ network: params.network,
40
+ jupiterApiKey: params.jupiterApiKey,
41
+ maxAllowedAccounts: params.maxAllowedAccounts,
42
+ simulateTransactions: params.simulateTransactions ?? false,
43
+ };
44
+ //@ts-ignore
45
+ this.globalConfig = {};
46
+ this.intents = new Map();
47
+ this.rebalanceIntents = new Map();
48
+ this.baskets = new Map();
49
+ }
50
+
51
+ delay = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
52
+
53
+ async update() {
54
+ let baskets
55
+ try { baskets = await this.params.symmetryCore.fetchAllBaskets(); } catch (e) { console.log("Error fetching baskets:", e); }
56
+ if (baskets) {
57
+ for (let basket of baskets)
58
+ this.baskets.set(basket.ownAddress.toBase58(), basket);
59
+ }
60
+
61
+ let intents;
62
+ try { intents = await this.params.symmetryCore.fetchAllIntents(); } catch (e) { console.log("Error fetching intents:", e); }
63
+ if (intents) {
64
+ for (let intent of intents) {
65
+ let oldInstance = this.intents.get(intent.ownAddress!.toBase58());
66
+ this.intents.set(intent.ownAddress!.toBase58(), intent);
67
+ let isMonitored = oldInstance ? true : false;
68
+ if (!isMonitored) {
69
+ console.log("New Intent:", intent.ownAddress!.toBase58());
70
+ this.monitorIntent(intent.ownAddress!.toBase58());
71
+ }
72
+ }
73
+ for (let intent of this.intents.values()) {
74
+ if (!intents.find(i => i.ownAddress!.equals(intent.ownAddress!))) {
75
+ this.intents.delete(intent.ownAddress!.toBase58());
76
+ console.log("Intent deleted:", intent.ownAddress!.toBase58());
77
+ }
78
+ }
79
+ }
80
+
81
+ let rebalanceIntents;
82
+ try { rebalanceIntents = await this.params.symmetryCore.fetchAllRebalanceIntents(); } catch (e) { console.log("Error fetching rebalance intents:", e); }
83
+ if (rebalanceIntents) {
84
+ for (let rebalanceIntent of rebalanceIntents) {
85
+ let oldInstance = this.rebalanceIntents.get(rebalanceIntent.formatted_data.pubkey);
86
+ this.rebalanceIntents.set(rebalanceIntent.formatted_data.pubkey, rebalanceIntent);
87
+ let isMonitored = oldInstance && (
88
+ oldInstance.formatted_data.current_action != "deposit_tokens" &&
89
+ oldInstance.formatted_data.current_action != "not_active"
90
+ );
91
+ let shouldMonitor = rebalanceIntent && (
92
+ rebalanceIntent.formatted_data.current_action != "deposit_tokens" &&
93
+ rebalanceIntent.formatted_data.current_action != "not_active"
94
+ );
95
+ if (!isMonitored && shouldMonitor) {
96
+ console.log("New Rebalance Intent:", rebalanceIntent.formatted_data.pubkey);
97
+ this.monitorRebalanceIntent(rebalanceIntent.formatted_data.pubkey);
98
+ }
99
+ }
100
+ for (let rebalanceIntent of this.rebalanceIntents.values()) {
101
+ if (!rebalanceIntents.find(i => i.formatted_data.pubkey == rebalanceIntent.formatted_data.pubkey)) {
102
+ this.rebalanceIntents.delete(rebalanceIntent.formatted_data.pubkey);
103
+ console.log("Rebalance Intent deleted:", rebalanceIntent.formatted_data.pubkey);
104
+ }
105
+ }
106
+ }
107
+
108
+ console.log("Baskets: ", this.baskets?.size, " Intents: ", this.intents.size, " Rebalance intents: ", this.rebalanceIntents.size);
109
+ }
110
+
111
+ async run(runTime?: number) {
112
+ console.log("Starting keeper monitor", this.params.wallet.publicKey.toBase58());
113
+
114
+ if (!runTime) runTime = 10 * 60;
115
+ if (runTime < 60) runTime = 60;
116
+ let startTime = Date.now() / 1000;
117
+ while (true) {
118
+ let now = Date.now() / 1000;
119
+ if (now + 45 > startTime + runTime) break;
120
+ await this.update();
121
+ await this.delay(Math.min(30 * 1000, Math.max(0, startTime + runTime - now - 45 + 0.2) * 1000));
122
+ }
123
+
124
+ this.baskets = new Map();
125
+ this.intents = new Map();
126
+ this.rebalanceIntents = new Map();
127
+ await this.delay(45 * 1000);
128
+ }
129
+
130
+ async monitorIntent(pubkey: string) {
131
+ let nextCheckTime = 0;
132
+ let numTries = 0;
133
+ while (true) {
134
+ let intent = this.intents.get(pubkey);
135
+ if (!intent) break;
136
+ let now = Date.now() / 1000;
137
+ if (now < nextCheckTime) {
138
+ await this.delay(Math.min(30 * 1000, Math.max(0, nextCheckTime - now + 0.2) * 1000));
139
+ continue;
140
+ }
141
+ nextCheckTime = now + 35;
142
+ if (intent.formatted!.activation_timestamp > now)
143
+ { nextCheckTime = intent.formatted!.activation_timestamp; continue; }
144
+ if (now < intent.formatted!.expiration_timestamp && numTries >= 2)
145
+ { nextCheckTime = intent.formatted!.expiration_timestamp; continue; }
146
+ if (now < intent.formatted!.expiration_timestamp) {
147
+ numTries += 1; try {
148
+ let tx = await this.params.symmetryCore.executeBasketIntentTx({
149
+ keeper: this.params.wallet.publicKey.toBase58(),
150
+ intent: intent.ownAddress!.toBase58(),
151
+ });
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);
154
+ nextCheckTime = now + 60;
155
+ } catch {}
156
+ continue;
157
+ }
158
+ numTries = Math.max(2, numTries);
159
+ if (numTries >= 4) break;
160
+ numTries += 1; try {
161
+ let tx = await this.params.symmetryCore.cancelBasketIntentTx({
162
+ keeper: this.params.wallet.publicKey.toBase58(),
163
+ intent: intent.ownAddress!.toBase58(),
164
+ });
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);
167
+ nextCheckTime = now + 60;
168
+ } catch (e) { if (numTries == 4) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
169
+ continue;
170
+ }
171
+ }
172
+
173
+ async monitorRebalanceIntent(pubkey: string) {
174
+ let nextCheckTime = 0;
175
+ let numTriesUpdatePrices = 0;
176
+ let numTriesMint = 0;
177
+ let numTriesRedeemTokens = 0;
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
+
189
+ while (true) {
190
+ let uiIntent = this.rebalanceIntents.get(pubkey);
191
+ if (!uiIntent) break;
192
+ let intent = uiIntent.formatted_data;
193
+ let chainData = uiIntent.chain_data;
194
+ let now = Date.now() / 1000;
195
+ if (now < nextCheckTime) {
196
+ await this.delay(Math.min(30 * 1000, Math.max(0, nextCheckTime - now + 0.2) * 1000));
197
+ continue;
198
+ }
199
+ nextCheckTime = now + 35;
200
+ if (intent.current_action == "not_active") continue;
201
+ if (intent.current_action == "deposit_tokens") continue;
202
+ if (intent.current_action == "update_prices" && intent.last_action_timestamp > now)
203
+ { nextCheckTime = intent.last_action_timestamp; continue; }
204
+ if (intent.current_action == "update_prices") {
205
+ if (numTriesUpdatePrices >= 5) break;
206
+ numTriesUpdatePrices += 1; try {
207
+ let tx = await this.params.symmetryCore.updateTokenPricesTx({
208
+ keeper: this.params.wallet.publicKey.toBase58(),
209
+ basket: intent.basket,
210
+ rebalance_intent: intent.pubkey,
211
+ });
212
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
213
+ console.log("Update Prices - ", pubkey, " : ", res);
214
+ } catch (e) { if (numTriesUpdatePrices == 5) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
215
+ nextCheckTime += 60;
216
+ continue;
217
+ }
218
+ if (intent.auctions[2].end_time > now) {
219
+ let basket = this.baskets.get(intent.basket);
220
+ if (!basket) continue;
221
+
222
+ let pairs = getSwapPairs(chainData, basket!);
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;
256
+ if (pair.value < 0.005) continue;
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;
264
+ console.log(pair, "Jup Quote:", parseFloat(quoteResponse?.outAmount ?? 0), "Requested In:", pair.inAmount);
265
+
266
+ let quoteResponseAmount = parseFloat(quoteResponse?.outAmount ?? 0);
267
+ if (this.params.network == "mainnet" && quoteResponseAmount * 1.005 <= pair.inAmount)
268
+ continue;
269
+
270
+ try {
271
+ let tx = await this.params.symmetryCore.flashSwapTx({
272
+ keeper: this.params.wallet.publicKey.toBase58(),
273
+ basket: basket!.ownAddress.toBase58(),
274
+ rebalance_intent: intent.pubkey,
275
+ mint_in: pair.inMint,
276
+ mint_out: pair.outMint,
277
+ amount_in: pair.inAmount,
278
+ amount_out: pair.outAmount,
279
+ mode: 2,
280
+ jup_token_ledger_ix: tokenLedgerInstruction,
281
+ jup_swap_ix: swapInstruction,
282
+ jup_address_lookup_table_addresses: addressLookupTableAddresses,
283
+ });
284
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
285
+ console.log("Flash Swap - ", pubkey, " : ", res);
286
+ jupQuotes = jupQuotes.filter(q => q && (q.inMint != pair.inMint || q.outMint != pair.outMint));
287
+ } catch {}
288
+ } catch { }
289
+ nextCheckTime = (Date.now() / 1000) + 8;
290
+ continue;
291
+ }
292
+ if (intent.rebalance_type == "deposit") {
293
+ if (numTriesMint >= 3) break;
294
+ lastJupQuotesUpdate = 0;
295
+ numTriesMint += 1; try {
296
+ let tx = await this.params.symmetryCore.mintTx({
297
+ keeper: this.params.wallet.publicKey.toBase58(),
298
+ rebalance_intent: intent.pubkey,
299
+ });
300
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
301
+ console.log("Mint - ", pubkey, " : ", res);
302
+ } catch (e) { if (numTriesMint == 3) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
303
+ continue;
304
+ }
305
+ let hasTokens = intent.tokens.find(token => token.amount > 0);
306
+ if (hasTokens && intent.rebalance_type == "withdraw") {
307
+ if (numTriesRedeemTokens >= 3) break;
308
+ numTriesRedeemTokens += 1; try {
309
+ let tx = await this.params.symmetryCore.redeemTokensTx({
310
+ keeper: this.params.wallet.publicKey.toBase58(),
311
+ rebalance_intent: intent.pubkey,
312
+ });
313
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
314
+ console.log("Redeem Tokens:", res);
315
+ } catch (e) { if (numTriesRedeemTokens == 3) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
316
+ continue;
317
+ }
318
+ if (numTriesClaimBounty >= 3) break;
319
+ numTriesClaimBounty += 1; try {
320
+ let tx = await this.params.symmetryCore.claimBountyTx({
321
+ keeper: this.params.wallet.publicKey.toBase58(),
322
+ rebalance_intent: intent.pubkey,
323
+ });
324
+ let res = await this.params.symmetryCore.signAndSendTxPayloadBatchSequence({txPayloadBatchSequence: tx, wallet: this.params.wallet, simulateTransactions: this.params.simulateTransactions});
325
+ console.log("Claim Bounty - ", pubkey, " : ", res);
326
+ nextCheckTime = now + 60;
327
+ } catch (e) { if (numTriesClaimBounty == 3) { console.log("Stop monitoring - ", pubkey, " : ", e); } }
328
+ continue;
329
+ }
330
+ }
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 >= 5) { 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 == 5) { 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
+