@optimex-xyz/market-maker-sdk 0.8.0 → 0.8.5

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.
Files changed (2) hide show
  1. package/README.md +387 -213
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # PMM API Integration Documentation
2
2
 
3
- > **CHANGELOG (v0.7.1)**:
3
+ > **CHANGELOG (v0.8.0)**:
4
+ >
4
5
  > - **Breaking Changes:**
5
- > - Environment key `dev` has been renamed to `staging` - please update your environment configuration accordingly
6
- > - Several functions from `routerService` moved to `protocolService`
6
+ > - Update to get router contract from protocol fetcher
7
+ > - Use consistent trade_id across all protocol
8
+ > - Add `user_receiving_address`, `user_refund_pubkey`, `from_user_address` to `indicative_quote` api requirement as optional fields.
7
9
  > - **Upgrade Notes:**
8
- > - For PMMs using version 6.2, you can update to v0.7.1 without needing to change anything
10
+ > - For PMMs older version you can update to v0.8.0 without needing to change anything
9
11
  > - If you need to use the Router, please use the values provided in the environment configuration section
10
12
 
11
13
  > **Note**: If you prefer using the SDK instead of direct API integration, please refer to the [PMM SDK Integration Guide](sdk-integration.md).
@@ -14,24 +16,64 @@ A comprehensive guide for implementing Private Market Makers (PMMs) in the cross
14
16
 
15
17
  ## Table of Contents
16
18
 
