@atomiqlabs/chain-solana 12.0.12 → 12.0.14

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 (114) hide show
  1. package/LICENSE +201 -201
  2. package/dist/index.d.ts +29 -29
  3. package/dist/index.js +45 -45
  4. package/dist/solana/SolanaChainType.d.ts +11 -11
  5. package/dist/solana/SolanaChainType.js +2 -2
  6. package/dist/solana/SolanaChains.d.ts +20 -20
  7. package/dist/solana/SolanaChains.js +25 -25
  8. package/dist/solana/SolanaInitializer.d.ts +18 -18
  9. package/dist/solana/SolanaInitializer.js +63 -63
  10. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
  11. package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
  12. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
  13. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
  14. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
  15. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
  16. package/dist/solana/btcrelay/program/programIdl.json +671 -671
  17. package/dist/solana/chain/SolanaAction.d.ts +26 -26
  18. package/dist/solana/chain/SolanaAction.js +86 -86
  19. package/dist/solana/chain/SolanaChainInterface.d.ts +65 -65
  20. package/dist/solana/chain/SolanaChainInterface.js +125 -125
  21. package/dist/solana/chain/SolanaModule.d.ts +14 -14
  22. package/dist/solana/chain/SolanaModule.js +13 -13
  23. package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
  24. package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
  25. package/dist/solana/chain/modules/SolanaBlocks.d.ts +28 -28
  26. package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
  27. package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
  28. package/dist/solana/chain/modules/SolanaEvents.js +238 -225
  29. package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
  30. package/dist/solana/chain/modules/SolanaFees.js +379 -379
  31. package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
  32. package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
  33. package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
  34. package/dist/solana/chain/modules/SolanaSlots.js +68 -68
  35. package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
  36. package/dist/solana/chain/modules/SolanaTokens.js +248 -248
  37. package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
  38. package/dist/solana/chain/modules/SolanaTransactions.js +323 -323
  39. package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
  40. package/dist/solana/events/SolanaChainEvents.js +256 -256
  41. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +75 -75
  42. package/dist/solana/events/SolanaChainEventsBrowser.js +172 -172
  43. package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
  44. package/dist/solana/program/SolanaProgramBase.js +43 -43
  45. package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
  46. package/dist/solana/program/SolanaProgramModule.js +11 -11
  47. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
  48. package/dist/solana/program/modules/SolanaProgramEvents.js +114 -114
  49. package/dist/solana/swaps/SolanaSwapData.d.ts +71 -71
  50. package/dist/solana/swaps/SolanaSwapData.js +292 -292
  51. package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
  52. package/dist/solana/swaps/SolanaSwapModule.js +11 -11
  53. package/dist/solana/swaps/SolanaSwapProgram.d.ts +224 -224
  54. package/dist/solana/swaps/SolanaSwapProgram.js +570 -570
  55. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
  56. package/dist/solana/swaps/SwapTypeEnum.js +42 -42
  57. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
  58. package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
  59. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
  60. package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
  61. package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
  62. package/dist/solana/swaps/modules/SwapClaim.js +291 -291
  63. package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
  64. package/dist/solana/swaps/modules/SwapInit.js +519 -519
  65. package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
  66. package/dist/solana/swaps/modules/SwapRefund.js +262 -262
  67. package/dist/solana/swaps/programIdl.json +945 -945
  68. package/dist/solana/swaps/programTypes.d.ts +943 -943
  69. package/dist/solana/swaps/programTypes.js +945 -945
  70. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -9
  71. package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
  72. package/dist/solana/wallet/SolanaSigner.d.ts +11 -11
  73. package/dist/solana/wallet/SolanaSigner.js +17 -17
  74. package/dist/utils/Utils.d.ts +53 -53
  75. package/dist/utils/Utils.js +170 -170
  76. package/package.json +41 -41
  77. package/src/index.ts +36 -36
  78. package/src/solana/SolanaChainType.ts +27 -27
  79. package/src/solana/SolanaChains.ts +23 -23
  80. package/src/solana/SolanaInitializer.ts +102 -102
  81. package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -589
  82. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
  83. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
  84. package/src/solana/btcrelay/program/programIdl.json +670 -670
  85. package/src/solana/chain/SolanaAction.ts +108 -108
  86. package/src/solana/chain/SolanaChainInterface.ts +192 -192
  87. package/src/solana/chain/SolanaModule.ts +20 -20
  88. package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
  89. package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
  90. package/src/solana/chain/modules/SolanaEvents.ts +270 -256
  91. package/src/solana/chain/modules/SolanaFees.ts +450 -450
  92. package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
  93. package/src/solana/chain/modules/SolanaSlots.ts +82 -82
  94. package/src/solana/chain/modules/SolanaTokens.ts +307 -307
  95. package/src/solana/chain/modules/SolanaTransactions.ts +365 -365
  96. package/src/solana/events/SolanaChainEvents.ts +299 -299
  97. package/src/solana/events/SolanaChainEventsBrowser.ts +209 -209
  98. package/src/solana/program/SolanaProgramBase.ts +79 -79
  99. package/src/solana/program/SolanaProgramModule.ts +15 -15
  100. package/src/solana/program/modules/SolanaProgramEvents.ts +155 -155
  101. package/src/solana/swaps/SolanaSwapData.ts +430 -430
  102. package/src/solana/swaps/SolanaSwapModule.ts +16 -16
  103. package/src/solana/swaps/SolanaSwapProgram.ts +854 -854
  104. package/src/solana/swaps/SwapTypeEnum.ts +29 -29
  105. package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
  106. package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
  107. package/src/solana/swaps/modules/SwapClaim.ts +389 -389
  108. package/src/solana/swaps/modules/SwapInit.ts +663 -663
  109. package/src/solana/swaps/modules/SwapRefund.ts +323 -323
  110. package/src/solana/swaps/programIdl.json +944 -944
  111. package/src/solana/swaps/programTypes.ts +1885 -1885
  112. package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
  113. package/src/solana/wallet/SolanaSigner.ts +24 -24
  114. package/src/utils/Utils.ts +180 -180
