@morpho-dev/router 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/drizzle/offers_v1.1/0000_init.sql +95 -0
- package/dist/drizzle/offers_v1.1/0001_new_table_for_collectors_block_numbers.sql +5 -0
- package/dist/drizzle/offers_v1.1/0002_update-liquidity-tables.sql +8 -0
- package/dist/drizzle/offers_v1.1/0003_add-not-null-for-queue-id.sql +1 -0
- package/dist/drizzle/offers_v1.1/0004_add-callback-id-to-offer.sql +1 -0
- package/dist/drizzle/offers_v1.1/0005_add-missing-indices-to-liquidity-tables.sql +2 -0
- package/dist/drizzle/offers_v1.1/meta/0000_snapshot.json +827 -0
- package/dist/drizzle/offers_v1.1/meta/0001_snapshot.json +827 -0
- package/dist/drizzle/offers_v1.1/meta/0002_snapshot.json +833 -0
- package/dist/drizzle/offers_v1.1/meta/0003_snapshot.json +833 -0
- package/dist/drizzle/offers_v1.1/meta/0004_snapshot.json +839 -0
- package/dist/drizzle/offers_v1.1/meta/0005_snapshot.json +877 -0
- package/dist/drizzle/offers_v1.1/meta/_journal.json +48 -0
- package/dist/index.browser.d.cts +1 -114
- package/dist/index.browser.d.ts +1 -114
- package/dist/index.browser.js +0 -410
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +2 -411
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +1352 -212
- package/dist/index.node.d.ts +1352 -212
- package/dist/index.node.js +2208 -1002
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +2204 -1008
- package/dist/index.node.mjs.map +1 -1
- package/package.json +10 -2
package/dist/index.node.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var mempool = require('@morpho-dev/mempool');
|
|
4
|
-
var
|
|
4
|
+
var viem = require('viem');
|
|
5
|
+
var actions = require('viem/actions');
|
|
6
|
+
var async_hooks = require('async_hooks');
|
|
5
7
|
var v4 = require('zod/v4');
|
|
8
|
+
var drizzleOrm = require('drizzle-orm');
|
|
9
|
+
var pgCore = require('drizzle-orm/pg-core');
|
|
10
|
+
var jsBase64 = require('js-base64');
|
|
6
11
|
var zodOpenapi = require('zod-openapi');
|
|
7
|
-
var viem = require('viem');
|
|
8
12
|
var nodeServer = require('@hono/node-server');
|
|
9
13
|
var hono = require('hono');
|
|
10
|
-
var
|
|
14
|
+
var path = require('path');
|
|
15
|
+
var pglite = require('@electric-sql/pglite');
|
|
16
|
+
var nodePostgres = require('drizzle-orm/node-postgres');
|
|
17
|
+
var migrator = require('drizzle-orm/node-postgres/migrator');
|
|
18
|
+
var pglite$1 = require('drizzle-orm/pglite');
|
|
19
|
+
var migrator$1 = require('drizzle-orm/pglite/migrator');
|
|
20
|
+
var pg = require('pg');
|
|
21
|
+
|
|
22
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
|
+
|
|
24
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
11
25
|
|
|
12
26
|
var __defProp = Object.defineProperty;
|
|
13
27
|
var __export = (target, all) => {
|
|
@@ -27,7 +41,7 @@ var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
|
27
41
|
return CallbackType2;
|
|
28
42
|
})(CallbackType || {});
|
|
29
43
|
function buildLiquidity(parameters) {
|
|
30
|
-
const { type, user, contract, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
44
|
+
const { type, user, contract, chainId, amount, index: index2 = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
31
45
|
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
32
46
|
throw new Error(`CallbackType not implemented: ${type}`);
|
|
33
47
|
const amountStr = amount.toString();
|
|
@@ -46,7 +60,7 @@ function buildLiquidity(parameters) {
|
|
|
46
60
|
queue: {
|
|
47
61
|
queueId: id,
|
|
48
62
|
availableLiquidityPoolId: id,
|
|
49
|
-
index,
|
|
63
|
+
index: index2,
|
|
50
64
|
updatedAt
|
|
51
65
|
},
|
|
52
66
|
pool: {
|
|
@@ -68,101 +82,436 @@ function getCallbackIdForOffer(offer) {
|
|
|
68
82
|
return null;
|
|
69
83
|
}
|
|
70
84
|
|
|
71
|
-
// src/
|
|
72
|
-
var
|
|
73
|
-
__export(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
// src/Collector/index.ts
|
|
86
|
+
var Collector_exports = {};
|
|
87
|
+
__export(Collector_exports, {
|
|
88
|
+
createBuyWithEmptyCallbackLiquidityCollector: () => createBuyWithEmptyCallbackLiquidityCollector,
|
|
89
|
+
createChainReorgsCollector: () => createChainReorgsCollector,
|
|
90
|
+
createConsumedEventsCollector: () => createConsumedEventsCollector,
|
|
91
|
+
createMempoolCollector: () => createMempoolCollector,
|
|
92
|
+
start: () => start
|
|
77
93
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
94
|
+
|
|
95
|
+
// src/Liquidity.ts
|
|
96
|
+
var Liquidity_exports = {};
|
|
97
|
+
__export(Liquidity_exports, {
|
|
98
|
+
fetch: () => fetch2,
|
|
99
|
+
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
100
|
+
serialize: () => serialize
|
|
101
|
+
});
|
|
102
|
+
async function fetchBalancesAndAllowances(parameters) {
|
|
103
|
+
const { client, spender, pairs, options } = parameters;
|
|
104
|
+
if (pairs.length === 0) return /* @__PURE__ */ new Map();
|
|
105
|
+
const batchSize = Math.max(1, options?.batchSize ?? 5e3);
|
|
106
|
+
const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
|
|
107
|
+
const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
|
|
108
|
+
const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
|
|
109
|
+
const out = /* @__PURE__ */ new Map();
|
|
110
|
+
for (const pairsBatch of mempool.Utils.batch(pairs, batchSize)) {
|
|
111
|
+
const balanceContracts = [];
|
|
112
|
+
const allowanceContracts = [];
|
|
113
|
+
for (const { user, token } of pairsBatch) {
|
|
114
|
+
balanceContracts.push({
|
|
115
|
+
address: token,
|
|
116
|
+
abi: viem.erc20Abi,
|
|
117
|
+
functionName: "balanceOf",
|
|
118
|
+
args: [user]
|
|
119
|
+
});
|
|
120
|
+
allowanceContracts.push({
|
|
121
|
+
address: token,
|
|
122
|
+
abi: viem.erc20Abi,
|
|
123
|
+
functionName: "allowance",
|
|
124
|
+
args: [user, spender]
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const [balances, allowances] = await Promise.all([
|
|
128
|
+
mempool.Utils.retry(
|
|
129
|
+
() => client.multicall({
|
|
130
|
+
allowFailure: false,
|
|
131
|
+
contracts: balanceContracts,
|
|
132
|
+
...blockNumber ? { blockNumber } : {}
|
|
133
|
+
}),
|
|
134
|
+
retryAttempts,
|
|
135
|
+
retryDelayMs
|
|
136
|
+
),
|
|
137
|
+
mempool.Utils.retry(
|
|
138
|
+
() => client.multicall({
|
|
139
|
+
allowFailure: false,
|
|
140
|
+
contracts: allowanceContracts,
|
|
141
|
+
...blockNumber ? { blockNumber } : {}
|
|
142
|
+
}),
|
|
143
|
+
retryAttempts,
|
|
144
|
+
retryDelayMs
|
|
145
|
+
)
|
|
146
|
+
]);
|
|
147
|
+
for (let i = 0; i < pairsBatch.length; i++) {
|
|
148
|
+
const { user, token } = pairsBatch[i];
|
|
149
|
+
const balance = balances[i];
|
|
150
|
+
const allowance = allowances[i];
|
|
151
|
+
let perUser = out.get(user);
|
|
152
|
+
if (!perUser) {
|
|
153
|
+
perUser = /* @__PURE__ */ new Map();
|
|
154
|
+
out.set(user, perUser);
|
|
155
|
+
}
|
|
156
|
+
perUser.set(token, { balance, allowance });
|
|
157
|
+
}
|
|
90
158
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
async function fetch2(parameters) {
|
|
162
|
+
const { client, chainId, spender, type, pairs, options } = parameters;
|
|
163
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
164
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
165
|
+
const map = await fetchBalancesAndAllowances({
|
|
166
|
+
client,
|
|
167
|
+
spender,
|
|
168
|
+
pairs: pairs.map(({ user, contract }) => ({ user, token: contract })),
|
|
169
|
+
options
|
|
170
|
+
});
|
|
171
|
+
const out = [];
|
|
172
|
+
for (const [user, perContract] of map) {
|
|
173
|
+
for (const [contract, { balance, allowance }] of perContract) {
|
|
174
|
+
const amount = balance < allowance ? balance : allowance;
|
|
175
|
+
out.push(
|
|
176
|
+
buildLiquidity({
|
|
177
|
+
type,
|
|
178
|
+
user,
|
|
179
|
+
contract,
|
|
180
|
+
chainId,
|
|
181
|
+
amount: amount.toString(),
|
|
182
|
+
index: 0
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
}
|
|
95
186
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
pattern: /^\d+$/,
|
|
107
|
-
error: "numeric string"
|
|
108
|
-
},
|
|
109
|
-
maturity: {
|
|
110
|
-
field: "maturity",
|
|
111
|
-
type: "number",
|
|
112
|
-
validator: (val) => val > 0,
|
|
113
|
-
error: "positive number"
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
function serialize(liquidity) {
|
|
190
|
+
const normalized = {
|
|
191
|
+
userPosition: {
|
|
192
|
+
id: liquidity.userPosition.id,
|
|
193
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
194
|
+
user: liquidity.userPosition.user,
|
|
195
|
+
chainId: String(liquidity.userPosition.chainId),
|
|
196
|
+
amount: String(liquidity.userPosition.amount)
|
|
114
197
|
},
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
198
|
+
queues: liquidity.queues.map((queueWithPool) => ({
|
|
199
|
+
queue: {
|
|
200
|
+
queueId: queueWithPool.queue.queueId,
|
|
201
|
+
availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
|
|
202
|
+
index: queueWithPool.queue.index
|
|
203
|
+
},
|
|
204
|
+
pool: {
|
|
205
|
+
id: queueWithPool.pool.id,
|
|
206
|
+
amount: String(queueWithPool.pool.amount)
|
|
207
|
+
}
|
|
208
|
+
})).sort(
|
|
209
|
+
(left, right) => {
|
|
210
|
+
const leftQueueId = left.queue.queueId || "";
|
|
211
|
+
const rightQueueId = right.queue.queueId || "";
|
|
212
|
+
if (leftQueueId < rightQueueId) return -1;
|
|
213
|
+
if (leftQueueId > rightQueueId) return 1;
|
|
214
|
+
const leftPoolId = left.pool.id;
|
|
215
|
+
const rightPoolId = right.pool.id;
|
|
216
|
+
if (leftPoolId < rightPoolId) return -1;
|
|
217
|
+
if (leftPoolId > rightPoolId) return 1;
|
|
218
|
+
const leftIndex = left.queue.index;
|
|
219
|
+
const rightIndex = right.queue.index;
|
|
220
|
+
if (leftIndex < rightIndex) return -1;
|
|
221
|
+
if (leftIndex > rightIndex) return 1;
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
)
|
|
121
225
|
};
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
226
|
+
return JSON.stringify(normalized);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/Collector/BuyWithEmptyCallbackLiquidityCollector.ts
|
|
230
|
+
function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
231
|
+
const {
|
|
232
|
+
client,
|
|
233
|
+
collectorBlockStore,
|
|
234
|
+
liquidityStore,
|
|
235
|
+
offerStore,
|
|
236
|
+
chain,
|
|
237
|
+
options: { maxBatchSize, interval } = {}
|
|
238
|
+
} = params;
|
|
239
|
+
const collectorName = "buy_with_empty_callback_liquidity";
|
|
240
|
+
async function getUniqueUserTokenPairs() {
|
|
241
|
+
const unique = /* @__PURE__ */ new Set();
|
|
242
|
+
const pairs = [];
|
|
243
|
+
let cursor;
|
|
244
|
+
do {
|
|
245
|
+
const { offers: offers2, nextCursor } = await offerStore.getAll({
|
|
246
|
+
query: {
|
|
247
|
+
side: "buy",
|
|
248
|
+
chains: [Number(chain.id)],
|
|
249
|
+
cursor,
|
|
250
|
+
limit: 1e3
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
cursor = nextCursor ?? void 0;
|
|
254
|
+
for (const offer of offers2) {
|
|
255
|
+
const creator = offer.offering;
|
|
256
|
+
const loanToken = offer.loanToken;
|
|
257
|
+
const key = `${offer.offering}-${offer.loanToken}`.toLowerCase();
|
|
258
|
+
if (unique.has(key)) continue;
|
|
259
|
+
unique.add(key);
|
|
260
|
+
pairs.push({ user: creator, token: loanToken });
|
|
261
|
+
}
|
|
262
|
+
} while (cursor);
|
|
263
|
+
return pairs;
|
|
264
|
+
}
|
|
265
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
266
|
+
return mempool.Utils.poll(
|
|
267
|
+
async () => {
|
|
268
|
+
const publicClient = client.extend(viem.publicActions);
|
|
269
|
+
const pairs = await getUniqueUserTokenPairs();
|
|
270
|
+
const latestBlockNumber = await actions.getBlockNumber(publicClient);
|
|
271
|
+
const liquidityEntries = await fetch2({
|
|
272
|
+
client: publicClient,
|
|
273
|
+
chainId: chain.id,
|
|
274
|
+
spender: chain.morpho,
|
|
275
|
+
type: "buy_with_empty_callback" /* BuyWithEmptyCallback */,
|
|
276
|
+
pairs: pairs.map(({ user, token }) => ({ user, contract: token })),
|
|
277
|
+
options: {
|
|
278
|
+
blockNumber: Number(latestBlockNumber),
|
|
279
|
+
batchSize: maxBatchSize
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
const existingLiquidityEntries = await liquidityStore.getAll();
|
|
283
|
+
const existingById = /* @__PURE__ */ new Map();
|
|
284
|
+
for (const liquidityEntry of existingLiquidityEntries) {
|
|
285
|
+
const liquidityId = liquidityEntry.userPosition.id;
|
|
286
|
+
const serializedEntry = serialize(liquidityEntry);
|
|
287
|
+
existingById.set(liquidityId, serializedEntry);
|
|
288
|
+
}
|
|
289
|
+
for (const liquidity of liquidityEntries) {
|
|
290
|
+
const serialized = serialize(liquidity);
|
|
291
|
+
const prevSerialized = existingById.get(liquidity.userPosition.id);
|
|
292
|
+
if (prevSerialized === serialized) continue;
|
|
293
|
+
await liquidityStore.save({ liquidity });
|
|
294
|
+
}
|
|
295
|
+
await collectorBlockStore.saveBlockNumber({
|
|
296
|
+
collectorName,
|
|
297
|
+
chainId: chain.id,
|
|
298
|
+
blockNumber: Number(latestBlockNumber)
|
|
299
|
+
});
|
|
300
|
+
emit(Number(latestBlockNumber));
|
|
301
|
+
},
|
|
302
|
+
{ interval: interval ?? 3e4 }
|
|
138
303
|
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
304
|
+
});
|
|
305
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
306
|
+
};
|
|
307
|
+
return {
|
|
308
|
+
name: collectorName,
|
|
309
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
310
|
+
collect,
|
|
311
|
+
onReorg
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function createChainReorgsCollector(parameters) {
|
|
315
|
+
const collectorName = "chain_reorgs";
|
|
316
|
+
const {
|
|
317
|
+
client,
|
|
318
|
+
subscribers,
|
|
319
|
+
collectorStore,
|
|
320
|
+
chain,
|
|
321
|
+
options: { maxBatchSize = 25, interval } = {}
|
|
322
|
+
} = parameters;
|
|
323
|
+
let finalizedBlock = null;
|
|
324
|
+
let unfinalizedBlocks = [];
|
|
325
|
+
const commonAncestor = (block) => {
|
|
326
|
+
const parent = unfinalizedBlocks.find((b) => b.hash === block.parentHash);
|
|
327
|
+
if (parent) return parent;
|
|
328
|
+
if (finalizedBlock == null) throw new Error("Failed to get common ancestor");
|
|
329
|
+
return finalizedBlock;
|
|
330
|
+
};
|
|
331
|
+
const reconcile = async (block) => {
|
|
332
|
+
if (block.hash === null || block.number === null || block.parentHash === null)
|
|
333
|
+
throw new Error("Failed to get block");
|
|
334
|
+
const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
|
|
335
|
+
if (latestBlock === void 0) {
|
|
336
|
+
unfinalizedBlocks.push({
|
|
337
|
+
hash: block.hash,
|
|
338
|
+
number: block.number,
|
|
339
|
+
parentHash: block.parentHash
|
|
340
|
+
});
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (latestBlock.hash === block.hash) return;
|
|
344
|
+
if (latestBlock.number >= block.number) {
|
|
345
|
+
const ancestor = commonAncestor(block);
|
|
346
|
+
console.log(
|
|
347
|
+
`reorg detected, latestBlock.number: ${latestBlock.number} > block.number: ${block.number} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
348
|
+
);
|
|
349
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
350
|
+
unfinalizedBlocks = [];
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (latestBlock.number + 1n < block.number) {
|
|
354
|
+
console.log(
|
|
355
|
+
`missing blocks between block ${latestBlock.number} and block ${block.number} on chain ${chain.id}`
|
|
356
|
+
);
|
|
357
|
+
const missingBlockNumbers = (() => {
|
|
358
|
+
const missingBlockNumbers2 = [];
|
|
359
|
+
let start2 = latestBlock.number + 1n;
|
|
360
|
+
const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
|
|
361
|
+
while (start2 < threshold) {
|
|
362
|
+
missingBlockNumbers2.push(start2);
|
|
363
|
+
start2 = start2 + 1n;
|
|
364
|
+
}
|
|
365
|
+
return missingBlockNumbers2;
|
|
366
|
+
})();
|
|
367
|
+
const missingBlocks = await Promise.all(
|
|
368
|
+
missingBlockNumbers.map(
|
|
369
|
+
(blockNumber) => client.getBlock({ blockNumber, includeTransactions: false })
|
|
370
|
+
)
|
|
371
|
+
);
|
|
372
|
+
for (const missingBlock of missingBlocks) await reconcile(missingBlock);
|
|
373
|
+
await reconcile(block);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (block.parentHash !== latestBlock.hash) {
|
|
377
|
+
const ancestor = commonAncestor(block);
|
|
378
|
+
console.log(
|
|
379
|
+
`reorg detected, block.parentHash: ${block.parentHash} !== latestBlock.hash: ${latestBlock.hash} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
380
|
+
);
|
|
381
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
382
|
+
unfinalizedBlocks = [];
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
unfinalizedBlocks.push({
|
|
386
|
+
hash: block.hash,
|
|
387
|
+
number: block.number,
|
|
388
|
+
parentHash: block.parentHash
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
392
|
+
let tick = 0;
|
|
393
|
+
const fetchFinalizedBlock = async () => {
|
|
394
|
+
if (tick % 20 === 0 || finalizedBlock === null) {
|
|
395
|
+
finalizedBlock = await client.getBlock({
|
|
396
|
+
blockTag: "finalized",
|
|
397
|
+
includeTransactions: false
|
|
398
|
+
});
|
|
399
|
+
if (finalizedBlock === null) throw new Error("Failed to get finalized block");
|
|
400
|
+
}
|
|
401
|
+
tick++;
|
|
402
|
+
};
|
|
403
|
+
return mempool.Utils.poll(
|
|
404
|
+
async () => {
|
|
405
|
+
await fetchFinalizedBlock();
|
|
406
|
+
const latestBlock = await client.getBlock({
|
|
407
|
+
blockTag: "latest",
|
|
408
|
+
includeTransactions: false
|
|
409
|
+
});
|
|
410
|
+
await reconcile(latestBlock);
|
|
411
|
+
const lastBlockNumber = Number(latestBlock.number);
|
|
412
|
+
await collectorStore.saveBlockNumber({
|
|
413
|
+
collectorName: "chain_reorgs",
|
|
414
|
+
chainId: chain.id,
|
|
415
|
+
blockNumber: lastBlockNumber
|
|
416
|
+
});
|
|
417
|
+
emit(lastBlockNumber);
|
|
418
|
+
},
|
|
419
|
+
{ interval: interval ?? 3e4 }
|
|
143
420
|
);
|
|
144
|
-
}
|
|
145
|
-
return
|
|
421
|
+
});
|
|
422
|
+
return {
|
|
423
|
+
name: collectorName,
|
|
424
|
+
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
425
|
+
collect,
|
|
426
|
+
onReorg: (_) => {
|
|
427
|
+
}
|
|
428
|
+
};
|
|
146
429
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
430
|
+
|
|
431
|
+
// src/Collector/Collector.ts
|
|
432
|
+
function start(collector) {
|
|
433
|
+
let stopped = false;
|
|
434
|
+
const it = collector.collect();
|
|
435
|
+
(async () => {
|
|
436
|
+
while (!stopped) await it.next();
|
|
437
|
+
await it.return();
|
|
438
|
+
})();
|
|
439
|
+
return () => {
|
|
440
|
+
stopped = true;
|
|
441
|
+
};
|
|
155
442
|
}
|
|
156
443
|
|
|
157
|
-
// src/
|
|
158
|
-
var
|
|
159
|
-
__export(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
444
|
+
// src/Logger.ts
|
|
445
|
+
var Logger_exports = {};
|
|
446
|
+
__export(Logger_exports, {
|
|
447
|
+
LogLevelValues: () => LogLevelValues,
|
|
448
|
+
defaultLogger: () => defaultLogger,
|
|
449
|
+
getLogger: () => getLogger,
|
|
450
|
+
runWithLogger: () => runWithLogger,
|
|
451
|
+
silentLogger: () => silentLogger
|
|
165
452
|
});
|
|
453
|
+
var LogLevelValues = [
|
|
454
|
+
"silent",
|
|
455
|
+
"trace",
|
|
456
|
+
"debug",
|
|
457
|
+
"info",
|
|
458
|
+
"warn",
|
|
459
|
+
"error",
|
|
460
|
+
"fatal"
|
|
461
|
+
];
|
|
462
|
+
function defaultLogger(minLevel) {
|
|
463
|
+
const threshold = minLevel ?? "trace";
|
|
464
|
+
const levelIndexByName = LogLevelValues.reduce(
|
|
465
|
+
(acc, lvl, idx) => {
|
|
466
|
+
acc[lvl] = idx;
|
|
467
|
+
return acc;
|
|
468
|
+
},
|
|
469
|
+
{}
|
|
470
|
+
);
|
|
471
|
+
const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
|
|
472
|
+
return {
|
|
473
|
+
// biome-ignore lint/suspicious/noConsole: console is used for logging
|
|
474
|
+
trace: isEnabled("trace") ? console.trace.bind(console) : () => {
|
|
475
|
+
},
|
|
476
|
+
// biome-ignore lint/suspicious/noConsole: console is used for logging
|
|
477
|
+
debug: isEnabled("debug") ? console.debug.bind(console) : () => {
|
|
478
|
+
},
|
|
479
|
+
// biome-ignore lint/suspicious/noConsole: console is used for logging
|
|
480
|
+
info: isEnabled("info") ? console.info.bind(console) : () => {
|
|
481
|
+
},
|
|
482
|
+
// biome-ignore lint/suspicious/noConsole: console is used for logging
|
|
483
|
+
warn: isEnabled("warn") ? console.warn.bind(console) : () => {
|
|
484
|
+
},
|
|
485
|
+
// biome-ignore lint/suspicious/noConsole: console is used for logging
|
|
486
|
+
error: isEnabled("error") ? console.error.bind(console) : () => {
|
|
487
|
+
},
|
|
488
|
+
fatal: isEnabled("fatal") ? (...args) => console.error("[fatal]", ...args) : () => {
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function silentLogger() {
|
|
493
|
+
return {
|
|
494
|
+
trace: () => {
|
|
495
|
+
},
|
|
496
|
+
debug: () => {
|
|
497
|
+
},
|
|
498
|
+
info: () => {
|
|
499
|
+
},
|
|
500
|
+
warn: () => {
|
|
501
|
+
},
|
|
502
|
+
error: () => {
|
|
503
|
+
},
|
|
504
|
+
fatal: () => {
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
var loggerContext = new async_hooks.AsyncLocalStorage();
|
|
509
|
+
function runWithLogger(logger, fn) {
|
|
510
|
+
return loggerContext.run(logger, fn);
|
|
511
|
+
}
|
|
512
|
+
function getLogger() {
|
|
513
|
+
return loggerContext.getStore() ?? defaultLogger();
|
|
514
|
+
}
|
|
166
515
|
|
|
167
516
|
// src/RouterOffer.ts
|
|
168
517
|
var RouterOffer_exports = {};
|
|
@@ -244,154 +593,711 @@ var InvalidRouterOfferError = class extends mempool.Errors.BaseError {
|
|
|
244
593
|
}
|
|
245
594
|
};
|
|
246
595
|
|
|
247
|
-
// src/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
message: "Chains must be comma-separated chain IDs"
|
|
266
|
-
}).transform((val) => val.split(",").map(Number)).optional().meta({
|
|
267
|
-
description: "Filter by multiple blockchain networks (comma-separated chain IDs)",
|
|
268
|
-
example: "1,137,10"
|
|
269
|
-
}),
|
|
270
|
-
loan_tokens: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
271
|
-
message: "Loan assets must be comma-separated Ethereum addresses"
|
|
272
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
273
|
-
description: "Filter by multiple loan assets (comma-separated)",
|
|
274
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
275
|
-
}),
|
|
276
|
-
status: v4.z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
|
|
277
|
-
message: "Status must be comma-separated status values"
|
|
278
|
-
}).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
|
|
279
|
-
message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
|
|
280
|
-
}).optional().meta({
|
|
281
|
-
description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
|
|
282
|
-
example: "valid,callback_error"
|
|
283
|
-
}),
|
|
284
|
-
callback_addresses: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
285
|
-
message: "Callback addresses must be comma-separated Ethereum addresses"
|
|
286
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
287
|
-
description: "Filter by multiple callback addresses (comma-separated)",
|
|
288
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
289
|
-
}),
|
|
290
|
-
// Asset range
|
|
291
|
-
min_amount: v4.z.bigint({ coerce: true }).positive({
|
|
292
|
-
message: "Min amount must be a positive number"
|
|
293
|
-
}).optional().meta({
|
|
294
|
-
description: "Minimum amount of assets in the offer",
|
|
295
|
-
example: "1000"
|
|
296
|
-
}),
|
|
297
|
-
max_amount: v4.z.bigint({ coerce: true }).positive({
|
|
298
|
-
message: "Max amount must be a positive number"
|
|
299
|
-
}).optional().meta({
|
|
300
|
-
description: "Maximum amount of assets in the offer",
|
|
301
|
-
example: "10000"
|
|
302
|
-
}),
|
|
303
|
-
// Rate range
|
|
304
|
-
min_rate: v4.z.bigint({ coerce: true }).positive({
|
|
305
|
-
message: "Min rate must be a positive number"
|
|
306
|
-
}).optional().meta({
|
|
307
|
-
description: "Minimum rate per asset (in wei)",
|
|
308
|
-
example: "500000000000000000"
|
|
309
|
-
}),
|
|
310
|
-
max_rate: v4.z.bigint({ coerce: true }).positive({
|
|
311
|
-
message: "Max rate must be a positive number"
|
|
312
|
-
}).optional().meta({
|
|
313
|
-
description: "Maximum rate per asset (in wei)",
|
|
314
|
-
example: "1500000000000000000"
|
|
315
|
-
}),
|
|
316
|
-
// Time range
|
|
317
|
-
min_maturity: v4.z.coerce.number().int().min(0).optional().meta({
|
|
318
|
-
description: "Minimum maturity timestamp (Unix timestamp in seconds)",
|
|
319
|
-
example: "1700000000"
|
|
320
|
-
}),
|
|
321
|
-
max_maturity: v4.z.coerce.number().int().min(0).optional().meta({
|
|
322
|
-
description: "Maximum maturity timestamp (Unix timestamp in seconds)",
|
|
323
|
-
example: "1800000000"
|
|
324
|
-
}),
|
|
325
|
-
min_expiry: v4.z.coerce.number().int().optional().meta({
|
|
326
|
-
description: "Minimum expiry timestamp (Unix timestamp in seconds)",
|
|
327
|
-
example: "1700000000"
|
|
328
|
-
}),
|
|
329
|
-
max_expiry: v4.z.coerce.number().int().optional().meta({
|
|
330
|
-
description: "Maximum expiry timestamp (Unix timestamp in seconds)",
|
|
331
|
-
example: "1800000000"
|
|
332
|
-
}),
|
|
333
|
-
// Collateral filtering
|
|
334
|
-
collateral_assets: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
335
|
-
message: "Collateral assets must be comma-separated Ethereum addresses"
|
|
336
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
337
|
-
description: "Filter by multiple collateral assets (comma-separated)",
|
|
338
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
339
|
-
}),
|
|
340
|
-
collateral_oracles: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
341
|
-
message: "Collateral oracles must be comma-separated Ethereum addresses"
|
|
342
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
343
|
-
description: "Filter by multiple rate oracles (comma-separated)",
|
|
344
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
345
|
-
}),
|
|
346
|
-
collateral_tuple: v4.z.string().transform((val, ctx) => {
|
|
347
|
-
const pattern = /^(0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)(#0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)*$/;
|
|
348
|
-
if (!pattern.test(val)) {
|
|
349
|
-
ctx.addIssue({
|
|
350
|
-
code: "custom",
|
|
351
|
-
message: "collateral_tuple has an invalid format",
|
|
352
|
-
input: val
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
return val.split("#").map((tuple) => {
|
|
356
|
-
const parts = tuple.split(":");
|
|
357
|
-
if (parts.length === 0 || !parts[0]) {
|
|
358
|
-
ctx.addIssue({
|
|
359
|
-
code: "custom",
|
|
360
|
-
message: "Asset address is required for each collateral tuple",
|
|
361
|
-
path: ["asset"],
|
|
362
|
-
input: val
|
|
596
|
+
// src/Collector/ConsumedEventsCollector.ts
|
|
597
|
+
function createConsumedEventsCollector(parameters) {
|
|
598
|
+
const {
|
|
599
|
+
client,
|
|
600
|
+
contractAddress,
|
|
601
|
+
offerStore,
|
|
602
|
+
collectorBlockStore,
|
|
603
|
+
options: { maxBatchSize, interval } = {},
|
|
604
|
+
chainId
|
|
605
|
+
} = parameters;
|
|
606
|
+
const logger = getLogger();
|
|
607
|
+
const collectorName = "consumed_events";
|
|
608
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
609
|
+
return mempool.Utils.poll(
|
|
610
|
+
async () => {
|
|
611
|
+
const lastSyncedBlock = await collectorBlockStore.getBlockNumber({
|
|
612
|
+
collectorName,
|
|
613
|
+
chainId
|
|
363
614
|
});
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
code: "custom",
|
|
372
|
-
message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
|
|
373
|
-
path: ["lltv"],
|
|
374
|
-
input: val
|
|
615
|
+
const stream = mempool.Chain.streamLogs({
|
|
616
|
+
client,
|
|
617
|
+
contractAddress,
|
|
618
|
+
event: consumedEvent,
|
|
619
|
+
blockNumberGte: lastSyncedBlock,
|
|
620
|
+
order: "asc",
|
|
621
|
+
options: { maxBatchSize }
|
|
375
622
|
});
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
623
|
+
for await (const logs of stream) {
|
|
624
|
+
if (logs.length === 0) break;
|
|
625
|
+
const parsedLogs = viem.parseEventLogs({ abi: [consumedEvent], logs });
|
|
626
|
+
const offersConsumed = [];
|
|
627
|
+
for (const log of parsedLogs) {
|
|
628
|
+
offersConsumed.push(
|
|
629
|
+
fromConsumedLog({
|
|
630
|
+
blockNumber: log.blockNumber,
|
|
631
|
+
logIndex: log.logIndex,
|
|
632
|
+
chainId: Number(chainId),
|
|
633
|
+
transactionHash: log.transactionHash,
|
|
634
|
+
user: log.args.user,
|
|
635
|
+
nonce: log.args.nonce,
|
|
636
|
+
amount: log.args.amount
|
|
637
|
+
})
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
try {
|
|
641
|
+
for (const offerConsumed of offersConsumed) {
|
|
642
|
+
logger.debug?.("updating consumed amount", { offerConsumed });
|
|
643
|
+
await offerStore.updateConsumedAmount({
|
|
644
|
+
id: offerConsumed.id,
|
|
645
|
+
chainId: offerConsumed.chainId,
|
|
646
|
+
offering: offerConsumed.offering,
|
|
647
|
+
nonce: offerConsumed.nonce,
|
|
648
|
+
consumed: offerConsumed.amount
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
const lastBlockNumber = Number(logs[logs.length - 1]?.blockNumber);
|
|
652
|
+
await collectorBlockStore.saveBlockNumber({
|
|
653
|
+
collectorName: "consumed_events",
|
|
654
|
+
chainId,
|
|
655
|
+
blockNumber: lastBlockNumber
|
|
656
|
+
});
|
|
657
|
+
emit(lastBlockNumber);
|
|
658
|
+
} catch (err) {
|
|
659
|
+
logger.error({ err }, "Failed to process offer_consumed events");
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
{ interval: interval ?? 3e4 }
|
|
664
|
+
);
|
|
665
|
+
});
|
|
666
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
667
|
+
};
|
|
668
|
+
return {
|
|
669
|
+
name: collectorName,
|
|
670
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName, chainId }),
|
|
671
|
+
onReorg,
|
|
672
|
+
collect
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/Validation.ts
|
|
677
|
+
var Validation_exports = {};
|
|
678
|
+
__export(Validation_exports, {
|
|
679
|
+
run: () => run
|
|
680
|
+
});
|
|
681
|
+
async function run(parameters) {
|
|
682
|
+
const { items, rules, ctx = {}, chunkSize } = parameters;
|
|
683
|
+
const issues = [];
|
|
684
|
+
let validItems = items.slice();
|
|
685
|
+
for (const rule of rules) {
|
|
686
|
+
if (validItems.length === 0) return { valid: [], issues };
|
|
687
|
+
const indicesToRemove = /* @__PURE__ */ new Set();
|
|
688
|
+
if (rule.kind === "single") {
|
|
689
|
+
for (let i = 0; i < validItems.length; i++) {
|
|
690
|
+
const item = validItems[i];
|
|
691
|
+
const issue = await rule.run(item, ctx);
|
|
692
|
+
if (issue) {
|
|
693
|
+
issues.push({ ...issue, ruleName: rule.name, item });
|
|
694
|
+
indicesToRemove.add(i);
|
|
390
695
|
}
|
|
391
696
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
697
|
+
} else if (rule.kind === "batch") {
|
|
698
|
+
const exec = async (slice, offset) => {
|
|
699
|
+
const map = await rule.run(slice, ctx);
|
|
700
|
+
for (let i = 0; i < slice.length; i++) {
|
|
701
|
+
const issue = map.get(i);
|
|
702
|
+
if (issue !== void 0) {
|
|
703
|
+
issues.push({ ...issue, ruleName: rule.name, item: slice[i] });
|
|
704
|
+
indicesToRemove.add(offset + i);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
if (!chunkSize) await exec(validItems, 0);
|
|
709
|
+
else {
|
|
710
|
+
for (let i = 0; i < validItems.length; i += chunkSize) {
|
|
711
|
+
await exec(validItems.slice(i, i + chunkSize), i);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
validItems = validItems.filter((_, i) => !indicesToRemove.has(i));
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
valid: validItems,
|
|
719
|
+
issues
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// src/ValidationRule.ts
|
|
724
|
+
var ValidationRule_exports = {};
|
|
725
|
+
__export(ValidationRule_exports, {
|
|
726
|
+
batch: () => batch,
|
|
727
|
+
morpho: () => morpho,
|
|
728
|
+
single: () => single
|
|
729
|
+
});
|
|
730
|
+
function single(name, run2) {
|
|
731
|
+
return { kind: "single", name, run: run2 };
|
|
732
|
+
}
|
|
733
|
+
function batch(name, run2) {
|
|
734
|
+
return { kind: "batch", name, run: run2 };
|
|
735
|
+
}
|
|
736
|
+
function morpho() {
|
|
737
|
+
const chainId = single("chain_id", (offer, { chain }) => {
|
|
738
|
+
if (chain.id !== offer.chainId) {
|
|
739
|
+
return {
|
|
740
|
+
message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
const loanToken = single("loan_token", (offer, { chain }) => {
|
|
745
|
+
const tokens = new Set(
|
|
746
|
+
Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
|
|
747
|
+
);
|
|
748
|
+
if (!tokens.has(offer.loanToken.toLowerCase())) {
|
|
749
|
+
return {
|
|
750
|
+
message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
const expiry = single("expiry", (offer, _) => {
|
|
755
|
+
if (offer.expiry < Math.floor(Date.now() / 1e3)) {
|
|
756
|
+
return { message: "Expiry mismatch" };
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
const callback = single("empty_callback", (offer, _) => {
|
|
760
|
+
if (!offer.buy || offer.callback.data !== "0x") {
|
|
761
|
+
return { message: "Callback not supported yet." };
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
return [
|
|
765
|
+
chainId,
|
|
766
|
+
loanToken,
|
|
767
|
+
expiry,
|
|
768
|
+
// note: callback rule should be the last one, since it does not mean that the offer is forever invalid
|
|
769
|
+
// integrators should be able to choose if they want to keep the offer or not
|
|
770
|
+
callback
|
|
771
|
+
];
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/Collector/MempoolCollector.ts
|
|
775
|
+
function createMempoolCollector(parameters) {
|
|
776
|
+
const logger = getLogger();
|
|
777
|
+
const {
|
|
778
|
+
mempool: mempool$1,
|
|
779
|
+
offerStore,
|
|
780
|
+
collectorBlockStore,
|
|
781
|
+
chain,
|
|
782
|
+
options: { maxBatchSize, interval } = {}
|
|
783
|
+
} = parameters;
|
|
784
|
+
const collectorName = "mempool_offers";
|
|
785
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
786
|
+
return mempool$1.watch({
|
|
787
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
788
|
+
polling: { maxBatchSize, interval },
|
|
789
|
+
onOffers: async (offers2, blockNumber) => {
|
|
790
|
+
try {
|
|
791
|
+
const { valid: validOffers, issues } = await run({
|
|
792
|
+
items: offers2,
|
|
793
|
+
rules: morpho(),
|
|
794
|
+
ctx: { chain }
|
|
795
|
+
});
|
|
796
|
+
const invalidOffersToSave = [];
|
|
797
|
+
const issueToStatus = {
|
|
798
|
+
empty_callback: "callback_not_supported",
|
|
799
|
+
sell_offers_empty_callback: "callback_not_supported",
|
|
800
|
+
buy_offers_empty_callback: "callback_error"
|
|
801
|
+
};
|
|
802
|
+
for (const issue of issues) {
|
|
803
|
+
const status = issueToStatus[issue.ruleName];
|
|
804
|
+
if (status) {
|
|
805
|
+
invalidOffersToSave.push({
|
|
806
|
+
offer: issue.item,
|
|
807
|
+
status,
|
|
808
|
+
metadata: { issue: issue.ruleName }
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
await offerStore.createMany([
|
|
813
|
+
...validOffers.map((offer) => ({
|
|
814
|
+
offer,
|
|
815
|
+
status: "valid"
|
|
816
|
+
})),
|
|
817
|
+
...invalidOffersToSave
|
|
818
|
+
]);
|
|
819
|
+
} catch (err) {
|
|
820
|
+
logger.error(
|
|
821
|
+
{ err },
|
|
822
|
+
"Failed to process offer_created events, falling back to unverified status"
|
|
823
|
+
);
|
|
824
|
+
await offerStore.createMany(
|
|
825
|
+
offers2.map((offer) => ({
|
|
826
|
+
offer,
|
|
827
|
+
status: "unverified",
|
|
828
|
+
metadata: { issue: "Offer processing failed" }
|
|
829
|
+
}))
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
await collectorBlockStore.saveBlockNumber({
|
|
833
|
+
collectorName,
|
|
834
|
+
chainId: chain.id,
|
|
835
|
+
blockNumber
|
|
836
|
+
});
|
|
837
|
+
emit(blockNumber);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
842
|
+
};
|
|
843
|
+
return {
|
|
844
|
+
name: collectorName,
|
|
845
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
846
|
+
collect,
|
|
847
|
+
onReorg
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/CollectorBlockStore.ts
|
|
852
|
+
var CollectorBlockStore_exports = {};
|
|
853
|
+
__export(CollectorBlockStore_exports, {
|
|
854
|
+
create: () => create,
|
|
855
|
+
memory: () => memory
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
// src/OfferStore/drizzle/schema.ts
|
|
859
|
+
var schema_exports = {};
|
|
860
|
+
__export(schema_exports, {
|
|
861
|
+
VERSION: () => VERSION,
|
|
862
|
+
availableLiquidityPools: () => availableLiquidityPools,
|
|
863
|
+
availableLiquidityQueues: () => availableLiquidityQueues,
|
|
864
|
+
collectorBlockNumbers: () => collectorBlockNumbers,
|
|
865
|
+
consumed: () => consumed,
|
|
866
|
+
offerCollaterals: () => offerCollaterals,
|
|
867
|
+
offerStatus: () => offerStatus,
|
|
868
|
+
offers: () => offers,
|
|
869
|
+
userPositions: () => userPositions
|
|
870
|
+
});
|
|
871
|
+
var VERSION = "offers_v1.1";
|
|
872
|
+
var s = pgCore.pgSchema(VERSION);
|
|
873
|
+
var offers = s.table(
|
|
874
|
+
"offers",
|
|
875
|
+
{
|
|
876
|
+
hash: pgCore.varchar("hash", { length: 66 }).primaryKey(),
|
|
877
|
+
offering: pgCore.varchar("offering", { length: 42 }).notNull(),
|
|
878
|
+
assets: pgCore.numeric("assets", { precision: 78, scale: 0 }).notNull(),
|
|
879
|
+
rate: pgCore.bigint("rate", { mode: "bigint" }).notNull(),
|
|
880
|
+
maturity: pgCore.integer("maturity").notNull(),
|
|
881
|
+
expiry: pgCore.integer("expiry").notNull(),
|
|
882
|
+
start: pgCore.integer("start").notNull(),
|
|
883
|
+
nonce: pgCore.bigint("nonce", { mode: "bigint" }).notNull(),
|
|
884
|
+
buy: pgCore.boolean("buy").notNull(),
|
|
885
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
886
|
+
loanToken: pgCore.varchar("loan_token", { length: 42 }).notNull(),
|
|
887
|
+
callbackAddress: pgCore.varchar("callback_address", { length: 42 }).notNull(),
|
|
888
|
+
callbackData: pgCore.text("callback_data").notNull(),
|
|
889
|
+
callbackGasLimit: pgCore.bigint("callback_gas_limit", { mode: "bigint" }).notNull(),
|
|
890
|
+
signature: pgCore.varchar("signature", { length: 132 }),
|
|
891
|
+
callbackId: pgCore.varchar("callback_id", { length: 256 }),
|
|
892
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
893
|
+
},
|
|
894
|
+
(table) => [
|
|
895
|
+
pgCore.index("offers_offering_idx").on(table.offering),
|
|
896
|
+
pgCore.index("offers_buy_idx").on(table.buy),
|
|
897
|
+
pgCore.index("offers_chain_id_idx").on(table.chainId),
|
|
898
|
+
pgCore.index("offers_loan_token_idx").on(table.loanToken),
|
|
899
|
+
pgCore.index("offers_maturity_idx").on(table.maturity),
|
|
900
|
+
pgCore.index("offers_expiry_idx").on(table.expiry),
|
|
901
|
+
pgCore.index("offers_rate_idx").on(table.rate),
|
|
902
|
+
pgCore.index("offers_assets_idx").on(table.assets),
|
|
903
|
+
// Compound indices for cursor pagination with hash
|
|
904
|
+
pgCore.index("offers_rate_hash_idx").on(table.rate, table.hash),
|
|
905
|
+
pgCore.index("offers_maturity_hash_idx").on(table.maturity, table.hash),
|
|
906
|
+
pgCore.index("offers_expiry_hash_idx").on(table.expiry, table.hash),
|
|
907
|
+
pgCore.index("offers_assets_hash_idx").on(table.assets, table.hash)
|
|
908
|
+
]
|
|
909
|
+
);
|
|
910
|
+
var offerCollaterals = s.table(
|
|
911
|
+
"offer_collaterals",
|
|
912
|
+
{
|
|
913
|
+
id: pgCore.serial("id").primaryKey(),
|
|
914
|
+
offerHash: pgCore.varchar("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
915
|
+
asset: pgCore.varchar("asset", { length: 42 }).notNull(),
|
|
916
|
+
oracle: pgCore.varchar("oracle", { length: 42 }).notNull(),
|
|
917
|
+
lltv: pgCore.bigint("lltv", { mode: "bigint" }).notNull()
|
|
918
|
+
},
|
|
919
|
+
(table) => [
|
|
920
|
+
pgCore.index("offer_collaterals_offer_hash_idx").on(table.offerHash),
|
|
921
|
+
pgCore.index("offer_collaterals_asset_idx").on(table.asset),
|
|
922
|
+
pgCore.index("offer_collaterals_oracle_idx").on(table.oracle),
|
|
923
|
+
// Composite index
|
|
924
|
+
pgCore.index("offer_collaterals_tuple_idx").on(table.asset, table.oracle, table.lltv)
|
|
925
|
+
]
|
|
926
|
+
);
|
|
927
|
+
var offerStatus = s.table(
|
|
928
|
+
"offer_status",
|
|
929
|
+
{
|
|
930
|
+
id: pgCore.serial("id").primaryKey(),
|
|
931
|
+
offerHash: pgCore.varchar("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
932
|
+
status: pgCore.text("status").$type().notNull(),
|
|
933
|
+
metadata: pgCore.jsonb("metadata"),
|
|
934
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
935
|
+
},
|
|
936
|
+
(table) => [
|
|
937
|
+
pgCore.index("offer_status_offer_hash_created_at_idx").on(table.offerHash, drizzleOrm.desc(table.createdAt)),
|
|
938
|
+
pgCore.index("offer_status_status_idx").on(table.status)
|
|
939
|
+
]
|
|
940
|
+
);
|
|
941
|
+
var consumed = s.table(
|
|
942
|
+
"consumed_per_user_and_nonce",
|
|
943
|
+
{
|
|
944
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
945
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
946
|
+
offering: pgCore.varchar("offering", { length: 42 }).notNull(),
|
|
947
|
+
nonce: pgCore.bigint("nonce", { mode: "bigint" }).notNull(),
|
|
948
|
+
consumed: pgCore.numeric("consumed", { precision: 78, scale: 0 }).notNull(),
|
|
949
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
950
|
+
},
|
|
951
|
+
(table) => [
|
|
952
|
+
pgCore.index("consumed_per_user_and_nonce_chain_id_offering_nonce_created_at_idx").on(
|
|
953
|
+
table.chainId,
|
|
954
|
+
table.offering,
|
|
955
|
+
table.nonce,
|
|
956
|
+
drizzleOrm.desc(table.createdAt)
|
|
957
|
+
)
|
|
958
|
+
]
|
|
959
|
+
);
|
|
960
|
+
var collectorBlockNumbers = s.table(
|
|
961
|
+
"collector_block_numbers",
|
|
962
|
+
{
|
|
963
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
964
|
+
name: pgCore.text("name").notNull(),
|
|
965
|
+
blockNumber: pgCore.bigint("block_number", { mode: "number" }).notNull(),
|
|
966
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
967
|
+
},
|
|
968
|
+
(table) => [pgCore.uniqueIndex("collector_block_numbers_chain_name_idx").on(table.chainId, table.name)]
|
|
969
|
+
);
|
|
970
|
+
var availableLiquidityPools = s.table("available_liquidity_pools", {
|
|
971
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
972
|
+
amount: pgCore.numeric("amount", { precision: 78, scale: 0 }).notNull(),
|
|
973
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
974
|
+
});
|
|
975
|
+
var availableLiquidityQueues = s.table(
|
|
976
|
+
"available_liquidity_queues",
|
|
977
|
+
{
|
|
978
|
+
queueId: pgCore.varchar("queue_id", { length: 255 }).notNull(),
|
|
979
|
+
availableLiquidityPoolId: pgCore.varchar("available_liquidity_pool_id", { length: 255 }).notNull().references(() => availableLiquidityPools.id, { onDelete: "cascade" }),
|
|
980
|
+
index: pgCore.integer("index").notNull(),
|
|
981
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
982
|
+
},
|
|
983
|
+
(table) => [
|
|
984
|
+
pgCore.primaryKey({
|
|
985
|
+
columns: [table.queueId, table.availableLiquidityPoolId],
|
|
986
|
+
name: "available_liquidity_queues_pk"
|
|
987
|
+
}),
|
|
988
|
+
pgCore.index("available_liquidity_queues_queue_index_idx").on(table.queueId, table.index)
|
|
989
|
+
]
|
|
990
|
+
);
|
|
991
|
+
var userPositions = s.table(
|
|
992
|
+
"user_positions",
|
|
993
|
+
{
|
|
994
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
995
|
+
availableLiquidityQueueId: pgCore.varchar("available_liquidity_queue_id", { length: 255 }).notNull(),
|
|
996
|
+
user: pgCore.varchar("user", { length: 255 }).notNull(),
|
|
997
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
998
|
+
amount: pgCore.numeric("amount", { precision: 78, scale: 0 }).notNull(),
|
|
999
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1000
|
+
},
|
|
1001
|
+
(table) => [
|
|
1002
|
+
pgCore.index("user_positions_available_liquidity_queue_id_idx").on(table.availableLiquidityQueueId)
|
|
1003
|
+
]
|
|
1004
|
+
);
|
|
1005
|
+
|
|
1006
|
+
// src/CollectorBlockStore.ts
|
|
1007
|
+
var create = (config) => {
|
|
1008
|
+
const db = config.db;
|
|
1009
|
+
return {
|
|
1010
|
+
getBlockNumber: async (parameters) => {
|
|
1011
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1012
|
+
const result = await db.select({ blockNumber: collectorBlockNumbers.blockNumber }).from(collectorBlockNumbers).where(
|
|
1013
|
+
drizzleOrm.and(
|
|
1014
|
+
drizzleOrm.eq(collectorBlockNumbers.name, name),
|
|
1015
|
+
drizzleOrm.eq(collectorBlockNumbers.chainId, parameters.chainId)
|
|
1016
|
+
)
|
|
1017
|
+
).limit(1);
|
|
1018
|
+
if (result.length === 0)
|
|
1019
|
+
return mempool.Chain.getChain(parameters.chainId)?.mempool.deploymentBlock || 0;
|
|
1020
|
+
return Number(result[0].blockNumber);
|
|
1021
|
+
},
|
|
1022
|
+
saveBlockNumber: async (parameters) => {
|
|
1023
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1024
|
+
await db.insert(collectorBlockNumbers).values({
|
|
1025
|
+
chainId: parameters.chainId,
|
|
1026
|
+
name,
|
|
1027
|
+
blockNumber: parameters.blockNumber,
|
|
1028
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1029
|
+
}).onConflictDoUpdate({
|
|
1030
|
+
target: [collectorBlockNumbers.chainId, collectorBlockNumbers.name],
|
|
1031
|
+
set: {
|
|
1032
|
+
blockNumber: parameters.blockNumber,
|
|
1033
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
};
|
|
1039
|
+
function memory() {
|
|
1040
|
+
const blockNumbers = /* @__PURE__ */ new Map();
|
|
1041
|
+
return {
|
|
1042
|
+
getBlockNumber: async (parameters) => {
|
|
1043
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1044
|
+
return blockNumbers.get(name)?.get(parameters.chainId) || mempool.Chain.getChain(parameters.chainId)?.mempool.deploymentBlock || 0;
|
|
1045
|
+
},
|
|
1046
|
+
saveBlockNumber: async (parameters) => {
|
|
1047
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1048
|
+
if (!blockNumbers.has(name)) {
|
|
1049
|
+
blockNumbers.set(name, /* @__PURE__ */ new Map());
|
|
1050
|
+
}
|
|
1051
|
+
if (!blockNumbers.get(name)?.has(parameters.chainId)) {
|
|
1052
|
+
blockNumbers.get(name).set(parameters.chainId, parameters.blockNumber);
|
|
1053
|
+
}
|
|
1054
|
+
blockNumbers.get(name).set(parameters.chainId, parameters.blockNumber);
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// src/Cursor.ts
|
|
1060
|
+
var Cursor_exports = {};
|
|
1061
|
+
__export(Cursor_exports, {
|
|
1062
|
+
decode: () => decode,
|
|
1063
|
+
encode: () => encode,
|
|
1064
|
+
validate: () => validate
|
|
1065
|
+
});
|
|
1066
|
+
function validate(cursor) {
|
|
1067
|
+
if (!cursor || typeof cursor !== "object") {
|
|
1068
|
+
throw new Error("Cursor must be an object");
|
|
1069
|
+
}
|
|
1070
|
+
const c = cursor;
|
|
1071
|
+
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
1072
|
+
throw new Error(
|
|
1073
|
+
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
if (!["asc", "desc"].includes(c.dir)) {
|
|
1077
|
+
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
1078
|
+
}
|
|
1079
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
1080
|
+
throw new Error(
|
|
1081
|
+
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
const validations = {
|
|
1085
|
+
rate: {
|
|
1086
|
+
field: "rate",
|
|
1087
|
+
type: "string",
|
|
1088
|
+
pattern: /^\d+$/,
|
|
1089
|
+
error: "numeric string"
|
|
1090
|
+
},
|
|
1091
|
+
amount: {
|
|
1092
|
+
field: "assets",
|
|
1093
|
+
type: "string",
|
|
1094
|
+
pattern: /^\d+$/,
|
|
1095
|
+
error: "numeric string"
|
|
1096
|
+
},
|
|
1097
|
+
maturity: {
|
|
1098
|
+
field: "maturity",
|
|
1099
|
+
type: "number",
|
|
1100
|
+
validator: (val) => val > 0,
|
|
1101
|
+
error: "positive number"
|
|
1102
|
+
},
|
|
1103
|
+
expiry: {
|
|
1104
|
+
field: "expiry",
|
|
1105
|
+
type: "number",
|
|
1106
|
+
validator: (val) => val > 0,
|
|
1107
|
+
error: "positive number"
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
const validation = validations[c.sort];
|
|
1111
|
+
if (!validation) {
|
|
1112
|
+
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
1113
|
+
}
|
|
1114
|
+
const fieldValue = c[validation.field];
|
|
1115
|
+
if (!fieldValue) {
|
|
1116
|
+
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
1117
|
+
}
|
|
1118
|
+
if (typeof fieldValue !== validation.type) {
|
|
1119
|
+
throw new Error(
|
|
1120
|
+
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
1124
|
+
throw new Error(
|
|
1125
|
+
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
if (validation.validator && !validation.validator(fieldValue)) {
|
|
1129
|
+
throw new Error(
|
|
1130
|
+
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
function encode(c) {
|
|
1136
|
+
return jsBase64.Base64.encodeURL(JSON.stringify(c));
|
|
1137
|
+
}
|
|
1138
|
+
function decode(token) {
|
|
1139
|
+
if (!token) return null;
|
|
1140
|
+
const decoded = JSON.parse(jsBase64.Base64.decode(token));
|
|
1141
|
+
validate(decoded);
|
|
1142
|
+
return decoded;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// src/core/apiSchema/index.ts
|
|
1146
|
+
var apiSchema_exports = {};
|
|
1147
|
+
__export(apiSchema_exports, {
|
|
1148
|
+
OpenApi: () => OpenApi,
|
|
1149
|
+
fromResponse: () => fromResponse,
|
|
1150
|
+
parse: () => parse,
|
|
1151
|
+
safeParse: () => safeParse,
|
|
1152
|
+
toResponse: () => toResponse
|
|
1153
|
+
});
|
|
1154
|
+
var MAX_LIMIT = 100;
|
|
1155
|
+
var DEFAULT_LIMIT = 20;
|
|
1156
|
+
var MAX_LLTV = 100;
|
|
1157
|
+
var MIN_LLTV = 0;
|
|
1158
|
+
var GetOffersQueryParams = v4.z.object({
|
|
1159
|
+
// Core filtering parameters
|
|
1160
|
+
creators: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
1161
|
+
message: "Creators must be comma-separated Ethereum addresses"
|
|
1162
|
+
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
1163
|
+
description: "Filter by multiple creator addresses (comma-separated)",
|
|
1164
|
+
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
1165
|
+
}),
|
|
1166
|
+
side: v4.z.enum(["buy", "sell"]).optional().meta({
|
|
1167
|
+
description: "Filter by offer type: buy offers or sell offers",
|
|
1168
|
+
example: "buy"
|
|
1169
|
+
}),
|
|
1170
|
+
chains: v4.z.string().regex(/^\d+(,\d+)*$/, {
|
|
1171
|
+
message: "Chains must be comma-separated chain IDs"
|
|
1172
|
+
}).transform((val) => val.split(",").map(Number)).optional().meta({
|
|
1173
|
+
description: "Filter by multiple blockchain networks (comma-separated chain IDs)",
|
|
1174
|
+
example: "1,137,10"
|
|
1175
|
+
}),
|
|
1176
|
+
loan_tokens: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
1177
|
+
message: "Loan assets must be comma-separated Ethereum addresses"
|
|
1178
|
+
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
1179
|
+
description: "Filter by multiple loan assets (comma-separated)",
|
|
1180
|
+
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
1181
|
+
}),
|
|
1182
|
+
status: v4.z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
|
|
1183
|
+
message: "Status must be comma-separated status values"
|
|
1184
|
+
}).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
|
|
1185
|
+
message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
|
|
1186
|
+
}).optional().meta({
|
|
1187
|
+
description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
|
|
1188
|
+
example: "valid,callback_error"
|
|
1189
|
+
}),
|
|
1190
|
+
callback_addresses: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
1191
|
+
message: "Callback addresses must be comma-separated Ethereum addresses"
|
|
1192
|
+
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
1193
|
+
description: "Filter by multiple callback addresses (comma-separated)",
|
|
1194
|
+
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
1195
|
+
}),
|
|
1196
|
+
// Asset range
|
|
1197
|
+
min_amount: v4.z.bigint({ coerce: true }).positive({
|
|
1198
|
+
message: "Min amount must be a positive number"
|
|
1199
|
+
}).optional().meta({
|
|
1200
|
+
description: "Minimum amount of assets in the offer",
|
|
1201
|
+
example: "1000"
|
|
1202
|
+
}),
|
|
1203
|
+
max_amount: v4.z.bigint({ coerce: true }).positive({
|
|
1204
|
+
message: "Max amount must be a positive number"
|
|
1205
|
+
}).optional().meta({
|
|
1206
|
+
description: "Maximum amount of assets in the offer",
|
|
1207
|
+
example: "10000"
|
|
1208
|
+
}),
|
|
1209
|
+
// Rate range
|
|
1210
|
+
min_rate: v4.z.bigint({ coerce: true }).positive({
|
|
1211
|
+
message: "Min rate must be a positive number"
|
|
1212
|
+
}).optional().meta({
|
|
1213
|
+
description: "Minimum rate per asset (in wei)",
|
|
1214
|
+
example: "500000000000000000"
|
|
1215
|
+
}),
|
|
1216
|
+
max_rate: v4.z.bigint({ coerce: true }).positive({
|
|
1217
|
+
message: "Max rate must be a positive number"
|
|
1218
|
+
}).optional().meta({
|
|
1219
|
+
description: "Maximum rate per asset (in wei)",
|
|
1220
|
+
example: "1500000000000000000"
|
|
1221
|
+
}),
|
|
1222
|
+
// Time range
|
|
1223
|
+
min_maturity: v4.z.coerce.number().int().min(0).optional().meta({
|
|
1224
|
+
description: "Minimum maturity timestamp (Unix timestamp in seconds)",
|
|
1225
|
+
example: "1700000000"
|
|
1226
|
+
}),
|
|
1227
|
+
max_maturity: v4.z.coerce.number().int().min(0).optional().meta({
|
|
1228
|
+
description: "Maximum maturity timestamp (Unix timestamp in seconds)",
|
|
1229
|
+
example: "1800000000"
|
|
1230
|
+
}),
|
|
1231
|
+
min_expiry: v4.z.coerce.number().int().optional().meta({
|
|
1232
|
+
description: "Minimum expiry timestamp (Unix timestamp in seconds)",
|
|
1233
|
+
example: "1700000000"
|
|
1234
|
+
}),
|
|
1235
|
+
max_expiry: v4.z.coerce.number().int().optional().meta({
|
|
1236
|
+
description: "Maximum expiry timestamp (Unix timestamp in seconds)",
|
|
1237
|
+
example: "1800000000"
|
|
1238
|
+
}),
|
|
1239
|
+
// Collateral filtering
|
|
1240
|
+
collateral_assets: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
1241
|
+
message: "Collateral assets must be comma-separated Ethereum addresses"
|
|
1242
|
+
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
1243
|
+
description: "Filter by multiple collateral assets (comma-separated)",
|
|
1244
|
+
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
1245
|
+
}),
|
|
1246
|
+
collateral_oracles: v4.z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
1247
|
+
message: "Collateral oracles must be comma-separated Ethereum addresses"
|
|
1248
|
+
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
1249
|
+
description: "Filter by multiple rate oracles (comma-separated)",
|
|
1250
|
+
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
1251
|
+
}),
|
|
1252
|
+
collateral_tuple: v4.z.string().transform((val, ctx) => {
|
|
1253
|
+
const pattern = /^(0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)(#0x[a-fA-F0-9]{40}(:0x[a-fA-F0-9]{40})?(:[0-9]+(\.[0-9]+)?)?)*$/;
|
|
1254
|
+
if (!pattern.test(val)) {
|
|
1255
|
+
ctx.addIssue({
|
|
1256
|
+
code: "custom",
|
|
1257
|
+
message: "collateral_tuple has an invalid format",
|
|
1258
|
+
input: val
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
return val.split("#").map((tuple) => {
|
|
1262
|
+
const parts = tuple.split(":");
|
|
1263
|
+
if (parts.length === 0 || !parts[0]) {
|
|
1264
|
+
ctx.addIssue({
|
|
1265
|
+
code: "custom",
|
|
1266
|
+
message: "Asset address is required for each collateral tuple",
|
|
1267
|
+
path: ["asset"],
|
|
1268
|
+
input: val
|
|
1269
|
+
});
|
|
1270
|
+
return v4.z.NEVER;
|
|
1271
|
+
}
|
|
1272
|
+
const asset = parts[0]?.toLowerCase();
|
|
1273
|
+
const oracle = parts[1]?.toLowerCase();
|
|
1274
|
+
const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
|
|
1275
|
+
if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
|
|
1276
|
+
ctx.addIssue({
|
|
1277
|
+
code: "custom",
|
|
1278
|
+
message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
|
|
1279
|
+
path: ["lltv"],
|
|
1280
|
+
input: val
|
|
1281
|
+
});
|
|
1282
|
+
return v4.z.NEVER;
|
|
1283
|
+
}
|
|
1284
|
+
let lltvValue;
|
|
1285
|
+
if (lltv !== void 0) {
|
|
1286
|
+
try {
|
|
1287
|
+
lltvValue = mempool.LLTV.from(viem.parseUnits(lltv.toString(), 16));
|
|
1288
|
+
} catch (e) {
|
|
1289
|
+
ctx.issues.push({
|
|
1290
|
+
code: "custom",
|
|
1291
|
+
message: e instanceof mempool.LLTV.InvalidLLTVError || e instanceof mempool.LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
|
|
1292
|
+
input: lltv,
|
|
1293
|
+
path: ["lltv"]
|
|
1294
|
+
});
|
|
1295
|
+
return v4.z.NEVER;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
return {
|
|
1299
|
+
asset,
|
|
1300
|
+
oracle,
|
|
395
1301
|
lltv: lltvValue
|
|
396
1302
|
};
|
|
397
1303
|
});
|
|
@@ -741,19 +1647,19 @@ var OpenApi = zodOpenapi.createDocument({
|
|
|
741
1647
|
|
|
742
1648
|
// src/core/apiSchema/utils.ts
|
|
743
1649
|
function toResponse(routerOffer) {
|
|
744
|
-
const { consumed, status, metadata, ...offer } = routerOffer;
|
|
1650
|
+
const { consumed: consumed2, status, metadata, ...offer } = routerOffer;
|
|
745
1651
|
return {
|
|
746
1652
|
offer,
|
|
747
|
-
consumed,
|
|
1653
|
+
consumed: consumed2,
|
|
748
1654
|
status,
|
|
749
1655
|
metadata
|
|
750
1656
|
};
|
|
751
1657
|
}
|
|
752
1658
|
function fromResponse(offerResponse) {
|
|
753
|
-
const { offer, consumed, status, metadata } = offerResponse;
|
|
1659
|
+
const { offer, consumed: consumed2, status, metadata } = offerResponse;
|
|
754
1660
|
return {
|
|
755
1661
|
...offer,
|
|
756
|
-
consumed,
|
|
1662
|
+
consumed: consumed2,
|
|
757
1663
|
status,
|
|
758
1664
|
metadata
|
|
759
1665
|
};
|
|
@@ -879,8 +1785,8 @@ async function get(config, parameters) {
|
|
|
879
1785
|
if (parameters.limit !== void 0) {
|
|
880
1786
|
url.searchParams.set("limit", parameters.limit.toString());
|
|
881
1787
|
}
|
|
882
|
-
const { cursor: returnedCursor, data:
|
|
883
|
-
const routerOffers =
|
|
1788
|
+
const { cursor: returnedCursor, data: offers2 } = await getApi(config, url);
|
|
1789
|
+
const routerOffers = offers2.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
884
1790
|
return {
|
|
885
1791
|
cursor: returnedCursor,
|
|
886
1792
|
offers: routerOffers.map(from).map(toResponse)
|
|
@@ -921,8 +1827,8 @@ async function match(config, parameters) {
|
|
|
921
1827
|
if (parameters.limit !== void 0) {
|
|
922
1828
|
url.searchParams.set("limit", parameters.limit.toString());
|
|
923
1829
|
}
|
|
924
|
-
const { cursor: returnedCursor, data:
|
|
925
|
-
const routerOffers =
|
|
1830
|
+
const { cursor: returnedCursor, data: offers2 } = await getApi(config, url);
|
|
1831
|
+
const routerOffers = offers2.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
926
1832
|
return {
|
|
927
1833
|
cursor: returnedCursor,
|
|
928
1834
|
offers: routerOffers.map(from).map(toResponse)
|
|
@@ -934,501 +1840,87 @@ async function getApi(config, url) {
|
|
|
934
1840
|
switch (true) {
|
|
935
1841
|
case pathname.includes("/v1/offers/match"):
|
|
936
1842
|
action = "match_offers";
|
|
937
|
-
break;
|
|
938
|
-
case pathname.includes("/v1/offers"):
|
|
939
|
-
action = "get_offers";
|
|
940
|
-
break;
|
|
941
|
-
default:
|
|
942
|
-
throw new HttpGetOffersFailedError("Unknown endpoint", {
|
|
943
|
-
details: `Unsupported path: ${pathname}`
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
|
|
947
|
-
if (!schemaParseResult.success) {
|
|
948
|
-
throw new HttpGetOffersFailedError(`Invalid URL parameters`, {
|
|
949
|
-
details: schemaParseResult.error.issues[0]?.message
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
const response = await fetch(url.toString(), {
|
|
953
|
-
method: "GET",
|
|
954
|
-
headers: config.headers
|
|
955
|
-
});
|
|
956
|
-
if (!response.ok) {
|
|
957
|
-
switch (response.status) {
|
|
958
|
-
case 401:
|
|
959
|
-
throw new HttpUnauthorizedError();
|
|
960
|
-
case 403:
|
|
961
|
-
throw new HttpForbiddenError();
|
|
962
|
-
case 429:
|
|
963
|
-
throw new HttpRateLimitError();
|
|
964
|
-
}
|
|
965
|
-
throw new HttpGetOffersFailedError(`GET request returned ${response.status}`, {
|
|
966
|
-
details: await response.text()
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
return response.json();
|
|
970
|
-
}
|
|
971
|
-
var InvalidUrlError = class extends mempool.Errors.BaseError {
|
|
972
|
-
name = "Router.InvalidUrlError";
|
|
973
|
-
constructor(url) {
|
|
974
|
-
super(`URL "${url}" is not http/https.`);
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
var HttpUnauthorizedError = class extends mempool.Errors.BaseError {
|
|
978
|
-
name = "Router.HttpUnauthorizedError";
|
|
979
|
-
constructor() {
|
|
980
|
-
super("Unauthorized.", {
|
|
981
|
-
metaMessages: ["Ensure that an API key is provided."]
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
var HttpForbiddenError = class extends mempool.Errors.BaseError {
|
|
986
|
-
name = "Router.HttpForbiddenError";
|
|
987
|
-
constructor() {
|
|
988
|
-
super("Forbidden.", {
|
|
989
|
-
metaMessages: ["Ensure that the API key is valid."]
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
};
|
|
993
|
-
var HttpRateLimitError = class extends mempool.Errors.BaseError {
|
|
994
|
-
name = "Router.HttpRateLimitError";
|
|
995
|
-
constructor() {
|
|
996
|
-
super("Rate limit exceeded.", {
|
|
997
|
-
metaMessages: [
|
|
998
|
-
"The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
|
|
999
|
-
]
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
|
|
1004
|
-
name = "Router.HttpGetOffersFailedError";
|
|
1005
|
-
constructor(message, { details } = {}) {
|
|
1006
|
-
super(message, {
|
|
1007
|
-
metaMessages: [details]
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
|
-
|
|
1012
|
-
// src/OfferStore/index.ts
|
|
1013
|
-
var OfferStore_exports = {};
|
|
1014
|
-
__export(OfferStore_exports, {
|
|
1015
|
-
memory: () => memory
|
|
1016
|
-
});
|
|
1017
|
-
function memory(parameters) {
|
|
1018
|
-
const map = parameters.offers;
|
|
1019
|
-
const filled = parameters.filled;
|
|
1020
|
-
const consumedIds = /* @__PURE__ */ new Set();
|
|
1021
|
-
const create = async (parameters2) => {
|
|
1022
|
-
if (map.has(parameters2.offer.hash.toLowerCase())) return parameters2.offer.hash;
|
|
1023
|
-
const callbackId = getCallbackIdForOffer(parameters2.offer);
|
|
1024
|
-
map.set(parameters2.offer.hash.toLowerCase(), {
|
|
1025
|
-
...parameters2.offer,
|
|
1026
|
-
...callbackId ? { callbackId } : {},
|
|
1027
|
-
status: parameters2.status,
|
|
1028
|
-
metadata: parameters2.metadata
|
|
1029
|
-
});
|
|
1030
|
-
const chainId = parameters2.offer.chainId;
|
|
1031
|
-
const address = parameters2.offer.offering.toLowerCase();
|
|
1032
|
-
const nonce = parameters2.offer.nonce;
|
|
1033
|
-
const filledForChain = filled.get(chainId) || /* @__PURE__ */ new Map();
|
|
1034
|
-
const filledForOffering = filledForChain.get(address) || /* @__PURE__ */ new Map();
|
|
1035
|
-
if (!filledForOffering.has(nonce)) filledForOffering.set(nonce, 0n);
|
|
1036
|
-
filledForChain.set(address, filledForOffering);
|
|
1037
|
-
filled.set(chainId, filledForChain);
|
|
1038
|
-
return Promise.resolve(parameters2.offer.hash);
|
|
1039
|
-
};
|
|
1040
|
-
const sort = (sortBy, sortOrder, a, b) => {
|
|
1041
|
-
sortBy = sortBy || "expiry";
|
|
1042
|
-
sortOrder = sortOrder || "desc";
|
|
1043
|
-
const sortKey = sortBy === "amount" ? "assets" : sortBy;
|
|
1044
|
-
if (a[sortKey] === b[sortKey]) {
|
|
1045
|
-
if (a.hash === b.hash) return 0;
|
|
1046
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : b.hash > a.hash ? 1 : -1;
|
|
1047
|
-
}
|
|
1048
|
-
switch (sortBy) {
|
|
1049
|
-
case "rate":
|
|
1050
|
-
if (a.rate === b.rate) {
|
|
1051
|
-
if (a.hash === b.hash) return 0;
|
|
1052
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
|
|
1053
|
-
}
|
|
1054
|
-
return sortOrder === "asc" ? a.rate > b.rate ? 1 : -1 : b.rate > a.rate ? 1 : -1;
|
|
1055
|
-
case "maturity":
|
|
1056
|
-
if (a.maturity === b.maturity) {
|
|
1057
|
-
if (a.hash === b.hash) return 0;
|
|
1058
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
|
|
1059
|
-
}
|
|
1060
|
-
return sortOrder === "asc" ? a.maturity > b.maturity ? 1 : -1 : b.maturity > a.maturity ? 1 : -1;
|
|
1061
|
-
case "expiry":
|
|
1062
|
-
if (a.expiry === b.expiry) {
|
|
1063
|
-
if (a.hash === b.hash) return 0;
|
|
1064
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
|
|
1065
|
-
}
|
|
1066
|
-
return sortOrder === "asc" ? a.expiry > b.expiry ? 1 : -1 : b.expiry > a.expiry ? 1 : -1;
|
|
1067
|
-
case "amount":
|
|
1068
|
-
if (a.assets === b.assets) {
|
|
1069
|
-
if (a.hash === b.hash) return 0;
|
|
1070
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
|
|
1071
|
-
}
|
|
1072
|
-
return sortOrder === "asc" ? a.assets > b.assets ? 1 : -1 : b.assets > a.assets ? 1 : -1;
|
|
1073
|
-
default:
|
|
1074
|
-
if (a.expiry === b.expiry) {
|
|
1075
|
-
if (a.hash === b.hash) return 0;
|
|
1076
|
-
return sortOrder === "asc" ? a.hash > b.hash ? 1 : -1 : a.hash > b.hash ? -1 : 1;
|
|
1077
|
-
}
|
|
1078
|
-
return sortOrder === "asc" ? a.expiry > b.expiry ? 1 : -1 : b.expiry > a.expiry ? 1 : -1;
|
|
1079
|
-
}
|
|
1080
|
-
};
|
|
1081
|
-
return {
|
|
1082
|
-
create,
|
|
1083
|
-
createMany: async (parameters2) => {
|
|
1084
|
-
return Promise.all(
|
|
1085
|
-
parameters2.map((p) => create({ offer: p.offer, status: p.status, metadata: p.metadata }))
|
|
1086
|
-
);
|
|
1087
|
-
},
|
|
1088
|
-
getAll: async (params) => {
|
|
1089
|
-
const { query } = params || {};
|
|
1090
|
-
let {
|
|
1091
|
-
creators,
|
|
1092
|
-
side,
|
|
1093
|
-
chains,
|
|
1094
|
-
loanTokens,
|
|
1095
|
-
status = ["valid"],
|
|
1096
|
-
callbackAddresses,
|
|
1097
|
-
minAmount,
|
|
1098
|
-
maxAmount,
|
|
1099
|
-
minRate,
|
|
1100
|
-
maxRate,
|
|
1101
|
-
minMaturity,
|
|
1102
|
-
maxMaturity,
|
|
1103
|
-
minExpiry,
|
|
1104
|
-
maxExpiry,
|
|
1105
|
-
collateralAssets,
|
|
1106
|
-
collateralOracles,
|
|
1107
|
-
collateralTuple,
|
|
1108
|
-
minLltv,
|
|
1109
|
-
maxLltv,
|
|
1110
|
-
sortBy = "expiry",
|
|
1111
|
-
sortOrder = "desc",
|
|
1112
|
-
cursor: queryCursor,
|
|
1113
|
-
limit = 20
|
|
1114
|
-
} = query || {};
|
|
1115
|
-
const now = mempool.Time.now();
|
|
1116
|
-
const buy = side === "buy";
|
|
1117
|
-
let offers = Array.from(map.values()).map((o) => ({
|
|
1118
|
-
...o,
|
|
1119
|
-
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1120
|
-
})).filter((o) => o.consumed < o.assets);
|
|
1121
|
-
const cursor = decode(queryCursor);
|
|
1122
|
-
if (cursor) {
|
|
1123
|
-
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
1124
|
-
throw new Error("Cursor does not match the current sort parameters");
|
|
1125
|
-
}
|
|
1126
|
-
switch (cursor.sort) {
|
|
1127
|
-
case "rate":
|
|
1128
|
-
offers = offers.filter(
|
|
1129
|
-
(o) => (sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)) && (o.rate !== BigInt(cursor.rate) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1130
|
-
);
|
|
1131
|
-
break;
|
|
1132
|
-
case "maturity":
|
|
1133
|
-
offers = offers.filter(
|
|
1134
|
-
(o) => (sortOrder === "asc" ? o.maturity >= BigInt(cursor.maturity) : o.maturity <= BigInt(cursor.maturity)) && (o.maturity !== mempool.Maturity.from(cursor.maturity) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1135
|
-
);
|
|
1136
|
-
break;
|
|
1137
|
-
case "expiry":
|
|
1138
|
-
offers = offers.filter(
|
|
1139
|
-
(o) => (sortOrder === "asc" ? o.expiry >= cursor.expiry : o.expiry <= cursor.expiry) && (o.expiry !== cursor.expiry || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1140
|
-
);
|
|
1141
|
-
break;
|
|
1142
|
-
case "amount":
|
|
1143
|
-
offers = offers.filter(
|
|
1144
|
-
(o) => (sortOrder === "asc" ? o.assets >= BigInt(cursor.assets) : o.assets <= BigInt(cursor.assets)) && (o.assets !== BigInt(cursor.assets) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1145
|
-
);
|
|
1146
|
-
break;
|
|
1147
|
-
default:
|
|
1148
|
-
throw new Error("Invalid sort parameter");
|
|
1149
|
-
}
|
|
1150
|
-
offers = offers.filter((o) => o.hash !== cursor.hash);
|
|
1151
|
-
}
|
|
1152
|
-
creators && (creators = creators.map((c) => c.toLowerCase()));
|
|
1153
|
-
loanTokens && (loanTokens = loanTokens.map((lt) => lt.toLowerCase()));
|
|
1154
|
-
callbackAddresses && (callbackAddresses = callbackAddresses.map((ca) => ca.toLowerCase()));
|
|
1155
|
-
collateralAssets && (collateralAssets = collateralAssets.map((ca) => ca.toLowerCase()));
|
|
1156
|
-
collateralOracles && (collateralOracles = collateralOracles.map((co) => co.toLowerCase()));
|
|
1157
|
-
collateralTuple && (collateralTuple = collateralTuple.map((ct) => ({
|
|
1158
|
-
asset: ct.asset.toLowerCase(),
|
|
1159
|
-
oracle: ct.oracle?.toLowerCase()
|
|
1160
|
-
})));
|
|
1161
|
-
offers = offers.filter((o) => o.expiry >= now);
|
|
1162
|
-
creators && (offers = offers.filter((o) => creators.includes(o.offering.toLowerCase())));
|
|
1163
|
-
side && (offers = offers.filter((o) => o.buy === buy));
|
|
1164
|
-
chains && (offers = offers.filter((o) => chains.includes(Number(o.chainId))));
|
|
1165
|
-
loanTokens && (offers = offers.filter((o) => loanTokens.includes(o.loanToken.toLowerCase())));
|
|
1166
|
-
status && (offers = offers.filter((o) => status.includes(o.status)));
|
|
1167
|
-
callbackAddresses && (offers = offers.filter(
|
|
1168
|
-
(o) => callbackAddresses.includes(o.callback.address.toLowerCase())
|
|
1169
|
-
));
|
|
1170
|
-
minAmount && (offers = offers.filter((o) => o.assets >= minAmount));
|
|
1171
|
-
maxAmount && (offers = offers.filter((o) => o.assets <= maxAmount));
|
|
1172
|
-
minRate && (offers = offers.filter((o) => o.rate >= minRate));
|
|
1173
|
-
maxRate && (offers = offers.filter((o) => o.rate <= maxRate));
|
|
1174
|
-
minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
|
|
1175
|
-
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1176
|
-
minExpiry && (offers = offers.filter((o) => o.expiry >= minExpiry));
|
|
1177
|
-
maxExpiry && (offers = offers.filter((o) => o.expiry <= maxExpiry));
|
|
1178
|
-
collateralAssets && (offers = offers.filter(
|
|
1179
|
-
(o) => o.collaterals.some((c) => collateralAssets.includes(c.asset.toLowerCase()))
|
|
1180
|
-
));
|
|
1181
|
-
collateralOracles && (offers = offers.filter(
|
|
1182
|
-
(o) => o.collaterals.some((c) => collateralOracles.includes(c.oracle.toLowerCase()))
|
|
1183
|
-
));
|
|
1184
|
-
collateralTuple && (offers = offers.filter(
|
|
1185
|
-
(o) => o.collaterals.some(
|
|
1186
|
-
(c) => collateralTuple.some(
|
|
1187
|
-
(ct) => c.asset.toLowerCase() === ct.asset.toLowerCase() && (ct.oracle ? c.oracle.toLowerCase() === ct.oracle.toLowerCase() : true) && (ct.lltv ? c.lltv === mempool.LLTV.from(BigInt(ct.lltv)) : true)
|
|
1188
|
-
)
|
|
1189
|
-
)
|
|
1190
|
-
));
|
|
1191
|
-
minLltv && (offers = offers.filter(
|
|
1192
|
-
(o) => o.collaterals.every((c) => c.lltv >= viem.parseUnits(minLltv.toString(), 16))
|
|
1193
|
-
));
|
|
1194
|
-
maxLltv && (offers = offers.filter(
|
|
1195
|
-
(o) => o.collaterals.every((c) => c.lltv <= viem.parseUnits(maxLltv.toString(), 16))
|
|
1196
|
-
));
|
|
1197
|
-
offers = offers.sort((a, b) => sort(sortBy, sortOrder, a, b));
|
|
1198
|
-
let nextCursor = null;
|
|
1199
|
-
if (offers.length > limit) {
|
|
1200
|
-
const last = offers[limit - 1];
|
|
1201
|
-
const base = {
|
|
1202
|
-
sort: sortBy,
|
|
1203
|
-
dir: sortOrder,
|
|
1204
|
-
hash: last.hash
|
|
1205
|
-
};
|
|
1206
|
-
switch (sortBy) {
|
|
1207
|
-
case "rate":
|
|
1208
|
-
base.rate = last.rate.toString();
|
|
1209
|
-
break;
|
|
1210
|
-
case "amount":
|
|
1211
|
-
base.assets = last.assets.toString();
|
|
1212
|
-
break;
|
|
1213
|
-
case "maturity":
|
|
1214
|
-
base.maturity = last.maturity;
|
|
1215
|
-
break;
|
|
1216
|
-
default:
|
|
1217
|
-
base.expiry = last.expiry;
|
|
1218
|
-
}
|
|
1219
|
-
nextCursor = encode(base);
|
|
1220
|
-
}
|
|
1221
|
-
offers = offers.slice(0, limit);
|
|
1222
|
-
const data = offers.map((o) => ({
|
|
1223
|
-
...mempool.Offer.from({
|
|
1224
|
-
offering: o.offering,
|
|
1225
|
-
assets: o.assets,
|
|
1226
|
-
rate: o.rate,
|
|
1227
|
-
maturity: mempool.Maturity.from(o.maturity),
|
|
1228
|
-
expiry: o.expiry,
|
|
1229
|
-
start: o.start,
|
|
1230
|
-
nonce: o.nonce,
|
|
1231
|
-
buy: o.buy,
|
|
1232
|
-
chainId: o.chainId,
|
|
1233
|
-
loanToken: o.loanToken,
|
|
1234
|
-
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1235
|
-
callback: {
|
|
1236
|
-
address: o.callback.address,
|
|
1237
|
-
data: o.callback.data,
|
|
1238
|
-
gasLimit: o.callback.gasLimit
|
|
1239
|
-
},
|
|
1240
|
-
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1241
|
-
}),
|
|
1242
|
-
consumed: o.consumed,
|
|
1243
|
-
status: o.status,
|
|
1244
|
-
...o.metadata ? { metadata: o.metadata } : {}
|
|
1245
|
-
}));
|
|
1246
|
-
return {
|
|
1247
|
-
offers: data,
|
|
1248
|
-
nextCursor
|
|
1249
|
-
};
|
|
1250
|
-
},
|
|
1251
|
-
findMatchingOffers: async (params) => {
|
|
1252
|
-
const {
|
|
1253
|
-
side,
|
|
1254
|
-
chainId,
|
|
1255
|
-
rate,
|
|
1256
|
-
collaterals = [],
|
|
1257
|
-
maturity,
|
|
1258
|
-
minMaturity,
|
|
1259
|
-
maxMaturity,
|
|
1260
|
-
loanToken,
|
|
1261
|
-
creator,
|
|
1262
|
-
cursor: queryCursor,
|
|
1263
|
-
limit = 20
|
|
1264
|
-
} = params;
|
|
1265
|
-
const now = mempool.Time.now();
|
|
1266
|
-
const isBuying = side === "buy";
|
|
1267
|
-
const sortOrder = isBuying ? "desc" : "asc";
|
|
1268
|
-
let offers = Array.from(map.values()).map((o) => ({
|
|
1269
|
-
...o,
|
|
1270
|
-
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1271
|
-
})).filter((o) => o.consumed < o.assets);
|
|
1272
|
-
const cursor = decode(queryCursor);
|
|
1273
|
-
if (cursor) {
|
|
1274
|
-
if (cursor.sort !== "rate" || cursor.dir !== sortOrder) {
|
|
1275
|
-
throw new Error("Cursor does not match the current sort parameters");
|
|
1276
|
-
}
|
|
1277
|
-
offers = offers.filter(
|
|
1278
|
-
(o) => sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)
|
|
1279
|
-
);
|
|
1280
|
-
}
|
|
1281
|
-
offers = offers.filter((o) => o.buy === !isBuying);
|
|
1282
|
-
offers = offers.filter((o) => o.chainId === BigInt(chainId));
|
|
1283
|
-
offers = offers.filter((o) => o.expiry >= now);
|
|
1284
|
-
rate && (offers = offers.filter((o) => isBuying ? o.rate >= rate : o.rate <= rate));
|
|
1285
|
-
collaterals.length > 0 && (offers = offers.filter(
|
|
1286
|
-
(o) => isBuying ? (
|
|
1287
|
-
// when wanting to buy, sell offer collaterals ⊆ user buy collaterals
|
|
1288
|
-
o.collaterals.every((oc) => {
|
|
1289
|
-
return collaterals.some(
|
|
1290
|
-
(c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
|
|
1291
|
-
);
|
|
1292
|
-
})
|
|
1293
|
-
) : (
|
|
1294
|
-
// when wanting to sell, user sell collaterals ⊆ buy offer collaterals
|
|
1295
|
-
collaterals.every((c) => {
|
|
1296
|
-
return o.collaterals.some(
|
|
1297
|
-
(oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
|
|
1298
|
-
);
|
|
1299
|
-
})
|
|
1300
|
-
)
|
|
1301
|
-
));
|
|
1302
|
-
maturity && (offers = offers.filter((o) => o.maturity === maturity));
|
|
1303
|
-
minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
|
|
1304
|
-
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1305
|
-
loanToken && (offers = offers.filter((o) => o.loanToken.toLowerCase() === loanToken.toLowerCase()));
|
|
1306
|
-
creator && (offers = offers.filter((o) => o.offering.toLowerCase() === creator.toLowerCase()));
|
|
1307
|
-
offers = offers.filter((o) => ["valid"].includes(o.status));
|
|
1308
|
-
const byGroup = /* @__PURE__ */ new Map();
|
|
1309
|
-
for (const offer of offers) {
|
|
1310
|
-
const groupKey = `${offer.chainId}-${offer.offering.toLowerCase()}-${offer.nonce}-${offer.buy}`;
|
|
1311
|
-
const current = byGroup.get(groupKey);
|
|
1312
|
-
if (!current) {
|
|
1313
|
-
byGroup.set(groupKey, offer);
|
|
1314
|
-
continue;
|
|
1315
|
-
}
|
|
1316
|
-
const remainingCandidate = offer.assets - offer.consumed;
|
|
1317
|
-
const remainingCurrent = current.assets - current.consumed;
|
|
1318
|
-
let candidateIsBetter = false;
|
|
1319
|
-
if (offer.buy) {
|
|
1320
|
-
if (offer.rate !== current.rate) candidateIsBetter = offer.rate < current.rate;
|
|
1321
|
-
else if (remainingCandidate !== remainingCurrent)
|
|
1322
|
-
candidateIsBetter = remainingCandidate > remainingCurrent;
|
|
1323
|
-
else if (offer.maturity !== current.maturity)
|
|
1324
|
-
candidateIsBetter = offer.maturity > current.maturity;
|
|
1325
|
-
else candidateIsBetter = offer.hash < current.hash;
|
|
1326
|
-
} else {
|
|
1327
|
-
if (offer.rate !== current.rate) candidateIsBetter = offer.rate > current.rate;
|
|
1328
|
-
else if (remainingCandidate !== remainingCurrent)
|
|
1329
|
-
candidateIsBetter = remainingCandidate > remainingCurrent;
|
|
1330
|
-
else if (offer.maturity !== current.maturity)
|
|
1331
|
-
candidateIsBetter = offer.maturity > current.maturity;
|
|
1332
|
-
else candidateIsBetter = offer.hash < current.hash;
|
|
1333
|
-
}
|
|
1334
|
-
if (candidateIsBetter) byGroup.set(groupKey, offer);
|
|
1335
|
-
}
|
|
1336
|
-
offers = Array.from(byGroup.values());
|
|
1337
|
-
offers = offers.sort((a, b) => sort("rate", sortOrder, a, b));
|
|
1338
|
-
cursor && (offers = offers.filter((o) => o.hash !== cursor.hash));
|
|
1339
|
-
let nextCursor = null;
|
|
1340
|
-
if (offers.length > limit) {
|
|
1341
|
-
const last = offers[limit - 1];
|
|
1342
|
-
nextCursor = encode({
|
|
1343
|
-
sort: "rate",
|
|
1344
|
-
dir: sortOrder,
|
|
1345
|
-
hash: last.hash,
|
|
1346
|
-
rate: last.rate.toString()
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
1349
|
-
offers = offers.slice(0, limit);
|
|
1350
|
-
const data = offers.map((o) => ({
|
|
1351
|
-
...mempool.Offer.from({
|
|
1352
|
-
offering: o.offering,
|
|
1353
|
-
assets: o.assets,
|
|
1354
|
-
rate: o.rate,
|
|
1355
|
-
maturity: mempool.Maturity.from(o.maturity),
|
|
1356
|
-
expiry: o.expiry,
|
|
1357
|
-
start: o.start,
|
|
1358
|
-
nonce: o.nonce,
|
|
1359
|
-
buy: o.buy,
|
|
1360
|
-
chainId: o.chainId,
|
|
1361
|
-
loanToken: o.loanToken,
|
|
1362
|
-
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1363
|
-
callback: {
|
|
1364
|
-
address: o.callback.address,
|
|
1365
|
-
data: o.callback.data,
|
|
1366
|
-
gasLimit: o.callback.gasLimit
|
|
1367
|
-
},
|
|
1368
|
-
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1369
|
-
}),
|
|
1370
|
-
consumed: o.consumed,
|
|
1371
|
-
status: o.status,
|
|
1372
|
-
...o.metadata ? { metadata: o.metadata } : {}
|
|
1373
|
-
}));
|
|
1374
|
-
return {
|
|
1375
|
-
offers: data,
|
|
1376
|
-
nextCursor
|
|
1377
|
-
};
|
|
1378
|
-
},
|
|
1379
|
-
delete: async (hash) => {
|
|
1380
|
-
if (!map.has(hash.toLowerCase())) return false;
|
|
1381
|
-
map.delete(hash.toLowerCase());
|
|
1382
|
-
return true;
|
|
1383
|
-
},
|
|
1384
|
-
deleteMany: async (hashes) => {
|
|
1385
|
-
let deleted = 0;
|
|
1386
|
-
for (const hash of hashes) {
|
|
1387
|
-
if (map.has(hash.toLowerCase())) {
|
|
1388
|
-
map.delete(hash.toLowerCase());
|
|
1389
|
-
deleted++;
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
return deleted;
|
|
1393
|
-
},
|
|
1394
|
-
updateStatus: async (parameters2) => {
|
|
1395
|
-
const key = parameters2.offerHash.toLowerCase();
|
|
1396
|
-
const existing = map.get(key);
|
|
1397
|
-
if (!existing) return;
|
|
1398
|
-
if (existing.status === parameters2.status) return;
|
|
1399
|
-
map.set(key, {
|
|
1400
|
-
...existing,
|
|
1401
|
-
status: parameters2.status,
|
|
1402
|
-
metadata: parameters2.metadata
|
|
1843
|
+
break;
|
|
1844
|
+
case pathname.includes("/v1/offers"):
|
|
1845
|
+
action = "get_offers";
|
|
1846
|
+
break;
|
|
1847
|
+
default:
|
|
1848
|
+
throw new HttpGetOffersFailedError("Unknown endpoint", {
|
|
1849
|
+
details: `Unsupported path: ${pathname}`
|
|
1403
1850
|
});
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1851
|
+
}
|
|
1852
|
+
const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
|
|
1853
|
+
if (!schemaParseResult.success) {
|
|
1854
|
+
throw new HttpGetOffersFailedError(`Invalid URL parameters`, {
|
|
1855
|
+
details: schemaParseResult.error.issues[0]?.message
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
const response = await fetch(url.toString(), {
|
|
1859
|
+
method: "GET",
|
|
1860
|
+
headers: config.headers
|
|
1861
|
+
});
|
|
1862
|
+
if (!response.ok) {
|
|
1863
|
+
switch (response.status) {
|
|
1864
|
+
case 401:
|
|
1865
|
+
throw new HttpUnauthorizedError();
|
|
1866
|
+
case 403:
|
|
1867
|
+
throw new HttpForbiddenError();
|
|
1868
|
+
case 429:
|
|
1869
|
+
throw new HttpRateLimitError();
|
|
1417
1870
|
}
|
|
1418
|
-
|
|
1871
|
+
throw new HttpGetOffersFailedError(`GET request returned ${response.status}`, {
|
|
1872
|
+
details: await response.text()
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
return response.json();
|
|
1419
1876
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1877
|
+
var InvalidUrlError = class extends mempool.Errors.BaseError {
|
|
1878
|
+
name = "Router.InvalidUrlError";
|
|
1879
|
+
constructor(url) {
|
|
1880
|
+
super(`URL "${url}" is not http/https.`);
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
var HttpUnauthorizedError = class extends mempool.Errors.BaseError {
|
|
1884
|
+
name = "Router.HttpUnauthorizedError";
|
|
1885
|
+
constructor() {
|
|
1886
|
+
super("Unauthorized.", {
|
|
1887
|
+
metaMessages: ["Ensure that an API key is provided."]
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
};
|
|
1891
|
+
var HttpForbiddenError = class extends mempool.Errors.BaseError {
|
|
1892
|
+
name = "Router.HttpForbiddenError";
|
|
1893
|
+
constructor() {
|
|
1894
|
+
super("Forbidden.", {
|
|
1895
|
+
metaMessages: ["Ensure that the API key is valid."]
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
var HttpRateLimitError = class extends mempool.Errors.BaseError {
|
|
1900
|
+
name = "Router.HttpRateLimitError";
|
|
1901
|
+
constructor() {
|
|
1902
|
+
super("Rate limit exceeded.", {
|
|
1903
|
+
metaMessages: [
|
|
1904
|
+
"The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
|
|
1905
|
+
]
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
|
|
1910
|
+
name = "Router.HttpGetOffersFailedError";
|
|
1911
|
+
constructor(message, { details } = {}) {
|
|
1912
|
+
super(message, {
|
|
1913
|
+
metaMessages: [details]
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1422
1917
|
async function serve(parameters) {
|
|
1423
1918
|
const app = new hono.Hono();
|
|
1424
|
-
const store = parameters.store
|
|
1425
|
-
offers: /* @__PURE__ */ new Map(),
|
|
1426
|
-
filled: /* @__PURE__ */ new Map()
|
|
1427
|
-
});
|
|
1919
|
+
const store = parameters.store;
|
|
1428
1920
|
app.get("/v1/offers", async (c) => {
|
|
1429
1921
|
try {
|
|
1430
1922
|
const params = parse("get_offers", c.req.query());
|
|
1431
|
-
const
|
|
1923
|
+
const offers2 = await store.getAll({
|
|
1432
1924
|
query: {
|
|
1433
1925
|
creators: params.creators,
|
|
1434
1926
|
side: params.side,
|
|
@@ -1456,10 +1948,10 @@ async function serve(parameters) {
|
|
|
1456
1948
|
}
|
|
1457
1949
|
});
|
|
1458
1950
|
return success(c, {
|
|
1459
|
-
data:
|
|
1951
|
+
data: offers2.offers.map(
|
|
1460
1952
|
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
1461
1953
|
),
|
|
1462
|
-
cursor:
|
|
1954
|
+
cursor: offers2.nextCursor ?? null
|
|
1463
1955
|
});
|
|
1464
1956
|
} catch (err) {
|
|
1465
1957
|
console.error(err);
|
|
@@ -1469,7 +1961,7 @@ async function serve(parameters) {
|
|
|
1469
1961
|
app.get("/v1/offers/match", async (c) => {
|
|
1470
1962
|
try {
|
|
1471
1963
|
const params = parse("match_offers", c.req.query());
|
|
1472
|
-
const
|
|
1964
|
+
const offers2 = await store.findMatchingOffers({
|
|
1473
1965
|
side: params.side,
|
|
1474
1966
|
chainId: params.chain_id,
|
|
1475
1967
|
rate: params.rate,
|
|
@@ -1483,10 +1975,10 @@ async function serve(parameters) {
|
|
|
1483
1975
|
limit: params.limit
|
|
1484
1976
|
});
|
|
1485
1977
|
return success(c, {
|
|
1486
|
-
data:
|
|
1978
|
+
data: offers2.offers.map(
|
|
1487
1979
|
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
1488
1980
|
),
|
|
1489
|
-
cursor:
|
|
1981
|
+
cursor: offers2.nextCursor ?? null
|
|
1490
1982
|
});
|
|
1491
1983
|
} catch (err) {
|
|
1492
1984
|
console.error(err);
|
|
@@ -1594,318 +2086,1032 @@ function handleAPIError(error2, c) {
|
|
|
1594
2086
|
});
|
|
1595
2087
|
}
|
|
1596
2088
|
|
|
1597
|
-
// src/
|
|
1598
|
-
var
|
|
1599
|
-
__export(
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
serialize: () => serialize
|
|
1603
|
-
});
|
|
1604
|
-
async function fetchBalancesAndAllowances(parameters) {
|
|
1605
|
-
const { client, spender, pairs, options } = parameters;
|
|
1606
|
-
if (pairs.length === 0) return /* @__PURE__ */ new Map();
|
|
1607
|
-
const batchSize = Math.max(1, options?.batchSize ?? 5e3);
|
|
1608
|
-
const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
|
|
1609
|
-
const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
|
|
1610
|
-
const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
|
|
1611
|
-
const out = /* @__PURE__ */ new Map();
|
|
1612
|
-
for (const pairsBatch of mempool.Utils.batch(pairs, batchSize)) {
|
|
1613
|
-
const balanceContracts = [];
|
|
1614
|
-
const allowanceContracts = [];
|
|
1615
|
-
for (const { user, token } of pairsBatch) {
|
|
1616
|
-
balanceContracts.push({
|
|
1617
|
-
address: token,
|
|
1618
|
-
abi: viem.erc20Abi,
|
|
1619
|
-
functionName: "balanceOf",
|
|
1620
|
-
args: [user]
|
|
1621
|
-
});
|
|
1622
|
-
allowanceContracts.push({
|
|
1623
|
-
address: token,
|
|
1624
|
-
abi: viem.erc20Abi,
|
|
1625
|
-
functionName: "allowance",
|
|
1626
|
-
args: [user, spender]
|
|
1627
|
-
});
|
|
1628
|
-
}
|
|
1629
|
-
const [balances, allowances] = await Promise.all([
|
|
1630
|
-
mempool.Utils.retry(
|
|
1631
|
-
() => client.multicall({
|
|
1632
|
-
allowFailure: false,
|
|
1633
|
-
contracts: balanceContracts,
|
|
1634
|
-
...blockNumber ? { blockNumber } : {}
|
|
1635
|
-
}),
|
|
1636
|
-
retryAttempts,
|
|
1637
|
-
retryDelayMs
|
|
1638
|
-
),
|
|
1639
|
-
mempool.Utils.retry(
|
|
1640
|
-
() => client.multicall({
|
|
1641
|
-
allowFailure: false,
|
|
1642
|
-
contracts: allowanceContracts,
|
|
1643
|
-
...blockNumber ? { blockNumber } : {}
|
|
1644
|
-
}),
|
|
1645
|
-
retryAttempts,
|
|
1646
|
-
retryDelayMs
|
|
1647
|
-
)
|
|
1648
|
-
]);
|
|
1649
|
-
for (let i = 0; i < pairsBatch.length; i++) {
|
|
1650
|
-
const { user, token } = pairsBatch[i];
|
|
1651
|
-
const balance = balances[i];
|
|
1652
|
-
const allowance = allowances[i];
|
|
1653
|
-
let perUser = out.get(user);
|
|
1654
|
-
if (!perUser) {
|
|
1655
|
-
perUser = /* @__PURE__ */ new Map();
|
|
1656
|
-
out.set(user, perUser);
|
|
1657
|
-
}
|
|
1658
|
-
perUser.set(token, { balance, allowance });
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
return out;
|
|
1662
|
-
}
|
|
1663
|
-
async function fetch2(parameters) {
|
|
1664
|
-
const { client, chainId, spender, type, pairs, options } = parameters;
|
|
1665
|
-
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
1666
|
-
throw new Error(`CallbackType not implemented: ${type}`);
|
|
1667
|
-
const map = await fetchBalancesAndAllowances({
|
|
1668
|
-
client,
|
|
1669
|
-
spender,
|
|
1670
|
-
pairs: pairs.map(({ user, contract }) => ({ user, token: contract })),
|
|
1671
|
-
options
|
|
1672
|
-
});
|
|
1673
|
-
const out = [];
|
|
1674
|
-
for (const [user, perContract] of map) {
|
|
1675
|
-
for (const [contract, { balance, allowance }] of perContract) {
|
|
1676
|
-
const amount = balance < allowance ? balance : allowance;
|
|
1677
|
-
out.push(
|
|
1678
|
-
buildLiquidity({
|
|
1679
|
-
type,
|
|
1680
|
-
user,
|
|
1681
|
-
contract,
|
|
1682
|
-
chainId,
|
|
1683
|
-
amount: amount.toString(),
|
|
1684
|
-
index: 0
|
|
1685
|
-
})
|
|
1686
|
-
);
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
return out;
|
|
1690
|
-
}
|
|
1691
|
-
function serialize(liquidity) {
|
|
1692
|
-
const normalized = {
|
|
1693
|
-
userPosition: {
|
|
1694
|
-
id: liquidity.userPosition.id,
|
|
1695
|
-
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
1696
|
-
user: liquidity.userPosition.user,
|
|
1697
|
-
chainId: String(liquidity.userPosition.chainId),
|
|
1698
|
-
amount: String(liquidity.userPosition.amount)
|
|
1699
|
-
},
|
|
1700
|
-
queues: liquidity.queues.map((queueWithPool) => ({
|
|
1701
|
-
queue: {
|
|
1702
|
-
queueId: queueWithPool.queue.queueId,
|
|
1703
|
-
availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
|
|
1704
|
-
index: queueWithPool.queue.index
|
|
1705
|
-
},
|
|
1706
|
-
pool: {
|
|
1707
|
-
id: queueWithPool.pool.id,
|
|
1708
|
-
amount: String(queueWithPool.pool.amount)
|
|
1709
|
-
}
|
|
1710
|
-
})).sort(
|
|
1711
|
-
(left, right) => {
|
|
1712
|
-
const leftQueueId = left.queue.queueId || "";
|
|
1713
|
-
const rightQueueId = right.queue.queueId || "";
|
|
1714
|
-
if (leftQueueId < rightQueueId) return -1;
|
|
1715
|
-
if (leftQueueId > rightQueueId) return 1;
|
|
1716
|
-
const leftPoolId = left.pool.id;
|
|
1717
|
-
const rightPoolId = right.pool.id;
|
|
1718
|
-
if (leftPoolId < rightPoolId) return -1;
|
|
1719
|
-
if (leftPoolId > rightPoolId) return 1;
|
|
1720
|
-
const leftIndex = left.queue.index;
|
|
1721
|
-
const rightIndex = right.queue.index;
|
|
1722
|
-
if (leftIndex < rightIndex) return -1;
|
|
1723
|
-
if (leftIndex > rightIndex) return 1;
|
|
1724
|
-
return 0;
|
|
1725
|
-
}
|
|
1726
|
-
)
|
|
1727
|
-
};
|
|
1728
|
-
return JSON.stringify(normalized);
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
// src/Logger.ts
|
|
1732
|
-
var Logger_exports = {};
|
|
1733
|
-
__export(Logger_exports, {
|
|
1734
|
-
LogLevelValues: () => LogLevelValues,
|
|
1735
|
-
defaultLogger: () => defaultLogger,
|
|
1736
|
-
getLogger: () => getLogger,
|
|
1737
|
-
runWithLogger: () => runWithLogger,
|
|
1738
|
-
silentLogger: () => silentLogger
|
|
2089
|
+
// src/LiquidityStore.ts
|
|
2090
|
+
var LiquidityStore_exports = {};
|
|
2091
|
+
__export(LiquidityStore_exports, {
|
|
2092
|
+
create: () => create2,
|
|
2093
|
+
memory: () => memory2
|
|
1739
2094
|
});
|
|
1740
|
-
var
|
|
1741
|
-
|
|
1742
|
-
"trace",
|
|
1743
|
-
"debug",
|
|
1744
|
-
"info",
|
|
1745
|
-
"warn",
|
|
1746
|
-
"error",
|
|
1747
|
-
"fatal"
|
|
1748
|
-
];
|
|
1749
|
-
function defaultLogger(minLevel) {
|
|
1750
|
-
const threshold = minLevel ?? "trace";
|
|
1751
|
-
const levelIndexByName = LogLevelValues.reduce(
|
|
1752
|
-
(acc, lvl, idx) => {
|
|
1753
|
-
acc[lvl] = idx;
|
|
1754
|
-
return acc;
|
|
1755
|
-
},
|
|
1756
|
-
{}
|
|
1757
|
-
);
|
|
1758
|
-
const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
|
|
2095
|
+
var create2 = (config) => {
|
|
2096
|
+
const db = config.db;
|
|
1759
2097
|
return {
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
2098
|
+
getByUserPositionId: async (parameters) => {
|
|
2099
|
+
const up = await db.select().from(userPositions).where(drizzleOrm.eq(userPositions.id, parameters.userPositionId)).limit(1);
|
|
2100
|
+
if (up.length === 0) return null;
|
|
2101
|
+
const userPositionRow = up[0];
|
|
2102
|
+
const rows = await db.select({ queue: availableLiquidityQueues, pool: availableLiquidityPools }).from(availableLiquidityQueues).innerJoin(
|
|
2103
|
+
availableLiquidityPools,
|
|
2104
|
+
drizzleOrm.eq(availableLiquidityPools.id, availableLiquidityQueues.availableLiquidityPoolId)
|
|
2105
|
+
).where(drizzleOrm.eq(availableLiquidityQueues.queueId, userPositionRow.availableLiquidityQueueId));
|
|
2106
|
+
const queues = rows.map((row) => ({
|
|
2107
|
+
queue: row.queue,
|
|
2108
|
+
pool: row.pool
|
|
2109
|
+
}));
|
|
2110
|
+
return { userPosition: userPositionRow, queues };
|
|
1771
2111
|
},
|
|
1772
|
-
|
|
1773
|
-
|
|
2112
|
+
getAll: async () => {
|
|
2113
|
+
const rows = await db.select({
|
|
2114
|
+
userPosition: userPositions,
|
|
2115
|
+
queue: availableLiquidityQueues,
|
|
2116
|
+
pool: availableLiquidityPools
|
|
2117
|
+
}).from(userPositions).innerJoin(
|
|
2118
|
+
availableLiquidityQueues,
|
|
2119
|
+
drizzleOrm.eq(availableLiquidityQueues.queueId, userPositions.availableLiquidityQueueId)
|
|
2120
|
+
).innerJoin(
|
|
2121
|
+
availableLiquidityPools,
|
|
2122
|
+
drizzleOrm.eq(availableLiquidityPools.id, availableLiquidityQueues.availableLiquidityPoolId)
|
|
2123
|
+
);
|
|
2124
|
+
const byUserPosition = /* @__PURE__ */ new Map();
|
|
2125
|
+
for (const row of rows) {
|
|
2126
|
+
const id = row.userPosition.id;
|
|
2127
|
+
if (!byUserPosition.has(id)) {
|
|
2128
|
+
byUserPosition.set(id, { userPosition: row.userPosition, queues: [] });
|
|
2129
|
+
}
|
|
2130
|
+
byUserPosition.get(id).queues.push({ queue: row.queue, pool: row.pool });
|
|
2131
|
+
}
|
|
2132
|
+
return Array.from(byUserPosition.values());
|
|
1774
2133
|
},
|
|
1775
|
-
|
|
2134
|
+
save: async (parameters) => {
|
|
2135
|
+
const { liquidity } = parameters;
|
|
2136
|
+
await db.transaction(async (tx) => {
|
|
2137
|
+
for (const qp of liquidity.queues) {
|
|
2138
|
+
await tx.insert(availableLiquidityPools).values({
|
|
2139
|
+
id: qp.pool.id,
|
|
2140
|
+
amount: qp.pool.amount,
|
|
2141
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2142
|
+
}).onConflictDoUpdate({
|
|
2143
|
+
target: availableLiquidityPools.id,
|
|
2144
|
+
set: {
|
|
2145
|
+
amount: qp.pool.amount,
|
|
2146
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
for (const qp of liquidity.queues) {
|
|
2151
|
+
await tx.insert(availableLiquidityQueues).values({
|
|
2152
|
+
queueId: qp.queue.queueId,
|
|
2153
|
+
availableLiquidityPoolId: qp.pool.id,
|
|
2154
|
+
index: qp.queue.index,
|
|
2155
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2156
|
+
}).onConflictDoUpdate({
|
|
2157
|
+
target: [
|
|
2158
|
+
availableLiquidityQueues.queueId,
|
|
2159
|
+
availableLiquidityQueues.availableLiquidityPoolId
|
|
2160
|
+
],
|
|
2161
|
+
set: {
|
|
2162
|
+
index: qp.queue.index,
|
|
2163
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
await tx.insert(userPositions).values({
|
|
2168
|
+
id: liquidity.userPosition.id,
|
|
2169
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
2170
|
+
user: liquidity.userPosition.user,
|
|
2171
|
+
chainId: liquidity.userPosition.chainId,
|
|
2172
|
+
amount: liquidity.userPosition.amount,
|
|
2173
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2174
|
+
}).onConflictDoUpdate({
|
|
2175
|
+
target: userPositions.id,
|
|
2176
|
+
set: {
|
|
2177
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
2178
|
+
user: liquidity.userPosition.user,
|
|
2179
|
+
chainId: liquidity.userPosition.chainId,
|
|
2180
|
+
amount: liquidity.userPosition.amount,
|
|
2181
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
});
|
|
1776
2185
|
}
|
|
1777
2186
|
};
|
|
1778
|
-
}
|
|
1779
|
-
function
|
|
2187
|
+
};
|
|
2188
|
+
function memory2() {
|
|
2189
|
+
const poolsById = /* @__PURE__ */ new Map();
|
|
2190
|
+
const queuesByComposite = /* @__PURE__ */ new Map();
|
|
2191
|
+
const queueIndexByQueueId = /* @__PURE__ */ new Map();
|
|
2192
|
+
const userPositionsById = /* @__PURE__ */ new Map();
|
|
1780
2193
|
return {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2194
|
+
getByUserPositionId: async (parameters) => {
|
|
2195
|
+
const up = userPositionsById.get(parameters.userPositionId);
|
|
2196
|
+
if (!up) return null;
|
|
2197
|
+
const compositeKeys = queueIndexByQueueId.get(up.availableLiquidityQueueId) || /* @__PURE__ */ new Set();
|
|
2198
|
+
const queues = [];
|
|
2199
|
+
for (const key of compositeKeys) {
|
|
2200
|
+
const q = queuesByComposite.get(key);
|
|
2201
|
+
if (!q) continue;
|
|
2202
|
+
const p = poolsById.get(q.availableLiquidityPoolId);
|
|
2203
|
+
if (!p) continue;
|
|
2204
|
+
queues.push({ queue: q, pool: p });
|
|
2205
|
+
}
|
|
2206
|
+
return { userPosition: up, queues };
|
|
1788
2207
|
},
|
|
1789
|
-
|
|
2208
|
+
getAll: async () => {
|
|
2209
|
+
const results = [];
|
|
2210
|
+
for (const up of userPositionsById.values()) {
|
|
2211
|
+
const compositeKeys = queueIndexByQueueId.get(up.availableLiquidityQueueId) || /* @__PURE__ */ new Set();
|
|
2212
|
+
const queues = [];
|
|
2213
|
+
for (const key of compositeKeys) {
|
|
2214
|
+
const q = queuesByComposite.get(key);
|
|
2215
|
+
if (!q) continue;
|
|
2216
|
+
const p = poolsById.get(q.availableLiquidityPoolId);
|
|
2217
|
+
if (!p) continue;
|
|
2218
|
+
queues.push({ queue: q, pool: p });
|
|
2219
|
+
}
|
|
2220
|
+
results.push({ userPosition: up, queues });
|
|
2221
|
+
}
|
|
2222
|
+
return results;
|
|
1790
2223
|
},
|
|
1791
|
-
|
|
2224
|
+
save: async (parameters) => {
|
|
2225
|
+
const { liquidity } = parameters;
|
|
2226
|
+
for (const qp of liquidity.queues) {
|
|
2227
|
+
poolsById.set(qp.pool.id, {
|
|
2228
|
+
id: qp.pool.id,
|
|
2229
|
+
amount: qp.pool.amount,
|
|
2230
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
for (const qp of liquidity.queues) {
|
|
2234
|
+
const qid = qp.queue.queueId;
|
|
2235
|
+
if (!qid) continue;
|
|
2236
|
+
const composite = `${qid}::${qp.pool.id}`;
|
|
2237
|
+
queuesByComposite.set(composite, {
|
|
2238
|
+
queueId: qid,
|
|
2239
|
+
availableLiquidityPoolId: qp.pool.id,
|
|
2240
|
+
index: qp.queue.index,
|
|
2241
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2242
|
+
});
|
|
2243
|
+
if (!queueIndexByQueueId.has(qid)) queueIndexByQueueId.set(qid, /* @__PURE__ */ new Set());
|
|
2244
|
+
queueIndexByQueueId.get(qid).add(composite);
|
|
2245
|
+
}
|
|
2246
|
+
userPositionsById.set(liquidity.userPosition.id, {
|
|
2247
|
+
id: liquidity.userPosition.id,
|
|
2248
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
2249
|
+
user: liquidity.userPosition.user,
|
|
2250
|
+
chainId: liquidity.userPosition.chainId,
|
|
2251
|
+
amount: liquidity.userPosition.amount,
|
|
2252
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2253
|
+
});
|
|
1792
2254
|
}
|
|
1793
2255
|
};
|
|
1794
2256
|
}
|
|
1795
|
-
var loggerContext = new async_hooks.AsyncLocalStorage();
|
|
1796
|
-
function runWithLogger(logger, fn) {
|
|
1797
|
-
return loggerContext.run(logger, fn);
|
|
1798
|
-
}
|
|
1799
|
-
function getLogger() {
|
|
1800
|
-
return loggerContext.getStore() ?? defaultLogger();
|
|
1801
|
-
}
|
|
1802
2257
|
|
|
1803
|
-
// src/
|
|
1804
|
-
var
|
|
1805
|
-
__export(
|
|
1806
|
-
|
|
2258
|
+
// src/OfferStore/index.ts
|
|
2259
|
+
var OfferStore_exports = {};
|
|
2260
|
+
__export(OfferStore_exports, {
|
|
2261
|
+
create: () => create3
|
|
1807
2262
|
});
|
|
1808
|
-
|
|
1809
|
-
const
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2263
|
+
function create3(config) {
|
|
2264
|
+
const db = config.db;
|
|
2265
|
+
return {
|
|
2266
|
+
create: async (parameters) => {
|
|
2267
|
+
const { offer, status, metadata } = parameters;
|
|
2268
|
+
return await db.transaction(async (tx) => {
|
|
2269
|
+
const callbackId = getCallbackIdForOffer(offer);
|
|
2270
|
+
const result = await tx.insert(offers).values({
|
|
2271
|
+
hash: offer.hash.toLowerCase(),
|
|
2272
|
+
offering: offer.offering.toLowerCase(),
|
|
2273
|
+
assets: offer.assets.toString(),
|
|
2274
|
+
rate: offer.rate,
|
|
2275
|
+
maturity: offer.maturity,
|
|
2276
|
+
expiry: offer.expiry,
|
|
2277
|
+
start: offer.start,
|
|
2278
|
+
nonce: offer.nonce,
|
|
2279
|
+
buy: offer.buy,
|
|
2280
|
+
chainId: offer.chainId,
|
|
2281
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
2282
|
+
callbackAddress: offer.callback.address.toLowerCase(),
|
|
2283
|
+
callbackData: offer.callback.data,
|
|
2284
|
+
callbackGasLimit: offer.callback.gasLimit,
|
|
2285
|
+
signature: offer.signature,
|
|
2286
|
+
callbackId: callbackId ?? null
|
|
2287
|
+
}).onConflictDoNothing().returning();
|
|
2288
|
+
if (result.length === 0) {
|
|
2289
|
+
return offer.hash;
|
|
2290
|
+
}
|
|
2291
|
+
for (const collateral of offer.collaterals) {
|
|
2292
|
+
await tx.insert(offerCollaterals).values({
|
|
2293
|
+
offerHash: offer.hash.toLowerCase(),
|
|
2294
|
+
asset: collateral.asset.toLowerCase(),
|
|
2295
|
+
oracle: collateral.oracle.toLowerCase(),
|
|
2296
|
+
lltv: collateral.lltv
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
await tx.insert(offerStatus).values({
|
|
2300
|
+
offerHash: offer.hash.toLowerCase(),
|
|
2301
|
+
status,
|
|
2302
|
+
metadata
|
|
2303
|
+
});
|
|
2304
|
+
return offer.hash;
|
|
2305
|
+
});
|
|
2306
|
+
},
|
|
2307
|
+
createMany: async (parameters) => {
|
|
2308
|
+
return await db.transaction(async (tx) => {
|
|
2309
|
+
const hashes = [];
|
|
2310
|
+
for (const { offer, status, metadata } of parameters) {
|
|
2311
|
+
const callbackId = getCallbackIdForOffer(offer);
|
|
2312
|
+
const result = await tx.insert(offers).values({
|
|
2313
|
+
hash: offer.hash.toLowerCase(),
|
|
2314
|
+
offering: offer.offering.toLowerCase(),
|
|
2315
|
+
assets: offer.assets.toString(),
|
|
2316
|
+
rate: offer.rate,
|
|
2317
|
+
maturity: offer.maturity,
|
|
2318
|
+
expiry: offer.expiry,
|
|
2319
|
+
start: offer.start,
|
|
2320
|
+
nonce: offer.nonce,
|
|
2321
|
+
buy: offer.buy,
|
|
2322
|
+
chainId: offer.chainId,
|
|
2323
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
2324
|
+
callbackAddress: offer.callback.address.toLowerCase(),
|
|
2325
|
+
callbackData: offer.callback.data,
|
|
2326
|
+
callbackGasLimit: offer.callback.gasLimit,
|
|
2327
|
+
signature: offer.signature,
|
|
2328
|
+
callbackId: callbackId ?? null
|
|
2329
|
+
}).onConflictDoNothing().returning();
|
|
2330
|
+
if (result.length === 0) continue;
|
|
2331
|
+
for (const collateral of offer.collaterals) {
|
|
2332
|
+
await tx.insert(offerCollaterals).values({
|
|
2333
|
+
offerHash: offer.hash.toLowerCase(),
|
|
2334
|
+
asset: collateral.asset.toLowerCase(),
|
|
2335
|
+
oracle: collateral.oracle.toLowerCase(),
|
|
2336
|
+
lltv: collateral.lltv
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
await tx.insert(offerStatus).values({
|
|
2340
|
+
offerHash: offer.hash.toLowerCase(),
|
|
2341
|
+
status,
|
|
2342
|
+
metadata
|
|
2343
|
+
});
|
|
2344
|
+
hashes.push(offer.hash);
|
|
1822
2345
|
}
|
|
2346
|
+
return hashes;
|
|
2347
|
+
});
|
|
2348
|
+
},
|
|
2349
|
+
getAll: async (params) => {
|
|
2350
|
+
const { query } = params ?? {};
|
|
2351
|
+
const conditions = [];
|
|
2352
|
+
const now = mempool.Time.now();
|
|
2353
|
+
if (query?.creators && query.creators.length > 0) {
|
|
2354
|
+
conditions.push(
|
|
2355
|
+
drizzleOrm.inArray(
|
|
2356
|
+
offers.offering,
|
|
2357
|
+
query.creators.map((c) => c.toLowerCase())
|
|
2358
|
+
)
|
|
2359
|
+
);
|
|
1823
2360
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
2361
|
+
if (query?.side) {
|
|
2362
|
+
conditions.push(drizzleOrm.eq(offers.buy, query.side === "buy"));
|
|
2363
|
+
}
|
|
2364
|
+
if (query?.chains && query.chains.length > 0) {
|
|
2365
|
+
conditions.push(
|
|
2366
|
+
drizzleOrm.inArray(
|
|
2367
|
+
offers.chainId,
|
|
2368
|
+
query.chains.map((chain) => BigInt(chain))
|
|
2369
|
+
)
|
|
2370
|
+
);
|
|
2371
|
+
}
|
|
2372
|
+
if (query?.loanTokens && query.loanTokens.length > 0) {
|
|
2373
|
+
conditions.push(
|
|
2374
|
+
drizzleOrm.inArray(
|
|
2375
|
+
offers.loanToken,
|
|
2376
|
+
query.loanTokens.map((t) => t.toLowerCase())
|
|
2377
|
+
)
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
if (query?.callbackAddresses && query.callbackAddresses.length > 0) {
|
|
2381
|
+
conditions.push(
|
|
2382
|
+
drizzleOrm.inArray(
|
|
2383
|
+
offers.callbackAddress,
|
|
2384
|
+
query.callbackAddresses.map((c) => c.toLowerCase())
|
|
2385
|
+
)
|
|
2386
|
+
);
|
|
2387
|
+
}
|
|
2388
|
+
conditions.push(drizzleOrm.gte(offers.expiry, now));
|
|
2389
|
+
if (query?.minAmount !== void 0) {
|
|
2390
|
+
conditions.push(drizzleOrm.gte(offers.assets, query.minAmount.toString()));
|
|
2391
|
+
}
|
|
2392
|
+
if (query?.maxAmount !== void 0) {
|
|
2393
|
+
conditions.push(drizzleOrm.lte(offers.assets, query.maxAmount.toString()));
|
|
2394
|
+
}
|
|
2395
|
+
if (query?.minRate !== void 0) {
|
|
2396
|
+
conditions.push(drizzleOrm.gte(offers.rate, query.minRate));
|
|
2397
|
+
}
|
|
2398
|
+
if (query?.maxRate !== void 0) {
|
|
2399
|
+
conditions.push(drizzleOrm.lte(offers.rate, query.maxRate));
|
|
2400
|
+
}
|
|
2401
|
+
if (query?.minMaturity !== void 0) {
|
|
2402
|
+
conditions.push(drizzleOrm.gte(offers.maturity, query.minMaturity));
|
|
2403
|
+
}
|
|
2404
|
+
if (query?.maxMaturity !== void 0) {
|
|
2405
|
+
conditions.push(drizzleOrm.lte(offers.maturity, query.maxMaturity));
|
|
2406
|
+
}
|
|
2407
|
+
if (query?.minExpiry !== void 0) {
|
|
2408
|
+
conditions.push(drizzleOrm.gte(offers.expiry, query.minExpiry));
|
|
2409
|
+
}
|
|
2410
|
+
if (query?.maxExpiry !== void 0) {
|
|
2411
|
+
conditions.push(drizzleOrm.lte(offers.expiry, query.maxExpiry));
|
|
2412
|
+
}
|
|
2413
|
+
if (query?.collateralAssets && query.collateralAssets.length > 0) {
|
|
2414
|
+
conditions.push(
|
|
2415
|
+
drizzleOrm.inArray(
|
|
2416
|
+
offerCollaterals.asset,
|
|
2417
|
+
query.collateralAssets.map((a) => a.toLowerCase())
|
|
2418
|
+
)
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
if (query?.collateralOracles && query.collateralOracles.length > 0) {
|
|
2422
|
+
conditions.push(
|
|
2423
|
+
drizzleOrm.inArray(
|
|
2424
|
+
offerCollaterals.oracle,
|
|
2425
|
+
query.collateralOracles.map((o) => o.toLowerCase())
|
|
2426
|
+
)
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
if (query?.collateralTuple && query.collateralTuple.length > 0) {
|
|
2430
|
+
const tupleClauses = query.collateralTuple.map((tuple) => {
|
|
2431
|
+
const parts = [
|
|
2432
|
+
drizzleOrm.sql`${offerCollaterals.asset} = ${tuple.asset.toLowerCase()}`
|
|
2433
|
+
];
|
|
2434
|
+
if (tuple.oracle) {
|
|
2435
|
+
parts.push(drizzleOrm.sql`${offerCollaterals.oracle} = ${tuple.oracle.toLowerCase()}`);
|
|
2436
|
+
}
|
|
2437
|
+
if (tuple.lltv !== void 0) {
|
|
2438
|
+
parts.push(drizzleOrm.sql`${offerCollaterals.lltv} = ${tuple.lltv}`);
|
|
2439
|
+
}
|
|
2440
|
+
let clause = parts[0];
|
|
2441
|
+
for (let i = 1; i < parts.length; i++) {
|
|
2442
|
+
clause = drizzleOrm.sql`${clause} AND ${parts[i]}`;
|
|
1832
2443
|
}
|
|
2444
|
+
return drizzleOrm.sql`(${clause})`;
|
|
2445
|
+
}).filter((c) => c !== void 0);
|
|
2446
|
+
if (tupleClauses.length > 0) {
|
|
2447
|
+
let combined = tupleClauses[0];
|
|
2448
|
+
for (let i = 1; i < tupleClauses.length; i++) {
|
|
2449
|
+
combined = drizzleOrm.sql`${combined} OR ${tupleClauses[i]}`;
|
|
2450
|
+
}
|
|
2451
|
+
conditions.push(drizzleOrm.sql`(${combined})`);
|
|
1833
2452
|
}
|
|
1834
|
-
}
|
|
1835
|
-
if (
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
2453
|
+
}
|
|
2454
|
+
if (query?.minLltv !== void 0) {
|
|
2455
|
+
conditions.push(drizzleOrm.gte(offerCollaterals.lltv, viem.parseUnits(query.minLltv.toString(), 16)));
|
|
2456
|
+
}
|
|
2457
|
+
if (query?.maxLltv !== void 0) {
|
|
2458
|
+
conditions.push(drizzleOrm.lte(offerCollaterals.lltv, viem.parseUnits(query.maxLltv.toString(), 16)));
|
|
2459
|
+
}
|
|
2460
|
+
const sortBy = query?.sortBy ?? "expiry";
|
|
2461
|
+
const sortOrder = query?.sortOrder ?? "desc";
|
|
2462
|
+
const sortColumn = (() => {
|
|
2463
|
+
switch (sortBy) {
|
|
2464
|
+
case "rate":
|
|
2465
|
+
return offers.rate;
|
|
2466
|
+
case "maturity":
|
|
2467
|
+
return offers.maturity;
|
|
2468
|
+
case "expiry":
|
|
2469
|
+
return offers.expiry;
|
|
2470
|
+
case "amount":
|
|
2471
|
+
return offers.assets;
|
|
2472
|
+
default:
|
|
2473
|
+
return offers.expiry;
|
|
2474
|
+
}
|
|
2475
|
+
})();
|
|
2476
|
+
const cursor = decode(query?.cursor);
|
|
2477
|
+
if (cursor) {
|
|
2478
|
+
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
2479
|
+
throw new Error("Cursor does not match the current sort parameters");
|
|
2480
|
+
}
|
|
2481
|
+
const op = sortOrder === "asc" ? drizzleOrm.sql`>` : drizzleOrm.sql`<`;
|
|
2482
|
+
const cursorVal = (() => {
|
|
2483
|
+
switch (sortBy) {
|
|
2484
|
+
case "rate":
|
|
2485
|
+
return BigInt(cursor.rate);
|
|
2486
|
+
case "amount":
|
|
2487
|
+
return BigInt(cursor.assets);
|
|
2488
|
+
case "maturity":
|
|
2489
|
+
return cursor.maturity;
|
|
2490
|
+
case "expiry":
|
|
2491
|
+
return cursor.expiry;
|
|
2492
|
+
default:
|
|
2493
|
+
return cursor.expiry;
|
|
2494
|
+
}
|
|
2495
|
+
})();
|
|
2496
|
+
conditions.push(drizzleOrm.sql`(${sortColumn}, ${offers.hash}) ${op} (${cursorVal}, ${cursor.hash})`);
|
|
2497
|
+
}
|
|
2498
|
+
const limit = query?.limit ?? 20;
|
|
2499
|
+
const latestStatus = db.select({
|
|
2500
|
+
status: offerStatus.status,
|
|
2501
|
+
metadata: offerStatus.metadata
|
|
2502
|
+
}).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, offers.hash)).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1).as("latest_status");
|
|
2503
|
+
const sumConsumed = db.select({
|
|
2504
|
+
consumed: drizzleOrm.sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
|
|
2505
|
+
}).from(consumed).where(
|
|
2506
|
+
drizzleOrm.and(
|
|
2507
|
+
drizzleOrm.eq(consumed.offering, offers.offering),
|
|
2508
|
+
drizzleOrm.eq(consumed.nonce, offers.nonce),
|
|
2509
|
+
drizzleOrm.eq(consumed.chainId, offers.chainId)
|
|
2510
|
+
)
|
|
2511
|
+
).as("sum_consumed");
|
|
2512
|
+
const results = await db.select({
|
|
2513
|
+
hash: offers.hash,
|
|
2514
|
+
offering: offers.offering,
|
|
2515
|
+
assets: offers.assets,
|
|
2516
|
+
consumed: sumConsumed.consumed,
|
|
2517
|
+
rate: offers.rate,
|
|
2518
|
+
maturity: offers.maturity,
|
|
2519
|
+
expiry: offers.expiry,
|
|
2520
|
+
start: offers.start,
|
|
2521
|
+
nonce: offers.nonce,
|
|
2522
|
+
buy: offers.buy,
|
|
2523
|
+
chainId: offers.chainId,
|
|
2524
|
+
loanToken: offers.loanToken,
|
|
2525
|
+
callbackAddress: offers.callbackAddress,
|
|
2526
|
+
callbackData: offers.callbackData,
|
|
2527
|
+
callbackGasLimit: offers.callbackGasLimit,
|
|
2528
|
+
signature: offers.signature,
|
|
2529
|
+
createdAt: offers.createdAt,
|
|
2530
|
+
collateralAsset: offerCollaterals.asset,
|
|
2531
|
+
collateralOracle: offerCollaterals.oracle,
|
|
2532
|
+
collateralLltv: offerCollaterals.lltv,
|
|
2533
|
+
status: latestStatus.status,
|
|
2534
|
+
metadata: latestStatus.metadata
|
|
2535
|
+
}).from(offers).leftJoin(offerCollaterals, drizzleOrm.eq(offers.hash, offerCollaterals.offerHash)).leftJoinLateral(latestStatus, drizzleOrm.sql`true`).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
|
|
2536
|
+
drizzleOrm.and(
|
|
2537
|
+
conditions.length > 0 ? drizzleOrm.and(...conditions) : drizzleOrm.sql`true`,
|
|
2538
|
+
query?.status && query.status.length > 0 ? drizzleOrm.inArray(latestStatus.status, query.status) : drizzleOrm.eq(latestStatus.status, "valid"),
|
|
2539
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) > 0`
|
|
2540
|
+
)
|
|
2541
|
+
).orderBy(
|
|
2542
|
+
...sortOrder === "asc" ? [drizzleOrm.asc(sortColumn), drizzleOrm.asc(offers.hash)] : [drizzleOrm.desc(sortColumn), drizzleOrm.desc(offers.hash)]
|
|
2543
|
+
).limit(limit);
|
|
2544
|
+
const offerMap = /* @__PURE__ */ new Map();
|
|
2545
|
+
for (const row of results) {
|
|
2546
|
+
const offer = offerMap.get(row.hash) || {
|
|
2547
|
+
hash: row.hash,
|
|
2548
|
+
offering: row.offering,
|
|
2549
|
+
assets: BigInt(row.assets),
|
|
2550
|
+
consumed: BigInt(row.consumed),
|
|
2551
|
+
rate: row.rate,
|
|
2552
|
+
maturity: row.maturity,
|
|
2553
|
+
expiry: row.expiry,
|
|
2554
|
+
start: row.start,
|
|
2555
|
+
nonce: row.nonce,
|
|
2556
|
+
buy: row.buy,
|
|
2557
|
+
chainId: row.chainId,
|
|
2558
|
+
loanToken: row.loanToken,
|
|
2559
|
+
callbackAddress: row.callbackAddress,
|
|
2560
|
+
callbackData: row.callbackData,
|
|
2561
|
+
callbackGasLimit: row.callbackGasLimit,
|
|
2562
|
+
signature: row.signature,
|
|
2563
|
+
createdAt: row.createdAt,
|
|
2564
|
+
collaterals: [],
|
|
2565
|
+
status: row.status,
|
|
2566
|
+
metadata: row.metadata
|
|
2567
|
+
};
|
|
2568
|
+
if (row.collateralAsset && row.collateralOracle && row.collateralLltv) {
|
|
2569
|
+
offer.collaterals.push({
|
|
2570
|
+
asset: row.collateralAsset,
|
|
2571
|
+
oracle: row.collateralOracle,
|
|
2572
|
+
lltv: mempool.LLTV.from(row.collateralLltv)
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
offerMap.set(row.hash, offer);
|
|
2576
|
+
}
|
|
2577
|
+
let nextCursor = null;
|
|
2578
|
+
if (results.length === limit && results.length > 0) {
|
|
2579
|
+
const lastRow = results[results.length - 1];
|
|
2580
|
+
const base = {
|
|
2581
|
+
sort: sortBy,
|
|
2582
|
+
dir: sortOrder,
|
|
2583
|
+
hash: lastRow.hash
|
|
2584
|
+
};
|
|
2585
|
+
switch (sortBy) {
|
|
2586
|
+
case "rate":
|
|
2587
|
+
base.rate = lastRow.rate.toString();
|
|
2588
|
+
break;
|
|
2589
|
+
case "amount":
|
|
2590
|
+
base.assets = lastRow.assets.toString();
|
|
2591
|
+
break;
|
|
2592
|
+
case "maturity":
|
|
2593
|
+
base.maturity = lastRow.maturity;
|
|
2594
|
+
break;
|
|
2595
|
+
default:
|
|
2596
|
+
base.expiry = lastRow.expiry;
|
|
2597
|
+
}
|
|
2598
|
+
nextCursor = encode(base);
|
|
2599
|
+
}
|
|
2600
|
+
const transformedResults = [];
|
|
2601
|
+
for (const offerData of offerMap.values()) {
|
|
2602
|
+
const offer = mempool.Offer.from({
|
|
2603
|
+
offering: offerData.offering,
|
|
2604
|
+
assets: offerData.assets,
|
|
2605
|
+
rate: offerData.rate,
|
|
2606
|
+
maturity: mempool.Maturity.from(offerData.maturity),
|
|
2607
|
+
expiry: offerData.expiry,
|
|
2608
|
+
start: offerData.start,
|
|
2609
|
+
nonce: offerData.nonce,
|
|
2610
|
+
buy: offerData.buy,
|
|
2611
|
+
chainId: offerData.chainId,
|
|
2612
|
+
loanToken: offerData.loanToken,
|
|
2613
|
+
collaterals: offerData.collaterals.map((c) => ({
|
|
2614
|
+
asset: c.asset,
|
|
2615
|
+
oracle: c.oracle,
|
|
2616
|
+
lltv: mempool.LLTV.from(c.lltv)
|
|
2617
|
+
})).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
2618
|
+
callback: {
|
|
2619
|
+
address: offerData.callbackAddress,
|
|
2620
|
+
data: offerData.callbackData,
|
|
2621
|
+
gasLimit: offerData.callbackGasLimit
|
|
2622
|
+
},
|
|
2623
|
+
...offerData.signature !== null ? { signature: offerData.signature } : void 0
|
|
2624
|
+
});
|
|
2625
|
+
transformedResults.push({
|
|
2626
|
+
...offer,
|
|
2627
|
+
consumed: offerData.consumed,
|
|
2628
|
+
status: offerData.status,
|
|
2629
|
+
...offerData.metadata !== null ? { metadata: offerData.metadata } : void 0
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
return { offers: transformedResults, nextCursor };
|
|
2633
|
+
},
|
|
2634
|
+
/**
|
|
2635
|
+
* Returns offers that match specified offer based on the provided parameters.
|
|
2636
|
+
*
|
|
2637
|
+
* Rules for filtering:
|
|
2638
|
+
* - If offer expiry is in the past compared to now, it should not be included
|
|
2639
|
+
* - If offer maturity is in the past compared to now, it should not be included
|
|
2640
|
+
* - It should return if buyOffer.rate >= sellOffer.rate
|
|
2641
|
+
* - It should return if buyOffer.userAddress != sellOffer.userAddress
|
|
2642
|
+
* - It should return if maturity matches
|
|
2643
|
+
* - It should return if loanToken matches
|
|
2644
|
+
* - If the incoming intent is a buy / lend offer, only consider existing sell / borrow offers.
|
|
2645
|
+
* - If the incoming intent is a sell / borrow offer, only consider existing buy / lend offers.
|
|
2646
|
+
* - When the intent is lend: require offer_collaterals ⊆ intent_collaterals
|
|
2647
|
+
* - When the intent is borrow: require intent_collaterals ⊆ offer_collaterals
|
|
2648
|
+
* - It should sort by increasing rate if the input offer is a buy/lend offer
|
|
2649
|
+
* - It should sort by decreasing rate if the input offer is a sell/borrow offer
|
|
2650
|
+
*/
|
|
2651
|
+
findMatchingOffers: async (params) => {
|
|
2652
|
+
const {
|
|
2653
|
+
side,
|
|
2654
|
+
chainId,
|
|
2655
|
+
rate,
|
|
2656
|
+
collaterals = [],
|
|
2657
|
+
maturity,
|
|
2658
|
+
minMaturity,
|
|
2659
|
+
maxMaturity,
|
|
2660
|
+
loanToken,
|
|
2661
|
+
creator,
|
|
2662
|
+
cursor,
|
|
2663
|
+
limit = 20
|
|
2664
|
+
} = params;
|
|
2665
|
+
const isIncomingBuy = side === "buy";
|
|
2666
|
+
const nowEpochSeconds = Math.floor(Date.now() / 1e3);
|
|
2667
|
+
const rateSortDirection = isIncomingBuy ? "desc" : "asc";
|
|
2668
|
+
const baseWhereClauses = [];
|
|
2669
|
+
baseWhereClauses.push(drizzleOrm.eq(offers.buy, !isIncomingBuy));
|
|
2670
|
+
baseWhereClauses.push(drizzleOrm.eq(offers.chainId, BigInt(chainId)));
|
|
2671
|
+
baseWhereClauses.push(drizzleOrm.gte(offers.expiry, nowEpochSeconds));
|
|
2672
|
+
baseWhereClauses.push(drizzleOrm.gte(offers.maturity, nowEpochSeconds));
|
|
2673
|
+
if (maturity) baseWhereClauses.push(drizzleOrm.eq(offers.maturity, maturity));
|
|
2674
|
+
if (minMaturity) baseWhereClauses.push(drizzleOrm.gte(offers.maturity, minMaturity));
|
|
2675
|
+
if (maxMaturity) baseWhereClauses.push(drizzleOrm.lte(offers.maturity, maxMaturity));
|
|
2676
|
+
if (loanToken) baseWhereClauses.push(drizzleOrm.eq(offers.loanToken, loanToken.toLowerCase()));
|
|
2677
|
+
if (creator) baseWhereClauses.push(drizzleOrm.eq(offers.offering, creator.toLowerCase()));
|
|
2678
|
+
if (rate)
|
|
2679
|
+
baseWhereClauses.push(isIncomingBuy ? drizzleOrm.gte(offers.rate, rate) : drizzleOrm.lte(offers.rate, rate));
|
|
2680
|
+
const parsedCursor = decode(cursor);
|
|
2681
|
+
if (parsedCursor) {
|
|
2682
|
+
if (parsedCursor.sort !== "rate" || parsedCursor.dir !== rateSortDirection) {
|
|
2683
|
+
throw new Error("Cursor does not match the current sort parameters");
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
if (collaterals.length > 0) {
|
|
2687
|
+
const collateralValues = collaterals.map(
|
|
2688
|
+
(c) => `('${c.asset.toLowerCase()}', '${c.oracle.toLowerCase()}', ${c.lltv.toString()})`
|
|
2689
|
+
).join(", ");
|
|
2690
|
+
const userCollaterals = drizzleOrm.sql`(VALUES ${drizzleOrm.sql.raw(collateralValues)}) AS user_collaterals(asset, oracle, lltv)`;
|
|
2691
|
+
const sellPredicate = drizzleOrm.sql`
|
|
2692
|
+
NOT EXISTS (
|
|
2693
|
+
SELECT 1 FROM ${userCollaterals}
|
|
2694
|
+
WHERE NOT EXISTS (
|
|
2695
|
+
SELECT 1 FROM ${offerCollaterals} offer_collaterals
|
|
2696
|
+
WHERE offer_collaterals.offer_hash = ${offers.hash}
|
|
2697
|
+
AND offer_collaterals.asset = user_collaterals.asset
|
|
2698
|
+
AND offer_collaterals.oracle = user_collaterals.oracle
|
|
2699
|
+
AND offer_collaterals.lltv = user_collaterals.lltv
|
|
2700
|
+
)
|
|
2701
|
+
)
|
|
2702
|
+
`;
|
|
2703
|
+
const buyPredicate = drizzleOrm.sql`
|
|
2704
|
+
NOT EXISTS (
|
|
2705
|
+
SELECT 1 FROM ${offerCollaterals} offer_collaterals
|
|
2706
|
+
WHERE offer_collaterals.offer_hash = ${offers.hash}
|
|
2707
|
+
AND NOT EXISTS (
|
|
2708
|
+
SELECT 1 FROM ${userCollaterals}
|
|
2709
|
+
WHERE user_collaterals.asset = offer_collaterals.asset
|
|
2710
|
+
AND user_collaterals.oracle = offer_collaterals.oracle
|
|
2711
|
+
AND user_collaterals.lltv = offer_collaterals.lltv
|
|
2712
|
+
)
|
|
2713
|
+
)
|
|
2714
|
+
`;
|
|
2715
|
+
baseWhereClauses.push(isIncomingBuy ? buyPredicate : sellPredicate);
|
|
2716
|
+
}
|
|
2717
|
+
const latestStatus = db.select({
|
|
2718
|
+
status: offerStatus.status,
|
|
2719
|
+
metadata: offerStatus.metadata
|
|
2720
|
+
}).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, offers.hash)).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1).as("latest_status");
|
|
2721
|
+
const sumConsumed = db.select({
|
|
2722
|
+
consumed: drizzleOrm.sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
|
|
2723
|
+
}).from(consumed).where(
|
|
2724
|
+
drizzleOrm.and(
|
|
2725
|
+
drizzleOrm.eq(consumed.offering, offers.offering),
|
|
2726
|
+
drizzleOrm.eq(consumed.nonce, offers.nonce),
|
|
2727
|
+
drizzleOrm.eq(consumed.chainId, offers.chainId)
|
|
2728
|
+
)
|
|
2729
|
+
).as("sum_consumed");
|
|
2730
|
+
const statusCondition = drizzleOrm.eq(latestStatus.status, "valid");
|
|
2731
|
+
const bestOffers = db.selectDistinctOn(
|
|
2732
|
+
// group key
|
|
2733
|
+
[offers.offering, offers.nonce, offers.buy],
|
|
2734
|
+
{
|
|
2735
|
+
hash: offers.hash,
|
|
2736
|
+
offering: offers.offering,
|
|
2737
|
+
assets: offers.assets,
|
|
2738
|
+
consumed: sumConsumed.consumed,
|
|
2739
|
+
remaining: drizzleOrm.sql`${offers.assets} - COALESCE(${sumConsumed.consumed}, 0)`.as("remaining"),
|
|
2740
|
+
rate: offers.rate,
|
|
2741
|
+
maturity: offers.maturity,
|
|
2742
|
+
expiry: offers.expiry,
|
|
2743
|
+
start: offers.start,
|
|
2744
|
+
nonce: offers.nonce,
|
|
2745
|
+
buy: offers.buy,
|
|
2746
|
+
chainId: offers.chainId,
|
|
2747
|
+
loanToken: offers.loanToken,
|
|
2748
|
+
callbackAddress: offers.callbackAddress,
|
|
2749
|
+
callbackData: offers.callbackData,
|
|
2750
|
+
callbackGasLimit: offers.callbackGasLimit,
|
|
2751
|
+
signature: offers.signature,
|
|
2752
|
+
callbackId: offers.callbackId,
|
|
2753
|
+
status: latestStatus.status,
|
|
2754
|
+
metadata: latestStatus.metadata
|
|
2755
|
+
}
|
|
2756
|
+
).from(offers).leftJoinLateral(latestStatus, drizzleOrm.sql`true`).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
|
|
2757
|
+
drizzleOrm.and(
|
|
2758
|
+
drizzleOrm.and(...baseWhereClauses),
|
|
2759
|
+
statusCondition,
|
|
2760
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) > 0`
|
|
2761
|
+
)
|
|
2762
|
+
).orderBy(
|
|
2763
|
+
offers.offering,
|
|
2764
|
+
offers.nonce,
|
|
2765
|
+
offers.buy,
|
|
2766
|
+
// 1 price (direction depends on side)
|
|
2767
|
+
drizzleOrm.sql`CASE WHEN ${offers.buy} THEN ${offers.rate} ELSE -${offers.rate} END`,
|
|
2768
|
+
// 2 size (remaining)
|
|
2769
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) DESC`,
|
|
2770
|
+
// 3 term (longer maturity)
|
|
2771
|
+
drizzleOrm.desc(offers.maturity)
|
|
2772
|
+
).as("best_offers");
|
|
2773
|
+
const queueLiquidity = db.select({
|
|
2774
|
+
callbackId: userPositions.id,
|
|
2775
|
+
userAmount: userPositions.amount,
|
|
2776
|
+
// user's per-callback cap
|
|
2777
|
+
queueLiquidity: drizzleOrm.sql`COALESCE(SUM(${availableLiquidityPools.amount}), 0)`.as(
|
|
2778
|
+
"queue_liquidity"
|
|
2779
|
+
)
|
|
2780
|
+
}).from(userPositions).leftJoin(
|
|
2781
|
+
availableLiquidityQueues,
|
|
2782
|
+
drizzleOrm.eq(userPositions.availableLiquidityQueueId, availableLiquidityQueues.queueId)
|
|
2783
|
+
).leftJoin(
|
|
2784
|
+
availableLiquidityPools,
|
|
2785
|
+
drizzleOrm.eq(availableLiquidityQueues.availableLiquidityPoolId, availableLiquidityPools.id)
|
|
2786
|
+
).groupBy(userPositions.id).as("queue_liquidity");
|
|
2787
|
+
const sortExpr = drizzleOrm.sql`CASE WHEN ${bestOffers.buy} THEN ${bestOffers.rate} ELSE -${bestOffers.rate} END`;
|
|
2788
|
+
const offersWithEligibility = db.select({
|
|
2789
|
+
hash: bestOffers.hash,
|
|
2790
|
+
offering: bestOffers.offering,
|
|
2791
|
+
assets: bestOffers.assets,
|
|
2792
|
+
consumed: bestOffers.consumed,
|
|
2793
|
+
remaining: bestOffers.remaining,
|
|
2794
|
+
rate: bestOffers.rate,
|
|
2795
|
+
maturity: bestOffers.maturity,
|
|
2796
|
+
expiry: bestOffers.expiry,
|
|
2797
|
+
start: bestOffers.start,
|
|
2798
|
+
nonce: bestOffers.nonce,
|
|
2799
|
+
buy: bestOffers.buy,
|
|
2800
|
+
chainId: bestOffers.chainId,
|
|
2801
|
+
loanToken: bestOffers.loanToken,
|
|
2802
|
+
callbackAddress: bestOffers.callbackAddress,
|
|
2803
|
+
callbackData: bestOffers.callbackData,
|
|
2804
|
+
callbackGasLimit: bestOffers.callbackGasLimit,
|
|
2805
|
+
signature: bestOffers.signature,
|
|
2806
|
+
callbackId: bestOffers.callbackId,
|
|
2807
|
+
status: bestOffers.status,
|
|
2808
|
+
metadata: bestOffers.metadata,
|
|
2809
|
+
// liquidity caps
|
|
2810
|
+
userAmount: drizzleOrm.sql`COALESCE(${queueLiquidity.userAmount}, 0)`.as("user_amount"),
|
|
2811
|
+
queueLiquidity: drizzleOrm.sql`COALESCE(${queueLiquidity.queueLiquidity}, 0)`.as(
|
|
2812
|
+
"queue_liquidity"
|
|
2813
|
+
),
|
|
2814
|
+
// running total of remaining per callback, ordered by the *same* priority as selection
|
|
2815
|
+
cumulativeRemaining: drizzleOrm.sql`
|
|
2816
|
+
SUM(${bestOffers.remaining}) OVER (
|
|
2817
|
+
PARTITION BY ${bestOffers.callbackId}
|
|
2818
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2819
|
+
ROWS UNBOUNDED PRECEDING
|
|
2820
|
+
)
|
|
2821
|
+
`.as("cumulative_remaining"),
|
|
2822
|
+
eligible: drizzleOrm.sql`
|
|
2823
|
+
CASE
|
|
2824
|
+
WHEN ${bestOffers.buy} = false THEN true
|
|
2825
|
+
WHEN ${bestOffers.remaining} <= 0 THEN false
|
|
2826
|
+
ELSE ( SUM(${bestOffers.remaining}) OVER (
|
|
2827
|
+
PARTITION BY ${bestOffers.callbackId}
|
|
2828
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2829
|
+
ROWS UNBOUNDED PRECEDING
|
|
2830
|
+
)
|
|
2831
|
+
<= LEAST(
|
|
2832
|
+
COALESCE(${queueLiquidity.userAmount}, 0),
|
|
2833
|
+
COALESCE(${queueLiquidity.queueLiquidity}, 0)
|
|
2834
|
+
)
|
|
2835
|
+
)
|
|
2836
|
+
END
|
|
2837
|
+
`.as("eligible")
|
|
2838
|
+
}).from(bestOffers).leftJoin(queueLiquidity, drizzleOrm.eq(bestOffers.callbackId, queueLiquidity.callbackId)).as("offers_with_eligibility");
|
|
2839
|
+
const hardCap = limit * 5 + 1 + (parsedCursor ? 1 : 0);
|
|
2840
|
+
const validOffers = db.select({
|
|
2841
|
+
// pass-through all fields + a global row_number for hard cap
|
|
2842
|
+
hash: offersWithEligibility.hash,
|
|
2843
|
+
offering: offersWithEligibility.offering,
|
|
2844
|
+
assets: offersWithEligibility.assets,
|
|
2845
|
+
consumed: offersWithEligibility.consumed,
|
|
2846
|
+
remaining: offersWithEligibility.remaining,
|
|
2847
|
+
rate: offersWithEligibility.rate,
|
|
2848
|
+
maturity: offersWithEligibility.maturity,
|
|
2849
|
+
expiry: offersWithEligibility.expiry,
|
|
2850
|
+
start: offersWithEligibility.start,
|
|
2851
|
+
nonce: offersWithEligibility.nonce,
|
|
2852
|
+
buy: offersWithEligibility.buy,
|
|
2853
|
+
chainId: offersWithEligibility.chainId,
|
|
2854
|
+
loanToken: offersWithEligibility.loanToken,
|
|
2855
|
+
callbackAddress: offersWithEligibility.callbackAddress,
|
|
2856
|
+
callbackData: offersWithEligibility.callbackData,
|
|
2857
|
+
callbackGasLimit: offersWithEligibility.callbackGasLimit,
|
|
2858
|
+
signature: offersWithEligibility.signature,
|
|
2859
|
+
callbackId: offersWithEligibility.callbackId,
|
|
2860
|
+
status: offersWithEligibility.status,
|
|
2861
|
+
metadata: offersWithEligibility.metadata,
|
|
2862
|
+
userAmount: offersWithEligibility.userAmount,
|
|
2863
|
+
queueLiquidity: offersWithEligibility.queueLiquidity,
|
|
2864
|
+
cumulativeRemaining: offersWithEligibility.cumulativeRemaining,
|
|
2865
|
+
eligible: offersWithEligibility.eligible,
|
|
2866
|
+
// sort expression is the same again as in offersWithEligibility
|
|
2867
|
+
rowNumber: drizzleOrm.sql`
|
|
2868
|
+
ROW_NUMBER() OVER (
|
|
2869
|
+
ORDER BY
|
|
2870
|
+
CASE WHEN ${offersWithEligibility.buy} THEN ${offersWithEligibility.rate} ELSE -${offersWithEligibility.rate} END,
|
|
2871
|
+
${drizzleOrm.asc(offersWithEligibility.hash)}
|
|
2872
|
+
)
|
|
2873
|
+
`.as("row_number")
|
|
2874
|
+
}).from(offersWithEligibility).where(drizzleOrm.sql`${offersWithEligibility.eligible} = true`).limit(hardCap).as("valid_offers");
|
|
2875
|
+
const cursorTuple = parsedCursor ? {
|
|
2876
|
+
rate: BigInt(parsedCursor.rate),
|
|
2877
|
+
hash: parsedCursor.hash
|
|
2878
|
+
} : null;
|
|
2879
|
+
const withCollats = await db.select({
|
|
2880
|
+
// base fields
|
|
2881
|
+
hash: drizzleOrm.sql`${validOffers.hash}`,
|
|
2882
|
+
offering: drizzleOrm.sql`${validOffers.offering}`,
|
|
2883
|
+
assets: validOffers.assets,
|
|
2884
|
+
consumed: validOffers.consumed,
|
|
2885
|
+
rate: validOffers.rate,
|
|
2886
|
+
maturity: validOffers.maturity,
|
|
2887
|
+
expiry: validOffers.expiry,
|
|
2888
|
+
start: validOffers.start,
|
|
2889
|
+
nonce: validOffers.nonce,
|
|
2890
|
+
buy: validOffers.buy,
|
|
2891
|
+
chainId: validOffers.chainId,
|
|
2892
|
+
loanToken: drizzleOrm.sql`${validOffers.loanToken}`,
|
|
2893
|
+
callbackAddress: drizzleOrm.sql`${validOffers.callbackAddress}`,
|
|
2894
|
+
callbackData: drizzleOrm.sql`${validOffers.callbackData}`,
|
|
2895
|
+
callbackGasLimit: validOffers.callbackGasLimit,
|
|
2896
|
+
signature: drizzleOrm.sql`${validOffers.signature}`,
|
|
2897
|
+
callbackId: validOffers.callbackId,
|
|
2898
|
+
status: drizzleOrm.sql`${validOffers.status}`,
|
|
2899
|
+
metadata: validOffers.metadata,
|
|
2900
|
+
// collateral fields
|
|
2901
|
+
collateralAsset: drizzleOrm.sql`${offerCollaterals.asset}`,
|
|
2902
|
+
collateralOracle: drizzleOrm.sql`${offerCollaterals.oracle}`,
|
|
2903
|
+
collateralLltv: offerCollaterals.lltv
|
|
2904
|
+
}).from(validOffers).leftJoin(offerCollaterals, drizzleOrm.eq(validOffers.hash, offerCollaterals.offerHash)).where(
|
|
2905
|
+
drizzleOrm.and(
|
|
2906
|
+
...cursorTuple ? [
|
|
2907
|
+
drizzleOrm.sql`(${validOffers.rate}, ${validOffers.hash}) ${rateSortDirection === "asc" ? drizzleOrm.sql`>` : drizzleOrm.sql`<`} (${cursorTuple.rate}, ${cursorTuple.hash})`
|
|
2908
|
+
] : []
|
|
2909
|
+
)
|
|
2910
|
+
).orderBy(
|
|
2911
|
+
rateSortDirection === "asc" ? drizzleOrm.asc(validOffers.rate) : drizzleOrm.desc(validOffers.rate),
|
|
2912
|
+
drizzleOrm.asc(validOffers.hash)
|
|
2913
|
+
);
|
|
2914
|
+
const buildOffersMap = (rows, skipHash) => {
|
|
2915
|
+
const map = /* @__PURE__ */ new Map();
|
|
2916
|
+
for (const row of rows) {
|
|
2917
|
+
const entry = map.get(row.hash) ?? { base: row, collaterals: [] };
|
|
2918
|
+
if (row.collateralAsset && row.collateralOracle && row.collateralLltv) {
|
|
2919
|
+
entry.collaterals.push({
|
|
2920
|
+
asset: row.collateralAsset,
|
|
2921
|
+
oracle: row.collateralOracle,
|
|
2922
|
+
lltv: mempool.LLTV.from(row.collateralLltv)
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
map.set(row.hash, entry);
|
|
1839
2926
|
}
|
|
2927
|
+
return map;
|
|
2928
|
+
};
|
|
2929
|
+
const allEntries = Array.from(buildOffersMap(withCollats).values());
|
|
2930
|
+
const pageEntries = allEntries.slice(0, limit);
|
|
2931
|
+
let nextCursor = null;
|
|
2932
|
+
if (allEntries.length > limit) {
|
|
2933
|
+
const last = pageEntries[pageEntries.length - 1].base;
|
|
2934
|
+
nextCursor = encode({
|
|
2935
|
+
sort: "rate",
|
|
2936
|
+
dir: rateSortDirection,
|
|
2937
|
+
hash: last.hash,
|
|
2938
|
+
rate: last.rate.toString()
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
const data = pageEntries.map(({ base, collaterals: cols }) => ({
|
|
2942
|
+
...mempool.Offer.from({
|
|
2943
|
+
offering: base.offering,
|
|
2944
|
+
assets: BigInt(base.assets),
|
|
2945
|
+
rate: base.rate,
|
|
2946
|
+
maturity: mempool.Maturity.from(base.maturity),
|
|
2947
|
+
expiry: base.expiry,
|
|
2948
|
+
start: base.start,
|
|
2949
|
+
nonce: base.nonce,
|
|
2950
|
+
buy: base.buy,
|
|
2951
|
+
chainId: base.chainId,
|
|
2952
|
+
loanToken: base.loanToken,
|
|
2953
|
+
collaterals: cols.sort(
|
|
2954
|
+
(a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())
|
|
2955
|
+
),
|
|
2956
|
+
callback: {
|
|
2957
|
+
address: base.callbackAddress,
|
|
2958
|
+
data: base.callbackData,
|
|
2959
|
+
gasLimit: base.callbackGasLimit
|
|
2960
|
+
},
|
|
2961
|
+
...base.signature !== null ? { signature: base.signature } : void 0
|
|
2962
|
+
}),
|
|
2963
|
+
consumed: BigInt(base.consumed),
|
|
2964
|
+
status: base.status,
|
|
2965
|
+
...base.metadata ? { metadata: base.metadata } : {}
|
|
2966
|
+
}));
|
|
2967
|
+
return { offers: data, nextCursor };
|
|
2968
|
+
},
|
|
2969
|
+
delete: async (hash) => {
|
|
2970
|
+
const result = await db.delete(offers).where(drizzleOrm.eq(offers.hash, hash.toLowerCase()));
|
|
2971
|
+
return result.affectedRows > 0;
|
|
2972
|
+
},
|
|
2973
|
+
deleteMany: async (hashes) => {
|
|
2974
|
+
if (hashes.length === 0) {
|
|
2975
|
+
return 0;
|
|
1840
2976
|
}
|
|
2977
|
+
return await db.transaction(async (tx) => {
|
|
2978
|
+
const normalizedHashes = hashes.map((hash) => hash.toLowerCase());
|
|
2979
|
+
const result = await tx.delete(offers).where(
|
|
2980
|
+
drizzleOrm.sql`${offers.hash} = ANY(${drizzleOrm.sql.raw(`ARRAY[${normalizedHashes.map((h) => `'${h}'`).join(", ")}]`)})`
|
|
2981
|
+
);
|
|
2982
|
+
return result.affectedRows;
|
|
2983
|
+
});
|
|
2984
|
+
},
|
|
2985
|
+
updateStatus: async (parameters) => {
|
|
2986
|
+
const latest = await db.select({ status: offerStatus.status }).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, parameters.offerHash.toLowerCase())).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1);
|
|
2987
|
+
if (latest.length > 0 && latest[0].status === parameters.status) return;
|
|
2988
|
+
await db.insert(offerStatus).values({
|
|
2989
|
+
offerHash: parameters.offerHash.toLowerCase(),
|
|
2990
|
+
status: parameters.status,
|
|
2991
|
+
metadata: parameters.metadata
|
|
2992
|
+
});
|
|
2993
|
+
},
|
|
2994
|
+
updateConsumedAmount: async (parameters) => {
|
|
2995
|
+
await db.insert(consumed).values({
|
|
2996
|
+
id: parameters.id,
|
|
2997
|
+
chainId: parameters.chainId,
|
|
2998
|
+
offering: parameters.offering.toLowerCase(),
|
|
2999
|
+
nonce: parameters.nonce,
|
|
3000
|
+
consumed: parameters.consumed.toString()
|
|
3001
|
+
}).onConflictDoNothing();
|
|
1841
3002
|
}
|
|
1842
|
-
validItems = validItems.filter((_, i) => !indicesToRemove.has(i));
|
|
1843
|
-
}
|
|
1844
|
-
return {
|
|
1845
|
-
valid: validItems,
|
|
1846
|
-
issues
|
|
1847
3003
|
};
|
|
1848
3004
|
}
|
|
1849
3005
|
|
|
1850
|
-
// src/
|
|
1851
|
-
var
|
|
1852
|
-
__export(
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
3006
|
+
// src/OfferStore/PG.ts
|
|
3007
|
+
var PG_exports = {};
|
|
3008
|
+
__export(PG_exports, {
|
|
3009
|
+
applyMigrations: () => applyMigrations,
|
|
3010
|
+
clean: () => clean,
|
|
3011
|
+
connect: () => connect2
|
|
1856
3012
|
});
|
|
1857
|
-
function
|
|
1858
|
-
|
|
3013
|
+
function connect2(parameters) {
|
|
3014
|
+
if (parameters.type === "pg") {
|
|
3015
|
+
const pool2 = new pg.Pool({ connectionString: parameters.endpoint });
|
|
3016
|
+
const client2 = nodePostgres.drizzle(pool2, { schema: schema_exports });
|
|
3017
|
+
return Object.assign(client2, { name: "pg", pool: pool2 });
|
|
3018
|
+
}
|
|
3019
|
+
const pool = new pglite.PGlite();
|
|
3020
|
+
const client = pglite$1.drizzle(pool, { schema: schema_exports });
|
|
3021
|
+
return Object.assign(client, { name: "pglite", pool });
|
|
1859
3022
|
}
|
|
1860
|
-
function
|
|
1861
|
-
|
|
3023
|
+
async function applyMigrations(pg) {
|
|
3024
|
+
const migrationsFolder = process.env.AWS_LAMBDA_FUNCTION_NAME ? path__default.default.join(process.cwd(), "drizzle", VERSION) : path__default.default.join(__dirname, "drizzle", VERSION);
|
|
3025
|
+
await pg.execute(`create schema if not exists "${VERSION}"`);
|
|
3026
|
+
if (pg.name === "pg") {
|
|
3027
|
+
await migrator.migrate(pg, { migrationsFolder });
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
3030
|
+
await migrator$1.migrate(pg, { migrationsFolder });
|
|
1862
3031
|
}
|
|
1863
|
-
function
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
3032
|
+
async function clean(pg) {
|
|
3033
|
+
await pg.execute(`drop schema if exists "${VERSION}" cascade`);
|
|
3034
|
+
await pg.execute(`create schema "${VERSION}"`);
|
|
3035
|
+
await pg.execute("drop schema if exists drizzle cascade");
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
// src/Services.ts
|
|
3039
|
+
var Services_exports = {};
|
|
3040
|
+
__export(Services_exports, {
|
|
3041
|
+
from: () => from2
|
|
3042
|
+
});
|
|
3043
|
+
var from2 = async (config) => {
|
|
3044
|
+
const { chain, rpcUrl, dbConfig } = config;
|
|
3045
|
+
const walletClient = viem.createWalletClient({
|
|
3046
|
+
chain,
|
|
3047
|
+
transport: viem.http(rpcUrl)
|
|
3048
|
+
}).extend(viem.publicActions);
|
|
3049
|
+
if (dbConfig.type === "pg" && !dbConfig.endpoint) {
|
|
3050
|
+
throw new Error("dbOffer.endpoint is required when dbOffer.type is pg");
|
|
3051
|
+
}
|
|
3052
|
+
const DB = dbConfig.type === "pg" ? connect2({ type: "pg", endpoint: dbConfig.endpoint }) : connect2({ type: "pglite" });
|
|
3053
|
+
const offerStore = create3({ db: DB });
|
|
3054
|
+
const collectorBlockStore = create({
|
|
3055
|
+
db: DB
|
|
1870
3056
|
});
|
|
1871
|
-
const
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
3057
|
+
const liquidityStore = create2({ db: DB });
|
|
3058
|
+
const mempoolOffersCollector = createMempoolCollector({
|
|
3059
|
+
mempool: mempool.Mempool.connect({
|
|
3060
|
+
client: walletClient,
|
|
3061
|
+
mempoolAddress: chain.mempool.address
|
|
3062
|
+
}),
|
|
3063
|
+
offerStore,
|
|
3064
|
+
collectorBlockStore,
|
|
3065
|
+
chain,
|
|
3066
|
+
options: {
|
|
3067
|
+
interval: 1e4
|
|
1879
3068
|
}
|
|
1880
3069
|
});
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
3070
|
+
const consumedEventsCollector = createConsumedEventsCollector({
|
|
3071
|
+
client: walletClient,
|
|
3072
|
+
contractAddress: chain.morpho,
|
|
3073
|
+
offerStore,
|
|
3074
|
+
collectorBlockStore,
|
|
3075
|
+
chainId: chain.id,
|
|
3076
|
+
options: {
|
|
3077
|
+
interval: 1e4
|
|
1884
3078
|
}
|
|
1885
3079
|
});
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
3080
|
+
const buyWithEmptyCallbackLiquidityCollector = createBuyWithEmptyCallbackLiquidityCollector({
|
|
3081
|
+
client: walletClient,
|
|
3082
|
+
offerStore,
|
|
3083
|
+
collectorBlockStore,
|
|
3084
|
+
liquidityStore,
|
|
3085
|
+
chain,
|
|
3086
|
+
options: {
|
|
3087
|
+
interval: 1e4
|
|
1889
3088
|
}
|
|
1890
3089
|
});
|
|
1891
|
-
return
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
}
|
|
3090
|
+
return {
|
|
3091
|
+
offerStore,
|
|
3092
|
+
collectorBlockStore,
|
|
3093
|
+
liquidityStore,
|
|
3094
|
+
mempoolOffersCollector,
|
|
3095
|
+
consumedEventsCollector,
|
|
3096
|
+
buyWithEmptyCallbackLiquidityCollector,
|
|
3097
|
+
DB
|
|
3098
|
+
};
|
|
3099
|
+
};
|
|
1900
3100
|
|
|
1901
3101
|
exports.ApiSchema = apiSchema_exports;
|
|
1902
3102
|
exports.Callback = Callback_exports;
|
|
3103
|
+
exports.Collector = Collector_exports;
|
|
3104
|
+
exports.CollectorBlockStore = CollectorBlockStore_exports;
|
|
1903
3105
|
exports.Cursor = Cursor_exports;
|
|
1904
3106
|
exports.Liquidity = Liquidity_exports;
|
|
3107
|
+
exports.LiquidityStore = LiquidityStore_exports;
|
|
1905
3108
|
exports.Logger = Logger_exports;
|
|
1906
3109
|
exports.OfferStore = OfferStore_exports;
|
|
3110
|
+
exports.OffersSchema = schema_exports;
|
|
3111
|
+
exports.PG = PG_exports;
|
|
1907
3112
|
exports.Router = router_exports;
|
|
1908
3113
|
exports.RouterOffer = RouterOffer_exports;
|
|
3114
|
+
exports.Services = Services_exports;
|
|
1909
3115
|
exports.Validation = Validation_exports;
|
|
1910
3116
|
exports.ValidationRule = ValidationRule_exports;
|
|
1911
3117
|
Object.keys(mempool).forEach(function (k) {
|