@atomiqlabs/chain-solana 10.0.0-dev.3 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +201 -201
  2. package/dist/index.d.ts +29 -29
  3. package/dist/index.js +45 -45
  4. package/dist/solana/SolanaChainType.d.ts +10 -10
  5. package/dist/solana/SolanaChainType.js +2 -2
  6. package/dist/solana/SolanaChains.d.ts +20 -20
  7. package/dist/solana/SolanaChains.js +25 -25
  8. package/dist/solana/SolanaInitializer.d.ts +18 -18
  9. package/dist/solana/SolanaInitializer.js +63 -63
  10. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
  11. package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
  12. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
  13. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
  14. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
  15. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
  16. package/dist/solana/btcrelay/program/programIdl.json +671 -671
  17. package/dist/solana/chain/SolanaAction.d.ts +26 -26
  18. package/dist/solana/chain/SolanaAction.js +86 -86
  19. package/dist/solana/chain/SolanaChainInterface.d.ts +58 -58
  20. package/dist/solana/chain/SolanaChainInterface.js +112 -112
  21. package/dist/solana/chain/SolanaModule.d.ts +14 -14
  22. package/dist/solana/chain/SolanaModule.js +13 -13
  23. package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
  24. package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
  25. package/dist/solana/chain/modules/SolanaBlocks.d.ts +28 -28
  26. package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
  27. package/dist/solana/chain/modules/SolanaEvents.d.ts +25 -25
  28. package/dist/solana/chain/modules/SolanaEvents.js +58 -58
  29. package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
  30. package/dist/solana/chain/modules/SolanaFees.js +379 -379
  31. package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
  32. package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
  33. package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
  34. package/dist/solana/chain/modules/SolanaSlots.js +68 -68
  35. package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
  36. package/dist/solana/chain/modules/SolanaTokens.js +248 -248
  37. package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
  38. package/dist/solana/chain/modules/SolanaTransactions.js +332 -332
  39. package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
  40. package/dist/solana/events/SolanaChainEvents.js +256 -256
  41. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -85
  42. package/dist/solana/events/SolanaChainEventsBrowser.js +194 -194
  43. package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
  44. package/dist/solana/program/SolanaProgramBase.js +43 -43
  45. package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
  46. package/dist/solana/program/SolanaProgramModule.js +11 -11
  47. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +59 -59
  48. package/dist/solana/program/modules/SolanaProgramEvents.js +103 -103
  49. package/dist/solana/swaps/SolanaSwapData.d.ts +59 -59
  50. package/dist/solana/swaps/SolanaSwapData.js +267 -267
  51. package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
  52. package/dist/solana/swaps/SolanaSwapModule.js +11 -11
  53. package/dist/solana/swaps/SolanaSwapProgram.d.ts +202 -202
  54. package/dist/solana/swaps/SolanaSwapProgram.js +470 -463
  55. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
  56. package/dist/solana/swaps/SwapTypeEnum.js +42 -42
  57. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
  58. package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
  59. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
  60. package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
  61. package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
  62. package/dist/solana/swaps/modules/SwapClaim.js +291 -291
  63. package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
  64. package/dist/solana/swaps/modules/SwapInit.js +519 -519
  65. package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
  66. package/dist/solana/swaps/modules/SwapRefund.js +252 -252
  67. package/dist/solana/swaps/programIdl.json +945 -945
  68. package/dist/solana/swaps/programTypes.d.ts +943 -943
  69. package/dist/solana/swaps/programTypes.js +945 -945
  70. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -9
  71. package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
  72. package/dist/solana/wallet/SolanaSigner.d.ts +10 -10
  73. package/dist/solana/wallet/SolanaSigner.js +16 -16
  74. package/dist/utils/Utils.d.ts +53 -53
  75. package/dist/utils/Utils.js +170 -170
  76. package/package.json +41 -41
  77. package/src/index.ts +36 -36
  78. package/src/solana/SolanaChainType.ts +25 -25
  79. package/src/solana/SolanaChains.ts +23 -23
  80. package/src/solana/SolanaInitializer.ts +102 -102
  81. package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -588
  82. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
  83. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
  84. package/src/solana/btcrelay/program/programIdl.json +670 -670
  85. package/src/solana/chain/SolanaAction.ts +108 -108
  86. package/src/solana/chain/SolanaChainInterface.ts +174 -174
  87. package/src/solana/chain/SolanaModule.ts +20 -20
  88. package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
  89. package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
  90. package/src/solana/chain/modules/SolanaEvents.ts +56 -56
  91. package/src/solana/chain/modules/SolanaFees.ts +450 -450
  92. package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
  93. package/src/solana/chain/modules/SolanaSlots.ts +82 -82
  94. package/src/solana/chain/modules/SolanaTokens.ts +307 -307
  95. package/src/solana/chain/modules/SolanaTransactions.ts +370 -370
  96. package/src/solana/events/SolanaChainEvents.ts +299 -299
  97. package/src/solana/events/SolanaChainEventsBrowser.ts +256 -256
  98. package/src/solana/program/SolanaProgramBase.ts +79 -79
  99. package/src/solana/program/SolanaProgramModule.ts +15 -15
  100. package/src/solana/program/modules/SolanaProgramEvents.ts +140 -140
  101. package/src/solana/swaps/SolanaSwapData.ts +379 -379
  102. package/src/solana/swaps/SolanaSwapModule.ts +16 -16
  103. package/src/solana/swaps/SolanaSwapProgram.ts +697 -692
  104. package/src/solana/swaps/SwapTypeEnum.ts +29 -29
  105. package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
  106. package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
  107. package/src/solana/swaps/modules/SwapClaim.ts +389 -389
  108. package/src/solana/swaps/modules/SwapInit.ts +663 -663
  109. package/src/solana/swaps/modules/SwapRefund.ts +312 -312
  110. package/src/solana/swaps/programIdl.json +944 -944
  111. package/src/solana/swaps/programTypes.ts +1885 -1885
  112. package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
  113. package/src/solana/wallet/SolanaSigner.ts +23 -23
  114. package/src/utils/Utils.ts +180 -180
