@atomiqlabs/chain-solana 12.0.12 → 12.0.13

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 +11 -11
  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 +65 -65
  20. package/dist/solana/chain/SolanaChainInterface.js +125 -125
  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 +68 -68
  28. package/dist/solana/chain/modules/SolanaEvents.js +225 -225
  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 +323 -323
  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 +75 -75
  42. package/dist/solana/events/SolanaChainEventsBrowser.js +172 -172
  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 +53 -53
  48. package/dist/solana/program/modules/SolanaProgramEvents.js +114 -114
  49. package/dist/solana/swaps/SolanaSwapData.d.ts +71 -71
  50. package/dist/solana/swaps/SolanaSwapData.js +292 -292
  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 +224 -224
  54. package/dist/solana/swaps/SolanaSwapProgram.js +570 -570
  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 +262 -262
  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 +11 -11
  73. package/dist/solana/wallet/SolanaSigner.js +17 -17
  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 +27 -27
  79. package/src/solana/SolanaChains.ts +23 -23
  80. package/src/solana/SolanaInitializer.ts +102 -102
  81. package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -589
  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 +192 -192
  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 +256 -256
  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 +365 -365
  96. package/src/solana/events/SolanaChainEvents.ts +299 -299
  97. package/src/solana/events/SolanaChainEventsBrowser.ts +209 -209
  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 +155 -155
  101. package/src/solana/swaps/SolanaSwapData.ts +430 -430
  102. package/src/solana/swaps/SolanaSwapModule.ts +16 -16
  103. package/src/solana/swaps/SolanaSwapProgram.ts +854 -854
  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 +323 -323
  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 +24 -24
  114. package/src/utils/Utils.ts +180 -180
