@waiaas/adapter-solana 2.5.0 → 2.6.0-rc.1
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/incoming-tx-parser.d.ts +80 -0
- package/dist/incoming-tx-parser.d.ts.map +1 -0
- package/dist/incoming-tx-parser.js +138 -0
- package/dist/incoming-tx-parser.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/solana-incoming-subscriber.d.ts +91 -0
- package/dist/solana-incoming-subscriber.d.ts.map +1 -0
- package/dist/solana-incoming-subscriber.js +271 -0
- package/dist/solana-incoming-subscriber.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsing functions for detecting incoming SOL and SPL/Token-2022 transfers.
|
|
3
|
+
*
|
|
4
|
+
* Operates on plain objects (the shape returned by getTransaction jsonParsed).
|
|
5
|
+
* No @solana/kit imports -- pure logic for testability.
|
|
6
|
+
*
|
|
7
|
+
* References:
|
|
8
|
+
* Design doc 76 sections 3.2.1 (SOL native), 3.2.2 (SPL/Token-2022)
|
|
9
|
+
*/
|
|
10
|
+
import type { IncomingTransaction } from '@waiaas/core';
|
|
11
|
+
/** Account key entry from a jsonParsed transaction. */
|
|
12
|
+
export interface SolanaAccountKey {
|
|
13
|
+
pubkey: string;
|
|
14
|
+
signer: boolean;
|
|
15
|
+
writable: boolean;
|
|
16
|
+
source?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Token balance entry from preTokenBalances / postTokenBalances. */
|
|
19
|
+
export interface SolanaTokenBalance {
|
|
20
|
+
accountIndex: number;
|
|
21
|
+
mint: string;
|
|
22
|
+
owner: string;
|
|
23
|
+
uiTokenAmount: {
|
|
24
|
+
amount: string;
|
|
25
|
+
decimals: number;
|
|
26
|
+
uiAmount: number | null;
|
|
27
|
+
uiAmountString: string;
|
|
28
|
+
};
|
|
29
|
+
programId?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Transaction result shape from getTransaction jsonParsed. */
|
|
32
|
+
export interface SolanaTransactionResult {
|
|
33
|
+
slot: number;
|
|
34
|
+
transaction: {
|
|
35
|
+
signatures: string[];
|
|
36
|
+
message: {
|
|
37
|
+
accountKeys: SolanaAccountKey[];
|
|
38
|
+
instructions: any[];
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
meta: {
|
|
43
|
+
err: unknown | null;
|
|
44
|
+
preBalances: number[];
|
|
45
|
+
postBalances: number[];
|
|
46
|
+
preTokenBalances: SolanaTokenBalance[];
|
|
47
|
+
postTokenBalances: SolanaTokenBalance[];
|
|
48
|
+
[key: string]: any;
|
|
49
|
+
} | null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Detect an incoming SOL native transfer by comparing preBalances/postBalances.
|
|
53
|
+
*
|
|
54
|
+
* Returns an IncomingTransaction if the wallet's balance increased (positive delta),
|
|
55
|
+
* or null if not an incoming transfer.
|
|
56
|
+
*
|
|
57
|
+
* @param tx - Transaction result from getTransaction (jsonParsed)
|
|
58
|
+
* @param walletAddress - The wallet address to check for incoming transfers
|
|
59
|
+
* @param walletId - Wallet ID for the IncomingTransaction record
|
|
60
|
+
* @param network - Network identifier (e.g. 'devnet')
|
|
61
|
+
* @param generateId - ID generator function (injected for testability)
|
|
62
|
+
*/
|
|
63
|
+
export declare function parseSOLTransfer(tx: SolanaTransactionResult, walletAddress: string, walletId: string, network: string, generateId: () => string): IncomingTransaction | null;
|
|
64
|
+
/**
|
|
65
|
+
* Detect incoming SPL Token and Token-2022 transfers by comparing
|
|
66
|
+
* preTokenBalances/postTokenBalances.
|
|
67
|
+
*
|
|
68
|
+
* Returns an array of IncomingTransaction objects (one per token with positive delta).
|
|
69
|
+
* Handles first-time token receipt (no preTokenBalance entry) by defaulting to 0n.
|
|
70
|
+
* Both SPL Token and Token-2022 produce identical preTokenBalances/postTokenBalances
|
|
71
|
+
* structures -- no program-specific logic needed.
|
|
72
|
+
*
|
|
73
|
+
* @param tx - Transaction result from getTransaction (jsonParsed)
|
|
74
|
+
* @param walletAddress - The wallet address to check for incoming transfers
|
|
75
|
+
* @param walletId - Wallet ID for the IncomingTransaction record
|
|
76
|
+
* @param network - Network identifier (e.g. 'devnet')
|
|
77
|
+
* @param generateId - ID generator function (injected for testability)
|
|
78
|
+
*/
|
|
79
|
+
export declare function parseSPLTransfers(tx: SolanaTransactionResult, walletAddress: string, walletId: string, network: string, generateId: () => string): IncomingTransaction[];
|
|
80
|
+
//# sourceMappingURL=incoming-tx-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incoming-tx-parser.d.ts","sourceRoot":"","sources":["../src/incoming-tx-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxD,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE;QACb,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+DAA+D;AAC/D,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,EAAE;YACP,WAAW,EAAE,gBAAgB,EAAE,CAAC;YAEhC,YAAY,EAAE,GAAG,EAAE,CAAC;YAEpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;SACpB,CAAC;KACH,CAAC;IACF,IAAI,EAAE;QACJ,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;QACpB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;QACvC,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;QAExC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,GAAG,IAAI,CAAC;CACV;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,uBAAuB,EAC3B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,MAAM,GACvB,mBAAmB,GAAG,IAAI,CAwC5B;AAiCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,uBAAuB,EAC3B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,MAAM,GACvB,mBAAmB,EAAE,CA0CvB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parsing functions for detecting incoming SOL and SPL/Token-2022 transfers.
|
|
3
|
+
*
|
|
4
|
+
* Operates on plain objects (the shape returned by getTransaction jsonParsed).
|
|
5
|
+
* No @solana/kit imports -- pure logic for testability.
|
|
6
|
+
*
|
|
7
|
+
* References:
|
|
8
|
+
* Design doc 76 sections 3.2.1 (SOL native), 3.2.2 (SPL/Token-2022)
|
|
9
|
+
*/
|
|
10
|
+
// ─── SOL Native Transfer Detection ─────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Detect an incoming SOL native transfer by comparing preBalances/postBalances.
|
|
13
|
+
*
|
|
14
|
+
* Returns an IncomingTransaction if the wallet's balance increased (positive delta),
|
|
15
|
+
* or null if not an incoming transfer.
|
|
16
|
+
*
|
|
17
|
+
* @param tx - Transaction result from getTransaction (jsonParsed)
|
|
18
|
+
* @param walletAddress - The wallet address to check for incoming transfers
|
|
19
|
+
* @param walletId - Wallet ID for the IncomingTransaction record
|
|
20
|
+
* @param network - Network identifier (e.g. 'devnet')
|
|
21
|
+
* @param generateId - ID generator function (injected for testability)
|
|
22
|
+
*/
|
|
23
|
+
export function parseSOLTransfer(tx, walletAddress, walletId, network, generateId) {
|
|
24
|
+
const { meta, transaction } = tx;
|
|
25
|
+
if (!meta || meta.err !== null)
|
|
26
|
+
return null;
|
|
27
|
+
const accountKeys = transaction.message.accountKeys;
|
|
28
|
+
const walletIndex = accountKeys.findIndex((key) => key.pubkey === walletAddress);
|
|
29
|
+
if (walletIndex === -1)
|
|
30
|
+
return null;
|
|
31
|
+
const preBalance = BigInt(meta.preBalances[walletIndex]);
|
|
32
|
+
const postBalance = BigInt(meta.postBalances[walletIndex]);
|
|
33
|
+
const delta = postBalance - preBalance;
|
|
34
|
+
if (delta <= 0n)
|
|
35
|
+
return null;
|
|
36
|
+
// Find sender: first account with decreased balance (excluding wallet)
|
|
37
|
+
let fromAddress = 'unknown';
|
|
38
|
+
for (let i = 0; i < meta.preBalances.length; i++) {
|
|
39
|
+
if (i === walletIndex)
|
|
40
|
+
continue;
|
|
41
|
+
if (BigInt(meta.preBalances[i]) > BigInt(meta.postBalances[i])) {
|
|
42
|
+
fromAddress = accountKeys[i].pubkey;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
id: generateId(),
|
|
48
|
+
txHash: transaction.signatures[0],
|
|
49
|
+
walletId,
|
|
50
|
+
fromAddress,
|
|
51
|
+
amount: delta.toString(),
|
|
52
|
+
tokenAddress: null,
|
|
53
|
+
chain: 'solana',
|
|
54
|
+
network,
|
|
55
|
+
status: 'DETECTED',
|
|
56
|
+
blockNumber: tx.slot,
|
|
57
|
+
detectedAt: Math.floor(Date.now() / 1000),
|
|
58
|
+
confirmedAt: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// ─── SPL / Token-2022 Transfer Detection ────────────────────────
|
|
62
|
+
/**
|
|
63
|
+
* Find the sender of a token transfer for a specific mint.
|
|
64
|
+
*
|
|
65
|
+
* Looks in preTokenBalances for accounts (different owner) with decreased balance
|
|
66
|
+
* for the same mint.
|
|
67
|
+
*/
|
|
68
|
+
function findTokenSender(meta, mint, walletAddress) {
|
|
69
|
+
for (const tb of meta.preTokenBalances) {
|
|
70
|
+
if (tb.mint !== mint)
|
|
71
|
+
continue;
|
|
72
|
+
if (tb.owner === walletAddress)
|
|
73
|
+
continue;
|
|
74
|
+
// Check if this owner's balance decreased for this mint
|
|
75
|
+
const postEntry = meta.postTokenBalances.find((ptb) => ptb.mint === mint && ptb.owner === tb.owner);
|
|
76
|
+
const preAmount = BigInt(tb.uiTokenAmount.amount);
|
|
77
|
+
const postAmount = postEntry ? BigInt(postEntry.uiTokenAmount.amount) : 0n;
|
|
78
|
+
if (preAmount > postAmount) {
|
|
79
|
+
return tb.owner;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return 'unknown';
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Detect incoming SPL Token and Token-2022 transfers by comparing
|
|
86
|
+
* preTokenBalances/postTokenBalances.
|
|
87
|
+
*
|
|
88
|
+
* Returns an array of IncomingTransaction objects (one per token with positive delta).
|
|
89
|
+
* Handles first-time token receipt (no preTokenBalance entry) by defaulting to 0n.
|
|
90
|
+
* Both SPL Token and Token-2022 produce identical preTokenBalances/postTokenBalances
|
|
91
|
+
* structures -- no program-specific logic needed.
|
|
92
|
+
*
|
|
93
|
+
* @param tx - Transaction result from getTransaction (jsonParsed)
|
|
94
|
+
* @param walletAddress - The wallet address to check for incoming transfers
|
|
95
|
+
* @param walletId - Wallet ID for the IncomingTransaction record
|
|
96
|
+
* @param network - Network identifier (e.g. 'devnet')
|
|
97
|
+
* @param generateId - ID generator function (injected for testability)
|
|
98
|
+
*/
|
|
99
|
+
export function parseSPLTransfers(tx, walletAddress, walletId, network, generateId) {
|
|
100
|
+
const { meta } = tx;
|
|
101
|
+
if (!meta || meta.err !== null)
|
|
102
|
+
return [];
|
|
103
|
+
const results = [];
|
|
104
|
+
// Build pre-state map: mint -> amount for wallet's token balances
|
|
105
|
+
const preMap = new Map();
|
|
106
|
+
for (const tb of meta.preTokenBalances) {
|
|
107
|
+
if (tb.owner === walletAddress) {
|
|
108
|
+
preMap.set(tb.mint, BigInt(tb.uiTokenAmount.amount));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Compare post-state: positive delta = incoming token transfer
|
|
112
|
+
for (const tb of meta.postTokenBalances) {
|
|
113
|
+
if (tb.owner !== walletAddress)
|
|
114
|
+
continue;
|
|
115
|
+
const preAmount = preMap.get(tb.mint) ?? 0n; // 0n for first-time receipt (Pitfall 1)
|
|
116
|
+
const postAmount = BigInt(tb.uiTokenAmount.amount);
|
|
117
|
+
const delta = postAmount - preAmount;
|
|
118
|
+
if (delta <= 0n)
|
|
119
|
+
continue;
|
|
120
|
+
const fromAddress = findTokenSender(meta, tb.mint, walletAddress);
|
|
121
|
+
results.push({
|
|
122
|
+
id: generateId(),
|
|
123
|
+
txHash: tx.transaction.signatures[0],
|
|
124
|
+
walletId,
|
|
125
|
+
fromAddress,
|
|
126
|
+
amount: delta.toString(),
|
|
127
|
+
tokenAddress: tb.mint,
|
|
128
|
+
chain: 'solana',
|
|
129
|
+
network,
|
|
130
|
+
status: 'DETECTED',
|
|
131
|
+
blockNumber: tx.slot,
|
|
132
|
+
detectedAt: Math.floor(Date.now() / 1000),
|
|
133
|
+
confirmedAt: null,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=incoming-tx-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incoming-tx-parser.js","sourceRoot":"","sources":["../src/incoming-tx-parser.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export { SolanaAdapter } from './adapter.js';
|
|
2
|
+
export { SolanaIncomingSubscriber, SolanaHeartbeat } from './solana-incoming-subscriber.js';
|
|
3
|
+
export { parseSOLTransfer, parseSPLTransfers } from './incoming-tx-parser.js';
|
|
4
|
+
export type { SolanaIncomingSubscriberConfig } from './solana-incoming-subscriber.js';
|
|
5
|
+
export type { SolanaTransactionResult } from './incoming-tx-parser.js';
|
|
2
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// @waiaas/adapter-solana
|
|
2
2
|
export { SolanaAdapter } from './adapter.js';
|
|
3
|
+
export { SolanaIncomingSubscriber, SolanaHeartbeat } from './solana-incoming-subscriber.js';
|
|
4
|
+
export { parseSOLTransfer, parseSPLTransfers } from './incoming-tx-parser.js';
|
|
3
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SolanaIncomingSubscriber -- IChainSubscriber implementation for Solana.
|
|
3
|
+
*
|
|
4
|
+
* Detects incoming SOL, SPL, and Token-2022 transfers via:
|
|
5
|
+
* - WebSocket: logsSubscribe({ mentions: [address] }) with getTransaction follow-up
|
|
6
|
+
* - HTTP polling: getSignaturesForAddress + getTransaction (fallback / BackgroundWorkers)
|
|
7
|
+
*
|
|
8
|
+
* SolanaHeartbeat sends getSlot() RPC ping every 60s to prevent provider inactivity timeout.
|
|
9
|
+
*
|
|
10
|
+
* References:
|
|
11
|
+
* Design doc 76 sections 3.1-3.7 (Solana subscriber), 5.3 (heartbeat)
|
|
12
|
+
* IChainSubscriber 6-method interface from @waiaas/core
|
|
13
|
+
*/
|
|
14
|
+
import type { IChainSubscriber, ChainType, IncomingTransaction } from '@waiaas/core';
|
|
15
|
+
export interface SolanaIncomingSubscriberConfig {
|
|
16
|
+
rpcUrl: string;
|
|
17
|
+
wsUrl: string;
|
|
18
|
+
mode?: 'websocket' | 'polling';
|
|
19
|
+
generateId?: () => string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Sends periodic getSlot() RPC pings to prevent WebSocket inactivity timeout.
|
|
23
|
+
* Uses HTTP RPC (not WebSocket ping frames) for provider compatibility.
|
|
24
|
+
* timer.unref() prevents blocking process exit.
|
|
25
|
+
*/
|
|
26
|
+
export declare class SolanaHeartbeat {
|
|
27
|
+
private timer;
|
|
28
|
+
private readonly INTERVAL_MS;
|
|
29
|
+
/**
|
|
30
|
+
* Start the heartbeat with a getSlot function.
|
|
31
|
+
* Replaces any existing timer (prevents double intervals).
|
|
32
|
+
*
|
|
33
|
+
* @param rpcGetSlot - Function that sends a getSlot RPC call
|
|
34
|
+
*/
|
|
35
|
+
start(rpcGetSlot: () => Promise<unknown>): void;
|
|
36
|
+
/** Stop the heartbeat timer. */
|
|
37
|
+
stop(): void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Solana chain subscriber implementing IChainSubscriber.
|
|
41
|
+
*
|
|
42
|
+
* Manages per-wallet WebSocket subscriptions via logsNotifications({ mentions: [addr] })
|
|
43
|
+
* and provides a pollAll() method for HTTP-based fallback polling.
|
|
44
|
+
*/
|
|
45
|
+
export declare class SolanaIncomingSubscriber implements IChainSubscriber {
|
|
46
|
+
readonly chain: ChainType;
|
|
47
|
+
private readonly mode;
|
|
48
|
+
private readonly generateId;
|
|
49
|
+
private readonly rpc;
|
|
50
|
+
private readonly rpcSubscriptions;
|
|
51
|
+
private readonly heartbeat;
|
|
52
|
+
private readonly subscriptions;
|
|
53
|
+
private connected;
|
|
54
|
+
private disconnectResolve;
|
|
55
|
+
constructor(config: SolanaIncomingSubscriberConfig);
|
|
56
|
+
subscribe(walletId: string, addr: string, network: string, onTransaction: (tx: IncomingTransaction) => void): Promise<void>;
|
|
57
|
+
unsubscribe(walletId: string): Promise<void>;
|
|
58
|
+
subscribedWallets(): string[];
|
|
59
|
+
connect(): Promise<void>;
|
|
60
|
+
waitForDisconnect(): Promise<void>;
|
|
61
|
+
destroy(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Checks if a Solana transaction has reached 'finalized' commitment.
|
|
64
|
+
* Returns true if the transaction exists at finalized level, false otherwise.
|
|
65
|
+
* Used by the confirmation worker to transition DETECTED → CONFIRMED.
|
|
66
|
+
*/
|
|
67
|
+
checkFinalized(txHash: string): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Poll all subscribed wallets for recent incoming transfers via HTTP RPC.
|
|
70
|
+
* For each wallet: getSignaturesForAddress -> getTransaction -> parse.
|
|
71
|
+
* Per-wallet errors are isolated (logged and skipped).
|
|
72
|
+
*/
|
|
73
|
+
pollAll(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Poll a single wallet for recent incoming transfers.
|
|
76
|
+
*/
|
|
77
|
+
private pollWallet;
|
|
78
|
+
/**
|
|
79
|
+
* Start a fire-and-forget WebSocket subscription for a wallet.
|
|
80
|
+
*/
|
|
81
|
+
private startWebSocketSubscription;
|
|
82
|
+
/**
|
|
83
|
+
* Process a transaction result: parse SOL and SPL transfers and call onTransaction.
|
|
84
|
+
*/
|
|
85
|
+
private processTransaction;
|
|
86
|
+
/**
|
|
87
|
+
* Find walletId by address from subscriptions Map.
|
|
88
|
+
*/
|
|
89
|
+
private findWalletIdByAddress;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=solana-incoming-subscriber.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-incoming-subscriber.d.ts","sourceRoot":"","sources":["../src/solana-incoming-subscriber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgBrF,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;CAC3B;AAID;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IAEtC;;;;;OAKG;IACH,KAAK,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI;IAY/C,gCAAgC;IAChC,IAAI,IAAI,IAAI;CAMb;AAID;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAC/D,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAY;IAErC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0B;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyB;IAC1D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IAEtE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,iBAAiB,CAA6B;gBAE1C,MAAM,EAAE,8BAA8B;IAS5C,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,CAAC,EAAE,EAAE,mBAAmB,KAAK,IAAI,GAC/C,OAAO,CAAC,IAAI,CAAC;IAiBV,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD,iBAAiB,IAAI,MAAM,EAAE;IAMvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBxB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;;;OAIG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBtD;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B;;OAEG;YACW,UAAU;IAiCxB;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAuDlC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA8B1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAM9B"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SolanaIncomingSubscriber -- IChainSubscriber implementation for Solana.
|
|
3
|
+
*
|
|
4
|
+
* Detects incoming SOL, SPL, and Token-2022 transfers via:
|
|
5
|
+
* - WebSocket: logsSubscribe({ mentions: [address] }) with getTransaction follow-up
|
|
6
|
+
* - HTTP polling: getSignaturesForAddress + getTransaction (fallback / BackgroundWorkers)
|
|
7
|
+
*
|
|
8
|
+
* SolanaHeartbeat sends getSlot() RPC ping every 60s to prevent provider inactivity timeout.
|
|
9
|
+
*
|
|
10
|
+
* References:
|
|
11
|
+
* Design doc 76 sections 3.1-3.7 (Solana subscriber), 5.3 (heartbeat)
|
|
12
|
+
* IChainSubscriber 6-method interface from @waiaas/core
|
|
13
|
+
*/
|
|
14
|
+
import { createSolanaRpc, createSolanaRpcSubscriptions, address, } from '@solana/kit';
|
|
15
|
+
import { parseSOLTransfer, parseSPLTransfers } from './incoming-tx-parser.js';
|
|
16
|
+
// ─── SolanaHeartbeat ────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Sends periodic getSlot() RPC pings to prevent WebSocket inactivity timeout.
|
|
19
|
+
* Uses HTTP RPC (not WebSocket ping frames) for provider compatibility.
|
|
20
|
+
* timer.unref() prevents blocking process exit.
|
|
21
|
+
*/
|
|
22
|
+
export class SolanaHeartbeat {
|
|
23
|
+
timer = null;
|
|
24
|
+
INTERVAL_MS = 60_000;
|
|
25
|
+
/**
|
|
26
|
+
* Start the heartbeat with a getSlot function.
|
|
27
|
+
* Replaces any existing timer (prevents double intervals).
|
|
28
|
+
*
|
|
29
|
+
* @param rpcGetSlot - Function that sends a getSlot RPC call
|
|
30
|
+
*/
|
|
31
|
+
start(rpcGetSlot) {
|
|
32
|
+
this.stop();
|
|
33
|
+
this.timer = setInterval(async () => {
|
|
34
|
+
try {
|
|
35
|
+
await rpcGetSlot();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Heartbeat failure is non-fatal; reconnectLoop handles real disconnects
|
|
39
|
+
}
|
|
40
|
+
}, this.INTERVAL_MS);
|
|
41
|
+
this.timer.unref();
|
|
42
|
+
}
|
|
43
|
+
/** Stop the heartbeat timer. */
|
|
44
|
+
stop() {
|
|
45
|
+
if (this.timer) {
|
|
46
|
+
clearInterval(this.timer);
|
|
47
|
+
this.timer = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ─── SolanaIncomingSubscriber ───────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Solana chain subscriber implementing IChainSubscriber.
|
|
54
|
+
*
|
|
55
|
+
* Manages per-wallet WebSocket subscriptions via logsNotifications({ mentions: [addr] })
|
|
56
|
+
* and provides a pollAll() method for HTTP-based fallback polling.
|
|
57
|
+
*/
|
|
58
|
+
export class SolanaIncomingSubscriber {
|
|
59
|
+
chain = 'solana';
|
|
60
|
+
mode;
|
|
61
|
+
generateId;
|
|
62
|
+
rpc;
|
|
63
|
+
rpcSubscriptions;
|
|
64
|
+
heartbeat = new SolanaHeartbeat();
|
|
65
|
+
subscriptions = new Map();
|
|
66
|
+
connected = false;
|
|
67
|
+
disconnectResolve = null;
|
|
68
|
+
constructor(config) {
|
|
69
|
+
this.mode = config.mode ?? 'websocket';
|
|
70
|
+
this.generateId = config.generateId ?? (() => crypto.randomUUID());
|
|
71
|
+
this.rpc = createSolanaRpc(config.rpcUrl);
|
|
72
|
+
this.rpcSubscriptions = createSolanaRpcSubscriptions(config.wsUrl);
|
|
73
|
+
}
|
|
74
|
+
// -- Subscription management (2) --
|
|
75
|
+
async subscribe(walletId, addr, network, onTransaction) {
|
|
76
|
+
// Idempotent: re-subscribing same walletId is a no-op
|
|
77
|
+
if (this.subscriptions.has(walletId))
|
|
78
|
+
return;
|
|
79
|
+
const abortController = new AbortController();
|
|
80
|
+
this.subscriptions.set(walletId, {
|
|
81
|
+
address: addr,
|
|
82
|
+
network,
|
|
83
|
+
abortController,
|
|
84
|
+
onTransaction,
|
|
85
|
+
});
|
|
86
|
+
if (this.mode === 'websocket' && this.connected) {
|
|
87
|
+
this.startWebSocketSubscription(walletId, addr, network, onTransaction, abortController);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async unsubscribe(walletId) {
|
|
91
|
+
const sub = this.subscriptions.get(walletId);
|
|
92
|
+
if (!sub)
|
|
93
|
+
return; // idempotent
|
|
94
|
+
sub.abortController.abort();
|
|
95
|
+
this.subscriptions.delete(walletId);
|
|
96
|
+
}
|
|
97
|
+
// -- Query (1) --
|
|
98
|
+
subscribedWallets() {
|
|
99
|
+
return Array.from(this.subscriptions.keys());
|
|
100
|
+
}
|
|
101
|
+
// -- Lifecycle (3) --
|
|
102
|
+
async connect() {
|
|
103
|
+
this.connected = true;
|
|
104
|
+
// Start heartbeat: getSlot() HTTP RPC ping every 60s
|
|
105
|
+
this.heartbeat.start(() => this.rpc.getSlot().send());
|
|
106
|
+
// Start WebSocket subscriptions for all existing wallets
|
|
107
|
+
if (this.mode === 'websocket') {
|
|
108
|
+
for (const [walletId, sub] of this.subscriptions.entries()) {
|
|
109
|
+
this.startWebSocketSubscription(walletId, sub.address, sub.network, sub.onTransaction, sub.abortController);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async waitForDisconnect() {
|
|
114
|
+
return new Promise((resolve) => {
|
|
115
|
+
this.disconnectResolve = resolve;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async destroy() {
|
|
119
|
+
this.heartbeat.stop();
|
|
120
|
+
// Abort all subscription AbortControllers
|
|
121
|
+
for (const sub of this.subscriptions.values()) {
|
|
122
|
+
sub.abortController.abort();
|
|
123
|
+
}
|
|
124
|
+
this.subscriptions.clear();
|
|
125
|
+
this.connected = false;
|
|
126
|
+
this.disconnectResolve?.();
|
|
127
|
+
}
|
|
128
|
+
// -- RPC helpers (used by confirmation worker) --
|
|
129
|
+
/**
|
|
130
|
+
* Checks if a Solana transaction has reached 'finalized' commitment.
|
|
131
|
+
* Returns true if the transaction exists at finalized level, false otherwise.
|
|
132
|
+
* Used by the confirmation worker to transition DETECTED → CONFIRMED.
|
|
133
|
+
*/
|
|
134
|
+
async checkFinalized(txHash) {
|
|
135
|
+
try {
|
|
136
|
+
const tx = await this.rpc
|
|
137
|
+
.getTransaction(txHash, {
|
|
138
|
+
commitment: 'finalized',
|
|
139
|
+
maxSupportedTransactionVersion: 0,
|
|
140
|
+
encoding: 'jsonParsed',
|
|
141
|
+
})
|
|
142
|
+
.send();
|
|
143
|
+
return tx !== null;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// -- Polling (public for BackgroundWorkers in Phase 226) --
|
|
150
|
+
/**
|
|
151
|
+
* Poll all subscribed wallets for recent incoming transfers via HTTP RPC.
|
|
152
|
+
* For each wallet: getSignaturesForAddress -> getTransaction -> parse.
|
|
153
|
+
* Per-wallet errors are isolated (logged and skipped).
|
|
154
|
+
*/
|
|
155
|
+
async pollAll() {
|
|
156
|
+
for (const [, sub] of this.subscriptions.entries()) {
|
|
157
|
+
try {
|
|
158
|
+
await this.pollWallet(sub);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Per-wallet error isolation: log warning, continue to next wallet
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// -- Private methods --
|
|
166
|
+
/**
|
|
167
|
+
* Poll a single wallet for recent incoming transfers.
|
|
168
|
+
*/
|
|
169
|
+
async pollWallet(sub) {
|
|
170
|
+
const signatures = await this.rpc
|
|
171
|
+
.getSignaturesForAddress(address(sub.address), {
|
|
172
|
+
limit: 100,
|
|
173
|
+
commitment: 'confirmed',
|
|
174
|
+
})
|
|
175
|
+
.send();
|
|
176
|
+
for (const sigInfo of signatures) {
|
|
177
|
+
// Skip failed transactions
|
|
178
|
+
if (sigInfo.err !== null)
|
|
179
|
+
continue;
|
|
180
|
+
try {
|
|
181
|
+
const tx = await this.rpc
|
|
182
|
+
.getTransaction(sigInfo.signature, {
|
|
183
|
+
commitment: 'confirmed',
|
|
184
|
+
maxSupportedTransactionVersion: 0,
|
|
185
|
+
encoding: 'jsonParsed',
|
|
186
|
+
})
|
|
187
|
+
.send();
|
|
188
|
+
if (!tx)
|
|
189
|
+
continue;
|
|
190
|
+
this.processTransaction(tx, sub);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Skip individual transaction errors
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Start a fire-and-forget WebSocket subscription for a wallet.
|
|
199
|
+
*/
|
|
200
|
+
startWebSocketSubscription(_walletId, addr, network, onTransaction, abortController) {
|
|
201
|
+
void (async () => {
|
|
202
|
+
try {
|
|
203
|
+
const logNotifications = await this.rpcSubscriptions
|
|
204
|
+
.logsNotifications({ mentions: [address(addr)] }, { commitment: 'confirmed' })
|
|
205
|
+
.subscribe({ abortSignal: abortController.signal });
|
|
206
|
+
for await (const notification of logNotifications) {
|
|
207
|
+
const { signature, err } = notification.value;
|
|
208
|
+
if (err !== null)
|
|
209
|
+
continue;
|
|
210
|
+
try {
|
|
211
|
+
const tx = await this.rpc
|
|
212
|
+
.getTransaction(signature, {
|
|
213
|
+
commitment: 'confirmed',
|
|
214
|
+
maxSupportedTransactionVersion: 0,
|
|
215
|
+
encoding: 'jsonParsed',
|
|
216
|
+
})
|
|
217
|
+
.send();
|
|
218
|
+
if (!tx)
|
|
219
|
+
continue;
|
|
220
|
+
// Find the subscription entry for this wallet to get network info
|
|
221
|
+
const sub = {
|
|
222
|
+
address: addr,
|
|
223
|
+
network,
|
|
224
|
+
abortController,
|
|
225
|
+
onTransaction,
|
|
226
|
+
};
|
|
227
|
+
this.processTransaction(tx, sub);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Skip individual transaction processing errors
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (_error) {
|
|
235
|
+
// If abort was requested, silently exit
|
|
236
|
+
if (abortController.signal.aborted)
|
|
237
|
+
return;
|
|
238
|
+
// Otherwise, signal disconnection
|
|
239
|
+
this.disconnectResolve?.();
|
|
240
|
+
}
|
|
241
|
+
})();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Process a transaction result: parse SOL and SPL transfers and call onTransaction.
|
|
245
|
+
*/
|
|
246
|
+
processTransaction(tx, sub) {
|
|
247
|
+
// Check for SOL native transfer
|
|
248
|
+
const solTransfer = parseSOLTransfer(tx, sub.address,
|
|
249
|
+
// walletId is not stored directly in SubscriptionEntry -- look it up
|
|
250
|
+
this.findWalletIdByAddress(sub.address) ?? '', sub.network, this.generateId);
|
|
251
|
+
if (solTransfer) {
|
|
252
|
+
sub.onTransaction(solTransfer);
|
|
253
|
+
}
|
|
254
|
+
// Check for SPL/Token-2022 transfers
|
|
255
|
+
const splTransfers = parseSPLTransfers(tx, sub.address, this.findWalletIdByAddress(sub.address) ?? '', sub.network, this.generateId);
|
|
256
|
+
for (const transfer of splTransfers) {
|
|
257
|
+
sub.onTransaction(transfer);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Find walletId by address from subscriptions Map.
|
|
262
|
+
*/
|
|
263
|
+
findWalletIdByAddress(addr) {
|
|
264
|
+
for (const [walletId, sub] of this.subscriptions.entries()) {
|
|
265
|
+
if (sub.address === addr)
|
|
266
|
+
return walletId;
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=solana-incoming-subscriber.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-incoming-subscriber.js","sourceRoot":"","sources":["../src/solana-incoming-subscriber.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waiaas/adapter-solana",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0-rc.1",
|
|
4
4
|
"description": "WAIaaS Solana chain adapter - SPL/Token-2022 support",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@solana-program/system": "^0.11.0",
|
|
32
32
|
"@solana-program/token": "^0.10.0",
|
|
33
33
|
"@solana/kit": "^6.0.1",
|
|
34
|
-
"@waiaas/core": "2.
|
|
34
|
+
"@waiaas/core": "2.6.0-rc.1"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -p tsconfig.build.json",
|