@@ -1,692 +1,697 @@
1
- import {SolanaSwapData} from "./SolanaSwapData";
2
- import {IdlAccounts} from "@coral-xyz/anchor";
3
- import {
4
- PublicKey,
5
- } from "@solana/web3.js";
6
- import {sha256} from "@noble/hashes/sha2";
7
- import {SolanaBtcRelay} from "../btcrelay/SolanaBtcRelay";
8
- import * as programIdl from "./programIdl.json";
9
- import {
10
- IStorageManager,
11
- SwapContract,
12
- ChainSwapType,
13
- IntermediaryReputationType,
14
- TransactionConfirmationOptions,
15
- SignatureData,
16
- RelaySynchronizer,
17
- BigIntBufferUtils,
18
- SwapCommitState,
19
- SwapCommitStateType, SwapNotCommitedState, SwapExpiredState, SwapPaidState
20
- } from "@atomiqlabs/base";
21
- import {SolanaBtcStoredHeader} from "../btcrelay/headers/SolanaBtcStoredHeader";
22
- import {
23
- getAssociatedTokenAddressSync,
24
- } from "@solana/spl-token";
25
- import {SwapProgram} from "./programTypes";
26
- import {SolanaChainInterface} from "../chain/SolanaChainInterface";
27
- import {SolanaProgramBase} from "../program/SolanaProgramBase";
28
- import {SolanaTx} from "../chain/modules/SolanaTransactions";
29
- import {SwapInit, SolanaPreFetchData, SolanaPreFetchVerification} from "./modules/SwapInit";
30
- import {SolanaDataAccount, StoredDataAccount} from "./modules/SolanaDataAccount";
31
- import {SwapRefund} from "./modules/SwapRefund";
32
- import {SwapClaim} from "./modules/SwapClaim";
33
- import {SolanaLpVault} from "./modules/SolanaLpVault";
34
- import {Buffer} from "buffer";
35
- import {SolanaSigner} from "../wallet/SolanaSigner";
36
- import {fromClaimHash, toBN, toClaimHash} from "../../utils/Utils";
37
- import {SolanaTokens} from "../chain/modules/SolanaTokens";
38
- import * as BN from "bn.js";
39
-
40
- function toPublicKeyOrNull(str: string | null): PublicKey | null {
41
- return str==null ? null : new PublicKey(str);
42
- }
43
-
44
- export class SolanaSwapProgram
45
- extends SolanaProgramBase<SwapProgram>
46
- implements SwapContract<
47
- SolanaSwapData,
48
- SolanaTx,
49
- SolanaPreFetchData,
50
- SolanaPreFetchVerification,
51
- SolanaSigner,
52
- "SOLANA"
53
- > {
54
-
55
- ////////////////////////
56
- //// Constants
57
- public readonly ESCROW_STATE_RENT_EXEMPT = 2658720;
58
-
59
- ////////////////////////
60
- //// PDA accessors
61
- readonly SwapVaultAuthority = this.pda("authority");
62
- readonly SwapVault = this.pda("vault", (tokenAddress: PublicKey) => [tokenAddress.toBuffer()]);
63
- readonly SwapUserVault = this.pda("uservault",
64
- (publicKey: PublicKey, tokenAddress: PublicKey) => [publicKey.toBuffer(), tokenAddress.toBuffer()]
65
- );
66
- readonly SwapEscrowState = this.pda("state", (hash: Buffer) => [hash]);
67
-
68
- ////////////////////////
69
- //// Timeouts
70
- readonly chainId: "SOLANA" = "SOLANA";
71
- readonly claimWithSecretTimeout: number = 45;
72
- readonly claimWithTxDataTimeout: number = 120;
73
- readonly refundTimeout: number = 45;
74
- readonly claimGracePeriod: number = 10*60;
75
- readonly refundGracePeriod: number = 10*60;
76
- readonly authGracePeriod: number = 5*60;
77
-
78
- ////////////////////////
79
- //// Services
80
- readonly Init: SwapInit;
81
- readonly Refund: SwapRefund;
82
- readonly Claim: SwapClaim;
83
- readonly DataAccount: SolanaDataAccount;
84
- readonly LpVault: SolanaLpVault;
85
-
86
- constructor(
87
- chainInterface: SolanaChainInterface,
88
- btcRelay: SolanaBtcRelay<any>,
89
- storage: IStorageManager<StoredDataAccount>,
90
- programAddress?: string
91
- ) {
92
- super(chainInterface, programIdl, programAddress);
93
-
94
- this.Init = new SwapInit(chainInterface, this);
95
- this.Refund = new SwapRefund(chainInterface, this);
96
- this.Claim = new SwapClaim(chainInterface, this, btcRelay);
97
- this.DataAccount = new SolanaDataAccount(chainInterface, this, storage);
98
- this.LpVault = new SolanaLpVault(chainInterface, this);
99
- }
100
-
101
- async start(): Promise<void> {
102
- await this.DataAccount.init();
103
- }
104
-
105
- getClaimableDeposits(signer: string): Promise<{count: number, totalValue: bigint}> {
106
- return this.DataAccount.getDataAccountsInfo(new PublicKey(signer));
107
- }
108
-
109
- claimDeposits(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
110
- return this.DataAccount.sweepDataAccounts(signer);
111
- }
112
-
113
- ////////////////////////////////////////////
114
- //// Signatures
115
- preFetchForInitSignatureVerification(data: SolanaPreFetchData): Promise<SolanaPreFetchVerification> {
116
- return this.Init.preFetchForInitSignatureVerification(data);
117
- }
118
-
119
- preFetchBlockDataForSignatures(): Promise<SolanaPreFetchData> {
120
- return this.Init.preFetchBlockDataForSignatures();
121
- }
122
-
123
- getInitSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number, preFetchedBlockData?: SolanaPreFetchData, feeRate?: string): Promise<SignatureData> {
124
- return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
125
- }
126
-
127
- isValidInitAuthorization(signer: string, swapData: SolanaSwapData, {timeout, prefix, signature}, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
128
- return this.Init.isSignatureValid(signer, swapData, timeout, prefix, signature, feeRate, preFetchedData);
129
- }
130
-
131
- getInitAuthorizationExpiry(swapData: SolanaSwapData, {timeout, prefix, signature}, preFetchedData?: SolanaPreFetchVerification): Promise<number> {
132
- return this.Init.getSignatureExpiry(timeout, signature, preFetchedData);
133
- }
134
-
135
- isInitAuthorizationExpired(swapData: SolanaSwapData, {timeout, prefix, signature}): Promise<boolean> {
136
- return this.Init.isSignatureExpired(signature, timeout);
137
- }
138
-
139
- getRefundSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number): Promise<SignatureData> {
140
- return this.Refund.signSwapRefund(signer, swapData, authorizationTimeout);
141
- }
142
-
143
- isValidRefundAuthorization(swapData: SolanaSwapData, {timeout, prefix, signature}): Promise<Buffer> {
144
- return this.Refund.isSignatureValid(swapData, timeout, prefix, signature);
145
- }
146
-
147
- getDataSignature(signer: SolanaSigner, data: Buffer): Promise<string> {
148
- return this.Chain.Signatures.getDataSignature(signer, data);
149
- }
150
-
151
- isValidDataSignature(data: Buffer, signature: string, publicKey: string): Promise<boolean> {
152
- return this.Chain.Signatures.isValidDataSignature(data, signature, publicKey);
153
- }
154
-
155
- ////////////////////////////////////////////
156
- //// Swap data utils
157
- /**
158
- * Checks whether the claim is claimable by us, that means not expired, we are claimer & the swap is commited
159
- *
160
- * @param signer
161
- * @param data
162
- */
163
- async isClaimable(signer: string, data: SolanaSwapData): Promise<boolean> {
164
- if(!data.isClaimer(signer)) return false;
165
- if(await this.isExpired(signer, data)) return false;
166
- return await this.isCommited(data);
167
- }
168
-
169
- /**
170
- * Checks whether a swap is commited, i.e. the swap still exists on-chain and was not claimed nor refunded
171
- *
172
- * @param swapData
173
- */
174
- async isCommited(swapData: SolanaSwapData): Promise<boolean> {
175
- const paymentHash = Buffer.from(swapData.paymentHash, "hex");
176
-
177
- const account: IdlAccounts<SwapProgram>["escrowState"] = await this.program.account.escrowState.fetchNullable(this.SwapEscrowState(paymentHash));
178
- if(account==null) return false;
179
-
180
- return swapData.correctPDA(account);
181
- }
182
-
183
- /**
184
- * Checks whether the swap is expired, takes into consideration possible on-chain time skew, therefore for claimer
185
- * the swap expires a bit sooner than it should've & for the offerer it expires a bit later
186
- *
187
- * @param signer
188
- * @param data
189
- */
190
- isExpired(signer: string, data: SolanaSwapData): Promise<boolean> {
191
- let currentTimestamp: BN = new BN(Math.floor(Date.now()/1000));
192
- if(data.isClaimer(signer)) currentTimestamp = currentTimestamp.add(new BN(this.claimGracePeriod));
193
- if(data.isOfferer(signer)) currentTimestamp = currentTimestamp.sub(new BN(this.refundGracePeriod));
194
- return Promise.resolve(data.expiry.lt(currentTimestamp));
195
- }
196
-
197
- /**
198
- * Checks if the swap is refundable by us, checks if we are offerer, if the swap is already expired & if the swap
199
- * is still commited
200
- *
201
- * @param signer
202
- * @param data
203
- */
204
- async isRequestRefundable(signer: string, data: SolanaSwapData): Promise<boolean> {
205
- //Swap can only be refunded by the offerer
206
- if(!data.isOfferer(signer)) return false;
207
- if(!(await this.isExpired(signer, data))) return false;
208
- return await this.isCommited(data);
209
- }
210
-
211
- /**
212
- * Get the swap payment hash to be used for an on-chain swap, this just uses a sha256 hash of the values
213
- *
214
- * @param outputScript output script required to claim the swap
215
- * @param amount sats sent required to claim the swap
216
- * @param confirmations
217
- * @param nonce swap nonce uniquely identifying the transaction to prevent replay attacks
218
- */
219
- getHashForOnchain(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
220
- nonce ??= 0n;
221
- const paymentHash = Buffer.from(sha256(Buffer.concat([
222
- BigIntBufferUtils.toBuffer(nonce, "le", 8),
223
- BigIntBufferUtils.toBuffer(amount, "le", 8),
224
- outputScript
225
- ]))).toString("hex");
226
- return Buffer.from(toClaimHash(paymentHash, nonce, confirmations), "hex");
227
- }
228
-
229
- getHashForHtlc(swapHash: Buffer): Buffer {
230
- return Buffer.from(toClaimHash(
231
- swapHash.toString("hex"),
232
- 0n,
233
- 0
234
- ), "hex");
235
- }
236
-
237
- getHashForTxId(txId: string, confirmations: number): Buffer {
238
- return Buffer.from(toClaimHash(
239
- Buffer.from(txId, "hex").reverse().toString("hex"),
240
- 0n,
241
- confirmations
242
- ), "hex");
243
- }
244
-
245
- ////////////////////////////////////////////
246
- //// Swap data getters
247
- /**
248
- * Gets the status of the specific swap, this also checks if we are offerer/claimer & checks for expiry (to see
249
- * if swap is refundable)
250
- *
251
- * @param signer
252
- * @param data
253
- */
254
- async getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState> {
255
- const escrowStateKey = this.SwapEscrowState(Buffer.from(data.paymentHash, "hex"));
256
- const [escrowState, isExpired] = await Promise.all([
257
- this.program.account.escrowState.fetchNullable(escrowStateKey) as Promise<IdlAccounts<SwapProgram>["escrowState"]>,
258
- this.isExpired(signer,data)
259
- ]);
260
-
261
- if(escrowState!=null) {
262
- if(data.correctPDA(escrowState)) {
263
- if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.REFUNDABLE};
264
- return {type: SwapCommitStateType.COMMITED};
265
- }
266
-
267
- if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.EXPIRED};
268
- return {type: SwapCommitStateType.NOT_COMMITED};
269
- }
270
-
271
- //Check if paid or what
272
- const status: SwapNotCommitedState | SwapExpiredState | SwapPaidState = await this.Events.findInEvents(escrowStateKey, async (event, info) => {
273
- if(event.name==="ClaimEvent") {
274
- if(!event.data.sequence.eq(data.sequence)) return null;
275
- return {
276
- type: SwapCommitStateType.PAID,
277
- getClaimTxId: () => Promise.resolve(info.signature),
278
- getTxBlock: async () => {
279
- return {
280
- blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
281
- blockTime: info.blockTime
282
- };
283
- }
284
- }
285
- }
286
- if(event.name==="RefundEvent") {
287
- if(!event.data.sequence.eq(data.sequence)) return null;
288
- return {
289
- type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
290
- getRefundTxId: () => Promise.resolve(info.signature),
291
- getTxBlock: async () => {
292
- return {
293
- blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
294
- blockTime: info.blockTime
295
- };
296
- }
297
- };
298
- }
299
- });
300
- if(status!=null) return status;
301
-
302
- if(isExpired) return {type: SwapCommitStateType.EXPIRED};
303
- return {type: SwapCommitStateType.NOT_COMMITED};
304
- }
305
-
306
- /**
307
- * Checks the status of the specific payment hash
308
- *
309
- * @param claimHash
310
- */
311
- async getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType> {
312
- const {paymentHash} = fromClaimHash(claimHash);
313
- const escrowStateKey = this.SwapEscrowState(Buffer.from(paymentHash, "hex"));
314
- const abortController = new AbortController();
315
-
316
- //Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
317
- const eventsPromise = this.Events.findInEvents(escrowStateKey, async (event) => {
318
- if(event.name==="ClaimEvent") return SwapCommitStateType.PAID;
319
- if(event.name==="RefundEvent") return SwapCommitStateType.NOT_COMMITED;
320
- }, abortController.signal).catch(e => {
321
- abortController.abort(e)
322
- return null;
323
- });
324
-
325
- const escrowState = await this.program.account.escrowState.fetchNullable(escrowStateKey);
326
- abortController.signal.throwIfAborted();
327
- if(escrowState!=null) {
328
- abortController.abort();
329
- return SwapCommitStateType.COMMITED;
330
- }
331
-
332
- //Check if paid or what
333
- const eventsStatus = await eventsPromise;
334
- abortController.signal.throwIfAborted();
335
- if(eventsStatus!=null) return eventsStatus;
336
-
337
- return SwapCommitStateType.NOT_COMMITED;
338
- }
339
-
340
- /**
341
- * Returns the data committed for a specific payment hash, or null if no data is currently commited for
342
- * the specific swap
343
- *
344
- * @param claimHashHex
345
- */
346
- async getCommitedData(claimHashHex: string): Promise<SolanaSwapData> {
347
- const {paymentHash} = fromClaimHash(claimHashHex);
348
- const paymentHashBuffer = Buffer.from(paymentHash, "hex");
349
-
350
- const account: IdlAccounts<SwapProgram>["escrowState"] = await this.program.account.escrowState.fetchNullable(this.SwapEscrowState(paymentHashBuffer));
351
- if(account==null) return null;
352
-
353
- return SolanaSwapData.fromEscrowState(account);
354
- }
355
-
356
- ////////////////////////////////////////////
357
- //// Swap data initializer
358
- createSwapData(
359
- type: ChainSwapType,
360
- offerer: string,
361
- claimer: string,
362
- token: string,
363
- amount: bigint,
364
- claimHash: string,
365
- sequence: bigint,
366
- expiry: bigint,
367
- payIn: boolean,
368
- payOut: boolean,
369
- securityDeposit: bigint,
370
- claimerBounty: bigint,
371
- depositToken?: string
372
- ): Promise<SolanaSwapData> {
373
- if(depositToken!=null) {
374
- if(!new PublicKey(depositToken).equals(SolanaTokens.WSOL_ADDRESS)) throw new Error("Only SOL supported as deposit token!");
375
- }
376
- const tokenAddr: PublicKey = new PublicKey(token);
377
- const offererKey = offerer==null ? null : new PublicKey(offerer);
378
- const claimerKey = claimer==null ? null : new PublicKey(claimer);
379
- const {paymentHash, nonce, confirmations} = fromClaimHash(claimHash);
380
- return Promise.resolve(new SolanaSwapData(
381
- offererKey,
382
- claimerKey,
383
- tokenAddr,
384
- toBN(amount),
385
- paymentHash,
386
- toBN(sequence),
387
- toBN(expiry),
388
- nonce,
389
- confirmations,
390
- payOut,
391
- type==null ? null : SolanaSwapData.typeToKind(type),
392
- payIn,
393
- offererKey==null ? null : payIn ? getAssociatedTokenAddressSync(tokenAddr, offererKey) : PublicKey.default,
394
- claimerKey==null ? null : payOut ? getAssociatedTokenAddressSync(tokenAddr, claimerKey) : PublicKey.default,
395
- toBN(securityDeposit),
396
- toBN(claimerBounty),
397
- null
398
- ));
399
- }
400
-
401
- ////////////////////////////////////////////
402
- //// Utils
403
- async getBalance(signer: string, tokenAddress: string, inContract: boolean): Promise<bigint> {
404
- if(!inContract) {
405
- return await this.Chain.getBalance(signer, tokenAddress);
406
- }
407
-
408
- const token = new PublicKey(tokenAddress);
409
- const publicKey = new PublicKey(signer);
410
-
411
- return await this.getIntermediaryBalance(publicKey, token);
412
- }
413
-
414
- getIntermediaryData(address: string, token: string): Promise<{
415
- balance: bigint,
416
- reputation: IntermediaryReputationType
417
- }> {
418
- return this.LpVault.getIntermediaryData(new PublicKey(address), new PublicKey(token));
419
- }
420
-
421
- getIntermediaryReputation(address: string, token: string): Promise<IntermediaryReputationType> {
422
- return this.LpVault.getIntermediaryReputation(new PublicKey(address), new PublicKey(token));
423
- }
424
-
425
- getIntermediaryBalance(address: PublicKey, token: PublicKey): Promise<bigint> {
426
- return this.LpVault.getIntermediaryBalance(address, token);
427
- }
428
-
429
- ////////////////////////////////////////////
430
- //// Transaction initializers
431
- async txsClaimWithSecret(
432
- signer: string | SolanaSigner,
433
- swapData: SolanaSwapData,
434
- secret: string,
435
- checkExpiry?: boolean,
436
- initAta?: boolean,
437
- feeRate?: string,
438
- skipAtaCheck?: boolean
439
- ): Promise<SolanaTx[]> {
440
- return this.Claim.txsClaimWithSecret(typeof(signer)==="string" ? new PublicKey(signer) : signer.getPublicKey(), swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck)
441
- }
442
-
443
- async txsClaimWithTxData(
444
- signer: string | SolanaSigner,
445
- swapData: SolanaSwapData,
446
- tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
447
- requiredConfirmations: number,
448
- vout: number,
449
- commitedHeader?: SolanaBtcStoredHeader,
450
- synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
451
- initAta?: boolean,
452
- feeRate?: string,
453
- storageAccHolder?: {storageAcc: PublicKey}
454
- ): Promise<SolanaTx[] | null> {
455
- if(swapData.confirmations!==requiredConfirmations) throw new Error("Invalid requiredConfirmations provided!");
456
- return this.Claim.txsClaimWithTxData(typeof(signer)==="string" ? new PublicKey(signer) : signer, swapData, tx, vout, commitedHeader, synchronizer, initAta, storageAccHolder, feeRate);
457
- }
458
-
459
- txsRefund(signer: string, swapData: SolanaSwapData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
460
- if(!swapData.isOfferer(signer)) throw new Error("Only offerer can refund on Solana");
461
- return this.Refund.txsRefund(swapData, check, initAta, feeRate);
462
- }
463
-
464
- txsRefundWithAuthorization(signer: string, swapData: SolanaSwapData, {timeout, prefix, signature}, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
465
- if(!swapData.isOfferer(signer)) throw new Error("Only offerer can refund on Solana");
466
- return this.Refund.txsRefundWithAuthorization(swapData,timeout,prefix,signature,check,initAta,feeRate);
467
- }
468
-
469
- txsInit(sender: string, swapData: SolanaSwapData, {timeout, prefix, signature}, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
470
- if(swapData.isPayIn()) {
471
- if(!swapData.isOfferer(sender)) throw new Error("Only offerer can create payIn=true swap");
472
- return this.Init.txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate);
473
- } else {
474
- if(!swapData.isClaimer(sender)) throw new Error("Only claimer can create payIn=false swap");
475
- return this.Init.txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate);
476
- }
477
- }
478
-
479
- txsWithdraw(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
480
- return this.LpVault.txsWithdraw(new PublicKey(signer), new PublicKey(token), amount, feeRate);
481
- }
482
-
483
- txsDeposit(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
484
- return this.LpVault.txsDeposit(new PublicKey(signer), new PublicKey(token), amount, feeRate);
485
- }
486
-
487
- ////////////////////////////////////////////
488
- //// Executors
489
- async claimWithSecret(
490
- signer: SolanaSigner,
491
- swapData: SolanaSwapData,
492
- secret: string,
493
- checkExpiry?: boolean,
494
- initAta?: boolean,
495
- txOptions?: TransactionConfirmationOptions
496
- ): Promise<string> {
497
- const result = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, checkExpiry, initAta, txOptions?.feeRate);
498
- const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
499
- return signature;
500
- }
501
-
502
- async claimWithTxData(
503
- signer: SolanaSigner,
504
- swapData: SolanaSwapData,
505
- tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
506
- requiredConfirmations: number,
507
- vout: number,
508
- commitedHeader?: SolanaBtcStoredHeader,
509
- synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
510
- initAta?: boolean,
511
- txOptions?: TransactionConfirmationOptions
512
- ): Promise<string> {
513
- if(requiredConfirmations!==swapData.confirmations) throw new Error("Invalid requiredConfirmations provided!");
514
-
515
- const data: {storageAcc: PublicKey} = {
516
- storageAcc: null
517
- };
518
-
519
- const txs = await this.Claim.txsClaimWithTxData(
520
- signer, swapData, tx, vout,
521
- commitedHeader, synchronizer, initAta, data, txOptions?.feeRate
522
- );
523
- if(txs===null) throw new Error("Btc relay not synchronized to required blockheight!");
524
-
525
- //TODO: This doesn't return proper tx signature
526
- const [signature] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal);
527
- await this.DataAccount.removeDataAccount(data.storageAcc);
528
-
529
- return signature;
530
- }
531
-
532
- async refund(
533
- signer: SolanaSigner,
534
- swapData: SolanaSwapData,
535
- check?: boolean,
536
- initAta?: boolean,
537
- txOptions?: TransactionConfirmationOptions
538
- ): Promise<string> {
539
- let result = await this.txsRefund(signer.getAddress(), swapData, check, initAta, txOptions?.feeRate);
540
-
541
- const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
542
-
543
- return signature;
544
- }
545
-
546
- async refundWithAuthorization(
547
- signer: SolanaSigner,
548
- swapData: SolanaSwapData,
549
- signature: SignatureData,
550
- check?: boolean,
551
- initAta?: boolean,
552
- txOptions?: TransactionConfirmationOptions
553
- ): Promise<string> {
554
- let result = await this.txsRefundWithAuthorization(signer.getAddress(), swapData, signature, check, initAta, txOptions?.feeRate);
555
-
556
- const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
557
-
558
- return txSignature;
559
- }
560
-
561
- async init(
562
- signer: SolanaSigner,
563
- swapData: SolanaSwapData,
564
- signature: SignatureData,
565
- skipChecks?: boolean,
566
- txOptions?: TransactionConfirmationOptions
567
- ): Promise<string> {
568
- if(swapData.isPayIn()) {
569
- if(!signer.getPublicKey().equals(swapData.offerer)) throw new Error("Invalid signer provided!");
570
- } else {
571
- if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
572
- }
573
-
574
- const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
575
-
576
- const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
577
-
578
- return txSignature;
579
- }
580
-
581
- async initAndClaimWithSecret(
582
- signer: SolanaSigner,
583
- swapData: SolanaSwapData,
584
- signature: SignatureData,
585
- secret: string,
586
- skipChecks?: boolean,
587
- txOptions?: TransactionConfirmationOptions
588
- ): Promise<string[]> {
589
- if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
590
-
591
- const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
592
- const txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
593
-
594
- return await this.Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
595
- }
596
-
597
- async withdraw(
598
- signer: SolanaSigner,
599
- token: string,
600
- amount: bigint,
601
- txOptions?: TransactionConfirmationOptions
602
- ): Promise<string> {
603
- const txs = await this.LpVault.txsWithdraw(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
604
- const [txId] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
605
- return txId;
606
- }
607
-
608
- async deposit(
609
- signer: SolanaSigner,
610
- token: string,
611
- amount: bigint,
612
- txOptions?: TransactionConfirmationOptions
613
- ): Promise<string> {
614
- const txs = await this.LpVault.txsDeposit(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
615
- const [txId] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
616
- return txId;
617
- }
618
-
619
- ////////////////////////////////////////////
620
- //// Fees
621
- getInitPayInFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
622
- const paymentHash = claimHash==null ? null : fromClaimHash(claimHash).paymentHash;
623
- return this.Init.getInitPayInFeeRate(
624
- toPublicKeyOrNull(offerer),
625
- toPublicKeyOrNull(claimer),
626
- toPublicKeyOrNull(token),
627
- paymentHash
628
- );
629
- }
630
-
631
- getInitFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
632
- const paymentHash = claimHash==null ? null : fromClaimHash(claimHash).paymentHash;
633
- return this.Init.getInitFeeRate(
634
- toPublicKeyOrNull(offerer),
635
- toPublicKeyOrNull(claimer),
636
- toPublicKeyOrNull(token),
637
- paymentHash
638
- );
639
- }
640
-
641
- getRefundFeeRate(swapData: SolanaSwapData): Promise<string> {
642
- return this.Refund.getRefundFeeRate(swapData);
643
- }
644
-
645
- getClaimFeeRate(signer: string, swapData: SolanaSwapData): Promise<string> {
646
- return this.Claim.getClaimFeeRate(new PublicKey(signer), swapData);
647
- }
648
-
649
- getClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
650
- return this.Claim.getClaimFee(new PublicKey(signer), swapData, feeRate);
651
- }
652
-
653
- getRawClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
654
- return this.Claim.getRawClaimFee(new PublicKey(signer), swapData, feeRate);
655
- }
656
-
657
- /**
658
- * Get the estimated solana fee of the commit transaction
659
- */
660
- getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
661
- return this.Init.getInitFee(swapData, feeRate);
662
- }
663
-
664
- /**
665
- * Get the estimated solana fee of the commit transaction, without any deposits
666
- */
667
- getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
668
- return this.Init.getRawInitFee(swapData, feeRate);
669
- }
670
-
671
- /**
672
- * Get the estimated solana transaction fee of the refund transaction
673
- */
674
- getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
675
- return this.Refund.getRefundFee(swapData, feeRate);
676
- }
677
-
678
- /**
679
- * Get the estimated solana transaction fee of the refund transaction
680
- */
681
- getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
682
- return this.Refund.getRawRefundFee(swapData, feeRate);
683
- }
684
-
685
- getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
686
- return Buffer.from(sha256(Buffer.concat([
687
- BigIntBufferUtils.toBuffer(amount, "le", 8),
688
- outputScript
689
- ])));
690
- }
691
-
692
- }
1
+ import {SolanaSwapData} from "./SolanaSwapData";
2
+ import {IdlAccounts} from "@coral-xyz/anchor";
3
+ import {
4
+ PublicKey,
5
+ } from "@solana/web3.js";
6
+ import {sha256} from "@noble/hashes/sha2";
7
+ import {SolanaBtcRelay} from "../btcrelay/SolanaBtcRelay";
8
+ import * as programIdl from "./programIdl.json";
9
+ import {
10
+ IStorageManager,
11
+ SwapContract,
12
+ ChainSwapType,
13
+ IntermediaryReputationType,
14
+ TransactionConfirmationOptions,
15
+ SignatureData,
16
+ RelaySynchronizer,
17
+ BigIntBufferUtils,
18
+ SwapCommitState,
19
+ SwapCommitStateType, SwapNotCommitedState, SwapExpiredState, SwapPaidState
20
+ } from "@atomiqlabs/base";
21
+ import {SolanaBtcStoredHeader} from "../btcrelay/headers/SolanaBtcStoredHeader";
22
+ import {
23
+ getAssociatedTokenAddressSync,
24
+ } from "@solana/spl-token";
25
+ import {SwapProgram} from "./programTypes";
26
+ import {SolanaChainInterface} from "../chain/SolanaChainInterface";
27
+ import {SolanaProgramBase} from "../program/SolanaProgramBase";
28
+ import {SolanaTx} from "../chain/modules/SolanaTransactions";
29
+ import {SwapInit, SolanaPreFetchData, SolanaPreFetchVerification} from "./modules/SwapInit";
30
+ import {SolanaDataAccount, StoredDataAccount} from "./modules/SolanaDataAccount";
31
+ import {SwapRefund} from "./modules/SwapRefund";
32
+ import {SwapClaim} from "./modules/SwapClaim";
33
+ import {SolanaLpVault} from "./modules/SolanaLpVault";
34
+ import {Buffer} from "buffer";
35
+ import {SolanaSigner} from "../wallet/SolanaSigner";
36
+ import {fromClaimHash, toBN, toClaimHash, toEscrowHash} from "../../utils/Utils";
37
+ import {SolanaTokens} from "../chain/modules/SolanaTokens";
38
+ import * as BN from "bn.js";
39
+
40
+ function toPublicKeyOrNull(str: string | null): PublicKey | null {
41
+ return str==null ? null : new PublicKey(str);
42
+ }
43
+
44
+ export class SolanaSwapProgram
45
+ extends SolanaProgramBase<SwapProgram>
46
+ implements SwapContract<
47
+ SolanaSwapData,
48
+ SolanaTx,
49
+ SolanaPreFetchData,
50
+ SolanaPreFetchVerification,
51
+ SolanaSigner,
52
+ "SOLANA"
53
+ > {
54
+
55
+ ////////////////////////
56
+ //// Constants
57
+ public readonly ESCROW_STATE_RENT_EXEMPT = 2658720;
58
+
59
+ ////////////////////////
60
+ //// PDA accessors
61
+ readonly SwapVaultAuthority = this.pda("authority");
62
+ readonly SwapVault = this.pda("vault", (tokenAddress: PublicKey) => [tokenAddress.toBuffer()]);
63
+ readonly SwapUserVault = this.pda("uservault",
64
+ (publicKey: PublicKey, tokenAddress: PublicKey) => [publicKey.toBuffer(), tokenAddress.toBuffer()]
65
+ );
66
+ readonly SwapEscrowState = this.pda("state", (hash: Buffer) => [hash]);
67
+
68
+ ////////////////////////
69
+ //// Timeouts
70
+ readonly chainId: "SOLANA" = "SOLANA";
71
+ readonly claimWithSecretTimeout: number = 45;
72
+ readonly claimWithTxDataTimeout: number = 120;
73
+ readonly refundTimeout: number = 45;
74
+ readonly claimGracePeriod: number = 10*60;
75
+ readonly refundGracePeriod: number = 10*60;
76
+ readonly authGracePeriod: number = 5*60;
77
+
78
+ ////////////////////////
79
+ //// Services
80
+ readonly Init: SwapInit;
81
+ readonly Refund: SwapRefund;
82
+ readonly Claim: SwapClaim;
83
+ readonly DataAccount: SolanaDataAccount;
84
+ readonly LpVault: SolanaLpVault;
85
+
86
+ constructor(
87
+ chainInterface: SolanaChainInterface,
88
+ btcRelay: SolanaBtcRelay<any>,
89
+ storage: IStorageManager<StoredDataAccount>,
90
+ programAddress?: string
91
+ ) {
92
+ super(chainInterface, programIdl, programAddress);
93
+
94
+ this.Init = new SwapInit(chainInterface, this);
95
+ this.Refund = new SwapRefund(chainInterface, this);
96
+ this.Claim = new SwapClaim(chainInterface, this, btcRelay);
97
+ this.DataAccount = new SolanaDataAccount(chainInterface, this, storage);
98
+ this.LpVault = new SolanaLpVault(chainInterface, this);
99
+ }
100
+
101
+ async start(): Promise<void> {
102
+ await this.DataAccount.init();
103
+ }
104
+
105
+ getClaimableDeposits(signer: string): Promise<{count: number, totalValue: bigint}> {
106
+ return this.DataAccount.getDataAccountsInfo(new PublicKey(signer));
107
+ }
108
+
109
+ claimDeposits(signer: SolanaSigner): Promise<{txIds: string[], count: number, totalValue: bigint}> {
110
+ return this.DataAccount.sweepDataAccounts(signer);
111
+ }
112
+
113
+ ////////////////////////////////////////////
114
+ //// Signatures
115
+ preFetchForInitSignatureVerification(data: SolanaPreFetchData): Promise<SolanaPreFetchVerification> {
116
+ return this.Init.preFetchForInitSignatureVerification(data);
117
+ }
118
+
119
+ preFetchBlockDataForSignatures(): Promise<SolanaPreFetchData> {
120
+ return this.Init.preFetchBlockDataForSignatures();
121
+ }
122
+
123
+ getInitSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number, preFetchedBlockData?: SolanaPreFetchData, feeRate?: string): Promise<SignatureData> {
124
+ return this.Init.signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate);
125
+ }
126
+
127
+ isValidInitAuthorization(signer: string, swapData: SolanaSwapData, {timeout, prefix, signature}, feeRate?: string, preFetchedData?: SolanaPreFetchVerification): Promise<Buffer> {
128
+ return this.Init.isSignatureValid(signer, swapData, timeout, prefix, signature, feeRate, preFetchedData);
129
+ }
130
+
131
+ getInitAuthorizationExpiry(swapData: SolanaSwapData, {timeout, prefix, signature}, preFetchedData?: SolanaPreFetchVerification): Promise<number> {
132
+ return this.Init.getSignatureExpiry(timeout, signature, preFetchedData);
133
+ }
134
+
135
+ isInitAuthorizationExpired(swapData: SolanaSwapData, {timeout, prefix, signature}): Promise<boolean> {
136
+ return this.Init.isSignatureExpired(signature, timeout);
137
+ }
138
+
139
+ getRefundSignature(signer: SolanaSigner, swapData: SolanaSwapData, authorizationTimeout: number): Promise<SignatureData> {
140
+ return this.Refund.signSwapRefund(signer, swapData, authorizationTimeout);
141
+ }
142
+
143
+ isValidRefundAuthorization(swapData: SolanaSwapData, {timeout, prefix, signature}): Promise<Buffer> {
144
+ return this.Refund.isSignatureValid(swapData, timeout, prefix, signature);
145
+ }
146
+
147
+ getDataSignature(signer: SolanaSigner, data: Buffer): Promise<string> {
148
+ return this.Chain.Signatures.getDataSignature(signer, data);
149
+ }
150
+
151
+ isValidDataSignature(data: Buffer, signature: string, publicKey: string): Promise<boolean> {
152
+ return this.Chain.Signatures.isValidDataSignature(data, signature, publicKey);
153
+ }
154
+
155
+ ////////////////////////////////////////////
156
+ //// Swap data utils
157
+ /**
158
+ * Checks whether the claim is claimable by us, that means not expired, we are claimer & the swap is commited
159
+ *
160
+ * @param signer
161
+ * @param data
162
+ */
163
+ async isClaimable(signer: string, data: SolanaSwapData): Promise<boolean> {
164
+ if(!data.isClaimer(signer)) return false;
165
+ if(await this.isExpired(signer, data)) return false;
166
+ return await this.isCommited(data);
167
+ }
168
+
169
+ /**
170
+ * Checks whether a swap is commited, i.e. the swap still exists on-chain and was not claimed nor refunded
171
+ *
172
+ * @param swapData
173
+ */
174
+ async isCommited(swapData: SolanaSwapData): Promise<boolean> {
175
+ const paymentHash = Buffer.from(swapData.paymentHash, "hex");
176
+
177
+ const account: IdlAccounts<SwapProgram>["escrowState"] = await this.program.account.escrowState.fetchNullable(this.SwapEscrowState(paymentHash));
178
+ if(account==null) return false;
179
+
180
+ return swapData.correctPDA(account);
181
+ }
182
+
183
+ /**
184
+ * Checks whether the swap is expired, takes into consideration possible on-chain time skew, therefore for claimer
185
+ * the swap expires a bit sooner than it should've & for the offerer it expires a bit later
186
+ *
187
+ * @param signer
188
+ * @param data
189
+ */
190
+ isExpired(signer: string, data: SolanaSwapData): Promise<boolean> {
191
+ let currentTimestamp: BN = new BN(Math.floor(Date.now()/1000));
192
+ if(data.isClaimer(signer)) currentTimestamp = currentTimestamp.add(new BN(this.claimGracePeriod));
193
+ if(data.isOfferer(signer)) currentTimestamp = currentTimestamp.sub(new BN(this.refundGracePeriod));
194
+ return Promise.resolve(data.expiry.lt(currentTimestamp));
195
+ }
196
+
197
+ /**
198
+ * Checks if the swap is refundable by us, checks if we are offerer, if the swap is already expired & if the swap
199
+ * is still commited
200
+ *
201
+ * @param signer
202
+ * @param data
203
+ */
204
+ async isRequestRefundable(signer: string, data: SolanaSwapData): Promise<boolean> {
205
+ //Swap can only be refunded by the offerer
206
+ if(!data.isOfferer(signer)) return false;
207
+ if(!(await this.isExpired(signer, data))) return false;
208
+ return await this.isCommited(data);
209
+ }
210
+
211
+ /**
212
+ * Get the swap payment hash to be used for an on-chain swap, this just uses a sha256 hash of the values
213
+ *
214
+ * @param outputScript output script required to claim the swap
215
+ * @param amount sats sent required to claim the swap
216
+ * @param confirmations
217
+ * @param nonce swap nonce uniquely identifying the transaction to prevent replay attacks
218
+ */
219
+ getHashForOnchain(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
220
+ nonce ??= 0n;
221
+ const paymentHash = Buffer.from(sha256(Buffer.concat([
222
+ BigIntBufferUtils.toBuffer(nonce, "le", 8),
223
+ BigIntBufferUtils.toBuffer(amount, "le", 8),
224
+ outputScript
225
+ ]))).toString("hex");
226
+ return Buffer.from(toClaimHash(paymentHash, nonce, confirmations), "hex");
227
+ }
228
+
229
+ getHashForHtlc(swapHash: Buffer): Buffer {
230
+ return Buffer.from(toClaimHash(
231
+ swapHash.toString("hex"),
232
+ 0n,
233
+ 0
234
+ ), "hex");
235
+ }
236
+
237
+ getHashForTxId(txId: string, confirmations: number): Buffer {
238
+ return Buffer.from(toClaimHash(
239
+ Buffer.from(txId, "hex").reverse().toString("hex"),
240
+ 0n,
241
+ confirmations
242
+ ), "hex");
243
+ }
244
+
245
+ ////////////////////////////////////////////
246
+ //// Swap data getters
247
+ /**
248
+ * Gets the status of the specific swap, this also checks if we are offerer/claimer & checks for expiry (to see
249
+ * if swap is refundable)
250
+ *
251
+ * @param signer
252
+ * @param data
253
+ */
254
+ async getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState> {
255
+ const escrowStateKey = this.SwapEscrowState(Buffer.from(data.paymentHash, "hex"));
256
+ const [escrowState, isExpired] = await Promise.all([
257
+ this.program.account.escrowState.fetchNullable(escrowStateKey) as Promise<IdlAccounts<SwapProgram>["escrowState"]>,
258
+ this.isExpired(signer,data)
259
+ ]);
260
+
261
+ if(escrowState!=null) {
262
+ if(data.correctPDA(escrowState)) {
263
+ if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.REFUNDABLE};
264
+ return {type: SwapCommitStateType.COMMITED};
265
+ }
266
+
267
+ if(data.isOfferer(signer) && isExpired) return {type: SwapCommitStateType.EXPIRED};
268
+ return {type: SwapCommitStateType.NOT_COMMITED};
269
+ }
270
+
271
+ //Check if paid or what
272
+ const status: SwapNotCommitedState | SwapExpiredState | SwapPaidState = await this.Events.findInEvents(escrowStateKey, async (event, info) => {
273
+ if(event.name==="ClaimEvent") {
274
+ const paymentHash = Buffer.from(event.data.hash).toString("hex");
275
+ if(paymentHash!==data.paymentHash) return null;
276
+ if(!event.data.sequence.eq(data.sequence)) return null;
277
+ return {
278
+ type: SwapCommitStateType.PAID,
279
+ getClaimTxId: () => Promise.resolve(info.signature),
280
+ getClaimResult: () => Promise.resolve(Buffer.from(event.data.secret).toString("hex")),
281
+ getTxBlock: async () => {
282
+ return {
283
+ blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
284
+ blockTime: info.blockTime
285
+ };
286
+ }
287
+ }
288
+ }
289
+ if(event.name==="RefundEvent") {
290
+ const paymentHash = Buffer.from(event.data.hash).toString("hex");
291
+ if(paymentHash!==data.paymentHash) return null;
292
+ if(!event.data.sequence.eq(data.sequence)) return null;
293
+ return {
294
+ type: isExpired ? SwapCommitStateType.EXPIRED : SwapCommitStateType.NOT_COMMITED,
295
+ getRefundTxId: () => Promise.resolve(info.signature),
296
+ getTxBlock: async () => {
297
+ return {
298
+ blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
299
+ blockTime: info.blockTime
300
+ };
301
+ }
302
+ };
303
+ }
304
+ });
305
+ if(status!=null) return status;
306
+
307
+ if(isExpired) return {type: SwapCommitStateType.EXPIRED};
308
+ return {type: SwapCommitStateType.NOT_COMMITED};
309
+ }
310
+
311
+ /**
312
+ * Checks the status of the specific payment hash
313
+ *
314
+ * @param claimHash
315
+ */
316
+ async getClaimHashStatus(claimHash: string): Promise<SwapCommitStateType> {
317
+ const {paymentHash} = fromClaimHash(claimHash);
318
+ const escrowStateKey = this.SwapEscrowState(Buffer.from(paymentHash, "hex"));
319
+ const abortController = new AbortController();
320
+
321
+ //Start fetching events before checking escrow PDA, this call is used when quoting, so saving 100ms here helps a lot!
322
+ const eventsPromise = this.Events.findInEvents(escrowStateKey, async (event) => {
323
+ if(event.name==="ClaimEvent") return SwapCommitStateType.PAID;
324
+ if(event.name==="RefundEvent") return SwapCommitStateType.NOT_COMMITED;
325
+ }, abortController.signal).catch(e => {
326
+ abortController.abort(e)
327
+ return null;
328
+ });
329
+
330
+ const escrowState = await this.program.account.escrowState.fetchNullable(escrowStateKey);
331
+ abortController.signal.throwIfAborted();
332
+ if(escrowState!=null) {
333
+ abortController.abort();
334
+ return SwapCommitStateType.COMMITED;
335
+ }
336
+
337
+ //Check if paid or what
338
+ const eventsStatus = await eventsPromise;
339
+ abortController.signal.throwIfAborted();
340
+ if(eventsStatus!=null) return eventsStatus;
341
+
342
+ return SwapCommitStateType.NOT_COMMITED;
343
+ }
344
+
345
+ /**
346
+ * Returns the data committed for a specific payment hash, or null if no data is currently commited for
347
+ * the specific swap
348
+ *
349
+ * @param claimHashHex
350
+ */
351
+ async getCommitedData(claimHashHex: string): Promise<SolanaSwapData> {
352
+ const {paymentHash} = fromClaimHash(claimHashHex);
353
+ const paymentHashBuffer = Buffer.from(paymentHash, "hex");
354
+
355
+ const account: IdlAccounts<SwapProgram>["escrowState"] = await this.program.account.escrowState.fetchNullable(this.SwapEscrowState(paymentHashBuffer));
356
+ if(account==null) return null;
357
+
358
+ return SolanaSwapData.fromEscrowState(account);
359
+ }
360
+
361
+ ////////////////////////////////////////////
362
+ //// Swap data initializer
363
+ createSwapData(
364
+ type: ChainSwapType,
365
+ offerer: string,
366
+ claimer: string,
367
+ token: string,
368
+ amount: bigint,
369
+ claimHash: string,
370
+ sequence: bigint,
371
+ expiry: bigint,
372
+ payIn: boolean,
373
+ payOut: boolean,
374
+ securityDeposit: bigint,
375
+ claimerBounty: bigint,
376
+ depositToken?: string
377
+ ): Promise<SolanaSwapData> {
378
+ if(depositToken!=null) {
379
+ if(!new PublicKey(depositToken).equals(SolanaTokens.WSOL_ADDRESS)) throw new Error("Only SOL supported as deposit token!");
380
+ }
381
+ const tokenAddr: PublicKey = new PublicKey(token);
382
+ const offererKey = offerer==null ? null : new PublicKey(offerer);
383
+ const claimerKey = claimer==null ? null : new PublicKey(claimer);
384
+ const {paymentHash, nonce, confirmations} = fromClaimHash(claimHash);
385
+ return Promise.resolve(new SolanaSwapData(
386
+ offererKey,
387
+ claimerKey,
388
+ tokenAddr,
389
+ toBN(amount),
390
+ paymentHash,
391
+ toBN(sequence),
392
+ toBN(expiry),
393
+ nonce,
394
+ confirmations,
395
+ payOut,
396
+ type==null ? null : SolanaSwapData.typeToKind(type),
397
+ payIn,
398
+ offererKey==null ? null : payIn ? getAssociatedTokenAddressSync(tokenAddr, offererKey) : PublicKey.default,
399
+ claimerKey==null ? null : payOut ? getAssociatedTokenAddressSync(tokenAddr, claimerKey) : PublicKey.default,
400
+ toBN(securityDeposit),
401
+ toBN(claimerBounty),
402
+ null
403
+ ));
404
+ }
405
+
406
+ ////////////////////////////////////////////
407
+ //// Utils
408
+ async getBalance(signer: string, tokenAddress: string, inContract: boolean): Promise<bigint> {
409
+ if(!inContract) {
410
+ return await this.Chain.getBalance(signer, tokenAddress);
411
+ }
412
+
413
+ const token = new PublicKey(tokenAddress);
414
+ const publicKey = new PublicKey(signer);
415
+
416
+ return await this.getIntermediaryBalance(publicKey, token);
417
+ }
418
+
419
+ getIntermediaryData(address: string, token: string): Promise<{
420
+ balance: bigint,
421
+ reputation: IntermediaryReputationType
422
+ }> {
423
+ return this.LpVault.getIntermediaryData(new PublicKey(address), new PublicKey(token));
424
+ }
425
+
426
+ getIntermediaryReputation(address: string, token: string): Promise<IntermediaryReputationType> {
427
+ return this.LpVault.getIntermediaryReputation(new PublicKey(address), new PublicKey(token));
428
+ }
429
+
430
+ getIntermediaryBalance(address: PublicKey, token: PublicKey): Promise<bigint> {
431
+ return this.LpVault.getIntermediaryBalance(address, token);
432
+ }
433
+
434
+ ////////////////////////////////////////////
435
+ //// Transaction initializers
436
+ async txsClaimWithSecret(
437
+ signer: string | SolanaSigner,
438
+ swapData: SolanaSwapData,
439
+ secret: string,
440
+ checkExpiry?: boolean,
441
+ initAta?: boolean,
442
+ feeRate?: string,
443
+ skipAtaCheck?: boolean
444
+ ): Promise<SolanaTx[]> {
445
+ return this.Claim.txsClaimWithSecret(typeof(signer)==="string" ? new PublicKey(signer) : signer.getPublicKey(), swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck)
446
+ }
447
+
448
+ async txsClaimWithTxData(
449
+ signer: string | SolanaSigner,
450
+ swapData: SolanaSwapData,
451
+ tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
452
+ requiredConfirmations: number,
453
+ vout: number,
454
+ commitedHeader?: SolanaBtcStoredHeader,
455
+ synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
456
+ initAta?: boolean,
457
+ feeRate?: string,
458
+ storageAccHolder?: {storageAcc: PublicKey}
459
+ ): Promise<SolanaTx[] | null> {
460
+ if(swapData.confirmations!==requiredConfirmations) throw new Error("Invalid requiredConfirmations provided!");
461
+ return this.Claim.txsClaimWithTxData(typeof(signer)==="string" ? new PublicKey(signer) : signer, swapData, tx, vout, commitedHeader, synchronizer, initAta, storageAccHolder, feeRate);
462
+ }
463
+
464
+ txsRefund(signer: string, swapData: SolanaSwapData, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
465
+ if(!swapData.isOfferer(signer)) throw new Error("Only offerer can refund on Solana");
466
+ return this.Refund.txsRefund(swapData, check, initAta, feeRate);
467
+ }
468
+
469
+ txsRefundWithAuthorization(signer: string, swapData: SolanaSwapData, {timeout, prefix, signature}, check?: boolean, initAta?: boolean, feeRate?: string): Promise<SolanaTx[]> {
470
+ if(!swapData.isOfferer(signer)) throw new Error("Only offerer can refund on Solana");
471
+ return this.Refund.txsRefundWithAuthorization(swapData,timeout,prefix,signature,check,initAta,feeRate);
472
+ }
473
+
474
+ txsInit(sender: string, swapData: SolanaSwapData, {timeout, prefix, signature}, skipChecks?: boolean, feeRate?: string): Promise<SolanaTx[]> {
475
+ if(swapData.isPayIn()) {
476
+ if(!swapData.isOfferer(sender)) throw new Error("Only offerer can create payIn=true swap");
477
+ return this.Init.txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate);
478
+ } else {
479
+ if(!swapData.isClaimer(sender)) throw new Error("Only claimer can create payIn=false swap");
480
+ return this.Init.txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate);
481
+ }
482
+ }
483
+
484
+ txsWithdraw(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
485
+ return this.LpVault.txsWithdraw(new PublicKey(signer), new PublicKey(token), amount, feeRate);
486
+ }
487
+
488
+ txsDeposit(signer: string, token: string, amount: bigint, feeRate?: string): Promise<SolanaTx[]> {
489
+ return this.LpVault.txsDeposit(new PublicKey(signer), new PublicKey(token), amount, feeRate);
490
+ }
491
+
492
+ ////////////////////////////////////////////
493
+ //// Executors
494
+ async claimWithSecret(
495
+ signer: SolanaSigner,
496
+ swapData: SolanaSwapData,
497
+ secret: string,
498
+ checkExpiry?: boolean,
499
+ initAta?: boolean,
500
+ txOptions?: TransactionConfirmationOptions
501
+ ): Promise<string> {
502
+ const result = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, checkExpiry, initAta, txOptions?.feeRate);
503
+ const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
504
+ return signature;
505
+ }
506
+
507
+ async claimWithTxData(
508
+ signer: SolanaSigner,
509
+ swapData: SolanaSwapData,
510
+ tx: { blockhash: string, confirmations: number, txid: string, hex: string, height: number },
511
+ requiredConfirmations: number,
512
+ vout: number,
513
+ commitedHeader?: SolanaBtcStoredHeader,
514
+ synchronizer?: RelaySynchronizer<any, SolanaTx, any>,
515
+ initAta?: boolean,
516
+ txOptions?: TransactionConfirmationOptions
517
+ ): Promise<string> {
518
+ if(requiredConfirmations!==swapData.confirmations) throw new Error("Invalid requiredConfirmations provided!");
519
+
520
+ const data: {storageAcc: PublicKey} = {
521
+ storageAcc: null
522
+ };
523
+
524
+ const txs = await this.Claim.txsClaimWithTxData(
525
+ signer, swapData, tx, vout,
526
+ commitedHeader, synchronizer, initAta, data, txOptions?.feeRate
527
+ );
528
+ if(txs===null) throw new Error("Btc relay not synchronized to required blockheight!");
529
+
530
+ //TODO: This doesn't return proper tx signature
531
+ const [signature] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal);
532
+ await this.DataAccount.removeDataAccount(data.storageAcc);
533
+
534
+ return signature;
535
+ }
536
+
537
+ async refund(
538
+ signer: SolanaSigner,
539
+ swapData: SolanaSwapData,
540
+ check?: boolean,
541
+ initAta?: boolean,
542
+ txOptions?: TransactionConfirmationOptions
543
+ ): Promise<string> {
544
+ let result = await this.txsRefund(signer.getAddress(), swapData, check, initAta, txOptions?.feeRate);
545
+
546
+ const [signature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
547
+
548
+ return signature;
549
+ }
550
+
551
+ async refundWithAuthorization(
552
+ signer: SolanaSigner,
553
+ swapData: SolanaSwapData,
554
+ signature: SignatureData,
555
+ check?: boolean,
556
+ initAta?: boolean,
557
+ txOptions?: TransactionConfirmationOptions
558
+ ): Promise<string> {
559
+ let result = await this.txsRefundWithAuthorization(signer.getAddress(), swapData, signature, check, initAta, txOptions?.feeRate);
560
+
561
+ const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
562
+
563
+ return txSignature;
564
+ }
565
+
566
+ async init(
567
+ signer: SolanaSigner,
568
+ swapData: SolanaSwapData,
569
+ signature: SignatureData,
570
+ skipChecks?: boolean,
571
+ txOptions?: TransactionConfirmationOptions
572
+ ): Promise<string> {
573
+ if(swapData.isPayIn()) {
574
+ if(!signer.getPublicKey().equals(swapData.offerer)) throw new Error("Invalid signer provided!");
575
+ } else {
576
+ if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
577
+ }
578
+
579
+ const result = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
580
+
581
+ const [txSignature] = await this.Chain.sendAndConfirm(signer, result, txOptions?.waitForConfirmation, txOptions?.abortSignal);
582
+
583
+ return txSignature;
584
+ }
585
+
586
+ async initAndClaimWithSecret(
587
+ signer: SolanaSigner,
588
+ swapData: SolanaSwapData,
589
+ signature: SignatureData,
590
+ secret: string,
591
+ skipChecks?: boolean,
592
+ txOptions?: TransactionConfirmationOptions
593
+ ): Promise<string[]> {
594
+ if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer provided!");
595
+
596
+ const txsCommit = await this.txsInit(signer.getAddress(), swapData, signature, skipChecks, txOptions?.feeRate);
597
+ const txsClaim = await this.Claim.txsClaimWithSecret(signer.getPublicKey(), swapData, secret, true, false, txOptions?.feeRate, true);
598
+
599
+ return await this.Chain.sendAndConfirm(signer, txsCommit.concat(txsClaim), txOptions?.waitForConfirmation, txOptions?.abortSignal);
600
+ }
601
+
602
+ async withdraw(
603
+ signer: SolanaSigner,
604
+ token: string,
605
+ amount: bigint,
606
+ txOptions?: TransactionConfirmationOptions
607
+ ): Promise<string> {
608
+ const txs = await this.LpVault.txsWithdraw(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
609
+ const [txId] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
610
+ return txId;
611
+ }
612
+
613
+ async deposit(
614
+ signer: SolanaSigner,
615
+ token: string,
616
+ amount: bigint,
617
+ txOptions?: TransactionConfirmationOptions
618
+ ): Promise<string> {
619
+ const txs = await this.LpVault.txsDeposit(signer.getPublicKey(), new PublicKey(token), amount, txOptions?.feeRate);
620
+ const [txId] = await this.Chain.sendAndConfirm(signer, txs, txOptions?.waitForConfirmation, txOptions?.abortSignal, false);
621
+ return txId;
622
+ }
623
+
624
+ ////////////////////////////////////////////
625
+ //// Fees
626
+ getInitPayInFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
627
+ const paymentHash = claimHash==null ? null : fromClaimHash(claimHash).paymentHash;
628
+ return this.Init.getInitPayInFeeRate(
629
+ toPublicKeyOrNull(offerer),
630
+ toPublicKeyOrNull(claimer),
631
+ toPublicKeyOrNull(token),
632
+ paymentHash
633
+ );
634
+ }
635
+
636
+ getInitFeeRate(offerer?: string, claimer?: string, token?: string, claimHash?: string): Promise<string> {
637
+ const paymentHash = claimHash==null ? null : fromClaimHash(claimHash).paymentHash;
638
+ return this.Init.getInitFeeRate(
639
+ toPublicKeyOrNull(offerer),
640
+ toPublicKeyOrNull(claimer),
641
+ toPublicKeyOrNull(token),
642
+ paymentHash
643
+ );
644
+ }
645
+
646
+ getRefundFeeRate(swapData: SolanaSwapData): Promise<string> {
647
+ return this.Refund.getRefundFeeRate(swapData);
648
+ }
649
+
650
+ getClaimFeeRate(signer: string, swapData: SolanaSwapData): Promise<string> {
651
+ return this.Claim.getClaimFeeRate(new PublicKey(signer), swapData);
652
+ }
653
+
654
+ getClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
655
+ return this.Claim.getClaimFee(new PublicKey(signer), swapData, feeRate);
656
+ }
657
+
658
+ getRawClaimFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
659
+ return this.Claim.getRawClaimFee(new PublicKey(signer), swapData, feeRate);
660
+ }
661
+
662
+ /**
663
+ * Get the estimated solana fee of the commit transaction
664
+ */
665
+ getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
666
+ return this.Init.getInitFee(swapData, feeRate);
667
+ }
668
+
669
+ /**
670
+ * Get the estimated solana fee of the commit transaction, without any deposits
671
+ */
672
+ getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
673
+ return this.Init.getRawInitFee(swapData, feeRate);
674
+ }
675
+
676
+ /**
677
+ * Get the estimated solana transaction fee of the refund transaction
678
+ */
679
+ getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
680
+ return this.Refund.getRefundFee(swapData, feeRate);
681
+ }
682
+
683
+ /**
684
+ * Get the estimated solana transaction fee of the refund transaction
685
+ */
686
+ getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
687
+ return this.Refund.getRawRefundFee(swapData, feeRate);
688
+ }
689
+
690
+ getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer {
691
+ return Buffer.from(sha256(Buffer.concat([
692
+ BigIntBufferUtils.toBuffer(amount, "le", 8),
693
+ outputScript
694
+ ])));
695
+ }
696
+
697
+ }