@optimex-xyz/market-maker-sdk 0.4.6 → 0.4.8

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/README.md CHANGED
@@ -1,18 +1,17 @@
1
- # PMM SDK Integration Documentation
1
+ # PMM API Integration Documentation
2
2
 
3
- A comprehensive toolkit for implementing Private Market Makers (PMMs) in the cross-chain trading network. This guide covers the required integration points between PMMs and our solver backend, enabling cross-chain liquidity provision and settlement.
3
+ > **Note**: If you prefer using the SDK instead of direct API integration, please refer to the [PMM SDK Integration Guide](sdk-integration.md).
4
+
5
+ A comprehensive guide for implementing Private Market Makers (PMMs) in the cross-chain trading network. This documentation covers the required integration points between PMMs and our solver backend, enabling cross-chain liquidity provision and settlement.
4
6
 
5
7
  ## Table of Contents
6
8
 
7
- - [PMM SDK Integration Documentation](#pmm-sdk-integration-documentation)
9
+ - [PMM API Integration Documentation](#pmm-api-integration-documentation)
8
10
  - [Table of Contents](#table-of-contents)
9
11
  - [1. Overview](#1-overview)
10
- - [1.1. Repository Structure](#11-repository-structure)
11
- - [1.2. Example Implementation](#12-example-implementation)
12
+ - [1.1. Integration Flow](#11-integration-flow)
12
13
  - [2. Quick Start](#2-quick-start)
13
- - [2.1. Installation](#21-installation)
14
- - [2.2. Environment Setup](#22-environment-setup)
15
- - [2.3. Available Environments](#23-available-environments)
14
+ - [2.1. API Environments](#21-api-environments)
16
15
  - [3. PMM Backend APIs](#3-pmm-backend-apis)
17
16
  - [3.1. Endpoint: `/indicative-quote`](#31-endpoint-indicative-quote)
18
17
  - [Description](#description)
@@ -44,21 +43,32 @@ A comprehensive toolkit for implementing Private Market Makers (PMMs) in the cro
44
43
  - [Example Request](#example-request-4)
45
44
  - [Expected Response](#expected-response-4)
46
45
  - [Example Implementation](#example-implementation-4)
47
- - [4. SDK Functions for PMMs](#4-sdk-functions-for-pmms)
48
- - [4.1. Function: getTokens](#41-function-gettokens)
46
+ - [4. Solver API Endpoints for PMMs](#4-solver-api-endpoints-for-pmms)
47
+ - [4.1. Endpoint: `/v1/market-maker/tokens`](#41-endpoint-v1market-makertokens)
49
48
  - [Description](#description-5)
50
- - [Example Code](#example-code)
51
- - [4.2. Function: submitSettlementTx](#42-function-submitsettlementtx)
49
+ - [Request Parameters](#request-parameters-5)
50
+ - [Example Request](#example-request-5)
51
+ - [Expected Response](#expected-response-5)
52
+ - [4.2. Endpoint: `/v1/market-maker/submit-settlement-tx`](#42-endpoint-v1market-makersubmit-settlement-tx)
52
53
  - [Description](#description-6)
53
- - [Example Implementation](#example-implementation-5)
54
+ - [Request Parameters](#request-parameters-6)
55
+ - [Example Request](#example-request-6)
56
+ - [Expected Response](#expected-response-6)
54
57
  - [Notes](#notes)
58
+ - [4.3. Endpoint: `/v1/market-maker/trades/:tradeId`](#43-endpoint-v1market-makertradestradeid)
59
+ - [Description](#description-7)
60
+ - [Request Parameters](#request-parameters-7)
61
+ - [Example Request](#example-request-7)
62
+ - [Expected Response](#expected-response-7)
55
63
  - [5. PMM Making Payment](#5-pmm-making-payment)
56
64
  - [5.1. EVM](#51-evm)
57
65
  - [5.2. Bitcoin](#52-bitcoin)
58
66
 
59
67
  ## 1. Overview
60
68
 
61
- This repository contains everything needed to integrate your PMM with 's solver network:
69
+ This documentation contains everything needed to integrate your PMM with the solver network. The integration requires implementing specific API endpoints in your PMM service and making calls to the solver backend APIs.
70
+
71
+ ### 1.1. Integration Flow
62
72
 
63
73
  ```mermaid
64
74
  sequenceDiagram
@@ -86,44 +96,13 @@ sequenceDiagram
86
96
  Solver->>PMM: POST /signal-payment
87
97
  PMM-->>Solver: Acknowledge signal
88
98
  PMM->>Chain: Execute settlement (transfer)
89
- PMM->>Solver: POST /submit-settlement-tx
99
+ PMM->>Solver: POST /v1/market-maker/submit-settlement-tx
90
100
  Solver-->>PMM: Confirm settlement submission
91
101
  ```
92
102
 
93
- ### 1.1. Repository Structure
94
- The repository consists of:
95
- - `abi/`: Smart contract ABIs and interfaces
96
- - `example/`: A complete mock PMM implementation showing how to integrate the SDK
97
- - `src/`: Source code for the market maker SDK
98
-
99
- ### 1.2. Example Implementation
100
- The [Example](example/) directory contains a fully functional mock PMM. Use this implementation as a reference while integrating the `@optimex-xyz/market-maker-sdk` into your own PMM service.
101
-
102
103
  ## 2. Quick Start
103
104
 
104
- ### 2.1. Installation
105
-
106
- ```bash
107
- npm install @optimex-xyz/market-maker-sdk
108
- # or
109
- yarn add @optimex-xyz/market-maker-sdk
110
- ```
111
-
112
- ### 2.2. Environment Setup
113
-
114
- you can directly specify the environment
115
-
116
- ```typescript
117
- import { sdk, Environment } from '@optimex-xyz/market-maker-sdk'
118
-
119
- // Change to development environment
120
- sdk.setEnvironment('dev' as Environment)
121
-
122
- // Change to production environment
123
- sdk.setEnvironment('production' as Environment)
124
- ```
125
-
126
- ### 2.3. Available Environments
105
+ ### 2.1. API Environments
127
106
 
128
107
  | Environment | Description |
129
108
  | ------------ | -------------------------------------------------------------------- |
@@ -179,34 +158,49 @@ GET /indicative-quote?from_token_id=ETH&to_token_id=BTC&amount=10000000000000000
179
158
  #### Example Implementation
180
159
 
181
160
  ```js
182
- import { Token, tokenService } from '@optimex-xyz/market-maker-sdk'
183
-
184
- export const IndicativeQuoteResponseSchema = z.object({
185
- sessionId: z.string(),
186
- pmmReceivingAddress: z.string(),
187
- indicativeQuote: z.string(),
188
- error: z.string().optional(),
189
- });
190
-
191
- export type IndicativeQuoteResponse = z.infer<
192
- typeof IndicativeQuoteResponseSchema
193
- >;
194
-
195
- async getIndicativeQuote(dto: GetIndicativeQuoteDto): Promise<IndicativeQuoteResponse> {
196
- const sessionId = dto.sessionId || this.generateSessionId()
197
-
198
- const [fromToken, toToken] = Promise.all([
199
- this.tokenService.getTokenByTokenId(dto.fromTokenId),
200
- this.tokenService.getTokenByTokenId(dto.toTokenId),
201
- ])
202
-
203
- const quote = this.calculateBestQuote()
204
-
205
- return {
206
- sessionId,
207
- pmmReceivingAddress,
208
- indicativeQuote: quote,
209
- error: '',
161
+ async function getIndicativeQuote(req, res) {
162
+ try {
163
+ const { from_token_id, to_token_id, amount, session_id } = req.query;
164
+
165
+ // Generate a session ID if not provided
166
+ const sessionId = session_id || generateSessionId();
167
+
168
+ // Fetch token information from Solver API
169
+ const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens');
170
+ const tokensData = await tokensResponse.json();
171
+
172
+ // Find the from token and to token
173
+ const fromToken = tokensData.data.tokens.find(token => token.token_id === from_token_id);
174
+ const toToken = tokensData.data.tokens.find(token => token.token_id === to_token_id);
175
+
176
+ if (!fromToken || !toToken) {
177
+ return res.status(400).json({
178
+ session_id: sessionId,
179
+ pmm_receiving_address: '',
180
+ indicative_quote: '0',
181
+ error: 'Token not found'
182
+ });
183
+ }
184
+
185
+ // Calculate the quote (implementation specific to your PMM)
186
+ const quote = calculateQuote(fromToken, toToken, amount);
187
+
188
+ // Get the receiving address for this token pair
189
+ const pmmReceivingAddress = getPMMReceivingAddress(fromToken.network_id);
190
+
191
+ return res.status(200).json({
192
+ session_id: sessionId,
193
+ pmm_receiving_address: pmmReceivingAddress,
194
+ indicative_quote: quote,
195
+ error: ''
196
+ });
197
+ } catch (error) {
198
+ return res.status(500).json({
199
+ session_id: req.query.session_id || '',
200
+ pmm_receiving_address: '',
201
+ indicative_quote: '0',
202
+ error: error.message
203
+ });
210
204
  }
211
205
  }
212
206
  ```
@@ -259,44 +253,78 @@ GET /commitment-quote?session_id=12345&trade_id=abcd1234&from_token_id=ETH&to_to
259
253
  #### Example Implementation
260
254
 
261
255
  ```js
262
- import { Token, tokenService } from '@optimex-xyz/market-maker-sdk'
263
-
264
- export const GetCommitmentQuoteSchema = z.object({
265
- sessionId: z.string(),
266
- tradeId: z.string(),
267
- fromTokenId: z.string(),
268
- toTokenId: z.string(),
269
- amount: z.string(),
270
- fromUserAddress: z.string(),
271
- toUserAddress: z.string(),
272
- userDepositTx: z.string(),
273
- userDepositVault: z.string(),
274
- tradeDeadline: z.string(),
275
- scriptDeadline: z.string(),
276
- });
277
-
278
- export class GetCommitmentQuoteDto extends createZodDto(
279
- GetCommitmentQuoteSchema
280
- ) {}
281
-
282
- async getCommitmentQuote(dto: GetCommitmentQuoteDto): Promise<CommitmentQuoteResponse> {
283
- const session = await this.sessionRepo.findById(dto.sessionId)
284
-
285
- const [fromToken, toToken] = await Promise.all([
286
- tokenService.getTokenByTokenId(dto.fromTokenId),
287
- tokenService.getTokenByTokenId(dto.toTokenId),
288
- ])
289
-
290
- const quote = this.calculateBestQuote(...)
291
-
292
- await this.tradeService.createTrade({ tradeId: dto.tradeId })
293
-
294
- await this.tradeService.updateTradeQuote(dto.tradeId, { commitmentQuote: quote })
295
-
296
- return {
297
- tradeId: dto.tradeId,
298
- commitmentQuote: quote,
299
- error: '',
256
+ async function getCommitmentQuote(req, res) {
257
+ try {
258
+ const {
259
+ session_id,
260
+ trade_id,
261
+ from_token_id,
262
+ to_token_id,
263
+ amount,
264
+ from_user_address,
265
+ to_user_address,
266
+ user_deposit_tx,
267
+ user_deposit_vault,
268
+ trade_deadline,
269
+ script_deadline
270
+ } = req.query;
271
+
272
+ // Validate the session exists
273
+ const session = await sessionRepository.findById(session_id);
274
+ if (!session) {
275
+ return res.status(400).json({
276
+ trade_id,
277
+ commitment_quote: '0',
278
+ error: 'Session not found'
279
+ });
280
+ }
281
+
282
+ // Fetch token information from Solver API
283
+ const tokensResponse = await fetch('https://api.solver.example/v1/market-maker/tokens');
284
+ const tokensData = await tokensResponse.json();
285
+
286
+ // Find the from token and to token
287
+ const fromToken = tokensData.data.tokens.find(token => token.token_id === from_token_id);
288
+ const toToken = tokensData.data.tokens.find(token => token.token_id === to_token_id);
289
+
290
+ if (!fromToken || !toToken) {
291
+ return res.status(400).json({
292
+ trade_id,
293
+ commitment_quote: '0',
294
+ error: 'Token not found'
295
+ });
296
+ }
297
+
298
+ // Calculate the final quote (implementation specific to your PMM)
299
+ const quote = calculateFinalQuote(fromToken, toToken, amount, trade_deadline);
300
+
301
+ // Store the trade in the database
302
+ await tradeRepository.create({
303
+ tradeId: trade_id,
304
+ sessionId: session_id,
305
+ fromTokenId: from_token_id,
306
+ toTokenId: to_token_id,
307
+ amount,
308
+ fromUserAddress: from_user_address,
309
+ toUserAddress: to_user_address,
310
+ userDepositTx: user_deposit_tx,
311
+ userDepositVault: user_deposit_vault,
312
+ tradeDeadline: trade_deadline,
313
+ scriptDeadline: script_deadline,
314
+ commitmentQuote: quote
315
+ });
316
+
317
+ return res.status(200).json({
318
+ trade_id,
319
+ commitment_quote: quote,
320
+ error: ''
321
+ });
322
+ } catch (error) {
323
+ return res.status(500).json({
324
+ trade_id: req.query.trade_id || '',
325
+ commitment_quote: '0',
326
+ error: error.message
327
+ });
300
328
  }
301
329
  }
302
330
  ```
@@ -343,77 +371,62 @@ GET /settlement-signature?trade_id=abcd1234&committed_quote=987654321000000000&t
343
371
 
344
372
  #### Example Implementation
345
373
 
346
- ```ts
347
- import {
348
- getCommitInfoHash,
349
- getSignature,
350
- routerService,
351
- SignatureType,
352
- signerService,
353
- } from '@optimex-xyz/market-maker-sdk'
354
-
355
- export const GetSettlementSignatureSchema = z.object({
356
- tradeId: z.string(),
357
- committedQuote: z.string(),
358
- tradeDeadline: z.string(),
359
- scriptDeadline: z.string(),
360
- })
361
-
362
- export class GetSettlementSignatureDto extends createZodDto(GetSettlementSignatureSchema) {}
363
-
364
- async getSettlementSignature(dto: GetSettlementSignatureDto, trade: Trade): Promise<SettlementSignatureResponseDto> {
374
+ ```js
375
+ async function getSettlementSignature(req, res) {
365
376
  try {
366
- const { tradeId } = trade
377
+ const { trade_id, committed_quote, trade_deadline, script_deadline } = req.query;
378
+
379
+ // Fetch the trade from the database
380
+ const trade = await tradeRepository.findById(trade_id);
381
+ if (!trade) {
382
+ return res.status(400).json({
383
+ trade_id,
384
+ signature: '',
385
+ deadline: 0,
386
+ error: 'Trade not found'
387
+ });
388
+ }
367
389
 
368
- // Get data directly from l2 contract or using routerService ( wrapper of l2 contract )
369
- const [presigns, tradeData] = await Promise.all([
370
- routerService.getPresigns(tradeId),
371
- routerService.getTradeData(tradeId),
372
- ])
390
+ // Fetch trade details from Solver API
391
+ const tradeDetailsResponse = await fetch(`https://api.solver.example/v1/market-maker/trades/${trade_id}`);
392
+ const tradeDetails = await tradeDetailsResponse.json();
373
393
 
374
- const { toChain } = tradeData.tradeInfo
375
- const deadline = BigInt(Math.floor(Date.now() / 1000) + 1800)
394
+ // Calculate a deadline (30 minutes from now)
395
+ const deadline = Math.floor(Date.now() / 1000) + 1800;
376
396
 
377
- const pmmId = ... // hexString
378
- const pmmPresign = presigns.find((t) => t.pmmId === pmmId)
379
- if (!pmmPresign) {
380
- throw new BadRequestException('pmmPresign not found')
381
- }
397
+ // Get PMM data
398
+ const pmmId = process.env.PMM_ID; // Your PMM ID
382
399
 
383
- // get amountOut from the committed quote
384
- const amountOut = BigInt(dto.committedQuote)
400
+ // Get the presigns and trade data from tradeDetails
401
+ const { from_token, to_token } = tradeDetails.data;
385
402
 
386
- const commitInfoHash = getCommitInfoHash(
387
- pmmPresign.pmmId,
388
- pmmPresign.pmmRecvAddress,
389
- toChain[1],
390
- toChain[2],
391
- amountOut,
403
+ // Create a commitment info hash
404
+ const commitInfoHash = createCommitInfoHash(
405
+ pmmId,
406
+ trade.pmmReceivingAddress,
407
+ to_token.chain,
408
+ to_token.address,
409
+ committed_quote,
392
410
  deadline
393
- )
411
+ );
394
412
 
395
- const signerAddress = await this.routerService.getSigner()
413
+ // Sign the commitment with your private key
414
+ const privateKey = process.env.PMM_PRIVATE_KEY;
415
+ const signature = signMessage(privateKey, trade_id, commitInfoHash);
396
416
 
397
- const domain = await signerService.getDomain()
398
-
399
- const signature = await getSignature(
400
- this.pmmWallet,
401
- this.provider,
402
- signerAddress,
403
- tradeId,
404
- commitInfoHash,
405
- SignatureType.VerifyingContract,
406
- domain
407
- )
408
-
409
- return {
410
- tradeId: tradeId,
417
+ return res.status(200).json({
418
+ trade_id,
411
419
  signature,
412
- deadline: Number(deadline),
413
- error: '',
414
- }
415
- } catch (error: any) {
416
- // Handle error
420
+ deadline,
421
+ error: ''
422
+ });
423
+ } catch (error) {
424
+ return res.status(500).json({
425
+ trade_id: req.query.trade_id || '',
426
+ signature: '',
427
+ deadline: 0,
428
+ error: error.message
429
+ });
417
430
  }
418
431
  }
419
432
  ```
@@ -461,30 +474,39 @@ trade_id=abcd1234&trade_deadline=1696012800&script_deadline=1696016400&chosen=tr
461
474
 
462
475
  #### Example Implementation
463
476
 
464
- ```ts
465
- export const AckSettlementSchema = z.object({
466
- tradeId: z.string(),
467
- tradeDeadline: z.string(),
468
- scriptDeadline: z.string(),
469
- chosen: z.string().refine((val) => val === 'true' || val === 'false', {
470
- message: "chosen must be 'true' or 'false'",
471
- }),
472
- })
477
+ ```js
478
+ async function ackSettlement(req, res) {
479
+ try {
480
+ const { trade_id, trade_deadline, script_deadline, chosen } = req.body;
481
+
482
+ // Fetch the trade from the database
483
+ const trade = await tradeRepository.findById(trade_id);
484
+ if (!trade) {
485
+ return res.status(400).json({
486
+ trade_id,
487
+ status: 'error',
488
+ error: 'Trade not found'
489
+ });
490
+ }
473
491
 
474
- export class AckSettlementDto extends createZodDto(AckSettlementSchema) {}
492
+ // Update trade status based on whether it was chosen
493
+ await tradeRepository.update(trade_id, {
494
+ chosen: chosen === 'true',
495
+ tradeDeadline: trade_deadline,
496
+ scriptDeadline: script_deadline
497
+ });
475
498
 
476
- async ackSettlement(dto: AckSettlementDto, trade: Trade): Promise<AckSettlementResponseDto> {
477
- try {
478
- return {
479
- tradeId: dto.tradeId,
499
+ return res.status(200).json({
500
+ trade_id,
480
501
  status: 'acknowledged',
481
- error: '',
482
- }
483
- } catch (error: any) {
484
- if (error instanceof HttpException) {
485
- throw error
486
- }
487
- throw new BadRequestException(error.message)
502
+ error: ''
503
+ });
504
+ } catch (error) {
505
+ return res.status(500).json({
506
+ trade_id: req.body.trade_id || '',
507
+ status: 'error',
508
+ error: error.message
509
+ });
488
510
  }
489
511
  }
490
512
  ```
@@ -500,7 +522,7 @@ Used by the solver to signal the chosen PMM to start submitting their payment.
500
522
  - **HTTP Method**: `POST`
501
523
  - **Form Parameters**:
502
524
  - `trade_id` (string): The unique identifier for the trade.
503
- - `protocol_fee_amount` (string): The amount of protocol fee the PMM has to submit, in base 10.
525
+ - `total_fee_amount` (string): The amount of total fee the PMM has to submit, in base 10.
504
526
  - `trade_deadline` (string): The UNIX timestamp (in seconds) by which the user expects to receive payment.
505
527
  - `script_deadline` (string): The UNIX timestamp (in seconds) after which the user can withdraw their deposit if not paid.
506
528
 
@@ -510,7 +532,7 @@ Used by the solver to signal the chosen PMM to start submitting their payment.
510
532
  POST /signal-payment
511
533
  Content-Type: application/x-www-form-urlencoded
512
534
 
513
- trade_id=abcd1234&protocol_fee_amount=1000000000000000&trade_deadline=1696012800&script_deadline=1696016400
535
+ trade_id=abcd1234&total_fee_amount=1000000000000000&trade_deadline=1696012800&script_deadline=1696016400
514
536
  ```
515
537
 
516
538
  #### Expected Response
@@ -532,121 +554,202 @@ trade_id=abcd1234&protocol_fee_amount=1000000000000000&trade_deadline=1696012800
532
554
 
533
555
  #### Example Implementation
534
556
 
535
- ```ts
536
- export const SignalPaymentSchema = z.object({
537
- tradeId: z.string(),
538
- protocolFeeAmount: z.string(),
539
- tradeDeadline: z.string(),
540
- scriptDeadline: z.string(),
541
- })
542
-
543
- export class SignalPaymentDto extends createZodDto(SignalPaymentSchema) {}
544
-
545
- async signalPayment(dto: SignalPaymentDto, trade: Trade): Promise<SignalPaymentResponseDto> {
557
+ ```js
558
+ async function signalPayment(req, res) {
546
559
  try {
547
- // enqueue transfer with dto and trade
560
+ const { trade_id, total_fee_amount, trade_deadline, script_deadline } = req.body;
561
+
562
+ // Fetch the trade from the database
563
+ const trade = await tradeRepository.findById(trade_id);
564
+ if (!trade) {
565
+ return res.status(400).json({
566
+ trade_id,
567
+ status: 'error',
568
+ error: 'Trade not found'
569
+ });
570
+ }
548
571
 
549
- return {
550
- tradeId: dto.tradeId,
572
+ // Update trade with fee amount
573
+ await tradeRepository.update(trade_id, {
574
+ totalFeeAmount: total_fee_amount,
575
+ tradeDeadline: trade_deadline,
576
+ scriptDeadline: script_deadline
577
+ });
578
+
579
+ // Queue the payment task
580
+ await paymentQueue.add({
581
+ tradeId: trade_id,
582
+ totalFeeAmount: total_fee_amount
583
+ });
584
+
585
+ return res.status(200).json({
586
+ trade_id,
551
587
  status: 'acknowledged',
552
- error: '',
553
- }
554
- } catch (error: any) {
555
- if (error instanceof HttpException) {
556
- throw error
557
- }
558
- throw new BadRequestException(error.message)
588
+ error: ''
589
+ });
590
+ } catch (error) {
591
+ return res.status(500).json({
592
+ trade_id: req.body.trade_id || '',
593
+ status: 'error',
594
+ error: error.message
595
+ });
559
596
  }
560
597
  }
561
598
  ```
562
599
 
563
- ## 4. SDK Functions for PMMs
600
+ ## 4. Solver API Endpoints for PMMs
601
+
602
+
603
+ These API endpoints are provided by the Solver backend for PMMs to retrieve token information and submit settlement data.
564
604
 
565
- These SDK functions facilitate PMM-Solver communication and are essential for implementing the required backend APIs.
605
+ > **Note**: The base URL for the Solver API endpoints will be provided separately. All endpoint paths in this documentation should be appended to that base URL.
566
606
 
567
- ### 4.1. Function: getTokens
607
+
608
+ ### 4.1. Endpoint: `/v1/market-maker/tokens`
568
609
 
569
610
  #### Description
570
611
 
571
- Returns a list of all supported tokens across different networks.
612
+ Returns a list of tokens supported by the Solver Backend.
613
+
614
+ #### Request Parameters
572
615
 
573
- #### Example Code
616
+ - **HTTP Method**: `GET`
574
617
 
575
- ```ts
576
- import { tokenService } from '@optimex-xyz/market-maker-sdk'
618
+ #### Example Request
577
619
 
578
- tokenService.getTokens()
620
+ ```
621
+ GET /v1/market-maker/tokens
579
622
  ```
580
623
 
581
- ### 4.2. Function: submitSettlementTx
624
+ #### Expected Response
625
+
626
+ - **HTTP Status**: `200 OK`
627
+ - **Response Body** (JSON):
628
+
629
+ ```json
630
+ {
631
+ "data": {
632
+ "supported_networks": [
633
+ {
634
+ "network_id": "bitcoin_testnet",
635
+ "name": "Bitcoin Testnet",
636
+ "symbol": "tBTC",
637
+ "type": "BTC",
638
+ "logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/btc_network.svg"
639
+ },
640
+ {
641
+ "network_id": "ethereum_sepolia",
642
+ "name": "Ethereum Sepolia",
643
+ "symbol": "ETH",
644
+ "type": "EVM",
645
+ "logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/eth_network.svg"
646
+ }
647
+ ],
648
+ "tokens": [
649
+ {
650
+ "id": 2,
651
+ "network_id": "bitcoin_testnet",
652
+ "token_id": "tBTC",
653
+ "network_name": "Bitcoin Testnet",
654
+ "network_symbol": "tBTC",
655
+ "network_type": "BTC",
656
+ "token_name": "Bitcoin Testnet",
657
+ "token_symbol": "tBTC",
658
+ "token_address": "native",
659
+ "token_decimals": 8,
660
+ "token_logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/tbtc.svg",
661
+ "network_logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/btc_network.svg",
662
+ "active": true,
663
+ "created_at": "2024-10-28T07:24:33.179Z",
664
+ "updated_at": "2024-11-07T04:40:46.454Z"
665
+ },
666
+ {
667
+ "id": 11,
668
+ "network_id": "ethereum_sepolia",
669
+ "token_id": "ETH",
670
+ "network_name": "Ethereum Sepolia",
671
+ "network_symbol": "ETH",
672
+ "network_type": "EVM",
673
+ "token_name": "Ethereum Sepolia",
674
+ "token_symbol": "ETH",
675
+ "token_address": "native",
676
+ "token_decimals": 18,
677
+ "token_logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/eth.svg",
678
+ "network_logo_uri": "https://storage.googleapis.com/bitfi-static-35291d79/images/tokens/eth_network.svg",
679
+ "active": true,
680
+ "created_at": "2024-11-22T08:36:59.175Z",
681
+ "updated_at": "2024-11-22T08:36:59.175Z"
682
+ }
683
+ ],
684
+ "pairs": [
685
+ {
686
+ "from_token_id": "ETH",
687
+ "to_token_id": "tBTC",
688
+ "is_active": true
689
+ },
690
+ {
691
+ "from_token_id": "tBTC",
692
+ "to_token_id": "ETH",
693
+ "is_active": true
694
+ }
695
+ ]
696
+ }
697
+ }
698
+ ```
699
+
700
+ ### 4.2. Endpoint: `/v1/market-maker/submit-settlement-tx`
582
701
 
583
702
  #### Description
584
703
 
585
704
  Allows the PMM to submit the settlement transaction hash for one or more trades. This step is necessary to complete the trade settlement process.
586
705
 
587
- Parameters:
706
+ #### Request Parameters
707
+
708
+ - **HTTP Method**: `POST`
709
+ - **Request Body** (JSON):
710
+
711
+ ```json
712
+ {
713
+ "trade_ids": ["0xTradeID1", "0xTradeID2", "..."],
714
+ "pmm_id": "pmm001",
715
+ "settlement_tx": "SettlementTransactionData",
716
+ "signature": "0xSignatureData",
717
+ "start_index": 0,
718
+ "signed_at": 1719158400
719
+ }
720
+ ```
721
+
588
722
  - `trade_ids` (array of strings): An array of trade IDs associated with the settlement transaction.
589
723
  - `pmm_id` (string): The PMM's ID, which must match the one committed for the trade(s).
590
- - `settlement_tx` (string): The raw transaction data (in hex) representing the settlement.
724
+ - `settlement_tx` (string): The raw transaction data representing the settlement.
591
725
  - `signature` (string): The PMM's signature on the settlement transaction.
592
726
  - `start_index` (integer): The index indicating the starting point for settlement processing (used for batch settlements).
593
727
  - `signed_at` (integer): The UNIX timestamp (in seconds) when the PMM signed the settlement transaction.
594
728
 
595
- #### Example Implementation
596
-
597
- ```ts
598
- import {
599
- getMakePaymentHash,
600
- getSignature,
601
- routerService,
602
- SignatureType,
603
- signerService,
604
- solverService,
605
- } from '@optimex-xyz/market-maker-sdk'
606
-
607
- async submit(job: Job<string>) {
608
- const { tradeId, paymentTxId } = toObject(job.data) as SubmitSettlementEvent
609
-
610
- try {
611
- const tradeIds: BytesLike[] = [tradeId]
612
- const startIdx = BigInt(tradeIds.indexOf(tradeId))
613
-
614
- const signerAddress = await this.routerService.getSigner()
615
-
616
- const signedAt = Math.floor(Date.now() / 1000)
617
-
618
- const makePaymentInfoHash = getMakePaymentHash(tradeIds, BigInt(signedAt), startIdx, ensureHexPrefix(paymentTxId))
729
+ #### Example Request
619
730
 
620
- const domain = await signerService.getDomain()
731
+ ```
732
+ POST /v1/market-maker/submit-settlement-tx
733
+ Content-Type: application/json
621
734
 
622
- const signature = await getSignature(
623
- this.pmmWallet,
624
- this.provider,
625
- signerAddress,
626
- tradeId,
627
- makePaymentInfoHash,
628
- SignatureType.MakePayment,
629
- domain
630
- )
631
-
632
- const pmmId = ... // string
633
- const requestPayload = {
634
- tradeIds: [tradeId],
635
- pmmId: pmmId,
636
- settlementTx: ensureHexPrefix(paymentTxId),
637
- signature: signature,
638
- startIndex: 0,
639
- signedAt: signedAt,
640
- }
735
+ {
736
+ "trade_ids": ["0xabcdef123456...", "0x123456abcdef..."],
737
+ "pmm_id": "pmm001",
738
+ "settlement_tx": "0xRawTransactionData",
739
+ "signature": "0xSignatureData",
740
+ "start_index": 0,
741
+ "signed_at": 1719158400
742
+ }
743
+ ```
641
744
 
642
- const response = await this.solverSerivce.submitSettlementTx(requestPayload)
745
+ #### Expected Response
643
746
 
644
- return response
645
- } catch (error: any) {
646
- this.logger.error('submit settlement error', error.stack)
747
+ - **HTTP Status**: `200 OK`
748
+ - **Response Body** (JSON):
647
749
 
648
- throw error // Re-throw the error for the queue to handle
649
- }
750
+ ```json
751
+ {
752
+ "message": "Settlement transaction submitted successfully"
650
753
  }
651
754
  ```
652
755
 
@@ -656,100 +759,85 @@ async submit(job: Job<string>) {
656
759
  - **Start Index**: Used when submitting a batch of settlements to indicate the position within the batch.
657
760
  - **Signature**: Must be valid and verifiable by the solver backend.
658
761
 
659
- ## 5. PMM Making Payment
762
+ ### 4.3. Endpoint: `/v1/market-maker/trades/:tradeId`
660
763
 
661
- ```ts
662
- import { Token } from '@optimex-xyz/market-maker-sdk'
764
+ #### Description
663
765
 
664
- export interface TransferParams {
665
- toAddress: string
666
- amount: bigint
667
- token: Token
668
- tradeId: string
669
- }
766
+ Returns detailed information about a specific trade by its trade ID. This endpoint allows PMMs to fetch comprehensive data about a trade, including token information, user addresses, quotes, settlement details, and current state.
670
767
 
671
- export interface ITransferStrategy {
672
- transfer(params: TransferParams): Promise<string>
673
- }
674
- ```
768
+ #### Request Parameters
675
769
 
676
- ### 5.1. EVM
770
+ - **HTTP Method**: `GET`
771
+ - **Path Parameters**:
772
+ - `tradeId` (string): The unique identifier for the trade to retrieve.
677
773
 
678
- 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.
774
+ #### Example Request
679
775
 
680
- Example implementation:
776
+ ```
777
+ GET /v1/market-maker/trades/0x650e2c921a85eb0b8831ff838d4d98c0a5cd2ede5c0cb6bb4a15969f51c75424
778
+ ```
681
779
 
682
- ```ts
683
- import { config, ensureHexPrefix, ERC20__factory, Payment__factory, routerService } from '@optimex-xyz/market-maker-sdk'
780
+ #### Expected Response
684
781
 
685
- import { ITransferStrategy, TransferParams } from '../interfaces/transfer-strategy.interface'
782
+ - **HTTP Status**: `200 OK`
783
+ - **Response Body** (JSON): See the detailed response format in the updated PMM Integration API Documentation.
686
784
 
687
- @Injectable()
688
- export class EVMTransferStrategy implements ITransferStrategy {
689
- private pmmPrivateKey: string
785
+ ## 5. PMM Making Payment
690
786
 
691
- private routerService = routerService
692
- private readonly rpcMap = new Map<string, string>([['ethereum_sepolia', 'https://eth-sepolia.public.blastapi.io']])
787
+ ### 5.1. EVM
693
788
 
694
- constructor(private configService: ConfigService) {
695
- this.pmmPrivateKey = this.configService.getOrThrow<string>('PMM_EVM_PRIVATE_KEY')
696
- }
789
+ 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.
697
790
 
698
- async transfer(params: TransferParams): Promise<string> {
699
- const { toAddress, amount, token, tradeId } = params
700
- const { tokenAddress, networkId } = token
791
+ Example implementation:
701
792
 
702
- const signer = this.getSigner(networkId)
793
+ ```js
794
+ const { ethers } = require('ethers');
703
795
 
704
- const paymentAddress = this.getPaymentAddress(networkId)
796
+ async function makeEVMPayment(tradeId, toAddress, amount, token, protocolFeeAmount) {
797
+ try {
798
+ // Get the private key from your secure storage
799
+ const privateKey = process.env.PMM_EVM_PRIVATE_KEY;
705
800
 
706
- if (tokenAddress !== 'native') {
707
- // allowance with ERC20
708
- }
801
+ // Set up the provider and signer
802
+ const rpcUrl = getRpcUrlForNetwork(token.networkId);
803
+ const provider = new ethers.JsonRpcProvider(rpcUrl);
804
+ const signer = new ethers.Wallet(privateKey, provider);
709
805
 
710
- const paymentContract = Payment__factory.connect(paymentAddress, signer)
806
+ // Get the payment contract address
807
+ const paymentAddress = getPaymentAddressForNetwork(token.networkId);
711
808
 
712
- const feeDetail = await this.routerService.getFeeDetails(tradeId)
809
+ // Create the contract instance
810
+ const paymentAbi = [
811
+ // ABI for the payment contract
812
+ "function payment(bytes32 tradeId, address token, address recipient, uint256 amount, uint256 feeAmount, uint256 deadline) payable returns (bool)"
813
+ ];
814
+ const paymentContract = new ethers.Contract(paymentAddress, paymentAbi, signer);
713
815
 
714
- const deadline = BigInt(Math.floor(Date.now() / 1000) + 30 * 60)
816
+ // Calculate the deadline (30 minutes from now)
817
+ const deadline = Math.floor(Date.now() / 1000) + 30 * 60;
715
818
 
716
- const decoder = errorDecoder()
819
+ // If the token is native, we need to set the value
820
+ const value = token.tokenAddress === 'native' ? amount : 0;
821
+ const tokenAddress = token.tokenAddress === 'native' ? ethers.ZeroAddress : token.tokenAddress;
717
822
 
823
+ // Submit the transaction
718
824
  const tx = await paymentContract.payment(
719
825
  tradeId,
720
- tokenAddress === 'native' ? ZeroAddress : tokenAddress,
826
+ tokenAddress,
721
827
  toAddress,
722
828
  amount,
723
- feeDetail.totalAmount,
829
+ protocolFeeAmount,
724
830
  deadline,
725
- {
726
- value: tokenAddress === 'native' ? amount : 0n,
727
- }
728
- )
729
-
730
- this.logger.log(`Transfer transaction sent: ${tx.hash}`)
731
-
732
- return ensureHexPrefix(tx.hash)
733
- }
734
-
735
- private getSigner(networkId: string) {
736
- const rpcUrl = this.rpcMap.get(networkId)
831
+ { value }
832
+ );
737
833
 
738
- if (!rpcUrl) {
739
- throw new Error(`Unsupported networkId: ${networkId}`)
740
- }
741
-
742
- const provider = new ethers.JsonRpcProvider(rpcUrl)
743
- return new ethers.Wallet(this.pmmPrivateKey, provider)
744
- }
834
+ console.log(`Transfer transaction sent: ${tx.hash}`);
745
835
 
746
- private getPaymentAddress(networkId: string) {
747
- const paymentAddress = config.getPaymentAddress(networkId)
748
- if (!paymentAddress) {
749
- throw new Error(`Unsupported networkId: ${networkId}`)
750
- }
751
-
752
- return paymentAddress
836
+ // Return the transaction hash with the 0x prefix
837
+ return `0x${tx.hash.replace(/^0x/, '')}`;
838
+ } catch (error) {
839
+ console.error('EVM payment error:', error);
840
+ throw error;
753
841
  }
754
842
  }
755
843
  ```
@@ -760,177 +848,4 @@ In case the target chain is Bitcoin, the transaction should have at least N + 1
760
848
 
761
849
  Example implementation:
762
850
 
763
- ```ts
764
- import * as bitcoin from 'bitcoinjs-lib'
765
- import { ECPairFactory } from 'ecpair'
766
- import * as ecc from 'tiny-secp256k1'
767
-
768
- import { getTradeIdsHash, Token } from '@optimex-xyz/market-maker-sdk'
769
-
770
- import { ITransferStrategy, TransferParams } from '../interfaces/transfer-strategy.interface'
771
-
772
- interface UTXO {
773
- txid: string
774
- vout: number
775
- value: number
776
- status: {
777
- confirmed: boolean
778
- block_height: number
779
- block_hash: string
780
- block_time: number
781
- }
782
- }
783
-
784
- @Injectable()
785
- export class BTCTransferStrategy implements ITransferStrategy {
786
- private readonly privateKey: string
787
- private readonly ECPair = ECPairFactory(ecc)
788
-
789
- private readonly networkMap = new Map<string, bitcoin.Network>([
790
- ['bitcoin_testnet', bitcoin.networks.testnet],
791
- ['bitcoin', bitcoin.networks.bitcoin],
792
- ])
793
-
794
- private readonly rpcMap = new Map<string, string>([
795
- ['bitcoin_testnet', 'https://blockstream.info/testnet'],
796
- ['bitcoin', 'https://blockstream.info'],
797
- ])
798
-
799
- constructor(private configService: ConfigService) {
800
- this.privateKey = this.configService.getOrThrow<string>('PMM_BTC_PRIVATE_KEY')
801
- bitcoin.initEccLib(ecc)
802
- }
803
-
804
- async transfer(params: TransferParams): Promise<string> {
805
- const { toAddress, amount, token, tradeId } = params
806
-
807
- const network = this.getNetwork(token.networkId)
808
- const rpcUrl = this.getRpcUrl(token.networkId)
809
-
810
- const txId = await this.sendBTC(this.privateKey, toAddress, amount, network, rpcUrl, token, [tradeId])
811
-
812
- return ensureHexPrefix(txId)
813
- }
814
-
815
- private createPayment(publicKey: Uint8Array, network: bitcoin.Network) {
816
- const p2tr = bitcoin.payments.p2tr({
817
- internalPubkey: Buffer.from(publicKey.slice(1, 33)),
818
- network,
819
- })
820
-
821
- return {
822
- payment: p2tr,
823
- keypair: this.ECPair.fromWIF(this.privateKey, network),
824
- }
825
- }
826
-
827
- private async sendBTC(
828
- privateKey: string,
829
- toAddress: string,
830
- amountInSatoshis: bigint,
831
- network: bitcoin.Network,
832
- rpcUrl: string,
833
- token: Token,
834
- tradeIds: string[]
835
- ): Promise<string> {
836
- const keyPair = this.ECPair.fromWIF(privateKey, network)
837
- const { payment, keypair } = this.createPayment(keyPair.publicKey, network)
838
-
839
- if (!payment.address) {
840
- throw new Error('Could not generate address')
841
- }
842
-
843
- const utxos = await this.getUTXOs(payment.address, rpcUrl)
844
- if (utxos.length === 0) {
845
- throw new Error(`No UTXOs found in ${token.networkSymbol} wallet`)
846
- }
847
-
848
- const psbt = new bitcoin.Psbt({ network })
849
- let totalInput = 0n
850
-
851
- for (const utxo of utxos) {
852
- if (!payment.output) {
853
- throw new Error('Could not generate output script')
854
- }
855
-
856
- const internalKey = Buffer.from(keypair.publicKey.slice(1, 33))
857
-
858
- psbt.addInput({
859
- hash: utxo.txid,
860
- index: utxo.vout,
861
- witnessUtxo: {
862
- script: payment.output,
863
- value: BigInt(utxo.value),
864
- },
865
- tapInternalKey: internalKey,
866
- })
867
-
868
- totalInput += BigInt(utxo.value)
869
- }
870
-
871
- this.logger.log(`Total input: ${totalInput.toString()} ${token.tokenSymbol}`)
872
-
873
- if (totalInput < amountInSatoshis) {
874
- throw new Error(
875
- `Insufficient balance in ${token.networkSymbol} wallet. ` +
876
- `Need ${amountInSatoshis} satoshis, but only have ${totalInput} satoshis`
877
- )
878
- }
879
-
880
- const feeRate = await this.getFeeRate(rpcUrl)
881
- const fee = BigInt(Math.ceil(200 * feeRate))
882
- const changeAmount = totalInput - amountInSatoshis - fee
883
-
884
- psbt.addOutput({
885
- address: toAddress,
886
- value: amountInSatoshis,
887
- })
888
-
889
- if (changeAmount > 546n) {
890
- psbt.addOutput({
891
- address: payment.address,
892
- value: changeAmount,
893
- })
894
- }
895
-
896
- const tradeIdsHash = getTradeIdsHash(tradeIds)
897
-
898
- // Add OP_RETURN output with tradeIds hash
899
- psbt.addOutput({
900
- script: bitcoin.script.compile([bitcoin.opcodes['OP_RETURN'], Buffer.from(tradeIdsHash.slice(2), 'hex')]),
901
- value: 0n,
902
- })
903
-
904
- const toXOnly = (pubKey: Uint8Array) => (pubKey.length === 32 ? pubKey : pubKey.slice(1, 33))
905
- const tweakedSigner = keyPair.tweak(bitcoin.crypto.taggedHash('TapTweak', toXOnly(keyPair.publicKey)))
906
-
907
- for (let i = 0; i < psbt.data.inputs.length; i++) {
908
- psbt.signInput(i, tweakedSigner, [bitcoin.Transaction.SIGHASH_DEFAULT])
909
- this.logger.log(`Input ${i} signed successfully`)
910
- }
911
-
912
- psbt.finalizeAllInputs()
913
-
914
- const tx = psbt.extractTransaction()
915
- const rawTx = tx.toHex()
916
-
917
- const response = await axios.post(`${rpcUrl}/api/tx`, rawTx, {
918
- headers: {
919
- 'Content-Type': 'text/plain',
920
- },
921
- })
922
-
923
- return response.data
924
- }
925
-
926
- private async getUTXOs(address: string, rpcUrl: string): Promise<UTXO[]> {
927
- const response = await axios.get<UTXO[]>(`${rpcUrl}/api/address/${address}/utxo`)
928
- return response.data
929
- }
930
-
931
- private async getFeeRate(rpcUrl: string): Promise<number> {
932
- try {
933
- const response = await axios.get<{ [key: string]: number }>(`${rpcUrl}/api/fee-estimates`)
934
- return response.data[0]
935
- } catch (error) {
936
- console.error(`Error fetching
851
+ ```js
package/dist/index.js CHANGED
@@ -80,7 +80,7 @@ var environments = {
80
80
  }
81
81
  },
82
82
  production: {
83
- backendUrl: "https://api.optimex.cc",
83
+ backendUrl: "https://api.optimex.xyz",
84
84
  rpcUrl: "https://bitfi-ledger-testnet.alt.technology",
85
85
  routerAddress: "0x272599CE3602A49B580A5C4a4d3C1067E30248D2",
86
86
  paymentAddressMap: {
@@ -3757,7 +3757,7 @@ var SolverService = class {
3757
3757
  SubmitSettlementRequestSchema.parse(params);
3758
3758
  const snakeCaseParams = convertToSnakeCase(params);
3759
3759
  const response = await import_axios.default.post(
3760
- `${this.baseURL}/market-maker/submit-settlement-tx`,
3760
+ `${this.baseURL}/v1/market-maker/submit-settlement-tx`,
3761
3761
  snakeCaseParams,
3762
3762
  {
3763
3763
  headers: {
@@ -3785,7 +3785,7 @@ var SolverService = class {
3785
3785
  */
3786
3786
  async getTradeDetail(tradeId) {
3787
3787
  try {
3788
- const response = await import_axios.default.get(`${this.baseURL}/market-maker/trades/${tradeId}`, {
3788
+ const response = await import_axios.default.get(`${this.baseURL}/v1/market-maker/trades/${tradeId}`, {
3789
3789
  headers: {
3790
3790
  "Content-Type": "application/json",
3791
3791
  Accept: "application/json"
@@ -3852,7 +3852,7 @@ var TokenService = class {
3852
3852
  */
3853
3853
  async getTokens() {
3854
3854
  try {
3855
- const response = await import_axios2.default.get(`${this.baseURL}/market-maker/tokens`, {
3855
+ const response = await import_axios2.default.get(`${this.baseURL}/v1/market-maker/tokens`, {
3856
3856
  headers: {
3857
3857
  Accept: "application/json"
3858
3858
  }
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ var environments = {
15
15
  }
16
16
  },
17
17
  production: {
18
- backendUrl: "https://api.optimex.cc",
18
+ backendUrl: "https://api.optimex.xyz",
19
19
  rpcUrl: "https://bitfi-ledger-testnet.alt.technology",
20
20
  routerAddress: "0x272599CE3602A49B580A5C4a4d3C1067E30248D2",
21
21
  paymentAddressMap: {
@@ -3692,7 +3692,7 @@ var SolverService = class {
3692
3692
  SubmitSettlementRequestSchema.parse(params);
3693
3693
  const snakeCaseParams = convertToSnakeCase(params);
3694
3694
  const response = await axios.post(
3695
- `${this.baseURL}/market-maker/submit-settlement-tx`,
3695
+ `${this.baseURL}/v1/market-maker/submit-settlement-tx`,
3696
3696
  snakeCaseParams,
3697
3697
  {
3698
3698
  headers: {
@@ -3720,7 +3720,7 @@ var SolverService = class {
3720
3720
  */
3721
3721
  async getTradeDetail(tradeId) {
3722
3722
  try {
3723
- const response = await axios.get(`${this.baseURL}/market-maker/trades/${tradeId}`, {
3723
+ const response = await axios.get(`${this.baseURL}/v1/market-maker/trades/${tradeId}`, {
3724
3724
  headers: {
3725
3725
  "Content-Type": "application/json",
3726
3726
  Accept: "application/json"
@@ -3787,7 +3787,7 @@ var TokenService = class {
3787
3787
  */
3788
3788
  async getTokens() {
3789
3789
  try {
3790
- const response = await axios2.get(`${this.baseURL}/market-maker/tokens`, {
3790
+ const response = await axios2.get(`${this.baseURL}/v1/market-maker/tokens`, {
3791
3791
  headers: {
3792
3792
  Accept: "application/json"
3793
3793
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimex-xyz/market-maker-sdk",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"