@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.
- package/README.md +387 -213
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# PMM API Integration Documentation
|
|
2
2
|
|
|
3
|
-
> **CHANGELOG (v0.
|
|
3
|
+
> **CHANGELOG (v0.8.0)**:
|
|
4
|
+
>
|
|
4
5
|
> - **Breaking Changes:**
|
|
5
|
-
> -
|
|
6
|
-
> -
|
|
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
|
|
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
|
-
- [
|
|
19
|
-
- [
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
80
|
-
|
|
|
81
|
-
| `dev`
|
|
82
|
-
| `staging`
|
|
83
|
-
| `prelive`
|
|
84
|
-
| `production`
|
|
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**: [
|
|
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**: [
|
|
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: `/
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
-
|
|
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
|
-
|
|
964
|
-
|
|
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
|
```
|