@atomiqlabs/chain-solana 11.0.0 → 12.0.6

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.
@@ -1,8 +1,10 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
2
  import * as BN from "bn.js";
3
- import { SwapData, ChainSwapType } from "@atomiqlabs/base";
3
+ import { ChainSwapType, SwapData } from "@atomiqlabs/base";
4
4
  import { SwapProgram } from "./programTypes";
5
5
  import { IdlAccounts, IdlTypes } from "@coral-xyz/anchor";
6
+ import { SingleInstructionWithAccounts } from "../program/modules/SolanaProgramEvents";
7
+ export type InitInstruction = SingleInstructionWithAccounts<SwapProgram["instructions"][2 | 3], SwapProgram>;
6
8
  export declare class SolanaSwapData extends SwapData {
7
9
  offerer: PublicKey;
8
10
  claimer: PublicKey;
@@ -41,6 +43,7 @@ export declare class SolanaSwapData extends SwapData {
41
43
  getEscrowHash(): string;
42
44
  getSequence(): bigint;
43
45
  getTxoHashHint(): string;
46
+ getHTLCHashHint(): string;
44
47
  getExtraData(): string;
45
48
  setExtraData(txoHash: string): void;
46
49
  getSecurityDeposit(): bigint;
@@ -49,6 +52,15 @@ export declare class SolanaSwapData extends SwapData {
49
52
  toSwapDataStruct(): IdlTypes<SwapProgram>["SwapData"];
50
53
  correctPDA(account: IdlAccounts<SwapProgram>["escrowState"]): boolean;
51
54
  equals(other: SolanaSwapData): boolean;
55
+ /**
56
+ * Converts initialize instruction data into {SolanaSwapData}
57
+ *
58
+ * @param initIx
59
+ * @param txoHash
60
+ * @private
61
+ * @returns {SolanaSwapData} converted and parsed swap data
62
+ */
63
+ static fromInstruction(initIx: InitInstruction, txoHash: string): SolanaSwapData;
52
64
  static fromEscrowState(account: IdlAccounts<SwapProgram>["escrowState"]): SolanaSwapData;
53
65
  static typeToKind(type: ChainSwapType): number;
54
66
  static kindToType(value: number): ChainSwapType;
@@ -135,6 +135,11 @@ class SolanaSwapData extends base_1.SwapData {
135
135
  return null; //Txo hash opt-out flag
136
136
  return this.txoHash;
137
137
  }
138
+ getHTLCHashHint() {
139
+ if (this.getType() === base_1.ChainSwapType.HTLC)
140
+ return this.paymentHash;
141
+ return null;
142
+ }
138
143
  getExtraData() {
139
144
  return this.txoHash;
140
145
  }
@@ -213,6 +218,26 @@ class SolanaSwapData extends base_1.SwapData {
213
218
  other.claimerBounty.eq(this.claimerBounty) &&
214
219
  other.token.equals(this.token);
215
220
  }
221
+ /**
222
+ * Converts initialize instruction data into {SolanaSwapData}
223
+ *
224
+ * @param initIx
225
+ * @param txoHash
226
+ * @private
227
+ * @returns {SolanaSwapData} converted and parsed swap data
228
+ */
229
+ static fromInstruction(initIx, txoHash) {
230
+ const paymentHash = buffer_1.Buffer.from(initIx.data.swapData.hash);
231
+ let securityDeposit = new BN(0);
232
+ let claimerBounty = new BN(0);
233
+ let payIn = true;
234
+ if (initIx.name === "offererInitialize") {
235
+ payIn = false;
236
+ securityDeposit = initIx.data.securityDeposit;
237
+ claimerBounty = initIx.data.claimerBounty;
238
+ }
239
+ return new SolanaSwapData(initIx.accounts.offerer, initIx.accounts.claimer, initIx.accounts.mint, initIx.data.swapData.amount, paymentHash.toString("hex"), initIx.data.swapData.sequence, initIx.data.swapData.expiry, initIx.data.swapData.nonce, initIx.data.swapData.confirmations, initIx.data.swapData.payOut, SwapTypeEnum_1.SwapTypeEnum.toNumber(initIx.data.swapData.kind), payIn, initIx.name === "offererInitializePayIn" ? initIx.accounts.offererAta : web3_js_1.PublicKey.default, initIx.data.swapData.payOut ? initIx.accounts.claimerAta : web3_js_1.PublicKey.default, securityDeposit, claimerBounty, txoHash);
240
+ }
216
241
  static fromEscrowState(account) {
217
242
  const data = account.data;
218
243
  return new SolanaSwapData(account.offerer, account.claimer, account.mint, data.amount, buffer_1.Buffer.from(data.hash).toString("hex"), data.sequence, data.expiry, data.nonce, data.confirmations, data.payOut, SwapTypeEnum_1.SwapTypeEnum.toNumber(data.kind), data.payIn, account.offererAta, account.claimerAta, account.securityDeposit, account.claimerBounty, null);
@@ -118,6 +118,12 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
118
118
  * @param data
119
119
  */
120
120
  getCommitStatus(signer: string, data: SolanaSwapData): Promise<SwapCommitState>;
121
+ getCommitStatuses(request: {
122
+ signer: string;
123
+ swapData: SolanaSwapData;
124
+ }[]): Promise<{
125
+ [p: string]: SwapCommitState;
126
+ }>;
121
127
  /**
122
128
  * Checks the status of the specific payment hash
123
129
  *
@@ -131,6 +137,22 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
131
137
  * @param claimHashHex
132
138
  */
133
139
  getCommitedData(claimHashHex: string): Promise<SolanaSwapData>;
140
+ getHistoricalSwaps(signer: string, startBlockheight?: number): Promise<{
141
+ swaps: {
142
+ [escrowHash: string]: {
143
+ init?: {
144
+ data: SolanaSwapData;
145
+ getInitTxId: () => Promise<string>;
146
+ getTxBlock: () => Promise<{
147
+ blockTime: number;
148
+ blockHeight: number;
149
+ }>;
150
+ };
151
+ state: SwapCommitState;
152
+ };
153
+ };
154
+ latestBlockheight: number;
155
+ }>;
134
156
  createSwapData(type: ChainSwapType, offerer: string, claimer: string, token: string, amount: bigint, claimHash: string, sequence: bigint, expiry: bigint, payIn: boolean, payOut: boolean, securityDeposit: bigint, claimerBounty: bigint, depositToken?: string): Promise<SolanaSwapData>;
135
157
  getBalance(signer: string, tokenAddress: string, inContract: boolean): Promise<bigint>;
136
158
  getIntermediaryData(address: string, token: string): Promise<{
@@ -185,18 +207,18 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
185
207
  /**
186
208
  * Get the estimated solana fee of the commit transaction
187
209
  */
188
- getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
210
+ getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
189
211
  /**
190
212
  * Get the estimated solana fee of the commit transaction, without any deposits
191
213
  */
192
- getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
214
+ getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
193
215
  /**
194
216
  * Get the estimated solana transaction fee of the refund transaction
195
217
  */
196
- getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
218
+ getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
197
219
  /**
198
220
  * Get the estimated solana transaction fee of the refund transaction
199
221
  */
200
- getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
222
+ getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
201
223
  getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer;
202
224
  }
@@ -20,6 +20,7 @@ const BN = require("bn.js");
20
20
  function toPublicKeyOrNull(str) {
21
21
  return str == null ? null : new web3_js_1.PublicKey(str);
22
22
  }
23
+ const MAX_PARALLEL_COMMIT_STATUS_CHECKS = 5;
23
24
  class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
24
25
  constructor(chainInterface, btcRelay, storage, programAddress) {
25
26
  super(chainInterface, programIdl, programAddress);
@@ -194,7 +195,7 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
194
195
  return { type: base_1.SwapCommitStateType.NOT_COMMITED };
195
196
  }
196
197
  //Check if paid or what
197
- const status = await this.Events.findInEvents(escrowStateKey, async (event, info) => {
198
+ const status = await this.Events.findInEvents(escrowStateKey, async (event, tx) => {
198
199
  if (event.name === "ClaimEvent") {
199
200
  const paymentHash = buffer_1.Buffer.from(event.data.hash).toString("hex");
200
201
  if (paymentHash !== data.paymentHash)
@@ -203,14 +204,12 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
203
204
  return null;
204
205
  return {
205
206
  type: base_1.SwapCommitStateType.PAID,
206
- getClaimTxId: () => Promise.resolve(info.signature),
207
+ getClaimTxId: () => Promise.resolve(tx.transaction.signatures[0]),
207
208
  getClaimResult: () => Promise.resolve(buffer_1.Buffer.from(event.data.secret).toString("hex")),
208
- getTxBlock: async () => {
209
- return {
210
- blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
211
- blockTime: info.blockTime
212
- };
213
- }
209
+ getTxBlock: () => Promise.resolve({
210
+ blockHeight: tx.slot,
211
+ blockTime: tx.blockTime
212
+ })
214
213
  };
215
214
  }
216
215
  if (event.name === "RefundEvent") {
@@ -221,13 +220,11 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
221
220
  return null;
222
221
  return {
223
222
  type: isExpired ? base_1.SwapCommitStateType.EXPIRED : base_1.SwapCommitStateType.NOT_COMMITED,
224
- getRefundTxId: () => Promise.resolve(info.signature),
225
- getTxBlock: async () => {
226
- return {
227
- blockHeight: (await this.Chain.Blocks.getParsedBlock(info.slot)).blockHeight,
228
- blockTime: info.blockTime
229
- };
230
- }
223
+ getRefundTxId: () => Promise.resolve(tx.transaction.signatures[0]),
224
+ getTxBlock: () => Promise.resolve({
225
+ blockHeight: tx.slot,
226
+ blockTime: tx.blockTime
227
+ })
231
228
  };
232
229
  }
233
230
  });
@@ -237,6 +234,21 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
237
234
  return { type: base_1.SwapCommitStateType.EXPIRED };
238
235
  return { type: base_1.SwapCommitStateType.NOT_COMMITED };
239
236
  }
237
+ async getCommitStatuses(request) {
238
+ const result = {};
239
+ let promises = [];
240
+ for (let { signer, swapData } of request) {
241
+ promises.push(this.getCommitStatus(signer, swapData).then(val => {
242
+ result[swapData.getEscrowHash()] = val;
243
+ }));
244
+ if (promises.length >= MAX_PARALLEL_COMMIT_STATUS_CHECKS) {
245
+ await Promise.all(promises);
246
+ promises = [];
247
+ }
248
+ }
249
+ await Promise.all(promises);
250
+ return result;
251
+ }
240
252
  /**
241
253
  * Checks the status of the specific payment hash
242
254
  *
@@ -283,6 +295,91 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
283
295
  return null;
284
296
  return SolanaSwapData_1.SolanaSwapData.fromEscrowState(account);
285
297
  }
298
+ async getHistoricalSwaps(signer, startBlockheight) {
299
+ let latestBlockheight;
300
+ const events = [];
301
+ await this.Events.findInEvents(new web3_js_1.PublicKey(signer), async (event, tx) => {
302
+ if (latestBlockheight == null)
303
+ latestBlockheight = tx.slot;
304
+ events.push({ event, tx });
305
+ }, undefined, undefined, startBlockheight);
306
+ const swapsOpened = {};
307
+ const resultingSwaps = {};
308
+ events.reverse();
309
+ for (let { event, tx } of events) {
310
+ const txSignature = tx.transaction.signatures[0];
311
+ const paymentHash = buffer_1.Buffer.from(event.data.hash).toString("hex");
312
+ const escrowHash = (0, Utils_1.toEscrowHash)(paymentHash, event.data.sequence);
313
+ if (event.name === "InitializeEvent") {
314
+ //Parse swap data from initialize event
315
+ const txoHash = buffer_1.Buffer.from(event.data.txoHash).toString("hex");
316
+ const instructions = this.Events.decodeInstructions(tx.transaction.message);
317
+ if (instructions == null) {
318
+ this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because cannot parse instructions!`);
319
+ continue;
320
+ }
321
+ const initIx = instructions.find(ix => ix != null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize"));
322
+ if (initIx == null) {
323
+ this.logger.warn(`getHistoricalSwaps(): Skipping tx ${txSignature} because cannot init instruction not found!`);
324
+ continue;
325
+ }
326
+ swapsOpened[escrowHash] = {
327
+ data: SolanaSwapData_1.SolanaSwapData.fromInstruction(initIx, txoHash),
328
+ getInitTxId: () => Promise.resolve(txSignature),
329
+ getTxBlock: () => Promise.resolve({
330
+ blockHeight: tx.slot,
331
+ blockTime: tx.blockTime
332
+ })
333
+ };
334
+ }
335
+ if (event.name === "ClaimEvent") {
336
+ const foundSwapData = swapsOpened[escrowHash];
337
+ delete swapsOpened[escrowHash];
338
+ resultingSwaps[escrowHash] = {
339
+ init: foundSwapData,
340
+ state: {
341
+ type: base_1.SwapCommitStateType.PAID,
342
+ getClaimTxId: () => Promise.resolve(txSignature),
343
+ getClaimResult: () => Promise.resolve(buffer_1.Buffer.from(event.data.secret).toString("hex")),
344
+ getTxBlock: () => Promise.resolve({
345
+ blockHeight: tx.slot,
346
+ blockTime: tx.blockTime
347
+ })
348
+ }
349
+ };
350
+ }
351
+ if (event.name === "RefundEvent") {
352
+ const foundSwapData = swapsOpened[escrowHash];
353
+ delete swapsOpened[escrowHash];
354
+ const isExpired = foundSwapData != null && await this.isExpired(signer, foundSwapData.data);
355
+ resultingSwaps[escrowHash] = {
356
+ init: foundSwapData,
357
+ state: {
358
+ type: isExpired ? base_1.SwapCommitStateType.EXPIRED : base_1.SwapCommitStateType.NOT_COMMITED,
359
+ getRefundTxId: () => Promise.resolve(txSignature),
360
+ getTxBlock: () => Promise.resolve({
361
+ blockHeight: tx.slot,
362
+ blockTime: tx.blockTime
363
+ })
364
+ }
365
+ };
366
+ }
367
+ }
368
+ for (let escrowHash in swapsOpened) {
369
+ const foundSwapData = swapsOpened[escrowHash];
370
+ const isExpired = await this.isExpired(signer, foundSwapData.data);
371
+ resultingSwaps[escrowHash] = {
372
+ init: foundSwapData,
373
+ state: foundSwapData.data.isOfferer(signer) && isExpired
374
+ ? { type: base_1.SwapCommitStateType.REFUNDABLE }
375
+ : { type: base_1.SwapCommitStateType.COMMITED }
376
+ };
377
+ }
378
+ return {
379
+ swaps: resultingSwaps,
380
+ latestBlockheight
381
+ };
382
+ }
286
383
  ////////////////////////////////////////////
287
384
  //// Swap data initializer
288
385
  createSwapData(type, offerer, claimer, token, amount, claimHash, sequence, expiry, payIn, payOut, securityDeposit, claimerBounty, depositToken) {
@@ -439,25 +536,25 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
439
536
  /**
440
537
  * Get the estimated solana fee of the commit transaction
441
538
  */
442
- getCommitFee(swapData, feeRate) {
539
+ getCommitFee(signer, swapData, feeRate) {
443
540
  return this.Init.getInitFee(swapData, feeRate);
444
541
  }
445
542
  /**
446
543
  * Get the estimated solana fee of the commit transaction, without any deposits
447
544
  */
448
- getRawCommitFee(swapData, feeRate) {
545
+ getRawCommitFee(signer, swapData, feeRate) {
449
546
  return this.Init.getRawInitFee(swapData, feeRate);
450
547
  }
451
548
  /**
452
549
  * Get the estimated solana transaction fee of the refund transaction
453
550
  */
454
- getRefundFee(swapData, feeRate) {
551
+ getRefundFee(signer, swapData, feeRate) {
455
552
  return this.Refund.getRefundFee(swapData, feeRate);
456
553
  }
457
554
  /**
458
555
  * Get the estimated solana transaction fee of the refund transaction
459
556
  */
460
- getRawRefundFee(swapData, feeRate) {
557
+ getRawRefundFee(signer, swapData, feeRate) {
461
558
  return this.Refund.getRawRefundFee(swapData, feeRate);
462
559
  }
463
560
  getExtraData(outputScript, amount, confirmations, nonce) {
@@ -2,6 +2,7 @@ import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
2
2
  import { AbstractSigner } from "@atomiqlabs/base";
3
3
  import { PublicKey, Signer } from "@solana/web3.js";
4
4
  export declare class SolanaSigner implements AbstractSigner {
5
+ type: "AtomiqAbstractSigner";
5
6
  wallet: Wallet;
6
7
  keypair?: Signer;
7
8
  constructor(wallet: Wallet, keypair?: Signer);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SolanaSigner = void 0;
4
4
  class SolanaSigner {
5
5
  constructor(wallet, keypair) {
6
+ this.type = "AtomiqAbstractSigner";
6
7
  this.wallet = wallet;
7
8
  this.keypair = keypair;
8
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/chain-solana",
3
- "version": "11.0.0",
3
+ "version": "12.0.6",
4
4
  "description": "Solana specific base implementation",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "author": "adambor",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@atomiqlabs/base": "^11.0.0",
25
+ "@atomiqlabs/base": "^12.0.3",
26
26
  "@noble/hashes": "^1.7.1",
27
27
  "bn.js": "5.2.1",
28
28
  "bs58": "4.0.1",
@@ -7,6 +7,7 @@ import {SolanaSwapData} from "./swaps/SolanaSwapData";
7
7
  import {SolanaChainEventsBrowser} from "./events/SolanaChainEventsBrowser";
8
8
  import {SolanaBtcRelay} from "./btcrelay/SolanaBtcRelay";
9
9
  import {SolanaChainInterface} from "./chain/SolanaChainInterface";
10
+ import {Wallet} from "@coral-xyz/anchor/dist/cjs/provider";
10
11
 
11
12
  export type SolanaChainType = ChainType<
12
13
  "SOLANA",
@@ -14,6 +15,7 @@ export type SolanaChainType = ChainType<
14
15
  SolanaPreFetchVerification,
15
16
  SolanaTx,
16
17
  SolanaSigner,
18
+ Wallet,
17
19
  SolanaSwapData,
18
20
  SolanaSwapProgram,
19
21
  SolanaChainInterface,
@@ -12,6 +12,7 @@ import {SolanaAddresses} from "./modules/SolanaAddresses";
12
12
  import {SolanaSigner} from "../wallet/SolanaSigner";
13
13
  import {Buffer} from "buffer";
14
14
  import {SolanaKeypairWallet} from "../wallet/SolanaKeypairWallet";
15
+ import {Wallet} from "@coral-xyz/anchor/dist/cjs/provider";
15
16
 
16
17
  export type SolanaRetryPolicy = {
17
18
  maxRetries?: number,
@@ -23,7 +24,8 @@ export type SolanaRetryPolicy = {
23
24
  export class SolanaChainInterface implements ChainInterface<
24
25
  SolanaTx,
25
26
  SolanaSigner,
26
- "SOLANA"
27
+ "SOLANA",
28
+ Wallet
27
29
  > {
28
30
  readonly chainId = "SOLANA";
29
31
 
@@ -78,6 +80,10 @@ export class SolanaChainInterface implements ChainInterface<
78
80
  return SolanaAddresses.isValidAddress(address);
79
81
  }
80
82
 
83
+ normalizeAddress(address: string): string {
84
+ return address;
85
+ }
86
+
81
87
  getNativeCurrencyAddress(): string {
82
88
  return this.Tokens.getNativeCurrencyAddress().toString();
83
89
  }
@@ -128,6 +134,14 @@ export class SolanaChainInterface implements ChainInterface<
128
134
  return this.Transactions.getTxStatus(tx);
129
135
  }
130
136
 
137
+ async getFinalizedBlock(): Promise<{ height: number; blockHash: string }> {
138
+ const {block} = await this.Blocks.findLatestParsedBlock("finalized");
139
+ return {
140
+ height: block.blockHeight,
141
+ blockHash: block.blockhash
142
+ };
143
+ }
144
+
131
145
 
132
146
  ///////////////////////////////////
133
147
  //// Callbacks & handlers
@@ -172,4 +186,8 @@ export class SolanaChainInterface implements ChainInterface<
172
186
  return new SolanaSigner(wallet, keypair);
173
187
  }
174
188
 
189
+ wrapSigner(signer: Wallet): Promise<SolanaSigner> {
190
+ return Promise.resolve(new SolanaSigner(signer));
191
+ }
192
+
175
193
  }