@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,1074 +1,1074 @@
1
- import {SolanaSwapData, InitInstruction} from "./SolanaSwapData";
2
- import {IdlAccounts, Program} from "@coral-xyz/anchor";
3
- import {
4
- ParsedTransactionWithMeta,
5
- PublicKey,
6
- } from "@solana/web3.js";
7
- import {sha256} from "@noble/hashes/sha2";
8
- import {SolanaBtcRelay} from "../btcrelay/SolanaBtcRelay";
9
- import * as programIdlV1 from "./v1/programIdl.json";
10
- import * as programIdlV2 from "./v2/programIdl.json";
11
- import {
12
- IStorageManager,
13
- SwapContract,
14
- ChainSwapType,
15
- IntermediaryReputationType,
16
- TransactionConfirmationOptions,
17
- SignatureData,
18
- RelaySynchronizer,
19
- BigIntBufferUtils,
20
- SwapCommitState,
21
- SwapCommitStateType, SwapNotCommitedState, SwapExpiredState, SwapPaidState, BitcoinNetwork
22
- } from "@atomiqlabs/base";
23
- import {SolanaBtcStoredHeader} from "../btcrelay/headers/SolanaBtcStoredHeader";
24
- import {
25
- getAssociatedTokenAddressSync,
26
- } from "@solana/spl-token";
27
- import {SwapProgram} from "./v1/programTypes";
28
- import {SolanaChainInterface} from "../chain/SolanaChainInterface";
29
- import {SolanaProgramBase} from "../program/SolanaProgramBase";
30
- import {SolanaTx} from "../chain/modules/SolanaTransactions";
31
- import {SwapInit, SolanaPreFetchData, SolanaPreFetchVerification} from "./modules/SwapInit";
32
- import {SolanaDataAccount, StoredDataAccount} from "./modules/SolanaDataAccount";
33
- import {SwapRefund} from "./modules/SwapRefund";
34
- import {SwapClaim} from "./modules/SwapClaim";
35
- import {SolanaLpVault} from "./modules/SolanaLpVault";
36
- import {Buffer} from "buffer";
37
- import {SolanaSigner} from "../wallet/SolanaSigner";
38
- import {fromClaimHash, onceAsync, toBN, toClaimHash, toEscrowHash} from "../../utils/Utils";
39
- import {SolanaTokens} from "../chain/modules/SolanaTokens";
40
- import * as BN from "bn.js";
41
- import {ProgramEvent} from "../program/modules/SolanaProgramEvents";
42
- import {SwapProgramV2} from "./v2/programTypes";
43
- import {SolanaChains} from "../SolanaChains";
44
-
45
- export function isSwapProgramV1(obj: any): obj is Program<SwapProgram> {
46
- return obj.idl.version==="0.1.0";
47
- }
48
-
49
- export function isSwapProgramV2(obj: any): obj is Program<SwapProgramV2> {
50
- return obj.idl.version==="0.2.0";
51
- }
52
-
53
- function toPublicKeyOrNull(str: string | null | undefined): PublicKey | undefined {
54
- return str==null ? undefined : new PublicKey(str);
55
- }
56
-
57
- const MAX_PARALLEL_COMMIT_STATUS_CHECKS = 5;
58
-
59
- /**
60
- * Solana swap (escrow manager) program representation handling PrTLC (on-chain) and HTLC (lightning) based swaps.
61
- *
62
- * @category Swaps
63
- */
64
- export class SolanaSwapProgram<Version extends "v1" | "v2" = "v1" | "v2">
65
- extends SolanaProgramBase<SwapProgram | SwapProgramV2>
66
- implements SwapContract<
67
- SolanaSwapData,
68
- SolanaTx,
69
- SolanaPreFetchData,
70
- SolanaPreFetchVerification,
71
- SolanaSigner,
72
- "SOLANA"
73
- > {
74
-
75
- ////////////////////////
76
- //// Constants
77
- /**
78
- * Rent-exempt amount (lamports) for escrow state accounts.
79
- */
80
- public readonly ESCROW_STATE_RENT_EXEMPT: number;
81
-
82
- public readonly version: Version;
83
-
84
- public readonly supportsInitWithoutClaimer: Version extends "v1" ? false : true;
85
-
86
- ////////////////////////
87
- //// PDA accessors
88
- /**
89
- * PDA of the swap vault authority.
90
- * @internal
91
- */
92
- static readonly _SwapVaultAuthority = SolanaProgramBase._pda("authority"); // Only necessary for V1 program
93
- readonly _SwapVaultAuthority = SolanaSwapProgram._SwapVaultAuthority(this.program.programId); // Only necessary for V1 program
94
- /**
95
- * PDA helper for global token vault accounts.
96
- * @internal
97
- */
98
- static readonly _SwapVault = SolanaProgramBase._pda("vault", (tokenAddress: PublicKey) => [tokenAddress.toBuffer()]);
99
- readonly _SwapVault = SolanaSwapProgram._SwapVault.bind(this, this.program.programId);
100
- /**
101
- * PDA helper for per-user token vault accounts.
102
- * @internal
103
- */
104
- static readonly _SwapUserVault = SolanaProgramBase._pda("uservault",
105
- (publicKey: PublicKey, tokenAddress: PublicKey) => [publicKey.toBuffer(), tokenAddress.toBuffer()]
106
- );
107
- readonly _SwapUserVault = SolanaSwapProgram._SwapUserVault.bind(this, this.program.programId);
108
- /**
109
- * PDA helper for escrow state accounts.
110
- * @internal
111
- */
112
- static readonly _SwapEscrowState = SolanaProgramBase._pda("state", (hash: Buffer) => [hash]);
113
- readonly _SwapEscrowState = SolanaSwapProgram._SwapEscrowState.bind(this, this.program.programId);
114
-
115
- ////////////////////////
116
- //// Timeouts
117
- /**
118
- * @inheritDoc
119
- */
120
- readonly chainId: "SOLANA" = "SOLANA";
121
- /**
122
- * @inheritDoc
123
- */
124
- readonly claimWithSecretTimeout: number = 45;
125
- /**
126
- * @inheritDoc
127
- */
128
- readonly claimWithTxDataTimeout: number = 120;
129
- /**
130
- * @inheritDoc
131
- */
132
- readonly refundTimeout: number = 45;
133
- /**
134
- * Grace period (seconds) applied to claimer-side expiry checks.
135
- */
136
- private readonly claimGracePeriod: number = 10*60;
137
- /**
138
- * Grace period (seconds) applied to offerer-side expiry checks.
139
- */
140
- private readonly refundGracePeriod: number = 10*60;
141
- /**
142
- * Authorization grace period in seconds.
143
- * @internal
144
- */
145
- readonly _authGracePeriod: number = 30;
146
-
147
- ////////////////////////
148
- //// Services
149
- /**
150
- * Swap initialization service.
151
- */
152
- private readonly Init: SwapInit;
153
- /**
154
- * Swap refund service.
155
- */
156
- private readonly Refund: SwapRefund;
157
- /**
158
- * Swap claim service.
159
- */
160
- private readonly Claim: SwapClaim;
161
- /**
162
- * LP vault interaction service.
163
- */
164
- private readonly LpVault: SolanaLpVault;
165
- /**
166
- * Temporary data-account lifecycle service.
167
- * @internal
168
- */
169
- readonly _DataAccount: SolanaDataAccount;
170
-
171
- constructor(
172
- chainInterface: SolanaChainInterface,
173
- btcRelay: SolanaBtcRelay<any>,
174
- storage: IStorageManager<StoredDataAccount>,
175
- programAddress?: string,
176
- bitcoinNetwork?: BitcoinNetwork,
177
- version?: Version
178
- ) {
179
- version ??= "v1" as any;
180
- if(bitcoinNetwork!=null && programAddress==null) {
181
- programAddress = SolanaChains[bitcoinNetwork]?.addresses[version ?? "v1"]?.swapContract;
182
- }
183
- super(chainInterface, version==="v1" ? programIdlV1 : programIdlV2, programAddress);
184
-
185
- this.version = version!;
186
- this.ESCROW_STATE_RENT_EXEMPT = this.version==="v1" ? 2658720 : 2665680;
187
- this.supportsInitWithoutClaimer = (this.version!=="v1") as any;
188
-
189
- this.Init = new SwapInit(chainInterface, this);
190
- this.Refund = new SwapRefund(chainInterface, this);
191
- this.Claim = new SwapClaim(chainInterface, this, btcRelay);
192
- this._DataAccount = new SolanaDataAccount(chainInterface, this, storage);
193
- this.LpVault = new SolanaLpVault(chainInterface, this);
194
- }
195
-
196
- /**
197
- * @inheritDoc
198
- */
199
- async start(): Promise<void> {
200
- await this._DataAccount.init();
201
- }
202
-
203
- /**
204
- * @inheritDoc
205
- */
206
- getClaimableDeposits(signer: string): Promise<{count: number, totalValue: bigint}> {
207
- return this._DataAccount.getDataAccountsInfo(new PublicKey(signer));
208
- }
209
-
210
- /**
211
- * @inheritDoc
212
- */
213
- claimDeposits(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
214
- return this._DataAccount.sweepDataAccounts(signer);
215
- }
216
-
217
- ////////////////////////////////////////////
218
- //// Signatures
219
- /**
220
- * @inheritDoc
221
- */
222
- preFetchForInitSignatureVerification(data: SolanaPreFetchData): Promise<SolanaPreFetchVerification> {
223
- return this.Init.preFetchForInitSignatureVerification(data);
224
- }
225
-
226
- /**
227
- * @inheritDoc
228
- */
229
- preFetchBlockDataForSignatures(): Promise<SolanaPreFetchData> {
230
- return this.Init.preFetchBlockDataForSignatures();
231
- }
232
-
233
- /**
234
- * @inheritDoc
235
- */
236
- getInitSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number, preFetchedBlockData?: SolanaPreFetchData, feeRate?: string): Promise<SignatureData> {
237
- return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
238
- }
239
-
240
- /**
241
- * @inheritDoc
242
- */
243
- isValidInitAuthorization(signer: string, swapData: SolanaSwapData, sig: SignatureData, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
244
- return this.Init.isSignatureValid(new PublicKey(signer), swapData, sig.timeout, sig.prefix, sig.signature, feeRate, preFetchedData);
245
- }
246
-
247
- /**
248
- * @inheritDoc
249
- */
250
- getInitAuthorizationExpiry(swapData: SolanaSwapData, sig: SignatureData, preFetchedData?: SolanaPreFetchVerification): Promise<number> {
251
- return this.Init.getSignatureExpiry(sig.timeout, sig.signature, preFetchedData);
252
- }
253
-
254
- /**
255
- * @inheritDoc
256
- */
257
- isInitAuthorizationExpired(swapData: SolanaSwapData, sig: SignatureData): Promise<boolean> {
258
- return this.Init.isSignatureExpired(sig.signature, sig.timeout);
259
- }
260
-
261
- /**
262
- * @inheritDoc
263
- */
264
- getRefundSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number): Promise<SignatureData> {
265
- return this.Refund.signSwapRefund(signer, swapData, authorizationTimeout);
266
- }
267
-
268
- /**
269
- * @inheritDoc
270
- */
271
- isValidRefundAuthorization(swapData: SolanaSwapData, sig: SignatureData): Promise<Buffer> {
272
- return this.Refund.isSignatureValid(swapData, sig.timeout, sig.prefix, sig.signature);
273
- }
274
-
275
- /**
276
- * @inheritDoc
277
- */
278
- getDataSignature(signer: SolanaSigner, data: Buffer): Promise<string> {
279
- return this._Chain.Signatures.getDataSignature(signer, data);
280
- }
281
-
282
- /**
283
- * @inheritDoc
284
- */
285
- isValidDataSignature(data: Buffer, signature: string, publicKey: string): Promise<boolean> {
286
- return this._Chain.Signatures.isValidDataSignature(data, signature, publicKey);
287
- }
288
-
289
- ////////////////////////////////////////////
290
- //// Swap data utils
291
- /**
292
- * @inheritDoc
293
- */
294
- async isClaimable(signer: string, data: SolanaSwapData): Promise<boolean> {
295
- if(!data.isClaimer(signer)) return false;
296
- if(await this.isExpired(signer, data)) return false;
297
- return await this.isCommited(data);
298
- }
299
-
300
- /**
301
- * @inheritDoc
302
- */
303
- async isCommited(swapData: SolanaSwapData): Promise<boolean> {
304
- const paymentHash = Buffer.from(swapData.paymentHash, "hex");
305
-
306
- const account = await this.program.account.escrowState.fetchNullable(this._SwapEscrowState(paymentHash));
307
- if(account==null) return false;
308
-
309
- return swapData.correctPDA(account);
310
- }
311
-
312
- /**
313
- * @inheritDoc
314
- */
315
- isExpired(signer: string, data: SolanaSwapData, refundSide?: boolean): Promise<boolean> {
316
- let currentTimestamp: BN = new BN(Math.floor(Date.now()/1000));
317
- if(data.isClaimer(signer) && !refundSide) {
318
- currentTimestamp = currentTimestamp.add(new BN(this.claimGracePeriod));
319
- } else {
320
- currentTimestamp = currentTimestamp.sub(new BN(this.refundGracePeriod));
321
- }
322
- return Promise.resolve(data.expiry.lt(currentTimestamp));
323
- }
324
-
325
- /**
326
- * @inheritDoc
327
- */
328
- async isRequestRefundable(signer: string, data: SolanaSwapData): Promise<boolean> {
329
- //V1 Swap can only be refunded by the offerer
330
- if(isSwapProgramV1(this.program) && !data.isOfferer(signer)) return false;
331
- if(!(await this.isExpired(signer, data, true))) return false;
332
- return await this.isCommited(data);
333
- }
334
-
335
- /**
336
- * @inheritDoc
337
- */
338
- getHashForOnchain(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
339
- nonce ??= 0n;
340
- const paymentHash = Buffer.from(sha256(Buffer.concat([
341
- BigIntBufferUtils.toBuffer(nonce, "le", 8),
342
- BigIntBufferUtils.toBuffer(amount, "le", 8),
343
- outputScript
344
- ]))).toString("hex");
345
- return Buffer.from(toClaimHash(paymentHash, nonce, confirmations), "hex");
346
- }
347
-
348
- /**
349
- * @inheritDoc
350
- */
351
- getHashForHtlc(swapHash: Buffer): Buffer {
352
- return Buffer.from(toClaimHash(
353
- swapHash.toString("hex"),
354
- 0n,
355
- 0
356
- ), "hex");
357
- }
358
-
359
- /**
360
- * @inheritDoc
361
- */
362
- getHashForTxId(txId: string, confirmations: number): Buffer {
363
- return Buffer.from(toClaimHash(
364
- Buffer.from(txId, "hex").reverse().toString("hex"),
365
- 0n,
366
- confirmations
367
- ), "hex");
368
- }
369
-
370
- ////////////////////////////////////////////
371
- //// Swap data getters
372
- /**
373
- * @inheritDoc
374
- */
375
- async getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState> {
376
- const escrowStateKey = this._SwapEscrowState(Buffer.from(data.paymentHash, "hex"));
377
- const [escrowState, isExpired] = await Promise.all([
378
- this.program.account.escrowState.fetchNullable(escrowStateKey) as Promise<IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]>,
379
- this.isExpired(signer,data)
380
- ]);
381
-
382
- const getInitTxId = onceAsync(async () => {
383
- const txId = await this._Events.findInEvents(escrowStateKey, async (event, tx) => {
384
- if (event.name === "InitializeEvent") {
385
- const paymentHash = Buffer.from(event.data.hash).toString("hex");
386
- if(paymentHash!==data.paymentHash) return null;
387
- if(!event.data.sequence.eq(data.sequence)) return null;
388
- return tx.transaction.signatures[0];
389
- }
390
- });
391
- if(txId==null) throw new Error("Initialize event not found!");
392
- return txId;
393
- });
394
-
395
- if(escrowState!=null) {
396
- if(data.correctPDA(escrowState)) {
397
- if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.REFUNDABLE, getInitTxId};
398
- return {type: SwapCommitStateType.COMMITED, getInitTxId};
399
- }
400
-
401
- if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.EXPIRED};
402
- return {type: SwapCommitStateType.NOT_COMMITED};
403
- }
404
-
405
- //Check if paid or what
406
- const status: SwapNotCommitedState | SwapExpiredState | SwapPaidState | null = await this._Events.findInEvents(escrowStateKey, async (event, tx) => {
407
- if(event.name==="ClaimEvent") {
408
- const paymentHash = Buffer.from(event.data.hash).toString("hex");
409
- if(paymentHash!==data.paymentHash) return null;
410
- if(!event.data.sequence.eq(data.sequence)) return null;
411
- return {
412
- type: SwapCommitStateType.PAID,
413
- getInitTxId,
414
- getClaimTxId: () => Promise.resolve(tx.transaction.signatures[0]),
415
- getClaimResult: () => Promise.resolve(Buffer.from(event.data.secret).toString("hex")),
416
- getTxBlock: () => Promise.resolve({
417
- blockHeight: tx.slot,
418
- blockTime: tx.blockTime!
419
- })
420
- }
421
- }
422
- if(event.name==="RefundEvent") {
423
- const paymentHash = Buffer.from(event.data.hash).toString("hex");
424
- if(paymentHash!==data.paymentHash) return null;
425
- if(!event.data.sequence.eq(data.sequence)) return null;
426
- return {
427
- type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
428
- getInitTxId,
429
- getRefundTxId: () => Promise.resolve(tx.transaction.signatures[0]),
430
- getTxBlock: () => Promise.resolve({
431
- blockHeight: tx.slot,
432
- blockTime: tx.blockTime!
433
- })
434
- };
435
- }
436
- });
437
- if(status!=null) return status;
438
-
439
- if(isExpired) return {type: SwapCommitStateType.EXPIRED};
440
- return {type: SwapCommitStateType.NOT_COMMITED};
441
- }
442
-
443
- /**
444
- * @inheritDoc
445
- */
446
- async getCommitStatuses(request: { signer: string; swapData: SolanaSwapData }[]): Promise<{
447
- [p: string]: SwapCommitState
448
- }> {
449
- const result: {
450
- [p: string]: SwapCommitState
451
- } = {};
452
- let promises: Promise<void>[] = [];
453
- for(let {signer, swapData} of request) {
454
- promises.push(this.getCommitStatus(signer, swapData).then(val => {
455
- result[swapData.getEscrowHash()] = val;
456
- }));
457
- if(promises.length>=MAX_PARALLEL_COMMIT_STATUS_CHECKS) {
458
- await Promise.all(promises);
459
- promises = [];
460
- }
461
- }
462
- await Promise.all(promises);
463
- return result;
464
- }
465
-
466
- /**
467
- * @inheritDoc
468
- */
469
- async getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType> {
470
- const {paymentHash} = fromClaimHash(claimHash);
471
- const escrowStateKey = this._SwapEscrowState(Buffer.from(paymentHash, "hex"));
472
- const abortController = new AbortController();
473
-
474
- //Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
475
- const eventsPromise = this._Events.findInEvents(escrowStateKey, async (event) => {
476
- if(event.name==="ClaimEvent") return SwapCommitStateType.PAID;
477
- if(event.name==="RefundEvent") return SwapCommitStateType.NOT_COMMITED;
478
- }, abortController.signal).catch(e => {
479
- abortController.abort(e)
480
- return null;
481
- });
482
-
483
- const escrowState = await this.program.account.escrowState.fetchNullable(escrowStateKey);
484
- abortController.signal.throwIfAborted();
485
- if(escrowState!=null) {
486
- abortController.abort();
487
- return SwapCommitStateType.COMMITED;
488
- }
489
-
490
- //Check if paid or what
491
- const eventsStatus = await eventsPromise;
492
- abortController.signal.throwIfAborted();
493
- if(eventsStatus!=null) return eventsStatus;
494
-
495
- return SwapCommitStateType.NOT_COMMITED;
496
- }
497
-
498
- /**
499
- * @inheritDoc
500
- */
501
- async getCommitedData(claimHashHex: string): Promise<SolanaSwapData | null> {
502
- const {paymentHash} = fromClaimHash(claimHashHex);
503
- const paymentHashBuffer = Buffer.from(paymentHash, "hex");
504
-
505
- const account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"] | null =
506
- await this.program.account.escrowState.fetchNullable(
507
- this._SwapEscrowState(paymentHashBuffer)
508
- );
509
- if(account==null) return null;
510
-
511
- return SolanaSwapData.fromEscrowState(this.program.programId, this.version, account);
512
- }
513
-
514
- /**
515
- * @inheritDoc
516
- */
517
- async getHistoricalSwaps(signer: string, startBlockheight?: number): Promise<{
518
- swaps: {
519
- [escrowHash: string]: {
520
- init?: {
521
- data: SolanaSwapData,
522
- getInitTxId: () => Promise<string>,
523
- getTxBlock: () => Promise<{
524
- blockTime: number,
525
- blockHeight: number
526
- }>
527
- },
528
- state: SwapCommitState
529
- }
530
- },
531
- latestBlockheight?: number
532
- }> {
533
- let latestBlockheight: number | undefined;
534
-
535
- const events: {event: ProgramEvent<SwapProgram>, tx: ParsedTransactionWithMeta}[] = [];
536
-
537
- await this._Events.findInEvents(new PublicKey(signer), async (event, tx) => {
538
- if(latestBlockheight==null) latestBlockheight = tx.slot;
539
- events.push({event, tx});
540
- }, undefined, undefined, startBlockheight);
541
-
542
- this.logger.debug(`getHistoricalSwaps(): Found ${events.length} atomiq related events!`);
543
-
544
- const swapsOpened: {[escrowHash: string]: {
545
- data: SolanaSwapData,
546
- getInitTxId: () => Promise<string>,
547
- getTxBlock: () => Promise<{
548
- blockTime: number,
549
- blockHeight: number
550
- }>
551
- }} = {};
552
- const resultingSwaps: {
553
- [escrowHash: string]: {
554
- init?: {
555
- data: SolanaSwapData,
556
- getInitTxId: () => Promise<string>,
557
- getTxBlock: () => Promise<{
558
- blockTime: number,
559
- blockHeight: number
560
- }>
561
- },
562
- state: SwapCommitState
563
- }
564
- } = {};
565
-
566
- events.reverse();
567
- for(let {event, tx} of events) {
568
- const txSignature = tx.transaction.signatures[0];
569
- const paymentHash: string = Buffer.from(event.data.hash).toString("hex");
570
- const escrowHash = toEscrowHash(paymentHash, event.data.sequence);
571
-
572
- if(event.name==="InitializeEvent") {
573
- //Parse swap data from initialize event
574
- const txoHash: string = Buffer.from(event.data.txoHash).toString("hex");
575
- const instructions = this._Events.decodeInstructions(tx.transaction.message);
576
- if(instructions == null) {
577
- this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because cannot parse instructions!`);
578
- continue;
579
- }
580
-
581
- const initIx = instructions.find(
582
- ix => ix!=null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize")
583
- ) as InitInstruction;
584
- if(initIx == null) {
585
- this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because init instruction not found!`);
586
- continue;
587
- }
588
-
589
- swapsOpened[escrowHash] = {
590
- data: SolanaSwapData.fromInstruction(this.program.programId, this.version, initIx, txoHash),
591
- getInitTxId: () => Promise.resolve(txSignature),
592
- getTxBlock: () => Promise.resolve({
593
- blockHeight: tx.slot,
594
- blockTime: tx.blockTime!
595
- })
596
- };
597
- }
598
-
599
- if(event.name==="ClaimEvent") {
600
- const foundSwapData = swapsOpened[escrowHash];
601
- delete swapsOpened[escrowHash];
602
- resultingSwaps[escrowHash] = {
603
- init: foundSwapData,
604
- state: {
605
- type: SwapCommitStateType.PAID,
606
- getInitTxId: foundSwapData?.getInitTxId,
607
- getClaimTxId: () => Promise.resolve(txSignature),
608
- getClaimResult: () => Promise.resolve(Buffer.from(event.data.secret).toString("hex")),
609
- getTxBlock: () => Promise.resolve({
610
- blockHeight: tx.slot,
611
- blockTime: tx.blockTime!
612
- })
613
- }
614
- }
615
- }
616
-
617
- if(event.name==="RefundEvent") {
618
- const foundSwapData = swapsOpened[escrowHash];
619
- delete swapsOpened[escrowHash];
620
- const isExpired = foundSwapData!=null && await this.isExpired(signer, foundSwapData.data);
621
- resultingSwaps[escrowHash] = {
622
- init: foundSwapData,
623
- state: {
624
- type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
625
- getInitTxId: foundSwapData?.getInitTxId,
626
- getRefundTxId: () => Promise.resolve(txSignature),
627
- getTxBlock: () => Promise.resolve({
628
- blockHeight: tx.slot,
629
- blockTime: tx.blockTime!
630
- })
631
- }
632
- }
633
- }
634
- }
635
-
636
- this.logger.debug(`getHistoricalSwaps(): Found ${Object.keys(resultingSwaps).length} settled swaps!`);
637
- this.logger.debug(`getHistoricalSwaps(): Found ${Object.keys(swapsOpened).length} unsettled swaps!`);
638
-
639
- for(let escrowHash in swapsOpened) {
640
- const foundSwapData = swapsOpened[escrowHash];
641
- const isExpired = await this.isExpired(signer, foundSwapData.data);
642
- resultingSwaps[escrowHash] = {
643
- init: foundSwapData,
644
- state: foundSwapData.data.isOfferer(signer) && isExpired
645
- ? {type: SwapCommitStateType.REFUNDABLE, getInitTxId: foundSwapData.getInitTxId}
646
- : {type: SwapCommitStateType.COMMITED, getInitTxId: foundSwapData.getInitTxId}
647
- }
648
- }
649
-
650
- return {
651
- swaps: resultingSwaps,
652
- latestBlockheight: latestBlockheight ?? startBlockheight
653
- }
654
- }
655
-
656
- ////////////////////////////////////////////
657
- //// Swap data initializer
658
- /**
659
- * @inheritDoc
660
- */
661
- createSwapData(
662
- type: ChainSwapType,
663
- offerer: string,
664
- claimer: string,
665
- token: string,
666
- amount: bigint,
667
- claimHash: string,
668
- sequence: bigint,
669
- expiry: bigint,
670
- payIn: boolean,
671
- payOut: boolean,
672
- securityDeposit: bigint,
673
- claimerBounty: bigint,
674
- depositToken?: string
675
- ): Promise<SolanaSwapData> {
676
- if(depositToken!=null) {
677
- if(!new PublicKey(depositToken).equals(SolanaTokens.WSOL_ADDRESS)) throw new Error("Only SOL supported as deposit token!");
678
- }
679
- const tokenAddr: PublicKey = new PublicKey(token);
680
- const offererKey = new PublicKey(offerer);
681
- const claimerKey = new PublicKey(claimer);
682
- const {paymentHash, nonce, confirmations} = fromClaimHash(claimHash);
683
- const swapData = new SolanaSwapData({
684
- programId: this.program.programId,
685
- version: this.version,
686
- offerer: offererKey,
687
- claimer: claimerKey,
688
- token: tokenAddr,
689
- amount: toBN(amount),
690
- paymentHash,
691
- sequence: toBN(sequence),
692
- expiry: toBN(expiry),
693
- nonce,
694
- confirmations,
695
- payOut,
696
- kind: SolanaSwapData.typeToKind(type),
697
- payIn,
698
- offererAta: payIn ? getAssociatedTokenAddressSync(tokenAddr, offererKey) : PublicKey.default,
699
- claimerAta: payOut ? getAssociatedTokenAddressSync(tokenAddr, claimerKey) : PublicKey.default,
700
- securityDeposit: toBN(securityDeposit),
701
- claimerBounty: toBN(claimerBounty)
702
- });
703
- return Promise.resolve(swapData);
704
- }
705
-
706
- ////////////////////////////////////////////
707
- //// Utils
708
- /**
709
- * @inheritDoc
710
- */
711
- async getBalance(signer: string, tokenAddress: string, inContract: boolean): Promise<bigint> {
712
- if(!inContract) {
713
- return await this._Chain.getBalance(signer, tokenAddress);
714
- }
715
-
716
- const token = new PublicKey(tokenAddress);
717
- const publicKey = new PublicKey(signer);
718
-
719
- return await this.getIntermediaryBalance(publicKey, token);
720
- }
721
-
722
- /**
723
- * @inheritDoc
724
- */
725
- getIntermediaryData(address: string, token: string): Promise<{
726
- balance: bigint,
727
- reputation: IntermediaryReputationType
728
- } | null> {
729
- return this.LpVault.getIntermediaryData(new PublicKey(address), new PublicKey(token));
730
- }
731
-
732
- /**
733
- * @inheritDoc
734
- */
735
- getIntermediaryReputation(address: string, token: string): Promise<IntermediaryReputationType | null> {
736
- return this.LpVault.getIntermediaryReputation(new PublicKey(address), new PublicKey(token));
737
- }
738
-
739
- /**
740
- * Returns intermediary vault balance for a specific token.
741
- *
742
- * @param address Intermediary address
743
- * @param token Token mint
744
- */
745
- getIntermediaryBalance(address: PublicKey, token: PublicKey): Promise<bigint> {
746
- return this.LpVault.getIntermediaryBalance(address, token);
747
- }
748
-
749
- ////////////////////////////////////////////
750
- //// Transaction initializers
751
- /**
752
- * @inheritDoc
753
- */
754
- async txsClaimWithSecret(
755
- signer: string | SolanaSigner,
756
- swapData: SolanaSwapData,
757
- secret: string,
758
- checkExpiry?: boolean,
759
- initAta?: boolean,
760
- feeRate?: string,
761
- skipAtaCheck?: boolean
762
- ): Promise<SolanaTx[]> {
763
- return this.Claim.txsClaimWithSecret(typeof(signer)==="string" ? new PublicKey(signer) : signer.getPublicKey(), swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck)
764
- }
765
-
766
- /**
767
- * @inheritDoc
768
- */
769
- async txsClaimWithTxData(
770
- signer: string | SolanaSigner,
771
- swapData: SolanaSwapData,
772
- tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
773
- requiredConfirmations: number,
774
- vout: number,
775
- commitedHeader?: SolanaBtcStoredHeader,
776
- synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
777
- initAta?: boolean,
778
- feeRate?: string,
779
- ): Promise<SolanaTx[]> {
780
- if(swapData.confirmations!==requiredConfirmations) throw new Error("Invalid requiredConfirmations provided!");
781
- const {txs} = await this.Claim.txsClaimWithTxData(
782
- typeof(signer)==="string" ? new PublicKey(signer) : signer,
783
- swapData, tx, vout, commitedHeader, synchronizer, initAta, feeRate
784
- );
785
- return txs;
786
- }
787
-
788
- /**
789
- * @inheritDoc
790
- */
791
- txsRefund(signer: string, swapData: SolanaSwapData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
792
- return this.Refund.txsRefund(new PublicKey(signer), swapData, check, initAta, feeRate);
793
- }
794
-
795
- /**
796
- * @inheritDoc
797
- */
798
- txsRefundWithAuthorization(signer: string, swapData: SolanaSwapData, sig: SignatureData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
799
- return this.Refund.txsRefundWithAuthorization(new PublicKey(signer), swapData, sig.timeout, sig.prefix, sig.signature,check,initAta, feeRate);
800
- }
801
-
802
- /**
803
- * @inheritDoc
804
- */
805
- txsInit(sender: string, swapData: SolanaSwapData, sig: SignatureData, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
806
- if(swapData.isPayIn()) {
807
- return this.Init.txsInitPayIn(new PublicKey(sender), swapData, sig.timeout, sig.prefix, sig.signature, skipChecks, feeRate);
808
- } else {
809
- return this.Init.txsInit(new PublicKey(sender), swapData, sig.timeout, sig.prefix, sig.signature, skipChecks, feeRate);
810
- }
811
- }
812
-
813
- /**
814
- * @inheritDoc
815
- */
816
- txsWithdraw(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
817
- return this.LpVault.txsWithdraw(new PublicKey(signer), new PublicKey(token), amount, feeRate);
818
- }
819
-
820
- /**
821
- * @inheritDoc
822
- */
823
- txsDeposit(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
824
- return this.LpVault.txsDeposit(new PublicKey(signer), new PublicKey(token), amount, feeRate);
825
- }
826
-
827
- ////////////////////////////////////////////
828
- //// Executors
829
- /**
830
- * @inheritDoc
831
- */
832
- async claimWithSecret(
833
- signer: SolanaSigner,
834
- swapData: SolanaSwapData,
835
- secret: string,
836
- checkExpiry?: boolean,
837
- initAta?: boolean,
838
- txOptions?: TransactionConfirmationOptions
839
- ): Promise<string> {
840
- const result = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, checkExpiry, initAta, txOptions?.feeRate);
841
- const [signature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
842
- return signature;
843
- }
844
-
845
- /**
846
- * @inheritDoc
847
- */
848
- async claimWithTxData(
849
- signer: SolanaSigner,
850
- swapData: SolanaSwapData,
851
- tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
852
- requiredConfirmations: number,
853
- vout: number,
854
- commitedHeader?: SolanaBtcStoredHeader,
855
- synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
856
- initAta?: boolean,
857
- txOptions?: TransactionConfirmationOptions
858
- ): Promise<string> {
859
- if(requiredConfirmations!==swapData.confirmations) throw new Error("Invalid requiredConfirmations provided!");
860
-
861
- const {txs, claimTxIndex, storageAcc} = await this.Claim.txsClaimWithTxData(
862
- signer, swapData, tx, vout,
863
- commitedHeader, synchronizer, initAta, txOptions?.feeRate
864
- );
865
-
866
- const signatures = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal);
867
- await this._DataAccount.removeDataAccount(storageAcc);
868
-
869
- return signatures[claimTxIndex] ?? signatures[0];
870
- }
871
-
872
- /**
873
- * @inheritDoc
874
- */
875
- async refund(
876
- signer: SolanaSigner,
877
- swapData: SolanaSwapData,
878
- check?: boolean,
879
- initAta?: boolean,
880
- txOptions?: TransactionConfirmationOptions
881
- ): Promise<string> {
882
- let result = await this.txsRefund(signer.getAddress(), swapData, check, initAta, txOptions?.feeRate);
883
-
884
- const [signature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
885
-
886
- return signature;
887
- }
888
-
889
- /**
890
- * @inheritDoc
891
- */
892
- async refundWithAuthorization(
893
- signer: SolanaSigner,
894
- swapData: SolanaSwapData,
895
- signature: SignatureData,
896
- check?: boolean,
897
- initAta?: boolean,
898
- txOptions?: TransactionConfirmationOptions
899
- ): Promise<string> {
900
- let result = await this.txsRefundWithAuthorization(signer.getAddress(), swapData, signature, check, initAta, txOptions?.feeRate);
901
-
902
- const [txSignature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
903
-
904
- return txSignature;
905
- }
906
-
907
- /**
908
- * @inheritDoc
909
- */
910
- async init(
911
- signer: SolanaSigner,
912
- swapData: SolanaSwapData,
913
- signature: SignatureData,
914
- skipChecks?: boolean,
915
- txOptions?: TransactionConfirmationOptions
916
- ): Promise<string> {
917
- const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
918
-
919
- const signatures = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
920
-
921
- return signatures[signatures.length-1];
922
- }
923
-
924
- /**
925
- * @inheritDoc
926
- */
927
- async initAndClaimWithSecret(
928
- signer: SolanaSigner,
929
- swapData: SolanaSwapData,
930
- signature: SignatureData,
931
- secret: string,
932
- skipChecks?: boolean,
933
- txOptions?: TransactionConfirmationOptions
934
- ): Promise<string[]> {
935
- if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
936
-
937
- const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
938
- let txsClaim: SolanaTx[];
939
- if(isSwapProgramV1(this.program)) {
940
- // In V1 the initialize instruction has to contain the ATA initialization, hence we can skip checking it in claim
941
- txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
942
- } else {
943
- // In V2 the initialize instruction doesn't necessarily contain the ATA initialization, hence the claim instruction needs to
944
- // check and initialize the ATA if needed
945
- txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, true, txOptions?.feeRate, false);
946
- }
947
-
948
- const signatures = await this._Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
949
- return [signatures[txsCommit.length-1], signatures[signatures.length-1]]
950
- }
951
-
952
- /**
953
- * @inheritDoc
954
- */
955
- async withdraw(
956
- signer: SolanaSigner,
957
- token: string,
958
- amount: bigint,
959
- txOptions?: TransactionConfirmationOptions
960
- ): Promise<string> {
961
- const txs = await this.LpVault.txsWithdraw(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
962
- const [txId] = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
963
- return txId;
964
- }
965
-
966
- /**
967
- * @inheritDoc
968
- */
969
- async deposit(
970
- signer: SolanaSigner,
971
- token: string,
972
- amount: bigint,
973
- txOptions?: TransactionConfirmationOptions
974
- ): Promise<string> {
975
- const txs = await this.LpVault.txsDeposit(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
976
- const [txId] = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
977
- return txId;
978
- }
979
-
980
- ////////////////////////////////////////////
981
- //// Fees
982
- /**
983
- * @inheritDoc
984
- */
985
- getInitPayInFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
986
- const paymentHash = claimHash==null ? undefined : fromClaimHash(claimHash).paymentHash;
987
- return this.Init.getInitPayInFeeRate(
988
- toPublicKeyOrNull(offerer),
989
- toPublicKeyOrNull(claimer),
990
- toPublicKeyOrNull(token),
991
- paymentHash
992
- );
993
- }
994
-
995
- /**
996
- * @inheritDoc
997
- */
998
- getInitFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
999
- const paymentHash = claimHash==null ? undefined : fromClaimHash(claimHash).paymentHash;
1000
- return this.Init.getInitFeeRate(
1001
- toPublicKeyOrNull(offerer),
1002
- toPublicKeyOrNull(claimer),
1003
- toPublicKeyOrNull(token),
1004
- paymentHash
1005
- );
1006
- }
1007
-
1008
- /**
1009
- * @inheritDoc
1010
- */
1011
- getRefundFeeRate(swapData: SolanaSwapData): Promise<string> {
1012
- return this.Refund.getRefundFeeRate(swapData);
1013
- }
1014
-
1015
- /**
1016
- * @inheritDoc
1017
- */
1018
- getClaimFeeRate(signer: string, swapData: SolanaSwapData): Promise<string> {
1019
- return this.Claim.getClaimFeeRate(new PublicKey(signer), swapData);
1020
- }
1021
-
1022
- /**
1023
- * @inheritDoc
1024
- */
1025
- getClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1026
- return this.Claim.getClaimFee(new PublicKey(signer), swapData, feeRate);
1027
- }
1028
-
1029
- /**
1030
- * @inheritDoc
1031
- */
1032
- getRawClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1033
- return this.Claim.getRawClaimFee(new PublicKey(signer), swapData, feeRate);
1034
- }
1035
-
1036
- /**
1037
- * @inheritDoc
1038
- */
1039
- getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1040
- return this.Init.getInitFee(swapData, feeRate);
1041
- }
1042
-
1043
- /**
1044
- * @inheritDoc
1045
- */
1046
- getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1047
- return this.Init.getRawInitFee(swapData, feeRate);
1048
- }
1049
-
1050
- /**
1051
- * @inheritDoc
1052
- */
1053
- getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1054
- return this.Refund.getRefundFee(swapData, feeRate);
1055
- }
1056
-
1057
- /**
1058
- * @inheritDoc
1059
- */
1060
- getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1061
- return this.Refund.getRawRefundFee(swapData, feeRate);
1062
- }
1063
-
1064
- /**
1065
- * @inheritDoc
1066
- */
1067
- getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
1068
- return Buffer.from(sha256(Buffer.concat([
1069
- BigIntBufferUtils.toBuffer(amount, "le", 8),
1070
- outputScript
1071
- ])));
1072
- }
1073
-
1074
- }
1
+ import {SolanaSwapData, InitInstruction} from "./SolanaSwapData";
2
+ import {IdlAccounts, Program} from "@coral-xyz/anchor";
3
+ import {
4
+ ParsedTransactionWithMeta,
5
+ PublicKey,
6
+ } from "@solana/web3.js";
7
+ import {sha256} from "@noble/hashes/sha2";
8
+ import {SolanaBtcRelay} from "../btcrelay/SolanaBtcRelay";
9
+ import * as programIdlV1 from "./v1/programIdl.json";
10
+ import * as programIdlV2 from "./v2/programIdl.json";
11
+ import {
12
+ IStorageManager,
13
+ SwapContract,
14
+ ChainSwapType,
15
+ IntermediaryReputationType,
16
+ TransactionConfirmationOptions,
17
+ SignatureData,
18
+ RelaySynchronizer,
19
+ BigIntBufferUtils,
20
+ SwapCommitState,
21
+ SwapCommitStateType, SwapNotCommitedState, SwapExpiredState, SwapPaidState, BitcoinNetwork
22
+ } from "@atomiqlabs/base";
23
+ import {SolanaBtcStoredHeader} from "../btcrelay/headers/SolanaBtcStoredHeader";
24
+ import {
25
+ getAssociatedTokenAddressSync,
26
+ } from "@solana/spl-token";
27
+ import {SwapProgram} from "./v1/programTypes";
28
+ import {SolanaChainInterface} from "../chain/SolanaChainInterface";
29
+ import {SolanaProgramBase} from "../program/SolanaProgramBase";
30
+ import {SolanaTx} from "../chain/modules/SolanaTransactions";
31
+ import {SwapInit, SolanaPreFetchData, SolanaPreFetchVerification} from "./modules/SwapInit";
32
+ import {SolanaDataAccount, StoredDataAccount} from "./modules/SolanaDataAccount";
33
+ import {SwapRefund} from "./modules/SwapRefund";
34
+ import {SwapClaim} from "./modules/SwapClaim";
35
+ import {SolanaLpVault} from "./modules/SolanaLpVault";
36
+ import {Buffer} from "buffer";
37
+ import {SolanaSigner} from "../wallet/SolanaSigner";
38
+ import {fromClaimHash, onceAsync, toBN, toClaimHash, toEscrowHash} from "../../utils/Utils";
39
+ import {SolanaTokens} from "../chain/modules/SolanaTokens";
40
+ import * as BN from "bn.js";
41
+ import {ProgramEvent} from "../program/modules/SolanaProgramEvents";
42
+ import {SwapProgramV2} from "./v2/programTypes";
43
+ import {SolanaChains} from "../SolanaChains";
44
+
45
+ export function isSwapProgramV1(obj: any): obj is Program<SwapProgram> {
46
+ return obj.idl.version==="0.1.0";
47
+ }
48
+
49
+ export function isSwapProgramV2(obj: any): obj is Program<SwapProgramV2> {
50
+ return obj.idl.version==="0.2.0";
51
+ }
52
+
53
+ function toPublicKeyOrNull(str: string | null | undefined): PublicKey | undefined {
54
+ return str==null ? undefined : new PublicKey(str);
55
+ }
56
+
57
+ const MAX_PARALLEL_COMMIT_STATUS_CHECKS = 5;
58
+
59
+ /**
60
+ * Solana swap (escrow manager) program representation handling PrTLC (on-chain) and HTLC (lightning) based swaps.
61
+ *
62
+ * @category Swaps
63
+ */
64
+ export class SolanaSwapProgram<Version extends "v1" | "v2" = "v1" | "v2">
65
+ extends SolanaProgramBase<SwapProgram | SwapProgramV2>
66
+ implements SwapContract<
67
+ SolanaSwapData,
68
+ SolanaTx,
69
+ SolanaPreFetchData,
70
+ SolanaPreFetchVerification,
71
+ SolanaSigner,
72
+ "SOLANA"
73
+ > {
74
+
75
+ ////////////////////////
76
+ //// Constants
77
+ /**
78
+ * Rent-exempt amount (lamports) for escrow state accounts.
79
+ */
80
+ public readonly ESCROW_STATE_RENT_EXEMPT: number;
81
+
82
+ public readonly version: Version;
83
+
84
+ public readonly supportsInitWithoutClaimer: Version extends "v1" ? false : true;
85
+
86
+ ////////////////////////
87
+ //// PDA accessors
88
+ /**
89
+ * PDA of the swap vault authority.
90
+ * @internal
91
+ */
92
+ static readonly _SwapVaultAuthority = SolanaProgramBase._pda("authority"); // Only necessary for V1 program
93
+ readonly _SwapVaultAuthority = SolanaSwapProgram._SwapVaultAuthority(this.program.programId); // Only necessary for V1 program
94
+ /**
95
+ * PDA helper for global token vault accounts.
96
+ * @internal
97
+ */
98
+ static readonly _SwapVault = SolanaProgramBase._pda("vault", (tokenAddress: PublicKey) => [tokenAddress.toBuffer()]);
99
+ readonly _SwapVault = SolanaSwapProgram._SwapVault.bind(this, this.program.programId);
100
+ /**
101
+ * PDA helper for per-user token vault accounts.
102
+ * @internal
103
+ */
104
+ static readonly _SwapUserVault = SolanaProgramBase._pda("uservault",
105
+ (publicKey: PublicKey, tokenAddress: PublicKey) => [publicKey.toBuffer(), tokenAddress.toBuffer()]
106
+ );
107
+ readonly _SwapUserVault = SolanaSwapProgram._SwapUserVault.bind(this, this.program.programId);
108
+ /**
109
+ * PDA helper for escrow state accounts.
110
+ * @internal
111
+ */
112
+ static readonly _SwapEscrowState = SolanaProgramBase._pda("state", (hash: Buffer) => [hash]);
113
+ readonly _SwapEscrowState = SolanaSwapProgram._SwapEscrowState.bind(this, this.program.programId);
114
+
115
+ ////////////////////////
116
+ //// Timeouts
117
+ /**
118
+ * @inheritDoc
119
+ */
120
+ readonly chainId: "SOLANA" = "SOLANA";
121
+ /**
122
+ * @inheritDoc
123
+ */
124
+ readonly claimWithSecretTimeout: number = 45;
125
+ /**
126
+ * @inheritDoc
127
+ */
128
+ readonly claimWithTxDataTimeout: number = 120;
129
+ /**
130
+ * @inheritDoc
131
+ */
132
+ readonly refundTimeout: number = 45;
133
+ /**
134
+ * Grace period (seconds) applied to claimer-side expiry checks.
135
+ */
136
+ private readonly claimGracePeriod: number = 10*60;
137
+ /**
138
+ * Grace period (seconds) applied to offerer-side expiry checks.
139
+ */
140
+ private readonly refundGracePeriod: number = 10*60;
141
+ /**
142
+ * Authorization grace period in seconds.
143
+ * @internal
144
+ */
145
+ readonly _authGracePeriod: number = 30;
146
+
147
+ ////////////////////////
148
+ //// Services
149
+ /**
150
+ * Swap initialization service.
151
+ */
152
+ private readonly Init: SwapInit;
153
+ /**
154
+ * Swap refund service.
155
+ */
156
+ private readonly Refund: SwapRefund;
157
+ /**
158
+ * Swap claim service.
159
+ */
160
+ private readonly Claim: SwapClaim;
161
+ /**
162
+ * LP vault interaction service.
163
+ */
164
+ private readonly LpVault: SolanaLpVault;
165
+ /**
166
+ * Temporary data-account lifecycle service.
167
+ * @internal
168
+ */
169
+ readonly _DataAccount: SolanaDataAccount;
170
+
171
+ constructor(
172
+ chainInterface: SolanaChainInterface,
173
+ btcRelay: SolanaBtcRelay<any>,
174
+ storage: IStorageManager<StoredDataAccount>,
175
+ programAddress?: string,
176
+ bitcoinNetwork?: BitcoinNetwork,
177
+ version?: Version
178
+ ) {
179
+ version ??= "v1" as any;
180
+ if(bitcoinNetwork!=null && programAddress==null) {
181
+ programAddress = SolanaChains[bitcoinNetwork]?.addresses[version ?? "v1"]?.swapContract;
182
+ }
183
+ super(chainInterface, version==="v1" ? programIdlV1 : programIdlV2, programAddress);
184
+
185
+ this.version = version!;
186
+ this.ESCROW_STATE_RENT_EXEMPT = this.version==="v1" ? 2658720 : 2665680;
187
+ this.supportsInitWithoutClaimer = (this.version!=="v1") as any;
188
+
189
+ this.Init = new SwapInit(chainInterface, this);
190
+ this.Refund = new SwapRefund(chainInterface, this);
191
+ this.Claim = new SwapClaim(chainInterface, this, btcRelay);
192
+ this._DataAccount = new SolanaDataAccount(chainInterface, this, storage);
193
+ this.LpVault = new SolanaLpVault(chainInterface, this);
194
+ }
195
+
196
+ /**
197
+ * @inheritDoc
198
+ */
199
+ async start(): Promise<void> {
200
+ await this._DataAccount.init();
201
+ }
202
+
203
+ /**
204
+ * @inheritDoc
205
+ */
206
+ getClaimableDeposits(signer: string): Promise<{count: number, totalValue: bigint}> {
207
+ return this._DataAccount.getDataAccountsInfo(new PublicKey(signer));
208
+ }
209
+
210
+ /**
211
+ * @inheritDoc
212
+ */
213
+ claimDeposits(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
214
+ return this._DataAccount.sweepDataAccounts(signer);
215
+ }
216
+
217
+ ////////////////////////////////////////////
218
+ //// Signatures
219
+ /**
220
+ * @inheritDoc
221
+ */
222
+ preFetchForInitSignatureVerification(data: SolanaPreFetchData): Promise<SolanaPreFetchVerification> {
223
+ return this.Init.preFetchForInitSignatureVerification(data);
224
+ }
225
+
226
+ /**
227
+ * @inheritDoc
228
+ */
229
+ preFetchBlockDataForSignatures(): Promise<SolanaPreFetchData> {
230
+ return this.Init.preFetchBlockDataForSignatures();
231
+ }
232
+
233
+ /**
234
+ * @inheritDoc
235
+ */
236
+ getInitSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number, preFetchedBlockData?: SolanaPreFetchData, feeRate?: string): Promise<SignatureData> {
237
+ return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
238
+ }
239
+
240
+ /**
241
+ * @inheritDoc
242
+ */
243
+ isValidInitAuthorization(signer: string, swapData: SolanaSwapData, sig: SignatureData, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
244
+ return this.Init.isSignatureValid(new PublicKey(signer), swapData, sig.timeout, sig.prefix, sig.signature, feeRate, preFetchedData);
245
+ }
246
+
247
+ /**
248
+ * @inheritDoc
249
+ */
250
+ getInitAuthorizationExpiry(swapData: SolanaSwapData, sig: SignatureData, preFetchedData?: SolanaPreFetchVerification): Promise<number> {
251
+ return this.Init.getSignatureExpiry(sig.timeout, sig.signature, preFetchedData);
252
+ }
253
+
254
+ /**
255
+ * @inheritDoc
256
+ */
257
+ isInitAuthorizationExpired(swapData: SolanaSwapData, sig: SignatureData): Promise<boolean> {
258
+ return this.Init.isSignatureExpired(sig.signature, sig.timeout);
259
+ }
260
+
261
+ /**
262
+ * @inheritDoc
263
+ */
264
+ getRefundSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number): Promise<SignatureData> {
265
+ return this.Refund.signSwapRefund(signer, swapData, authorizationTimeout);
266
+ }
267
+
268
+ /**
269
+ * @inheritDoc
270
+ */
271
+ isValidRefundAuthorization(swapData: SolanaSwapData, sig: SignatureData): Promise<Buffer> {
272
+ return this.Refund.isSignatureValid(swapData, sig.timeout, sig.prefix, sig.signature);
273
+ }
274
+
275
+ /**
276
+ * @inheritDoc
277
+ */
278
+ getDataSignature(signer: SolanaSigner, data: Buffer): Promise<string> {
279
+ return this._Chain.Signatures.getDataSignature(signer, data);
280
+ }
281
+
282
+ /**
283
+ * @inheritDoc
284
+ */
285
+ isValidDataSignature(data: Buffer, signature: string, publicKey: string): Promise<boolean> {
286
+ return this._Chain.Signatures.isValidDataSignature(data, signature, publicKey);
287
+ }
288
+
289
+ ////////////////////////////////////////////
290
+ //// Swap data utils
291
+ /**
292
+ * @inheritDoc
293
+ */
294
+ async isClaimable(signer: string, data: SolanaSwapData): Promise<boolean> {
295
+ if(!data.isClaimer(signer)) return false;
296
+ if(await this.isExpired(signer, data)) return false;
297
+ return await this.isCommited(data);
298
+ }
299
+
300
+ /**
301
+ * @inheritDoc
302
+ */
303
+ async isCommited(swapData: SolanaSwapData): Promise<boolean> {
304
+ const paymentHash = Buffer.from(swapData.paymentHash, "hex");
305
+
306
+ const account = await this.program.account.escrowState.fetchNullable(this._SwapEscrowState(paymentHash));
307
+ if(account==null) return false;
308
+
309
+ return swapData.correctPDA(account);
310
+ }
311
+
312
+ /**
313
+ * @inheritDoc
314
+ */
315
+ isExpired(signer: string, data: SolanaSwapData, refundSide?: boolean): Promise<boolean> {
316
+ let currentTimestamp: BN = new BN(Math.floor(Date.now()/1000));
317
+ if(data.isClaimer(signer) && !refundSide) {
318
+ currentTimestamp = currentTimestamp.add(new BN(this.claimGracePeriod));
319
+ } else {
320
+ currentTimestamp = currentTimestamp.sub(new BN(this.refundGracePeriod));
321
+ }
322
+ return Promise.resolve(data.expiry.lt(currentTimestamp));
323
+ }
324
+
325
+ /**
326
+ * @inheritDoc
327
+ */
328
+ async isRequestRefundable(signer: string, data: SolanaSwapData): Promise<boolean> {
329
+ //V1 Swap can only be refunded by the offerer
330
+ if(isSwapProgramV1(this.program) && !data.isOfferer(signer)) return false;
331
+ if(!(await this.isExpired(signer, data, true))) return false;
332
+ return await this.isCommited(data);
333
+ }
334
+
335
+ /**
336
+ * @inheritDoc
337
+ */
338
+ getHashForOnchain(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
339
+ nonce ??= 0n;
340
+ const paymentHash = Buffer.from(sha256(Buffer.concat([
341
+ BigIntBufferUtils.toBuffer(nonce, "le", 8),
342
+ BigIntBufferUtils.toBuffer(amount, "le", 8),
343
+ outputScript
344
+ ]))).toString("hex");
345
+ return Buffer.from(toClaimHash(paymentHash, nonce, confirmations), "hex");
346
+ }
347
+
348
+ /**
349
+ * @inheritDoc
350
+ */
351
+ getHashForHtlc(swapHash: Buffer): Buffer {
352
+ return Buffer.from(toClaimHash(
353
+ swapHash.toString("hex"),
354
+ 0n,
355
+ 0
356
+ ), "hex");
357
+ }
358
+
359
+ /**
360
+ * @inheritDoc
361
+ */
362
+ getHashForTxId(txId: string, confirmations: number): Buffer {
363
+ return Buffer.from(toClaimHash(
364
+ Buffer.from(txId, "hex").reverse().toString("hex"),
365
+ 0n,
366
+ confirmations
367
+ ), "hex");
368
+ }
369
+
370
+ ////////////////////////////////////////////
371
+ //// Swap data getters
372
+ /**
373
+ * @inheritDoc
374
+ */
375
+ async getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState> {
376
+ const escrowStateKey = this._SwapEscrowState(Buffer.from(data.paymentHash, "hex"));
377
+ const [escrowState, isExpired] = await Promise.all([
378
+ this.program.account.escrowState.fetchNullable(escrowStateKey) as Promise<IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]>,
379
+ this.isExpired(signer,data)
380
+ ]);
381
+
382
+ const getInitTxId = onceAsync(async () => {
383
+ const txId = await this._Events.findInEvents(escrowStateKey, async (event, tx) => {
384
+ if (event.name === "InitializeEvent") {
385
+ const paymentHash = Buffer.from(event.data.hash).toString("hex");
386
+ if(paymentHash!==data.paymentHash) return null;
387
+ if(!event.data.sequence.eq(data.sequence)) return null;
388
+ return tx.transaction.signatures[0];
389
+ }
390
+ });
391
+ if(txId==null) throw new Error("Initialize event not found!");
392
+ return txId;
393
+ });
394
+
395
+ if(escrowState!=null) {
396
+ if(data.correctPDA(escrowState)) {
397
+ if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.REFUNDABLE, getInitTxId};
398
+ return {type: SwapCommitStateType.COMMITED, getInitTxId};
399
+ }
400
+
401
+ if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.EXPIRED};
402
+ return {type: SwapCommitStateType.NOT_COMMITED};
403
+ }
404
+
405
+ //Check if paid or what
406
+ const status: SwapNotCommitedState | SwapExpiredState | SwapPaidState | null = await this._Events.findInEvents(escrowStateKey, async (event, tx) => {
407
+ if(event.name==="ClaimEvent") {
408
+ const paymentHash = Buffer.from(event.data.hash).toString("hex");
409
+ if(paymentHash!==data.paymentHash) return null;
410
+ if(!event.data.sequence.eq(data.sequence)) return null;
411
+ return {
412
+ type: SwapCommitStateType.PAID,
413
+ getInitTxId,
414
+ getClaimTxId: () => Promise.resolve(tx.transaction.signatures[0]),
415
+ getClaimResult: () => Promise.resolve(Buffer.from(event.data.secret).toString("hex")),
416
+ getTxBlock: () => Promise.resolve({
417
+ blockHeight: tx.slot,
418
+ blockTime: tx.blockTime!
419
+ })
420
+ }
421
+ }
422
+ if(event.name==="RefundEvent") {
423
+ const paymentHash = Buffer.from(event.data.hash).toString("hex");
424
+ if(paymentHash!==data.paymentHash) return null;
425
+ if(!event.data.sequence.eq(data.sequence)) return null;
426
+ return {
427
+ type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
428
+ getInitTxId,
429
+ getRefundTxId: () => Promise.resolve(tx.transaction.signatures[0]),
430
+ getTxBlock: () => Promise.resolve({
431
+ blockHeight: tx.slot,
432
+ blockTime: tx.blockTime!
433
+ })
434
+ };
435
+ }
436
+ });
437
+ if(status!=null) return status;
438
+
439
+ if(isExpired) return {type: SwapCommitStateType.EXPIRED};
440
+ return {type: SwapCommitStateType.NOT_COMMITED};
441
+ }
442
+
443
+ /**
444
+ * @inheritDoc
445
+ */
446
+ async getCommitStatuses(request: { signer: string; swapData: SolanaSwapData }[]): Promise<{
447
+ [p: string]: SwapCommitState
448
+ }> {
449
+ const result: {
450
+ [p: string]: SwapCommitState
451
+ } = {};
452
+ let promises: Promise<void>[] = [];
453
+ for(let {signer, swapData} of request) {
454
+ promises.push(this.getCommitStatus(signer, swapData).then(val => {
455
+ result[swapData.getEscrowHash()] = val;
456
+ }));
457
+ if(promises.length>=MAX_PARALLEL_COMMIT_STATUS_CHECKS) {
458
+ await Promise.all(promises);
459
+ promises = [];
460
+ }
461
+ }
462
+ await Promise.all(promises);
463
+ return result;
464
+ }
465
+
466
+ /**
467
+ * @inheritDoc
468
+ */
469
+ async getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType> {
470
+ const {paymentHash} = fromClaimHash(claimHash);
471
+ const escrowStateKey = this._SwapEscrowState(Buffer.from(paymentHash, "hex"));
472
+ const abortController = new AbortController();
473
+
474
+ //Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
475
+ const eventsPromise = this._Events.findInEvents(escrowStateKey, async (event) => {
476
+ if(event.name==="ClaimEvent") return SwapCommitStateType.PAID;
477
+ if(event.name==="RefundEvent") return SwapCommitStateType.NOT_COMMITED;
478
+ }, abortController.signal).catch(e => {
479
+ abortController.abort(e)
480
+ return null;
481
+ });
482
+
483
+ const escrowState = await this.program.account.escrowState.fetchNullable(escrowStateKey);
484
+ abortController.signal.throwIfAborted();
485
+ if(escrowState!=null) {
486
+ abortController.abort();
487
+ return SwapCommitStateType.COMMITED;
488
+ }
489
+
490
+ //Check if paid or what
491
+ const eventsStatus = await eventsPromise;
492
+ abortController.signal.throwIfAborted();
493
+ if(eventsStatus!=null) return eventsStatus;
494
+
495
+ return SwapCommitStateType.NOT_COMMITED;
496
+ }
497
+
498
+ /**
499
+ * @inheritDoc
500
+ */
501
+ async getCommitedData(claimHashHex: string): Promise<SolanaSwapData | null> {
502
+ const {paymentHash} = fromClaimHash(claimHashHex);
503
+ const paymentHashBuffer = Buffer.from(paymentHash, "hex");
504
+
505
+ const account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"] | null =
506
+ await this.program.account.escrowState.fetchNullable(
507
+ this._SwapEscrowState(paymentHashBuffer)
508
+ );
509
+ if(account==null) return null;
510
+
511
+ return SolanaSwapData.fromEscrowState(this.program.programId, this.version, account);
512
+ }
513
+
514
+ /**
515
+ * @inheritDoc
516
+ */
517
+ async getHistoricalSwaps(signer: string, startBlockheight?: number): Promise<{
518
+ swaps: {
519
+ [escrowHash: string]: {
520
+ init?: {
521
+ data: SolanaSwapData,
522
+ getInitTxId: () => Promise<string>,
523
+ getTxBlock: () => Promise<{
524
+ blockTime: number,
525
+ blockHeight: number
526
+ }>
527
+ },
528
+ state: SwapCommitState
529
+ }
530
+ },
531
+ latestBlockheight?: number
532
+ }> {
533
+ let latestBlockheight: number | undefined;
534
+
535
+ const events: {event: ProgramEvent<SwapProgram>, tx: ParsedTransactionWithMeta}[] = [];
536
+
537
+ await this._Events.findInEvents(new PublicKey(signer), async (event, tx) => {
538
+ if(latestBlockheight==null) latestBlockheight = tx.slot;
539
+ events.push({event, tx});
540
+ }, undefined, undefined, startBlockheight);
541
+
542
+ this.logger.debug(`getHistoricalSwaps(): Found ${events.length} atomiq related events!`);
543
+
544
+ const swapsOpened: {[escrowHash: string]: {
545
+ data: SolanaSwapData,
546
+ getInitTxId: () => Promise<string>,
547
+ getTxBlock: () => Promise<{
548
+ blockTime: number,
549
+ blockHeight: number
550
+ }>
551
+ }} = {};
552
+ const resultingSwaps: {
553
+ [escrowHash: string]: {
554
+ init?: {
555
+ data: SolanaSwapData,
556
+ getInitTxId: () => Promise<string>,
557
+ getTxBlock: () => Promise<{
558
+ blockTime: number,
559
+ blockHeight: number
560
+ }>
561
+ },
562
+ state: SwapCommitState
563
+ }
564
+ } = {};
565
+
566
+ events.reverse();
567
+ for(let {event, tx} of events) {
568
+ const txSignature = tx.transaction.signatures[0];
569
+ const paymentHash: string = Buffer.from(event.data.hash).toString("hex");
570
+ const escrowHash = toEscrowHash(paymentHash, event.data.sequence);
571
+
572
+ if(event.name==="InitializeEvent") {
573
+ //Parse swap data from initialize event
574
+ const txoHash: string = Buffer.from(event.data.txoHash).toString("hex");
575
+ const instructions = this._Events.decodeInstructions(tx.transaction.message);
576
+ if(instructions == null) {
577
+ this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because cannot parse instructions!`);
578
+ continue;
579
+ }
580
+
581
+ const initIx = instructions.find(
582
+ ix => ix!=null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize")
583
+ ) as InitInstruction;
584
+ if(initIx == null) {
585
+ this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because init instruction not found!`);
586
+ continue;
587
+ }
588
+
589
+ swapsOpened[escrowHash] = {
590
+ data: SolanaSwapData.fromInstruction(this.program.programId, this.version, initIx, txoHash),
591
+ getInitTxId: () => Promise.resolve(txSignature),
592
+ getTxBlock: () => Promise.resolve({
593
+ blockHeight: tx.slot,
594
+ blockTime: tx.blockTime!
595
+ })
596
+ };
597
+ }
598
+
599
+ if(event.name==="ClaimEvent") {
600
+ const foundSwapData = swapsOpened[escrowHash];
601
+ delete swapsOpened[escrowHash];
602
+ resultingSwaps[escrowHash] = {
603
+ init: foundSwapData,
604
+ state: {
605
+ type: SwapCommitStateType.PAID,
606
+ getInitTxId: foundSwapData?.getInitTxId,
607
+ getClaimTxId: () => Promise.resolve(txSignature),
608
+ getClaimResult: () => Promise.resolve(Buffer.from(event.data.secret).toString("hex")),
609
+ getTxBlock: () => Promise.resolve({
610
+ blockHeight: tx.slot,
611
+ blockTime: tx.blockTime!
612
+ })
613
+ }
614
+ }
615
+ }
616
+
617
+ if(event.name==="RefundEvent") {
618
+ const foundSwapData = swapsOpened[escrowHash];
619
+ delete swapsOpened[escrowHash];
620
+ const isExpired = foundSwapData!=null && await this.isExpired(signer, foundSwapData.data);
621
+ resultingSwaps[escrowHash] = {
622
+ init: foundSwapData,
623
+ state: {
624
+ type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
625
+ getInitTxId: foundSwapData?.getInitTxId,
626
+ getRefundTxId: () => Promise.resolve(txSignature),
627
+ getTxBlock: () => Promise.resolve({
628
+ blockHeight: tx.slot,
629
+ blockTime: tx.blockTime!
630
+ })
631
+ }
632
+ }
633
+ }
634
+ }
635
+
636
+ this.logger.debug(`getHistoricalSwaps(): Found ${Object.keys(resultingSwaps).length} settled swaps!`);
637
+ this.logger.debug(`getHistoricalSwaps(): Found ${Object.keys(swapsOpened).length} unsettled swaps!`);
638
+
639
+ for(let escrowHash in swapsOpened) {
640
+ const foundSwapData = swapsOpened[escrowHash];
641
+ const isExpired = await this.isExpired(signer, foundSwapData.data);
642
+ resultingSwaps[escrowHash] = {
643
+ init: foundSwapData,
644
+ state: foundSwapData.data.isOfferer(signer) && isExpired
645
+ ? {type: SwapCommitStateType.REFUNDABLE, getInitTxId: foundSwapData.getInitTxId}
646
+ : {type: SwapCommitStateType.COMMITED, getInitTxId: foundSwapData.getInitTxId}
647
+ }
648
+ }
649
+
650
+ return {
651
+ swaps: resultingSwaps,
652
+ latestBlockheight: latestBlockheight ?? startBlockheight
653
+ }
654
+ }
655
+
656
+ ////////////////////////////////////////////
657
+ //// Swap data initializer
658
+ /**
659
+ * @inheritDoc
660
+ */
661
+ createSwapData(
662
+ type: ChainSwapType,
663
+ offerer: string,
664
+ claimer: string,
665
+ token: string,
666
+ amount: bigint,
667
+ claimHash: string,
668
+ sequence: bigint,
669
+ expiry: bigint,
670
+ payIn: boolean,
671
+ payOut: boolean,
672
+ securityDeposit: bigint,
673
+ claimerBounty: bigint,
674
+ depositToken?: string
675
+ ): Promise<SolanaSwapData> {
676
+ if(depositToken!=null) {
677
+ if(!new PublicKey(depositToken).equals(SolanaTokens.WSOL_ADDRESS)) throw new Error("Only SOL supported as deposit token!");
678
+ }
679
+ const tokenAddr: PublicKey = new PublicKey(token);
680
+ const offererKey = new PublicKey(offerer);
681
+ const claimerKey = new PublicKey(claimer);
682
+ const {paymentHash, nonce, confirmations} = fromClaimHash(claimHash);
683
+ const swapData = new SolanaSwapData({
684
+ programId: this.program.programId,
685
+ version: this.version,
686
+ offerer: offererKey,
687
+ claimer: claimerKey,
688
+ token: tokenAddr,
689
+ amount: toBN(amount),
690
+ paymentHash,
691
+ sequence: toBN(sequence),
692
+ expiry: toBN(expiry),
693
+ nonce,
694
+ confirmations,
695
+ payOut,
696
+ kind: SolanaSwapData.typeToKind(type),
697
+ payIn,
698
+ offererAta: payIn ? getAssociatedTokenAddressSync(tokenAddr, offererKey) : PublicKey.default,
699
+ claimerAta: payOut ? getAssociatedTokenAddressSync(tokenAddr, claimerKey) : PublicKey.default,
700
+ securityDeposit: toBN(securityDeposit),
701
+ claimerBounty: toBN(claimerBounty)
702
+ });
703
+ return Promise.resolve(swapData);
704
+ }
705
+
706
+ ////////////////////////////////////////////
707
+ //// Utils
708
+ /**
709
+ * @inheritDoc
710
+ */
711
+ async getBalance(signer: string, tokenAddress: string, inContract: boolean): Promise<bigint> {
712
+ if(!inContract) {
713
+ return await this._Chain.getBalance(signer, tokenAddress);
714
+ }
715
+
716
+ const token = new PublicKey(tokenAddress);
717
+ const publicKey = new PublicKey(signer);
718
+
719
+ return await this.getIntermediaryBalance(publicKey, token);
720
+ }
721
+
722
+ /**
723
+ * @inheritDoc
724
+ */
725
+ getIntermediaryData(address: string, token: string): Promise<{
726
+ balance: bigint,
727
+ reputation: IntermediaryReputationType
728
+ } | null> {
729
+ return this.LpVault.getIntermediaryData(new PublicKey(address), new PublicKey(token));
730
+ }
731
+
732
+ /**
733
+ * @inheritDoc
734
+ */
735
+ getIntermediaryReputation(address: string, token: string): Promise<IntermediaryReputationType | null> {
736
+ return this.LpVault.getIntermediaryReputation(new PublicKey(address), new PublicKey(token));
737
+ }
738
+
739
+ /**
740
+ * Returns intermediary vault balance for a specific token.
741
+ *
742
+ * @param address Intermediary address
743
+ * @param token Token mint
744
+ */
745
+ getIntermediaryBalance(address: PublicKey, token: PublicKey): Promise<bigint> {
746
+ return this.LpVault.getIntermediaryBalance(address, token);
747
+ }
748
+
749
+ ////////////////////////////////////////////
750
+ //// Transaction initializers
751
+ /**
752
+ * @inheritDoc
753
+ */
754
+ async txsClaimWithSecret(
755
+ signer: string | SolanaSigner,
756
+ swapData: SolanaSwapData,
757
+ secret: string,
758
+ checkExpiry?: boolean,
759
+ initAta?: boolean,
760
+ feeRate?: string,
761
+ skipAtaCheck?: boolean
762
+ ): Promise<SolanaTx[]> {
763
+ return this.Claim.txsClaimWithSecret(typeof(signer)==="string" ? new PublicKey(signer) : signer.getPublicKey(), swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck)
764
+ }
765
+
766
+ /**
767
+ * @inheritDoc
768
+ */
769
+ async txsClaimWithTxData(
770
+ signer: string | SolanaSigner,
771
+ swapData: SolanaSwapData,
772
+ tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
773
+ requiredConfirmations: number,
774
+ vout: number,
775
+ commitedHeader?: SolanaBtcStoredHeader,
776
+ synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
777
+ initAta?: boolean,
778
+ feeRate?: string,
779
+ ): Promise<SolanaTx[]> {
780
+ if(swapData.confirmations!==requiredConfirmations) throw new Error("Invalid requiredConfirmations provided!");
781
+ const {txs} = await this.Claim.txsClaimWithTxData(
782
+ typeof(signer)==="string" ? new PublicKey(signer) : signer,
783
+ swapData, tx, vout, commitedHeader, synchronizer, initAta, feeRate
784
+ );
785
+ return txs;
786
+ }
787
+
788
+ /**
789
+ * @inheritDoc
790
+ */
791
+ txsRefund(signer: string, swapData: SolanaSwapData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
792
+ return this.Refund.txsRefund(new PublicKey(signer), swapData, check, initAta, feeRate);
793
+ }
794
+
795
+ /**
796
+ * @inheritDoc
797
+ */
798
+ txsRefundWithAuthorization(signer: string, swapData: SolanaSwapData, sig: SignatureData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
799
+ return this.Refund.txsRefundWithAuthorization(new PublicKey(signer), swapData, sig.timeout, sig.prefix, sig.signature,check,initAta, feeRate);
800
+ }
801
+
802
+ /**
803
+ * @inheritDoc
804
+ */
805
+ txsInit(sender: string, swapData: SolanaSwapData, sig: SignatureData, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
806
+ if(swapData.isPayIn()) {
807
+ return this.Init.txsInitPayIn(new PublicKey(sender), swapData, sig.timeout, sig.prefix, sig.signature, skipChecks, feeRate);
808
+ } else {
809
+ return this.Init.txsInit(new PublicKey(sender), swapData, sig.timeout, sig.prefix, sig.signature, skipChecks, feeRate);
810
+ }
811
+ }
812
+
813
+ /**
814
+ * @inheritDoc
815
+ */
816
+ txsWithdraw(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
817
+ return this.LpVault.txsWithdraw(new PublicKey(signer), new PublicKey(token), amount, feeRate);
818
+ }
819
+
820
+ /**
821
+ * @inheritDoc
822
+ */
823
+ txsDeposit(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
824
+ return this.LpVault.txsDeposit(new PublicKey(signer), new PublicKey(token), amount, feeRate);
825
+ }
826
+
827
+ ////////////////////////////////////////////
828
+ //// Executors
829
+ /**
830
+ * @inheritDoc
831
+ */
832
+ async claimWithSecret(
833
+ signer: SolanaSigner,
834
+ swapData: SolanaSwapData,
835
+ secret: string,
836
+ checkExpiry?: boolean,
837
+ initAta?: boolean,
838
+ txOptions?: TransactionConfirmationOptions
839
+ ): Promise<string> {
840
+ const result = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, checkExpiry, initAta, txOptions?.feeRate);
841
+ const [signature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
842
+ return signature;
843
+ }
844
+
845
+ /**
846
+ * @inheritDoc
847
+ */
848
+ async claimWithTxData(
849
+ signer: SolanaSigner,
850
+ swapData: SolanaSwapData,
851
+ tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
852
+ requiredConfirmations: number,
853
+ vout: number,
854
+ commitedHeader?: SolanaBtcStoredHeader,
855
+ synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
856
+ initAta?: boolean,
857
+ txOptions?: TransactionConfirmationOptions
858
+ ): Promise<string> {
859
+ if(requiredConfirmations!==swapData.confirmations) throw new Error("Invalid requiredConfirmations provided!");
860
+
861
+ const {txs, claimTxIndex, storageAcc} = await this.Claim.txsClaimWithTxData(
862
+ signer, swapData, tx, vout,
863
+ commitedHeader, synchronizer, initAta, txOptions?.feeRate
864
+ );
865
+
866
+ const signatures = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal);
867
+ await this._DataAccount.removeDataAccount(storageAcc);
868
+
869
+ return signatures[claimTxIndex] ?? signatures[0];
870
+ }
871
+
872
+ /**
873
+ * @inheritDoc
874
+ */
875
+ async refund(
876
+ signer: SolanaSigner,
877
+ swapData: SolanaSwapData,
878
+ check?: boolean,
879
+ initAta?: boolean,
880
+ txOptions?: TransactionConfirmationOptions
881
+ ): Promise<string> {
882
+ let result = await this.txsRefund(signer.getAddress(), swapData, check, initAta, txOptions?.feeRate);
883
+
884
+ const [signature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
885
+
886
+ return signature;
887
+ }
888
+
889
+ /**
890
+ * @inheritDoc
891
+ */
892
+ async refundWithAuthorization(
893
+ signer: SolanaSigner,
894
+ swapData: SolanaSwapData,
895
+ signature: SignatureData,
896
+ check?: boolean,
897
+ initAta?: boolean,
898
+ txOptions?: TransactionConfirmationOptions
899
+ ): Promise<string> {
900
+ let result = await this.txsRefundWithAuthorization(signer.getAddress(), swapData, signature, check, initAta, txOptions?.feeRate);
901
+
902
+ const [txSignature] = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
903
+
904
+ return txSignature;
905
+ }
906
+
907
+ /**
908
+ * @inheritDoc
909
+ */
910
+ async init(
911
+ signer: SolanaSigner,
912
+ swapData: SolanaSwapData,
913
+ signature: SignatureData,
914
+ skipChecks?: boolean,
915
+ txOptions?: TransactionConfirmationOptions
916
+ ): Promise<string> {
917
+ const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
918
+
919
+ const signatures = await this._Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
920
+
921
+ return signatures[signatures.length-1];
922
+ }
923
+
924
+ /**
925
+ * @inheritDoc
926
+ */
927
+ async initAndClaimWithSecret(
928
+ signer: SolanaSigner,
929
+ swapData: SolanaSwapData,
930
+ signature: SignatureData,
931
+ secret: string,
932
+ skipChecks?: boolean,
933
+ txOptions?: TransactionConfirmationOptions
934
+ ): Promise<string[]> {
935
+ if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
936
+
937
+ const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
938
+ let txsClaim: SolanaTx[];
939
+ if(isSwapProgramV1(this.program)) {
940
+ // In V1 the initialize instruction has to contain the ATA initialization, hence we can skip checking it in claim
941
+ txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
942
+ } else {
943
+ // In V2 the initialize instruction doesn't necessarily contain the ATA initialization, hence the claim instruction needs to
944
+ // check and initialize the ATA if needed
945
+ txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, true, txOptions?.feeRate, false);
946
+ }
947
+
948
+ const signatures = await this._Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
949
+ return [signatures[txsCommit.length-1], signatures[signatures.length-1]]
950
+ }
951
+
952
+ /**
953
+ * @inheritDoc
954
+ */
955
+ async withdraw(
956
+ signer: SolanaSigner,
957
+ token: string,
958
+ amount: bigint,
959
+ txOptions?: TransactionConfirmationOptions
960
+ ): Promise<string> {
961
+ const txs = await this.LpVault.txsWithdraw(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
962
+ const [txId] = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
963
+ return txId;
964
+ }
965
+
966
+ /**
967
+ * @inheritDoc
968
+ */
969
+ async deposit(
970
+ signer: SolanaSigner,
971
+ token: string,
972
+ amount: bigint,
973
+ txOptions?: TransactionConfirmationOptions
974
+ ): Promise<string> {
975
+ const txs = await this.LpVault.txsDeposit(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
976
+ const [txId] = await this._Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
977
+ return txId;
978
+ }
979
+
980
+ ////////////////////////////////////////////
981
+ //// Fees
982
+ /**
983
+ * @inheritDoc
984
+ */
985
+ getInitPayInFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
986
+ const paymentHash = claimHash==null ? undefined : fromClaimHash(claimHash).paymentHash;
987
+ return this.Init.getInitPayInFeeRate(
988
+ toPublicKeyOrNull(offerer),
989
+ toPublicKeyOrNull(claimer),
990
+ toPublicKeyOrNull(token),
991
+ paymentHash
992
+ );
993
+ }
994
+
995
+ /**
996
+ * @inheritDoc
997
+ */
998
+ getInitFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
999
+ const paymentHash = claimHash==null ? undefined : fromClaimHash(claimHash).paymentHash;
1000
+ return this.Init.getInitFeeRate(
1001
+ toPublicKeyOrNull(offerer),
1002
+ toPublicKeyOrNull(claimer),
1003
+ toPublicKeyOrNull(token),
1004
+ paymentHash
1005
+ );
1006
+ }
1007
+
1008
+ /**
1009
+ * @inheritDoc
1010
+ */
1011
+ getRefundFeeRate(swapData: SolanaSwapData): Promise<string> {
1012
+ return this.Refund.getRefundFeeRate(swapData);
1013
+ }
1014
+
1015
+ /**
1016
+ * @inheritDoc
1017
+ */
1018
+ getClaimFeeRate(signer: string, swapData: SolanaSwapData): Promise<string> {
1019
+ return this.Claim.getClaimFeeRate(new PublicKey(signer), swapData);
1020
+ }
1021
+
1022
+ /**
1023
+ * @inheritDoc
1024
+ */
1025
+ getClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1026
+ return this.Claim.getClaimFee(new PublicKey(signer), swapData, feeRate);
1027
+ }
1028
+
1029
+ /**
1030
+ * @inheritDoc
1031
+ */
1032
+ getRawClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1033
+ return this.Claim.getRawClaimFee(new PublicKey(signer), swapData, feeRate);
1034
+ }
1035
+
1036
+ /**
1037
+ * @inheritDoc
1038
+ */
1039
+ getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1040
+ return this.Init.getInitFee(swapData, feeRate);
1041
+ }
1042
+
1043
+ /**
1044
+ * @inheritDoc
1045
+ */
1046
+ getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1047
+ return this.Init.getRawInitFee(swapData, feeRate);
1048
+ }
1049
+
1050
+ /**
1051
+ * @inheritDoc
1052
+ */
1053
+ getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1054
+ return this.Refund.getRefundFee(swapData, feeRate);
1055
+ }
1056
+
1057
+ /**
1058
+ * @inheritDoc
1059
+ */
1060
+ getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
1061
+ return this.Refund.getRawRefundFee(swapData, feeRate);
1062
+ }
1063
+
1064
+ /**
1065
+ * @inheritDoc
1066
+ */
1067
+ getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
1068
+ return Buffer.from(sha256(Buffer.concat([
1069
+ BigIntBufferUtils.toBuffer(amount, "le", 8),
1070
+ outputScript
1071
+ ])));
1072
+ }
1073
+
1074
+ }