@@ -13,7 +13,7 @@ dotenv.config();
13
13
// Verify environment variables
14
14
const { PORT , LND_MACAROON , LND_SOCKET , RPC_URL , LSP_PRIVATE_KEY , CHAIN_ID } =
15
15
process . env ;
16
- if ( ! RPC_URL || ! LSP_PRIVATE_KEY || ! CHAIN_ID ) {
16
+ if ( ! RPC_URL || ! LSP_PRIVATE_KEY || ! CHAIN_ID ) {
17
17
console . error ( "Missing environment variables" ) ;
18
18
process . exit ( 1 ) ;
19
19
}
@@ -39,7 +39,9 @@ export type CachedPayment = {
39
39
contractId : string ;
40
40
secret : string ;
41
41
} ;
42
+
42
43
let cachedPayments : CachedPayment [ ] = [ ] ;
44
+ let pendingContracts : string [ ] = [ ] ;
43
45
// ideally this should be stored in a database, but for the sake of simplicity we are using an in-memory cache
44
46
45
47
console . log ( `RPC Provider is running on ${ RPC_URL } ` ) ;
@@ -49,17 +51,33 @@ console.log(`LSP Address: ${signer.address}`);
49
51
wss . on ( "connection" , ( ws : WebSocket ) => {
50
52
const serverStatus = process . env . LND_MACAROON ? "ACTIVE" : "MOCK" ;
51
53
console . log ( "Client connected" ) ;
52
- ws . send ( JSON . stringify ( { status : serverStatus , message : "Connected to server" } ) ) ;
54
+ ws . send (
55
+ JSON . stringify ( {
56
+ serverStatus : serverStatus ,
57
+ message : "Connected to server" ,
58
+ } )
59
+ ) ;
53
60
54
61
ws . on ( "message" , async ( message : string ) => {
55
62
console . log ( "Received message:" , message ) ;
63
+ const request : InvoiceRequest = JSON . parse ( message ) ;
64
+ if ( pendingContracts . includes ( request . contractId ) ) {
65
+ ws . send (
66
+ JSON . stringify ( {
67
+ status : "error" ,
68
+ message : "Contract is already being processed." ,
69
+ } )
70
+ ) ;
71
+ return ;
72
+ }
73
+ pendingContracts . push ( request . contractId ) ;
56
74
try {
57
- const request : InvoiceRequest = JSON . parse ( message ) ;
58
75
await processInvoiceRequest ( request , ws ) ;
59
76
} catch ( error ) {
60
77
console . error ( "Error processing message:" , error ) ;
61
78
ws . send ( JSON . stringify ( { status : "error" , message : "Invalid request" } ) ) ;
62
79
}
80
+ pendingContracts = pendingContracts . filter ( ( c ) => c !== request . contractId ) ;
63
81
} ) ;
64
82
65
83
ws . on ( "close" , ( ) => console . log ( "Client disconnected" ) ) ;
@@ -75,22 +93,24 @@ async function processInvoiceRequest(request: InvoiceRequest, ws: WebSocket) {
75
93
76
94
console . log ( "Invoice Request Received:" , request ) ;
77
95
78
- // Check if LND_MACAROON and LND_SOCKET are empty to simulate mock mode
79
- if ( ! process . env . LND_MACAROON && ! process . env . LND_SOCKET ) {
80
- console . log ( "Mock Server Mode: Simulating payment success" ) ;
81
-
82
- // Simulate processing delay
83
- await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ; // 1 second delay for realism
84
-
85
- // Directly respond with a simulated success message
86
- ws . send ( JSON . stringify ( {
96
+ // Check if LND_MACAROON and LND_SOCKET are empty to simulate mock mode
97
+ if ( ! process . env . LND_MACAROON && ! process . env . LND_SOCKET ) {
98
+ console . log ( "Mock Server Mode: Simulating payment success" ) ;
99
+
100
+ // Simulate processing delay
101
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ; // 1 second delay for realism
102
+
103
+ // Directly respond with a simulated success message
104
+ ws . send (
105
+ JSON . stringify ( {
87
106
status : "success" ,
88
107
message : "Invoice paid successfully in mock mode." ,
89
- } ) ) ;
90
-
91
- // Exit early since we're in mock mode
92
- return ;
93
- }
108
+ } )
109
+ ) ;
110
+
111
+ // Exit early since we're in mock mode
112
+ return ;
113
+ }
94
114
95
115
try {
96
116
const options = { gasPrice : ethers . parseUnits ( "0.001" , "gwei" ) } ;
@@ -135,10 +155,14 @@ async function processInvoiceRequest(request: InvoiceRequest, ws: WebSocket) {
135
155
max_fee : providerConfig . maxLNFee ,
136
156
} ) ;
137
157
console . log ( "Payment Response:" , paymentResponse ) ;
138
-
158
+ ws . send (
159
+ JSON . stringify ( {
160
+ status : "success" ,
161
+ message : "Invoice paid successfully." ,
162
+ } )
163
+ ) ;
139
164
// Critical point, if this withdraw fails, the LSP will lose funds
140
165
// We should cache the paymentResponse.secret and request.contractId and retry the withdrawal if it fails
141
-
142
166
await htlcContract
143
167
. withdraw ( request . contractId , "0x" + paymentResponse . secret , options )
144
168
. then ( ( tx : any ) => {
@@ -151,13 +175,7 @@ async function processInvoiceRequest(request: InvoiceRequest, ws: WebSocket) {
151
175
secret : paymentResponse . secret ,
152
176
} ) ;
153
177
} ) ;
154
-
155
- ws . send (
156
- JSON . stringify ( {
157
- status : "success" ,
158
- message : "Invoice paid successfully." ,
159
- } )
160
- ) ;
178
+ console . log ( "Payment processed successfully" ) ;
161
179
} catch ( error ) {
162
180
console . error ( "Error during invoice processing:" , error ) ;
163
181
ws . send (
0 commit comments