@morpho-dev/router 0.1.2 → 0.1.4
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/index.browser.d.cts +132 -149
- package/dist/index.browser.d.ts +132 -149
- package/dist/index.browser.js +355 -302
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +355 -303
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +175 -193
- package/dist/index.node.d.ts +175 -193
- package/dist/index.node.js +355 -303
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +355 -304
- package/dist/index.node.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.node.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var mempool = require('@morpho-dev/mempool');
|
|
4
|
+
var jsBase64 = require('js-base64');
|
|
4
5
|
var v4 = require('zod/v4');
|
|
5
6
|
var zodOpenapi = require('zod-openapi');
|
|
6
7
|
var viem = require('viem');
|
|
7
|
-
var jsBase64 = require('js-base64');
|
|
8
8
|
var nodeServer = require('@hono/node-server');
|
|
9
9
|
var hono = require('hono');
|
|
10
10
|
var async_hooks = require('async_hooks');
|
|
@@ -15,6 +15,145 @@ var __export = (target, all) => {
|
|
|
15
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/Callback.ts
|
|
19
|
+
var Callback_exports = {};
|
|
20
|
+
__export(Callback_exports, {
|
|
21
|
+
CallbackType: () => CallbackType,
|
|
22
|
+
buildLiquidity: () => buildLiquidity,
|
|
23
|
+
getCallbackIdForOffer: () => getCallbackIdForOffer
|
|
24
|
+
});
|
|
25
|
+
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
26
|
+
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
27
|
+
return CallbackType2;
|
|
28
|
+
})(CallbackType || {});
|
|
29
|
+
function buildLiquidity(parameters) {
|
|
30
|
+
const { type, user, contract, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
31
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
32
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
33
|
+
const amountStr = amount.toString();
|
|
34
|
+
const id = `${user}-${chainId.toString()}-${type}-${contract}`.toLowerCase();
|
|
35
|
+
return {
|
|
36
|
+
userPosition: {
|
|
37
|
+
id,
|
|
38
|
+
availableLiquidityQueueId: id,
|
|
39
|
+
user: user.toLowerCase(),
|
|
40
|
+
chainId,
|
|
41
|
+
amount: amountStr,
|
|
42
|
+
updatedAt
|
|
43
|
+
},
|
|
44
|
+
queues: [
|
|
45
|
+
{
|
|
46
|
+
queue: {
|
|
47
|
+
queueId: id,
|
|
48
|
+
availableLiquidityPoolId: id,
|
|
49
|
+
index,
|
|
50
|
+
updatedAt
|
|
51
|
+
},
|
|
52
|
+
pool: {
|
|
53
|
+
id,
|
|
54
|
+
amount: amountStr,
|
|
55
|
+
updatedAt
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function getCallbackIdForOffer(offer) {
|
|
62
|
+
if (offer.buy && offer.callback.data === "0x") {
|
|
63
|
+
const type = "buy_with_empty_callback" /* BuyWithEmptyCallback */;
|
|
64
|
+
const user = offer.offering;
|
|
65
|
+
const loanToken = offer.loanToken;
|
|
66
|
+
return `${user}-${offer.chainId.toString()}-${type}-${loanToken}`.toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/Cursor.ts
|
|
72
|
+
var Cursor_exports = {};
|
|
73
|
+
__export(Cursor_exports, {
|
|
74
|
+
decode: () => decode,
|
|
75
|
+
encode: () => encode,
|
|
76
|
+
validate: () => validate
|
|
77
|
+
});
|
|
78
|
+
function validate(cursor) {
|
|
79
|
+
if (!cursor || typeof cursor !== "object") {
|
|
80
|
+
throw new Error("Cursor must be an object");
|
|
81
|
+
}
|
|
82
|
+
const c = cursor;
|
|
83
|
+
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (!["asc", "desc"].includes(c.dir)) {
|
|
89
|
+
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
90
|
+
}
|
|
91
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const validations = {
|
|
97
|
+
rate: {
|
|
98
|
+
field: "rate",
|
|
99
|
+
type: "string",
|
|
100
|
+
pattern: /^\d+$/,
|
|
101
|
+
error: "numeric string"
|
|
102
|
+
},
|
|
103
|
+
amount: {
|
|
104
|
+
field: "assets",
|
|
105
|
+
type: "string",
|
|
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"
|
|
114
|
+
},
|
|
115
|
+
expiry: {
|
|
116
|
+
field: "expiry",
|
|
117
|
+
type: "number",
|
|
118
|
+
validator: (val) => val > 0,
|
|
119
|
+
error: "positive number"
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const validation = validations[c.sort];
|
|
123
|
+
if (!validation) {
|
|
124
|
+
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
125
|
+
}
|
|
126
|
+
const fieldValue = c[validation.field];
|
|
127
|
+
if (!fieldValue) {
|
|
128
|
+
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
129
|
+
}
|
|
130
|
+
if (typeof fieldValue !== validation.type) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (validation.validator && !validation.validator(fieldValue)) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
function encode(c) {
|
|
148
|
+
return jsBase64.Base64.encodeURL(JSON.stringify(c));
|
|
149
|
+
}
|
|
150
|
+
function decode(token) {
|
|
151
|
+
if (!token) return null;
|
|
152
|
+
const decoded = JSON.parse(jsBase64.Base64.decode(token));
|
|
153
|
+
validate(decoded);
|
|
154
|
+
return decoded;
|
|
155
|
+
}
|
|
156
|
+
|
|
18
157
|
// src/core/apiSchema/index.ts
|
|
19
158
|
var apiSchema_exports = {};
|
|
20
159
|
__export(apiSchema_exports, {
|
|
@@ -105,140 +244,6 @@ var InvalidRouterOfferError = class extends mempool.Errors.BaseError {
|
|
|
105
244
|
}
|
|
106
245
|
};
|
|
107
246
|
|
|
108
|
-
// src/utils/index.ts
|
|
109
|
-
var utils_exports = {};
|
|
110
|
-
__export(utils_exports, {
|
|
111
|
-
batch: () => batch,
|
|
112
|
-
decodeCursor: () => decodeCursor,
|
|
113
|
-
encodeCursor: () => encodeCursor,
|
|
114
|
-
poll: () => poll,
|
|
115
|
-
retry: () => retry,
|
|
116
|
-
validateCursor: () => validateCursor,
|
|
117
|
-
wait: () => wait
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// src/utils/batch.ts
|
|
121
|
-
function* batch(array, batchSize) {
|
|
122
|
-
for (let i = 0; i < array.length; i += batchSize) {
|
|
123
|
-
yield array.slice(i, i + batchSize);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function validateCursor(cursor) {
|
|
127
|
-
if (!cursor || typeof cursor !== "object") {
|
|
128
|
-
throw new Error("Cursor must be an object");
|
|
129
|
-
}
|
|
130
|
-
const c = cursor;
|
|
131
|
-
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
if (!["asc", "desc"].includes(c.dir)) {
|
|
137
|
-
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
138
|
-
}
|
|
139
|
-
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
140
|
-
throw new Error(
|
|
141
|
-
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
const validations = {
|
|
145
|
-
rate: {
|
|
146
|
-
field: "rate",
|
|
147
|
-
type: "string",
|
|
148
|
-
pattern: /^\d+$/,
|
|
149
|
-
error: "numeric string"
|
|
150
|
-
},
|
|
151
|
-
amount: {
|
|
152
|
-
field: "assets",
|
|
153
|
-
type: "string",
|
|
154
|
-
pattern: /^\d+$/,
|
|
155
|
-
error: "numeric string"
|
|
156
|
-
},
|
|
157
|
-
maturity: {
|
|
158
|
-
field: "maturity",
|
|
159
|
-
type: "number",
|
|
160
|
-
validator: (val) => val > 0,
|
|
161
|
-
error: "positive number"
|
|
162
|
-
},
|
|
163
|
-
expiry: {
|
|
164
|
-
field: "expiry",
|
|
165
|
-
type: "number",
|
|
166
|
-
validator: (val) => val > 0,
|
|
167
|
-
error: "positive number"
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
const validation = validations[c.sort];
|
|
171
|
-
if (!validation) {
|
|
172
|
-
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
173
|
-
}
|
|
174
|
-
const fieldValue = c[validation.field];
|
|
175
|
-
if (!fieldValue) {
|
|
176
|
-
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
177
|
-
}
|
|
178
|
-
if (typeof fieldValue !== validation.type) {
|
|
179
|
-
throw new Error(
|
|
180
|
-
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
184
|
-
throw new Error(
|
|
185
|
-
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
if (validation.validator && !validation.validator(fieldValue)) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
195
|
-
function encodeCursor(c) {
|
|
196
|
-
return jsBase64.Base64.encodeURL(JSON.stringify(c));
|
|
197
|
-
}
|
|
198
|
-
function decodeCursor(token) {
|
|
199
|
-
if (!token) return null;
|
|
200
|
-
const decoded = JSON.parse(jsBase64.Base64.decode(token));
|
|
201
|
-
validateCursor(decoded);
|
|
202
|
-
return decoded;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// src/utils/wait.ts
|
|
206
|
-
async function wait(time) {
|
|
207
|
-
return new Promise((res) => setTimeout(res, time));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// src/utils/poll.ts
|
|
211
|
-
function poll(fn, { interval }) {
|
|
212
|
-
let active = true;
|
|
213
|
-
const unwatch = () => active = false;
|
|
214
|
-
const watch = async () => {
|
|
215
|
-
await wait(interval);
|
|
216
|
-
const poll2 = async () => {
|
|
217
|
-
if (!active) return;
|
|
218
|
-
await fn({ unpoll: unwatch });
|
|
219
|
-
await wait(interval);
|
|
220
|
-
poll2();
|
|
221
|
-
};
|
|
222
|
-
poll2();
|
|
223
|
-
};
|
|
224
|
-
watch();
|
|
225
|
-
return unwatch;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/utils/retry.ts
|
|
229
|
-
var retry = async (fn, attempts = 3, delayMs = 50) => {
|
|
230
|
-
let lastErr;
|
|
231
|
-
for (let i = 0; i < attempts; i++) {
|
|
232
|
-
try {
|
|
233
|
-
return await fn();
|
|
234
|
-
} catch (err) {
|
|
235
|
-
lastErr = err;
|
|
236
|
-
if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
throw lastErr;
|
|
240
|
-
};
|
|
241
|
-
|
|
242
247
|
// src/core/apiSchema/requests.ts
|
|
243
248
|
var MAX_LIMIT = 100;
|
|
244
249
|
var DEFAULT_LIMIT = 20;
|
|
@@ -416,7 +421,7 @@ var GetOffersQueryParams = v4.z.object({
|
|
|
416
421
|
(val) => {
|
|
417
422
|
if (!val) return true;
|
|
418
423
|
try {
|
|
419
|
-
const decoded =
|
|
424
|
+
const decoded = decode(val);
|
|
420
425
|
return decoded !== null;
|
|
421
426
|
} catch (_error) {
|
|
422
427
|
return false;
|
|
@@ -583,21 +588,12 @@ var MatchOffersQueryParams = v4.z.object({
|
|
|
583
588
|
description: "Filter by a specific offer creator address",
|
|
584
589
|
example: "0x1234567890123456789012345678901234567890"
|
|
585
590
|
}),
|
|
586
|
-
// Status filtering
|
|
587
|
-
status: v4.z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
|
|
588
|
-
message: "Status must be comma-separated status values"
|
|
589
|
-
}).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
|
|
590
|
-
message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
|
|
591
|
-
}).optional().meta({
|
|
592
|
-
description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
|
|
593
|
-
example: "valid,callback_error"
|
|
594
|
-
}),
|
|
595
591
|
// Pagination
|
|
596
592
|
cursor: v4.z.string().optional().refine(
|
|
597
593
|
(val) => {
|
|
598
594
|
if (!val) return true;
|
|
599
595
|
try {
|
|
600
|
-
const decoded =
|
|
596
|
+
const decoded = decode(val);
|
|
601
597
|
return decoded !== null;
|
|
602
598
|
} catch (_error) {
|
|
603
599
|
return false;
|
|
@@ -1024,8 +1020,10 @@ function memory(parameters) {
|
|
|
1024
1020
|
const consumedIds = /* @__PURE__ */ new Set();
|
|
1025
1021
|
const create = async (parameters2) => {
|
|
1026
1022
|
if (map.has(parameters2.offer.hash.toLowerCase())) return parameters2.offer.hash;
|
|
1023
|
+
const callbackId = getCallbackIdForOffer(parameters2.offer);
|
|
1027
1024
|
map.set(parameters2.offer.hash.toLowerCase(), {
|
|
1028
1025
|
...parameters2.offer,
|
|
1026
|
+
...callbackId ? { callbackId } : {},
|
|
1029
1027
|
status: parameters2.status,
|
|
1030
1028
|
metadata: parameters2.metadata
|
|
1031
1029
|
});
|
|
@@ -1120,7 +1118,7 @@ function memory(parameters) {
|
|
|
1120
1118
|
...o,
|
|
1121
1119
|
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1122
1120
|
})).filter((o) => o.consumed < o.assets);
|
|
1123
|
-
const cursor =
|
|
1121
|
+
const cursor = decode(queryCursor);
|
|
1124
1122
|
if (cursor) {
|
|
1125
1123
|
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
1126
1124
|
throw new Error("Cursor does not match the current sort parameters");
|
|
@@ -1218,11 +1216,35 @@ function memory(parameters) {
|
|
|
1218
1216
|
default:
|
|
1219
1217
|
base.expiry = last.expiry;
|
|
1220
1218
|
}
|
|
1221
|
-
nextCursor =
|
|
1219
|
+
nextCursor = encode(base);
|
|
1222
1220
|
}
|
|
1223
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
|
+
}));
|
|
1224
1246
|
return {
|
|
1225
|
-
offers,
|
|
1247
|
+
offers: data,
|
|
1226
1248
|
nextCursor
|
|
1227
1249
|
};
|
|
1228
1250
|
},
|
|
@@ -1237,7 +1259,6 @@ function memory(parameters) {
|
|
|
1237
1259
|
maxMaturity,
|
|
1238
1260
|
loanToken,
|
|
1239
1261
|
creator,
|
|
1240
|
-
status,
|
|
1241
1262
|
cursor: queryCursor,
|
|
1242
1263
|
limit = 20
|
|
1243
1264
|
} = params;
|
|
@@ -1248,7 +1269,7 @@ function memory(parameters) {
|
|
|
1248
1269
|
...o,
|
|
1249
1270
|
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1250
1271
|
})).filter((o) => o.consumed < o.assets);
|
|
1251
|
-
const cursor =
|
|
1272
|
+
const cursor = decode(queryCursor);
|
|
1252
1273
|
if (cursor) {
|
|
1253
1274
|
if (cursor.sort !== "rate" || cursor.dir !== sortOrder) {
|
|
1254
1275
|
throw new Error("Cursor does not match the current sort parameters");
|
|
@@ -1283,7 +1304,7 @@ function memory(parameters) {
|
|
|
1283
1304
|
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1284
1305
|
loanToken && (offers = offers.filter((o) => o.loanToken.toLowerCase() === loanToken.toLowerCase()));
|
|
1285
1306
|
creator && (offers = offers.filter((o) => o.offering.toLowerCase() === creator.toLowerCase()));
|
|
1286
|
-
|
|
1307
|
+
offers = offers.filter((o) => ["valid"].includes(o.status));
|
|
1287
1308
|
const byGroup = /* @__PURE__ */ new Map();
|
|
1288
1309
|
for (const offer of offers) {
|
|
1289
1310
|
const groupKey = `${offer.chainId}-${offer.offering.toLowerCase()}-${offer.nonce}-${offer.buy}`;
|
|
@@ -1318,7 +1339,7 @@ function memory(parameters) {
|
|
|
1318
1339
|
let nextCursor = null;
|
|
1319
1340
|
if (offers.length > limit) {
|
|
1320
1341
|
const last = offers[limit - 1];
|
|
1321
|
-
nextCursor =
|
|
1342
|
+
nextCursor = encode({
|
|
1322
1343
|
sort: "rate",
|
|
1323
1344
|
dir: sortOrder,
|
|
1324
1345
|
hash: last.hash,
|
|
@@ -1326,8 +1347,32 @@ function memory(parameters) {
|
|
|
1326
1347
|
});
|
|
1327
1348
|
}
|
|
1328
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
|
+
}));
|
|
1329
1374
|
return {
|
|
1330
|
-
offers,
|
|
1375
|
+
offers: data,
|
|
1331
1376
|
nextCursor
|
|
1332
1377
|
};
|
|
1333
1378
|
},
|
|
@@ -1434,7 +1479,6 @@ async function serve(parameters) {
|
|
|
1434
1479
|
maxMaturity: params.max_maturity,
|
|
1435
1480
|
loanToken: params.loan_token,
|
|
1436
1481
|
creator: params.creator,
|
|
1437
|
-
status: params.status,
|
|
1438
1482
|
cursor: params.cursor,
|
|
1439
1483
|
limit: params.limit
|
|
1440
1484
|
});
|
|
@@ -1550,6 +1594,140 @@ function handleAPIError(error2, c) {
|
|
|
1550
1594
|
});
|
|
1551
1595
|
}
|
|
1552
1596
|
|
|
1597
|
+
// src/Liquidity.ts
|
|
1598
|
+
var Liquidity_exports = {};
|
|
1599
|
+
__export(Liquidity_exports, {
|
|
1600
|
+
fetch: () => fetch2,
|
|
1601
|
+
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
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
|
+
|
|
1553
1731
|
// src/Logger.ts
|
|
1554
1732
|
var Logger_exports = {};
|
|
1555
1733
|
__export(Logger_exports, {
|
|
@@ -1622,18 +1800,6 @@ function getLogger() {
|
|
|
1622
1800
|
return loggerContext.getStore() ?? defaultLogger();
|
|
1623
1801
|
}
|
|
1624
1802
|
|
|
1625
|
-
// src/RouterEvent.ts
|
|
1626
|
-
var RouterEvent_exports = {};
|
|
1627
|
-
__export(RouterEvent_exports, {
|
|
1628
|
-
from: () => from2,
|
|
1629
|
-
types: () => types
|
|
1630
|
-
});
|
|
1631
|
-
var types = ["offer_created", "offer_consumed", "offer_validation"];
|
|
1632
|
-
function from2(base) {
|
|
1633
|
-
const id = base.type === "offer_consumed" ? `${base.type}:${base.offerConsumed.id}` : `${base.type}:${base.offer.hash.toLowerCase()}`;
|
|
1634
|
-
return { id, ...base };
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
1803
|
// src/Validation.ts
|
|
1638
1804
|
var Validation_exports = {};
|
|
1639
1805
|
__export(Validation_exports, {
|
|
@@ -1684,35 +1850,29 @@ async function run(parameters) {
|
|
|
1684
1850
|
// src/ValidationRule.ts
|
|
1685
1851
|
var ValidationRule_exports = {};
|
|
1686
1852
|
__export(ValidationRule_exports, {
|
|
1687
|
-
batch: () =>
|
|
1853
|
+
batch: () => batch,
|
|
1688
1854
|
morpho: () => morpho,
|
|
1689
1855
|
single: () => single
|
|
1690
1856
|
});
|
|
1691
1857
|
function single(name, run2) {
|
|
1692
1858
|
return { kind: "single", name, run: run2 };
|
|
1693
1859
|
}
|
|
1694
|
-
function
|
|
1860
|
+
function batch(name, run2) {
|
|
1695
1861
|
return { kind: "batch", name, run: run2 };
|
|
1696
1862
|
}
|
|
1697
|
-
function morpho(
|
|
1698
|
-
const {
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
new Set(Array.from(chain.whitelistedAssets).map((a) => a.toLowerCase()))
|
|
1704
|
-
])
|
|
1705
|
-
);
|
|
1706
|
-
const morphoPerChain = new Map(
|
|
1707
|
-
whitelistedChains.map((chain) => [chain.id, chain.morpho.toLowerCase()])
|
|
1708
|
-
);
|
|
1709
|
-
const chainId = single("chain_id", (offer, _) => {
|
|
1710
|
-
if (!whitelistedChainIds.has(offer.chainId)) {
|
|
1711
|
-
return { message: `Chain ID ${offer.chainId} is not whitelisted` };
|
|
1863
|
+
function morpho() {
|
|
1864
|
+
const chainId = single("chain_id", (offer, { chain }) => {
|
|
1865
|
+
if (chain.id !== offer.chainId) {
|
|
1866
|
+
return {
|
|
1867
|
+
message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
|
|
1868
|
+
};
|
|
1712
1869
|
}
|
|
1713
1870
|
});
|
|
1714
|
-
const loanToken = single("loan_token", (offer,
|
|
1715
|
-
|
|
1871
|
+
const loanToken = single("loan_token", (offer, { chain }) => {
|
|
1872
|
+
const tokens = new Set(
|
|
1873
|
+
Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
|
|
1874
|
+
);
|
|
1875
|
+
if (!tokens.has(offer.loanToken.toLowerCase())) {
|
|
1716
1876
|
return {
|
|
1717
1877
|
message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
|
|
1718
1878
|
};
|
|
@@ -1723,137 +1883,29 @@ function morpho(parameters) {
|
|
|
1723
1883
|
return { message: "Expiry mismatch" };
|
|
1724
1884
|
}
|
|
1725
1885
|
});
|
|
1726
|
-
const
|
|
1727
|
-
if (offer.callback.data !== "0x") {
|
|
1728
|
-
return { message: "Callback
|
|
1886
|
+
const callback = single("empty_callback", (offer, _) => {
|
|
1887
|
+
if (!offer.buy || offer.callback.data !== "0x") {
|
|
1888
|
+
return { message: "Callback not supported yet." };
|
|
1729
1889
|
}
|
|
1730
1890
|
});
|
|
1731
|
-
const sellOffersEmptyCallback = single(
|
|
1732
|
-
"sell_offers_empty_callback",
|
|
1733
|
-
(offer, _) => {
|
|
1734
|
-
if (!offer.buy && offer.callback.data === "0x") {
|
|
1735
|
-
return { message: "Sell offers with empty callback are not supported yet." };
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
);
|
|
1739
|
-
const buyOffersEmptyCallback = batch2(
|
|
1740
|
-
"buy_offers_empty_callback",
|
|
1741
|
-
async (offers, { publicClients }) => {
|
|
1742
|
-
const issues = /* @__PURE__ */ new Map();
|
|
1743
|
-
const hashToIndex = /* @__PURE__ */ new Map();
|
|
1744
|
-
for (let i = 0; i < offers.length; i++) {
|
|
1745
|
-
const offer = offers[i];
|
|
1746
|
-
hashToIndex.set(offer.hash, i);
|
|
1747
|
-
}
|
|
1748
|
-
const { buyOffers, sellOffers: _sellOffers } = offers.reduce(
|
|
1749
|
-
(acc, offer) => {
|
|
1750
|
-
offer.buy ? acc.buyOffers.push(offer) : issues.set(hashToIndex.get(offer.hash), {
|
|
1751
|
-
message: "Onchain callback for sell offers is not supported yet."
|
|
1752
|
-
});
|
|
1753
|
-
return acc;
|
|
1754
|
-
},
|
|
1755
|
-
{ buyOffers: [], sellOffers: [] }
|
|
1756
|
-
);
|
|
1757
|
-
const buyOffersPerLoanAsset = /* @__PURE__ */ new Map();
|
|
1758
|
-
for (const offer of buyOffers) {
|
|
1759
|
-
const chainName = mempool.Chain.getChain(offer.chainId)?.name;
|
|
1760
|
-
const loanTokens = buyOffersPerLoanAsset.get(chainName) ?? /* @__PURE__ */ new Map();
|
|
1761
|
-
const offers2 = loanTokens.get(offer.loanToken.toLowerCase()) ?? [];
|
|
1762
|
-
offers2.push(offer);
|
|
1763
|
-
loanTokens.set(offer.loanToken.toLowerCase(), offers2);
|
|
1764
|
-
buyOffersPerLoanAsset.set(chainName, loanTokens);
|
|
1765
|
-
}
|
|
1766
|
-
await Promise.all(
|
|
1767
|
-
Array.from(buyOffersPerLoanAsset.entries()).map(async ([name, loanTokens]) => {
|
|
1768
|
-
const chainName = name;
|
|
1769
|
-
const publicClient = publicClients[chainName];
|
|
1770
|
-
const morpho2 = morphoPerChain.get(mempool.Chain.chains[chainName].id);
|
|
1771
|
-
if (!publicClient) {
|
|
1772
|
-
const offers2 = Array.from(loanTokens.values()).flat();
|
|
1773
|
-
for (const offer of offers2) {
|
|
1774
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1775
|
-
message: `Public client for chain "${chainName}" is not available`
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
return;
|
|
1779
|
-
}
|
|
1780
|
-
const balances = /* @__PURE__ */ new Map();
|
|
1781
|
-
const allowances = /* @__PURE__ */ new Map();
|
|
1782
|
-
for (const [loanToken2, offers2] of loanTokens) {
|
|
1783
|
-
const data = await Promise.all(
|
|
1784
|
-
offers2.flatMap((offer) => [
|
|
1785
|
-
publicClient.readContract({
|
|
1786
|
-
address: loanToken2,
|
|
1787
|
-
abi: viem.parseAbi([
|
|
1788
|
-
"function balanceOf(address owner) view returns (uint256 balance)"
|
|
1789
|
-
]),
|
|
1790
|
-
functionName: "balanceOf",
|
|
1791
|
-
args: [offer.offering]
|
|
1792
|
-
}),
|
|
1793
|
-
publicClient.readContract({
|
|
1794
|
-
address: loanToken2,
|
|
1795
|
-
abi: viem.parseAbi([
|
|
1796
|
-
"function allowance(address owner, address spender) public view returns (uint256 remaining)"
|
|
1797
|
-
]),
|
|
1798
|
-
functionName: "allowance",
|
|
1799
|
-
args: [offer.offering, morpho2]
|
|
1800
|
-
})
|
|
1801
|
-
])
|
|
1802
|
-
);
|
|
1803
|
-
for (let i = 0; i < offers2.length; i++) {
|
|
1804
|
-
const user = offers2[i].offering.toLowerCase();
|
|
1805
|
-
const balance = data[i * 2] || 0n;
|
|
1806
|
-
const allowance = data[i * 2 + 1] || 0n;
|
|
1807
|
-
const userBalances = balances.get(user) ?? /* @__PURE__ */ new Map();
|
|
1808
|
-
userBalances.set(loanToken2.toLowerCase(), balance);
|
|
1809
|
-
const userAllowances = allowances.get(user) ?? /* @__PURE__ */ new Map();
|
|
1810
|
-
userAllowances.set(loanToken2.toLowerCase(), allowance);
|
|
1811
|
-
balances.set(user, userBalances);
|
|
1812
|
-
allowances.set(user, userAllowances);
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
for (const offer of Array.from(loanTokens.values()).flat()) {
|
|
1816
|
-
const user = offer.offering.toLowerCase();
|
|
1817
|
-
const userBalances = balances.get(user);
|
|
1818
|
-
const balance = userBalances?.get(offer.loanToken.toLowerCase());
|
|
1819
|
-
if (balance < offer.assets) {
|
|
1820
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1821
|
-
message: `Insufficient balance for ${offer.loanToken} on chain ${offer.chainId} (${balance.toString()} < ${offer.assets.toString()})`
|
|
1822
|
-
});
|
|
1823
|
-
continue;
|
|
1824
|
-
}
|
|
1825
|
-
const userAllowances = allowances.get(user);
|
|
1826
|
-
const allowance = userAllowances?.get(offer.loanToken.toLowerCase());
|
|
1827
|
-
if (allowance < offer.assets) {
|
|
1828
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1829
|
-
message: `Insufficient allowance for ${offer.loanToken} on chain ${offer.chainId} (${allowance.toString()} < ${offer.assets.toString()})`
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
})
|
|
1834
|
-
);
|
|
1835
|
-
return issues;
|
|
1836
|
-
}
|
|
1837
|
-
);
|
|
1838
1891
|
return [
|
|
1839
1892
|
chainId,
|
|
1840
1893
|
loanToken,
|
|
1841
1894
|
expiry,
|
|
1842
|
-
// note: callback
|
|
1843
|
-
// integrators should be able to
|
|
1844
|
-
|
|
1845
|
-
sellOffersEmptyCallback,
|
|
1846
|
-
buyOffersEmptyCallback
|
|
1895
|
+
// note: callback rule should be the last one, since it does not mean that the offer is forever invalid
|
|
1896
|
+
// integrators should be able to choose if they want to keep the offer or not
|
|
1897
|
+
callback
|
|
1847
1898
|
];
|
|
1848
1899
|
}
|
|
1849
1900
|
|
|
1850
1901
|
exports.ApiSchema = apiSchema_exports;
|
|
1902
|
+
exports.Callback = Callback_exports;
|
|
1903
|
+
exports.Cursor = Cursor_exports;
|
|
1904
|
+
exports.Liquidity = Liquidity_exports;
|
|
1851
1905
|
exports.Logger = Logger_exports;
|
|
1852
1906
|
exports.OfferStore = OfferStore_exports;
|
|
1853
1907
|
exports.Router = router_exports;
|
|
1854
|
-
exports.RouterEvent = RouterEvent_exports;
|
|
1855
1908
|
exports.RouterOffer = RouterOffer_exports;
|
|
1856
|
-
exports.Utils = utils_exports;
|
|
1857
1909
|
exports.Validation = Validation_exports;
|
|
1858
1910
|
exports.ValidationRule = ValidationRule_exports;
|
|
1859
1911
|
Object.keys(mempool).forEach(function (k) {
|