@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,735 +1,735 @@
1
- import {PublicKey, SystemProgram, SYSVAR_INSTRUCTIONS_PUBKEY} from "@solana/web3.js";
2
- import * as BN from "bn.js";
3
- import {ChainSwapType, SwapData} from "@atomiqlabs/base";
4
- import {SwapProgram} from "./v1/programTypes";
5
- import {IdlAccounts, IdlTypes} from "@coral-xyz/anchor";
6
- import {SwapTypeEnum} from "./SwapTypeEnum";
7
- import {Buffer} from "buffer";
8
- import {getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID} from "@solana/spl-token";
9
- import {Serialized, toBigInt, toClaimHash, toEscrowHash} from "../../utils/Utils";
10
- import {SolanaTokens} from "../chain/modules/SolanaTokens";
11
- import {SingleInstructionWithAccounts} from "../program/modules/SolanaProgramEvents";
12
- import {SolanaSwapProgram} from "./SolanaSwapProgram";
13
- import {SwapProgramV2} from "./v2/programTypes";
14
-
15
- export type InitInstruction = SingleInstructionWithAccounts<SwapProgram["instructions"][2 | 3] | SwapProgramV2["instructions"][2 | 3], SwapProgram>;
16
-
17
- const EXPIRY_BLOCKHEIGHT_THRESHOLD = new BN("1000000000");
18
-
19
- export type SolanaSwapDataCtorArgs = {
20
- programId: PublicKey,
21
- version: "v1" | "v2",
22
-
23
- offerer: PublicKey,
24
- claimer: PublicKey,
25
- token: PublicKey,
26
- amount: BN,
27
- paymentHash: string,
28
- sequence: BN,
29
- expiry: BN,
30
-
31
- nonce: BN,
32
- confirmations: number,
33
- payOut: boolean,
34
- kind: number,
35
- payIn: boolean,
36
- offererAta?: PublicKey,
37
- claimerAta?: PublicKey,
38
-
39
- securityDeposit: BN,
40
- claimerBounty: BN,
41
-
42
- txoHash?: string,
43
- offererInitializer?: boolean
44
- };
45
-
46
- export function isSerializedData(obj: any): obj is ({type: "sol"} & Serialized<SolanaSwapData>) {
47
- return obj.type==="sol";
48
- }
49
-
50
- /**
51
- * Represents Solana swap data for executing PrTLC (on-chain) or HTLC (lightning) based swaps.
52
- *
53
- * @category Swaps
54
- */
55
- export class SolanaSwapData extends SwapData {
56
-
57
- /**
58
- * Program ID for which this swap data was created
59
- */
60
- programId: PublicKey;
61
- /**
62
- * Program version for which this swap was created
63
- */
64
- version: "v1" | "v2";
65
- /**
66
- * Offerer address funding the swap.
67
- */
68
- offerer: PublicKey;
69
- /**
70
- * Claimer address receiving the swap funds.
71
- */
72
- claimer: PublicKey;
73
- /**
74
- * Token mint used for the swap.
75
- */
76
- token: PublicKey;
77
- /**
78
- * Swap amount.
79
- */
80
- amount: BN;
81
- /**
82
- * Payment hash identifying the swap.
83
- */
84
- paymentHash: string;
85
- /**
86
- * Swap sequence used for uniqueness.
87
- */
88
- sequence: BN;
89
- /**
90
- * Swap expiry timestamp.
91
- */
92
- expiry: BN;
93
- /**
94
- * Nonce used in claim hash derivation.
95
- */
96
- nonce: BN;
97
- /**
98
- * Required bitcoin confirmations for claim.
99
- */
100
- confirmations: number;
101
- /**
102
- * Whether funds are paid out to claimer wallet directly.
103
- */
104
- payOut: boolean;
105
- /**
106
- * Solana on-chain swap kind discriminator.
107
- */
108
- kind: number;
109
- /**
110
- * Whether funds are paid in from offerer wallet.
111
- */
112
- payIn: boolean;
113
- /**
114
- * Optional claimer associated token account.
115
- */
116
- claimerAta?: PublicKey;
117
- /**
118
- * Optional offerer associated token account.
119
- */
120
- offererAta?: PublicKey;
121
-
122
- /**
123
- * Security deposit amount.
124
- */
125
- securityDeposit: BN;
126
- /**
127
- * Claimer bounty amount.
128
- */
129
- claimerBounty: BN;
130
-
131
- /**
132
- * Optional txo hash hint.
133
- */
134
- txoHash?: string;
135
-
136
- /**
137
- * Optional flag whether the offerer is the initializer for V2 swap data
138
- */
139
- offererInitializer?: boolean;
140
-
141
- /**
142
- * Creates swap data from structured constructor arguments.
143
- *
144
- * @param args Swap data fields
145
- */
146
- constructor(args: SolanaSwapDataCtorArgs);
147
- /**
148
- * Deserializes swap data from serialized storage representation.
149
- *
150
- * @param data Serialized swap data from {@link SolanaSwapData.serialize}
151
- */
152
- constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
153
-
154
- constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
155
- super();
156
- if(!isSerializedData(data)) {
157
- this.programId = data.programId;
158
- this.version = data.version;
159
- this.offerer = data.offerer;
160
- this.claimer = data.claimer;
161
- this.token = data.token;
162
- this.amount = data.amount;
163
- this.paymentHash = data.paymentHash;
164
- this.sequence = data.sequence;
165
- this.expiry = data.expiry;
166
- this.nonce = data.nonce;
167
- this.confirmations = data.confirmations;
168
- this.payOut = data.payOut;
169
- this.kind = data.kind;
170
- this.payIn = data.payIn;
171
- this.claimerAta = data.claimerAta;
172
- this.offererAta = data.offererAta;
173
- this.securityDeposit = data.securityDeposit;
174
- this.claimerBounty = data.claimerBounty;
175
- this.txoHash = data.txoHash;
176
- this.offererInitializer = data.offererInitializer;
177
- } else {
178
- this.programId = new PublicKey(data.programId ?? "4hfUykhqmD7ZRvNh1HuzVKEY7ToENixtdUKZspNDCrEM");
179
- this.version = data.version ?? "v1";
180
- this.offerer = new PublicKey(data.offerer);
181
- this.claimer = new PublicKey(data.claimer);
182
- this.token = new PublicKey(data.token);
183
- this.amount = new BN(data.amount);
184
- this.paymentHash = data.paymentHash;
185
- this.sequence = new BN(data.sequence);
186
- this.expiry = new BN(data.expiry);
187
- this.nonce = new BN(data.nonce);
188
- this.confirmations = data.confirmations;
189
- this.payOut = data.payOut;
190
- this.kind = data.kind;
191
- this.payIn = data.payIn;
192
- this.claimerAta = data.claimerAta==null ? undefined : new PublicKey(data.claimerAta);
193
- this.offererAta = data.offererAta==null ? undefined : new PublicKey(data.offererAta);
194
- this.securityDeposit = new BN(data.securityDeposit);
195
- this.claimerBounty = new BN(data.claimerBounty);
196
- this.txoHash = data.txoHash;
197
- this.offererInitializer = data.offererInitializer;
198
- }
199
- }
200
-
201
- /**
202
- * @inheritDoc
203
- */
204
- getOfferer(): string {
205
- return this.offerer.toBase58();
206
- }
207
-
208
- /**
209
- * @inheritDoc
210
- */
211
- setOfferer(newOfferer: string) {
212
- this.offerer = new PublicKey(newOfferer);
213
- this.offererAta = getAssociatedTokenAddressSync(this.token, this.offerer);
214
- this.payIn = true;
215
- }
216
-
217
- /**
218
- * @inheritDoc
219
- */
220
- getClaimer(): string {
221
- return this.claimer.toBase58();
222
- }
223
-
224
- /**
225
- * @inheritDoc
226
- */
227
- setClaimer(newClaimer: string) {
228
- this.claimer = new PublicKey(newClaimer);
229
- this.payIn = false;
230
- this.payOut = true;
231
- this.claimerAta = getAssociatedTokenAddressSync(this.token, this.claimer);
232
- }
233
-
234
- /**
235
- * @inheritDoc
236
- */
237
- serialize(): {type: "sol"} & Serialized<SolanaSwapData> {
238
- return {
239
- type: "sol",
240
- programId: this.programId?.toBase58(),
241
- version: this.version,
242
- offerer: this.offerer?.toBase58(),
243
- claimer: this.claimer?.toBase58(),
244
- token: this.token?.toBase58(),
245
- amount: this.amount?.toString(10),
246
- paymentHash: this.paymentHash,
247
- sequence: this.sequence?.toString(10),
248
- expiry: this.expiry?.toString(10),
249
- nonce: this.nonce?.toString(10),
250
- confirmations: this.confirmations,
251
- payOut: this.payOut,
252
- kind: this.kind,
253
- payIn: this.payIn,
254
- offererAta: this.offererAta?.toBase58(),
255
- claimerAta: this.claimerAta?.toBase58(),
256
- securityDeposit: this.securityDeposit?.toString(10),
257
- claimerBounty: this.claimerBounty?.toString(10),
258
- txoHash: this.txoHash,
259
- offererInitializer: this.offererInitializer
260
- }
261
- }
262
-
263
- /**
264
- * @inheritDoc
265
- */
266
- getAmount(): bigint {
267
- return toBigInt(this.amount);
268
- }
269
-
270
- /**
271
- * @inheritDoc
272
- */
273
- getToken(): string {
274
- return this.token.toString();
275
- }
276
-
277
- /**
278
- * @inheritDoc
279
- */
280
- isToken(token: string): boolean {
281
- return this.token.equals(new PublicKey(token));
282
- }
283
-
284
- /**
285
- * @inheritDoc
286
- */
287
- getType(): ChainSwapType {
288
- return SolanaSwapData.kindToType(this.kind);
289
- }
290
-
291
- /**
292
- * @inheritDoc
293
- */
294
- getExpiry(): bigint {
295
- if(this.expiry.lt(EXPIRY_BLOCKHEIGHT_THRESHOLD)) throw new Error("Expiry expressed as bitcoin blockheight!");
296
- return toBigInt(this.expiry);
297
- }
298
-
299
- /**
300
- * @inheritDoc
301
- */
302
- getConfirmationsHint(): number {
303
- return this.confirmations;
304
- }
305
-
306
- /**
307
- * @inheritDoc
308
- */
309
- getNonceHint(): bigint {
310
- return toBigInt(this.nonce);
311
- }
312
-
313
- /**
314
- * @inheritDoc
315
- */
316
- isPayIn(): boolean {
317
- return this.payIn;
318
- }
319
-
320
- /**
321
- * @inheritDoc
322
- */
323
- isPayOut(): boolean {
324
- return this.payOut;
325
- }
326
-
327
- /**
328
- * @inheritDoc
329
- */
330
- isTrackingReputation(): boolean {
331
- return !this.payOut;
332
- }
333
-
334
- /**
335
- * @inheritDoc
336
- */
337
- getClaimHash(): string {
338
- return toClaimHash(this.paymentHash, toBigInt(this.nonce), this.confirmations);
339
- }
340
-
341
- /**
342
- * @inheritDoc
343
- */
344
- getEscrowHash(): string {
345
- return toEscrowHash(this.paymentHash, this.sequence);
346
- }
347
-
348
- /**
349
- * @inheritDoc
350
- */
351
- getSequence(): bigint {
352
- return toBigInt(this.sequence);
353
- }
354
-
355
- /**
356
- * @inheritDoc
357
- */
358
- getTxoHashHint(): string | null {
359
- if(this.txoHash==="0000000000000000000000000000000000000000000000000000000000000000") return null; //Txo hash opt-out flag
360
- return this.txoHash ?? null
361
- }
362
-
363
- /**
364
- * @inheritDoc
365
- */
366
- getHTLCHashHint(): string | null {
367
- if(this.getType()===ChainSwapType.HTLC) return this.paymentHash;
368
- return null;
369
- }
370
-
371
- /**
372
- * @inheritDoc
373
- */
374
- getExtraData(): string | null {
375
- return this.txoHash ?? null;
376
- }
377
-
378
- /**
379
- * @inheritDoc
380
- */
381
- setExtraData(txoHash: string): void {
382
- this.txoHash = txoHash;
383
- }
384
-
385
- /**
386
- * @inheritDoc
387
- */
388
- getSecurityDeposit(): bigint {
389
- return toBigInt(this.securityDeposit);
390
- }
391
-
392
- /**
393
- * @inheritDoc
394
- */
395
- getClaimerBounty(): bigint {
396
- return toBigInt(this.claimerBounty);
397
- }
398
-
399
- /**
400
- * @inheritDoc
401
- */
402
- getTotalDeposit(): bigint {
403
- return toBigInt(this.claimerBounty.lt(this.securityDeposit) ? this.securityDeposit : this.claimerBounty);
404
- }
405
-
406
- /**
407
- * Serializes the swap data into the Solana program `SwapData` struct representation.
408
- */
409
- toSwapDataStruct(): IdlTypes<SwapProgram>["SwapData"] {
410
- return {
411
- kind: SwapTypeEnum.fromNumber(this.kind as 0 | 1 | 2 | 3),
412
- confirmations: this.confirmations,
413
- nonce: this.nonce,
414
- hash: [...Buffer.from(this.paymentHash, "hex")],
415
- payIn: this.payIn,
416
- payOut: this.payOut,
417
- amount: this.amount,
418
- expiry: this.expiry,
419
- sequence: this.sequence
420
- }
421
- }
422
-
423
- /**
424
- * Checks whether the provided escrow account matches this swap data.
425
- *
426
- * @param account Escrow account data fetched from chain
427
- */
428
- correctPDA(account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]): boolean {
429
- return SwapTypeEnum.toNumber(account.data.kind)===this.kind &&
430
- account.data.confirmations===this.confirmations &&
431
- this.nonce.eq(account.data.nonce) &&
432
- Buffer.from(account.data.hash).toString("hex")===this.paymentHash &&
433
- account.data.payIn===this.payIn &&
434
- account.data.payOut===this.payOut &&
435
- this.amount.eq(account.data.amount) &&
436
- this.expiry.eq(account.data.expiry) &&
437
- this.sequence.eq(account.data.sequence) &&
438
-
439
- account.offerer.equals(this.offerer) &&
440
- (this.offererAta==null || account.offererAta.equals(this.offererAta)) &&
441
- account.claimer.equals(this.claimer) &&
442
- (this.claimerAta==null || account.claimerAta.equals(this.claimerAta)) &&
443
- account.mint.equals(this.token) &&
444
- this.claimerBounty.eq(account.claimerBounty) &&
445
- this.securityDeposit.eq(account.securityDeposit) &&
446
- (this.offererInitializer==null || account.offererInitializer===this.offererInitializer);
447
- }
448
-
449
- /**
450
- * @inheritDoc
451
- */
452
- equals(other: SolanaSwapData): boolean {
453
- if(this.claimerAta==null && other.claimerAta!=null) return false;
454
- if(this.claimerAta!=null && other.claimerAta==null) return false;
455
- if(this.claimerAta!=null && other.claimerAta!=null) {
456
- if(!this.claimerAta.equals(other.claimerAta)) return false;
457
- }
458
-
459
- if(this.offererAta==null && other.offererAta!=null) return false;
460
- if(this.offererAta!=null && other.offererAta==null) return false;
461
- if(this.offererAta!=null && other.offererAta!=null) {
462
- if(!this.offererAta.equals(other.offererAta)) return false;
463
- }
464
-
465
- if(this.offererInitializer==null && other.offererInitializer!=null) return false;
466
- if(this.offererInitializer!=null && other.offererInitializer==null) return false;
467
- if(this.offererInitializer!=null && other.offererInitializer!=null) {
468
- if(this.offererInitializer!==other.offererInitializer) return false;
469
- }
470
-
471
- return other.kind===this.kind &&
472
- other.confirmations===this.confirmations &&
473
- this.nonce.eq(other.nonce) &&
474
- other.paymentHash===this.paymentHash &&
475
- this.sequence.eq(other.sequence) &&
476
- other.payIn===this.payIn &&
477
- other.payOut===this.payOut &&
478
- other.offerer.equals(this.offerer) &&
479
- other.claimer.equals(this.claimer) &&
480
- other.expiry.eq(this.expiry) &&
481
- other.amount.eq(this.amount) &&
482
- other.securityDeposit.eq(this.securityDeposit) &&
483
- other.claimerBounty.eq(this.claimerBounty) &&
484
- other.token.equals(this.token)
485
- }
486
-
487
- /**
488
- * Converts initialize instruction data into {@link SolanaSwapData}.
489
- *
490
- * @param programId
491
- * @param version
492
- * @param initIx Decoded initialize instruction
493
- * @param txoHash Parsed txo hash hint from initialize event
494
- * @returns Converted and parsed swap data
495
- */
496
- static fromInstruction(
497
- programId: PublicKey,
498
- version: "v1" | "v2",
499
- initIx: InitInstruction,
500
- txoHash: string
501
- ): SolanaSwapData {
502
- const paymentHash: Buffer = Buffer.from(initIx.data.swapData.hash);
503
- let securityDeposit: BN = new BN(0);
504
- let claimerBounty: BN = new BN(0);
505
- let payIn: boolean = true;
506
- if(initIx.name === "offererInitialize") {
507
- payIn = false;
508
- securityDeposit = initIx.data.securityDeposit;
509
- claimerBounty = initIx.data.claimerBounty;
510
- }
511
- if(version!=="v1" && initIx.name === "offererInitializePayIn") {
512
- payIn = true;
513
- securityDeposit = initIx.data.securityDeposit;
514
- claimerBounty = initIx.data.claimerBounty;
515
- }
516
-
517
- return new SolanaSwapData({
518
- programId,
519
- version,
520
- offerer: initIx.accounts.offerer,
521
- claimer: initIx.accounts.claimer,
522
- token: initIx.accounts.mint,
523
- amount: initIx.data.swapData.amount,
524
- paymentHash: paymentHash.toString("hex"),
525
- sequence: initIx.data.swapData.sequence,
526
- expiry: initIx.data.swapData.expiry,
527
- nonce: initIx.data.swapData.nonce,
528
- confirmations: initIx.data.swapData.confirmations,
529
- payOut: initIx.data.swapData.payOut,
530
- kind: SwapTypeEnum.toNumber(initIx.data.swapData.kind),
531
- payIn,
532
- offererAta: initIx.name === "offererInitializePayIn" ? initIx.accounts.offererAta : PublicKey.default,
533
- claimerAta: initIx.data.swapData.payOut ? initIx.accounts.claimerAta : PublicKey.default,
534
- securityDeposit,
535
- claimerBounty,
536
- txoHash,
537
- offererInitializer: initIx.accounts.initializer!=null ? initIx.accounts.initializer.equals(initIx.accounts.offerer) : undefined
538
- });
539
- }
540
-
541
- /**
542
- * Deserializes swap data from an on-chain escrow account state.
543
- *
544
- * @param programId
545
- * @param version
546
- * @param account Escrow account state as returned by Anchor
547
- */
548
- static fromEscrowState(
549
- programId: PublicKey,
550
- version: "v1" | "v2",
551
- account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]
552
- ) {
553
- const data: IdlTypes<SwapProgram | SwapProgramV2>["SwapData"] = account.data;
554
-
555
- return new SolanaSwapData({
556
- programId,
557
- version,
558
- offerer: account.offerer,
559
- claimer: account.claimer,
560
- token: account.mint,
561
- amount: data.amount,
562
- paymentHash: Buffer.from(data.hash).toString("hex"),
563
- sequence: data.sequence,
564
- expiry: data.expiry,
565
- nonce: data.nonce,
566
- confirmations: data.confirmations,
567
- payOut: data.payOut,
568
- kind: SwapTypeEnum.toNumber(data.kind),
569
- payIn: data.payIn,
570
- offererAta: account.offererAta,
571
- claimerAta: account.claimerAta,
572
- securityDeposit: account.securityDeposit,
573
- claimerBounty: account.claimerBounty,
574
- offererInitializer: account.offererInitializer ?? undefined
575
- });
576
- }
577
-
578
- /**
579
- * Converts abstract swap type to Solana program kind discriminator.
580
- *
581
- * @param type Chain-agnostic swap type
582
- */
583
- static typeToKind(type: ChainSwapType): number {
584
- switch (type) {
585
- case ChainSwapType.HTLC:
586
- return 0;
587
- case ChainSwapType.CHAIN:
588
- return 1;
589
- case ChainSwapType.CHAIN_NONCED:
590
- return 2;
591
- case ChainSwapType.CHAIN_TXID:
592
- return 3;
593
- }
594
- }
595
-
596
- /**
597
- * Converts Solana program kind discriminator to abstract swap type.
598
- *
599
- * @param value Solana program swap kind value
600
- */
601
- static kindToType(value: number): ChainSwapType {
602
- switch(value) {
603
- case 0:
604
- return ChainSwapType.HTLC;
605
- case 1:
606
- return ChainSwapType.CHAIN;
607
- case 2:
608
- return ChainSwapType.CHAIN_NONCED;
609
- case 3:
610
- return ChainSwapType.CHAIN_TXID;
611
- }
612
- throw new Error("Unknown swap kind type!");
613
- }
614
-
615
- /**
616
- * @inheritDoc
617
- */
618
- isClaimer(address: string) {
619
- const _address = new PublicKey(address);
620
- if(this.isPayOut()) {
621
- //Also check that swapData's ATA is correct
622
- const ourAta = getAssociatedTokenAddressSync(this.token, _address);
623
- if(this.claimerAta==null || !this.claimerAta.equals(ourAta)) return false;
624
- }
625
- return this.claimer.equals(new PublicKey(address));
626
- }
627
-
628
- /**
629
- * @inheritDoc
630
- */
631
- isOfferer(address: string) {
632
- return this.offerer.equals(new PublicKey(address));
633
- }
634
-
635
- /**
636
- * @inheritDoc
637
- */
638
- getDepositToken(): string {
639
- return SolanaTokens.WSOL_ADDRESS.toString();
640
- }
641
-
642
- /**
643
- * @inheritDoc
644
- */
645
- isDepositToken(token: string): boolean {
646
- return SolanaTokens.WSOL_ADDRESS.equals(new PublicKey(token));
647
- }
648
-
649
- /**
650
- * @inheritDoc
651
- */
652
- getEscrowStruct(): any {
653
- return {
654
- accounts: {
655
- offerer: this.offerer.toString(),
656
- claimer: this.claimer.toString(),
657
- claimerAta: this.claimerAta==null || this.claimerAta.equals(PublicKey.default) ? null : this.claimerAta.toString(),
658
- offererAta: this.offererAta==null || this.offererAta.equals(PublicKey.default) ? null : this.offererAta.toString(),
659
- claimerUserData: SolanaSwapProgram._SwapUserVault(this.programId, this.claimer, this.token).toString(),
660
- offererUserData: SolanaSwapProgram._SwapUserVault(this.programId, this.offerer, this.token).toString(),
661
-
662
- initializer: this.offererInitializer!=null
663
- ? (
664
- this.offererInitializer ? this.offerer.toString() : this.claimer.toString()
665
- )
666
- : (
667
- this.isPayIn() ? this.offerer.toString() : this.claimer.toString()
668
- ),
669
-
670
- escrowState: SolanaSwapProgram._SwapEscrowState(this.programId, Buffer.from(this.paymentHash, "hex")).toString(),
671
- mint: this.token.toString(),
672
- vault: SolanaSwapProgram._SwapVault(this.programId, this.token).toString(),
673
- vaultAuthority: SolanaSwapProgram._SwapVaultAuthority(this.programId).toString(),
674
-
675
- systemProgram: SystemProgram.programId.toString(),
676
- tokenProgram: TOKEN_PROGRAM_ID.toString(),
677
- ixSysvar: SYSVAR_INSTRUCTIONS_PUBKEY.toString(),
678
- },
679
- data: {
680
- kind: SwapTypeEnum.fromNumber(this.kind as 0 | 1 | 2 | 3),
681
- confirmations: this.confirmations,
682
- nonce: this.nonce.toString(10),
683
- hash: [...Buffer.from(this.paymentHash, "hex")],
684
- payIn: this.payIn,
685
- payOut: this.payOut,
686
- amount: this.amount.toString(10),
687
- expiry: this.expiry.toString(10),
688
- sequence: this.sequence.toString(10)
689
- }
690
- }
691
- }
692
-
693
- }
694
-
695
- export class SolanaSwapDataV1 extends SolanaSwapData {
696
- /**
697
- * Creates swap data from structured constructor arguments.
698
- *
699
- * @param args Swap data fields
700
- */
701
- constructor(args: SolanaSwapDataCtorArgs);
702
- /**
703
- * Deserializes swap data from serialized storage representation.
704
- *
705
- * @param data Serialized swap data from {@link SolanaSwapData.serialize}
706
- */
707
- constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
708
-
709
- constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
710
- super(data as any);
711
- if(this.version!=="v1") throw new Error(`Invalid swap data version, expected v1, got ${this.version}`);
712
- }
713
- }
714
-
715
- export class SolanaSwapDataV2 extends SolanaSwapData {
716
- /**
717
- * Creates swap data from structured constructor arguments.
718
- *
719
- * @param args Swap data fields
720
- */
721
- constructor(args: SolanaSwapDataCtorArgs);
722
- /**
723
- * Deserializes swap data from serialized storage representation.
724
- *
725
- * @param data Serialized swap data from {@link SolanaSwapData.serialize}
726
- */
727
- constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
728
-
729
- constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
730
- super(data as any);
731
- if(this.version!=="v2") throw new Error(`Invalid swap data version, expected v2, got ${this.version}`);
732
- }
733
- }
734
-
735
- SwapData.deserializers["sol"] = SolanaSwapData;
1
+ import {PublicKey, SystemProgram, SYSVAR_INSTRUCTIONS_PUBKEY} from "@solana/web3.js";
2
+ import * as BN from "bn.js";
3
+ import {ChainSwapType, SwapData} from "@atomiqlabs/base";
4
+ import {SwapProgram} from "./v1/programTypes";
5
+ import {IdlAccounts, IdlTypes} from "@coral-xyz/anchor";
6
+ import {SwapTypeEnum} from "./SwapTypeEnum";
7
+ import {Buffer} from "buffer";
8
+ import {getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID} from "@solana/spl-token";
9
+ import {Serialized, toBigInt, toClaimHash, toEscrowHash} from "../../utils/Utils";
10
+ import {SolanaTokens} from "../chain/modules/SolanaTokens";
11
+ import {SingleInstructionWithAccounts} from "../program/modules/SolanaProgramEvents";
12
+ import {SolanaSwapProgram} from "./SolanaSwapProgram";
13
+ import {SwapProgramV2} from "./v2/programTypes";
14
+
15
+ export type InitInstruction = SingleInstructionWithAccounts<SwapProgram["instructions"][2 | 3] | SwapProgramV2["instructions"][2 | 3], SwapProgram>;
16
+
17
+ const EXPIRY_BLOCKHEIGHT_THRESHOLD = new BN("1000000000");
18
+
19
+ export type SolanaSwapDataCtorArgs = {
20
+ programId: PublicKey,
21
+ version: "v1" | "v2",
22
+
23
+ offerer: PublicKey,
24
+ claimer: PublicKey,
25
+ token: PublicKey,
26
+ amount: BN,
27
+ paymentHash: string,
28
+ sequence: BN,
29
+ expiry: BN,
30
+
31
+ nonce: BN,
32
+ confirmations: number,
33
+ payOut: boolean,
34
+ kind: number,
35
+ payIn: boolean,
36
+ offererAta?: PublicKey,
37
+ claimerAta?: PublicKey,
38
+
39
+ securityDeposit: BN,
40
+ claimerBounty: BN,
41
+
42
+ txoHash?: string,
43
+ offererInitializer?: boolean
44
+ };
45
+
46
+ export function isSerializedData(obj: any): obj is ({type: "sol"} & Serialized<SolanaSwapData>) {
47
+ return obj.type==="sol";
48
+ }
49
+
50
+ /**
51
+ * Represents Solana swap data for executing PrTLC (on-chain) or HTLC (lightning) based swaps.
52
+ *
53
+ * @category Swaps
54
+ */
55
+ export class SolanaSwapData extends SwapData {
56
+
57
+ /**
58
+ * Program ID for which this swap data was created
59
+ */
60
+ programId: PublicKey;
61
+ /**
62
+ * Program version for which this swap was created
63
+ */
64
+ version: "v1" | "v2";
65
+ /**
66
+ * Offerer address funding the swap.
67
+ */
68
+ offerer: PublicKey;
69
+ /**
70
+ * Claimer address receiving the swap funds.
71
+ */
72
+ claimer: PublicKey;
73
+ /**
74
+ * Token mint used for the swap.
75
+ */
76
+ token: PublicKey;
77
+ /**
78
+ * Swap amount.
79
+ */
80
+ amount: BN;
81
+ /**
82
+ * Payment hash identifying the swap.
83
+ */
84
+ paymentHash: string;
85
+ /**
86
+ * Swap sequence used for uniqueness.
87
+ */
88
+ sequence: BN;
89
+ /**
90
+ * Swap expiry timestamp.
91
+ */
92
+ expiry: BN;
93
+ /**
94
+ * Nonce used in claim hash derivation.
95
+ */
96
+ nonce: BN;
97
+ /**
98
+ * Required bitcoin confirmations for claim.
99
+ */
100
+ confirmations: number;
101
+ /**
102
+ * Whether funds are paid out to claimer wallet directly.
103
+ */
104
+ payOut: boolean;
105
+ /**
106
+ * Solana on-chain swap kind discriminator.
107
+ */
108
+ kind: number;
109
+ /**
110
+ * Whether funds are paid in from offerer wallet.
111
+ */
112
+ payIn: boolean;
113
+ /**
114
+ * Optional claimer associated token account.
115
+ */
116
+ claimerAta?: PublicKey;
117
+ /**
118
+ * Optional offerer associated token account.
119
+ */
120
+ offererAta?: PublicKey;
121
+
122
+ /**
123
+ * Security deposit amount.
124
+ */
125
+ securityDeposit: BN;
126
+ /**
127
+ * Claimer bounty amount.
128
+ */
129
+ claimerBounty: BN;
130
+
131
+ /**
132
+ * Optional txo hash hint.
133
+ */
134
+ txoHash?: string;
135
+
136
+ /**
137
+ * Optional flag whether the offerer is the initializer for V2 swap data
138
+ */
139
+ offererInitializer?: boolean;
140
+
141
+ /**
142
+ * Creates swap data from structured constructor arguments.
143
+ *
144
+ * @param args Swap data fields
145
+ */
146
+ constructor(args: SolanaSwapDataCtorArgs);
147
+ /**
148
+ * Deserializes swap data from serialized storage representation.
149
+ *
150
+ * @param data Serialized swap data from {@link SolanaSwapData.serialize}
151
+ */
152
+ constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
153
+
154
+ constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
155
+ super();
156
+ if(!isSerializedData(data)) {
157
+ this.programId = data.programId;
158
+ this.version = data.version;
159
+ this.offerer = data.offerer;
160
+ this.claimer = data.claimer;
161
+ this.token = data.token;
162
+ this.amount = data.amount;
163
+ this.paymentHash = data.paymentHash;
164
+ this.sequence = data.sequence;
165
+ this.expiry = data.expiry;
166
+ this.nonce = data.nonce;
167
+ this.confirmations = data.confirmations;
168
+ this.payOut = data.payOut;
169
+ this.kind = data.kind;
170
+ this.payIn = data.payIn;
171
+ this.claimerAta = data.claimerAta;
172
+ this.offererAta = data.offererAta;
173
+ this.securityDeposit = data.securityDeposit;
174
+ this.claimerBounty = data.claimerBounty;
175
+ this.txoHash = data.txoHash;
176
+ this.offererInitializer = data.offererInitializer;
177
+ } else {
178
+ this.programId = new PublicKey(data.programId ?? "4hfUykhqmD7ZRvNh1HuzVKEY7ToENixtdUKZspNDCrEM");
179
+ this.version = data.version ?? "v1";
180
+ this.offerer = new PublicKey(data.offerer);
181
+ this.claimer = new PublicKey(data.claimer);
182
+ this.token = new PublicKey(data.token);
183
+ this.amount = new BN(data.amount);
184
+ this.paymentHash = data.paymentHash;
185
+ this.sequence = new BN(data.sequence);
186
+ this.expiry = new BN(data.expiry);
187
+ this.nonce = new BN(data.nonce);
188
+ this.confirmations = data.confirmations;
189
+ this.payOut = data.payOut;
190
+ this.kind = data.kind;
191
+ this.payIn = data.payIn;
192
+ this.claimerAta = data.claimerAta==null ? undefined : new PublicKey(data.claimerAta);
193
+ this.offererAta = data.offererAta==null ? undefined : new PublicKey(data.offererAta);
194
+ this.securityDeposit = new BN(data.securityDeposit);
195
+ this.claimerBounty = new BN(data.claimerBounty);
196
+ this.txoHash = data.txoHash;
197
+ this.offererInitializer = data.offererInitializer;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * @inheritDoc
203
+ */
204
+ getOfferer(): string {
205
+ return this.offerer.toBase58();
206
+ }
207
+
208
+ /**
209
+ * @inheritDoc
210
+ */
211
+ setOfferer(newOfferer: string) {
212
+ this.offerer = new PublicKey(newOfferer);
213
+ this.offererAta = getAssociatedTokenAddressSync(this.token, this.offerer);
214
+ this.payIn = true;
215
+ }
216
+
217
+ /**
218
+ * @inheritDoc
219
+ */
220
+ getClaimer(): string {
221
+ return this.claimer.toBase58();
222
+ }
223
+
224
+ /**
225
+ * @inheritDoc
226
+ */
227
+ setClaimer(newClaimer: string) {
228
+ this.claimer = new PublicKey(newClaimer);
229
+ this.payIn = false;
230
+ this.payOut = true;
231
+ this.claimerAta = getAssociatedTokenAddressSync(this.token, this.claimer);
232
+ }
233
+
234
+ /**
235
+ * @inheritDoc
236
+ */
237
+ serialize(): {type: "sol"} & Serialized<SolanaSwapData> {
238
+ return {
239
+ type: "sol",
240
+ programId: this.programId?.toBase58(),
241
+ version: this.version,
242
+ offerer: this.offerer?.toBase58(),
243
+ claimer: this.claimer?.toBase58(),
244
+ token: this.token?.toBase58(),
245
+ amount: this.amount?.toString(10),
246
+ paymentHash: this.paymentHash,
247
+ sequence: this.sequence?.toString(10),
248
+ expiry: this.expiry?.toString(10),
249
+ nonce: this.nonce?.toString(10),
250
+ confirmations: this.confirmations,
251
+ payOut: this.payOut,
252
+ kind: this.kind,
253
+ payIn: this.payIn,
254
+ offererAta: this.offererAta?.toBase58(),
255
+ claimerAta: this.claimerAta?.toBase58(),
256
+ securityDeposit: this.securityDeposit?.toString(10),
257
+ claimerBounty: this.claimerBounty?.toString(10),
258
+ txoHash: this.txoHash,
259
+ offererInitializer: this.offererInitializer
260
+ }
261
+ }
262
+
263
+ /**
264
+ * @inheritDoc
265
+ */
266
+ getAmount(): bigint {
267
+ return toBigInt(this.amount);
268
+ }
269
+
270
+ /**
271
+ * @inheritDoc
272
+ */
273
+ getToken(): string {
274
+ return this.token.toString();
275
+ }
276
+
277
+ /**
278
+ * @inheritDoc
279
+ */
280
+ isToken(token: string): boolean {
281
+ return this.token.equals(new PublicKey(token));
282
+ }
283
+
284
+ /**
285
+ * @inheritDoc
286
+ */
287
+ getType(): ChainSwapType {
288
+ return SolanaSwapData.kindToType(this.kind);
289
+ }
290
+
291
+ /**
292
+ * @inheritDoc
293
+ */
294
+ getExpiry(): bigint {
295
+ if(this.expiry.lt(EXPIRY_BLOCKHEIGHT_THRESHOLD)) throw new Error("Expiry expressed as bitcoin blockheight!");
296
+ return toBigInt(this.expiry);
297
+ }
298
+
299
+ /**
300
+ * @inheritDoc
301
+ */
302
+ getConfirmationsHint(): number {
303
+ return this.confirmations;
304
+ }
305
+
306
+ /**
307
+ * @inheritDoc
308
+ */
309
+ getNonceHint(): bigint {
310
+ return toBigInt(this.nonce);
311
+ }
312
+
313
+ /**
314
+ * @inheritDoc
315
+ */
316
+ isPayIn(): boolean {
317
+ return this.payIn;
318
+ }
319
+
320
+ /**
321
+ * @inheritDoc
322
+ */
323
+ isPayOut(): boolean {
324
+ return this.payOut;
325
+ }
326
+
327
+ /**
328
+ * @inheritDoc
329
+ */
330
+ isTrackingReputation(): boolean {
331
+ return !this.payOut;
332
+ }
333
+
334
+ /**
335
+ * @inheritDoc
336
+ */
337
+ getClaimHash(): string {
338
+ return toClaimHash(this.paymentHash, toBigInt(this.nonce), this.confirmations);
339
+ }
340
+
341
+ /**
342
+ * @inheritDoc
343
+ */
344
+ getEscrowHash(): string {
345
+ return toEscrowHash(this.paymentHash, this.sequence);
346
+ }
347
+
348
+ /**
349
+ * @inheritDoc
350
+ */
351
+ getSequence(): bigint {
352
+ return toBigInt(this.sequence);
353
+ }
354
+
355
+ /**
356
+ * @inheritDoc
357
+ */
358
+ getTxoHashHint(): string | null {
359
+ if(this.txoHash==="0000000000000000000000000000000000000000000000000000000000000000") return null; //Txo hash opt-out flag
360
+ return this.txoHash ?? null
361
+ }
362
+
363
+ /**
364
+ * @inheritDoc
365
+ */
366
+ getHTLCHashHint(): string | null {
367
+ if(this.getType()===ChainSwapType.HTLC) return this.paymentHash;
368
+ return null;
369
+ }
370
+
371
+ /**
372
+ * @inheritDoc
373
+ */
374
+ getExtraData(): string | null {
375
+ return this.txoHash ?? null;
376
+ }
377
+
378
+ /**
379
+ * @inheritDoc
380
+ */
381
+ setExtraData(txoHash: string): void {
382
+ this.txoHash = txoHash;
383
+ }
384
+
385
+ /**
386
+ * @inheritDoc
387
+ */
388
+ getSecurityDeposit(): bigint {
389
+ return toBigInt(this.securityDeposit);
390
+ }
391
+
392
+ /**
393
+ * @inheritDoc
394
+ */
395
+ getClaimerBounty(): bigint {
396
+ return toBigInt(this.claimerBounty);
397
+ }
398
+
399
+ /**
400
+ * @inheritDoc
401
+ */
402
+ getTotalDeposit(): bigint {
403
+ return toBigInt(this.claimerBounty.lt(this.securityDeposit) ? this.securityDeposit : this.claimerBounty);
404
+ }
405
+
406
+ /**
407
+ * Serializes the swap data into the Solana program `SwapData` struct representation.
408
+ */
409
+ toSwapDataStruct(): IdlTypes<SwapProgram>["SwapData"] {
410
+ return {
411
+ kind: SwapTypeEnum.fromNumber(this.kind as 0 | 1 | 2 | 3),
412
+ confirmations: this.confirmations,
413
+ nonce: this.nonce,
414
+ hash: [...Buffer.from(this.paymentHash, "hex")],
415
+ payIn: this.payIn,
416
+ payOut: this.payOut,
417
+ amount: this.amount,
418
+ expiry: this.expiry,
419
+ sequence: this.sequence
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Checks whether the provided escrow account matches this swap data.
425
+ *
426
+ * @param account Escrow account data fetched from chain
427
+ */
428
+ correctPDA(account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]): boolean {
429
+ return SwapTypeEnum.toNumber(account.data.kind)===this.kind &&
430
+ account.data.confirmations===this.confirmations &&
431
+ this.nonce.eq(account.data.nonce) &&
432
+ Buffer.from(account.data.hash).toString("hex")===this.paymentHash &&
433
+ account.data.payIn===this.payIn &&
434
+ account.data.payOut===this.payOut &&
435
+ this.amount.eq(account.data.amount) &&
436
+ this.expiry.eq(account.data.expiry) &&
437
+ this.sequence.eq(account.data.sequence) &&
438
+
439
+ account.offerer.equals(this.offerer) &&
440
+ (this.offererAta==null || account.offererAta.equals(this.offererAta)) &&
441
+ account.claimer.equals(this.claimer) &&
442
+ (this.claimerAta==null || account.claimerAta.equals(this.claimerAta)) &&
443
+ account.mint.equals(this.token) &&
444
+ this.claimerBounty.eq(account.claimerBounty) &&
445
+ this.securityDeposit.eq(account.securityDeposit) &&
446
+ (this.offererInitializer==null || account.offererInitializer===this.offererInitializer);
447
+ }
448
+
449
+ /**
450
+ * @inheritDoc
451
+ */
452
+ equals(other: SolanaSwapData): boolean {
453
+ if(this.claimerAta==null && other.claimerAta!=null) return false;
454
+ if(this.claimerAta!=null && other.claimerAta==null) return false;
455
+ if(this.claimerAta!=null && other.claimerAta!=null) {
456
+ if(!this.claimerAta.equals(other.claimerAta)) return false;
457
+ }
458
+
459
+ if(this.offererAta==null && other.offererAta!=null) return false;
460
+ if(this.offererAta!=null && other.offererAta==null) return false;
461
+ if(this.offererAta!=null && other.offererAta!=null) {
462
+ if(!this.offererAta.equals(other.offererAta)) return false;
463
+ }
464
+
465
+ if(this.offererInitializer==null && other.offererInitializer!=null) return false;
466
+ if(this.offererInitializer!=null && other.offererInitializer==null) return false;
467
+ if(this.offererInitializer!=null && other.offererInitializer!=null) {
468
+ if(this.offererInitializer!==other.offererInitializer) return false;
469
+ }
470
+
471
+ return other.kind===this.kind &&
472
+ other.confirmations===this.confirmations &&
473
+ this.nonce.eq(other.nonce) &&
474
+ other.paymentHash===this.paymentHash &&
475
+ this.sequence.eq(other.sequence) &&
476
+ other.payIn===this.payIn &&
477
+ other.payOut===this.payOut &&
478
+ other.offerer.equals(this.offerer) &&
479
+ other.claimer.equals(this.claimer) &&
480
+ other.expiry.eq(this.expiry) &&
481
+ other.amount.eq(this.amount) &&
482
+ other.securityDeposit.eq(this.securityDeposit) &&
483
+ other.claimerBounty.eq(this.claimerBounty) &&
484
+ other.token.equals(this.token)
485
+ }
486
+
487
+ /**
488
+ * Converts initialize instruction data into {@link SolanaSwapData}.
489
+ *
490
+ * @param programId
491
+ * @param version
492
+ * @param initIx Decoded initialize instruction
493
+ * @param txoHash Parsed txo hash hint from initialize event
494
+ * @returns Converted and parsed swap data
495
+ */
496
+ static fromInstruction(
497
+ programId: PublicKey,
498
+ version: "v1" | "v2",
499
+ initIx: InitInstruction,
500
+ txoHash: string
501
+ ): SolanaSwapData {
502
+ const paymentHash: Buffer = Buffer.from(initIx.data.swapData.hash);
503
+ let securityDeposit: BN = new BN(0);
504
+ let claimerBounty: BN = new BN(0);
505
+ let payIn: boolean = true;
506
+ if(initIx.name === "offererInitialize") {
507
+ payIn = false;
508
+ securityDeposit = initIx.data.securityDeposit;
509
+ claimerBounty = initIx.data.claimerBounty;
510
+ }
511
+ if(version!=="v1" && initIx.name === "offererInitializePayIn") {
512
+ payIn = true;
513
+ securityDeposit = initIx.data.securityDeposit;
514
+ claimerBounty = initIx.data.claimerBounty;
515
+ }
516
+
517
+ return new SolanaSwapData({
518
+ programId,
519
+ version,
520
+ offerer: initIx.accounts.offerer,
521
+ claimer: initIx.accounts.claimer,
522
+ token: initIx.accounts.mint,
523
+ amount: initIx.data.swapData.amount,
524
+ paymentHash: paymentHash.toString("hex"),
525
+ sequence: initIx.data.swapData.sequence,
526
+ expiry: initIx.data.swapData.expiry,
527
+ nonce: initIx.data.swapData.nonce,
528
+ confirmations: initIx.data.swapData.confirmations,
529
+ payOut: initIx.data.swapData.payOut,
530
+ kind: SwapTypeEnum.toNumber(initIx.data.swapData.kind),
531
+ payIn,
532
+ offererAta: initIx.name === "offererInitializePayIn" ? initIx.accounts.offererAta : PublicKey.default,
533
+ claimerAta: initIx.data.swapData.payOut ? initIx.accounts.claimerAta : PublicKey.default,
534
+ securityDeposit,
535
+ claimerBounty,
536
+ txoHash,
537
+ offererInitializer: initIx.accounts.initializer!=null ? initIx.accounts.initializer.equals(initIx.accounts.offerer) : undefined
538
+ });
539
+ }
540
+
541
+ /**
542
+ * Deserializes swap data from an on-chain escrow account state.
543
+ *
544
+ * @param programId
545
+ * @param version
546
+ * @param account Escrow account state as returned by Anchor
547
+ */
548
+ static fromEscrowState(
549
+ programId: PublicKey,
550
+ version: "v1" | "v2",
551
+ account: IdlAccounts<SwapProgram | SwapProgramV2>["escrowState"]
552
+ ) {
553
+ const data: IdlTypes<SwapProgram | SwapProgramV2>["SwapData"] = account.data;
554
+
555
+ return new SolanaSwapData({
556
+ programId,
557
+ version,
558
+ offerer: account.offerer,
559
+ claimer: account.claimer,
560
+ token: account.mint,
561
+ amount: data.amount,
562
+ paymentHash: Buffer.from(data.hash).toString("hex"),
563
+ sequence: data.sequence,
564
+ expiry: data.expiry,
565
+ nonce: data.nonce,
566
+ confirmations: data.confirmations,
567
+ payOut: data.payOut,
568
+ kind: SwapTypeEnum.toNumber(data.kind),
569
+ payIn: data.payIn,
570
+ offererAta: account.offererAta,
571
+ claimerAta: account.claimerAta,
572
+ securityDeposit: account.securityDeposit,
573
+ claimerBounty: account.claimerBounty,
574
+ offererInitializer: account.offererInitializer ?? undefined
575
+ });
576
+ }
577
+
578
+ /**
579
+ * Converts abstract swap type to Solana program kind discriminator.
580
+ *
581
+ * @param type Chain-agnostic swap type
582
+ */
583
+ static typeToKind(type: ChainSwapType): number {
584
+ switch (type) {
585
+ case ChainSwapType.HTLC:
586
+ return 0;
587
+ case ChainSwapType.CHAIN:
588
+ return 1;
589
+ case ChainSwapType.CHAIN_NONCED:
590
+ return 2;
591
+ case ChainSwapType.CHAIN_TXID:
592
+ return 3;
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Converts Solana program kind discriminator to abstract swap type.
598
+ *
599
+ * @param value Solana program swap kind value
600
+ */
601
+ static kindToType(value: number): ChainSwapType {
602
+ switch(value) {
603
+ case 0:
604
+ return ChainSwapType.HTLC;
605
+ case 1:
606
+ return ChainSwapType.CHAIN;
607
+ case 2:
608
+ return ChainSwapType.CHAIN_NONCED;
609
+ case 3:
610
+ return ChainSwapType.CHAIN_TXID;
611
+ }
612
+ throw new Error("Unknown swap kind type!");
613
+ }
614
+
615
+ /**
616
+ * @inheritDoc
617
+ */
618
+ isClaimer(address: string) {
619
+ const _address = new PublicKey(address);
620
+ if(this.isPayOut()) {
621
+ //Also check that swapData's ATA is correct
622
+ const ourAta = getAssociatedTokenAddressSync(this.token, _address);
623
+ if(this.claimerAta==null || !this.claimerAta.equals(ourAta)) return false;
624
+ }
625
+ return this.claimer.equals(new PublicKey(address));
626
+ }
627
+
628
+ /**
629
+ * @inheritDoc
630
+ */
631
+ isOfferer(address: string) {
632
+ return this.offerer.equals(new PublicKey(address));
633
+ }
634
+
635
+ /**
636
+ * @inheritDoc
637
+ */
638
+ getDepositToken(): string {
639
+ return SolanaTokens.WSOL_ADDRESS.toString();
640
+ }
641
+
642
+ /**
643
+ * @inheritDoc
644
+ */
645
+ isDepositToken(token: string): boolean {
646
+ return SolanaTokens.WSOL_ADDRESS.equals(new PublicKey(token));
647
+ }
648
+
649
+ /**
650
+ * @inheritDoc
651
+ */
652
+ getEscrowStruct(): any {
653
+ return {
654
+ accounts: {
655
+ offerer: this.offerer.toString(),
656
+ claimer: this.claimer.toString(),
657
+ claimerAta: this.claimerAta==null || this.claimerAta.equals(PublicKey.default) ? null : this.claimerAta.toString(),
658
+ offererAta: this.offererAta==null || this.offererAta.equals(PublicKey.default) ? null : this.offererAta.toString(),
659
+ claimerUserData: SolanaSwapProgram._SwapUserVault(this.programId, this.claimer, this.token).toString(),
660
+ offererUserData: SolanaSwapProgram._SwapUserVault(this.programId, this.offerer, this.token).toString(),
661
+
662
+ initializer: this.offererInitializer!=null
663
+ ? (
664
+ this.offererInitializer ? this.offerer.toString() : this.claimer.toString()
665
+ )
666
+ : (
667
+ this.isPayIn() ? this.offerer.toString() : this.claimer.toString()
668
+ ),
669
+
670
+ escrowState: SolanaSwapProgram._SwapEscrowState(this.programId, Buffer.from(this.paymentHash, "hex")).toString(),
671
+ mint: this.token.toString(),
672
+ vault: SolanaSwapProgram._SwapVault(this.programId, this.token).toString(),
673
+ vaultAuthority: SolanaSwapProgram._SwapVaultAuthority(this.programId).toString(),
674
+
675
+ systemProgram: SystemProgram.programId.toString(),
676
+ tokenProgram: TOKEN_PROGRAM_ID.toString(),
677
+ ixSysvar: SYSVAR_INSTRUCTIONS_PUBKEY.toString(),
678
+ },
679
+ data: {
680
+ kind: SwapTypeEnum.fromNumber(this.kind as 0 | 1 | 2 | 3),
681
+ confirmations: this.confirmations,
682
+ nonce: this.nonce.toString(10),
683
+ hash: [...Buffer.from(this.paymentHash, "hex")],
684
+ payIn: this.payIn,
685
+ payOut: this.payOut,
686
+ amount: this.amount.toString(10),
687
+ expiry: this.expiry.toString(10),
688
+ sequence: this.sequence.toString(10)
689
+ }
690
+ }
691
+ }
692
+
693
+ }
694
+
695
+ export class SolanaSwapDataV1 extends SolanaSwapData {
696
+ /**
697
+ * Creates swap data from structured constructor arguments.
698
+ *
699
+ * @param args Swap data fields
700
+ */
701
+ constructor(args: SolanaSwapDataCtorArgs);
702
+ /**
703
+ * Deserializes swap data from serialized storage representation.
704
+ *
705
+ * @param data Serialized swap data from {@link SolanaSwapData.serialize}
706
+ */
707
+ constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
708
+
709
+ constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
710
+ super(data as any);
711
+ if(this.version!=="v1") throw new Error(`Invalid swap data version, expected v1, got ${this.version}`);
712
+ }
713
+ }
714
+
715
+ export class SolanaSwapDataV2 extends SolanaSwapData {
716
+ /**
717
+ * Creates swap data from structured constructor arguments.
718
+ *
719
+ * @param args Swap data fields
720
+ */
721
+ constructor(args: SolanaSwapDataCtorArgs);
722
+ /**
723
+ * Deserializes swap data from serialized storage representation.
724
+ *
725
+ * @param data Serialized swap data from {@link SolanaSwapData.serialize}
726
+ */
727
+ constructor(data: {type: "sol"} & Serialized<SolanaSwapData>);
728
+
729
+ constructor(data: ({type: "sol"} & Serialized<SolanaSwapData>) | SolanaSwapDataCtorArgs) {
730
+ super(data as any);
731
+ if(this.version!=="v2") throw new Error(`Invalid swap data version, expected v2, got ${this.version}`);
732
+ }
733
+ }
734
+
735
+ SwapData.deserializers["sol"] = SolanaSwapData;