@atomiqlabs/chain-solana 7.2.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 (105) hide show
  1. package/LICENSE +201 -0
  2. package/dist/index.d.ts +28 -0
  3. package/dist/index.js +44 -0
  4. package/dist/solana/SolanaChainType.d.ts +9 -0
  5. package/dist/solana/SolanaChainType.js +2 -0
  6. package/dist/solana/base/SolanaAction.d.ts +26 -0
  7. package/dist/solana/base/SolanaAction.js +99 -0
  8. package/dist/solana/base/SolanaBase.d.ts +36 -0
  9. package/dist/solana/base/SolanaBase.js +30 -0
  10. package/dist/solana/base/SolanaModule.d.ts +14 -0
  11. package/dist/solana/base/SolanaModule.js +13 -0
  12. package/dist/solana/base/modules/SolanaAddresses.d.ts +9 -0
  13. package/dist/solana/base/modules/SolanaAddresses.js +23 -0
  14. package/dist/solana/base/modules/SolanaBlocks.d.ts +28 -0
  15. package/dist/solana/base/modules/SolanaBlocks.js +83 -0
  16. package/dist/solana/base/modules/SolanaEvents.d.ts +25 -0
  17. package/dist/solana/base/modules/SolanaEvents.js +69 -0
  18. package/dist/solana/base/modules/SolanaFees.d.ts +121 -0
  19. package/dist/solana/base/modules/SolanaFees.js +393 -0
  20. package/dist/solana/base/modules/SolanaSignatures.d.ts +23 -0
  21. package/dist/solana/base/modules/SolanaSignatures.js +39 -0
  22. package/dist/solana/base/modules/SolanaSlots.d.ts +31 -0
  23. package/dist/solana/base/modules/SolanaSlots.js +81 -0
  24. package/dist/solana/base/modules/SolanaTokens.d.ts +134 -0
  25. package/dist/solana/base/modules/SolanaTokens.js +269 -0
  26. package/dist/solana/base/modules/SolanaTransactions.d.ts +124 -0
  27. package/dist/solana/base/modules/SolanaTransactions.js +354 -0
  28. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +229 -0
  29. package/dist/solana/btcrelay/SolanaBtcRelay.js +477 -0
  30. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -0
  31. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -0
  32. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -0
  33. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -0
  34. package/dist/solana/btcrelay/program/programIdl.json +671 -0
  35. package/dist/solana/events/SolanaChainEvents.d.ts +84 -0
  36. package/dist/solana/events/SolanaChainEvents.js +268 -0
  37. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -0
  38. package/dist/solana/events/SolanaChainEventsBrowser.js +202 -0
  39. package/dist/solana/program/SolanaProgramBase.d.ts +34 -0
  40. package/dist/solana/program/SolanaProgramBase.js +43 -0
  41. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +58 -0
  42. package/dist/solana/program/modules/SolanaProgramEvents.js +114 -0
  43. package/dist/solana/swaps/SolanaSwapData.d.ts +55 -0
  44. package/dist/solana/swaps/SolanaSwapData.js +251 -0
  45. package/dist/solana/swaps/SolanaSwapModule.d.ts +9 -0
  46. package/dist/solana/swaps/SolanaSwapModule.js +12 -0
  47. package/dist/solana/swaps/SolanaSwapProgram.d.ts +218 -0
  48. package/dist/solana/swaps/SolanaSwapProgram.js +523 -0
  49. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -0
  50. package/dist/solana/swaps/SwapTypeEnum.js +42 -0
  51. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -0
  52. package/dist/solana/swaps/modules/SolanaDataAccount.js +255 -0
  53. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +72 -0
  54. package/dist/solana/swaps/modules/SolanaLpVault.js +196 -0
  55. package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -0
  56. package/dist/solana/swaps/modules/SwapClaim.js +307 -0
  57. package/dist/solana/swaps/modules/SwapInit.d.ts +212 -0
  58. package/dist/solana/swaps/modules/SwapInit.js +508 -0
  59. package/dist/solana/swaps/modules/SwapRefund.d.ts +83 -0
  60. package/dist/solana/swaps/modules/SwapRefund.js +264 -0
  61. package/dist/solana/swaps/programIdl.json +945 -0
  62. package/dist/solana/swaps/programTypes.d.ts +943 -0
  63. package/dist/solana/swaps/programTypes.js +945 -0
  64. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -0
  65. package/dist/solana/wallet/SolanaKeypairWallet.js +33 -0
  66. package/dist/solana/wallet/SolanaSigner.d.ts +10 -0
  67. package/dist/solana/wallet/SolanaSigner.js +16 -0
  68. package/dist/utils/Utils.d.ts +43 -0
  69. package/dist/utils/Utils.js +143 -0
  70. package/package.json +40 -0
  71. package/src/index.ts +35 -0
  72. package/src/solana/SolanaChainType.ts +20 -0
  73. package/src/solana/base/SolanaAction.ts +109 -0
  74. package/src/solana/base/SolanaBase.ts +57 -0
  75. package/src/solana/base/SolanaModule.ts +21 -0
  76. package/src/solana/base/modules/SolanaAddresses.ts +22 -0
  77. package/src/solana/base/modules/SolanaBlocks.ts +79 -0
  78. package/src/solana/base/modules/SolanaEvents.ts +58 -0
  79. package/src/solana/base/modules/SolanaFees.ts +445 -0
  80. package/src/solana/base/modules/SolanaSignatures.ts +40 -0
  81. package/src/solana/base/modules/SolanaSlots.ts +83 -0
  82. package/src/solana/base/modules/SolanaTokens.ts +310 -0
  83. package/src/solana/base/modules/SolanaTransactions.ts +366 -0
  84. package/src/solana/btcrelay/SolanaBtcRelay.ts +591 -0
  85. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +58 -0
  86. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +103 -0
  87. package/src/solana/btcrelay/program/programIdl.json +671 -0
  88. package/src/solana/events/SolanaChainEvents.ts +286 -0
  89. package/src/solana/events/SolanaChainEventsBrowser.ts +251 -0
  90. package/src/solana/program/SolanaProgramBase.ts +77 -0
  91. package/src/solana/program/modules/SolanaProgramEvents.ts +140 -0
  92. package/src/solana/swaps/SolanaSwapData.ts +360 -0
  93. package/src/solana/swaps/SolanaSwapModule.ts +17 -0
  94. package/src/solana/swaps/SolanaSwapProgram.ts +739 -0
  95. package/src/solana/swaps/SwapTypeEnum.ts +30 -0
  96. package/src/solana/swaps/modules/SolanaDataAccount.ts +309 -0
  97. package/src/solana/swaps/modules/SolanaLpVault.ts +216 -0
  98. package/src/solana/swaps/modules/SwapClaim.ts +397 -0
  99. package/src/solana/swaps/modules/SwapInit.ts +621 -0
  100. package/src/solana/swaps/modules/SwapRefund.ts +316 -0
  101. package/src/solana/swaps/programIdl.json +945 -0
  102. package/src/solana/swaps/programTypes.ts +1885 -0
  103. package/src/solana/wallet/SolanaKeypairWallet.ts +36 -0
  104. package/src/solana/wallet/SolanaSigner.ts +23 -0
  105. package/src/utils/Utils.ts +145 -0