17
- - [PMM API Integration Documentation](#pmm-api-integration-documentation)
18
- - [1. Overview](#1-overview)
19
- - [2. Quick Start](#2-quick-start)
19
+ - [PMM API Integration Documentation](#pmm-api-integration-documentation)
20
+ - [Table of Contents](#table-of-contents)
21
+ - [1. Overview](#1-overview)
22
+ - [1.1. Integration Flow](#11-integration-flow)
23
+ - [2. Quick Start](#2-quick-start)
24
+ - [2.1. API Environments](#21-api-environments)
20
25
  - [3. PMM Backend APIs](#3-pmm-backend-apis)
21
- - [3.1. Endpoint: `/indicative-quote`](#31-endpoint-indicative-quote)
22
- - [3.2. Endpoint: `/commitment-quote`](#32-endpoint-commitment-quote)
23
- - [3.3. Endpoint: `/settlement-signature`](#33-endpoint-settlement-signature)
24
- - [3.4. Endpoint: `/ack-settlement`](#34-endpoint-ack-settlement)
25
- - [3.5. Endpoint: `/signal-payment`](#35-endpoint-signal-payment)
26
+ - [3.1. Endpoint: `/indicative-quote`](#31-endpoint-indicative-quote)
27
+ - [Description](#description)
28
+ - [Request Parameters](#request-parameters)
29
+ - [Example Request](#example-request)
30
+ - [Expected Response](#expected-response)
31
+ - [3.2. Endpoint: `/commitment-quote`](#32-endpoint-commitment-quote)
32
+ - [Description](#description-1)
33
+ - [Request Parameters](#request-parameters-1)
34
+ - [Example Request](#example-request-1)
35
+ - [Expected Response](#expected-response-1)
36
+ - [3.3. Endpoint: `/liquidation-quote`](#33-endpoint-liquidation-quote)
37
+ - [Description](#description-2)
38
+ - [Request Parameters](#request-parameters-2)
39
+ - [Example Request](#example-request-2)
40
+ - [Expected Response](#expected-response-2)
41
+ - [3.4. Endpoint: `/settlement-signature`](#34-endpoint-settlement-signature)
42
+ - [Description](#description-3)
43
+ - [Request Parameters](#request-parameters-3)
44
+ - [Example Request](#example-request-3)
45
+ - [Expected Response](#expected-response-3)
46
+ - [3.5. Endpoint: `/ack-settlement`](#35-endpoint-ack-settlement)
47
+ - [Description](#description-4)
48
+ - [Request Parameters](#request-parameters-4)
49
+ - [Example Request](#example-request-4)
50
+ - [Expected Response](#expected-response-4)
51
+ - [3.6. Endpoint: `/signal-payment`](#36-endpoint-signal-payment)
52
+ - [Description](#description-5)
53
+ - [Request Parameters](#request-parameters-5)
54
+ - [Example Request](#example-request-5)
55
+ - [Expected Response](#expected-response-5)
26
56
  - [4. Solver API Endpoints for PMMs](#4-solver-api-endpoints-for-pmms)
27
57
  - [4.1. Endpoint: `/v1/market-maker/tokens`](#41-endpoint-v1market-makertokens)
58
+ - [Description](#description-6)
59
+ - [Request Parameters](#request-parameters-6)
60
+ - [Example Request](#example-request-6)
61
+ - [Expected Response](#expected-response-6)
28
62
  - [4.2. Endpoint: `/v1/market-maker/submit-settlement-tx`](#42-endpoint-v1market-makersubmit-settlement-tx)
63
+ - [Description](#description-7)
64
+ - [Request Parameters](#request-parameters-7)
65
+ - [Example Request](#example-request-7)
66
+ - [Expected Response](#expected-response-7)
67
+ - [Notes](#notes)
29
68
  - [4.3. Endpoint: `/v1/market-maker/trades/:tradeId`](#43-endpoint-v1market-makertradestradeid)
69
+ - [Description](#description-8)
70
+ - [Request Parameters](#request-parameters-8)
71
+ - [Example Request](#example-request-8)
72
+ - [Expected Response](#expected-response-8)
30
73
  - [5. PMM Making Payment](#5-pmm-making-payment)
31
74
  - [5.1. EVM](#51-evm)
32
75
  - [5.2. Bitcoin](#52-bitcoin)
33
76
 
34
-
35
77
  ## 1. Overview
36
78
 
37
79
  The PMM integration with Optimex involves bidirectional API communication:
@@ -41,7 +83,6 @@ The PMM integration with Optimex involves bidirectional API communication:
41
83
 
42
84
  ### 1.1. Integration Flow
43
85
 
44
-
45
86
  ```mermaid
46
87
  sequenceDiagram
47
88
  participant User
@@ -76,22 +117,24 @@ sequenceDiagram
76
117
 
77
118
  ### 2.1. API Environments
78
119
 
79
- | Environment | Description |
80
- | ---------------- | -------------------------------------------------------------------- |
81
- | `dev` | internal environment with test networks and development services |
82
- | `staging` | Staging environment with test networks and staging services |
83
- | `prelive` | Pre production environment with mainnet networks for testing before release |
84
- | `production` | Production environment with mainnet networks and production services |
120
+ | Environment | Description |
121
+ | ------------ | --------------------------------------------------------------------------- |
122
+ | `dev` | internal environment with test networks and development services |
123
+ | `staging` | Staging environment with test networks and staging services |
124
+ | `prelive` | Pre production environment with mainnet networks for testing before release |
125
+ | `production` | Production environment with mainnet networks and production services |
85
126
 
86
127
  <details>
87
128
  <summary><strong>Staging Contracts</strong></summary>
88
129
 
89
130
  **Optimex L2 Testnet**
131
+
90
132
  - **Signer**: [0xA89F5060B810F3b6027D7663880c43ee77A865C7](https://scan-testnet.optimex.xyz/address/0xA89F5060B810F3b6027D7663880c43ee77A865C7)
91
- - **Router**: [0xa6E13A3c6c63C64d45bB453E0402B7D2eE53E564](https://scan-testnet.optimex.xyz/address/0xa6E13A3c6c63C64d45bB453E0402B7D2eE53E564)
133
+ - **Router**: [0x31C88ebd9E430455487b6a5c8971e8eF63e97ED4](https://scan-testnet.optimex.xyz/address/0x31C88ebd9E430455487b6a5c8971e8eF63e97ED4)
92
134
  - **ProtocolFetcherProxy**: [0x7c07151ca4DFd93F352Ab9B132A95866697c38c2](https://scan-testnet.optimex.xyz/address/0x7c07151ca4DFd93F352Ab9B132A95866697c38c2)
93
135
 
94
136
  **Ethereum Sepolia**
137
+
95
138
  - **Payment**: [0x7387DcCfE2f1D5F80b4ECDF91eF58541517e90D2](https://sepolia.etherscan.io/address/0x7387DcCfE2f1D5F80b4ECDF91eF58541517e90D2)
96
139
  - **ETHVault**: [0x17aD543010fc8E8065b85E203839C0CBEcdfC851](https://sepolia.etherscan.io/address/0x17aD543010fc8E8065b85E203839C0CBEcdfC851)
97
140
  - **WETHVault**: [0x673Ac1489457F43F04403940cE425ae19a9D639B](https://sepolia.etherscan.io/address/0x673Ac1489457F43F04403940cE425ae19a9D639B)
@@ -103,13 +146,14 @@ sequenceDiagram
103
146
  <details>
104
147
  <summary><strong>Production/Prelive Contracts</strong></summary>
105
148
 
106
-
107
149
  **Optimex L2 Mainnet**
150
+
108
151
  - **Signer**: [0xCF9786F123F1071023dB8049808C223e94c384be](https://scan.optimex.xyz/address/0xCF9786F123F1071023dB8049808C223e94c384be)
109
- - **Router**: [0xF7fedF4A250157010807E6eA60258E3B768149Ff](https://scan.optimex.xyz/address/0xF7fedF4A250157010807E6eA60258E3B768149Ff)
152
+ - **Router**: [0x1e878cCa765a8aAFEBecCa672c767441b4859634](https://scan.optimex.xyz/address/0x1e878cCa765a8aAFEBecCa672c767441b4859634)
110
153
  - **ProtocolFetcherProxy**: [0xFDEd4CEf9aE1E03D0BeF161262a266c1c157a32b](https://scan.optimex.xyz/address/0xFDEd4CEf9aE1E03D0BeF161262a266c1c157a32b)
111
154
 
112
155
  **Ethereum Mainnet**
156
+
113
157
  - **Payment**: [0x0A497AC4261E37FA4062762C23Cf3cB642C839b8](https://etherscan.io/address/0x0A497AC4261E37FA4062762C23Cf3cB642C839b8)
114
158
  - **ETHVault**: [0xF7fedF4A250157010807E6eA60258E3B768149Ff](https://etherscan.io/address/0xF7fedF4A250157010807E6eA60258E3B768149Ff)
115
159
  - **WETHVault**: [0xaD3f379AaED8Eca895209Af446F2e34f07145dbC](https://etherscan.io/address/0xaD3f379AaED8Eca895209Af446F2e34f07145dbC)
@@ -141,6 +185,9 @@ Provides an indicative quote for the given token pair and trade amount. The quot
141
185
  - `deposited` (boolean, optional): Whether the deposit has been confirmed. This allows the PMM to decide the returned quote.
142
186
  - `trade_timeout` (string, optional): The deadline when user is expected to receive tokens from PMM in UNIX timestamp. We expect the trade to be completed before this timeout. But if not, some actions can still be taken.
143
187
  - `script_timeout` (string, optional): The hard timeout for the trade, UNIX timestamp. After this timeout, the trade will not be processed further.
188
+ - `from_user_address` (string, optional): The user's address from which the input token will be sent from.
189
+ - `user_receiving_address` (string, optional): The user's address to which the output token will be sent to.
190
+ - `user_refund_pubkey` (string, optional): The user's public key to which the refund will be sent.
144
191
 
145
192
  #### Example Request
146
193
 
@@ -173,52 +220,53 @@ GET /indicative-quote?from_token_id=ETH&to_token_id=BTC&amount=10000000000000000
173
220
  ```js
174
221
  async function getIndicativeQuote(req, res) {
175
222
  try {
176
- const { from_token_id, to_token_id, amount, session_id } = req.query;
223
+ const { from_token_id, to_token_id, amount, session_id } = req.query
177
224
 
178
225
  // Generate a session ID if not provided
179
- const sessionId = session_id || generateSessionId();
226
+ const sessionId = session_id || generateSessionId()
180
227
 
181
228
  // Fetch token information from Solver API
182
- const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens');
183
- const tokensData = await tokensResponse.json();
229
+ const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens')
230
+ const tokensData = await tokensResponse.json()
184
231
 
185
232
  // Find the from token and to token
186
- const fromToken = tokensData.data.tokens.find(token => token.token_id === from_token_id);
187
- const toToken = tokensData.data.tokens.find(token => token.token_id === to_token_id);
233
+ const fromToken = tokensData.data.tokens.find((token) => token.token_id === from_token_id)
234
+ const toToken = tokensData.data.tokens.find((token) => token.token_id === to_token_id)
188
235
 
189
236
  if (!fromToken || !toToken) {
190
237
  return res.status(400).json({
191
238
  session_id: sessionId,
192
239
  pmm_receiving_address: '',
193
240
  indicative_quote: '0',
194
- error: 'Token not found'
195
- });
241
+ error: 'Token not found',
242
+ })
196
243
  }
197
244
 
198
245
  // Calculate the quote (implementation specific to your PMM)
199
246
  // Note: Treat amount as BigInt
200
- const amountBigInt = BigInt(amount);
201
- const quote = calculateQuote(fromToken, toToken, amountBigInt);
247
+ const amountBigInt = BigInt(amount)
248
+ const quote = calculateQuote(fromToken, toToken, amountBigInt)
202
249
 
203
250
  // Get the receiving address for this token pair
204
- const pmmReceivingAddress = getPMMReceivingAddress(fromToken.network_id);
251
+ const pmmReceivingAddress = getPMMReceivingAddress(fromToken.network_id)
205
252
 
206
253
  return res.status(200).json({
207
254
  session_id: sessionId,
208
255
  pmm_receiving_address: pmmReceivingAddress,
209
256
  indicative_quote: quote.toString(),
210
- error: ''
211
- });
257
+ error: '',
258
+ })
212
259
  } catch (error) {
213
260
  return res.status(500).json({
214
261
  session_id: req.query.session_id || '',
215
262
  pmm_receiving_address: '',
216
263
  indicative_quote: '0',
217
- error: error.message
218
- });
264
+ error: error.message,
265
+ })
219
266
  }
220
267
  }
221
268
  ```
269
+
222
270
  </details>
223
271
 
224
272
  ### 3.2. Endpoint: `/commitment-quote`
@@ -283,39 +331,39 @@ async function getCommitmentQuote(req, res) {
283
331
  user_deposit_tx,
284
332
  user_deposit_vault,
285
333
  trade_deadline,
286
- script_deadline
287
- } = req.query;
334
+ script_deadline,
335
+ } = req.query
288
336
 
289
337
  // Validate the session exists
290
- const session = await sessionRepository.findById(session_id);
338
+ const session = await sessionRepository.findById(session_id)
291
339
  if (!session) {
292
340
  return res.status(400).json({
293
341
  trade_id,
294
342
  commitment_quote: '0',
295
- error: 'Session not found'
296
- });
343
+ error: 'Session not found',
344
+ })
297
345
  }
298
346
 
299
347
  // Fetch token information from Solver API
300
- const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens');
301
- const tokensData = await tokensResponse.json();
348
+ const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens')
349
+ const tokensData = await tokensResponse.json()
302
350
 
303
351
  // Find the from token and to token
304
- const fromToken = tokensData.data.tokens.find(token => token.token_id === from_token_id);
305
- const toToken = tokensData.data.tokens.find(token => token.token_id === to_token_id);
352
+ const fromToken = tokensData.data.tokens.find((token) => token.token_id === from_token_id)
353
+ const toToken = tokensData.data.tokens.find((token) => token.token_id === to_token_id)
306
354
 
307
355
  if (!fromToken || !toToken) {
308
356
  return res.status(400).json({
309
357
  trade_id,
310
358
  commitment_quote: '0',
311
- error: 'Token not found'
312
- });
359
+ error: 'Token not found',
360
+ })
313
361
  }
314
362
 
315
363
  // Calculate the final quote (implementation specific to your PMM)
316
364
  // Note: Treat numeric values as BigInt
317
- const amountBigInt = BigInt(amount);
318
- const quote = calculateFinalQuote(fromToken, toToken, amountBigInt, trade_deadline);
365
+ const amountBigInt = BigInt(amount)
366
+ const quote = calculateFinalQuote(fromToken, toToken, amountBigInt, trade_deadline)
319
367
 
320
368
  // Store the trade in the database
321
369
  await tradeRepository.create({
@@ -330,26 +378,157 @@ async function getCommitmentQuote(req, res) {
330
378
  userDepositVault: user_deposit_vault,
331
379
  tradeDeadline: trade_deadline,
332
380
  scriptDeadline: script_deadline,
333
- commitmentQuote: quote.toString()
334
- });
381
+ commitmentQuote: quote.toString(),
382
+ })
335
383
 
336
384
  return res.status(200).json({
337
385
  trade_id,
338
386
  commitment_quote: quote.toString(),
339
- error: ''
340
- });
387
+ error: '',
388
+ })
341
389
  } catch (error) {
342
390
  return res.status(500).json({
343
391
  trade_id: req.query.trade_id || '',
344
392
  commitment_quote: '0',
345
- error: error.message
346
- });
393
+ error: error.message,
394
+ })
347
395
  }
348
396
  }
349
397
  ```
398
+
350
399
  </details>
351
400
 
352
- ### 3.3. Endpoint: `/settlement-signature`
401
+ ### 3.3. Endpoint: `/liquidation-quote`
402
+
403
+ #### Description
404
+
405
+ Provides a firm commitment quote for a liquidation trade. This endpoint is called after the user deposits funds and represents a binding commitment to execute the liquidation at the quoted rate.
406
+
407
+ #### Request Parameters
408
+
409
+ - **HTTP Method**: `GET`
410
+ - **Query Parameters**:
411
+ - `session_id` (string): Session identifier from the indicative quote.
412
+ - `trade_id` (string): Unique trade identifier.
413
+ - `from_token_id` (string): The ID of the source token.
414
+ - `to_token_id` (string): The ID of the destination token.
415
+ - `amount` (string): The amount of the source token to be traded, in base 10. This should be treated as a BigInt in your implementation.
416
+ - `payment_metadata` (string, optional): Hex string encoded data for smart contract payment method.
417
+ - `from_user_address` (string): The user's address from which the input token will be sent.
418
+ - `to_user_address` (string): The user's address to which the output token will be sent.
419
+ - `user_deposit_tx` (string): Transaction hash of user's deposit.
420
+ - `user_deposit_vault` (string): Vault containing user's deposit.
421
+ - `trade_deadline` (string): Expected payment deadline (UNIX timestamp).
422
+ - `script_deadline` (string): Withdrawal deadline if unpaid (UNIX timestamp).
423
+
424
+ #### Example Request
425
+
426
+ ```
427
+ GET /liquidation-quote?session_id=12345&trade_id=0x3bfe2fc4889a98a39b31b348e7b212ea3f2bea63fd1ea2e0c8ba326433677328&from_token_id=ETH&to_token_id=BTC&amount=1000000000000000000&from_user_address=0xUserAddress&to_user_address=bc1p68q6hew27ljf4ghvlnwqz0fq32qg7tsgc7jr5levfy8r74p5k52qqphk07&user_deposit_tx=0xDepositTxHash&user_deposit_vault=VaultData&trade_deadline=1696012800&script_deadline=1696016400
428
+ ```
429
+
430
+ #### Expected Response
431
+
432
+ - **HTTP Status**: `200 OK`
433
+ - **Response Body** (JSON):
434
+
435
+ ```json
436
+ {
437
+ "trade_id": "0x3bfe2fc4889a98a39b31b348e7b212ea3f2bea63fd1ea2e0c8ba326433677328",
438
+ "liquidation_quote": "987654321000000000",
439
+ "error": ""
440
+ }
441
+ ```
442
+
443
+ - `trade_id` (string): The trade ID associated with the request.
444
+ - `liquidation_quote` (string): **Firm committed quote** - PMM must honor this price. Should be treated as a BigInt in your implementation.
445
+ - `error` (string): Error message, if any (empty if no error).
446
+
447
+ <details>
448
+ <summary><strong>Example Implementation</strong></summary>
449
+
450
+ ```js
451
+ async function getLiquidationQuote(req, res) {
452
+ try {
453
+ const {
454
+ session_id,
455
+ trade_id,
456
+ from_token_id,
457
+ to_token_id,
458
+ amount,
459
+ from_user_address,
460
+ to_user_address,
461
+ user_deposit_tx,
462
+ user_deposit_vault,
463
+ trade_deadline,
464
+ script_deadline,
465
+ } = req.query
466
+
467
+ // Validate the session exists
468
+ const session = await sessionRepository.findById(session_id)
469
+ if (!session) {
470
+ return res.status(400).json({
471
+ trade_id,
472
+ liquidation_quote: '0',
473
+ error: 'Session not found',
474
+ })
475
+ }
476
+
477
+ // Fetch token information from Solver API
478
+ const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens')
479
+ const tokensData = await tokensResponse.json()
480
+
481
+ // Find the from token and to token
482
+ const fromToken = tokensData.data.tokens.find((token) => token.token_id === from_token_id)
483
+ const toToken = tokensData.data.tokens.find((token) => token.token_id === to_token_id)
484
+
485
+ if (!fromToken || !toToken) {
486
+ return res.status(400).json({
487
+ trade_id,
488
+ liquidation_quote: '0',
489
+ error: 'Token not found',
490
+ })
491
+ }
492
+
493
+ // Calculate the firm liquidation quote (implementation specific to your PMM)
494
+ // Note: Treat numeric values as BigInt
495
+ const amountBigInt = BigInt(amount)
496
+ const quote = calculateLiquidationQuote(fromToken, toToken, amountBigInt, trade_deadline)
497
+
498
+ // Store the trade in the database
499
+ await tradeRepository.create({
500
+ tradeId: trade_id,
501
+ sessionId: session_id,
502
+ fromTokenId: from_token_id,
503
+ toTokenId: to_token_id,
504
+ amount: amountBigInt.toString(),
505
+ fromUserAddress: from_user_address,
506
+ toUserAddress: to_user_address,
507
+ userDepositTx: user_deposit_tx,
508
+ userDepositVault: user_deposit_vault,
509
+ tradeDeadline: trade_deadline,
510
+ scriptDeadline: script_deadline,
511
+ liquidationQuote: quote.toString(),
512
+ })
513
+
514
+ return res.status(200).json({
515
+ trade_id,
516
+ liquidation_quote: quote.toString(),
517
+ error: '',
518
+ })
519
+ } catch (error) {
520
+ return res.status(500).json({
521
+ trade_id: req.query.trade_id || '',
522
+ liquidation_quote: '0',
523
+ error: error.message,
524
+ })
525
+ }
526
+ }
527
+ ```
528
+
529
+ </details>
530
+
531
+ ### 3.4. Endpoint: `/settlement-signature`
353
532
 
354
533
  #### Description
355
534
 
@@ -395,35 +574,35 @@ GET /settlement-signature?trade_id=0x3d09b8eb94466bffa126aeda68c8c0f330633a7d005
395
574
  ```js
396
575
  async function getSettlementSignature(req, res) {
397
576
  try {
398
- const { trade_id, committed_quote, trade_deadline, script_deadline } = req.query;
577
+ const { trade_id, committed_quote, trade_deadline, script_deadline } = req.query
399
578
 
400
579
  // Fetch the trade from the database
401
- const trade = await tradeRepository.findById(trade_id);
580
+ const trade = await tradeRepository.findById(trade_id)
402
581
  if (!trade) {
403
582
  return res.status(400).json({
404
583
  trade_id,
405
584
  signature: '',
406
585
  deadline: 0,
407
- error: 'Trade not found'
408
- });
586
+ error: 'Trade not found',
587
+ })
409
588
  }
410
589
 
411
590
  // Fetch trade details from Solver API
412
- const tradeDetailsResponse = await fetch(`https://api.solver.example/v1/market-maker/trades/${trade_id}`);
413
- const tradeDetails = await tradeDetailsResponse.json();
591
+ const tradeDetailsResponse = await fetch(`https://api.solver.example/v1/market-maker/trades/${trade_id}`)
592
+ const tradeDetails = await tradeDetailsResponse.json()
414
593
 
415
594
  // Calculate a deadline (30 minutes from now)
416
- const deadline = Math.floor(Date.now() / 1000) + 1800;
595
+ const deadline = Math.floor(Date.now() / 1000) + 1800
417
596
 
418
597
  // Get PMM data
419
- const pmmId = process.env.PMM_ID; // Your PMM ID
598
+ const pmmId = process.env.PMM_ID // Your PMM ID
420
599
 
421
600
  // Get the presigns and trade data from tradeDetails
422
- const { from_token, to_token } = tradeDetails.data;
601
+ const { from_token, to_token } = tradeDetails.data
423
602
 
424
603
  // Create a commitment info hash
425
604
  // Note: Treat numeric values as BigInt
426
- const committedQuoteBigInt = BigInt(committed_quote);
605
+ const committedQuoteBigInt = BigInt(committed_quote)
427
606
  const commitInfoHash = createCommitInfoHash(
428
607
  pmmId,
429
608
  trade.pmmReceivingAddress,
@@ -431,31 +610,32 @@ async function getSettlementSignature(req, res) {
431
610
  to_token.address,
432
611
  committedQuoteBigInt,
433
612
  deadline
434
- );
613
+ )
435
614
 
436
615
  // Sign the commitment with your private key
437
- const privateKey = process.env.PMM_PRIVATE_KEY;
438
- const signature = signMessage(privateKey, trade_id, commitInfoHash);
616
+ const privateKey = process.env.PMM_PRIVATE_KEY
617
+ const signature = signMessage(privateKey, trade_id, commitInfoHash)
439
618
 
440
619
  return res.status(200).json({
441
620
  trade_id,
442
621
  signature,
443
622
  deadline,
444
- error: ''
445
- });
623
+ error: '',
624
+ })
446
625
  } catch (error) {
447
626
  return res.status(500).json({
448
627
  trade_id: req.query.trade_id || '',
449
628
  signature: '',
450
629
  deadline: 0,
451
- error: error.message
452
- });
630
+ error: error.message,
631
+ })
453
632
  }
454
633
  }
455
634
  ```
635
+
456
636
  </details>
457
637
 
458
- ### 3.4. Endpoint: `/ack-settlement`
638
+ ### 3.5. Endpoint: `/ack-settlement`
459
639
 
460
640
  #### Description
461
641
 
@@ -502,42 +682,43 @@ trade_id=0x024be4dae899989e0c3d9b4459e5811613bcd04016dc56529f16a19d2a7724c0&trad
502
682
  ```js
503
683
  async function ackSettlement(req, res) {
504
684
  try {
505
- const { trade_id, trade_deadline, script_deadline, chosen } = req.body;
685
+ const { trade_id, trade_deadline, script_deadline, chosen } = req.body
506
686
 
507
687
  // Fetch the trade from the database
508
- const trade = await tradeRepository.findById(trade_id);
688
+ const trade = await tradeRepository.findById(trade_id)
509
689
  if (!trade) {
510
690
  return res.status(400).json({
511
691
  trade_id,
512
692
  status: 'error',
513
- error: 'Trade not found'
514
- });
693
+ error: 'Trade not found',
694
+ })
515
695
  }
516
696
 
517
697
  // Update trade status based on whether it was chosen
518
698
  await tradeRepository.update(trade_id, {
519
699
  chosen: chosen === 'true',
520
700
  tradeDeadline: trade_deadline,
521
- scriptDeadline: script_deadline
522
- });
701
+ scriptDeadline: script_deadline,
702
+ })
523
703
 
524
704
  return res.status(200).json({
525
705
  trade_id,
526
706
  status: 'acknowledged',
527
- error: ''
528
- });
707
+ error: '',
708
+ })
529
709
  } catch (error) {
530
710
  return res.status(500).json({
531
711
  trade_id: req.body.trade_id || '',
532
712
  status: 'error',
533
- error: error.message
534
- });
713
+ error: error.message,
714
+ })
535
715
  }
536
716
  }
537
717
  ```
718
+
538
719
  </details>
539
720
 
540
- ### 3.5. Endpoint: `/signal-payment`
721
+ ### 3.6. Endpoint: `/signal-payment`
541
722
 
542
723
  #### Description
543
724
 
@@ -584,45 +765,46 @@ trade_id=0x3bfe2fc4889a98a39b31b348e7b212ea3f2bea63fd1ea2e0c8ba326433677328&tota
584
765
  ```js
585
766
  async function signalPayment(req, res) {
586
767
  try {
587
- const { trade_id, total_fee_amount, trade_deadline, script_deadline } = req.body;
768
+ const { trade_id, total_fee_amount, trade_deadline, script_deadline } = req.body
588
769
 
589
770
  // Fetch the trade from the database
590
- const trade = await tradeRepository.findById(trade_id);
771
+ const trade = await tradeRepository.findById(trade_id)
591
772
  if (!trade) {
592
773
  return res.status(400).json({
593
774
  trade_id,
594
775
  status: 'error',
595
- error: 'Trade not found'
596
- });
776
+ error: 'Trade not found',
777
+ })
597
778
  }
598
779
 
599
780
  // Update trade with fee amount
600
781
  await tradeRepository.update(trade_id, {
601
782
  totalFeeAmount: total_fee_amount,
602
783
  tradeDeadline: trade_deadline,
603
- scriptDeadline: script_deadline
604
- });
784
+ scriptDeadline: script_deadline,
785
+ })
605
786
 
606
787
  // Queue the payment task
607
788
  await paymentQueue.add({
608
789
  tradeId: trade_id,
609
- totalFeeAmount: total_fee_amount
610
- });
790
+ totalFeeAmount: total_fee_amount,
791
+ })
611
792
 
612
793
  return res.status(200).json({
613
794
  trade_id,
614
795
  status: 'acknowledged',
615
- error: ''
616
- });
796
+ error: '',
797
+ })
617
798
  } catch (error) {
618
799
  return res.status(500).json({
619
800
  trade_id: req.body.trade_id || '',
620
801
  status: 'error',
621
- error: error.message
622
- });
802
+ error: error.message,
803
+ })
623
804
  }
624
805
  }
625
806
  ```
807
+
626
808
  </details>
627
809
 
628
810
  ## 4. Solver API Endpoints for PMMs
@@ -657,79 +839,81 @@ GET /v1/market-maker/tokens
657
839
 
658
840
  ```json
659
841
  {
660
- "data": {
661
- "supported_networks": [
662
- {
663
- "network_id": "bitcoin_testnet",
664
- "name": "Bitcoin Testnet",
665
- "symbol": "tBTC",
666
- "type": "BTC",
667
- "logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/btc_network.svg"
668
- },
669
- {
670
- "network_id": "ethereum_sepolia",
671
- "name": "Ethereum Sepolia",
672
- "symbol": "ETH",
673
- "type": "EVM",
674
- "logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth_network.svg"
675
- }
676
- ],
677
- "tokens": [
678
- {
679
- "id": 2,
680
- "network_id": "bitcoin_testnet",
681
- "token_id": "tBTC",
682
- "network_name": "Bitcoin Testnet",
683
- "network_symbol": "tBTC",
684
- "network_type": "BTC",
685
- "token_name": "Bitcoin Testnet",
686
- "token_symbol": "tBTC",
687
- "token_address": "native",
688
- "token_decimals": 8,
689
- "token_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/tbtc.svg",
690
- "network_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/btc_network.svg",
691
- "active": true,
692
- "created_at": "2024-10-28T07:24:33.179Z",
693
- "updated_at": "2024-11-07T04:40:46.454Z"
694
- },
695
- {
696
- "id": 11,
697
- "network_id": "ethereum_sepolia",
698
- "token_id": "ETH",
699
- "network_name": "Ethereum Sepolia",
700
- "network_symbol": "ETH",
701
- "network_type": "EVM",
702
- "token_name": "Ethereum Sepolia",
703
- "token_symbol": "ETH",
704
- "token_address": "native",
705
- "token_decimals": 18,
706
- "token_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth.svg",
707
- "network_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth_network.svg",
708
- "active": true,
709
- "created_at": "2024-11-22T08:36:59.175Z",
710
- "updated_at": "2024-11-22T08:36:59.175Z"
711
- }
712
- ],
713
- "pairs": [
714
- {
715
- "from_token_id": "ETH",
716
- "to_token_id": "tBTC",
717
- "is_active": true
718
- },
719
- {
720
- "from_token_id": "tBTC",
721
- "to_token_id": "ETH",
722
- "is_active": true
723
- }
724
- ]
725
- }
842
+ "data": {
843
+ "supported_networks": [
844
+ {
845
+ "network_id": "bitcoin_testnet",
846
+ "name": "Bitcoin Testnet",
847
+ "symbol": "tBTC",
848
+ "type": "BTC",
849
+ "logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/btc_network.svg"
850
+ },
851
+ {
852
+ "network_id": "ethereum_sepolia",
853
+ "name": "Ethereum Sepolia",
854
+ "symbol": "ETH",
855
+ "type": "EVM",
856
+ "logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth_network.svg"
857
+ }
858
+ ],
859
+ "tokens": [
860
+ {
861
+ "id": 2,
862
+ "network_id": "bitcoin_testnet",
863
+ "token_id": "tBTC",
864
+ "network_name": "Bitcoin Testnet",
865
+ "network_symbol": "tBTC",
866
+ "network_type": "BTC",
867
+ "token_name": "Bitcoin Testnet",
868
+ "token_symbol": "tBTC",
869
+ "token_address": "native",
870
+ "token_decimals": 8,
871
+ "token_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/tbtc.svg",
872
+ "network_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/btc_network.svg",
873
+ "active": true,
874
+ "created_at": "2024-10-28T07:24:33.179Z",
875
+ "updated_at": "2024-11-07T04:40:46.454Z"
876
+ },
877
+ {
878
+ "id": 11,
879
+ "network_id": "ethereum_sepolia",
880
+ "token_id": "ETH",
881
+ "network_name": "Ethereum Sepolia",
882
+ "network_symbol": "ETH",
883
+ "network_type": "EVM",
884
+ "token_name": "Ethereum Sepolia",
885
+ "token_symbol": "ETH",
886
+ "token_address": "native",
887
+ "token_decimals": 18,
888
+ "token_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth.svg",
889
+ "network_logo_uri": "https://storage.googleapis.com/Optimex-static-35291d79/images/tokens/eth_network.svg",
890
+ "active": true,
891
+ "created_at": "2024-11-22T08:36:59.175Z",
892
+ "updated_at": "2024-11-22T08:36:59.175Z"
893
+ }
894
+ ],
895
+ "pairs": [
896
+ {
897
+ "from_token_id": "ETH",
898
+ "to_token_id": "tBTC",
899
+ "is_active": true
900
+ },
901
+ {
902
+ "from_token_id": "tBTC",
903
+ "to_token_id": "ETH",
904
+ "is_active": true
905
+ }
906
+ ]
907
+ }
726
908
  }
727
909
  ```
910
+
728
911
  </details>
729
912
 
730
913
  ### 4.2. Endpoint: `/v1/market-maker/submit-settlement-tx`
731
914
 
732
915
  #### Description
916
+
733
917
  Allows the PMM to submit settlement transaction hashes for trades. This endpoint is essential for completing the trade settlement process and must be called after making payments.
734
918
 
735
919
  #### Request Parameters
@@ -755,17 +939,18 @@ Allows the PMM to submit settlement transaction hashes for trades. This endpoint
755
939
  - `signed_at` (integer): UNIX timestamp (seconds) when you signed this submission.
756
940
  - `settlement_tx` (string): Should be hex format with a `0x` prefix
757
941
 
758
- - **For EVM Chains:**
759
- - Use the transaction hash directly without additional encoding
760
- - Example: `settlement_tx`: [0x7a87d2c423e13533b5ae0ecc5af900a7b697048103f4f6e32d19edde5e707355](https://etherscan.io/tx/0x7a87d2c423e13533b5ae0ecc5af900a7b697048103f4f6e32d19edde5e707355)
942
+ - **For EVM Chains:**
943
+
944
+ - Use the transaction hash directly without additional encoding
945
+ - Example: `settlement_tx`: [0x7a87d2c423e13533b5ae0ecc5af900a7b697048103f4f6e32d19edde5e707355](https://etherscan.io/tx/0x7a87d2c423e13533b5ae0ecc5af900a7b697048103f4f6e32d19edde5e707355)
761
946
 
762
- - **For Bitcoin or Solana:**
763
- - Must encode raw_tx string using the `l2Encode` function
764
- - Example raw_tx string: `3d83c7846d6e5b04279175a9592705a15373f3029b866d5224cc0744489fe403`
765
- - After encoding
766
- ```
767
- "settlement_tx": "0x33643833633738343664366535623034323739313735613935393237303561313533373366333032396238363664353232346363303734343438396665343033"
768
- ```
947
+ - **For Bitcoin or Solana:**
948
+ - Must encode raw_tx string using the `l2Encode` function
949
+ - Example raw_tx string: `3d83c7846d6e5b04279175a9592705a15373f3029b866d5224cc0744489fe403`
950
+ - After encoding
951
+ ```
952
+ "settlement_tx": "0x33643833633738343664366535623034323739313735613935393237303561313533373366333032396238363664353232346363303734343438396665343033"
953
+ ```
769
954
 
770
955
  <details>
771
956
  <summary><strong>Bitcoin l2Encode</strong></summary>
@@ -785,6 +970,7 @@ export const l2Encode = (info: string) => {
785
970
  return ensureHexPrefix(ethers.hexlify(toUtf8Bytes(info)))
786
971
  }
787
972
  ```
973
+
788
974
  </details>
789
975
 
790
976
  #### Example Request
@@ -901,9 +1087,7 @@ GET /v1/market-maker/trades/0xfc24b9bc1299b50896027cb4c85d041c911e062147ffaf7ae9
901
1087
  "user_deposit_tx": "0x202186375a3b8d55de4d8d1afb7f6a5bec8978cef3b705e6cb379729d03b16c7",
902
1088
  "deposit_vault": "0xf7fedf4a250157010807e6ea60258e3b768149ff",
903
1089
  "payment_bundle": {
904
- "trade_ids": [
905
- "0xfc24b9bc1299b50896027cb4c85d041c911e062147ffaf7ae9c7e51b670086c2"
906
- ],
1090
+ "trade_ids": ["0xfc24b9bc1299b50896027cb4c85d041c911e062147ffaf7ae9c7e51b670086c2"],
907
1091
  "settlement_tx": "3d83c7846d6e5b04279175a9592705a15373f3029b866d5224cc0744489fe403",
908
1092
  "signature": "0x479a5a89e7a871026b60307351ea650fc667890b25d3d02df7ed2e93f94db90d7c3f8dbd823220896b8ad49b13a90851199236e82a644ffbe99e53503929fe151b",
909
1093
  "start_index": 0,
@@ -920,6 +1104,7 @@ GET /v1/market-maker/trades/0xfc24b9bc1299b50896027cb4c85d041c911e062147ffaf7ae9
920
1104
  }
921
1105
  }
922
1106
  ```
1107
+
923
1108
  </details>
924
1109
 
925
1110
  ## 5. PMM Making Payment
@@ -929,53 +1114,47 @@ GET /v1/market-maker/trades/0xfc24b9bc1299b50896027cb4c85d041c911e062147ffaf7ae9
929
1114
  In case the target chain is EVM-based, the transaction should emit the event from the `l1 payment contract` with the correct values for pmmAmountOut and protocolFee.
930
1115
 
931
1116
  ```js
932
- const { ethers } = require('ethers');
1117
+ const { ethers } = require('ethers')
933
1118
 
934
1119
  async function makeEVMPayment(tradeId, toAddress, amount, token, protocolFeeAmount) {
935
1120
  try {
936
1121
  // Get the private key from your secure storage
937
- const privateKey = process.env.PMM_EVM_PRIVATE_KEY;
1122
+ const privateKey = process.env.PMM_EVM_PRIVATE_KEY
938
1123
 
939
1124
  // Set up the provider and signer
940
- const rpcUrl = getRpcUrlForNetwork(token.networkId);
941
- const provider = new ethers.JsonRpcProvider(rpcUrl);
942
- const signer = new ethers.Wallet(privateKey, provider);
1125
+ const rpcUrl = getRpcUrlForNetwork(token.networkId)
1126
+ const provider = new ethers.JsonRpcProvider(rpcUrl)
1127
+ const signer = new ethers.Wallet(privateKey, provider)
943
1128
 
944
1129
  // Get the payment contract address
945
- const paymentAddress = getPaymentAddressForNetwork(token.networkId);
1130
+ const paymentAddress = getPaymentAddressForNetwork(token.networkId)
946
1131
 
947
1132
  // Create the contract instance
948
1133
  const paymentAbi = [
949
1134
  // ABI for the payment contract
950
- "function payment(bytes32 tradeId, address token, address recipient, uint256 amount, uint256 feeAmount, uint256 deadline) payable returns (bool)"
951
- ];
952
- const paymentContract = new ethers.Contract(paymentAddress, paymentAbi, signer);
1135
+ 'function payment(bytes32 tradeId, address token, address recipient, uint256 amount, uint256 feeAmount, uint256 deadline) payable returns (bool)',
1136
+ ]
1137
+ const paymentContract = new ethers.Contract(paymentAddress, paymentAbi, signer)
953
1138
 
954
1139
  // Calculate the deadline (30 minutes from now)
955
- const deadline = Math.floor(Date.now() / 1000) + 30 * 60;
1140
+ const deadline = Math.floor(Date.now() / 1000) + 30 * 60
956
1141
 
957
1142
  // If the token is native, we need to set the value
958
- const value = token.tokenAddress === 'native' ? amount : 0;
959
- const tokenAddress = token.tokenAddress === 'native' ? ethers.ZeroAddress : token.tokenAddress;
1143
+ const value = token.tokenAddress === 'native' ? amount : 0
1144
+ const tokenAddress = token.tokenAddress === 'native' ? ethers.ZeroAddress : token.tokenAddress
960
1145
 
961
1146
  // Submit the transaction
962
- const tx = await paymentContract.payment(
963
- tradeId,
964
- tokenAddress,
965
- toAddress,
966
- amount,
967
- protocolFeeAmount,
968
- deadline,
969
- { value }
970
- );
1147
+ const tx = await paymentContract.payment(tradeId, tokenAddress, toAddress, amount, protocolFeeAmount, deadline, {
1148
+ value,
1149
+ })
971
1150
 
972
- console.log(`Transfer transaction sent: ${tx.hash}`);
1151
+ console.log(`Transfer transaction sent: ${tx.hash}`)
973
1152
 
974
1153
  // Return the transaction hash with the 0x prefix
975
- return `0x${tx.hash.replace(/^0x/, '')}`;
1154
+ return `0x${tx.hash.replace(/^0x/, '')}`
976
1155
  } catch (error) {
977
- console.error('EVM payment error:', error);
978
- throw error;
1156
+ console.error('EVM payment error:', error)
1157
+ throw error
979
1158
  }
980
1159
  }
981
1160
  ```
@@ -985,11 +1164,12 @@ async function makeEVMPayment(tradeId, toAddress, amount, token, protocolFeeAmou
985
1164
  In case the target chain is Bitcoin, the transaction should have at least N + 1 outputs, with the first N outputs being the settlement UTXOs for trades, and one of them being the change UTXO for the user with the correct amount. The output N + 1 is the OP_RETURN output with the hash of tradeIds.
986
1165
 
987
1166
  ```js
1167
+ import { getTradeIdsHash } from '@optimex-xyz/market-maker-sdk'
1168
+
1169
+ import axios from 'axios'
988
1170
  import * as bitcoin from 'bitcoinjs-lib'
989
1171
  import { ECPairFactory } from 'ecpair'
990
1172
  import * as ecc from 'tiny-secp256k1'
991
- import axios from 'axios'
992
- import { getTradeIdsHash } from '@optimex-xyz/market-maker-sdk'
993
1173
 
994
1174
  async function makeBitcoinPayment(params) {
995
1175
  const { toAddress, amount, token, tradeId } = params
@@ -1046,9 +1226,7 @@ async function makeBitcoinPayment(params) {
1046
1226
 
1047
1227
  // Check if we have enough balance
1048
1228
  if (totalInput < amount) {
1049
- throw new Error(
1050
- `Insufficient balance. Need ${amount} satoshis, but only have ${totalInput} satoshis`
1051
- )
1229
+ throw new Error(`Insufficient balance. Need ${amount} satoshis, but only have ${totalInput} satoshis`)
1052
1230
  }
1053
1231
 
1054
1232
  // Get fee rate
@@ -1073,10 +1251,7 @@ async function makeBitcoinPayment(params) {
1073
1251
  // Add OP_RETURN output with trade ID hash
1074
1252
  const tradeIdsHash = getTradeIdsHash([tradeId])
1075
1253
  psbt.addOutput({
1076
- script: bitcoin.script.compile([
1077
- bitcoin.opcodes.OP_RETURN,
1078
- Buffer.from(tradeIdsHash.slice(2), 'hex')
1079
- ]),
1254
+ script: bitcoin.script.compile([bitcoin.opcodes.OP_RETURN, Buffer.from(tradeIdsHash.slice(2), 'hex')]),
1080
1255
  value: 0n,
1081
1256
  })
1082
1257
 
@@ -1103,5 +1278,4 @@ async function makeBitcoinPayment(params) {
1103
1278
 
1104
1279
  return response.data // Transaction ID
1105
1280
  }
1106
-
1107
1281
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimex-xyz/market-maker-sdk",
3
- "version": "0.8.0",
3
+ "version": "0.8.5",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"