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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +201 -201
  2. package/dist/index.d.ts +29 -29
  3. package/dist/index.js +45 -45
  4. package/dist/solana/SolanaChainType.d.ts +10 -10
  5. package/dist/solana/SolanaChainType.js +2 -2
  6. package/dist/solana/SolanaChains.d.ts +20 -20
  7. package/dist/solana/SolanaChains.js +25 -25
  8. package/dist/solana/SolanaInitializer.d.ts +18 -18
  9. package/dist/solana/SolanaInitializer.js +63 -63
  10. package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +228 -228
  11. package/dist/solana/btcrelay/SolanaBtcRelay.js +441 -441
  12. package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +29 -29
  13. package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +34 -34
  14. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +46 -46
  15. package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +78 -78
  16. package/dist/solana/btcrelay/program/programIdl.json +671 -671
  17. package/dist/solana/chain/SolanaAction.d.ts +26 -26
  18. package/dist/solana/chain/SolanaAction.js +86 -86
  19. package/dist/solana/chain/SolanaChainInterface.d.ts +58 -58
  20. package/dist/solana/chain/SolanaChainInterface.js +112 -112
  21. package/dist/solana/chain/SolanaModule.d.ts +14 -14
  22. package/dist/solana/chain/SolanaModule.js +13 -13
  23. package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
  24. package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
  25. package/dist/solana/chain/modules/SolanaBlocks.d.ts +28 -28
  26. package/dist/solana/chain/modules/SolanaBlocks.js +72 -72
  27. package/dist/solana/chain/modules/SolanaEvents.d.ts +25 -25
  28. package/dist/solana/chain/modules/SolanaEvents.js +58 -58
  29. package/dist/solana/chain/modules/SolanaFees.d.ts +121 -121
  30. package/dist/solana/chain/modules/SolanaFees.js +379 -379
  31. package/dist/solana/chain/modules/SolanaSignatures.d.ts +23 -23
  32. package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
  33. package/dist/solana/chain/modules/SolanaSlots.d.ts +31 -31
  34. package/dist/solana/chain/modules/SolanaSlots.js +68 -68
  35. package/dist/solana/chain/modules/SolanaTokens.d.ts +136 -136
  36. package/dist/solana/chain/modules/SolanaTokens.js +248 -248
  37. package/dist/solana/chain/modules/SolanaTransactions.d.ts +124 -124
  38. package/dist/solana/chain/modules/SolanaTransactions.js +332 -332
  39. package/dist/solana/events/SolanaChainEvents.d.ts +88 -88
  40. package/dist/solana/events/SolanaChainEvents.js +256 -256
  41. package/dist/solana/events/SolanaChainEventsBrowser.d.ts +85 -85
  42. package/dist/solana/events/SolanaChainEventsBrowser.js +194 -194
  43. package/dist/solana/program/SolanaProgramBase.d.ts +40 -40
  44. package/dist/solana/program/SolanaProgramBase.js +43 -43
  45. package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
  46. package/dist/solana/program/SolanaProgramModule.js +11 -11
  47. package/dist/solana/program/modules/SolanaProgramEvents.d.ts +59 -59
  48. package/dist/solana/program/modules/SolanaProgramEvents.js +103 -103
  49. package/dist/solana/swaps/SolanaSwapData.d.ts +59 -59
  50. package/dist/solana/swaps/SolanaSwapData.js +267 -267
  51. package/dist/solana/swaps/SolanaSwapModule.d.ts +10 -10
  52. package/dist/solana/swaps/SolanaSwapModule.js +11 -11
  53. package/dist/solana/swaps/SolanaSwapProgram.d.ts +202 -202
  54. package/dist/solana/swaps/SolanaSwapProgram.js +470 -463
  55. package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
  56. package/dist/solana/swaps/SwapTypeEnum.js +42 -42
  57. package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +94 -94
  58. package/dist/solana/swaps/modules/SolanaDataAccount.js +231 -231
  59. package/dist/solana/swaps/modules/SolanaLpVault.d.ts +71 -71
  60. package/dist/solana/swaps/modules/SolanaLpVault.js +173 -173
  61. package/dist/solana/swaps/modules/SwapClaim.d.ts +129 -129
  62. package/dist/solana/swaps/modules/SwapClaim.js +291 -291
  63. package/dist/solana/swaps/modules/SwapInit.d.ts +217 -217
  64. package/dist/solana/swaps/modules/SwapInit.js +519 -519
  65. package/dist/solana/swaps/modules/SwapRefund.d.ts +82 -82
  66. package/dist/solana/swaps/modules/SwapRefund.js +252 -252
  67. package/dist/solana/swaps/programIdl.json +945 -945
  68. package/dist/solana/swaps/programTypes.d.ts +943 -943
  69. package/dist/solana/swaps/programTypes.js +945 -945
  70. package/dist/solana/wallet/SolanaKeypairWallet.d.ts +9 -9
  71. package/dist/solana/wallet/SolanaKeypairWallet.js +33 -33
  72. package/dist/solana/wallet/SolanaSigner.d.ts +10 -10
  73. package/dist/solana/wallet/SolanaSigner.js +16 -16
  74. package/dist/utils/Utils.d.ts +53 -53
  75. package/dist/utils/Utils.js +170 -170
  76. package/package.json +41 -41
  77. package/src/index.ts +36 -36
  78. package/src/solana/SolanaChainType.ts +25 -25
  79. package/src/solana/SolanaChains.ts +23 -23
  80. package/src/solana/SolanaInitializer.ts +102 -102
  81. package/src/solana/btcrelay/SolanaBtcRelay.ts +589 -588
  82. package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +57 -57
  83. package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +102 -102
  84. package/src/solana/btcrelay/program/programIdl.json +670 -670
  85. package/src/solana/chain/SolanaAction.ts +108 -108
  86. package/src/solana/chain/SolanaChainInterface.ts +174 -174
  87. package/src/solana/chain/SolanaModule.ts +20 -20
  88. package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
  89. package/src/solana/chain/modules/SolanaBlocks.ts +78 -78
  90. package/src/solana/chain/modules/SolanaEvents.ts +56 -56
  91. package/src/solana/chain/modules/SolanaFees.ts +450 -450
  92. package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
  93. package/src/solana/chain/modules/SolanaSlots.ts +82 -82
  94. package/src/solana/chain/modules/SolanaTokens.ts +307 -307
  95. package/src/solana/chain/modules/SolanaTransactions.ts +370 -370
  96. package/src/solana/events/SolanaChainEvents.ts +299 -299
  97. package/src/solana/events/SolanaChainEventsBrowser.ts +256 -256
  98. package/src/solana/program/SolanaProgramBase.ts +79 -79
  99. package/src/solana/program/SolanaProgramModule.ts +15 -15
  100. package/src/solana/program/modules/SolanaProgramEvents.ts +140 -140
  101. package/src/solana/swaps/SolanaSwapData.ts +379 -379
  102. package/src/solana/swaps/SolanaSwapModule.ts +16 -16
  103. package/src/solana/swaps/SolanaSwapProgram.ts +697 -692
  104. package/src/solana/swaps/SwapTypeEnum.ts +29 -29
  105. package/src/solana/swaps/modules/SolanaDataAccount.ts +307 -307
  106. package/src/solana/swaps/modules/SolanaLpVault.ts +215 -215
  107. package/src/solana/swaps/modules/SwapClaim.ts +389 -389
  108. package/src/solana/swaps/modules/SwapInit.ts +663 -663
  109. package/src/solana/swaps/modules/SwapRefund.ts +312 -312
  110. package/src/solana/swaps/programIdl.json +944 -944
  111. package/src/solana/swaps/programTypes.ts +1885 -1885
  112. package/src/solana/wallet/SolanaKeypairWallet.ts +36 -36
  113. package/src/solana/wallet/SolanaSigner.ts +23 -23
  114. package/src/utils/Utils.ts +180 -180