@@ -1,291 +1,291 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SwapClaim = void 0;
4
- const SolanaSwapModule_1 = require("../SolanaSwapModule");
5
- const SolanaAction_1 = require("../../chain/SolanaAction");
6
- const spl_token_1 = require("@solana/spl-token");
7
- const base_1 = require("@atomiqlabs/base");
8
- const web3_js_1 = require("@solana/web3.js");
9
- const Utils_1 = require("../../../utils/Utils");
10
- const SolanaSigner_1 = require("../../wallet/SolanaSigner");
11
- const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
12
- const BN = require("bn.js");
13
- class SwapClaim extends SolanaSwapModule_1.SolanaSwapModule {
14
- async Claim(signer, swapData, secretOrDataKey) {
15
- const isDataKey = typeof (secretOrDataKey) !== "string";
16
- const accounts = {
17
- signer,
18
- initializer: swapData.isPayIn() ? swapData.offerer : swapData.claimer,
19
- escrowState: this.program.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")),
20
- ixSysvar: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
21
- data: isDataKey ? secretOrDataKey : null,
22
- };
23
- let secretBuffer = isDataKey ?
24
- Buffer.alloc(0) :
25
- Buffer.from(secretOrDataKey, "hex");
26
- if (swapData.isPayOut()) {
27
- return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
28
- .claimerClaimPayOut(secretBuffer)
29
- .accounts({
30
- ...accounts,
31
- claimerAta: swapData.claimerAta,
32
- vault: this.program.SwapVault(swapData.token),
33
- vaultAuthority: this.program.SwapVaultAuthority,
34
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID
35
- })
36
- .instruction(), this.getComputeBudget(swapData));
37
- }
38
- else {
39
- return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
40
- .claimerClaim(secretBuffer)
41
- .accounts({
42
- ...accounts,
43
- claimerUserData: this.program.SwapUserVault(swapData.claimer, swapData.token)
44
- })
45
- .instruction(), this.getComputeBudget(swapData));
46
- }
47
- }
48
- /**
49
- * Verify and claim action required for BTC on-chain swaps verified through btc relay, adds the btc relay verify
50
- * instruction to the 0th index in the transaction, also intentionally sets compute budget to null such that no
51
- * compute budget instruction is added, since that takes up too much space and txs are limited to 1232 bytes
52
- *
53
- * @param signer
54
- * @param swapData
55
- * @param storeDataKey
56
- * @param merkleProof
57
- * @param commitedHeader
58
- * @constructor
59
- * @private
60
- */
61
- async VerifyAndClaim(signer, swapData, storeDataKey, merkleProof, commitedHeader) {
62
- const action = await this.btcRelay.Verify(signer, merkleProof.reversedTxId, swapData.confirmations, merkleProof.pos, merkleProof.merkle, commitedHeader);
63
- action.addAction(await this.Claim(signer, swapData, storeDataKey));
64
- action.computeBudget = null;
65
- return action;
66
- }
67
- constructor(chainInterface, program, btcRelay) {
68
- super(chainInterface, program);
69
- this.btcRelay = btcRelay;
70
- }
71
- /**
72
- * Gets the compute budget required for claiming the swap
73
- *
74
- * @param swapData
75
- * @private
76
- */
77
- getComputeBudget(swapData) {
78
- if (swapData.isPayOut()) {
79
- return SwapClaim.CUCosts[swapData.getType() === base_1.ChainSwapType.HTLC ? "CLAIM_PAY_OUT" : "CLAIM_ONCHAIN_PAY_OUT"];
80
- }
81
- else {
82
- return SwapClaim.CUCosts[swapData.getType() === base_1.ChainSwapType.HTLC ? "CLAIM" : "CLAIM_ONCHAIN"];
83
- }
84
- }
85
- /**
86
- * Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
87
- * requiredConfirmations
88
- * If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
89
- * current chain tip & adds them to the txs array
90
- *
91
- * @param signer
92
- * @param txBlockheight transaction blockheight
93
- * @param requiredConfirmations required confirmation for the swap to be claimable with that TX
94
- * @param blockhash blockhash of the block which includes the transaction
95
- * @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
96
- * txns are added here
97
- * @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
98
- * to the required blockheight
99
- * @private
100
- */
101
- async getCommitedHeaderAndSynchronize(signer, txBlockheight, requiredConfirmations, blockhash, txs, synchronizer) {
102
- const requiredBlockheight = txBlockheight + requiredConfirmations - 1;
103
- const result = await (0, Utils_1.tryWithRetries)(() => this.btcRelay.retrieveLogAndBlockheight({
104
- blockhash: blockhash
105
- }, requiredBlockheight), this.retryPolicy);
106
- if (result != null)
107
- return result.header;
108
- //Need to synchronize
109
- if (synchronizer == null)
110
- return null;
111
- //TODO: We don't have to synchronize to tip, only to our required blockheight
112
- const resp = await synchronizer.syncToLatestTxs(signer.toString());
113
- this.logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
114
- "synchronizing ourselves in " + resp.txs.length + " txs");
115
- this.logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
116
- resp.txs.forEach(tx => txs.push(tx));
117
- //Retrieve computed header
118
- return resp.computedHeaderMap[txBlockheight];
119
- }
120
- /**
121
- * Adds the transactions required for initialization and writing of transaction data to the data account
122
- *
123
- * @param signer
124
- * @param tx transaction to be written
125
- * @param vout vout of the transaction to use to satisfy swap conditions
126
- * @param feeRate fee rate for the transactions
127
- * @param txs solana transaction array, init & write transactions are added here
128
- * @private
129
- * @returns {Promise<PublicKey>} publicKey/address of the data account
130
- */
131
- addTxsWriteTransactionData(signer, tx, vout, feeRate, txs) {
132
- const reversedTxId = Buffer.from(tx.txid, "hex").reverse();
133
- const writeData = Buffer.concat([
134
- Buffer.from(new BN(vout).toArray("le", 4)),
135
- Buffer.from(tx.hex, "hex")
136
- ]);
137
- this.logger.debug("addTxsWriteTransactionData(): writing transaction data: ", writeData.toString("hex"));
138
- return this.program.DataAccount.addTxsWriteData(signer, reversedTxId, writeData, txs, feeRate);
139
- }
140
- /**
141
- * Checks whether we should unwrap the WSOL to SOL when claiming the swap
142
- *
143
- * @param signer
144
- * @param swapData
145
- * @private
146
- */
147
- shouldUnwrap(signer, swapData) {
148
- return swapData.isPayOut() &&
149
- swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS) &&
150
- swapData.claimer.equals(signer);
151
- }
152
- /**
153
- * Creates transactions claiming the swap using a secret (for HTLC swaps)
154
- *
155
- * @param signer
156
- * @param swapData swap to claim
157
- * @param secret hex encoded secret pre-image to the HTLC hash
158
- * @param checkExpiry whether to check if the swap is already expired (trying to claim an expired swap with a secret
159
- * is dangerous because we might end up revealing the secret to the counterparty without being able to claim the swap)
160
- * @param initAta whether to init the claimer's ATA if it doesn't exist
161
- * @param feeRate fee rate to use for the transaction
162
- * @param skipAtaCheck whether to check if ATA exists
163
- */
164
- async txsClaimWithSecret(signer, swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck) {
165
- //We need to be sure that this transaction confirms in time, otherwise we reveal the secret to the counterparty
166
- // and won't claim the funds
167
- if (checkExpiry && await this.program.isExpired(swapData.claimer.toString(), swapData)) {
168
- throw new base_1.SwapDataVerificationError("Not enough time to reliably pay the invoice");
169
- }
170
- const shouldInitAta = !skipAtaCheck && swapData.isPayOut() && !await this.root.Tokens.ataExists(swapData.claimerAta);
171
- if (shouldInitAta && !initAta)
172
- throw new base_1.SwapDataVerificationError("ATA not initialized");
173
- if (feeRate == null)
174
- feeRate = await this.getClaimFeeRate(signer, swapData);
175
- const action = new SolanaAction_1.SolanaAction(signer, this.root);
176
- const shouldUnwrap = this.shouldUnwrap(signer, swapData);
177
- if (shouldInitAta) {
178
- const initAction = this.root.Tokens.InitAta(signer, swapData.claimer, swapData.token, swapData.claimerAta);
179
- if (initAction == null)
180
- throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
181
- action.add(initAction);
182
- }
183
- action.add(await this.Claim(signer, swapData, secret));
184
- if (shouldUnwrap)
185
- action.add(this.root.Tokens.Unwrap(signer));
186
- this.logger.debug("txsClaimWithSecret(): creating claim transaction, swap: " + swapData.getClaimHash() +
187
- " initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap);
188
- return [await action.tx(feeRate)];
189
- }
190
- /**
191
- * Creates transaction claiming the swap using a confirmed transaction data (for BTC on-chain swaps)
192
- *
193
- * @param signer
194
- * @param swapData swap to claim
195
- * @param blockheight blockheight of the bitcoin transaction
196
- * @param tx bitcoin transaction that satisfies the swap condition
197
- * @param vout vout of the bitcoin transaction that satisfies the swap condition
198
- * @param commitedHeader commited header data from btc relay (fetched internally if null)
199
- * @param synchronizer optional synchronizer to use in case we need to sync up the btc relay ourselves
200
- * @param initAta whether to initialize claimer's ATA
201
- * @param storageAccHolder an object holder filled in with the created data account where tx data is written
202
- * @param feeRate fee rate to be used for the transactions
203
- */
204
- async txsClaimWithTxData(signer, swapData, tx, vout, commitedHeader, synchronizer, initAta, storageAccHolder, feeRate) {
205
- const shouldInitAta = swapData.isPayOut() && !await this.root.Tokens.ataExists(swapData.claimerAta);
206
- if (shouldInitAta && !initAta)
207
- throw new base_1.SwapDataVerificationError("ATA not initialized");
208
- const signerKey = signer instanceof SolanaSigner_1.SolanaSigner ? signer.getPublicKey() : signer;
209
- if (feeRate == null)
210
- feeRate = await this.getClaimFeeRate(signerKey, swapData);
211
- const merkleProof = await this.btcRelay.bitcoinRpc.getMerkleProof(tx.txid, tx.blockhash);
212
- this.logger.debug("txsClaimWithTxData(): merkle proof computed: ", merkleProof);
213
- const txs = [];
214
- if (commitedHeader == null)
215
- commitedHeader = await this.getCommitedHeaderAndSynchronize(signerKey, tx.height, swapData.confirmations, tx.blockhash, txs, synchronizer);
216
- const storeDataKey = await this.addTxsWriteTransactionData(signer, tx, vout, feeRate, txs);
217
- if (storageAccHolder != null)
218
- storageAccHolder.storageAcc = storeDataKey;
219
- const shouldUnwrap = this.shouldUnwrap(signerKey, swapData);
220
- if (shouldInitAta) {
221
- const initAction = this.root.Tokens.InitAta(signerKey, swapData.claimer, swapData.token, swapData.claimerAta);
222
- if (initAction == null)
223
- throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
224
- await initAction.addToTxs(txs, feeRate);
225
- }
226
- const claimAction = await this.VerifyAndClaim(signerKey, swapData, storeDataKey, merkleProof, commitedHeader);
227
- await claimAction.addToTxs(txs, feeRate);
228
- if (shouldUnwrap)
229
- await this.root.Tokens.Unwrap(signerKey).addToTxs(txs, feeRate);
230
- this.logger.debug("txsClaimWithTxData(): creating claim transaction, swap: " + swapData.getClaimHash() +
231
- " initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap + " num txns: " + txs.length);
232
- return txs;
233
- }
234
- getClaimFeeRate(signer, swapData) {
235
- const accounts = [signer];
236
- if (swapData.payOut) {
237
- if (swapData.token != null)
238
- accounts.push(this.program.SwapVault(swapData.token));
239
- if (swapData.payIn) {
240
- if (swapData.offerer != null)
241
- accounts.push(swapData.offerer);
242
- }
243
- else {
244
- if (swapData.claimer != null)
245
- accounts.push(swapData.claimer);
246
- }
247
- if (swapData.claimerAta != null && !swapData.claimerAta.equals(web3_js_1.PublicKey.default))
248
- accounts.push(swapData.claimerAta);
249
- }
250
- else {
251
- if (swapData.claimer != null && swapData.token != null)
252
- accounts.push(this.program.SwapUserVault(swapData.claimer, swapData.token));
253
- if (swapData.payIn) {
254
- if (swapData.offerer != null)
255
- accounts.push(swapData.offerer);
256
- }
257
- else {
258
- if (swapData.claimer != null)
259
- accounts.push(swapData.claimer);
260
- }
261
- }
262
- if (swapData.paymentHash != null)
263
- accounts.push(this.program.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")));
264
- return this.root.Fees.getFeeRate(accounts);
265
- }
266
- /**
267
- * Get the estimated solana transaction fee of the claim transaction in the worst case scenario in case where the
268
- * ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
269
- */
270
- async getClaimFee(signer, swapData, feeRate) {
271
- return BigInt(swapData == null || swapData.payOut ? SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT : 0) +
272
- await this.getRawClaimFee(signer, swapData, feeRate);
273
- }
274
- /**
275
- * Get the estimated solana transaction fee of the claim transaction, without
276
- */
277
- async getRawClaimFee(signer, swapData, feeRate) {
278
- if (swapData == null)
279
- return 5000n;
280
- feeRate = feeRate || await this.getClaimFeeRate(signer, swapData);
281
- //Include rent exempt in claim fee, to take into consideration worst case cost when user destroys ATA
282
- return 5000n + this.root.Fees.getPriorityFee(this.getComputeBudget(swapData), feeRate);
283
- }
284
- }
285
- exports.SwapClaim = SwapClaim;
286
- SwapClaim.CUCosts = {
287
- CLAIM: 25000,
288
- CLAIM_PAY_OUT: 50000,
289
- CLAIM_ONCHAIN: 600000,
290
- CLAIM_ONCHAIN_PAY_OUT: 600000
291
- };
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SwapClaim = void 0;
4
+ const SolanaSwapModule_1 = require("../SolanaSwapModule");
5
+ const SolanaAction_1 = require("../../chain/SolanaAction");
6
+ const spl_token_1 = require("@solana/spl-token");
7
+ const base_1 = require("@atomiqlabs/base");
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const Utils_1 = require("../../../utils/Utils");
10
+ const SolanaSigner_1 = require("../../wallet/SolanaSigner");
11
+ const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
12
+ const BN = require("bn.js");
13
+ class SwapClaim extends SolanaSwapModule_1.SolanaSwapModule {
14
+ async Claim(signer, swapData, secretOrDataKey) {
15
+ const isDataKey = typeof (secretOrDataKey) !== "string";
16
+ const accounts = {
17
+ signer,
18
+ initializer: swapData.isPayIn() ? swapData.offerer : swapData.claimer,
19
+ escrowState: this.program.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")),
20
+ ixSysvar: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
21
+ data: isDataKey ? secretOrDataKey : null,
22
+ };
23
+ let secretBuffer = isDataKey ?
24
+ Buffer.alloc(0) :
25
+ Buffer.from(secretOrDataKey, "hex");
26
+ if (swapData.isPayOut()) {
27
+ return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
28
+ .claimerClaimPayOut(secretBuffer)
29
+ .accounts({
30
+ ...accounts,
31
+ claimerAta: swapData.claimerAta,
32
+ vault: this.program.SwapVault(swapData.token),
33
+ vaultAuthority: this.program.SwapVaultAuthority,
34
+ tokenProgram: spl_token_1.TOKEN_PROGRAM_ID
35
+ })
36
+ .instruction(), this.getComputeBudget(swapData));
37
+ }
38
+ else {
39
+ return new SolanaAction_1.SolanaAction(signer, this.root, await this.swapProgram.methods
40
+ .claimerClaim(secretBuffer)
41
+ .accounts({
42
+ ...accounts,
43
+ claimerUserData: this.program.SwapUserVault(swapData.claimer, swapData.token)
44
+ })
45
+ .instruction(), this.getComputeBudget(swapData));
46
+ }
47
+ }
48
+ /**
49
+ * Verify and claim action required for BTC on-chain swaps verified through btc relay, adds the btc relay verify
50
+ * instruction to the 0th index in the transaction, also intentionally sets compute budget to null such that no
51
+ * compute budget instruction is added, since that takes up too much space and txs are limited to 1232 bytes
52
+ *
53
+ * @param signer
54
+ * @param swapData
55
+ * @param storeDataKey
56
+ * @param merkleProof
57
+ * @param commitedHeader
58
+ * @constructor
59
+ * @private
60
+ */
61
+ async VerifyAndClaim(signer, swapData, storeDataKey, merkleProof, commitedHeader) {
62
+ const action = await this.btcRelay.Verify(signer, merkleProof.reversedTxId, swapData.confirmations, merkleProof.pos, merkleProof.merkle, commitedHeader);
63
+ action.addAction(await this.Claim(signer, swapData, storeDataKey));
64
+ action.computeBudget = null;
65
+ return action;
66
+ }
67
+ constructor(chainInterface, program, btcRelay) {
68
+ super(chainInterface, program);
69
+ this.btcRelay = btcRelay;
70
+ }
71
+ /**
72
+ * Gets the compute budget required for claiming the swap
73
+ *
74
+ * @param swapData
75
+ * @private
76
+ */
77
+ getComputeBudget(swapData) {
78
+ if (swapData.isPayOut()) {
79
+ return SwapClaim.CUCosts[swapData.getType() === base_1.ChainSwapType.HTLC ? "CLAIM_PAY_OUT" : "CLAIM_ONCHAIN_PAY_OUT"];
80
+ }
81
+ else {
82
+ return SwapClaim.CUCosts[swapData.getType() === base_1.ChainSwapType.HTLC ? "CLAIM" : "CLAIM_ONCHAIN"];
83
+ }
84
+ }
85
+ /**
86
+ * Gets committed header, identified by blockhash & blockheight, determines required BTC relay blockheight based on
87
+ * requiredConfirmations
88
+ * If synchronizer is passed & blockhash is not found, it produces transactions to sync up the btc relay to the
89
+ * current chain tip & adds them to the txs array
90
+ *
91
+ * @param signer
92
+ * @param txBlockheight transaction blockheight
93
+ * @param requiredConfirmations required confirmation for the swap to be claimable with that TX
94
+ * @param blockhash blockhash of the block which includes the transaction
95
+ * @param txs solana transaction array, in case we need to synchronize the btc relay ourselves the synchronization
96
+ * txns are added here
97
+ * @param synchronizer optional synchronizer to use to synchronize the btc relay in case it is not yet synchronized
98
+ * to the required blockheight
99
+ * @private
100
+ */
101
+ async getCommitedHeaderAndSynchronize(signer, txBlockheight, requiredConfirmations, blockhash, txs, synchronizer) {
102
+ const requiredBlockheight = txBlockheight + requiredConfirmations - 1;
103
+ const result = await (0, Utils_1.tryWithRetries)(() => this.btcRelay.retrieveLogAndBlockheight({
104
+ blockhash: blockhash
105
+ }, requiredBlockheight), this.retryPolicy);
106
+ if (result != null)
107
+ return result.header;
108
+ //Need to synchronize
109
+ if (synchronizer == null)
110
+ return null;
111
+ //TODO: We don't have to synchronize to tip, only to our required blockheight
112
+ const resp = await synchronizer.syncToLatestTxs(signer.toString());
113
+ this.logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay not synchronized to required blockheight, " +
114
+ "synchronizing ourselves in " + resp.txs.length + " txs");
115
+ this.logger.debug("getCommitedHeaderAndSynchronize(): BTC Relay computed header map: ", resp.computedHeaderMap);
116
+ resp.txs.forEach(tx => txs.push(tx));
117
+ //Retrieve computed header
118
+ return resp.computedHeaderMap[txBlockheight];
119
+ }
120
+ /**
121
+ * Adds the transactions required for initialization and writing of transaction data to the data account
122
+ *
123
+ * @param signer
124
+ * @param tx transaction to be written
125
+ * @param vout vout of the transaction to use to satisfy swap conditions
126
+ * @param feeRate fee rate for the transactions
127
+ * @param txs solana transaction array, init & write transactions are added here
128
+ * @private
129
+ * @returns {Promise<PublicKey>} publicKey/address of the data account
130
+ */
131
+ addTxsWriteTransactionData(signer, tx, vout, feeRate, txs) {
132
+ const reversedTxId = Buffer.from(tx.txid, "hex").reverse();
133
+ const writeData = Buffer.concat([
134
+ Buffer.from(new BN(vout).toArray("le", 4)),
135
+ Buffer.from(tx.hex, "hex")
136
+ ]);
137
+ this.logger.debug("addTxsWriteTransactionData(): writing transaction data: ", writeData.toString("hex"));
138
+ return this.program.DataAccount.addTxsWriteData(signer, reversedTxId, writeData, txs, feeRate);
139
+ }
140
+ /**
141
+ * Checks whether we should unwrap the WSOL to SOL when claiming the swap
142
+ *
143
+ * @param signer
144
+ * @param swapData
145
+ * @private
146
+ */
147
+ shouldUnwrap(signer, swapData) {
148
+ return swapData.isPayOut() &&
149
+ swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS) &&
150
+ swapData.claimer.equals(signer);
151
+ }
152
+ /**
153
+ * Creates transactions claiming the swap using a secret (for HTLC swaps)
154
+ *
155
+ * @param signer
156
+ * @param swapData swap to claim
157
+ * @param secret hex encoded secret pre-image to the HTLC hash
158
+ * @param checkExpiry whether to check if the swap is already expired (trying to claim an expired swap with a secret
159
+ * is dangerous because we might end up revealing the secret to the counterparty without being able to claim the swap)
160
+ * @param initAta whether to init the claimer's ATA if it doesn't exist
161
+ * @param feeRate fee rate to use for the transaction
162
+ * @param skipAtaCheck whether to check if ATA exists
163
+ */
164
+ async txsClaimWithSecret(signer, swapData, secret, checkExpiry, initAta, feeRate, skipAtaCheck) {
165
+ //We need to be sure that this transaction confirms in time, otherwise we reveal the secret to the counterparty
166
+ // and won't claim the funds
167
+ if (checkExpiry && await this.program.isExpired(swapData.claimer.toString(), swapData)) {
168
+ throw new base_1.SwapDataVerificationError("Not enough time to reliably pay the invoice");
169
+ }
170
+ const shouldInitAta = !skipAtaCheck && swapData.isPayOut() && !await this.root.Tokens.ataExists(swapData.claimerAta);
171
+ if (shouldInitAta && !initAta)
172
+ throw new base_1.SwapDataVerificationError("ATA not initialized");
173
+ if (feeRate == null)
174
+ feeRate = await this.getClaimFeeRate(signer, swapData);
175
+ const action = new SolanaAction_1.SolanaAction(signer, this.root);
176
+ const shouldUnwrap = this.shouldUnwrap(signer, swapData);
177
+ if (shouldInitAta) {
178
+ const initAction = this.root.Tokens.InitAta(signer, swapData.claimer, swapData.token, swapData.claimerAta);
179
+ if (initAction == null)
180
+ throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
181
+ action.add(initAction);
182
+ }
183
+ action.add(await this.Claim(signer, swapData, secret));
184
+ if (shouldUnwrap)
185
+ action.add(this.root.Tokens.Unwrap(signer));
186
+ this.logger.debug("txsClaimWithSecret(): creating claim transaction, swap: " + swapData.getClaimHash() +
187
+ " initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap);
188
+ return [await action.tx(feeRate)];
189
+ }
190
+ /**
191
+ * Creates transaction claiming the swap using a confirmed transaction data (for BTC on-chain swaps)
192
+ *
193
+ * @param signer
194
+ * @param swapData swap to claim
195
+ * @param blockheight blockheight of the bitcoin transaction
196
+ * @param tx bitcoin transaction that satisfies the swap condition
197
+ * @param vout vout of the bitcoin transaction that satisfies the swap condition
198
+ * @param commitedHeader commited header data from btc relay (fetched internally if null)
199
+ * @param synchronizer optional synchronizer to use in case we need to sync up the btc relay ourselves
200
+ * @param initAta whether to initialize claimer's ATA
201
+ * @param storageAccHolder an object holder filled in with the created data account where tx data is written
202
+ * @param feeRate fee rate to be used for the transactions
203
+ */
204
+ async txsClaimWithTxData(signer, swapData, tx, vout, commitedHeader, synchronizer, initAta, storageAccHolder, feeRate) {
205
+ const shouldInitAta = swapData.isPayOut() && !await this.root.Tokens.ataExists(swapData.claimerAta);
206
+ if (shouldInitAta && !initAta)
207
+ throw new base_1.SwapDataVerificationError("ATA not initialized");
208
+ const signerKey = signer instanceof SolanaSigner_1.SolanaSigner ? signer.getPublicKey() : signer;
209
+ if (feeRate == null)
210
+ feeRate = await this.getClaimFeeRate(signerKey, swapData);
211
+ const merkleProof = await this.btcRelay.bitcoinRpc.getMerkleProof(tx.txid, tx.blockhash);
212
+ this.logger.debug("txsClaimWithTxData(): merkle proof computed: ", merkleProof);
213
+ const txs = [];
214
+ if (commitedHeader == null)
215
+ commitedHeader = await this.getCommitedHeaderAndSynchronize(signerKey, tx.height, swapData.confirmations, tx.blockhash, txs, synchronizer);
216
+ const storeDataKey = await this.addTxsWriteTransactionData(signer, tx, vout, feeRate, txs);
217
+ if (storageAccHolder != null)
218
+ storageAccHolder.storageAcc = storeDataKey;
219
+ const shouldUnwrap = this.shouldUnwrap(signerKey, swapData);
220
+ if (shouldInitAta) {
221
+ const initAction = this.root.Tokens.InitAta(signerKey, swapData.claimer, swapData.token, swapData.claimerAta);
222
+ if (initAction == null)
223
+ throw new base_1.SwapDataVerificationError("Invalid claimer token account address");
224
+ await initAction.addToTxs(txs, feeRate);
225
+ }
226
+ const claimAction = await this.VerifyAndClaim(signerKey, swapData, storeDataKey, merkleProof, commitedHeader);
227
+ await claimAction.addToTxs(txs, feeRate);
228
+ if (shouldUnwrap)
229
+ await this.root.Tokens.Unwrap(signerKey).addToTxs(txs, feeRate);
230
+ this.logger.debug("txsClaimWithTxData(): creating claim transaction, swap: " + swapData.getClaimHash() +
231
+ " initializingAta: " + shouldInitAta + " unwrapping: " + shouldUnwrap + " num txns: " + txs.length);
232
+ return txs;
233
+ }
234
+ getClaimFeeRate(signer, swapData) {
235
+ const accounts = [signer];
236
+ if (swapData.payOut) {
237
+ if (swapData.token != null)
238
+ accounts.push(this.program.SwapVault(swapData.token));
239
+ if (swapData.payIn) {
240
+ if (swapData.offerer != null)
241
+ accounts.push(swapData.offerer);
242
+ }
243
+ else {
244
+ if (swapData.claimer != null)
245
+ accounts.push(swapData.claimer);
246
+ }
247
+ if (swapData.claimerAta != null && !swapData.claimerAta.equals(web3_js_1.PublicKey.default))
248
+ accounts.push(swapData.claimerAta);
249
+ }
250
+ else {
251
+ if (swapData.claimer != null && swapData.token != null)
252
+ accounts.push(this.program.SwapUserVault(swapData.claimer, swapData.token));
253
+ if (swapData.payIn) {
254
+ if (swapData.offerer != null)
255
+ accounts.push(swapData.offerer);
256
+ }
257
+ else {
258
+ if (swapData.claimer != null)
259
+ accounts.push(swapData.claimer);
260
+ }
261
+ }
262
+ if (swapData.paymentHash != null)
263
+ accounts.push(this.program.SwapEscrowState(Buffer.from(swapData.paymentHash, "hex")));
264
+ return this.root.Fees.getFeeRate(accounts);
265
+ }
266
+ /**
267
+ * Get the estimated solana transaction fee of the claim transaction in the worst case scenario in case where the
268
+ * ATA needs to be initialized again (i.e. adding the ATA rent exempt lamports to the fee)
269
+ */
270
+ async getClaimFee(signer, swapData, feeRate) {
271
+ return BigInt(swapData == null || swapData.payOut ? SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT : 0) +
272
+ await this.getRawClaimFee(signer, swapData, feeRate);
273
+ }
274
+ /**
275
+ * Get the estimated solana transaction fee of the claim transaction, without
276
+ */
277
+ async getRawClaimFee(signer, swapData, feeRate) {
278
+ if (swapData == null)
279
+ return 5000n;
280
+ feeRate = feeRate || await this.getClaimFeeRate(signer, swapData);
281
+ //Include rent exempt in claim fee, to take into consideration worst case cost when user destroys ATA
282
+ return 5000n + this.root.Fees.getPriorityFee(this.getComputeBudget(swapData), feeRate);
283
+ }
284
+ }
285
+ exports.SwapClaim = SwapClaim;
286
+ SwapClaim.CUCosts = {
287
+ CLAIM: 25000,
288
+ CLAIM_PAY_OUT: 50000,
289
+ CLAIM_ONCHAIN: 600000,
290
+ CLAIM_ONCHAIN_PAY_OUT: 600000
291
+ };