@atomiqlabs/chain-solana 11.0.0 → 12.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.
- package/dist/solana/SolanaChainType.d.ts +2 -1
- package/dist/solana/chain/SolanaChainInterface.d.ts +8 -1
- package/dist/solana/chain/SolanaChainInterface.js +13 -0
- package/dist/solana/chain/modules/SolanaTransactions.js +17 -26
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +10 -4
- package/dist/solana/swaps/SolanaSwapProgram.js +20 -4
- package/dist/solana/wallet/SolanaSigner.d.ts +1 -0
- package/dist/solana/wallet/SolanaSigner.js +1 -0
- package/package.json +2 -2
- package/src/solana/SolanaChainType.ts +2 -0
- package/src/solana/chain/SolanaChainInterface.ts +19 -1
- package/src/solana/chain/modules/SolanaTransactions.ts +17 -22
- package/src/solana/swaps/SolanaSwapProgram.ts +26 -4
- package/src/solana/wallet/SolanaSigner.ts +1 -0
|
@@ -7,4 +7,5 @@ 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
|
-
|
|
10
|
+
import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
|
|
11
|
+
export type SolanaChainType = ChainType<"SOLANA", SolanaPreFetchData, SolanaPreFetchVerification, SolanaTx, SolanaSigner, Wallet, SolanaSwapData, SolanaSwapProgram, SolanaChainInterface, SolanaChainEventsBrowser, SolanaBtcRelay<any>, never, never, never>;
|
|
@@ -10,13 +10,14 @@ import { SolanaEvents } from "./modules/SolanaEvents";
|
|
|
10
10
|
import { ChainInterface, TransactionConfirmationOptions } from "@atomiqlabs/base";
|
|
11
11
|
import { SolanaSigner } from "../wallet/SolanaSigner";
|
|
12
12
|
import { Buffer } from "buffer";
|
|
13
|
+
import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
|
|
13
14
|
export type SolanaRetryPolicy = {
|
|
14
15
|
maxRetries?: number;
|
|
15
16
|
delay?: number;
|
|
16
17
|
exponential?: boolean;
|
|
17
18
|
transactionResendInterval?: number;
|
|
18
19
|
};
|
|
19
|
-
export declare class SolanaChainInterface implements ChainInterface<SolanaTx, SolanaSigner, "SOLANA"> {
|
|
20
|
+
export declare class SolanaChainInterface implements ChainInterface<SolanaTx, SolanaSigner, "SOLANA", Wallet> {
|
|
20
21
|
readonly chainId = "SOLANA";
|
|
21
22
|
readonly SLOT_TIME = 400;
|
|
22
23
|
readonly TX_SLOT_VALIDITY = 151;
|
|
@@ -38,6 +39,7 @@ export declare class SolanaChainInterface implements ChainInterface<SolanaTx, So
|
|
|
38
39
|
constructor(connection: Connection, retryPolicy?: SolanaRetryPolicy, solanaFeeEstimator?: SolanaFees);
|
|
39
40
|
getBalance(signer: string, tokenAddress: string): Promise<bigint>;
|
|
40
41
|
isValidAddress(address: string): boolean;
|
|
42
|
+
normalizeAddress(address: string): string;
|
|
41
43
|
getNativeCurrencyAddress(): string;
|
|
42
44
|
txsTransfer(signer: string, token: string, amount: bigint, dstAddress: string, feeRate?: string): Promise<SolanaTx[]>;
|
|
43
45
|
transfer(signer: SolanaSigner, token: string, amount: bigint, dstAddress: string, txOptions?: TransactionConfirmationOptions): Promise<string>;
|
|
@@ -46,6 +48,10 @@ export declare class SolanaChainInterface implements ChainInterface<SolanaTx, So
|
|
|
46
48
|
deserializeTx(txData: string): Promise<SolanaTx>;
|
|
47
49
|
getTxIdStatus(txId: string): Promise<"not_found" | "pending" | "success" | "reverted">;
|
|
48
50
|
getTxStatus(tx: string): Promise<"not_found" | "pending" | "success" | "reverted">;
|
|
51
|
+
getFinalizedBlock(): Promise<{
|
|
52
|
+
height: number;
|
|
53
|
+
blockHash: string;
|
|
54
|
+
}>;
|
|
49
55
|
offBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): boolean;
|
|
50
56
|
onBeforeTxReplace(callback: (oldTx: string, oldTxId: string, newTx: string, newTxId: string) => Promise<void>): void;
|
|
51
57
|
onBeforeTxSigned(callback: (tx: SolanaTx) => Promise<void>): void;
|
|
@@ -55,4 +61,5 @@ export declare class SolanaChainInterface implements ChainInterface<SolanaTx, So
|
|
|
55
61
|
isValidToken(tokenIdentifier: string): boolean;
|
|
56
62
|
randomAddress(): string;
|
|
57
63
|
randomSigner(): SolanaSigner;
|
|
64
|
+
wrapSigner(signer: Wallet): Promise<SolanaSigner>;
|
|
58
65
|
}
|
|
@@ -45,6 +45,9 @@ class SolanaChainInterface {
|
|
|
45
45
|
isValidAddress(address) {
|
|
46
46
|
return SolanaAddresses_1.SolanaAddresses.isValidAddress(address);
|
|
47
47
|
}
|
|
48
|
+
normalizeAddress(address) {
|
|
49
|
+
return address;
|
|
50
|
+
}
|
|
48
51
|
getNativeCurrencyAddress() {
|
|
49
52
|
return this.Tokens.getNativeCurrencyAddress().toString();
|
|
50
53
|
}
|
|
@@ -73,6 +76,13 @@ class SolanaChainInterface {
|
|
|
73
76
|
getTxStatus(tx) {
|
|
74
77
|
return this.Transactions.getTxStatus(tx);
|
|
75
78
|
}
|
|
79
|
+
async getFinalizedBlock() {
|
|
80
|
+
const { block } = await this.Blocks.findLatestParsedBlock("finalized");
|
|
81
|
+
return {
|
|
82
|
+
height: block.blockHeight,
|
|
83
|
+
blockHash: block.blockhash
|
|
84
|
+
};
|
|
85
|
+
}
|
|
76
86
|
///////////////////////////////////
|
|
77
87
|
//// Callbacks & handlers
|
|
78
88
|
offBeforeTxReplace(callback) {
|
|
@@ -108,5 +118,8 @@ class SolanaChainInterface {
|
|
|
108
118
|
const wallet = new SolanaKeypairWallet_1.SolanaKeypairWallet(keypair);
|
|
109
119
|
return new SolanaSigner_1.SolanaSigner(wallet, keypair);
|
|
110
120
|
}
|
|
121
|
+
wrapSigner(signer) {
|
|
122
|
+
return Promise.resolve(new SolanaSigner_1.SolanaSigner(signer));
|
|
123
|
+
}
|
|
111
124
|
}
|
|
112
125
|
exports.SolanaChainInterface = SolanaChainInterface;
|
|
@@ -6,6 +6,7 @@ const SolanaModule_1 = require("../SolanaModule");
|
|
|
6
6
|
const bs58 = require("bs58");
|
|
7
7
|
const Utils_1 = require("../../../utils/Utils");
|
|
8
8
|
const buffer_1 = require("buffer");
|
|
9
|
+
const base_1 = require("@atomiqlabs/base");
|
|
9
10
|
class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
10
11
|
/**
|
|
11
12
|
* Sends raw solana transaction, first through the cbkSendTransaction callback (for e.g. sending the transaction
|
|
@@ -54,7 +55,7 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
|
54
55
|
resolve(signature);
|
|
55
56
|
}
|
|
56
57
|
if (status === "reverted")
|
|
57
|
-
reject(new
|
|
58
|
+
reject(new base_1.TransactionRevertedError("Transaction reverted!"));
|
|
58
59
|
clearInterval(watchdogInterval);
|
|
59
60
|
}, this.retryPolicy?.transactionResendInterval || 3000);
|
|
60
61
|
if (abortSignal != null)
|
|
@@ -95,7 +96,7 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
|
95
96
|
if (status === "success")
|
|
96
97
|
return signature;
|
|
97
98
|
if (status === "reverted")
|
|
98
|
-
throw new
|
|
99
|
+
throw new base_1.TransactionRevertedError("Transaction reverted!");
|
|
99
100
|
if (err instanceof web3_js_1.TransactionExpiredBlockheightExceededError || err.toString().startsWith("TransactionExpiredBlockheightExceededError")) {
|
|
100
101
|
throw new Error("Transaction expired before confirmation, please try again!");
|
|
101
102
|
}
|
|
@@ -104,7 +105,7 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
if (result.value.err != null)
|
|
107
|
-
throw new
|
|
108
|
+
throw new base_1.TransactionRevertedError("Transaction reverted!");
|
|
108
109
|
return signature;
|
|
109
110
|
}
|
|
110
111
|
/**
|
|
@@ -209,9 +210,10 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
|
209
210
|
};
|
|
210
211
|
this.logger.debug("sendAndConfirm(): sending transactions, count: " + _txs.length +
|
|
211
212
|
" waitForConfirmation: " + waitForConfirmation + " parallel: " + parallel);
|
|
213
|
+
const BATCH_SIZE = 50;
|
|
212
214
|
const signatures = [];
|
|
213
|
-
for (let e = 0; e < _txs.length; e +=
|
|
214
|
-
const txs = _txs.slice(e, e +
|
|
215
|
+
for (let e = 0; e < _txs.length; e += BATCH_SIZE) {
|
|
216
|
+
const txs = _txs.slice(e, e + BATCH_SIZE);
|
|
215
217
|
await this.prepareTransactions(signer, txs);
|
|
216
218
|
const signedTxs = await signer.wallet.signAllTransactions(txs.map(e => e.tx));
|
|
217
219
|
signedTxs.forEach((tx, index) => {
|
|
@@ -220,27 +222,16 @@ class SolanaTransactions extends SolanaModule_1.SolanaModule {
|
|
|
220
222
|
solTx.tx = tx;
|
|
221
223
|
});
|
|
222
224
|
this.logger.debug("sendAndConfirm(): sending transaction batch (" + e + ".." + (e + 50) + "), count: " + txs.length);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
for (let i = 0; i < txs.length; i++) {
|
|
236
|
-
const solTx = txs[i];
|
|
237
|
-
const signature = await this.sendSignedTransaction(solTx, options, onBeforePublish);
|
|
238
|
-
const confirmPromise = this.confirmTransaction(solTx, abortSignal, "confirmed");
|
|
239
|
-
//Don't await the last promise when !waitForConfirmation
|
|
240
|
-
if (i < txs.length - 1 || e + 50 < _txs.length || waitForConfirmation)
|
|
241
|
-
await confirmPromise;
|
|
242
|
-
signatures.push(signature);
|
|
243
|
-
}
|
|
225
|
+
//For solana we are forced to send txs one-by-one even with parallel, as we cannot determine their order upfront,
|
|
226
|
+
// however e.g. Jito could possibly handle sending a single package of up to 5 txns in order.
|
|
227
|
+
for (let i = 0; i < txs.length; i++) {
|
|
228
|
+
const solTx = txs[i];
|
|
229
|
+
const signature = await this.sendSignedTransaction(solTx, options, onBeforePublish);
|
|
230
|
+
const confirmPromise = this.confirmTransaction(solTx, abortSignal, "confirmed");
|
|
231
|
+
//Don't await the last promise when !waitForConfirmation
|
|
232
|
+
if (i < txs.length - 1 || e + 50 < _txs.length || waitForConfirmation)
|
|
233
|
+
await confirmPromise;
|
|
234
|
+
signatures.push(signature);
|
|
244
235
|
}
|
|
245
236
|
}
|
|
246
237
|
this.logger.info("sendAndConfirm(): sent transactions, count: " + _txs.length +
|
|
@@ -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
|
*
|
|
@@ -185,18 +191,18 @@ export declare class SolanaSwapProgram extends SolanaProgramBase<SwapProgram> im
|
|
|
185
191
|
/**
|
|
186
192
|
* Get the estimated solana fee of the commit transaction
|
|
187
193
|
*/
|
|
188
|
-
getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
194
|
+
getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
189
195
|
/**
|
|
190
196
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
191
197
|
*/
|
|
192
|
-
getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
198
|
+
getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
193
199
|
/**
|
|
194
200
|
* Get the estimated solana transaction fee of the refund transaction
|
|
195
201
|
*/
|
|
196
|
-
getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
202
|
+
getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
197
203
|
/**
|
|
198
204
|
* Get the estimated solana transaction fee of the refund transaction
|
|
199
205
|
*/
|
|
200
|
-
getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
206
|
+
getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint>;
|
|
201
207
|
getExtraData(outputScript: Buffer, amount: bigint, confirmations: number, nonce?: bigint): Buffer;
|
|
202
208
|
}
|
|
@@ -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);
|
|
@@ -237,6 +238,21 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
237
238
|
return { type: base_1.SwapCommitStateType.EXPIRED };
|
|
238
239
|
return { type: base_1.SwapCommitStateType.NOT_COMMITED };
|
|
239
240
|
}
|
|
241
|
+
async getCommitStatuses(request) {
|
|
242
|
+
const result = {};
|
|
243
|
+
let promises = [];
|
|
244
|
+
for (let { signer, swapData } of request) {
|
|
245
|
+
promises.push(this.getCommitStatus(signer, swapData).then(val => {
|
|
246
|
+
result[swapData.getEscrowHash()] = val;
|
|
247
|
+
}));
|
|
248
|
+
if (promises.length >= MAX_PARALLEL_COMMIT_STATUS_CHECKS) {
|
|
249
|
+
await Promise.all(promises);
|
|
250
|
+
promises = [];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
await Promise.all(promises);
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
240
256
|
/**
|
|
241
257
|
* Checks the status of the specific payment hash
|
|
242
258
|
*
|
|
@@ -439,25 +455,25 @@ class SolanaSwapProgram extends SolanaProgramBase_1.SolanaProgramBase {
|
|
|
439
455
|
/**
|
|
440
456
|
* Get the estimated solana fee of the commit transaction
|
|
441
457
|
*/
|
|
442
|
-
getCommitFee(swapData, feeRate) {
|
|
458
|
+
getCommitFee(signer, swapData, feeRate) {
|
|
443
459
|
return this.Init.getInitFee(swapData, feeRate);
|
|
444
460
|
}
|
|
445
461
|
/**
|
|
446
462
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
447
463
|
*/
|
|
448
|
-
getRawCommitFee(swapData, feeRate) {
|
|
464
|
+
getRawCommitFee(signer, swapData, feeRate) {
|
|
449
465
|
return this.Init.getRawInitFee(swapData, feeRate);
|
|
450
466
|
}
|
|
451
467
|
/**
|
|
452
468
|
* Get the estimated solana transaction fee of the refund transaction
|
|
453
469
|
*/
|
|
454
|
-
getRefundFee(swapData, feeRate) {
|
|
470
|
+
getRefundFee(signer, swapData, feeRate) {
|
|
455
471
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
456
472
|
}
|
|
457
473
|
/**
|
|
458
474
|
* Get the estimated solana transaction fee of the refund transaction
|
|
459
475
|
*/
|
|
460
|
-
getRawRefundFee(swapData, feeRate) {
|
|
476
|
+
getRawRefundFee(signer, swapData, feeRate) {
|
|
461
477
|
return this.Refund.getRawRefundFee(swapData, feeRate);
|
|
462
478
|
}
|
|
463
479
|
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);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomiqlabs/chain-solana",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
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": "^
|
|
25
|
+
"@atomiqlabs/base": "^12.0.0",
|
|
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
|
}
|
|
@@ -9,6 +9,7 @@ import * as bs58 from "bs58";
|
|
|
9
9
|
import {tryWithRetries} from "../../../utils/Utils";
|
|
10
10
|
import {Buffer} from "buffer";
|
|
11
11
|
import {SolanaSigner} from "../../wallet/SolanaSigner";
|
|
12
|
+
import {TransactionRevertedError} from "@atomiqlabs/base";
|
|
12
13
|
|
|
13
14
|
export type SolanaTx = {tx: Transaction, signers: Signer[]};
|
|
14
15
|
|
|
@@ -74,7 +75,7 @@ export class SolanaTransactions extends SolanaModule {
|
|
|
74
75
|
this.logger.info("txConfirmationAndResendWatchdog(): transaction confirmed from HTTP polling, signature: "+signature);
|
|
75
76
|
resolve(signature);
|
|
76
77
|
}
|
|
77
|
-
if(status==="reverted") reject(new
|
|
78
|
+
if(status==="reverted") reject(new TransactionRevertedError("Transaction reverted!"));
|
|
78
79
|
clearInterval(watchdogInterval);
|
|
79
80
|
}, this.retryPolicy?.transactionResendInterval || 3000);
|
|
80
81
|
|
|
@@ -119,14 +120,14 @@ export class SolanaTransactions extends SolanaModule {
|
|
|
119
120
|
);
|
|
120
121
|
this.logger.info("txConfirmFromWebsocket(): transaction status: "+status+" signature: "+signature);
|
|
121
122
|
if(status==="success") return signature;
|
|
122
|
-
if(status==="reverted") throw new
|
|
123
|
+
if(status==="reverted") throw new TransactionRevertedError("Transaction reverted!");
|
|
123
124
|
if(err instanceof TransactionExpiredBlockheightExceededError || err.toString().startsWith("TransactionExpiredBlockheightExceededError")) {
|
|
124
125
|
throw new Error("Transaction expired before confirmation, please try again!");
|
|
125
126
|
} else {
|
|
126
127
|
throw err;
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
|
-
if(result.value.err!=null) throw new
|
|
130
|
+
if(result.value.err!=null) throw new TransactionRevertedError("Transaction reverted!");
|
|
130
131
|
return signature;
|
|
131
132
|
}
|
|
132
133
|
|
|
@@ -239,9 +240,11 @@ export class SolanaTransactions extends SolanaModule {
|
|
|
239
240
|
this.logger.debug("sendAndConfirm(): sending transactions, count: "+_txs.length+
|
|
240
241
|
" waitForConfirmation: "+waitForConfirmation+" parallel: "+parallel);
|
|
241
242
|
|
|
243
|
+
const BATCH_SIZE = 50;
|
|
244
|
+
|
|
242
245
|
const signatures: string[] = [];
|
|
243
|
-
for(let e=0;e<_txs.length;e+=
|
|
244
|
-
const txs = _txs.slice(e, e+
|
|
246
|
+
for(let e=0;e<_txs.length;e+=BATCH_SIZE) {
|
|
247
|
+
const txs = _txs.slice(e, e+BATCH_SIZE);
|
|
245
248
|
|
|
246
249
|
await this.prepareTransactions(signer, txs)
|
|
247
250
|
const signedTxs = await signer.wallet.signAllTransactions(txs.map(e => e.tx));
|
|
@@ -252,23 +255,15 @@ export class SolanaTransactions extends SolanaModule {
|
|
|
252
255
|
});
|
|
253
256
|
this.logger.debug("sendAndConfirm(): sending transaction batch ("+e+".."+(e+50)+"), count: "+txs.length);
|
|
254
257
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if(
|
|
263
|
-
|
|
264
|
-
for(let i=0;i<txs.length;i++) {
|
|
265
|
-
const solTx = txs[i];
|
|
266
|
-
const signature = await this.sendSignedTransaction(solTx, options, onBeforePublish);
|
|
267
|
-
const confirmPromise = this.confirmTransaction(solTx, abortSignal, "confirmed");
|
|
268
|
-
//Don't await the last promise when !waitForConfirmation
|
|
269
|
-
if(i<txs.length-1 || e+50<_txs.length || waitForConfirmation) await confirmPromise;
|
|
270
|
-
signatures.push(signature);
|
|
271
|
-
}
|
|
258
|
+
//For solana we are forced to send txs one-by-one even with parallel, as we cannot determine their order upfront,
|
|
259
|
+
// however e.g. Jito could possibly handle sending a single package of up to 5 txns in order.
|
|
260
|
+
for(let i=0;i<txs.length;i++) {
|
|
261
|
+
const solTx = txs[i];
|
|
262
|
+
const signature = await this.sendSignedTransaction(solTx, options, onBeforePublish);
|
|
263
|
+
const confirmPromise = this.confirmTransaction(solTx, abortSignal, "confirmed");
|
|
264
|
+
//Don't await the last promise when !waitForConfirmation
|
|
265
|
+
if(i<txs.length-1 || e+50<_txs.length || waitForConfirmation) await confirmPromise;
|
|
266
|
+
signatures.push(signature);
|
|
272
267
|
}
|
|
273
268
|
}
|
|
274
269
|
|
|
@@ -41,6 +41,8 @@ function toPublicKeyOrNull(str: string | null): PublicKey | null {
|
|
|
41
41
|
return str==null ? null : new PublicKey(str);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
const MAX_PARALLEL_COMMIT_STATUS_CHECKS = 5;
|
|
45
|
+
|
|
44
46
|
export class SolanaSwapProgram
|
|
45
47
|
extends SolanaProgramBase<SwapProgram>
|
|
46
48
|
implements SwapContract<
|
|
@@ -308,6 +310,26 @@ export class SolanaSwapProgram
|
|
|
308
310
|
return {type: SwapCommitStateType.NOT_COMMITED};
|
|
309
311
|
}
|
|
310
312
|
|
|
313
|
+
async getCommitStatuses(request: { signer: string; swapData: SolanaSwapData }[]): Promise<{
|
|
314
|
+
[p: string]: SwapCommitState
|
|
315
|
+
}> {
|
|
316
|
+
const result: {
|
|
317
|
+
[p: string]: SwapCommitState
|
|
318
|
+
} = {};
|
|
319
|
+
let promises: Promise<void>[] = [];
|
|
320
|
+
for(let {signer, swapData} of request) {
|
|
321
|
+
promises.push(this.getCommitStatus(signer, swapData).then(val => {
|
|
322
|
+
result[swapData.getEscrowHash()] = val;
|
|
323
|
+
}));
|
|
324
|
+
if(promises.length>=MAX_PARALLEL_COMMIT_STATUS_CHECKS) {
|
|
325
|
+
await Promise.all(promises);
|
|
326
|
+
promises = [];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
await Promise.all(promises);
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
|
|
311
333
|
/**
|
|
312
334
|
* Checks the status of the specific payment hash
|
|
313
335
|
*
|
|
@@ -662,28 +684,28 @@ export class SolanaSwapProgram
|
|
|
662
684
|
/**
|
|
663
685
|
* Get the estimated solana fee of the commit transaction
|
|
664
686
|
*/
|
|
665
|
-
getCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
687
|
+
getCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
666
688
|
return this.Init.getInitFee(swapData, feeRate);
|
|
667
689
|
}
|
|
668
690
|
|
|
669
691
|
/**
|
|
670
692
|
* Get the estimated solana fee of the commit transaction, without any deposits
|
|
671
693
|
*/
|
|
672
|
-
getRawCommitFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
694
|
+
getRawCommitFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
673
695
|
return this.Init.getRawInitFee(swapData, feeRate);
|
|
674
696
|
}
|
|
675
697
|
|
|
676
698
|
/**
|
|
677
699
|
* Get the estimated solana transaction fee of the refund transaction
|
|
678
700
|
*/
|
|
679
|
-
getRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
701
|
+
getRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
680
702
|
return this.Refund.getRefundFee(swapData, feeRate);
|
|
681
703
|
}
|
|
682
704
|
|
|
683
705
|
/**
|
|
684
706
|
* Get the estimated solana transaction fee of the refund transaction
|
|
685
707
|
*/
|
|
686
|
-
getRawRefundFee(swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
708
|
+
getRawRefundFee(signer: string, swapData: SolanaSwapData, feeRate?: string): Promise<bigint> {
|
|
687
709
|
return this.Refund.getRawRefundFee(swapData, feeRate);
|
|
688
710
|
}
|
|
689
711
|
|