@@ -1,519 +1,519 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SwapInit = void 0;
4
- const web3_js_1 = require("@solana/web3.js");
5
- const base_1 = require("@atomiqlabs/base");
6
- const SolanaAction_1 = require("../../chain/SolanaAction");
7
- const spl_token_1 = require("@solana/spl-token");
8
- const SolanaSwapModule_1 = require("../SolanaSwapModule");
9
- const Utils_1 = require("../../../utils/Utils");
10
- const buffer_1 = require("buffer");
11
- const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
12
- class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
13
- constructor() {
14
- super(...arguments);
15
- this.SIGNATURE_SLOT_BUFFER = 20;
16
- this.SIGNATURE_PREFETCH_DATA_VALIDITY = 5000;
17
- }
18
- /**
19
- * bare Init action based on the data passed in swapData
20
- *
21
- * @param swapData
22
- * @param timeout
23
- * @private
24
- */
25
- async Init(swapData, timeout) {
26
- const claimerAta = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.claimer);
27
- const paymentHash = buffer_1.Buffer.from(swapData.paymentHash, "hex");
28
- const accounts = {
29
- claimer: swapData.claimer,
30
- offerer: swapData.offerer,
31
- escrowState: this.program.SwapEscrowState(paymentHash),
32
- mint: swapData.token,
33
- systemProgram: web3_js_1.SystemProgram.programId,
34
- claimerAta: swapData.payOut ? claimerAta : null,
35
- claimerUserData: !swapData.payOut ? this.program.SwapUserVault(swapData.claimer, swapData.token) : null
36
- };
37
- if (swapData.payIn) {
38
- const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.offerer);
39
- return new SolanaAction_1.SolanaAction(swapData.offerer, this.root, await this.swapProgram.methods
40
- .offererInitializePayIn(swapData.toSwapDataStruct(), [...buffer_1.Buffer.alloc(32, 0)], (0, Utils_1.toBN)(timeout))
41
- .accounts({
42
- ...accounts,
43
- offererAta: ata,
44
- vault: this.program.SwapVault(swapData.token),
45
- vaultAuthority: this.program.SwapVaultAuthority,
46
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
47
- })
48
- .instruction(), SwapInit.CUCosts.INIT_PAY_IN);
49
- }
50
- else {
51
- return new SolanaAction_1.SolanaAction(swapData.claimer, this.root, await this.swapProgram.methods
52
- .offererInitialize(swapData.toSwapDataStruct(), swapData.securityDeposit, swapData.claimerBounty, [...(swapData.txoHash != null ? buffer_1.Buffer.from(swapData.txoHash, "hex") : buffer_1.Buffer.alloc(32, 0))], (0, Utils_1.toBN)(timeout))
53
- .accounts({
54
- ...accounts,
55
- offererUserData: this.program.SwapUserVault(swapData.offerer, swapData.token),
56
- })
57
- .instruction(), SwapInit.CUCosts.INIT);
58
- }
59
- }
60
- /**
61
- * InitPayIn action which includes SOL to WSOL wrapping if indicated by the fee rate
62
- *
63
- * @param swapData
64
- * @param timeout
65
- * @param feeRate
66
- * @constructor
67
- * @private
68
- */
69
- async InitPayIn(swapData, timeout, feeRate) {
70
- if (!swapData.isPayIn())
71
- throw new Error("Must be payIn==true");
72
- const action = new SolanaAction_1.SolanaAction(swapData.offerer, this.root);
73
- if (this.shouldWrapOnInit(swapData, feeRate))
74
- action.addAction(this.Wrap(swapData, feeRate));
75
- action.addAction(await this.Init(swapData, timeout));
76
- return action;
77
- }
78
- /**
79
- * InitNotPayIn action with additional createAssociatedTokenAccountIdempotentInstruction instruction, such that
80
- * a recipient ATA is created if it doesn't exist
81
- *
82
- * @param swapData
83
- * @param timeout
84
- * @constructor
85
- * @private
86
- */
87
- async InitNotPayIn(swapData, timeout) {
88
- if (swapData.isPayIn())
89
- throw new Error("Must be payIn==false");
90
- const action = new SolanaAction_1.SolanaAction(swapData.claimer, this.root);
91
- action.addIx((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(swapData.claimer, swapData.claimerAta, swapData.claimer, swapData.token));
92
- action.addAction(await this.Init(swapData, timeout));
93
- return action;
94
- }
95
- Wrap(swapData, feeRate) {
96
- const data = this.extractAtaDataFromFeeRate(feeRate);
97
- if (data == null)
98
- throw new Error("Tried to add wrap instruction, but feeRate malformed: " + feeRate);
99
- return this.root.Tokens.Wrap(swapData.offerer, swapData.getAmount() - data.balance, data.initAta);
100
- }
101
- /**
102
- * Extracts data about SOL to WSOL wrapping from the fee rate, fee rate is used to convey this information from
103
- * the user to the intermediary, such that the intermediary creates valid signature for transaction including
104
- * the SOL to WSOL wrapping instructions
105
- *
106
- * @param feeRate
107
- * @private
108
- */
109
- extractAtaDataFromFeeRate(feeRate) {
110
- const hashArr = feeRate == null ? [] : feeRate.split("#");
111
- if (hashArr.length <= 1)
112
- return null;
113
- const arr = hashArr[1].split(";");
114
- if (arr.length <= 1)
115
- return null;
116
- return {
117
- balance: BigInt(arr[1]),
118
- initAta: arr[0] === "1"
119
- };
120
- }
121
- /**
122
- * Checks whether a wrap instruction (SOL -> WSOL) should be a part of the signed init message
123
- *
124
- * @param swapData
125
- * @param feeRate
126
- * @private
127
- * @returns {boolean} returns true if wrap instruction should be added
128
- */
129
- shouldWrapOnInit(swapData, feeRate) {
130
- const data = this.extractAtaDataFromFeeRate(feeRate);
131
- if (data == null)
132
- return false;
133
- return data.balance < swapData.getAmount();
134
- }
135
- /**
136
- * Returns the transaction to be signed as an initialization signature from the intermediary, also adds
137
- * SOL to WSOL wrapping if indicated by the fee rate
138
- *
139
- * @param swapData
140
- * @param timeout
141
- * @param feeRate
142
- * @private
143
- */
144
- async getTxToSign(swapData, timeout, feeRate) {
145
- const action = swapData.isPayIn() ?
146
- await this.InitPayIn(swapData, BigInt(timeout), feeRate) :
147
- await this.InitNotPayIn(swapData, BigInt(timeout));
148
- const tx = (await action.tx(feeRate)).tx;
149
- return tx;
150
- }
151
- /**
152
- * Returns auth prefix to be used with a specific swap, payIn=true & payIn=false use different prefixes (these
153
- * actually have no meaning for the smart contract/solana program in the Solana case)
154
- *
155
- * @param swapData
156
- * @private
157
- */
158
- getAuthPrefix(swapData) {
159
- return swapData.isPayIn() ? "claim_initialize" : "initialize";
160
- }
161
- /**
162
- * Returns "processed" slot required for signature validation, uses preFetchedData if provided & valid
163
- *
164
- * @param preFetchedData
165
- * @private
166
- */
167
- getSlotForSignature(preFetchedData) {
168
- if (preFetchedData != null &&
169
- preFetchedData.latestSlot != null &&
170
- preFetchedData.latestSlot.timestamp > Date.now() - this.root.Slots.SLOT_CACHE_TIME) {
171
- const estimatedSlotsPassed = Math.floor((Date.now() - preFetchedData.latestSlot.timestamp) / this.root.SLOT_TIME);
172
- const estimatedCurrentSlot = preFetchedData.latestSlot.slot + estimatedSlotsPassed;
173
- this.logger.debug("getSlotForSignature(): slot: " + preFetchedData.latestSlot.slot +
174
- " estimated passed slots: " + estimatedSlotsPassed + " estimated current slot: " + estimatedCurrentSlot);
175
- return Promise.resolve(estimatedCurrentSlot);
176
- }
177
- return this.root.Slots.getSlot("processed");
178
- }
179
- /**
180
- * Returns blockhash required for signature validation, uses preFetchedData if provided & valid
181
- *
182
- * @param txSlot
183
- * @param preFetchedData
184
- * @private
185
- */
186
- getBlockhashForSignature(txSlot, preFetchedData) {
187
- if (preFetchedData != null &&
188
- preFetchedData.transactionSlot != null &&
189
- preFetchedData.transactionSlot.slot === txSlot) {
190
- return Promise.resolve(preFetchedData.transactionSlot.blockhash);
191
- }
192
- return this.root.Blocks.getParsedBlock(txSlot).then(val => val.blockhash);
193
- }
194
- /**
195
- * Pre-fetches slot & block based on priorly received SolanaPreFetchData, such that it can later be used
196
- * by signature verification
197
- *
198
- * @param data
199
- */
200
- async preFetchForInitSignatureVerification(data) {
201
- const [latestSlot, txBlock] = await Promise.all([
202
- this.root.Slots.getSlotAndTimestamp("processed"),
203
- this.root.Blocks.getParsedBlock(data.slot)
204
- ]);
205
- return {
206
- latestSlot,
207
- transactionSlot: {
208
- slot: data.slot,
209
- blockhash: txBlock.blockhash
210
- }
211
- };
212
- }
213
- /**
214
- * Pre-fetches block data required for signing the init message by the LP, this can happen in parallel before
215
- * signing takes place making the quoting quicker
216
- */
217
- async preFetchBlockDataForSignatures() {
218
- const latestParsedBlock = await this.root.Blocks.findLatestParsedBlock("finalized");
219
- return {
220
- block: latestParsedBlock.block,
221
- slot: latestParsedBlock.slot,
222
- timestamp: Date.now()
223
- };
224
- }
225
- /**
226
- * Signs swap initialization authorization, using data from preFetchedBlockData if provided & still valid (subject
227
- * to SIGNATURE_PREFETCH_DATA_VALIDITY)
228
- *
229
- * @param signer
230
- * @param swapData
231
- * @param authorizationTimeout
232
- * @param feeRate
233
- * @param preFetchedBlockData
234
- * @public
235
- */
236
- async signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate) {
237
- if (signer.keypair == null)
238
- throw new Error("Unsupported");
239
- if (!signer.getPublicKey().equals(swapData.isPayIn() ? swapData.claimer : swapData.offerer))
240
- throw new Error("Invalid signer, wrong public key!");
241
- if (preFetchedBlockData != null && Date.now() - preFetchedBlockData.timestamp > this.SIGNATURE_PREFETCH_DATA_VALIDITY)
242
- preFetchedBlockData = null;
243
- const { block: latestBlock, slot: latestSlot } = preFetchedBlockData || await this.root.Blocks.findLatestParsedBlock("finalized");
244
- const authTimeout = Math.floor(Date.now() / 1000) + authorizationTimeout;
245
- const txToSign = await this.getTxToSign(swapData, authTimeout.toString(10), feeRate);
246
- txToSign.feePayer = swapData.isPayIn() ? swapData.offerer : swapData.claimer;
247
- txToSign.recentBlockhash = latestBlock.blockhash;
248
- txToSign.sign(signer.keypair);
249
- this.logger.debug("signSwapInitialization(): Signed tx: ", txToSign);
250
- const sig = txToSign.signatures.find(e => e.publicKey.equals(signer.getPublicKey()));
251
- return {
252
- prefix: this.getAuthPrefix(swapData),
253
- timeout: authTimeout.toString(10),
254
- signature: latestSlot + ";" + sig.signature.toString("hex")
255
- };
256
- }
257
- /**
258
- * Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
259
- *
260
- * @param sender
261
- * @param swapData
262
- * @param timeout
263
- * @param prefix
264
- * @param signature
265
- * @param feeRate
266
- * @param preFetchedData
267
- * @public
268
- */
269
- async isSignatureValid(sender, swapData, timeout, prefix, signature, feeRate, preFetchedData) {
270
- if (swapData.isPayIn()) {
271
- if (!swapData.isOfferer(sender))
272
- throw new base_1.SignatureVerificationError("Sender needs to be offerer in payIn=true swaps");
273
- }
274
- else {
275
- if (!swapData.isClaimer(sender))
276
- throw new base_1.SignatureVerificationError("Sender needs to be claimer in payIn=false swaps");
277
- }
278
- const signer = swapData.isPayIn() ? swapData.claimer : swapData.offerer;
279
- if (!swapData.isPayIn() && await this.program.isExpired(sender.toString(), swapData)) {
280
- throw new base_1.SignatureVerificationError("Swap will expire too soon!");
281
- }
282
- if (prefix !== this.getAuthPrefix(swapData))
283
- throw new base_1.SignatureVerificationError("Invalid prefix");
284
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
285
- const isExpired = (BigInt(timeout) - currentTimestamp) < BigInt(this.program.authGracePeriod);
286
- if (isExpired)
287
- throw new base_1.SignatureVerificationError("Authorization expired!");
288
- const [transactionSlot, signatureString] = signature.split(";");
289
- const txSlot = parseInt(transactionSlot);
290
- const [latestSlot, blockhash] = await Promise.all([
291
- this.getSlotForSignature(preFetchedData),
292
- this.getBlockhashForSignature(txSlot, preFetchedData)
293
- ]);
294
- const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
295
- const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
296
- if (slotsLeft < 0)
297
- throw new base_1.SignatureVerificationError("Authorization expired!");
298
- const txToSign = await this.getTxToSign(swapData, timeout, feeRate);
299
- txToSign.feePayer = new web3_js_1.PublicKey(sender);
300
- txToSign.recentBlockhash = blockhash;
301
- txToSign.addSignature(signer, buffer_1.Buffer.from(signatureString, "hex"));
302
- this.logger.debug("isSignatureValid(): Signed tx: ", txToSign);
303
- const valid = txToSign.verifySignatures(false);
304
- if (!valid)
305
- throw new base_1.SignatureVerificationError("Invalid signature!");
306
- return buffer_1.Buffer.from(blockhash);
307
- }
308
- /**
309
- * Gets expiry of the provided signature data, this is a minimum of slot expiry & swap signature expiry
310
- *
311
- * @param timeout
312
- * @param signature
313
- * @param preFetchedData
314
- * @public
315
- */
316
- async getSignatureExpiry(timeout, signature, preFetchedData) {
317
- const [transactionSlotStr, signatureString] = signature.split(";");
318
- const txSlot = parseInt(transactionSlotStr);
319
- const latestSlot = await this.getSlotForSignature(preFetchedData);
320
- const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
321
- const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
322
- const now = Date.now();
323
- const slotExpiryTime = now + (slotsLeft * this.root.SLOT_TIME);
324
- const timeoutExpiryTime = (parseInt(timeout) - this.program.authGracePeriod) * 1000;
325
- const expiry = Math.min(slotExpiryTime, timeoutExpiryTime);
326
- if (expiry < now)
327
- return 0;
328
- return expiry;
329
- }
330
- /**
331
- * Checks whether signature is expired for good (uses "finalized" slot)
332
- *
333
- * @param signature
334
- * @param timeout
335
- * @public
336
- */
337
- async isSignatureExpired(signature, timeout) {
338
- const [transactionSlotStr, signatureString] = signature.split(";");
339
- const txSlot = parseInt(transactionSlotStr);
340
- const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
341
- const latestSlot = await this.root.Slots.getSlot("finalized");
342
- const slotsLeft = lastValidTransactionSlot - latestSlot + this.SIGNATURE_SLOT_BUFFER;
343
- if (slotsLeft < 0)
344
- return true;
345
- if ((parseInt(timeout) + this.program.authGracePeriod) * 1000 < Date.now())
346
- return true;
347
- return false;
348
- }
349
- /**
350
- * Creates init transaction (InitPayIn) with a valid signature from an LP, also adds a SOL to WSOL wrapping ix to
351
- * the init transaction (if indicated by the fee rate) or adds the wrapping in a separate transaction (if no
352
- * indication in the fee rate)
353
- *
354
- * @param swapData swap to initialize
355
- * @param timeout init signature timeout
356
- * @param prefix init signature prefix
357
- * @param signature init signature
358
- * @param skipChecks whether to skip signature validity checks
359
- * @param feeRate fee rate to use for the transaction
360
- */
361
- async txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate) {
362
- if (!skipChecks) {
363
- const [_, payStatus] = await Promise.all([
364
- (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getOfferer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError),
365
- (0, Utils_1.tryWithRetries)(() => this.program.getClaimHashStatus(swapData.getClaimHash()), this.retryPolicy)
366
- ]);
367
- if (payStatus !== base_1.SwapCommitStateType.NOT_COMMITED)
368
- throw new base_1.SwapDataVerificationError("Invoice already being paid for or paid");
369
- }
370
- const [slotNumber, signatureStr] = signature.split(";");
371
- const block = await (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
372
- const txs = [];
373
- let isWrapping = false;
374
- const isWrappedInSignedTx = feeRate != null && feeRate.split("#").length > 1;
375
- if (!isWrappedInSignedTx && swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS)) {
376
- const ataAcc = await (0, Utils_1.tryWithRetries)(() => this.root.Tokens.getATAOrNull(swapData.offererAta), this.retryPolicy);
377
- const balance = ataAcc == null ? 0n : ataAcc.amount;
378
- if (balance < swapData.getAmount()) {
379
- //Need to wrap more SOL to WSOL
380
- await this.root.Tokens.Wrap(swapData.offerer, swapData.getAmount() - balance, ataAcc == null)
381
- .addToTxs(txs, feeRate, block);
382
- isWrapping = true;
383
- }
384
- }
385
- const initTx = await (await this.InitPayIn(swapData, BigInt(timeout), feeRate)).tx(feeRate, block);
386
- initTx.tx.addSignature(swapData.claimer, buffer_1.Buffer.from(signatureStr, "hex"));
387
- txs.push(initTx);
388
- this.logger.debug("txsInitPayIn(): create swap init TX, swap: " + swapData.getClaimHash() +
389
- " wrapping client-side: " + isWrapping + " feerate: " + feeRate);
390
- return txs;
391
- }
392
- /**
393
- * Creates init transactions (InitNotPayIn) with a valid signature from an intermediary
394
- *
395
- * @param swapData swap to initialize
396
- * @param timeout init signature timeout
397
- * @param prefix init signature prefix
398
- * @param signature init signature
399
- * @param skipChecks whether to skip signature validity checks
400
- * @param feeRate fee rate to use for the transaction
401
- */
402
- async txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate) {
403
- if (!skipChecks) {
404
- await (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getClaimer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError);
405
- }
406
- const [slotNumber, signatureStr] = signature.split(";");
407
- const block = await (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
408
- const initTx = await (await this.InitNotPayIn(swapData, BigInt(timeout))).tx(feeRate, block);
409
- initTx.tx.addSignature(swapData.offerer, buffer_1.Buffer.from(signatureStr, "hex"));
410
- this.logger.debug("txsInit(): create swap init TX, swap: " + swapData.getClaimHash() + " feerate: " + feeRate);
411
- return [initTx];
412
- }
413
- /**
414
- * Returns the fee rate to be used for a specific init transaction, also adding indication whether the WSOL ATA
415
- * should be initialized in the init transaction and/or current balance in the WSOL ATA
416
- *
417
- * @param offerer
418
- * @param claimer
419
- * @param token
420
- * @param paymentHash
421
- */
422
- async getInitPayInFeeRate(offerer, claimer, token, paymentHash) {
423
- const accounts = [];
424
- if (offerer != null)
425
- accounts.push(offerer);
426
- if (token != null) {
427
- accounts.push(this.program.SwapVault(token));
428
- if (offerer != null)
429
- accounts.push((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer));
430
- if (claimer != null)
431
- accounts.push(this.program.SwapUserVault(claimer, token));
432
- }
433
- if (paymentHash != null)
434
- accounts.push(this.program.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
435
- const shouldCheckWSOLAta = token != null && offerer != null && token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS);
436
- let [feeRate, _account] = await Promise.all([
437
- this.root.Fees.getFeeRate(accounts),
438
- shouldCheckWSOLAta ?
439
- this.root.Tokens.getATAOrNull((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer)) :
440
- Promise.resolve(null)
441
- ]);
442
- if (shouldCheckWSOLAta) {
443
- const account = _account;
444
- const balance = account == null ? 0n : account.amount;
445
- //Add an indication about whether the ATA is initialized & balance it contains
446
- feeRate += "#" + (account != null ? "0" : "1") + ";" + balance.toString(10);
447
- }
448
- this.logger.debug("getInitPayInFeeRate(): feerate computed: " + feeRate);
449
- return feeRate;
450
- }
451
- /**
452
- * Returns the fee rate to be used for a specific init transaction
453
- *
454
- * @param offerer
455
- * @param claimer
456
- * @param token
457
- * @param paymentHash
458
- */
459
- getInitFeeRate(offerer, claimer, token, paymentHash) {
460
- const accounts = [];
461
- if (offerer != null && token != null)
462
- accounts.push(this.program.SwapUserVault(offerer, token));
463
- if (claimer != null)
464
- accounts.push(claimer);
465
- if (paymentHash != null)
466
- accounts.push(this.program.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
467
- return this.root.Fees.getFeeRate(accounts);
468
- }
469
- /**
470
- * Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
471
- * and also deposit for ATAs
472
- */
473
- async getInitFee(swapData, feeRate) {
474
- if (swapData == null)
475
- return BigInt(this.program.ESCROW_STATE_RENT_EXEMPT) + await this.getRawInitFee(swapData, feeRate);
476
- feeRate = feeRate ||
477
- (swapData.payIn
478
- ? await this.getInitPayInFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash)
479
- : await this.getInitFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash));
480
- const [rawFee, initAta] = await Promise.all([
481
- this.getRawInitFee(swapData, feeRate),
482
- swapData != null && swapData.payOut ?
483
- this.root.Tokens.getATAOrNull((0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.claimer)).then(acc => acc == null) :
484
- Promise.resolve(null)
485
- ]);
486
- let resultingFee = BigInt(this.program.ESCROW_STATE_RENT_EXEMPT) + rawFee;
487
- if (initAta)
488
- resultingFee += BigInt(SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT);
489
- if (swapData.payIn && this.shouldWrapOnInit(swapData, feeRate) && this.extractAtaDataFromFeeRate(feeRate).initAta) {
490
- resultingFee += BigInt(SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT);
491
- }
492
- return resultingFee;
493
- }
494
- /**
495
- * Get the estimated solana fee of the init transaction, without the required deposit for creating swap PDA
496
- */
497
- async getRawInitFee(swapData, feeRate) {
498
- if (swapData == null)
499
- return 10000n;
500
- feeRate = feeRate ||
501
- (swapData.payIn
502
- ? await this.getInitPayInFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash)
503
- : await this.getInitFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash));
504
- let computeBudget = swapData.payIn ? SwapInit.CUCosts.INIT_PAY_IN : SwapInit.CUCosts.INIT;
505
- if (swapData.payIn && this.shouldWrapOnInit(swapData, feeRate)) {
506
- computeBudget += SolanaTokens_1.SolanaTokens.CUCosts.WRAP_SOL;
507
- const data = this.extractAtaDataFromFeeRate(feeRate);
508
- if (data.initAta)
509
- computeBudget += SolanaTokens_1.SolanaTokens.CUCosts.ATA_INIT;
510
- }
511
- const baseFee = swapData.payIn ? 10000n : 10000n + 5000n;
512
- return baseFee + this.root.Fees.getPriorityFee(computeBudget, feeRate);
513
- }
514
- }
515
- exports.SwapInit = SwapInit;
516
- SwapInit.CUCosts = {
517
- INIT: 90000,
518
- INIT_PAY_IN: 50000,
519
- };
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SwapInit = void 0;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const base_1 = require("@atomiqlabs/base");
6
+ const SolanaAction_1 = require("../../chain/SolanaAction");
7
+ const spl_token_1 = require("@solana/spl-token");
8
+ const SolanaSwapModule_1 = require("../SolanaSwapModule");
9
+ const Utils_1 = require("../../../utils/Utils");
10
+ const buffer_1 = require("buffer");
11
+ const SolanaTokens_1 = require("../../chain/modules/SolanaTokens");
12
+ class SwapInit extends SolanaSwapModule_1.SolanaSwapModule {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.SIGNATURE_SLOT_BUFFER = 20;
16
+ this.SIGNATURE_PREFETCH_DATA_VALIDITY = 5000;
17
+ }
18
+ /**
19
+ * bare Init action based on the data passed in swapData
20
+ *
21
+ * @param swapData
22
+ * @param timeout
23
+ * @private
24
+ */
25
+ async Init(swapData, timeout) {
26
+ const claimerAta = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.claimer);
27
+ const paymentHash = buffer_1.Buffer.from(swapData.paymentHash, "hex");
28
+ const accounts = {
29
+ claimer: swapData.claimer,
30
+ offerer: swapData.offerer,
31
+ escrowState: this.program.SwapEscrowState(paymentHash),
32
+ mint: swapData.token,
33
+ systemProgram: web3_js_1.SystemProgram.programId,
34
+ claimerAta: swapData.payOut ? claimerAta : null,
35
+ claimerUserData: !swapData.payOut ? this.program.SwapUserVault(swapData.claimer, swapData.token) : null
36
+ };
37
+ if (swapData.payIn) {
38
+ const ata = (0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.offerer);
39
+ return new SolanaAction_1.SolanaAction(swapData.offerer, this.root, await this.swapProgram.methods
40
+ .offererInitializePayIn(swapData.toSwapDataStruct(), [...buffer_1.Buffer.alloc(32, 0)], (0, Utils_1.toBN)(timeout))
41
+ .accounts({
42
+ ...accounts,
43
+ offererAta: ata,
44
+ vault: this.program.SwapVault(swapData.token),
45
+ vaultAuthority: this.program.SwapVaultAuthority,
46
+ tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
47
+ })
48
+ .instruction(), SwapInit.CUCosts.INIT_PAY_IN);
49
+ }
50
+ else {
51
+ return new SolanaAction_1.SolanaAction(swapData.claimer, this.root, await this.swapProgram.methods
52
+ .offererInitialize(swapData.toSwapDataStruct(), swapData.securityDeposit, swapData.claimerBounty, [...(swapData.txoHash != null ? buffer_1.Buffer.from(swapData.txoHash, "hex") : buffer_1.Buffer.alloc(32, 0))], (0, Utils_1.toBN)(timeout))
53
+ .accounts({
54
+ ...accounts,
55
+ offererUserData: this.program.SwapUserVault(swapData.offerer, swapData.token),
56
+ })
57
+ .instruction(), SwapInit.CUCosts.INIT);
58
+ }
59
+ }
60
+ /**
61
+ * InitPayIn action which includes SOL to WSOL wrapping if indicated by the fee rate
62
+ *
63
+ * @param swapData
64
+ * @param timeout
65
+ * @param feeRate
66
+ * @constructor
67
+ * @private
68
+ */
69
+ async InitPayIn(swapData, timeout, feeRate) {
70
+ if (!swapData.isPayIn())
71
+ throw new Error("Must be payIn==true");
72
+ const action = new SolanaAction_1.SolanaAction(swapData.offerer, this.root);
73
+ if (this.shouldWrapOnInit(swapData, feeRate))
74
+ action.addAction(this.Wrap(swapData, feeRate));
75
+ action.addAction(await this.Init(swapData, timeout));
76
+ return action;
77
+ }
78
+ /**
79
+ * InitNotPayIn action with additional createAssociatedTokenAccountIdempotentInstruction instruction, such that
80
+ * a recipient ATA is created if it doesn't exist
81
+ *
82
+ * @param swapData
83
+ * @param timeout
84
+ * @constructor
85
+ * @private
86
+ */
87
+ async InitNotPayIn(swapData, timeout) {
88
+ if (swapData.isPayIn())
89
+ throw new Error("Must be payIn==false");
90
+ const action = new SolanaAction_1.SolanaAction(swapData.claimer, this.root);
91
+ action.addIx((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(swapData.claimer, swapData.claimerAta, swapData.claimer, swapData.token));
92
+ action.addAction(await this.Init(swapData, timeout));
93
+ return action;
94
+ }
95
+ Wrap(swapData, feeRate) {
96
+ const data = this.extractAtaDataFromFeeRate(feeRate);
97
+ if (data == null)
98
+ throw new Error("Tried to add wrap instruction, but feeRate malformed: " + feeRate);
99
+ return this.root.Tokens.Wrap(swapData.offerer, swapData.getAmount() - data.balance, data.initAta);
100
+ }
101
+ /**
102
+ * Extracts data about SOL to WSOL wrapping from the fee rate, fee rate is used to convey this information from
103
+ * the user to the intermediary, such that the intermediary creates valid signature for transaction including
104
+ * the SOL to WSOL wrapping instructions
105
+ *
106
+ * @param feeRate
107
+ * @private
108
+ */
109
+ extractAtaDataFromFeeRate(feeRate) {
110
+ const hashArr = feeRate == null ? [] : feeRate.split("#");
111
+ if (hashArr.length <= 1)
112
+ return null;
113
+ const arr = hashArr[1].split(";");
114
+ if (arr.length <= 1)
115
+ return null;
116
+ return {
117
+ balance: BigInt(arr[1]),
118
+ initAta: arr[0] === "1"
119
+ };
120
+ }
121
+ /**
122
+ * Checks whether a wrap instruction (SOL -> WSOL) should be a part of the signed init message
123
+ *
124
+ * @param swapData
125
+ * @param feeRate
126
+ * @private
127
+ * @returns {boolean} returns true if wrap instruction should be added
128
+ */
129
+ shouldWrapOnInit(swapData, feeRate) {
130
+ const data = this.extractAtaDataFromFeeRate(feeRate);
131
+ if (data == null)
132
+ return false;
133
+ return data.balance < swapData.getAmount();
134
+ }
135
+ /**
136
+ * Returns the transaction to be signed as an initialization signature from the intermediary, also adds
137
+ * SOL to WSOL wrapping if indicated by the fee rate
138
+ *
139
+ * @param swapData
140
+ * @param timeout
141
+ * @param feeRate
142
+ * @private
143
+ */
144
+ async getTxToSign(swapData, timeout, feeRate) {
145
+ const action = swapData.isPayIn() ?
146
+ await this.InitPayIn(swapData, BigInt(timeout), feeRate) :
147
+ await this.InitNotPayIn(swapData, BigInt(timeout));
148
+ const tx = (await action.tx(feeRate)).tx;
149
+ return tx;
150
+ }
151
+ /**
152
+ * Returns auth prefix to be used with a specific swap, payIn=true & payIn=false use different prefixes (these
153
+ * actually have no meaning for the smart contract/solana program in the Solana case)
154
+ *
155
+ * @param swapData
156
+ * @private
157
+ */
158
+ getAuthPrefix(swapData) {
159
+ return swapData.isPayIn() ? "claim_initialize" : "initialize";
160
+ }
161
+ /**
162
+ * Returns "processed" slot required for signature validation, uses preFetchedData if provided & valid
163
+ *
164
+ * @param preFetchedData
165
+ * @private
166
+ */
167
+ getSlotForSignature(preFetchedData) {
168
+ if (preFetchedData != null &&
169
+ preFetchedData.latestSlot != null &&
170
+ preFetchedData.latestSlot.timestamp > Date.now() - this.root.Slots.SLOT_CACHE_TIME) {
171
+ const estimatedSlotsPassed = Math.floor((Date.now() - preFetchedData.latestSlot.timestamp) / this.root.SLOT_TIME);
172
+ const estimatedCurrentSlot = preFetchedData.latestSlot.slot + estimatedSlotsPassed;
173
+ this.logger.debug("getSlotForSignature(): slot: " + preFetchedData.latestSlot.slot +
174
+ " estimated passed slots: " + estimatedSlotsPassed + " estimated current slot: " + estimatedCurrentSlot);
175
+ return Promise.resolve(estimatedCurrentSlot);
176
+ }
177
+ return this.root.Slots.getSlot("processed");
178
+ }
179
+ /**
180
+ * Returns blockhash required for signature validation, uses preFetchedData if provided & valid
181
+ *
182
+ * @param txSlot
183
+ * @param preFetchedData
184
+ * @private
185
+ */
186
+ getBlockhashForSignature(txSlot, preFetchedData) {
187
+ if (preFetchedData != null &&
188
+ preFetchedData.transactionSlot != null &&
189
+ preFetchedData.transactionSlot.slot === txSlot) {
190
+ return Promise.resolve(preFetchedData.transactionSlot.blockhash);
191
+ }
192
+ return this.root.Blocks.getParsedBlock(txSlot).then(val => val.blockhash);
193
+ }
194
+ /**
195
+ * Pre-fetches slot & block based on priorly received SolanaPreFetchData, such that it can later be used
196
+ * by signature verification
197
+ *
198
+ * @param data
199
+ */
200
+ async preFetchForInitSignatureVerification(data) {
201
+ const [latestSlot, txBlock] = await Promise.all([
202
+ this.root.Slots.getSlotAndTimestamp("processed"),
203
+ this.root.Blocks.getParsedBlock(data.slot)
204
+ ]);
205
+ return {
206
+ latestSlot,
207
+ transactionSlot: {
208
+ slot: data.slot,
209
+ blockhash: txBlock.blockhash
210
+ }
211
+ };
212
+ }
213
+ /**
214
+ * Pre-fetches block data required for signing the init message by the LP, this can happen in parallel before
215
+ * signing takes place making the quoting quicker
216
+ */
217
+ async preFetchBlockDataForSignatures() {
218
+ const latestParsedBlock = await this.root.Blocks.findLatestParsedBlock("finalized");
219
+ return {
220
+ block: latestParsedBlock.block,
221
+ slot: latestParsedBlock.slot,
222
+ timestamp: Date.now()
223
+ };
224
+ }
225
+ /**
226
+ * Signs swap initialization authorization, using data from preFetchedBlockData if provided & still valid (subject
227
+ * to SIGNATURE_PREFETCH_DATA_VALIDITY)
228
+ *
229
+ * @param signer
230
+ * @param swapData
231
+ * @param authorizationTimeout
232
+ * @param feeRate
233
+ * @param preFetchedBlockData
234
+ * @public
235
+ */
236
+ async signSwapInitialization(signer, swapData, authorizationTimeout, preFetchedBlockData, feeRate) {
237
+ if (signer.keypair == null)
238
+ throw new Error("Unsupported");
239
+ if (!signer.getPublicKey().equals(swapData.isPayIn() ? swapData.claimer : swapData.offerer))
240
+ throw new Error("Invalid signer, wrong public key!");
241
+ if (preFetchedBlockData != null && Date.now() - preFetchedBlockData.timestamp > this.SIGNATURE_PREFETCH_DATA_VALIDITY)
242
+ preFetchedBlockData = null;
243
+ const { block: latestBlock, slot: latestSlot } = preFetchedBlockData || await this.root.Blocks.findLatestParsedBlock("finalized");
244
+ const authTimeout = Math.floor(Date.now() / 1000) + authorizationTimeout;
245
+ const txToSign = await this.getTxToSign(swapData, authTimeout.toString(10), feeRate);
246
+ txToSign.feePayer = swapData.isPayIn() ? swapData.offerer : swapData.claimer;
247
+ txToSign.recentBlockhash = latestBlock.blockhash;
248
+ txToSign.sign(signer.keypair);
249
+ this.logger.debug("signSwapInitialization(): Signed tx: ", txToSign);
250
+ const sig = txToSign.signatures.find(e => e.publicKey.equals(signer.getPublicKey()));
251
+ return {
252
+ prefix: this.getAuthPrefix(swapData),
253
+ timeout: authTimeout.toString(10),
254
+ signature: latestSlot + ";" + sig.signature.toString("hex")
255
+ };
256
+ }
257
+ /**
258
+ * Checks whether the provided signature data is valid, using preFetchedData if provided and still valid
259
+ *
260
+ * @param sender
261
+ * @param swapData
262
+ * @param timeout
263
+ * @param prefix
264
+ * @param signature
265
+ * @param feeRate
266
+ * @param preFetchedData
267
+ * @public
268
+ */
269
+ async isSignatureValid(sender, swapData, timeout, prefix, signature, feeRate, preFetchedData) {
270
+ if (swapData.isPayIn()) {
271
+ if (!swapData.isOfferer(sender))
272
+ throw new base_1.SignatureVerificationError("Sender needs to be offerer in payIn=true swaps");
273
+ }
274
+ else {
275
+ if (!swapData.isClaimer(sender))
276
+ throw new base_1.SignatureVerificationError("Sender needs to be claimer in payIn=false swaps");
277
+ }
278
+ const signer = swapData.isPayIn() ? swapData.claimer : swapData.offerer;
279
+ if (!swapData.isPayIn() && await this.program.isExpired(sender.toString(), swapData)) {
280
+ throw new base_1.SignatureVerificationError("Swap will expire too soon!");
281
+ }
282
+ if (prefix !== this.getAuthPrefix(swapData))
283
+ throw new base_1.SignatureVerificationError("Invalid prefix");
284
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
285
+ const isExpired = (BigInt(timeout) - currentTimestamp) < BigInt(this.program.authGracePeriod);
286
+ if (isExpired)
287
+ throw new base_1.SignatureVerificationError("Authorization expired!");
288
+ const [transactionSlot, signatureString] = signature.split(";");
289
+ const txSlot = parseInt(transactionSlot);
290
+ const [latestSlot, blockhash] = await Promise.all([
291
+ this.getSlotForSignature(preFetchedData),
292
+ this.getBlockhashForSignature(txSlot, preFetchedData)
293
+ ]);
294
+ const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
295
+ const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
296
+ if (slotsLeft < 0)
297
+ throw new base_1.SignatureVerificationError("Authorization expired!");
298
+ const txToSign = await this.getTxToSign(swapData, timeout, feeRate);
299
+ txToSign.feePayer = new web3_js_1.PublicKey(sender);
300
+ txToSign.recentBlockhash = blockhash;
301
+ txToSign.addSignature(signer, buffer_1.Buffer.from(signatureString, "hex"));
302
+ this.logger.debug("isSignatureValid(): Signed tx: ", txToSign);
303
+ const valid = txToSign.verifySignatures(false);
304
+ if (!valid)
305
+ throw new base_1.SignatureVerificationError("Invalid signature!");
306
+ return buffer_1.Buffer.from(blockhash);
307
+ }
308
+ /**
309
+ * Gets expiry of the provided signature data, this is a minimum of slot expiry & swap signature expiry
310
+ *
311
+ * @param timeout
312
+ * @param signature
313
+ * @param preFetchedData
314
+ * @public
315
+ */
316
+ async getSignatureExpiry(timeout, signature, preFetchedData) {
317
+ const [transactionSlotStr, signatureString] = signature.split(";");
318
+ const txSlot = parseInt(transactionSlotStr);
319
+ const latestSlot = await this.getSlotForSignature(preFetchedData);
320
+ const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
321
+ const slotsLeft = lastValidTransactionSlot - latestSlot - this.SIGNATURE_SLOT_BUFFER;
322
+ const now = Date.now();
323
+ const slotExpiryTime = now + (slotsLeft * this.root.SLOT_TIME);
324
+ const timeoutExpiryTime = (parseInt(timeout) - this.program.authGracePeriod) * 1000;
325
+ const expiry = Math.min(slotExpiryTime, timeoutExpiryTime);
326
+ if (expiry < now)
327
+ return 0;
328
+ return expiry;
329
+ }
330
+ /**
331
+ * Checks whether signature is expired for good (uses "finalized" slot)
332
+ *
333
+ * @param signature
334
+ * @param timeout
335
+ * @public
336
+ */
337
+ async isSignatureExpired(signature, timeout) {
338
+ const [transactionSlotStr, signatureString] = signature.split(";");
339
+ const txSlot = parseInt(transactionSlotStr);
340
+ const lastValidTransactionSlot = txSlot + this.root.TX_SLOT_VALIDITY;
341
+ const latestSlot = await this.root.Slots.getSlot("finalized");
342
+ const slotsLeft = lastValidTransactionSlot - latestSlot + this.SIGNATURE_SLOT_BUFFER;
343
+ if (slotsLeft < 0)
344
+ return true;
345
+ if ((parseInt(timeout) + this.program.authGracePeriod) * 1000 < Date.now())
346
+ return true;
347
+ return false;
348
+ }
349
+ /**
350
+ * Creates init transaction (InitPayIn) with a valid signature from an LP, also adds a SOL to WSOL wrapping ix to
351
+ * the init transaction (if indicated by the fee rate) or adds the wrapping in a separate transaction (if no
352
+ * indication in the fee rate)
353
+ *
354
+ * @param swapData swap to initialize
355
+ * @param timeout init signature timeout
356
+ * @param prefix init signature prefix
357
+ * @param signature init signature
358
+ * @param skipChecks whether to skip signature validity checks
359
+ * @param feeRate fee rate to use for the transaction
360
+ */
361
+ async txsInitPayIn(swapData, timeout, prefix, signature, skipChecks, feeRate) {
362
+ if (!skipChecks) {
363
+ const [_, payStatus] = await Promise.all([
364
+ (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getOfferer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError),
365
+ (0, Utils_1.tryWithRetries)(() => this.program.getClaimHashStatus(swapData.getClaimHash()), this.retryPolicy)
366
+ ]);
367
+ if (payStatus !== base_1.SwapCommitStateType.NOT_COMMITED)
368
+ throw new base_1.SwapDataVerificationError("Invoice already being paid for or paid");
369
+ }
370
+ const [slotNumber, signatureStr] = signature.split(";");
371
+ const block = await (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
372
+ const txs = [];
373
+ let isWrapping = false;
374
+ const isWrappedInSignedTx = feeRate != null && feeRate.split("#").length > 1;
375
+ if (!isWrappedInSignedTx && swapData.token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS)) {
376
+ const ataAcc = await (0, Utils_1.tryWithRetries)(() => this.root.Tokens.getATAOrNull(swapData.offererAta), this.retryPolicy);
377
+ const balance = ataAcc == null ? 0n : ataAcc.amount;
378
+ if (balance < swapData.getAmount()) {
379
+ //Need to wrap more SOL to WSOL
380
+ await this.root.Tokens.Wrap(swapData.offerer, swapData.getAmount() - balance, ataAcc == null)
381
+ .addToTxs(txs, feeRate, block);
382
+ isWrapping = true;
383
+ }
384
+ }
385
+ const initTx = await (await this.InitPayIn(swapData, BigInt(timeout), feeRate)).tx(feeRate, block);
386
+ initTx.tx.addSignature(swapData.claimer, buffer_1.Buffer.from(signatureStr, "hex"));
387
+ txs.push(initTx);
388
+ this.logger.debug("txsInitPayIn(): create swap init TX, swap: " + swapData.getClaimHash() +
389
+ " wrapping client-side: " + isWrapping + " feerate: " + feeRate);
390
+ return txs;
391
+ }
392
+ /**
393
+ * Creates init transactions (InitNotPayIn) with a valid signature from an intermediary
394
+ *
395
+ * @param swapData swap to initialize
396
+ * @param timeout init signature timeout
397
+ * @param prefix init signature prefix
398
+ * @param signature init signature
399
+ * @param skipChecks whether to skip signature validity checks
400
+ * @param feeRate fee rate to use for the transaction
401
+ */
402
+ async txsInit(swapData, timeout, prefix, signature, skipChecks, feeRate) {
403
+ if (!skipChecks) {
404
+ await (0, Utils_1.tryWithRetries)(() => this.isSignatureValid(swapData.getClaimer(), swapData, timeout, prefix, signature, feeRate), this.retryPolicy, (e) => e instanceof base_1.SignatureVerificationError);
405
+ }
406
+ const [slotNumber, signatureStr] = signature.split(";");
407
+ const block = await (0, Utils_1.tryWithRetries)(() => this.root.Blocks.getParsedBlock(parseInt(slotNumber)), this.retryPolicy);
408
+ const initTx = await (await this.InitNotPayIn(swapData, BigInt(timeout))).tx(feeRate, block);
409
+ initTx.tx.addSignature(swapData.offerer, buffer_1.Buffer.from(signatureStr, "hex"));
410
+ this.logger.debug("txsInit(): create swap init TX, swap: " + swapData.getClaimHash() + " feerate: " + feeRate);
411
+ return [initTx];
412
+ }
413
+ /**
414
+ * Returns the fee rate to be used for a specific init transaction, also adding indication whether the WSOL ATA
415
+ * should be initialized in the init transaction and/or current balance in the WSOL ATA
416
+ *
417
+ * @param offerer
418
+ * @param claimer
419
+ * @param token
420
+ * @param paymentHash
421
+ */
422
+ async getInitPayInFeeRate(offerer, claimer, token, paymentHash) {
423
+ const accounts = [];
424
+ if (offerer != null)
425
+ accounts.push(offerer);
426
+ if (token != null) {
427
+ accounts.push(this.program.SwapVault(token));
428
+ if (offerer != null)
429
+ accounts.push((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer));
430
+ if (claimer != null)
431
+ accounts.push(this.program.SwapUserVault(claimer, token));
432
+ }
433
+ if (paymentHash != null)
434
+ accounts.push(this.program.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
435
+ const shouldCheckWSOLAta = token != null && offerer != null && token.equals(SolanaTokens_1.SolanaTokens.WSOL_ADDRESS);
436
+ let [feeRate, _account] = await Promise.all([
437
+ this.root.Fees.getFeeRate(accounts),
438
+ shouldCheckWSOLAta ?
439
+ this.root.Tokens.getATAOrNull((0, spl_token_1.getAssociatedTokenAddressSync)(token, offerer)) :
440
+ Promise.resolve(null)
441
+ ]);
442
+ if (shouldCheckWSOLAta) {
443
+ const account = _account;
444
+ const balance = account == null ? 0n : account.amount;
445
+ //Add an indication about whether the ATA is initialized & balance it contains
446
+ feeRate += "#" + (account != null ? "0" : "1") + ";" + balance.toString(10);
447
+ }
448
+ this.logger.debug("getInitPayInFeeRate(): feerate computed: " + feeRate);
449
+ return feeRate;
450
+ }
451
+ /**
452
+ * Returns the fee rate to be used for a specific init transaction
453
+ *
454
+ * @param offerer
455
+ * @param claimer
456
+ * @param token
457
+ * @param paymentHash
458
+ */
459
+ getInitFeeRate(offerer, claimer, token, paymentHash) {
460
+ const accounts = [];
461
+ if (offerer != null && token != null)
462
+ accounts.push(this.program.SwapUserVault(offerer, token));
463
+ if (claimer != null)
464
+ accounts.push(claimer);
465
+ if (paymentHash != null)
466
+ accounts.push(this.program.SwapEscrowState(buffer_1.Buffer.from(paymentHash, "hex")));
467
+ return this.root.Fees.getFeeRate(accounts);
468
+ }
469
+ /**
470
+ * Get the estimated solana fee of the init transaction, this includes the required deposit for creating swap PDA
471
+ * and also deposit for ATAs
472
+ */
473
+ async getInitFee(swapData, feeRate) {
474
+ if (swapData == null)
475
+ return BigInt(this.program.ESCROW_STATE_RENT_EXEMPT) + await this.getRawInitFee(swapData, feeRate);
476
+ feeRate = feeRate ||
477
+ (swapData.payIn
478
+ ? await this.getInitPayInFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash)
479
+ : await this.getInitFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash));
480
+ const [rawFee, initAta] = await Promise.all([
481
+ this.getRawInitFee(swapData, feeRate),
482
+ swapData != null && swapData.payOut ?
483
+ this.root.Tokens.getATAOrNull((0, spl_token_1.getAssociatedTokenAddressSync)(swapData.token, swapData.claimer)).then(acc => acc == null) :
484
+ Promise.resolve(null)
485
+ ]);
486
+ let resultingFee = BigInt(this.program.ESCROW_STATE_RENT_EXEMPT) + rawFee;
487
+ if (initAta)
488
+ resultingFee += BigInt(SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT);
489
+ if (swapData.payIn && this.shouldWrapOnInit(swapData, feeRate) && this.extractAtaDataFromFeeRate(feeRate).initAta) {
490
+ resultingFee += BigInt(SolanaTokens_1.SolanaTokens.SPL_ATA_RENT_EXEMPT);
491
+ }
492
+ return resultingFee;
493
+ }
494
+ /**
495
+ * Get the estimated solana fee of the init transaction, without the required deposit for creating swap PDA
496
+ */
497
+ async getRawInitFee(swapData, feeRate) {
498
+ if (swapData == null)
499
+ return 10000n;
500
+ feeRate = feeRate ||
501
+ (swapData.payIn
502
+ ? await this.getInitPayInFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash)
503
+ : await this.getInitFeeRate(swapData.offerer, swapData.claimer, swapData.token, swapData.paymentHash));
504
+ let computeBudget = swapData.payIn ? SwapInit.CUCosts.INIT_PAY_IN : SwapInit.CUCosts.INIT;
505
+ if (swapData.payIn && this.shouldWrapOnInit(swapData, feeRate)) {
506
+ computeBudget += SolanaTokens_1.SolanaTokens.CUCosts.WRAP_SOL;
507
+ const data = this.extractAtaDataFromFeeRate(feeRate);
508
+ if (data.initAta)
509
+ computeBudget += SolanaTokens_1.SolanaTokens.CUCosts.ATA_INIT;
510
+ }
511
+ const baseFee = swapData.payIn ? 10000n : 10000n + 5000n;
512
+ return baseFee + this.root.Fees.getPriorityFee(computeBudget, feeRate);
513
+ }
514
+ }
515
+ exports.SwapInit = SwapInit;
516
+ SwapInit.CUCosts = {
517
+ INIT: 90000,
518
+ INIT_PAY_IN: 50000,
519
+ };