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