@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,404 +1,404 @@
1
- import {Connection, Keypair, PublicKey, SendOptions, Transaction} from "@solana/web3.js";
2
- import {SolanaFees} from "./modules/SolanaFees";
3
- import {SolanaBlocks} from "./modules/SolanaBlocks";
4
- import {SolanaSlots} from "./modules/SolanaSlots";
5
- import {SolanaTokens} from "./modules/SolanaTokens";
6
- import {SignedSolanaTx, SolanaTransactions, SolanaTx} from "./modules/SolanaTransactions";
7
- import {SolanaSignatures} from "./modules/SolanaSignatures";
8
- import {SolanaEvents} from "./modules/SolanaEvents";
9
- import {getLogger} from "../../utils/Utils";
10
- import {BitcoinNetwork, ChainInterface, TransactionConfirmationOptions} from "@atomiqlabs/base";
11
- import {SolanaAddresses} from "./modules/SolanaAddresses";
12
- import {SolanaSigner} from "../wallet/SolanaSigner";
13
- import {Buffer} from "buffer";
14
- import {SolanaKeypairWallet} from "../wallet/SolanaKeypairWallet";
15
- import {Wallet} from "@coral-xyz/anchor/dist/cjs/provider";
16
- import {SolanaChains} from "../SolanaChains";
17
- // @ts-ignore
18
- import * as bs58 from "bs58";
19
-
20
- const CLUSTER_BY_GENESIS_HASH: Record<string, "mainnet-beta" | "devnet" | "testnet"> = {
21
- "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d": "mainnet-beta",
22
- "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG": "devnet",
23
- "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY": "testnet",
24
- };
25
-
26
- /**
27
- * Retry policy configuration for Solana RPC calls
28
- * @category Chain Interface
29
- */
30
- export type SolanaRetryPolicy = {
31
- /**
32
- * Maximum retries to be attempted
33
- */
34
- maxRetries?: number,
35
- /**
36
- * Default delay between retries
37
- */
38
- delay?: number,
39
- /**
40
- * Whether the delays should scale exponentially, i.e. 1 second, 2 seconds, 4 seconds, 8 seconds
41
- */
42
- exponential?: boolean,
43
- /**
44
- * Interval between re-sending Solana transaction to the RPC
45
- */
46
- transactionResendInterval?: number
47
- }
48
-
49
- /**
50
- * Main chain interface for interacting with Solana blockchain
51
- * @category Chain Interface
52
- */
53
- export class SolanaChainInterface implements ChainInterface<
54
- SolanaTx,
55
- SignedSolanaTx,
56
- SolanaSigner,
57
- "SOLANA",
58
- Wallet
59
- > {
60
- /**
61
- * @inheritDoc
62
- */
63
- readonly chainId = "SOLANA";
64
-
65
- /**
66
- * Average Solana slot time in milliseconds.
67
- * @internal
68
- */
69
- readonly _SLOT_TIME = 400;
70
- /**
71
- * Approximate number of recent slots for which a transaction remains valid.
72
- * @internal
73
- */
74
- readonly _TX_SLOT_VALIDITY = 151;
75
-
76
- /**
77
- * Underlying Solana web3.js connection.
78
- * @internal
79
- */
80
- readonly _connection: Connection;
81
- /**
82
- * Retry policy used by chain modules.
83
- * @internal
84
- */
85
- readonly _retryPolicy?: SolanaRetryPolicy;
86
-
87
- /**
88
- * Block-related read module.
89
- */
90
- public readonly Blocks: SolanaBlocks;
91
- /**
92
- * Fee estimation and fee-rate module.
93
- */
94
- public Fees: SolanaFees;
95
- /**
96
- * Slot-related read module.
97
- */
98
- public readonly Slots: SolanaSlots;
99
- /**
100
- * Token operations module.
101
- */
102
- public readonly Tokens: SolanaTokens;
103
- /**
104
- * Transaction send/confirm/serialization module.
105
- */
106
- public readonly Transactions: SolanaTransactions;
107
- /**
108
- * Signature utilities module.
109
- */
110
- public readonly Signatures: SolanaSignatures;
111
- /**
112
- * Event/log scanning module.
113
- */
114
- public readonly Events: SolanaEvents;
115
-
116
- /**
117
- * @internal
118
- */
119
- protected readonly logger = getLogger(this.constructor.name+": ");
120
-
121
- constructor(
122
- connection: Connection,
123
- retryPolicy?: SolanaRetryPolicy,
124
- solanaFeeEstimator: SolanaFees = new SolanaFees(connection)
125
- ) {
126
- this._connection = connection;
127
- this._retryPolicy = retryPolicy;
128
-
129
- this.Blocks = new SolanaBlocks(this);
130
- this.Fees = solanaFeeEstimator;
131
- this.Slots = new SolanaSlots(this);
132
- this.Tokens = new SolanaTokens(this);
133
- this.Transactions = new SolanaTransactions(this);
134
- this.Signatures = new SolanaSignatures(this);
135
- this.Events = new SolanaEvents(this);
136
- }
137
-
138
- /**
139
- * @inheritDoc
140
- */
141
- async getBalance(signer: string, tokenAddress: string): Promise<bigint> {
142
- const token = new PublicKey(tokenAddress);
143
- const publicKey = new PublicKey(signer);
144
-
145
- let { balance } = await this.Tokens.getTokenBalance(publicKey, token);
146
- if(token.equals(SolanaTokens.WSOL_ADDRESS)) {
147
- const accountRentExemptCost = 1000000n;
148
- balance = balance - accountRentExemptCost;
149
- if(balance < 0n) balance = 0n;
150
- }
151
- this.logger.debug("getBalance(): token balance, token: "+token.toBase58()+" balance: "+balance.toString(10));
152
- return balance;
153
- }
154
-
155
- /**
156
- * @inheritDoc
157
- */
158
- isValidAddress(address: string): boolean {
159
- return SolanaAddresses.isValidAddress(address);
160
- }
161
-
162
- /**
163
- * @inheritDoc
164
- */
165
- normalizeAddress(address: string): string {
166
- return address;
167
- }
168
-
169
- /**
170
- * @inheritDoc
171
- */
172
- getNativeCurrencyAddress(): string {
173
- return this.Tokens.getNativeCurrencyAddress().toString();
174
- }
175
-
176
- /**
177
- * @inheritDoc
178
- */
179
- shouldGetNativeTokenDrop(tokenAddress: string): boolean {
180
- // True for all the tokens, because the contracts don't support native SOL handling yet
181
- return true;
182
- }
183
-
184
- /**
185
- * @inheritDoc
186
- */
187
- txsTransfer(signer: string, token: string, amount: bigint, dstAddress: string, feeRate?: string): Promise<SolanaTx[]> {
188
- return this.Tokens.txsTransfer(new PublicKey(signer), new PublicKey(token), amount, new PublicKey(dstAddress), feeRate);
189
- }
190
-
191
- /**
192
- * @inheritDoc
193
- */
194
- async transfer(
195
- signer: SolanaSigner,
196
- token: string,
197
- amount: bigint,
198
- dstAddress: string,
199
- txOptions?: TransactionConfirmationOptions
200
- ): Promise<string> {
201
- const txs = await this.Tokens.txsTransfer(signer.getPublicKey(), new PublicKey(token), amount, new PublicKey(dstAddress), txOptions?.feeRate);
202
- const [txId] = await this.Transactions.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
203
- return txId;
204
- }
205
-
206
-
207
- ////////////////////////////////////////////
208
- //// Transactions
209
- /**
210
- * @inheritDoc
211
- */
212
- sendAndConfirm(
213
- signer: SolanaSigner,
214
- txs: SolanaTx[],
215
- waitForConfirmation?: boolean,
216
- abortSignal?: AbortSignal,
217
- parallel?: boolean,
218
- onBeforePublish?: (txId: string, rawTx: string) => Promise<void>
219
- ): Promise<string[]> {
220
- return this.Transactions.sendAndConfirm(signer, txs, waitForConfirmation, abortSignal, parallel, onBeforePublish);
221
- }
222
-
223
- /**
224
- * @inheritDoc
225
- */
226
- sendSignedAndConfirm(
227
- txs: SignedSolanaTx[],
228
- waitForConfirmation?: boolean,
229
- abortSignal?: AbortSignal,
230
- parallel?: boolean,
231
- onBeforePublish?: (txId: string, rawTx: string) => Promise<void>
232
- ): Promise<string[]> {
233
- return this.Transactions.sendSignedAndConfirm(txs, waitForConfirmation, abortSignal, parallel, onBeforePublish);
234
- }
235
-
236
- /**
237
- * @inheritDoc
238
- */
239
- async prepareTxs(txs: SolanaTx[]): Promise<SolanaTx[]> {
240
- await this.Transactions.prepareTransactions(txs);
241
- return txs;
242
- }
243
-
244
- /**
245
- * @inheritDoc
246
- */
247
- serializeTx(tx: SolanaTx): Promise<string> {
248
- return Promise.resolve(this.Transactions.serializeUnsignedTx(tx));
249
- }
250
-
251
- /**
252
- * @inheritDoc
253
- */
254
- deserializeTx(txData: string): Promise<SolanaTx> {
255
- return Promise.resolve(this.Transactions.deserializeUnsignedTx(txData));
256
- }
257
-
258
- /**
259
- * @inheritDoc
260
- */
261
- serializeSignedTx(tx: Transaction): Promise<string> {
262
- return Promise.resolve(this.Transactions.serializeSignedTx(tx));
263
- }
264
-
265
- /**
266
- * @inheritDoc
267
- */
268
- deserializeSignedTx(txData: string): Promise<Transaction> {
269
- return Promise.resolve(this.Transactions.deserializeSignedTransaction(txData));
270
- }
271
-
272
- /**
273
- * @inheritDoc
274
- */
275
- getTxId(signedTX: SignedSolanaTx): Promise<string> {
276
- return Promise.resolve(bs58.encode(signedTX.signature));
277
- }
278
-
279
- /**
280
- * @inheritDoc
281
- */
282
- getTxIdStatus(txId: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
283
- return this.Transactions.getTxIdStatus(txId);
284
- }
285
-
286
- /**
287
- * @inheritDoc
288
- */
289
- getTxStatus(tx: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
290
- return this.Transactions.getTxStatus(tx);
291
- }
292
-
293
- /**
294
- * @inheritDoc
295
- */
296
- async getFinalizedBlock(): Promise<{ height: number; blockHash: string }> {
297
- const {block} = await this.Blocks.findLatestParsedBlock("finalized");
298
- return {
299
- height: block.blockHeight,
300
- blockHash: block.blockhash
301
- };
302
- }
303
-
304
-
305
- ///////////////////////////////////
306
- //// Callbacks & handlers
307
- /**
308
- * @inheritDoc
309
- */
310
- offBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): boolean {
311
- return true;
312
- }
313
-
314
- /**
315
- * @inheritDoc
316
- */
317
- onBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): void {}
318
-
319
- /**
320
- * @inheritDoc
321
- */
322
- onBeforeTxSigned(callback: (tx: SolanaTx) => Promise<void>): void {
323
- this.Transactions.onBeforeTxSigned(callback);
324
- }
325
-
326
- /**
327
- * @inheritDoc
328
- */
329
- offBeforeTxSigned(callback: (tx: SolanaTx) => Promise<void>): boolean {
330
- return this.Transactions.offBeforeTxSigned(callback);
331
- }
332
-
333
- /**
334
- * Registers a low-level transaction sender override hook.
335
- *
336
- * @param callback Callback used for raw transaction publishing
337
- */
338
- onSendTransaction(callback: (tx: Buffer, options?: SendOptions) => Promise<string>): void {
339
- this.Transactions.onSendTransaction(callback);
340
- }
341
-
342
- /**
343
- * Unregisters a previously registered transaction sender override hook.
344
- *
345
- * @param callback Previously registered callback
346
- */
347
- offSendTransaction(callback: (tx: Buffer, options?: SendOptions) => Promise<string>): boolean {
348
- return this.Transactions.offSendTransaction(callback);
349
- }
350
-
351
- /**
352
- * @inheritDoc
353
- */
354
- isValidToken(tokenIdentifier: string): boolean {
355
- try {
356
- new PublicKey(tokenIdentifier);
357
- return true;
358
- } catch (e) {
359
- return false;
360
- }
361
- }
362
-
363
- /**
364
- * @inheritDoc
365
- */
366
- randomAddress(): string {
367
- return Keypair.generate().publicKey.toString();
368
- }
369
-
370
- /**
371
- * @inheritDoc
372
- */
373
- randomSigner(): SolanaSigner {
374
- const keypair = Keypair.generate();
375
- const wallet = new SolanaKeypairWallet(keypair);
376
- return new SolanaSigner(wallet, keypair);
377
- }
378
-
379
- /**
380
- * @inheritDoc
381
- */
382
- wrapSigner(signer: Wallet): Promise<SolanaSigner> {
383
- return Promise.resolve(new SolanaSigner(signer));
384
- }
385
-
386
- async verifyNetwork(bitcoinNetwork: BitcoinNetwork): Promise<void> {
387
- const genesisHash = await this._connection.getGenesisHash();
388
- const result = CLUSTER_BY_GENESIS_HASH[genesisHash];
389
- if(result==null) {
390
- this.logger.warn(`verifyNetwork(): Unknown cluster detected, genesis hash: ${genesisHash}`);
391
- return;
392
- }
393
-
394
- const deployment = SolanaChains[bitcoinNetwork];
395
- if(deployment==null) {
396
- this.logger.warn(`verifyNetwork(): No Solana deployment is defined for ${BitcoinNetwork[bitcoinNetwork]}, the RPC check is skipped.`);
397
- return;
398
- }
399
-
400
- if(deployment.clusterName!==result)
401
- throw new Error(`Expected ${deployment.clusterName} Solana cluster for ${BitcoinNetwork[bitcoinNetwork]}, but got ${result}!`);
402
- }
403
-
404
- }
1
+ import {Connection, Keypair, PublicKey, SendOptions, Transaction} from "@solana/web3.js";
2
+ import {SolanaFees} from "./modules/SolanaFees";
3
+ import {SolanaBlocks} from "./modules/SolanaBlocks";
4
+ import {SolanaSlots} from "./modules/SolanaSlots";
5
+ import {SolanaTokens} from "./modules/SolanaTokens";
6
+ import {SignedSolanaTx, SolanaTransactions, SolanaTx} from "./modules/SolanaTransactions";
7
+ import {SolanaSignatures} from "./modules/SolanaSignatures";
8
+ import {SolanaEvents} from "./modules/SolanaEvents";
9
+ import {getLogger} from "../../utils/Utils";
10
+ import {BitcoinNetwork, ChainInterface, TransactionConfirmationOptions} from "@atomiqlabs/base";
11
+ import {SolanaAddresses} from "./modules/SolanaAddresses";
12
+ import {SolanaSigner} from "../wallet/SolanaSigner";
13
+ import {Buffer} from "buffer";
14
+ import {SolanaKeypairWallet} from "../wallet/SolanaKeypairWallet";
15
+ import {Wallet} from "@coral-xyz/anchor/dist/cjs/provider";
16
+ import {SolanaChains} from "../SolanaChains";
17
+ // @ts-ignore
18
+ import * as bs58 from "bs58";
19
+
20
+ const CLUSTER_BY_GENESIS_HASH: Record<string, "mainnet-beta" | "devnet" | "testnet"> = {
21
+ "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d": "mainnet-beta",
22
+ "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG": "devnet",
23
+ "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY": "testnet",
24
+ };
25
+
26
+ /**
27
+ * Retry policy configuration for Solana RPC calls
28
+ * @category Chain Interface
29
+ */
30
+ export type SolanaRetryPolicy = {
31
+ /**
32
+ * Maximum retries to be attempted
33
+ */
34
+ maxRetries?: number,
35
+ /**
36
+ * Default delay between retries
37
+ */
38
+ delay?: number,
39
+ /**
40
+ * Whether the delays should scale exponentially, i.e. 1 second, 2 seconds, 4 seconds, 8 seconds
41
+ */
42
+ exponential?: boolean,
43
+ /**
44
+ * Interval between re-sending Solana transaction to the RPC
45
+ */
46
+ transactionResendInterval?: number
47
+ }
48
+
49
+ /**
50
+ * Main chain interface for interacting with Solana blockchain
51
+ * @category Chain Interface
52
+ */
53
+ export class SolanaChainInterface implements ChainInterface<
54
+ SolanaTx,
55
+ SignedSolanaTx,
56
+ SolanaSigner,
57
+ "SOLANA",
58
+ Wallet
59
+ > {
60
+ /**
61
+ * @inheritDoc
62
+ */
63
+ readonly chainId = "SOLANA";
64
+
65
+ /**
66
+ * Average Solana slot time in milliseconds.
67
+ * @internal
68
+ */
69
+ readonly _SLOT_TIME = 400;
70
+ /**
71
+ * Approximate number of recent slots for which a transaction remains valid.
72
+ * @internal
73
+ */
74
+ readonly _TX_SLOT_VALIDITY = 151;
75
+
76
+ /**
77
+ * Underlying Solana web3.js connection.
78
+ * @internal
79
+ */
80
+ readonly _connection: Connection;
81
+ /**
82
+ * Retry policy used by chain modules.
83
+ * @internal
84
+ */
85
+ readonly _retryPolicy?: SolanaRetryPolicy;
86
+
87
+ /**
88
+ * Block-related read module.
89
+ */
90
+ public readonly Blocks: SolanaBlocks;
91
+ /**
92
+ * Fee estimation and fee-rate module.
93
+ */
94
+ public Fees: SolanaFees;
95
+ /**
96
+ * Slot-related read module.
97
+ */
98
+ public readonly Slots: SolanaSlots;
99
+ /**
100
+ * Token operations module.
101
+ */
102
+ public readonly Tokens: SolanaTokens;
103
+ /**
104
+ * Transaction send/confirm/serialization module.
105
+ */
106
+ public readonly Transactions: SolanaTransactions;
107
+ /**
108
+ * Signature utilities module.
109
+ */
110
+ public readonly Signatures: SolanaSignatures;
111
+ /**
112
+ * Event/log scanning module.
113
+ */
114
+ public readonly Events: SolanaEvents;
115
+
116
+ /**
117
+ * @internal
118
+ */
119
+ protected readonly logger = getLogger(this.constructor.name+": ");
120
+
121
+ constructor(
122
+ connection: Connection,
123
+ retryPolicy?: SolanaRetryPolicy,
124
+ solanaFeeEstimator: SolanaFees = new SolanaFees(connection)
125
+ ) {
126
+ this._connection = connection;
127
+ this._retryPolicy = retryPolicy;
128
+
129
+ this.Blocks = new SolanaBlocks(this);
130
+ this.Fees = solanaFeeEstimator;
131
+ this.Slots = new SolanaSlots(this);
132
+ this.Tokens = new SolanaTokens(this);
133
+ this.Transactions = new SolanaTransactions(this);
134
+ this.Signatures = new SolanaSignatures(this);
135
+ this.Events = new SolanaEvents(this);
136
+ }
137
+
138
+ /**
139
+ * @inheritDoc
140
+ */
141
+ async getBalance(signer: string, tokenAddress: string): Promise<bigint> {
142
+ const token = new PublicKey(tokenAddress);
143
+ const publicKey = new PublicKey(signer);
144
+
145
+ let { balance } = await this.Tokens.getTokenBalance(publicKey, token);
146
+ if(token.equals(SolanaTokens.WSOL_ADDRESS)) {
147
+ const accountRentExemptCost = 1000000n;
148
+ balance = balance - accountRentExemptCost;
149
+ if(balance < 0n) balance = 0n;
150
+ }
151
+ this.logger.debug("getBalance(): token balance, token: "+token.toBase58()+" balance: "+balance.toString(10));
152
+ return balance;
153
+ }
154
+
155
+ /**
156
+ * @inheritDoc
157
+ */
158
+ isValidAddress(address: string): boolean {
159
+ return SolanaAddresses.isValidAddress(address);
160
+ }
161
+
162
+ /**
163
+ * @inheritDoc
164
+ */
165
+ normalizeAddress(address: string): string {
166
+ return address;
167
+ }
168
+
169
+ /**
170
+ * @inheritDoc
171
+ */
172
+ getNativeCurrencyAddress(): string {
173
+ return this.Tokens.getNativeCurrencyAddress().toString();
174
+ }
175
+
176
+ /**
177
+ * @inheritDoc
178
+ */
179
+ shouldGetNativeTokenDrop(tokenAddress: string): boolean {
180
+ // True for all the tokens, because the contracts don't support native SOL handling yet
181
+ return true;
182
+ }
183
+
184
+ /**
185
+ * @inheritDoc
186
+ */
187
+ txsTransfer(signer: string, token: string, amount: bigint, dstAddress: string, feeRate?: string): Promise<SolanaTx[]> {
188
+ return this.Tokens.txsTransfer(new PublicKey(signer), new PublicKey(token), amount, new PublicKey(dstAddress), feeRate);
189
+ }
190
+
191
+ /**
192
+ * @inheritDoc
193
+ */
194
+ async transfer(
195
+ signer: SolanaSigner,
196
+ token: string,
197
+ amount: bigint,
198
+ dstAddress: string,
199
+ txOptions?: TransactionConfirmationOptions
200
+ ): Promise<string> {
201
+ const txs = await this.Tokens.txsTransfer(signer.getPublicKey(), new PublicKey(token), amount, new PublicKey(dstAddress), txOptions?.feeRate);
202
+ const [txId] = await this.Transactions.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
203
+ return txId;
204
+ }
205
+
206
+
207
+ ////////////////////////////////////////////
208
+ //// Transactions
209
+ /**
210
+ * @inheritDoc
211
+ */
212
+ sendAndConfirm(
213
+ signer: SolanaSigner,
214
+ txs: SolanaTx[],
215
+ waitForConfirmation?: boolean,
216
+ abortSignal?: AbortSignal,
217
+ parallel?: boolean,
218
+ onBeforePublish?: (txId: string, rawTx: string) => Promise<void>
219
+ ): Promise<string[]> {
220
+ return this.Transactions.sendAndConfirm(signer, txs, waitForConfirmation, abortSignal, parallel, onBeforePublish);
221
+ }
222
+
223
+ /**
224
+ * @inheritDoc
225
+ */
226
+ sendSignedAndConfirm(
227
+ txs: SignedSolanaTx[],
228
+ waitForConfirmation?: boolean,
229
+ abortSignal?: AbortSignal,
230
+ parallel?: boolean,
231
+ onBeforePublish?: (txId: string, rawTx: string) => Promise<void>
232
+ ): Promise<string[]> {
233
+ return this.Transactions.sendSignedAndConfirm(txs, waitForConfirmation, abortSignal, parallel, onBeforePublish);
234
+ }
235
+
236
+ /**
237
+ * @inheritDoc
238
+ */
239
+ async prepareTxs(txs: SolanaTx[]): Promise<SolanaTx[]> {
240
+ await this.Transactions.prepareTransactions(txs);
241
+ return txs;
242
+ }
243
+
244
+ /**
245
+ * @inheritDoc
246
+ */
247
+ serializeTx(tx: SolanaTx): Promise<string> {
248
+ return Promise.resolve(this.Transactions.serializeUnsignedTx(tx));
249
+ }
250
+
251
+ /**
252
+ * @inheritDoc
253
+ */
254
+ deserializeTx(txData: string): Promise<SolanaTx> {
255
+ return Promise.resolve(this.Transactions.deserializeUnsignedTx(txData));
256
+ }
257
+
258
+ /**
259
+ * @inheritDoc
260
+ */
261
+ serializeSignedTx(tx: Transaction): Promise<string> {
262
+ return Promise.resolve(this.Transactions.serializeSignedTx(tx));
263
+ }
264
+
265
+ /**
266
+ * @inheritDoc
267
+ */
268
+ deserializeSignedTx(txData: string): Promise<Transaction> {
269
+ return Promise.resolve(this.Transactions.deserializeSignedTransaction(txData));
270
+ }
271
+
272
+ /**
273
+ * @inheritDoc
274
+ */
275
+ getTxId(signedTX: SignedSolanaTx): Promise<string> {
276
+ return Promise.resolve(bs58.encode(signedTX.signature));
277
+ }
278
+
279
+ /**
280
+ * @inheritDoc
281
+ */
282
+ getTxIdStatus(txId: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
283
+ return this.Transactions.getTxIdStatus(txId);
284
+ }
285
+
286
+ /**
287
+ * @inheritDoc
288
+ */
289
+ getTxStatus(tx: string): Promise<"not_found" | "pending" | "success" | "reverted"> {
290
+ return this.Transactions.getTxStatus(tx);
291
+ }
292
+
293
+ /**
294
+ * @inheritDoc
295
+ */
296
+ async getFinalizedBlock(): Promise<{ height: number; blockHash: string }> {
297
+ const {block} = await this.Blocks.findLatestParsedBlock("finalized");
298
+ return {
299
+ height: block.blockHeight,
300
+ blockHash: block.blockhash
301
+ };
302
+ }
303
+
304
+
305
+ ///////////////////////////////////
306
+ //// Callbacks & handlers
307
+ /**
308
+ * @inheritDoc
309
+ */
310
+ offBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): boolean {
311
+ return true;
312
+ }
313
+
314
+ /**
315
+ * @inheritDoc
316
+ */
317
+ onBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): void {}
318
+
319
+ /**
320
+ * @inheritDoc
321
+ */
322
+ onBeforeTxSigned(callback: (tx: SolanaTx) => Promise<void>): void {
323
+ this.Transactions.onBeforeTxSigned(callback);
324
+ }
325
+
326
+ /**
327
+ * @inheritDoc
328
+ */
329
+ offBeforeTxSigned(callback: (tx: SolanaTx) => Promise<void>): boolean {
330
+ return this.Transactions.offBeforeTxSigned(callback);
331
+ }
332
+
333
+ /**
334
+ * Registers a low-level transaction sender override hook.
335
+ *
336
+ * @param callback Callback used for raw transaction publishing
337
+ */
338
+ onSendTransaction(callback: (tx: Buffer, options?: SendOptions) => Promise<string>): void {
339
+ this.Transactions.onSendTransaction(callback);
340
+ }
341
+
342
+ /**
343
+ * Unregisters a previously registered transaction sender override hook.
344
+ *
345
+ * @param callback Previously registered callback
346
+ */
347
+ offSendTransaction(callback: (tx: Buffer, options?: SendOptions) => Promise<string>): boolean {
348
+ return this.Transactions.offSendTransaction(callback);
349
+ }
350
+
351
+ /**
352
+ * @inheritDoc
353
+ */
354
+ isValidToken(tokenIdentifier: string): boolean {
355
+ try {
356
+ new PublicKey(tokenIdentifier);
357
+ return true;
358
+ } catch (e) {
359
+ return false;
360
+ }
361
+ }
362
+
363
+ /**
364
+ * @inheritDoc
365
+ */
366
+ randomAddress(): string {
367
+ return Keypair.generate().publicKey.toString();
368
+ }
369
+
370
+ /**
371
+ * @inheritDoc
372
+ */
373
+ randomSigner(): SolanaSigner {
374
+ const keypair = Keypair.generate();
375
+ const wallet = new SolanaKeypairWallet(keypair);
376
+ return new SolanaSigner(wallet, keypair);
377
+ }
378
+
379
+ /**
380
+ * @inheritDoc
381
+ */
382
+ wrapSigner(signer: Wallet): Promise<SolanaSigner> {
383
+ return Promise.resolve(new SolanaSigner(signer));
384
+ }
385
+
386
+ async verifyNetwork(bitcoinNetwork: BitcoinNetwork): Promise<void> {
387
+ const genesisHash = await this._connection.getGenesisHash();
388
+ const result = CLUSTER_BY_GENESIS_HASH[genesisHash];
389
+ if(result==null) {
390
+ this.logger.warn(`verifyNetwork(): Unknown cluster detected, genesis hash: ${genesisHash}`);
391
+ return;
392
+ }
393
+
394
+ const deployment = SolanaChains[bitcoinNetwork];
395
+ if(deployment==null) {
396
+ this.logger.warn(`verifyNetwork(): No Solana deployment is defined for ${BitcoinNetwork[bitcoinNetwork]}, the RPC check is skipped.`);
397
+ return;
398
+ }
399
+
400
+ if(deployment.clusterName!==result)
401
+ throw new Error(`Expected ${deployment.clusterName} Solana cluster for ${BitcoinNetwork[bitcoinNetwork]}, but got ${result}!`);
402
+ }
403
+
404
+ }