@@ -1,308 +1,308 @@
1
- import {SolanaSwapModule} from "../SolanaSwapModule";
2
- import {AccountInfo, PublicKey, Signer, SystemProgram} from "@solana/web3.js";
3
- import {IStorageManager, StorageObject} from "@atomiqlabs/base";
4
- import {SolanaSwapProgram} from "../SolanaSwapProgram";
5
- import {SolanaAction} from "../../chain/SolanaAction";
6
- import {SolanaTx} from "../../chain/modules/SolanaTransactions";
7
- import {tryWithRetries} from "../../../utils/Utils";
8
- import {SolanaSigner} from "../../wallet/SolanaSigner";
9
- import {randomBytes} from "@noble/hashes/utils";
10
- import {SolanaChainInterface} from "../../chain/SolanaChainInterface";
11
-
12
- export class StoredDataAccount implements StorageObject {
13
-
14
- accountKey: PublicKey;
15
- owner: PublicKey;
16
-
17
- constructor(accountKey: PublicKey, owner: PublicKey);
18
- constructor(data: any);
19
-
20
- constructor(accountKeyOrData: PublicKey | any, owner?: PublicKey) {
21
- if(accountKeyOrData instanceof PublicKey) {
22
- this.accountKey = accountKeyOrData;
23
- this.owner = owner;
24
- } else {
25
- this.accountKey = new PublicKey(accountKeyOrData.accountKey);
26
- this.owner = new PublicKey(accountKeyOrData.owner);
27
- }
28
- }
29
-
30
- serialize(): any {
31
- return {
32
- accountKey: this.accountKey.toBase58(),
33
- owner: this.owner.toBase58()
34
- }
35
- }
36
-
37
- }
38
-
39
- export class SolanaDataAccount extends SolanaSwapModule {
40
-
41
- readonly SwapTxDataAlt = this.program.keypair(
42
- (reversedTxId: Buffer, signer: Signer) => [Buffer.from(signer.secretKey), reversedTxId]
43
- );
44
- readonly SwapTxDataAltBuffer = this.program.keypair((reversedTxId: Buffer, secret: Buffer) => [secret, reversedTxId]);
45
-
46
- readonly storage: IStorageManager<StoredDataAccount>;
47
-
48
- private static readonly CUCosts = {
49
- DATA_REMOVE: 50000,
50
- DATA_CREATE_AND_WRITE: 15000,
51
- DATA_CREATE: 5000,
52
- DATA_WRITE: 15000
53
- };
54
-
55
- /**
56
- * Action for initialization of the data account
57
- *
58
- * @param signer
59
- * @param accountKey
60
- * @param dataLength
61
- * @private
62
- */
63
- private async InitDataAccount(
64
- signer: PublicKey,
65
- accountKey: Signer,
66
- dataLength: number
67
- ): Promise<SolanaAction> {
68
- const accountSize = 32+dataLength;
69
- const lamportsDeposit = await tryWithRetries(
70
- () => this.connection.getMinimumBalanceForRentExemption(accountSize),
71
- this.retryPolicy
72
- );
73
-
74
- return new SolanaAction(signer, this.root, [
75
- SystemProgram.createAccount({
76
- fromPubkey: signer,
77
- newAccountPubkey: accountKey.publicKey,
78
- lamports: lamportsDeposit,
79
- space: accountSize,
80
- programId: this.swapProgram.programId
81
- }),
82
- await this.swapProgram.methods
83
- .initData()
84
- .accounts({
85
- signer,
86
- data: accountKey.publicKey
87
- })
88
- .instruction(),
89
- ], SolanaDataAccount.CUCosts.DATA_CREATE, null, [accountKey]);
90
- }
91
-
92
- /**
93
- * Action for closing the specific data account
94
- *
95
- * @param signer
96
- * @param publicKey
97
- */
98
- private async CloseDataAccount(signer: PublicKey, publicKey: PublicKey): Promise<SolanaAction> {
99
- return new SolanaAction(
100
- signer,
101
- this.root,
102
- await this.swapProgram.methods
103
- .closeData()
104
- .accounts({
105
- signer,
106
- data: publicKey
107
- })
108
- .instruction(),
109
- SolanaDataAccount.CUCosts.DATA_REMOVE,
110
- await this.root.Fees.getFeeRate([signer, publicKey])
111
- );
112
- }
113
-
114
- /**
115
- * Action for writing data to a data account, writes up to sizeLimit starting from the offset position of the
116
- * provided writeData buffer
117
- *
118
- * @param signer
119
- * @param accountKey account public key to write to
120
- * @param writeData buffer holding the write data
121
- * @param offset data from buffer starting at offset are written
122
- * @param sizeLimit maximum amount of data to be written to the data account in this action
123
- * @private
124
- * @returns {Promise<{bytesWritten: number, action: SolanaAction}>} bytes written to the data account & action
125
- */
126
- private async WriteData(
127
- signer: PublicKey,
128
- accountKey: Signer,
129
- writeData: Buffer,
130
- offset: number,
131
- sizeLimit: number
132
- ): Promise<{bytesWritten: number, action: SolanaAction}> {
133
- const writeLen = Math.min(writeData.length-offset, sizeLimit);
134
-
135
- return {
136
- bytesWritten: writeLen,
137
- action: new SolanaAction(signer, this.root,
138
- await this.swapProgram.methods
139
- .writeData(offset, writeData.slice(offset, offset+writeLen))
140
- .accounts({
141
- signer,
142
- data: accountKey.publicKey
143
- })
144
- .instruction(),
145
- SolanaDataAccount.CUCosts.DATA_WRITE
146
- )
147
- };
148
- }
149
-
150
- constructor(chainInterface: SolanaChainInterface, program: SolanaSwapProgram, storage: IStorageManager<StoredDataAccount>) {
151
- super(chainInterface, program);
152
- this.storage = storage;
153
- }
154
-
155
- /**
156
- * Saves data account to the storage, the storage is required such that we are able to close the accounts later
157
- * manually in case the claim doesn't happen (expires due to fees, etc.)
158
- *
159
- * @param signer
160
- * @param publicKey
161
- * @private
162
- */
163
- private saveDataAccount(signer: PublicKey, publicKey: PublicKey): Promise<void> {
164
- return this.storage.saveData(publicKey.toBase58(), new StoredDataAccount(publicKey, signer));
165
- }
166
-
167
- /**
168
- * Initializes the data account handler, loads the existing data accounts which should be checked and closed
169
- */
170
- public async init() {
171
- await this.storage.init();
172
- const loadedData = await this.storage.loadData(StoredDataAccount);
173
- this.logger.info("init(): initialized & loaded stored data accounts, count: "+loadedData.length);
174
- }
175
-
176
- /**
177
- * Removes data account from the list of accounts that should be checked for reclaiming the locked SOL, this should
178
- * be called after a batch of transactions claiming the swap was confirmed
179
- *
180
- * @param publicKey
181
- */
182
- public removeDataAccount(publicKey: PublicKey): Promise<void> {
183
- return this.storage.removeData(publicKey.toBase58());
184
- }
185
-
186
- public async getDataAccountsInfo(signer: PublicKey): Promise<{closePublicKeys: PublicKey[], count: number, totalValue: bigint}> {
187
- const closePublicKeys: PublicKey[] = [];
188
- let totalLocked = 0n;
189
- for(let key in this.storage.data) {
190
- const {accountKey, owner} = this.storage.data[key];
191
-
192
- if(!owner.equals(signer)) continue;
193
-
194
- try {
195
- const fetchedDataAccount: AccountInfo<Buffer> = await this.connection.getAccountInfo(accountKey);
196
- if(fetchedDataAccount==null) {
197
- await this.removeDataAccount(accountKey);
198
- continue;
199
- }
200
- closePublicKeys.push(accountKey);
201
- totalLocked += BigInt(fetchedDataAccount.lamports);
202
- } catch (e) {}
203
- }
204
-
205
- return {
206
- closePublicKeys,
207
- count: closePublicKeys.length,
208
- totalValue: totalLocked
209
- }
210
- }
211
-
212
- /**
213
- * Sweeps all old data accounts, reclaiming the SOL locked in the PDAs
214
- */
215
- public async sweepDataAccounts(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
216
- const {closePublicKeys, totalValue} = await this.getDataAccountsInfo(signer.getPublicKey());
217
-
218
- if(closePublicKeys.length===0) {
219
- this.logger.debug("sweepDataAccounts(): no old data accounts found, no need to close any!");
220
- return;
221
- }
222
-
223
- this.logger.debug("sweepDataAccounts(): closing old data accounts: ", closePublicKeys);
224
-
225
- let txns: SolanaTx[] = [];
226
- for(let publicKey of closePublicKeys) {
227
- await (await this.CloseDataAccount(signer.getPublicKey(), publicKey)).addToTxs(txns);
228
- }
229
-
230
- const result = await this.root.Transactions.sendAndConfirm(signer, txns, true, null, true);
231
-
232
- this.logger.info("sweepDataAccounts(): old data accounts closed: "+
233
- closePublicKeys.map(pk => pk.toBase58()).join());
234
-
235
- for(let publicKey of closePublicKeys) {
236
- await this.removeDataAccount(publicKey);
237
- }
238
-
239
- return {
240
- txIds: result,
241
- count: closePublicKeys.length,
242
- totalValue: totalValue
243
- };
244
- }
245
-
246
- /**
247
- * Adds the transactions writing (and also initializing if it doesn't exist) data to the data account
248
- *
249
- * @param signer
250
- * @param reversedTxId reversed btc tx id is used to derive the data account address
251
- * @param writeData full data to be written to the data account
252
- * @param txs solana transactions array, where txns for writing & initializing will be added
253
- * @param feeRate fee rate to use for the transactions
254
- */
255
- public async addTxsWriteData(
256
- signer: SolanaSigner | PublicKey,
257
- reversedTxId: Buffer,
258
- writeData: Buffer,
259
- txs: SolanaTx[],
260
- feeRate: string
261
- ): Promise<PublicKey> {
262
- let txDataKey: Signer;
263
- let fetchedDataAccount: AccountInfo<Buffer> = null;
264
- if(signer instanceof SolanaSigner && signer.keypair!=null) {
265
- txDataKey = this.SwapTxDataAlt(reversedTxId, signer.keypair);
266
- fetchedDataAccount = await tryWithRetries<AccountInfo<Buffer>>(
267
- () => this.connection.getAccountInfo(txDataKey.publicKey),
268
- this.retryPolicy
269
- );
270
- } else {
271
- const secret = Buffer.from(randomBytes(32));
272
- txDataKey = this.SwapTxDataAltBuffer(reversedTxId, secret);
273
- }
274
-
275
- const signerKey = signer instanceof SolanaSigner ? signer.getPublicKey() : signer;
276
-
277
- let pointer = 0;
278
- if(fetchedDataAccount==null) {
279
- const action = new SolanaAction(signerKey, this.root);
280
- action.add(await this.InitDataAccount(signerKey, txDataKey, writeData.length));
281
- const {
282
- bytesWritten,
283
- action: writeAction
284
- } = await this.WriteData(signerKey, txDataKey, writeData, pointer, 420);
285
- this.logger.debug("addTxsWriteData(): Write partial data ("+pointer+" .. "+(pointer+bytesWritten)+")/"+writeData.length+
286
- " key: "+txDataKey.publicKey.toBase58());
287
- pointer += bytesWritten;
288
- action.add(writeAction);
289
-
290
- await action.addToTxs(txs, feeRate);
291
- await this.saveDataAccount(signerKey, txDataKey.publicKey);
292
- }
293
-
294
- while(pointer<writeData.length) {
295
- const {
296
- bytesWritten,
297
- action
298
- } = await this.WriteData(signerKey, txDataKey, writeData, pointer, 950);
299
- this.logger.debug("addTxsWriteData(): Write partial data ("+pointer+" .. "+(pointer+bytesWritten)+")/"+writeData.length+
300
- " key: "+txDataKey.publicKey.toBase58());
301
- pointer += bytesWritten;
302
- await action.addToTxs(txs, feeRate);
303
- }
304
-
305
- return txDataKey.publicKey;
306
- }
307
-
1
+ import {SolanaSwapModule} from "../SolanaSwapModule";
2
+ import {AccountInfo, PublicKey, Signer, SystemProgram} from "@solana/web3.js";
3
+ import {IStorageManager, StorageObject} from "@atomiqlabs/base";
4
+ import {SolanaSwapProgram} from "../SolanaSwapProgram";
5
+ import {SolanaAction} from "../../chain/SolanaAction";
6
+ import {SolanaTx} from "../../chain/modules/SolanaTransactions";
7
+ import {tryWithRetries} from "../../../utils/Utils";
8
+ import {SolanaSigner} from "../../wallet/SolanaSigner";
9
+ import {randomBytes} from "@noble/hashes/utils";
10
+ import {SolanaChainInterface} from "../../chain/SolanaChainInterface";
11
+
12
+ export class StoredDataAccount implements StorageObject {
13
+
14
+ accountKey: PublicKey;
15
+ owner: PublicKey;
16
+
17
+ constructor(accountKey: PublicKey, owner: PublicKey);
18
+ constructor(data: any);
19
+
20
+ constructor(accountKeyOrData: PublicKey | any, owner?: PublicKey) {
21
+ if(accountKeyOrData instanceof PublicKey) {
22
+ this.accountKey = accountKeyOrData;
23
+ this.owner = owner;
24
+ } else {
25
+ this.accountKey = new PublicKey(accountKeyOrData.accountKey);
26
+ this.owner = new PublicKey(accountKeyOrData.owner);
27
+ }
28
+ }
29
+
30
+ serialize(): any {
31
+ return {
32
+ accountKey: this.accountKey.toBase58(),
33
+ owner: this.owner.toBase58()
34
+ }
35
+ }
36
+
37
+ }
38
+
39
+ export class SolanaDataAccount extends SolanaSwapModule {
40
+
41
+ readonly SwapTxDataAlt = this.program.keypair(
42
+ (reversedTxId: Buffer, signer: Signer) => [Buffer.from(signer.secretKey), reversedTxId]
43
+ );
44
+ readonly SwapTxDataAltBuffer = this.program.keypair((reversedTxId: Buffer, secret: Buffer) => [secret, reversedTxId]);
45
+
46
+ readonly storage: IStorageManager<StoredDataAccount>;
47
+
48
+ private static readonly CUCosts = {
49
+ DATA_REMOVE: 50000,
50
+ DATA_CREATE_AND_WRITE: 15000,
51
+ DATA_CREATE: 5000,
52
+ DATA_WRITE: 15000
53
+ };
54
+
55
+ /**
56
+ * Action for initialization of the data account
57
+ *
58
+ * @param signer
59
+ * @param accountKey
60
+ * @param dataLength
61
+ * @private
62
+ */
63
+ private async InitDataAccount(
64
+ signer: PublicKey,
65
+ accountKey: Signer,
66
+ dataLength: number
67
+ ): Promise<SolanaAction> {
68
+ const accountSize = 32+dataLength;
69
+ const lamportsDeposit = await tryWithRetries(
70
+ () => this.connection.getMinimumBalanceForRentExemption(accountSize),
71
+ this.retryPolicy
72
+ );
73
+
74
+ return new SolanaAction(signer, this.root, [
75
+ SystemProgram.createAccount({
76
+ fromPubkey: signer,
77
+ newAccountPubkey: accountKey.publicKey,
78
+ lamports: lamportsDeposit,
79
+ space: accountSize,
80
+ programId: this.swapProgram.programId
81
+ }),
82
+ await this.swapProgram.methods
83
+ .initData()
84
+ .accounts({
85
+ signer,
86
+ data: accountKey.publicKey
87
+ })
88
+ .instruction(),
89
+ ], SolanaDataAccount.CUCosts.DATA_CREATE, null, [accountKey]);
90
+ }
91
+
92
+ /**
93
+ * Action for closing the specific data account
94
+ *
95
+ * @param signer
96
+ * @param publicKey
97
+ */
98
+ private async CloseDataAccount(signer: PublicKey, publicKey: PublicKey): Promise<SolanaAction> {
99
+ return new SolanaAction(
100
+ signer,
101
+ this.root,
102
+ await this.swapProgram.methods
103
+ .closeData()
104
+ .accounts({
105
+ signer,
106
+ data: publicKey
107
+ })
108
+ .instruction(),
109
+ SolanaDataAccount.CUCosts.DATA_REMOVE,
110
+ await this.root.Fees.getFeeRate([signer, publicKey])
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Action for writing data to a data account, writes up to sizeLimit starting from the offset position of the
116
+ * provided writeData buffer
117
+ *
118
+ * @param signer
119
+ * @param accountKey account public key to write to
120
+ * @param writeData buffer holding the write data
121
+ * @param offset data from buffer starting at offset are written
122
+ * @param sizeLimit maximum amount of data to be written to the data account in this action
123
+ * @private
124
+ * @returns {Promise<{bytesWritten: number, action: SolanaAction}>} bytes written to the data account & action
125
+ */
126
+ private async WriteData(
127
+ signer: PublicKey,
128
+ accountKey: Signer,
129
+ writeData: Buffer,
130
+ offset: number,
131
+ sizeLimit: number
132
+ ): Promise<{bytesWritten: number, action: SolanaAction}> {
133
+ const writeLen = Math.min(writeData.length-offset, sizeLimit);
134
+
135
+ return {
136
+ bytesWritten: writeLen,
137
+ action: new SolanaAction(signer, this.root,
138
+ await this.swapProgram.methods
139
+ .writeData(offset, writeData.slice(offset, offset+writeLen))
140
+ .accounts({
141
+ signer,
142
+ data: accountKey.publicKey
143
+ })
144
+ .instruction(),
145
+ SolanaDataAccount.CUCosts.DATA_WRITE
146
+ )
147
+ };
148
+ }
149
+
150
+ constructor(chainInterface: SolanaChainInterface, program: SolanaSwapProgram, storage: IStorageManager<StoredDataAccount>) {
151
+ super(chainInterface, program);
152
+ this.storage = storage;
153
+ }
154
+
155
+ /**
156
+ * Saves data account to the storage, the storage is required such that we are able to close the accounts later
157
+ * manually in case the claim doesn't happen (expires due to fees, etc.)
158
+ *
159
+ * @param signer
160
+ * @param publicKey
161
+ * @private
162
+ */
163
+ private saveDataAccount(signer: PublicKey, publicKey: PublicKey): Promise<void> {
164
+ return this.storage.saveData(publicKey.toBase58(), new StoredDataAccount(publicKey, signer));
165
+ }
166
+
167
+ /**
168
+ * Initializes the data account handler, loads the existing data accounts which should be checked and closed
169
+ */
170
+ public async init() {
171
+ await this.storage.init();
172
+ const loadedData = await this.storage.loadData(StoredDataAccount);
173
+ this.logger.info("init(): initialized & loaded stored data accounts, count: "+loadedData.length);
174
+ }
175
+
176
+ /**
177
+ * Removes data account from the list of accounts that should be checked for reclaiming the locked SOL, this should
178
+ * be called after a batch of transactions claiming the swap was confirmed
179
+ *
180
+ * @param publicKey
181
+ */
182
+ public removeDataAccount(publicKey: PublicKey): Promise<void> {
183
+ return this.storage.removeData(publicKey.toBase58());
184
+ }
185
+
186
+ public async getDataAccountsInfo(signer: PublicKey): Promise<{closePublicKeys: PublicKey[], count: number, totalValue: bigint}> {
187
+ const closePublicKeys: PublicKey[] = [];
188
+ let totalLocked = 0n;
189
+ for(let key in this.storage.data) {
190
+ const {accountKey, owner} = this.storage.data[key];
191
+
192
+ if(!owner.equals(signer)) continue;
193
+
194
+ try {
195
+ const fetchedDataAccount: AccountInfo<Buffer> = await this.connection.getAccountInfo(accountKey);
196
+ if(fetchedDataAccount==null) {
197
+ await this.removeDataAccount(accountKey);
198
+ continue;
199
+ }
200
+ closePublicKeys.push(accountKey);
201
+ totalLocked += BigInt(fetchedDataAccount.lamports);
202
+ } catch (e) {}
203
+ }
204
+
205
+ return {
206
+ closePublicKeys,
207
+ count: closePublicKeys.length,
208
+ totalValue: totalLocked
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Sweeps all old data accounts, reclaiming the SOL locked in the PDAs
214
+ */
215
+ public async sweepDataAccounts(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
216
+ const {closePublicKeys, totalValue} = await this.getDataAccountsInfo(signer.getPublicKey());
217
+
218
+ if(closePublicKeys.length===0) {
219
+ this.logger.debug("sweepDataAccounts(): no old data accounts found, no need to close any!");
220
+ return;
221
+ }
222
+
223
+ this.logger.debug("sweepDataAccounts(): closing old data accounts: ", closePublicKeys);
224
+
225
+ let txns: SolanaTx[] = [];
226
+ for(let publicKey of closePublicKeys) {
227
+ await (await this.CloseDataAccount(signer.getPublicKey(), publicKey)).addToTxs(txns);
228
+ }
229
+
230
+ const result = await this.root.Transactions.sendAndConfirm(signer, txns, true, null, true);
231
+
232
+ this.logger.info("sweepDataAccounts(): old data accounts closed: "+
233
+ closePublicKeys.map(pk => pk.toBase58()).join());
234
+
235
+ for(let publicKey of closePublicKeys) {
236
+ await this.removeDataAccount(publicKey);
237
+ }
238
+
239
+ return {
240
+ txIds: result,
241
+ count: closePublicKeys.length,
242
+ totalValue: totalValue
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Adds the transactions writing (and also initializing if it doesn't exist) data to the data account
248
+ *
249
+ * @param signer
250
+ * @param reversedTxId reversed btc tx id is used to derive the data account address
251
+ * @param writeData full data to be written to the data account
252
+ * @param txs solana transactions array, where txns for writing & initializing will be added
253
+ * @param feeRate fee rate to use for the transactions
254
+ */
255
+ public async addTxsWriteData(
256
+ signer: SolanaSigner | PublicKey,
257
+ reversedTxId: Buffer,
258
+ writeData: Buffer,
259
+ txs: SolanaTx[],
260
+ feeRate: string
261
+ ): Promise<PublicKey> {
262
+ let txDataKey: Signer;
263
+ let fetchedDataAccount: AccountInfo<Buffer> = null;
264
+ if(signer instanceof SolanaSigner && signer.keypair!=null) {
265
+ txDataKey = this.SwapTxDataAlt(reversedTxId, signer.keypair);
266
+ fetchedDataAccount = await tryWithRetries<AccountInfo<Buffer>>(
267
+ () => this.connection.getAccountInfo(txDataKey.publicKey),
268
+ this.retryPolicy
269
+ );
270
+ } else {
271
+ const secret = Buffer.from(randomBytes(32));
272
+ txDataKey = this.SwapTxDataAltBuffer(reversedTxId, secret);
273
+ }
274
+
275
+ const signerKey = signer instanceof SolanaSigner ? signer.getPublicKey() : signer;
276
+
277
+ let pointer = 0;
278
+ if(fetchedDataAccount==null) {
279
+ const action = new SolanaAction(signerKey, this.root);
280
+ action.add(await this.InitDataAccount(signerKey, txDataKey, writeData.length));
281
+ const {
282
+ bytesWritten,
283
+ action: writeAction
284
+ } = await this.WriteData(signerKey, txDataKey, writeData, pointer, 420);
285
+ this.logger.debug("addTxsWriteData(): Write partial data ("+pointer+" .. "+(pointer+bytesWritten)+")/"+writeData.length+
286
+ " key: "+txDataKey.publicKey.toBase58());
287
+ pointer += bytesWritten;
288
+ action.add(writeAction);
289
+
290
+ await action.addToTxs(txs, feeRate);
291
+ await this.saveDataAccount(signerKey, txDataKey.publicKey);
292
+ }
293
+
294
+ while(pointer<writeData.length) {
295
+ const {
296
+ bytesWritten,
297
+ action
298
+ } = await this.WriteData(signerKey, txDataKey, writeData, pointer, 950);
299
+ this.logger.debug("addTxsWriteData(): Write partial data ("+pointer+" .. "+(pointer+bytesWritten)+")/"+writeData.length+
300
+ " key: "+txDataKey.publicKey.toBase58());
301
+ pointer += bytesWritten;
302
+ await action.addToTxs(txs, feeRate);
303
+ }
304
+
305
+ return txDataKey.publicKey;
306
+ }
307
+
308
308
  }