@@ -0,0 +1,316 @@
1
+ import {SolanaSwapModule} from "../SolanaSwapModule";
2
+ import {SolanaSwapData} from "../SolanaSwapData";
3
+ import * as createHash from "create-hash";
4
+ import {sign} from "tweetnacl";
5
+ import {SignatureVerificationError, SwapDataVerificationError} from "@atomiqlabs/base";
6
+ import * as BN from "bn.js";
7
+ import {SolanaTx} from "../../base/modules/SolanaTransactions";
8
+ import {
9
+ Ed25519Program,
10
+ PublicKey,
11
+ SYSVAR_INSTRUCTIONS_PUBKEY
12
+ } from "@solana/web3.js";
13
+ import {
14
+ getAssociatedTokenAddressSync,
15
+ TOKEN_PROGRAM_ID
16
+ } from "@solana/spl-token";
17
+ import {SolanaAction} from "../../base/SolanaAction";
18
+ import {tryWithRetries} from "../../../utils/Utils";
19
+ import {Buffer} from "buffer";
20
+ import {SolanaSigner} from "../../wallet/SolanaSigner";
21
+
22
+
23
+ export class SwapRefund extends SolanaSwapModule {
24
+
25
+ private static readonly CUCosts = {
26
+ REFUND: 15000,
27
+ REFUND_PAY_OUT: 50000
28
+ };
29
+
30
+ /**
31
+ * Action for generic Refund instruction
32
+ *
33
+ * @param swapData
34
+ * @param refundAuthTimeout optional refund authorization timeout (should be 0 for refunding expired swaps)
35
+ * @constructor
36
+ * @private
37
+ */
38
+ private async Refund(swapData: SolanaSwapData, refundAuthTimeout?: BN): Promise<SolanaAction> {
39
+ const accounts = {
40
+ offerer: swapData.offerer,
41
+ claimer: swapData.claimer,
42
+ escrowState: this.root.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")),
43
+ claimerUserData: !swapData.payOut ? this.root.SwapUserVault(swapData.claimer, swapData.token) : null,
44
+ ixSysvar: refundAuthTimeout!=null ? SYSVAR_INSTRUCTIONS_PUBKEY : null
45
+ };
46
+
47
+ const useTimeout = refundAuthTimeout!=null ? refundAuthTimeout : new BN(0);
48
+ if(swapData.isPayIn()) {
49
+ const ata = getAssociatedTokenAddressSync(swapData.token, swapData.offerer);
50
+
51
+ return new SolanaAction(swapData.offerer, this.root,
52
+ await this.program.methods
53
+ .offererRefundPayIn(useTimeout)
54
+ .accounts({
55
+ ...accounts,
56
+ offererAta: ata,
57
+ vault: this.root.SwapVault(swapData.token),
58
+ vaultAuthority: this.root.SwapVaultAuthority,
59
+ tokenProgram: TOKEN_PROGRAM_ID
60
+ })
61
+ .instruction(),
62
+ SwapRefund.CUCosts.REFUND_PAY_OUT
63
+ );
64
+ } else {
65
+ return new SolanaAction(swapData.offerer, this.root,
66
+ await this.program.methods
67
+ .offererRefund(useTimeout)
68
+ .accounts({
69
+ ...accounts,
70
+ offererUserData: this.root.SwapUserVault(swapData.offerer, swapData.token)
71
+ })
72
+ .instruction(),
73
+ SwapRefund.CUCosts.REFUND
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Action for refunding with signature, adds the Ed25519 verify instruction
80
+ *
81
+ * @param swapData
82
+ * @param timeout
83
+ * @param prefix
84
+ * @param signature
85
+ * @constructor
86
+ * @private
87
+ */
88
+ private async RefundWithSignature(
89
+ swapData: SolanaSwapData,
90
+ timeout: string,
91
+ prefix: string,
92
+ signature: Buffer
93
+ ): Promise<SolanaAction> {
94
+ const action = new SolanaAction(swapData.offerer, this.root,
95
+ Ed25519Program.createInstructionWithPublicKey({
96
+ message: this.getRefundMessage(swapData, prefix, timeout),
97
+ publicKey: swapData.claimer.toBuffer(),
98
+ signature: signature
99
+ }),
100
+ 0,
101
+ null,
102
+ null,
103
+ true
104
+ );
105
+ action.addAction(await this.Refund(swapData, new BN(timeout)));
106
+ return action;
107
+ }
108
+
109
+ /**
110
+ * Gets the message to be signed as a refund authorization
111
+ *
112
+ * @param swapData
113
+ * @param prefix
114
+ * @param timeout
115
+ * @private
116
+ */
117
+ private getRefundMessage(swapData: SolanaSwapData, prefix: string, timeout: string): Buffer {
118
+ const messageBuffers = [
119
+ Buffer.from(prefix, "ascii"),
120
+ swapData.amount.toArrayLike(Buffer, "le", 8),
121
+ swapData.expiry.toArrayLike(Buffer, "le", 8),
122
+ swapData.sequence.toArrayLike(Buffer, "le", 8),
123
+ Buffer.from(swapData.paymentHash, "hex"),
124
+ new BN(timeout).toArrayLike(Buffer, "le", 8)
125
+ ];
126
+
127
+ return createHash("sha256").update(Buffer.concat(messageBuffers)).digest();
128
+ }
129
+
130
+ /**
131
+ * Checks whether we should unwrap the WSOL to SOL when refunding the swap
132
+ *
133
+ * @param swapData
134
+ * @private
135
+ */
136
+ private shouldUnwrap(swapData: SolanaSwapData): boolean {
137
+ return swapData.isPayIn() &&
138
+ swapData.token.equals(this.root.Tokens.WSOL_ADDRESS) &&
139
+ swapData.offerer.equals(swapData.offerer);
140
+ }
141
+
142
+ public signSwapRefund(
143
+ signer: SolanaSigner,
144
+ swapData: SolanaSwapData,
145
+ authorizationTimeout: number
146
+ ): Promise<{ prefix: string; timeout: string; signature: string }> {
147
+ if(signer.keypair==null) throw new Error("Unsupported");
148
+ if(!signer.getPublicKey().equals(swapData.claimer)) throw new Error("Invalid signer, public key mismatch!");
149
+
150
+ const authPrefix = "refund";
151
+ const authTimeout = Math.floor(Date.now()/1000)+authorizationTimeout;
152
+
153
+ const messageBuffer = this.getRefundMessage(swapData, authPrefix, authTimeout.toString(10));
154
+ const signature = sign.detached(messageBuffer, signer.keypair.secretKey);
155
+
156
+ return Promise.resolve({
157
+ prefix: authPrefix,
158
+ timeout: authTimeout.toString(10),
159
+ signature: Buffer.from(signature).toString("hex")
160
+ });
161
+ }
162
+
163
+ public isSignatureValid(swapData: SolanaSwapData, timeout: string, prefix: string, signature: string): Promise<Buffer> {
164
+ if(prefix!=="refund") throw new SignatureVerificationError("Invalid prefix");
165
+
166
+ const expiryTimestamp = new BN(timeout);
167
+ const currentTimestamp = new BN(Math.floor(Date.now() / 1000));
168
+
169
+ const isExpired = expiryTimestamp.sub(currentTimestamp).lt(new BN(this.root.authGracePeriod));
170
+ if(isExpired) throw new SignatureVerificationError("Authorization expired!");
171
+
172
+ const signatureBuffer = Buffer.from(signature, "hex");
173
+ const messageBuffer = this.getRefundMessage(swapData, prefix, timeout);
174
+
175
+ if(!sign.detached.verify(messageBuffer, signatureBuffer, swapData.claimer.toBuffer())) {
176
+ throw new SignatureVerificationError("Invalid signature!");
177
+ }
178
+
179
+ return Promise.resolve(messageBuffer);
180
+ }
181
+
182
+ /**
183
+ * Creates transactions required for refunding timed out swap, also unwraps WSOL to SOL
184
+ *
185
+ * @param swapData swap data to refund
186
+ * @param check whether to check if swap is already expired and refundable
187
+ * @param initAta should initialize ATA if it doesn't exist
188
+ * @param feeRate fee rate to be used for the transactions
189
+ */
190
+ public async txsRefund(
191
+ swapData: SolanaSwapData,
192
+ check?: boolean,
193
+ initAta?: boolean,
194
+ feeRate?: string
195
+ ): Promise<SolanaTx[]> {
196
+ if(check && !await tryWithRetries(() => this.root.isRequestRefundable(swapData.offerer.toString(), swapData), this.retryPolicy)) {
197
+ throw new SwapDataVerificationError("Not refundable yet!");
198
+ }
199
+ const shouldInitAta = swapData.isPayIn() && !await this.root.Tokens.ataExists(swapData.offererAta);
200
+ if(shouldInitAta && !initAta) throw new SwapDataVerificationError("ATA not initialized");
201
+
202
+ if(feeRate==null) feeRate = await this.root.getRefundFeeRate(swapData)
203
+
204
+ const shouldUnwrap = this.shouldUnwrap(swapData);
205
+ const action = new SolanaAction(swapData.offerer, this.root);
206
+ if(shouldInitAta) {
207
+ const initAction = this.root.Tokens.InitAta(swapData.offerer, swapData.offerer, swapData.token, swapData.offererAta);
208
+ if(initAction==null) throw new SwapDataVerificationError("Invalid claimer token account address")
209
+ action.addAction(initAction);
210
+ }
211
+ action.add(await this.Refund(swapData));
212
+ if(shouldUnwrap) action.add(this.root.Tokens.Unwrap(swapData.offerer));
213
+
214
+ this.logger.debug("txsRefund(): creating claim transaction, swap: "+swapData.getHash()+
215
+ " initializingAta: "+shouldInitAta+" unwrapping: "+shouldUnwrap);
216
+
217
+ return [await action.tx(feeRate)];
218
+ }
219
+
220
+ /**
221
+ * Creates transactions required for refunding the swap with authorization signature, also unwraps WSOL to SOL
222
+ *
223
+ * @param swapData swap data to refund
224
+ * @param timeout signature timeout
225
+ * @param prefix signature prefix of the counterparty
226
+ * @param signature signature of the counterparty
227
+ * @param check whether to check if swap is committed before attempting refund
228
+ * @param initAta should initialize ATA if it doesn't exist
229
+ * @param feeRate fee rate to be used for the transactions
230
+ */
231
+ public async txsRefundWithAuthorization(
232
+ swapData: SolanaSwapData,
233
+ timeout: string,
234
+ prefix: string,
235
+ signature: string,
236
+ check?: boolean,
237
+ initAta?: boolean,
238
+ feeRate?: string
239
+ ): Promise<SolanaTx[]> {
240
+ if(check && !await tryWithRetries(() => this.root.isCommited(swapData), this.retryPolicy)) {
241
+ throw new SwapDataVerificationError("Not correctly committed");
242
+ }
243
+ await tryWithRetries(
244
+ () => this.isSignatureValid(swapData, timeout, prefix, signature),
245
+ this.retryPolicy,
246
+ (e) => e instanceof SignatureVerificationError
247
+ );
248
+ const shouldInitAta = swapData.isPayIn() && !await this.root.Tokens.ataExists(swapData.offererAta);
249
+ if(shouldInitAta && !initAta) throw new SwapDataVerificationError("ATA not initialized");
250
+
251
+ if(feeRate==null) feeRate = await this.root.getRefundFeeRate(swapData);
252
+ console.log("[SolanaSwapProgram] txsRefundsWithAuthorization: feeRate: ", feeRate);
253
+
254
+ const signatureBuffer = Buffer.from(signature, "hex");
255
+
256
+ const shouldUnwrap = this.shouldUnwrap(swapData);
257
+ const action = await this.RefundWithSignature(swapData, timeout, prefix, signatureBuffer);
258
+ if(shouldInitAta) {
259
+ const initAction = this.root.Tokens.InitAta(swapData.offerer, swapData.offerer, swapData.token, swapData.offererAta);
260
+ if(initAction==null) throw new SwapDataVerificationError("Invalid claimer token account address");
261
+ action.addAction(initAction, 1); //Need to add it after the Ed25519 verify IX, but before the actual refund IX
262
+ }
263
+ if(shouldUnwrap) action.add(this.root.Tokens.Unwrap(swapData.offerer));
264
+
265
+ this.logger.debug("txsRefundWithAuthorization(): creating claim transaction, swap: "+swapData.getHash()+
266
+ " initializingAta: "+shouldInitAta+" unwrapping: "+shouldUnwrap+
267
+ " auth expiry: "+timeout+" signature: "+signature);
268
+
269
+ return [await action.tx(feeRate)];
270
+ }
271
+
272
+ public getRefundFeeRate(swapData: SolanaSwapData): Promise<string> {
273
+ const accounts: PublicKey[] = [];
274
+ if(swapData.payIn) {
275
+ if(swapData.token!=null) accounts.push(this.root.SwapVault(swapData.token));
276
+ if(swapData.offerer!=null) accounts.push(swapData.offerer);
277
+ if(swapData.claimer!=null) accounts.push(swapData.claimer);
278
+ if(swapData.offererAta!=null && !swapData.offererAta.equals(PublicKey.default)) accounts.push(swapData.offererAta);
279
+ } else {
280
+ if(swapData.offerer!=null) {
281
+ accounts.push(swapData.offerer);
282
+ if(swapData.token!=null) accounts.push(this.root.SwapUserVault(swapData.offerer, swapData.token));
283
+ }
284
+ if(swapData.claimer!=null) accounts.push(swapData.claimer);
285
+ }
286
+
287
+ if(swapData.paymentHash!=null) accounts.push(this.root.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")));
288
+
289
+ return this.root.Fees.getFeeRate(accounts);
290
+ }
291
+
292
+ /**
293
+ * Get the estimated solana transaction fee of the refund transaction, this fee might be negative since it
294
+ * includes the rebate for closing the swap PDA
295
+ */
296
+ async getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<BN> {
297
+ const rawFee = await this.getRawRefundFee(swapData, feeRate);
298
+ return rawFee.sub(new BN(this.root.ESCROW_STATE_RENT_EXEMPT));
299
+ }
300
+
301
+ /**
302
+ * Get the estimated solana transaction fee of the refund transaction
303
+ */
304
+ async getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<BN> {
305
+ if(swapData==null) return new BN(10000);
306
+
307
+ feeRate = feeRate || await this.getRefundFeeRate(swapData);
308
+
309
+ const computeBudget = swapData.payIn ? SwapRefund.CUCosts.REFUND_PAY_OUT : SwapRefund.CUCosts.REFUND;
310
+
311
+ return new BN(10000).add(
312
+ this.root.Fees.getPriorityFee(computeBudget, feeRate)
313
+ );
314
+ }
315
+
316
+ }