@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.
- 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/SolanaEvents.d.ts +45 -2
- package/dist/solana/chain/modules/SolanaEvents.js +167 -2
- package/dist/solana/chain/modules/SolanaTransactions.js +17 -26
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +1 -11
- package/dist/solana/events/SolanaChainEventsBrowser.js +1 -23
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +3 -9
- package/dist/solana/program/modules/SolanaProgramEvents.js +38 -27
- package/dist/solana/swaps/SolanaSwapData.d.ts +13 -1
- package/dist/solana/swaps/SolanaSwapData.js +25 -0
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +26 -4
- package/dist/solana/swaps/SolanaSwapProgram.js +116 -19
- 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/SolanaEvents.ts +199 -5
- package/src/solana/chain/modules/SolanaTransactions.ts +17 -22
- package/src/solana/events/SolanaChainEventsBrowser.ts +4 -51
- package/src/solana/program/modules/SolanaProgramEvents.ts +44 -29
- package/src/solana/swaps/SolanaSwapData.ts +52 -1
- package/src/solana/swaps/SolanaSwapProgram.ts +172 -20
- 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;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SolanaModule } from "../SolanaModule";
|
|
2
|
-
import { ConfirmedSignatureInfo, PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { ConfirmedSignatureInfo, ParsedTransactionWithMeta, PublicKey } from "@solana/web3.js";
|
|
3
3
|
export declare class SolanaEvents extends SolanaModule {
|
|
4
4
|
readonly LOG_FETCH_LIMIT = 500;
|
|
5
|
+
private usingHeliusTFA;
|
|
5
6
|
/**
|
|
6
7
|
* Gets the signatures for a given topicKey public key, if lastProcessedSignature is specified, it fetches only
|
|
7
8
|
* the signatures before this signature
|
|
@@ -12,6 +13,43 @@ export declare class SolanaEvents extends SolanaModule {
|
|
|
12
13
|
* @private
|
|
13
14
|
*/
|
|
14
15
|
private getSignatures;
|
|
16
|
+
/**
|
|
17
|
+
* Implements Helius getTransactionsForAddress RPC API
|
|
18
|
+
*
|
|
19
|
+
* @param account
|
|
20
|
+
* @param options
|
|
21
|
+
* @param commitment
|
|
22
|
+
*/
|
|
23
|
+
getTransactionsForAddress(account: PublicKey, options?: {
|
|
24
|
+
paginationToken?: string;
|
|
25
|
+
filters?: {
|
|
26
|
+
slot?: {
|
|
27
|
+
gte?: number;
|
|
28
|
+
lte?: number;
|
|
29
|
+
gt?: number;
|
|
30
|
+
lt?: number;
|
|
31
|
+
};
|
|
32
|
+
blockTime?: {
|
|
33
|
+
gte?: number;
|
|
34
|
+
lte?: number;
|
|
35
|
+
gt?: number;
|
|
36
|
+
lt?: number;
|
|
37
|
+
eq?: number;
|
|
38
|
+
};
|
|
39
|
+
signature?: {
|
|
40
|
+
gte?: number;
|
|
41
|
+
lte?: number;
|
|
42
|
+
gt?: number;
|
|
43
|
+
lt?: number;
|
|
44
|
+
eq?: number;
|
|
45
|
+
};
|
|
46
|
+
status?: "succeeded" | "failed" | "any";
|
|
47
|
+
};
|
|
48
|
+
}, commitment?: "finalized" | "confirmed" | "processed"): Promise<{
|
|
49
|
+
data: ParsedTransactionWithMeta[];
|
|
50
|
+
paginationToken?: string;
|
|
51
|
+
}>;
|
|
52
|
+
private _findInTxsTFA;
|
|
15
53
|
/**
|
|
16
54
|
* Runs a search backwards in time, processing transaction signatures for a specific topic public key
|
|
17
55
|
*
|
|
@@ -20,6 +58,11 @@ export declare class SolanaEvents extends SolanaModule {
|
|
|
20
58
|
* was found, or null if the search should continue
|
|
21
59
|
* @param abortSignal
|
|
22
60
|
* @param logFetchLimit
|
|
61
|
+
* @param startBlockheight
|
|
23
62
|
*/
|
|
24
|
-
|
|
63
|
+
private _findInSignatures;
|
|
64
|
+
findInSignatures<T>(topicKey: PublicKey, processor: (data: {
|
|
65
|
+
signatures?: ConfirmedSignatureInfo[];
|
|
66
|
+
txs?: ParsedTransactionWithMeta[];
|
|
67
|
+
}) => Promise<T>, abortSignal?: AbortSignal, logFetchLimit?: number, startBlockheight?: number): Promise<T>;
|
|
25
68
|
}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SolanaEvents = void 0;
|
|
4
4
|
const SolanaModule_1 = require("../SolanaModule");
|
|
5
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
6
|
class SolanaEvents extends SolanaModule_1.SolanaModule {
|
|
6
7
|
constructor() {
|
|
7
8
|
super(...arguments);
|
|
8
9
|
this.LOG_FETCH_LIMIT = 500;
|
|
10
|
+
this.usingHeliusTFA = "auto";
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Gets the signatures for a given topicKey public key, if lastProcessedSignature is specified, it fetches only
|
|
@@ -29,6 +31,147 @@ class SolanaEvents extends SolanaModule_1.SolanaModule {
|
|
|
29
31
|
}, "confirmed");
|
|
30
32
|
}
|
|
31
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Implements Helius getTransactionsForAddress RPC API
|
|
36
|
+
*
|
|
37
|
+
* @param account
|
|
38
|
+
* @param options
|
|
39
|
+
* @param commitment
|
|
40
|
+
*/
|
|
41
|
+
async getTransactionsForAddress(account, options, commitment) {
|
|
42
|
+
//Try to use getPriorityFeeEstimate api of Helius
|
|
43
|
+
const response = await this.connection._rpcRequest("getTransactionsForAddress", [
|
|
44
|
+
account.toString(),
|
|
45
|
+
{
|
|
46
|
+
...options,
|
|
47
|
+
transactionDetails: "full",
|
|
48
|
+
sortOrder: "desc",
|
|
49
|
+
limit: 100,
|
|
50
|
+
commitment: commitment ?? "confirmed",
|
|
51
|
+
encoding: "jsonParsed",
|
|
52
|
+
maxSupportedTransactionVersion: 0
|
|
53
|
+
}
|
|
54
|
+
]).catch(e => {
|
|
55
|
+
//Catching not supported errors
|
|
56
|
+
if (e.message != null && (e.message.includes("-32601") || e.message.includes("-32600"))) {
|
|
57
|
+
return {
|
|
58
|
+
error: {
|
|
59
|
+
code: -32601,
|
|
60
|
+
message: e.message
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
throw e;
|
|
65
|
+
});
|
|
66
|
+
if (response.error != null) {
|
|
67
|
+
//Catching not supported errors
|
|
68
|
+
if (response.error.code !== -32601 && response.error.code !== -32600)
|
|
69
|
+
throw new Error(response.error.message);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
data: response.result.data.map(val => {
|
|
74
|
+
return {
|
|
75
|
+
...val,
|
|
76
|
+
meta: val.meta == null ? undefined : {
|
|
77
|
+
//ParsedTransactionMeta
|
|
78
|
+
...val.meta,
|
|
79
|
+
innerInstructions: val.meta.innerInstructions == null ? undefined : val.meta.innerInstructions.map(innerIx => ({
|
|
80
|
+
//ParsedInnerInstruction
|
|
81
|
+
...innerIx,
|
|
82
|
+
instructions: innerIx.instructions.map(ix => {
|
|
83
|
+
if (ix.program != null && ix.programId != null) {
|
|
84
|
+
return {
|
|
85
|
+
//ParsedInstruction
|
|
86
|
+
...ix,
|
|
87
|
+
programId: new web3_js_1.PublicKey(ix.programId)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return {
|
|
92
|
+
//PartiallyDecodedInstruction
|
|
93
|
+
data: ix.data,
|
|
94
|
+
programId: new web3_js_1.PublicKey(ix.programId),
|
|
95
|
+
accounts: ix.accounts.map(pubkey => new web3_js_1.PublicKey(pubkey))
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
})),
|
|
100
|
+
loadedAddresses: val.meta.loadedAddresses == null ? undefined : {
|
|
101
|
+
writable: val.meta.loadedAddresses.writable.map(pubkey => new web3_js_1.PublicKey(pubkey)),
|
|
102
|
+
readonly: val.meta.loadedAddresses.readonly.map(pubkey => new web3_js_1.PublicKey(pubkey)),
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
transaction: {
|
|
106
|
+
//ParsedTransaction
|
|
107
|
+
...val.transaction,
|
|
108
|
+
message: {
|
|
109
|
+
//ParsedMessage
|
|
110
|
+
...val.transaction.message,
|
|
111
|
+
accountKeys: val.transaction.message.accountKeys.map(accountKey => ({
|
|
112
|
+
//ParsedMessageAccount
|
|
113
|
+
...accountKey,
|
|
114
|
+
pubkey: new web3_js_1.PublicKey(accountKey.pubkey)
|
|
115
|
+
})),
|
|
116
|
+
instructions: val.transaction.message.instructions.map(ix => {
|
|
117
|
+
if (ix.program != null && ix.programId != null) {
|
|
118
|
+
return {
|
|
119
|
+
//ParsedInstruction
|
|
120
|
+
...ix,
|
|
121
|
+
programId: new web3_js_1.PublicKey(ix.programId)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
return {
|
|
126
|
+
//PartiallyDecodedInstruction
|
|
127
|
+
data: ix.data,
|
|
128
|
+
programId: new web3_js_1.PublicKey(ix.programId),
|
|
129
|
+
accounts: ix.accounts.map(pubkey => new web3_js_1.PublicKey(pubkey))
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}),
|
|
133
|
+
addressTableLookups: val.transaction.message.addressTableLookups == null ? undefined : val.transaction.message.addressTableLookups.map(addressTableLookup => ({
|
|
134
|
+
//ParsedAddressTableLookup
|
|
135
|
+
...addressTableLookup,
|
|
136
|
+
accountKey: new web3_js_1.PublicKey(addressTableLookup.accountKey)
|
|
137
|
+
}))
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
paginationToken: response.result.paginationToken
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async _findInTxsTFA(topicKey, processor, abortSignal, startBlockheight) {
|
|
146
|
+
let paginationToken;
|
|
147
|
+
let txs = null;
|
|
148
|
+
while (txs == null || txs.length > 0) {
|
|
149
|
+
let filters = startBlockheight != null ? {
|
|
150
|
+
slot: { gte: startBlockheight }
|
|
151
|
+
} : {};
|
|
152
|
+
const tfaResult = await this.getTransactionsForAddress(topicKey, {
|
|
153
|
+
paginationToken,
|
|
154
|
+
filters: {
|
|
155
|
+
...filters,
|
|
156
|
+
status: "succeeded"
|
|
157
|
+
}
|
|
158
|
+
}, "confirmed");
|
|
159
|
+
if (tfaResult == null) {
|
|
160
|
+
//Not supported
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
txs = tfaResult.data;
|
|
164
|
+
paginationToken = tfaResult.paginationToken;
|
|
165
|
+
if (abortSignal != null)
|
|
166
|
+
abortSignal.throwIfAborted();
|
|
167
|
+
const result = await processor({ txs });
|
|
168
|
+
if (result != null)
|
|
169
|
+
return result;
|
|
170
|
+
if (paginationToken == null)
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
32
175
|
/**
|
|
33
176
|
* Runs a search backwards in time, processing transaction signatures for a specific topic public key
|
|
34
177
|
*
|
|
@@ -37,16 +180,24 @@ class SolanaEvents extends SolanaModule_1.SolanaModule {
|
|
|
37
180
|
* was found, or null if the search should continue
|
|
38
181
|
* @param abortSignal
|
|
39
182
|
* @param logFetchLimit
|
|
183
|
+
* @param startBlockheight
|
|
40
184
|
*/
|
|
41
|
-
async
|
|
185
|
+
async _findInSignatures(topicKey, processor, abortSignal, logFetchLimit, startBlockheight) {
|
|
42
186
|
if (logFetchLimit == null || logFetchLimit > this.LOG_FETCH_LIMIT)
|
|
43
187
|
logFetchLimit = this.LOG_FETCH_LIMIT;
|
|
44
188
|
let signatures = null;
|
|
45
189
|
while (signatures == null || signatures.length > 0) {
|
|
46
190
|
signatures = await this.getSignatures(topicKey, logFetchLimit, signatures != null ? signatures[signatures.length - 1].signature : null);
|
|
191
|
+
if (startBlockheight != null) {
|
|
192
|
+
const endIndex = signatures.findIndex(val => val.slot < startBlockheight);
|
|
193
|
+
if (endIndex === 0)
|
|
194
|
+
return null;
|
|
195
|
+
if (endIndex !== -1)
|
|
196
|
+
signatures = signatures.slice(0, endIndex - 1);
|
|
197
|
+
}
|
|
47
198
|
if (abortSignal != null)
|
|
48
199
|
abortSignal.throwIfAborted();
|
|
49
|
-
const result = await processor(signatures);
|
|
200
|
+
const result = await processor({ signatures });
|
|
50
201
|
if (result != null)
|
|
51
202
|
return result;
|
|
52
203
|
if (signatures.length < logFetchLimit)
|
|
@@ -54,5 +205,19 @@ class SolanaEvents extends SolanaModule_1.SolanaModule {
|
|
|
54
205
|
}
|
|
55
206
|
return null;
|
|
56
207
|
}
|
|
208
|
+
async findInSignatures(topicKey, processor, abortSignal, logFetchLimit, startBlockheight) {
|
|
209
|
+
if (this.usingHeliusTFA !== "no") {
|
|
210
|
+
//Attempt to use Helius's gTFA
|
|
211
|
+
const result = await this._findInTxsTFA(topicKey, processor, abortSignal, startBlockheight);
|
|
212
|
+
if (result !== undefined)
|
|
213
|
+
return result;
|
|
214
|
+
//Not supported
|
|
215
|
+
if (this.usingHeliusTFA === "yes")
|
|
216
|
+
throw new Error("Helius gTFA is not supported with current provider!");
|
|
217
|
+
//If set to auto, we can manually set to "no"
|
|
218
|
+
this.usingHeliusTFA = "no";
|
|
219
|
+
}
|
|
220
|
+
return await this._findInSignatures(topicKey, processor, abortSignal, logFetchLimit, startBlockheight);
|
|
221
|
+
}
|
|
57
222
|
}
|
|
58
223
|
exports.SolanaEvents = SolanaEvents;
|
|
@@ -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 +
|
|
@@ -3,7 +3,7 @@ import { SolanaSwapData } from "../swaps/SolanaSwapData";
|
|
|
3
3
|
import { IdlEvents } from "@coral-xyz/anchor";
|
|
4
4
|
import { SolanaSwapProgram } from "../swaps/SolanaSwapProgram";
|
|
5
5
|
import { Connection } from "@solana/web3.js";
|
|
6
|
-
import { InstructionWithAccounts, ProgramEvent
|
|
6
|
+
import { InstructionWithAccounts, ProgramEvent } from "../program/modules/SolanaProgramEvents";
|
|
7
7
|
import { SwapProgram } from "../swaps/programTypes";
|
|
8
8
|
export type EventObject = {
|
|
9
9
|
events: ProgramEvent<SwapProgram>[];
|
|
@@ -11,7 +11,6 @@ export type EventObject = {
|
|
|
11
11
|
blockTime: number;
|
|
12
12
|
signature: string;
|
|
13
13
|
};
|
|
14
|
-
export type InitInstruction = SingleInstructionWithAccounts<SwapProgram["instructions"][2 | 3], SwapProgram>;
|
|
15
14
|
/**
|
|
16
15
|
* Solana on-chain event handler for front-end systems without access to fs, uses pure WS to subscribe, might lose
|
|
17
16
|
* out on some events if the network is unreliable, front-end systems should take this into consideration and not
|
|
@@ -36,15 +35,6 @@ export declare class SolanaChainEventsBrowser implements ChainEvents<SolanaSwapD
|
|
|
36
35
|
* @returns {Promise<InstructionWithAccounts<SwapProgram>[]>} array of parsed instructions
|
|
37
36
|
*/
|
|
38
37
|
private getTransactionInstructions;
|
|
39
|
-
/**
|
|
40
|
-
* Converts initialize instruction data into {SolanaSwapData}
|
|
41
|
-
*
|
|
42
|
-
* @param initIx
|
|
43
|
-
* @param txoHash
|
|
44
|
-
* @private
|
|
45
|
-
* @returns {SolanaSwapData} converted and parsed swap data
|
|
46
|
-
*/
|
|
47
|
-
private instructionToSwapData;
|
|
48
38
|
/**
|
|
49
39
|
* Returns async getter for fetching on-demand initialize event swap data
|
|
50
40
|
*
|
|
@@ -4,8 +4,6 @@ exports.SolanaChainEventsBrowser = void 0;
|
|
|
4
4
|
const base_1 = require("@atomiqlabs/base");
|
|
5
5
|
const SolanaSwapData_1 = require("../swaps/SolanaSwapData");
|
|
6
6
|
const Utils_1 = require("../../utils/Utils");
|
|
7
|
-
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
-
const BN = require("bn.js");
|
|
9
7
|
const SwapTypeEnum_1 = require("../swaps/SwapTypeEnum");
|
|
10
8
|
const buffer_1 = require("buffer");
|
|
11
9
|
/**
|
|
@@ -43,26 +41,6 @@ class SolanaChainEventsBrowser {
|
|
|
43
41
|
return null;
|
|
44
42
|
return this.solanaSwapProgram.Events.decodeInstructions(transaction.transaction.message);
|
|
45
43
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Converts initialize instruction data into {SolanaSwapData}
|
|
48
|
-
*
|
|
49
|
-
* @param initIx
|
|
50
|
-
* @param txoHash
|
|
51
|
-
* @private
|
|
52
|
-
* @returns {SolanaSwapData} converted and parsed swap data
|
|
53
|
-
*/
|
|
54
|
-
instructionToSwapData(initIx, txoHash) {
|
|
55
|
-
const paymentHash = buffer_1.Buffer.from(initIx.data.swapData.hash);
|
|
56
|
-
let securityDeposit = new BN(0);
|
|
57
|
-
let claimerBounty = new BN(0);
|
|
58
|
-
let payIn = true;
|
|
59
|
-
if (initIx.name === "offererInitialize") {
|
|
60
|
-
payIn = false;
|
|
61
|
-
securityDeposit = initIx.data.securityDeposit;
|
|
62
|
-
claimerBounty = initIx.data.claimerBounty;
|
|
63
|
-
}
|
|
64
|
-
return new SolanaSwapData_1.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);
|
|
65
|
-
}
|
|
66
44
|
/**
|
|
67
45
|
* Returns async getter for fetching on-demand initialize event swap data
|
|
68
46
|
*
|
|
@@ -80,7 +58,7 @@ class SolanaChainEventsBrowser {
|
|
|
80
58
|
const initIx = eventObject.instructions.find(ix => ix != null && (ix.name === "offererInitializePayIn" || ix.name === "offererInitialize"));
|
|
81
59
|
if (initIx == null)
|
|
82
60
|
return null;
|
|
83
|
-
return
|
|
61
|
+
return SolanaSwapData_1.SolanaSwapData.fromInstruction(initIx, txoHash);
|
|
84
62
|
};
|
|
85
63
|
}
|
|
86
64
|
parseInitializeEvent(data, eventObject) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SolanaEvents } from "../../chain/modules/SolanaEvents";
|
|
2
2
|
import { DecodeType, Event, Idl, IdlTypes } from "@coral-xyz/anchor";
|
|
3
3
|
import { IdlField, IdlInstruction } from "@coral-xyz/anchor/dist/cjs/idl";
|
|
4
|
-
import {
|
|
4
|
+
import { ParsedMessage, ParsedTransactionWithMeta, PublicKey } from "@solana/web3.js";
|
|
5
5
|
import { SolanaProgramBase } from "../SolanaProgramBase";
|
|
6
6
|
import { SolanaChainInterface } from "../../chain/SolanaChainInterface";
|
|
7
7
|
type DecodedFieldOrNull<D, Defined> = D extends IdlField ? DecodeType<D["type"], Defined> : unknown;
|
|
@@ -25,13 +25,6 @@ export declare class SolanaProgramEvents<IDL extends Idl> extends SolanaEvents {
|
|
|
25
25
|
private readonly program;
|
|
26
26
|
private readonly nameMappedInstructions;
|
|
27
27
|
constructor(chain: SolanaChainInterface, program: SolanaProgramBase<IDL>);
|
|
28
|
-
/**
|
|
29
|
-
* Gets events from specific transaction as specified by signature, events are ordered from newest to oldest
|
|
30
|
-
*
|
|
31
|
-
* @param signature
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
|
-
private getEvents;
|
|
35
28
|
/**
|
|
36
29
|
* Runs a search backwards in time, processing the events for a specific topic public key
|
|
37
30
|
*
|
|
@@ -40,8 +33,9 @@ export declare class SolanaProgramEvents<IDL extends Idl> extends SolanaEvents {
|
|
|
40
33
|
* if the search should continue
|
|
41
34
|
* @param abortSignal
|
|
42
35
|
* @param logBatchSize how many signatures should be fetched in one getSignaturesForAddress call
|
|
36
|
+
* @param startBlockheight
|
|
43
37
|
*/
|
|
44
|
-
findInEvents<T>(topicKey: PublicKey, processor: (event: ProgramEvent<IDL>,
|
|
38
|
+
findInEvents<T>(topicKey: PublicKey, processor: (event: ProgramEvent<IDL>, tx: ParsedTransactionWithMeta) => Promise<T>, abortSignal?: AbortSignal, logBatchSize?: number, startBlockheight?: number): Promise<T>;
|
|
45
39
|
/**
|
|
46
40
|
* Decodes the instructions for this program from the transaction, leaves null in the returned instructions array
|
|
47
41
|
* for every instruction that doesn't correspond to this program (as those are impossible to parse)
|
|
@@ -14,23 +14,6 @@ class SolanaProgramEvents extends SolanaEvents_1.SolanaEvents {
|
|
|
14
14
|
this.nameMappedInstructions[ix.name] = ix;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Gets events from specific transaction as specified by signature, events are ordered from newest to oldest
|
|
19
|
-
*
|
|
20
|
-
* @param signature
|
|
21
|
-
* @private
|
|
22
|
-
*/
|
|
23
|
-
async getEvents(signature) {
|
|
24
|
-
const tx = await this.connection.getTransaction(signature, {
|
|
25
|
-
commitment: "confirmed",
|
|
26
|
-
maxSupportedTransactionVersion: 0
|
|
27
|
-
});
|
|
28
|
-
if (tx.meta.err)
|
|
29
|
-
return [];
|
|
30
|
-
const events = this.parseLogs(tx.meta.logMessages);
|
|
31
|
-
events.reverse();
|
|
32
|
-
return events;
|
|
33
|
-
}
|
|
34
17
|
/**
|
|
35
18
|
* Runs a search backwards in time, processing the events for a specific topic public key
|
|
36
19
|
*
|
|
@@ -39,19 +22,47 @@ class SolanaProgramEvents extends SolanaEvents_1.SolanaEvents {
|
|
|
39
22
|
* if the search should continue
|
|
40
23
|
* @param abortSignal
|
|
41
24
|
* @param logBatchSize how many signatures should be fetched in one getSignaturesForAddress call
|
|
25
|
+
* @param startBlockheight
|
|
42
26
|
*/
|
|
43
|
-
findInEvents(topicKey, processor, abortSignal, logBatchSize) {
|
|
44
|
-
return this.findInSignatures(topicKey, async (
|
|
45
|
-
|
|
46
|
-
for (let
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
27
|
+
findInEvents(topicKey, processor, abortSignal, logBatchSize, startBlockheight) {
|
|
28
|
+
return this.findInSignatures(topicKey, async (data) => {
|
|
29
|
+
if (data.signatures) {
|
|
30
|
+
for (let info of data.signatures) {
|
|
31
|
+
if (info.err == null)
|
|
32
|
+
continue;
|
|
33
|
+
const tx = await this.connection.getParsedTransaction(info.signature, {
|
|
34
|
+
commitment: "confirmed",
|
|
35
|
+
maxSupportedTransactionVersion: 0
|
|
36
|
+
});
|
|
37
|
+
if (tx.meta.err)
|
|
38
|
+
continue;
|
|
39
|
+
const events = this.parseLogs(tx.meta.logMessages);
|
|
40
|
+
events.reverse();
|
|
41
|
+
for (let event of events) {
|
|
42
|
+
if (abortSignal != null)
|
|
43
|
+
abortSignal.throwIfAborted();
|
|
44
|
+
const result = await processor(event, tx);
|
|
45
|
+
if (result != null)
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
for (let tx of data.txs) {
|
|
52
|
+
if (tx.meta.err)
|
|
53
|
+
continue;
|
|
54
|
+
const events = this.parseLogs(tx.meta.logMessages);
|
|
55
|
+
events.reverse();
|
|
56
|
+
for (let event of events) {
|
|
57
|
+
if (abortSignal != null)
|
|
58
|
+
abortSignal.throwIfAborted();
|
|
59
|
+
const result = await processor(event, tx);
|
|
60
|
+
if (result != null)
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
|
-
}, abortSignal, logBatchSize);
|
|
65
|
+
}, abortSignal, logBatchSize, startBlockheight);
|
|
55
66
|
}
|
|
56
67
|
/**
|
|
57
68
|
* Decodes the instructions for this program from the transaction, leaves null in the returned instructions array
|