@atomiqlabs/chain-solana 13.5.13 → 13.5.14
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/LICENSE +201 -201
- package/README.md +73 -73
- package/dist/index.d.ts +81 -81
- package/dist/index.js +102 -102
- package/dist/node/index.d.ts +9 -9
- package/dist/node/index.js +13 -13
- package/dist/solana/SolanaChainType.d.ts +15 -15
- package/dist/solana/SolanaChainType.js +2 -2
- package/dist/solana/SolanaChains.d.ts +12 -12
- package/dist/solana/SolanaChains.js +45 -45
- package/dist/solana/SolanaInitializer.d.ts +94 -94
- package/dist/solana/SolanaInitializer.js +174 -174
- package/dist/solana/btcrelay/SolanaBtcRelay.d.ts +222 -222
- package/dist/solana/btcrelay/SolanaBtcRelay.js +455 -455
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.d.ts +84 -84
- package/dist/solana/btcrelay/headers/SolanaBtcHeader.js +70 -70
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.d.ts +92 -92
- package/dist/solana/btcrelay/headers/SolanaBtcStoredHeader.js +109 -109
- package/dist/solana/btcrelay/program/programIdl.json +671 -671
- package/dist/solana/chain/SolanaAction.d.ts +26 -26
- package/dist/solana/chain/SolanaAction.js +87 -87
- package/dist/solana/chain/SolanaChainInterface.d.ts +224 -224
- package/dist/solana/chain/SolanaChainInterface.js +275 -275
- package/dist/solana/chain/SolanaModule.d.ts +14 -14
- package/dist/solana/chain/SolanaModule.js +13 -13
- package/dist/solana/chain/modules/SolanaAddresses.d.ts +8 -8
- package/dist/solana/chain/modules/SolanaAddresses.js +22 -22
- package/dist/solana/chain/modules/SolanaBlocks.d.ts +32 -32
- package/dist/solana/chain/modules/SolanaBlocks.js +78 -78
- package/dist/solana/chain/modules/SolanaEvents.d.ts +68 -68
- package/dist/solana/chain/modules/SolanaEvents.js +238 -238
- package/dist/solana/chain/modules/SolanaFees.d.ts +189 -189
- package/dist/solana/chain/modules/SolanaFees.js +434 -434
- package/dist/solana/chain/modules/SolanaSignatures.d.ts +24 -24
- package/dist/solana/chain/modules/SolanaSignatures.js +39 -39
- package/dist/solana/chain/modules/SolanaSlots.d.ts +33 -33
- package/dist/solana/chain/modules/SolanaSlots.js +72 -72
- package/dist/solana/chain/modules/SolanaTokens.d.ts +123 -123
- package/dist/solana/chain/modules/SolanaTokens.js +242 -242
- package/dist/solana/chain/modules/SolanaTransactions.d.ts +149 -149
- package/dist/solana/chain/modules/SolanaTransactions.js +445 -445
- package/dist/solana/connection/ConnectionWithRetries.d.ts +35 -35
- package/dist/solana/connection/ConnectionWithRetries.js +86 -71
- package/dist/solana/events/SolanaChainEvents.d.ts +45 -45
- package/dist/solana/events/SolanaChainEvents.js +108 -108
- package/dist/solana/events/SolanaChainEventsBrowser.d.ts +205 -205
- package/dist/solana/events/SolanaChainEventsBrowser.js +404 -404
- package/dist/solana/program/SolanaProgramBase.d.ts +73 -73
- package/dist/solana/program/SolanaProgramBase.js +54 -54
- package/dist/solana/program/SolanaProgramModule.d.ts +8 -8
- package/dist/solana/program/SolanaProgramModule.js +11 -11
- package/dist/solana/program/modules/SolanaProgramEvents.d.ts +53 -53
- package/dist/solana/program/modules/SolanaProgramEvents.js +117 -117
- package/dist/solana/swaps/SolanaSwapData.d.ts +333 -333
- package/dist/solana/swaps/SolanaSwapData.js +535 -535
- package/dist/solana/swaps/SolanaSwapModule.d.ts +11 -11
- package/dist/solana/swaps/SolanaSwapModule.js +12 -12
- package/dist/solana/swaps/SolanaSwapProgram.d.ts +376 -376
- package/dist/solana/swaps/SolanaSwapProgram.js +769 -769
- package/dist/solana/swaps/SwapTypeEnum.d.ts +11 -11
- package/dist/solana/swaps/SwapTypeEnum.js +43 -43
- package/dist/solana/swaps/modules/SolanaDataAccount.d.ts +95 -95
- package/dist/solana/swaps/modules/SolanaDataAccount.js +232 -232
- package/dist/solana/swaps/modules/SolanaLpVault.d.ts +69 -69
- package/dist/solana/swaps/modules/SolanaLpVault.js +171 -171
- package/dist/solana/swaps/modules/SwapClaim.d.ts +126 -126
- package/dist/solana/swaps/modules/SwapClaim.js +294 -294
- package/dist/solana/swaps/modules/SwapInit.d.ts +213 -213
- package/dist/solana/swaps/modules/SwapInit.js +658 -658
- package/dist/solana/swaps/modules/SwapRefund.d.ts +87 -87
- package/dist/solana/swaps/modules/SwapRefund.js +293 -293
- package/dist/solana/swaps/programIdl.json +945 -945
- package/dist/solana/swaps/programTypes.d.ts +943 -943
- package/dist/solana/swaps/programTypes.js +945 -945
- package/dist/solana/swaps/v1/programIdl.json +945 -945
- package/dist/solana/swaps/v1/programTypes.d.ts +943 -943
- package/dist/solana/swaps/v1/programTypes.js +945 -945
- package/dist/solana/swaps/v2/programIdl.json +952 -952
- package/dist/solana/swaps/v2/programTypes.d.ts +950 -950
- package/dist/solana/swaps/v2/programTypes.js +952 -952
- package/dist/solana/wallet/SolanaKeypairWallet.d.ts +29 -29
- package/dist/solana/wallet/SolanaKeypairWallet.js +50 -50
- package/dist/solana/wallet/SolanaSigner.d.ts +30 -30
- package/dist/solana/wallet/SolanaSigner.js +30 -30
- package/dist/utils/Utils.d.ts +58 -58
- package/dist/utils/Utils.js +170 -170
- package/node/index.d.ts +1 -1
- package/node/index.js +3 -3
- package/package.json +46 -46
- package/src/index.ts +87 -87
- package/src/node/index.ts +9 -9
- package/src/solana/SolanaChainType.ts +32 -32
- package/src/solana/SolanaChains.ts +46 -46
- package/src/solana/SolanaInitializer.ts +278 -278
- package/src/solana/btcrelay/SolanaBtcRelay.ts +615 -615
- package/src/solana/btcrelay/headers/SolanaBtcHeader.ts +116 -116
- package/src/solana/btcrelay/headers/SolanaBtcStoredHeader.ts +148 -148
- package/src/solana/btcrelay/program/programIdl.json +670 -670
- package/src/solana/chain/SolanaAction.ts +109 -109
- package/src/solana/chain/SolanaChainInterface.ts +404 -404
- package/src/solana/chain/SolanaModule.ts +20 -20
- package/src/solana/chain/modules/SolanaAddresses.ts +20 -20
- package/src/solana/chain/modules/SolanaBlocks.ts +89 -89
- package/src/solana/chain/modules/SolanaEvents.ts +271 -271
- package/src/solana/chain/modules/SolanaFees.ts +522 -522
- package/src/solana/chain/modules/SolanaSignatures.ts +39 -39
- package/src/solana/chain/modules/SolanaSlots.ts +85 -85
- package/src/solana/chain/modules/SolanaTokens.ts +300 -300
- package/src/solana/chain/modules/SolanaTransactions.ts +503 -503
- package/src/solana/connection/ConnectionWithRetries.ts +113 -96
- package/src/solana/events/SolanaChainEvents.ts +127 -127
- package/src/solana/events/SolanaChainEventsBrowser.ts +495 -495
- package/src/solana/program/SolanaProgramBase.ts +119 -119
- package/src/solana/program/SolanaProgramModule.ts +15 -15
- package/src/solana/program/modules/SolanaProgramEvents.ts +157 -157
- package/src/solana/swaps/SolanaSwapData.ts +735 -735
- package/src/solana/swaps/SolanaSwapModule.ts +19 -19
- package/src/solana/swaps/SolanaSwapProgram.ts +1074 -1074
- package/src/solana/swaps/SwapTypeEnum.ts +30 -30
- package/src/solana/swaps/modules/SolanaDataAccount.ts +302 -302
- package/src/solana/swaps/modules/SolanaLpVault.ts +208 -208
- package/src/solana/swaps/modules/SwapClaim.ts +387 -387
- package/src/solana/swaps/modules/SwapInit.ts +785 -785
- package/src/solana/swaps/modules/SwapRefund.ts +353 -353
- package/src/solana/swaps/v1/programIdl.json +944 -944
- package/src/solana/swaps/v1/programTypes.ts +1885 -1885
- package/src/solana/swaps/v2/programIdl.json +951 -951
- package/src/solana/swaps/v2/programTypes.ts +1899 -1899
- package/src/solana/wallet/SolanaKeypairWallet.ts +56 -56
- package/src/solana/wallet/SolanaSigner.ts +43 -43
- package/src/utils/Utils.ts +194 -194
|
@@ -1,96 +1,113 @@
|
|
|
1
|
-
import {Commitment, Connection, ConnectionConfig} from "@solana/web3.js";
|
|
2
|
-
import {tryWithRetries} from "../../utils/Utils";
|
|
3
|
-
|
|
4
|
-
export type ConnectionWithRetriesConfig = ConnectionConfig & {
|
|
5
|
-
retryPolicy?: {
|
|
6
|
-
maxRetries?: number, delay?: number, exponential?: boolean
|
|
7
|
-
};
|
|
8
|
-
requestTimeout?: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Solana connection with retry logic and request timeout handling for RPC calls.
|
|
13
|
-
*
|
|
14
|
-
* @category Providers
|
|
15
|
-
*/
|
|
16
|
-
export class ConnectionWithRetries extends Connection {
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Retry policy used for RPC requests.
|
|
20
|
-
*/
|
|
21
|
-
readonly retryPolicy?: {
|
|
22
|
-
maxRetries?: number, delay?: number, exponential?: boolean
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Per-request timeout in milliseconds.
|
|
26
|
-
*/
|
|
27
|
-
readonly requestTimeout: number;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Constructs a retry-enabled Solana connection.
|
|
31
|
-
*
|
|
32
|
-
* @param endpoint RPC endpoint URL
|
|
33
|
-
* @param commitmentOrConfig Commitment level or full connection configuration
|
|
34
|
-
*/
|
|
35
|
-
constructor(endpoint: string, commitmentOrConfig?: Commitment | ConnectionWithRetriesConfig) {
|
|
36
|
-
let config: ConnectionWithRetriesConfig;
|
|
37
|
-
if(typeof(commitmentOrConfig)==="string") {
|
|
38
|
-
config = {commitment: commitmentOrConfig};
|
|
39
|
-
} else {
|
|
40
|
-
config = commitmentOrConfig ?? {};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
config.fetch = (input: RequestInfo | URL, init?: RequestInit) => tryWithRetries(
|
|
44
|
-
async () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
1
|
+
import {Commitment, Connection, ConnectionConfig} from "@solana/web3.js";
|
|
2
|
+
import {tryWithRetries} from "../../utils/Utils";
|
|
3
|
+
|
|
4
|
+
export type ConnectionWithRetriesConfig = ConnectionConfig & {
|
|
5
|
+
retryPolicy?: {
|
|
6
|
+
maxRetries?: number, delay?: number, exponential?: boolean
|
|
7
|
+
};
|
|
8
|
+
requestTimeout?: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Solana connection with retry logic and request timeout handling for RPC calls.
|
|
13
|
+
*
|
|
14
|
+
* @category Providers
|
|
15
|
+
*/
|
|
16
|
+
export class ConnectionWithRetries extends Connection {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Retry policy used for RPC requests.
|
|
20
|
+
*/
|
|
21
|
+
readonly retryPolicy?: {
|
|
22
|
+
maxRetries?: number, delay?: number, exponential?: boolean
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Per-request timeout in milliseconds.
|
|
26
|
+
*/
|
|
27
|
+
readonly requestTimeout: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Constructs a retry-enabled Solana connection.
|
|
31
|
+
*
|
|
32
|
+
* @param endpoint RPC endpoint URL
|
|
33
|
+
* @param commitmentOrConfig Commitment level or full connection configuration
|
|
34
|
+
*/
|
|
35
|
+
constructor(endpoint: string, commitmentOrConfig?: Commitment | ConnectionWithRetriesConfig) {
|
|
36
|
+
let config: ConnectionWithRetriesConfig;
|
|
37
|
+
if(typeof(commitmentOrConfig)==="string") {
|
|
38
|
+
config = {commitment: commitmentOrConfig};
|
|
39
|
+
} else {
|
|
40
|
+
config = commitmentOrConfig ?? {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
config.fetch = (input: RequestInfo | URL, init?: RequestInit) => tryWithRetries(
|
|
44
|
+
async () => {
|
|
45
|
+
if(init?.signal?.aborted) {
|
|
46
|
+
throw init.signal.reason instanceof Error
|
|
47
|
+
? init.signal.reason
|
|
48
|
+
: new Error("Aborted");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const abortController = new AbortController();
|
|
52
|
+
const timeoutHandle = setTimeout(() => {
|
|
53
|
+
abortController.abort(new Error('Network request timed out'));
|
|
54
|
+
}, this.requestTimeout);
|
|
55
|
+
let originalSignal: AbortSignal | undefined;
|
|
56
|
+
let originalSignalListener: (() => void) | undefined;
|
|
57
|
+
if (init?.signal != null) {
|
|
58
|
+
originalSignal = init.signal;
|
|
59
|
+
originalSignal.addEventListener('abort', originalSignalListener = () => {
|
|
60
|
+
clearTimeout(timeoutHandle);
|
|
61
|
+
abortController.abort(
|
|
62
|
+
originalSignal?.reason instanceof Error
|
|
63
|
+
? originalSignal?.reason
|
|
64
|
+
: new Error("Aborted")
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const result = await fetch(input, {
|
|
70
|
+
...init,
|
|
71
|
+
signal: abortController.signal
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if(Math.floor(result.status/100)===5) {
|
|
75
|
+
throw new Error(`Internal server error: ${result.status}`);
|
|
76
|
+
}
|
|
77
|
+
if(result.status===429) {
|
|
78
|
+
throw new Error(`Too many requests (429)`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const textResult = await result.text();
|
|
83
|
+
return new Response(textResult, {
|
|
84
|
+
status: result.status,
|
|
85
|
+
statusText: result.statusText,
|
|
86
|
+
headers: result.headers,
|
|
87
|
+
});
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.error(`ConnectionWithRetries: fetch(): Failed to read response body: `, e);
|
|
90
|
+
throw new Error("Error reading response body");
|
|
91
|
+
}
|
|
92
|
+
} finally {
|
|
93
|
+
clearTimeout(timeoutHandle);
|
|
94
|
+
if(originalSignal!=null && originalSignalListener!=null)
|
|
95
|
+
originalSignal.removeEventListener("abort", originalSignalListener);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
this.retryPolicy,
|
|
99
|
+
(e) =>
|
|
100
|
+
!e?.message?.startsWith?.("Internal server error: ") &&
|
|
101
|
+
e?.message!=="Network request timed out" &&
|
|
102
|
+
e?.message!=="Too many requests (429)" &&
|
|
103
|
+
e?.message!=="Error reading response body",
|
|
104
|
+
init?.signal ?? undefined
|
|
105
|
+
);
|
|
106
|
+
config.disableRetryOnRateLimit = true;
|
|
107
|
+
|
|
108
|
+
super(endpoint, config);
|
|
109
|
+
this.retryPolicy = config?.retryPolicy;
|
|
110
|
+
this.requestTimeout = config?.requestTimeout ?? 15*1000;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
import {Connection} from "@solana/web3.js";
|
|
2
|
-
import * as fs from "fs/promises";
|
|
3
|
-
import {SolanaSwapProgram} from "../swaps/SolanaSwapProgram";
|
|
4
|
-
import {
|
|
5
|
-
SolanaChainEventsBrowser,
|
|
6
|
-
SolanaEventListenerState,
|
|
7
|
-
SolanaLegacyEventListenerState
|
|
8
|
-
} from "./SolanaChainEventsBrowser";
|
|
9
|
-
|
|
10
|
-
const BLOCKHEIGHT_FILENAME = "/blockheight.txt";
|
|
11
|
-
const LOG_FETCH_INTERVAL = 5*1000;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Event handler for backend Node.js systems with access to fs, uses HTTP polling in combination with WS to not miss
|
|
15
|
-
* any events
|
|
16
|
-
*/
|
|
17
|
-
export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
18
|
-
|
|
19
|
-
private readonly directory: string;
|
|
20
|
-
private readonly logFetchInterval: number;
|
|
21
|
-
|
|
22
|
-
private stopped: boolean = true;
|
|
23
|
-
private timeout?: NodeJS.Timeout;
|
|
24
|
-
|
|
25
|
-
constructor(
|
|
26
|
-
directory: string,
|
|
27
|
-
connection: Connection,
|
|
28
|
-
contractVersions: SolanaSwapProgram | {[version: string]: {swapContract: SolanaSwapProgram}},
|
|
29
|
-
logFetchInterval?: number
|
|
30
|
-
) {
|
|
31
|
-
super(connection, contractVersions);
|
|
32
|
-
this.directory = directory;
|
|
33
|
-
this.logFetchInterval = logFetchInterval || LOG_FETCH_INTERVAL;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Retrieves last signature & slot from filesystem
|
|
38
|
-
*
|
|
39
|
-
* @private
|
|
40
|
-
*/
|
|
41
|
-
private async getLastSignature(): Promise<SolanaLegacyEventListenerState | SolanaEventListenerState | null> {
|
|
42
|
-
let txt: string;
|
|
43
|
-
try {
|
|
44
|
-
txt = (await fs.readFile(this.directory+BLOCKHEIGHT_FILENAME)).toString();
|
|
45
|
-
} catch (e) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(txt);
|
|
51
|
-
} catch (e) {}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const arr = txt.split(";");
|
|
55
|
-
if(arr.length<2) return {
|
|
56
|
-
signature: txt,
|
|
57
|
-
slot: 0
|
|
58
|
-
};
|
|
59
|
-
return {
|
|
60
|
-
signature: arr[0],
|
|
61
|
-
slot: parseInt(arr[1])
|
|
62
|
-
};
|
|
63
|
-
} catch (e) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Saves last signature & slot to the filesystem
|
|
70
|
-
*
|
|
71
|
-
* @private
|
|
72
|
-
*/
|
|
73
|
-
private saveLastSignature(lastState: SolanaEventListenerState): Promise<void> {
|
|
74
|
-
return fs.writeFile(this.directory+BLOCKHEIGHT_FILENAME, JSON.stringify(lastState));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Polls for new events & processes them
|
|
79
|
-
*
|
|
80
|
-
* @private
|
|
81
|
-
*/
|
|
82
|
-
private async checkEvents() {
|
|
83
|
-
const lastSignature = await this.getLastSignature();
|
|
84
|
-
|
|
85
|
-
const result = await this.poll(lastSignature ?? undefined);
|
|
86
|
-
|
|
87
|
-
if(result!=null) {
|
|
88
|
-
await this.saveLastSignature(result);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private async setupHttpPolling() {
|
|
93
|
-
this.stopped = false;
|
|
94
|
-
let func: () => Promise<void>;
|
|
95
|
-
func = async () => {
|
|
96
|
-
await this.checkEvents().catch(e => {
|
|
97
|
-
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
98
|
-
});
|
|
99
|
-
if(this.stopped) return;
|
|
100
|
-
this.timeout = setTimeout(func, this.logFetchInterval);
|
|
101
|
-
};
|
|
102
|
-
await func();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @inheritDoc
|
|
107
|
-
*/
|
|
108
|
-
async init(noAutomaticPoll?: boolean): Promise<void> {
|
|
109
|
-
if(noAutomaticPoll) return;
|
|
110
|
-
try {
|
|
111
|
-
await fs.mkdir(this.directory);
|
|
112
|
-
} catch (e) {}
|
|
113
|
-
|
|
114
|
-
await this.setupHttpPolling();
|
|
115
|
-
this.setupWebsocket();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @inheritDoc
|
|
120
|
-
*/
|
|
121
|
-
stop(): Promise<void> {
|
|
122
|
-
this.stopped = true;
|
|
123
|
-
if(this.timeout!=null) clearTimeout(this.timeout)
|
|
124
|
-
return super.stop();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
}
|
|
1
|
+
import {Connection} from "@solana/web3.js";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import {SolanaSwapProgram} from "../swaps/SolanaSwapProgram";
|
|
4
|
+
import {
|
|
5
|
+
SolanaChainEventsBrowser,
|
|
6
|
+
SolanaEventListenerState,
|
|
7
|
+
SolanaLegacyEventListenerState
|
|
8
|
+
} from "./SolanaChainEventsBrowser";
|
|
9
|
+
|
|
10
|
+
const BLOCKHEIGHT_FILENAME = "/blockheight.txt";
|
|
11
|
+
const LOG_FETCH_INTERVAL = 5*1000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Event handler for backend Node.js systems with access to fs, uses HTTP polling in combination with WS to not miss
|
|
15
|
+
* any events
|
|
16
|
+
*/
|
|
17
|
+
export class SolanaChainEvents extends SolanaChainEventsBrowser {
|
|
18
|
+
|
|
19
|
+
private readonly directory: string;
|
|
20
|
+
private readonly logFetchInterval: number;
|
|
21
|
+
|
|
22
|
+
private stopped: boolean = true;
|
|
23
|
+
private timeout?: NodeJS.Timeout;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
directory: string,
|
|
27
|
+
connection: Connection,
|
|
28
|
+
contractVersions: SolanaSwapProgram | {[version: string]: {swapContract: SolanaSwapProgram}},
|
|
29
|
+
logFetchInterval?: number
|
|
30
|
+
) {
|
|
31
|
+
super(connection, contractVersions);
|
|
32
|
+
this.directory = directory;
|
|
33
|
+
this.logFetchInterval = logFetchInterval || LOG_FETCH_INTERVAL;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves last signature & slot from filesystem
|
|
38
|
+
*
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
private async getLastSignature(): Promise<SolanaLegacyEventListenerState | SolanaEventListenerState | null> {
|
|
42
|
+
let txt: string;
|
|
43
|
+
try {
|
|
44
|
+
txt = (await fs.readFile(this.directory+BLOCKHEIGHT_FILENAME)).toString();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(txt);
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const arr = txt.split(";");
|
|
55
|
+
if(arr.length<2) return {
|
|
56
|
+
signature: txt,
|
|
57
|
+
slot: 0
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
signature: arr[0],
|
|
61
|
+
slot: parseInt(arr[1])
|
|
62
|
+
};
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Saves last signature & slot to the filesystem
|
|
70
|
+
*
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
private saveLastSignature(lastState: SolanaEventListenerState): Promise<void> {
|
|
74
|
+
return fs.writeFile(this.directory+BLOCKHEIGHT_FILENAME, JSON.stringify(lastState));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Polls for new events & processes them
|
|
79
|
+
*
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
private async checkEvents() {
|
|
83
|
+
const lastSignature = await this.getLastSignature();
|
|
84
|
+
|
|
85
|
+
const result = await this.poll(lastSignature ?? undefined);
|
|
86
|
+
|
|
87
|
+
if(result!=null) {
|
|
88
|
+
await this.saveLastSignature(result);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private async setupHttpPolling() {
|
|
93
|
+
this.stopped = false;
|
|
94
|
+
let func: () => Promise<void>;
|
|
95
|
+
func = async () => {
|
|
96
|
+
await this.checkEvents().catch(e => {
|
|
97
|
+
this.logger.error("setupHttpPolling(): Failed to fetch Solana log: ", e);
|
|
98
|
+
});
|
|
99
|
+
if(this.stopped) return;
|
|
100
|
+
this.timeout = setTimeout(func, this.logFetchInterval);
|
|
101
|
+
};
|
|
102
|
+
await func();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @inheritDoc
|
|
107
|
+
*/
|
|
108
|
+
async init(noAutomaticPoll?: boolean): Promise<void> {
|
|
109
|
+
if(noAutomaticPoll) return;
|
|
110
|
+
try {
|
|
111
|
+
await fs.mkdir(this.directory);
|
|
112
|
+
} catch (e) {}
|
|
113
|
+
|
|
114
|
+
await this.setupHttpPolling();
|
|
115
|
+
this.setupWebsocket();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @inheritDoc
|
|
120
|
+
*/
|
|
121
|
+
stop(): Promise<void> {
|
|
122
|
+
this.stopped = true;
|
|
123
|
+
if(this.timeout!=null) clearTimeout(this.timeout)
|
|
124
|
+
return super.stop();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
}
|