@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.browser.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 viem = require('viem');
|
|
5
6
|
var v4 = require('zod/v4');
|
|
6
7
|
var zodOpenapi = require('zod-openapi');
|
|
7
|
-
var jsBase64 = require('js-base64');
|
|
8
8
|
|
|
9
9
|
var __defProp = Object.defineProperty;
|
|
10
10
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -14,6 +14,145 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
|
|
16
16
|
|
|
17
|
+
// src/Callback.ts
|
|
18
|
+
var Callback_exports = {};
|
|
19
|
+
__export(Callback_exports, {
|
|
20
|
+
CallbackType: () => CallbackType,
|
|
21
|
+
buildLiquidity: () => buildLiquidity,
|
|
22
|
+
getCallbackIdForOffer: () => getCallbackIdForOffer
|
|
23
|
+
});
|
|
24
|
+
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
25
|
+
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
26
|
+
return CallbackType2;
|
|
27
|
+
})(CallbackType || {});
|
|
28
|
+
function buildLiquidity(parameters) {
|
|
29
|
+
const { type, user, contract, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
30
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
31
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
32
|
+
const amountStr = amount.toString();
|
|
33
|
+
const id = `${user}-${chainId.toString()}-${type}-${contract}`.toLowerCase();
|
|
34
|
+
return {
|
|
35
|
+
userPosition: {
|
|
36
|
+
id,
|
|
37
|
+
availableLiquidityQueueId: id,
|
|
38
|
+
user: user.toLowerCase(),
|
|
39
|
+
chainId,
|
|
40
|
+
amount: amountStr,
|
|
41
|
+
updatedAt
|
|
42
|
+
},
|
|
43
|
+
queues: [
|
|
44
|
+
{
|
|
45
|
+
queue: {
|
|
46
|
+
queueId: id,
|
|
47
|
+
availableLiquidityPoolId: id,
|
|
48
|
+
index,
|
|
49
|
+
updatedAt
|
|
50
|
+
},
|
|
51
|
+
pool: {
|
|
52
|
+
id,
|
|
53
|
+
amount: amountStr,
|
|
54
|
+
updatedAt
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function getCallbackIdForOffer(offer) {
|
|
61
|
+
if (offer.buy && offer.callback.data === "0x") {
|
|
62
|
+
const type = "buy_with_empty_callback" /* BuyWithEmptyCallback */;
|
|
63
|
+
const user = offer.offering;
|
|
64
|
+
const loanToken = offer.loanToken;
|
|
65
|
+
return `${user}-${offer.chainId.toString()}-${type}-${loanToken}`.toLowerCase();
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/Cursor.ts
|
|
71
|
+
var Cursor_exports = {};
|
|
72
|
+
__export(Cursor_exports, {
|
|
73
|
+
decode: () => decode,
|
|
74
|
+
encode: () => encode,
|
|
75
|
+
validate: () => validate
|
|
76
|
+
});
|
|
77
|
+
function validate(cursor) {
|
|
78
|
+
if (!cursor || typeof cursor !== "object") {
|
|
79
|
+
throw new Error("Cursor must be an object");
|
|
80
|
+
}
|
|
81
|
+
const c = cursor;
|
|
82
|
+
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!["asc", "desc"].includes(c.dir)) {
|
|
88
|
+
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
89
|
+
}
|
|
90
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const validations = {
|
|
96
|
+
rate: {
|
|
97
|
+
field: "rate",
|
|
98
|
+
type: "string",
|
|
99
|
+
pattern: /^\d+$/,
|
|
100
|
+
error: "numeric string"
|
|
101
|
+
},
|
|
102
|
+
amount: {
|
|
103
|
+
field: "assets",
|
|
104
|
+
type: "string",
|
|
105
|
+
pattern: /^\d+$/,
|
|
106
|
+
error: "numeric string"
|
|
107
|
+
},
|
|
108
|
+
maturity: {
|
|
109
|
+
field: "maturity",
|
|
110
|
+
type: "number",
|
|
111
|
+
validator: (val) => val > 0,
|
|
112
|
+
error: "positive number"
|
|
113
|
+
},
|
|
114
|
+
expiry: {
|
|
115
|
+
field: "expiry",
|
|
116
|
+
type: "number",
|
|
117
|
+
validator: (val) => val > 0,
|
|
118
|
+
error: "positive number"
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const validation = validations[c.sort];
|
|
122
|
+
if (!validation) {
|
|
123
|
+
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
124
|
+
}
|
|
125
|
+
const fieldValue = c[validation.field];
|
|
126
|
+
if (!fieldValue) {
|
|
127
|
+
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
128
|
+
}
|
|
129
|
+
if (typeof fieldValue !== validation.type) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (validation.validator && !validation.validator(fieldValue)) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
function encode(c) {
|
|
147
|
+
return jsBase64.Base64.encodeURL(JSON.stringify(c));
|
|
148
|
+
}
|
|
149
|
+
function decode(token) {
|
|
150
|
+
if (!token) return null;
|
|
151
|
+
const decoded = JSON.parse(jsBase64.Base64.decode(token));
|
|
152
|
+
validate(decoded);
|
|
153
|
+
return decoded;
|
|
154
|
+
}
|
|
155
|
+
|
|
17
156
|
// src/core/router/Client.ts
|
|
18
157
|
var Client_exports = {};
|
|
19
158
|
__export(Client_exports, {
|
|
@@ -107,140 +246,6 @@ var InvalidRouterOfferError = class extends mempool.Errors.BaseError {
|
|
|
107
246
|
}
|
|
108
247
|
};
|
|
109
248
|
|
|
110
|
-
// src/utils/index.ts
|
|
111
|
-
var utils_exports = {};
|
|
112
|
-
__export(utils_exports, {
|
|
113
|
-
batch: () => batch,
|
|
114
|
-
decodeCursor: () => decodeCursor,
|
|
115
|
-
encodeCursor: () => encodeCursor,
|
|
116
|
-
poll: () => poll,
|
|
117
|
-
retry: () => retry,
|
|
118
|
-
validateCursor: () => validateCursor,
|
|
119
|
-
wait: () => wait
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// src/utils/batch.ts
|
|
123
|
-
function* batch(array, batchSize) {
|
|
124
|
-
for (let i = 0; i < array.length; i += batchSize) {
|
|
125
|
-
yield array.slice(i, i + batchSize);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function validateCursor(cursor) {
|
|
129
|
-
if (!cursor || typeof cursor !== "object") {
|
|
130
|
-
throw new Error("Cursor must be an object");
|
|
131
|
-
}
|
|
132
|
-
const c = cursor;
|
|
133
|
-
if (!["rate", "maturity", "expiry", "amount"].includes(c.sort)) {
|
|
134
|
-
throw new Error(
|
|
135
|
-
`Invalid sort field: ${c.sort}. Must be one of: rate, maturity, expiry, amount`
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
if (!["asc", "desc"].includes(c.dir)) {
|
|
139
|
-
throw new Error(`Invalid direction: ${c.dir}. Must be one of: asc, desc`);
|
|
140
|
-
}
|
|
141
|
-
if (!/^0x[a-fA-F0-9]{64}$/.test(c.hash)) {
|
|
142
|
-
throw new Error(
|
|
143
|
-
`Invalid hash format: ${c.hash}. Must be a 64-character hex string starting with 0x`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
const validations = {
|
|
147
|
-
rate: {
|
|
148
|
-
field: "rate",
|
|
149
|
-
type: "string",
|
|
150
|
-
pattern: /^\d+$/,
|
|
151
|
-
error: "numeric string"
|
|
152
|
-
},
|
|
153
|
-
amount: {
|
|
154
|
-
field: "assets",
|
|
155
|
-
type: "string",
|
|
156
|
-
pattern: /^\d+$/,
|
|
157
|
-
error: "numeric string"
|
|
158
|
-
},
|
|
159
|
-
maturity: {
|
|
160
|
-
field: "maturity",
|
|
161
|
-
type: "number",
|
|
162
|
-
validator: (val) => val > 0,
|
|
163
|
-
error: "positive number"
|
|
164
|
-
},
|
|
165
|
-
expiry: {
|
|
166
|
-
field: "expiry",
|
|
167
|
-
type: "number",
|
|
168
|
-
validator: (val) => val > 0,
|
|
169
|
-
error: "positive number"
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
const validation = validations[c.sort];
|
|
173
|
-
if (!validation) {
|
|
174
|
-
throw new Error(`Invalid sort field: ${c.sort}`);
|
|
175
|
-
}
|
|
176
|
-
const fieldValue = c[validation.field];
|
|
177
|
-
if (!fieldValue) {
|
|
178
|
-
throw new Error(`${c.sort} sort requires '${validation.field}' field to be present`);
|
|
179
|
-
}
|
|
180
|
-
if (typeof fieldValue !== validation.type) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
`${c.sort} sort requires '${validation.field}' field of type ${validation.type}`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
if (validation.pattern && !validation.pattern.test(fieldValue)) {
|
|
186
|
-
throw new Error(
|
|
187
|
-
`Invalid ${validation.field} format: ${fieldValue}. Must be a ${validation.error}`
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
if (validation.validator && !validation.validator(fieldValue)) {
|
|
191
|
-
throw new Error(
|
|
192
|
-
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
function encodeCursor(c) {
|
|
198
|
-
return jsBase64.Base64.encodeURL(JSON.stringify(c));
|
|
199
|
-
}
|
|
200
|
-
function decodeCursor(token) {
|
|
201
|
-
if (!token) return null;
|
|
202
|
-
const decoded = JSON.parse(jsBase64.Base64.decode(token));
|
|
203
|
-
validateCursor(decoded);
|
|
204
|
-
return decoded;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// src/utils/wait.ts
|
|
208
|
-
async function wait(time) {
|
|
209
|
-
return new Promise((res) => setTimeout(res, time));
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// src/utils/poll.ts
|
|
213
|
-
function poll(fn, { interval }) {
|
|
214
|
-
let active = true;
|
|
215
|
-
const unwatch = () => active = false;
|
|
216
|
-
const watch = async () => {
|
|
217
|
-
await wait(interval);
|
|
218
|
-
const poll2 = async () => {
|
|
219
|
-
if (!active) return;
|
|
220
|
-
await fn({ unpoll: unwatch });
|
|
221
|
-
await wait(interval);
|
|
222
|
-
poll2();
|
|
223
|
-
};
|
|
224
|
-
poll2();
|
|
225
|
-
};
|
|
226
|
-
watch();
|
|
227
|
-
return unwatch;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// src/utils/retry.ts
|
|
231
|
-
var retry = async (fn, attempts = 3, delayMs = 50) => {
|
|
232
|
-
let lastErr;
|
|
233
|
-
for (let i = 0; i < attempts; i++) {
|
|
234
|
-
try {
|
|
235
|
-
return await fn();
|
|
236
|
-
} catch (err) {
|
|
237
|
-
lastErr = err;
|
|
238
|
-
if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
throw lastErr;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
249
|
// src/core/apiSchema/requests.ts
|
|
245
250
|
var MAX_LIMIT = 100;
|
|
246
251
|
var DEFAULT_LIMIT = 20;
|
|
@@ -418,7 +423,7 @@ var GetOffersQueryParams = v4.z.object({
|
|
|
418
423
|
(val) => {
|
|
419
424
|
if (!val) return true;
|
|
420
425
|
try {
|
|
421
|
-
const decoded =
|
|
426
|
+
const decoded = decode(val);
|
|
422
427
|
return decoded !== null;
|
|
423
428
|
} catch (_error) {
|
|
424
429
|
return false;
|
|
@@ -585,21 +590,12 @@ var MatchOffersQueryParams = v4.z.object({
|
|
|
585
590
|
description: "Filter by a specific offer creator address",
|
|
586
591
|
example: "0x1234567890123456789012345678901234567890"
|
|
587
592
|
}),
|
|
588
|
-
// Status filtering
|
|
589
|
-
status: v4.z.string().regex(/^[a-zA-Z_]+(,[a-zA-Z_]+)*$/, {
|
|
590
|
-
message: "Status must be comma-separated status values"
|
|
591
|
-
}).transform((val) => val.split(",")).refine((statuses) => statuses.every((status) => OfferStatusValues.includes(status)), {
|
|
592
|
-
message: `Invalid status value. Must be one of: ${OfferStatusValues.join(", ")}`
|
|
593
|
-
}).optional().meta({
|
|
594
|
-
description: `Filter by multiple statuses (comma-separated). Valid values: ${OfferStatusValues.join(", ")}. By default, only offers with 'valid' status are returned.`,
|
|
595
|
-
example: "valid,callback_error"
|
|
596
|
-
}),
|
|
597
593
|
// Pagination
|
|
598
594
|
cursor: v4.z.string().optional().refine(
|
|
599
595
|
(val) => {
|
|
600
596
|
if (!val) return true;
|
|
601
597
|
try {
|
|
602
|
-
const decoded =
|
|
598
|
+
const decoded = decode(val);
|
|
603
599
|
return decoded !== null;
|
|
604
600
|
} catch (_error) {
|
|
605
601
|
return false;
|
|
@@ -992,6 +988,140 @@ var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
|
|
|
992
988
|
}
|
|
993
989
|
};
|
|
994
990
|
|
|
991
|
+
// src/Liquidity.ts
|
|
992
|
+
var Liquidity_exports = {};
|
|
993
|
+
__export(Liquidity_exports, {
|
|
994
|
+
fetch: () => fetch2,
|
|
995
|
+
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
996
|
+
serialize: () => serialize
|
|
997
|
+
});
|
|
998
|
+
async function fetchBalancesAndAllowances(parameters) {
|
|
999
|
+
const { client, spender, pairs, options } = parameters;
|
|
1000
|
+
if (pairs.length === 0) return /* @__PURE__ */ new Map();
|
|
1001
|
+
const batchSize = Math.max(1, options?.batchSize ?? 5e3);
|
|
1002
|
+
const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
|
|
1003
|
+
const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
|
|
1004
|
+
const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
|
|
1005
|
+
const out = /* @__PURE__ */ new Map();
|
|
1006
|
+
for (const pairsBatch of mempool.Utils.batch(pairs, batchSize)) {
|
|
1007
|
+
const balanceContracts = [];
|
|
1008
|
+
const allowanceContracts = [];
|
|
1009
|
+
for (const { user, token } of pairsBatch) {
|
|
1010
|
+
balanceContracts.push({
|
|
1011
|
+
address: token,
|
|
1012
|
+
abi: viem.erc20Abi,
|
|
1013
|
+
functionName: "balanceOf",
|
|
1014
|
+
args: [user]
|
|
1015
|
+
});
|
|
1016
|
+
allowanceContracts.push({
|
|
1017
|
+
address: token,
|
|
1018
|
+
abi: viem.erc20Abi,
|
|
1019
|
+
functionName: "allowance",
|
|
1020
|
+
args: [user, spender]
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
const [balances, allowances] = await Promise.all([
|
|
1024
|
+
mempool.Utils.retry(
|
|
1025
|
+
() => client.multicall({
|
|
1026
|
+
allowFailure: false,
|
|
1027
|
+
contracts: balanceContracts,
|
|
1028
|
+
...blockNumber ? { blockNumber } : {}
|
|
1029
|
+
}),
|
|
1030
|
+
retryAttempts,
|
|
1031
|
+
retryDelayMs
|
|
1032
|
+
),
|
|
1033
|
+
mempool.Utils.retry(
|
|
1034
|
+
() => client.multicall({
|
|
1035
|
+
allowFailure: false,
|
|
1036
|
+
contracts: allowanceContracts,
|
|
1037
|
+
...blockNumber ? { blockNumber } : {}
|
|
1038
|
+
}),
|
|
1039
|
+
retryAttempts,
|
|
1040
|
+
retryDelayMs
|
|
1041
|
+
)
|
|
1042
|
+
]);
|
|
1043
|
+
for (let i = 0; i < pairsBatch.length; i++) {
|
|
1044
|
+
const { user, token } = pairsBatch[i];
|
|
1045
|
+
const balance = balances[i];
|
|
1046
|
+
const allowance = allowances[i];
|
|
1047
|
+
let perUser = out.get(user);
|
|
1048
|
+
if (!perUser) {
|
|
1049
|
+
perUser = /* @__PURE__ */ new Map();
|
|
1050
|
+
out.set(user, perUser);
|
|
1051
|
+
}
|
|
1052
|
+
perUser.set(token, { balance, allowance });
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return out;
|
|
1056
|
+
}
|
|
1057
|
+
async function fetch2(parameters) {
|
|
1058
|
+
const { client, chainId, spender, type, pairs, options } = parameters;
|
|
1059
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
1060
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
1061
|
+
const map = await fetchBalancesAndAllowances({
|
|
1062
|
+
client,
|
|
1063
|
+
spender,
|
|
1064
|
+
pairs: pairs.map(({ user, contract }) => ({ user, token: contract })),
|
|
1065
|
+
options
|
|
1066
|
+
});
|
|
1067
|
+
const out = [];
|
|
1068
|
+
for (const [user, perContract] of map) {
|
|
1069
|
+
for (const [contract, { balance, allowance }] of perContract) {
|
|
1070
|
+
const amount = balance < allowance ? balance : allowance;
|
|
1071
|
+
out.push(
|
|
1072
|
+
buildLiquidity({
|
|
1073
|
+
type,
|
|
1074
|
+
user,
|
|
1075
|
+
contract,
|
|
1076
|
+
chainId,
|
|
1077
|
+
amount: amount.toString(),
|
|
1078
|
+
index: 0
|
|
1079
|
+
})
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return out;
|
|
1084
|
+
}
|
|
1085
|
+
function serialize(liquidity) {
|
|
1086
|
+
const normalized = {
|
|
1087
|
+
userPosition: {
|
|
1088
|
+
id: liquidity.userPosition.id,
|
|
1089
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
1090
|
+
user: liquidity.userPosition.user,
|
|
1091
|
+
chainId: String(liquidity.userPosition.chainId),
|
|
1092
|
+
amount: String(liquidity.userPosition.amount)
|
|
1093
|
+
},
|
|
1094
|
+
queues: liquidity.queues.map((queueWithPool) => ({
|
|
1095
|
+
queue: {
|
|
1096
|
+
queueId: queueWithPool.queue.queueId,
|
|
1097
|
+
availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
|
|
1098
|
+
index: queueWithPool.queue.index
|
|
1099
|
+
},
|
|
1100
|
+
pool: {
|
|
1101
|
+
id: queueWithPool.pool.id,
|
|
1102
|
+
amount: String(queueWithPool.pool.amount)
|
|
1103
|
+
}
|
|
1104
|
+
})).sort(
|
|
1105
|
+
(left, right) => {
|
|
1106
|
+
const leftQueueId = left.queue.queueId || "";
|
|
1107
|
+
const rightQueueId = right.queue.queueId || "";
|
|
1108
|
+
if (leftQueueId < rightQueueId) return -1;
|
|
1109
|
+
if (leftQueueId > rightQueueId) return 1;
|
|
1110
|
+
const leftPoolId = left.pool.id;
|
|
1111
|
+
const rightPoolId = right.pool.id;
|
|
1112
|
+
if (leftPoolId < rightPoolId) return -1;
|
|
1113
|
+
if (leftPoolId > rightPoolId) return 1;
|
|
1114
|
+
const leftIndex = left.queue.index;
|
|
1115
|
+
const rightIndex = right.queue.index;
|
|
1116
|
+
if (leftIndex < rightIndex) return -1;
|
|
1117
|
+
if (leftIndex > rightIndex) return 1;
|
|
1118
|
+
return 0;
|
|
1119
|
+
}
|
|
1120
|
+
)
|
|
1121
|
+
};
|
|
1122
|
+
return JSON.stringify(normalized);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
995
1125
|
// src/OfferStore/index.ts
|
|
996
1126
|
var OfferStore_exports = {};
|
|
997
1127
|
__export(OfferStore_exports, {
|
|
@@ -1003,8 +1133,10 @@ function memory(parameters) {
|
|
|
1003
1133
|
const consumedIds = /* @__PURE__ */ new Set();
|
|
1004
1134
|
const create = async (parameters2) => {
|
|
1005
1135
|
if (map.has(parameters2.offer.hash.toLowerCase())) return parameters2.offer.hash;
|
|
1136
|
+
const callbackId = getCallbackIdForOffer(parameters2.offer);
|
|
1006
1137
|
map.set(parameters2.offer.hash.toLowerCase(), {
|
|
1007
1138
|
...parameters2.offer,
|
|
1139
|
+
...callbackId ? { callbackId } : {},
|
|
1008
1140
|
status: parameters2.status,
|
|
1009
1141
|
metadata: parameters2.metadata
|
|
1010
1142
|
});
|
|
@@ -1099,7 +1231,7 @@ function memory(parameters) {
|
|
|
1099
1231
|
...o,
|
|
1100
1232
|
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1101
1233
|
})).filter((o) => o.consumed < o.assets);
|
|
1102
|
-
const cursor =
|
|
1234
|
+
const cursor = decode(queryCursor);
|
|
1103
1235
|
if (cursor) {
|
|
1104
1236
|
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
1105
1237
|
throw new Error("Cursor does not match the current sort parameters");
|
|
@@ -1197,11 +1329,35 @@ function memory(parameters) {
|
|
|
1197
1329
|
default:
|
|
1198
1330
|
base.expiry = last.expiry;
|
|
1199
1331
|
}
|
|
1200
|
-
nextCursor =
|
|
1332
|
+
nextCursor = encode(base);
|
|
1201
1333
|
}
|
|
1202
1334
|
offers = offers.slice(0, limit);
|
|
1335
|
+
const data = offers.map((o) => ({
|
|
1336
|
+
...mempool.Offer.from({
|
|
1337
|
+
offering: o.offering,
|
|
1338
|
+
assets: o.assets,
|
|
1339
|
+
rate: o.rate,
|
|
1340
|
+
maturity: mempool.Maturity.from(o.maturity),
|
|
1341
|
+
expiry: o.expiry,
|
|
1342
|
+
start: o.start,
|
|
1343
|
+
nonce: o.nonce,
|
|
1344
|
+
buy: o.buy,
|
|
1345
|
+
chainId: o.chainId,
|
|
1346
|
+
loanToken: o.loanToken,
|
|
1347
|
+
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1348
|
+
callback: {
|
|
1349
|
+
address: o.callback.address,
|
|
1350
|
+
data: o.callback.data,
|
|
1351
|
+
gasLimit: o.callback.gasLimit
|
|
1352
|
+
},
|
|
1353
|
+
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1354
|
+
}),
|
|
1355
|
+
consumed: o.consumed,
|
|
1356
|
+
status: o.status,
|
|
1357
|
+
...o.metadata ? { metadata: o.metadata } : {}
|
|
1358
|
+
}));
|
|
1203
1359
|
return {
|
|
1204
|
-
offers,
|
|
1360
|
+
offers: data,
|
|
1205
1361
|
nextCursor
|
|
1206
1362
|
};
|
|
1207
1363
|
},
|
|
@@ -1216,7 +1372,6 @@ function memory(parameters) {
|
|
|
1216
1372
|
maxMaturity,
|
|
1217
1373
|
loanToken,
|
|
1218
1374
|
creator,
|
|
1219
|
-
status,
|
|
1220
1375
|
cursor: queryCursor,
|
|
1221
1376
|
limit = 20
|
|
1222
1377
|
} = params;
|
|
@@ -1227,7 +1382,7 @@ function memory(parameters) {
|
|
|
1227
1382
|
...o,
|
|
1228
1383
|
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1229
1384
|
})).filter((o) => o.consumed < o.assets);
|
|
1230
|
-
const cursor =
|
|
1385
|
+
const cursor = decode(queryCursor);
|
|
1231
1386
|
if (cursor) {
|
|
1232
1387
|
if (cursor.sort !== "rate" || cursor.dir !== sortOrder) {
|
|
1233
1388
|
throw new Error("Cursor does not match the current sort parameters");
|
|
@@ -1262,7 +1417,7 @@ function memory(parameters) {
|
|
|
1262
1417
|
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1263
1418
|
loanToken && (offers = offers.filter((o) => o.loanToken.toLowerCase() === loanToken.toLowerCase()));
|
|
1264
1419
|
creator && (offers = offers.filter((o) => o.offering.toLowerCase() === creator.toLowerCase()));
|
|
1265
|
-
|
|
1420
|
+
offers = offers.filter((o) => ["valid"].includes(o.status));
|
|
1266
1421
|
const byGroup = /* @__PURE__ */ new Map();
|
|
1267
1422
|
for (const offer of offers) {
|
|
1268
1423
|
const groupKey = `${offer.chainId}-${offer.offering.toLowerCase()}-${offer.nonce}-${offer.buy}`;
|
|
@@ -1297,7 +1452,7 @@ function memory(parameters) {
|
|
|
1297
1452
|
let nextCursor = null;
|
|
1298
1453
|
if (offers.length > limit) {
|
|
1299
1454
|
const last = offers[limit - 1];
|
|
1300
|
-
nextCursor =
|
|
1455
|
+
nextCursor = encode({
|
|
1301
1456
|
sort: "rate",
|
|
1302
1457
|
dir: sortOrder,
|
|
1303
1458
|
hash: last.hash,
|
|
@@ -1305,8 +1460,32 @@ function memory(parameters) {
|
|
|
1305
1460
|
});
|
|
1306
1461
|
}
|
|
1307
1462
|
offers = offers.slice(0, limit);
|
|
1463
|
+
const data = offers.map((o) => ({
|
|
1464
|
+
...mempool.Offer.from({
|
|
1465
|
+
offering: o.offering,
|
|
1466
|
+
assets: o.assets,
|
|
1467
|
+
rate: o.rate,
|
|
1468
|
+
maturity: mempool.Maturity.from(o.maturity),
|
|
1469
|
+
expiry: o.expiry,
|
|
1470
|
+
start: o.start,
|
|
1471
|
+
nonce: o.nonce,
|
|
1472
|
+
buy: o.buy,
|
|
1473
|
+
chainId: o.chainId,
|
|
1474
|
+
loanToken: o.loanToken,
|
|
1475
|
+
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1476
|
+
callback: {
|
|
1477
|
+
address: o.callback.address,
|
|
1478
|
+
data: o.callback.data,
|
|
1479
|
+
gasLimit: o.callback.gasLimit
|
|
1480
|
+
},
|
|
1481
|
+
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1482
|
+
}),
|
|
1483
|
+
consumed: o.consumed,
|
|
1484
|
+
status: o.status,
|
|
1485
|
+
...o.metadata ? { metadata: o.metadata } : {}
|
|
1486
|
+
}));
|
|
1308
1487
|
return {
|
|
1309
|
-
offers,
|
|
1488
|
+
offers: data,
|
|
1310
1489
|
nextCursor
|
|
1311
1490
|
};
|
|
1312
1491
|
},
|
|
@@ -1352,18 +1531,6 @@ function memory(parameters) {
|
|
|
1352
1531
|
};
|
|
1353
1532
|
}
|
|
1354
1533
|
|
|
1355
|
-
// src/RouterEvent.ts
|
|
1356
|
-
var RouterEvent_exports = {};
|
|
1357
|
-
__export(RouterEvent_exports, {
|
|
1358
|
-
from: () => from2,
|
|
1359
|
-
types: () => types
|
|
1360
|
-
});
|
|
1361
|
-
var types = ["offer_created", "offer_consumed", "offer_validation"];
|
|
1362
|
-
function from2(base) {
|
|
1363
|
-
const id = base.type === "offer_consumed" ? `${base.type}:${base.offerConsumed.id}` : `${base.type}:${base.offer.hash.toLowerCase()}`;
|
|
1364
|
-
return { id, ...base };
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
1534
|
// src/Validation.ts
|
|
1368
1535
|
var Validation_exports = {};
|
|
1369
1536
|
__export(Validation_exports, {
|
|
@@ -1414,35 +1581,29 @@ async function run(parameters) {
|
|
|
1414
1581
|
// src/ValidationRule.ts
|
|
1415
1582
|
var ValidationRule_exports = {};
|
|
1416
1583
|
__export(ValidationRule_exports, {
|
|
1417
|
-
batch: () =>
|
|
1584
|
+
batch: () => batch,
|
|
1418
1585
|
morpho: () => morpho,
|
|
1419
1586
|
single: () => single
|
|
1420
1587
|
});
|
|
1421
1588
|
function single(name, run2) {
|
|
1422
1589
|
return { kind: "single", name, run: run2 };
|
|
1423
1590
|
}
|
|
1424
|
-
function
|
|
1591
|
+
function batch(name, run2) {
|
|
1425
1592
|
return { kind: "batch", name, run: run2 };
|
|
1426
1593
|
}
|
|
1427
|
-
function morpho(
|
|
1428
|
-
const {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
new Set(Array.from(chain.whitelistedAssets).map((a) => a.toLowerCase()))
|
|
1434
|
-
])
|
|
1435
|
-
);
|
|
1436
|
-
const morphoPerChain = new Map(
|
|
1437
|
-
whitelistedChains.map((chain) => [chain.id, chain.morpho.toLowerCase()])
|
|
1438
|
-
);
|
|
1439
|
-
const chainId = single("chain_id", (offer, _) => {
|
|
1440
|
-
if (!whitelistedChainIds.has(offer.chainId)) {
|
|
1441
|
-
return { message: `Chain ID ${offer.chainId} is not whitelisted` };
|
|
1594
|
+
function morpho() {
|
|
1595
|
+
const chainId = single("chain_id", (offer, { chain }) => {
|
|
1596
|
+
if (chain.id !== offer.chainId) {
|
|
1597
|
+
return {
|
|
1598
|
+
message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
|
|
1599
|
+
};
|
|
1442
1600
|
}
|
|
1443
1601
|
});
|
|
1444
|
-
const loanToken = single("loan_token", (offer,
|
|
1445
|
-
|
|
1602
|
+
const loanToken = single("loan_token", (offer, { chain }) => {
|
|
1603
|
+
const tokens = new Set(
|
|
1604
|
+
Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
|
|
1605
|
+
);
|
|
1606
|
+
if (!tokens.has(offer.loanToken.toLowerCase())) {
|
|
1446
1607
|
return {
|
|
1447
1608
|
message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
|
|
1448
1609
|
};
|
|
@@ -1453,135 +1614,27 @@ function morpho(parameters) {
|
|
|
1453
1614
|
return { message: "Expiry mismatch" };
|
|
1454
1615
|
}
|
|
1455
1616
|
});
|
|
1456
|
-
const
|
|
1457
|
-
if (offer.callback.data !== "0x") {
|
|
1458
|
-
return { message: "Callback
|
|
1617
|
+
const callback = single("empty_callback", (offer, _) => {
|
|
1618
|
+
if (!offer.buy || offer.callback.data !== "0x") {
|
|
1619
|
+
return { message: "Callback not supported yet." };
|
|
1459
1620
|
}
|
|
1460
1621
|
});
|
|
1461
|
-
const sellOffersEmptyCallback = single(
|
|
1462
|
-
"sell_offers_empty_callback",
|
|
1463
|
-
(offer, _) => {
|
|
1464
|
-
if (!offer.buy && offer.callback.data === "0x") {
|
|
1465
|
-
return { message: "Sell offers with empty callback are not supported yet." };
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
);
|
|
1469
|
-
const buyOffersEmptyCallback = batch2(
|
|
1470
|
-
"buy_offers_empty_callback",
|
|
1471
|
-
async (offers, { publicClients }) => {
|
|
1472
|
-
const issues = /* @__PURE__ */ new Map();
|
|
1473
|
-
const hashToIndex = /* @__PURE__ */ new Map();
|
|
1474
|
-
for (let i = 0; i < offers.length; i++) {
|
|
1475
|
-
const offer = offers[i];
|
|
1476
|
-
hashToIndex.set(offer.hash, i);
|
|
1477
|
-
}
|
|
1478
|
-
const { buyOffers, sellOffers: _sellOffers } = offers.reduce(
|
|
1479
|
-
(acc, offer) => {
|
|
1480
|
-
offer.buy ? acc.buyOffers.push(offer) : issues.set(hashToIndex.get(offer.hash), {
|
|
1481
|
-
message: "Onchain callback for sell offers is not supported yet."
|
|
1482
|
-
});
|
|
1483
|
-
return acc;
|
|
1484
|
-
},
|
|
1485
|
-
{ buyOffers: [], sellOffers: [] }
|
|
1486
|
-
);
|
|
1487
|
-
const buyOffersPerLoanAsset = /* @__PURE__ */ new Map();
|
|
1488
|
-
for (const offer of buyOffers) {
|
|
1489
|
-
const chainName = mempool.Chain.getChain(offer.chainId)?.name;
|
|
1490
|
-
const loanTokens = buyOffersPerLoanAsset.get(chainName) ?? /* @__PURE__ */ new Map();
|
|
1491
|
-
const offers2 = loanTokens.get(offer.loanToken.toLowerCase()) ?? [];
|
|
1492
|
-
offers2.push(offer);
|
|
1493
|
-
loanTokens.set(offer.loanToken.toLowerCase(), offers2);
|
|
1494
|
-
buyOffersPerLoanAsset.set(chainName, loanTokens);
|
|
1495
|
-
}
|
|
1496
|
-
await Promise.all(
|
|
1497
|
-
Array.from(buyOffersPerLoanAsset.entries()).map(async ([name, loanTokens]) => {
|
|
1498
|
-
const chainName = name;
|
|
1499
|
-
const publicClient = publicClients[chainName];
|
|
1500
|
-
const morpho2 = morphoPerChain.get(mempool.Chain.chains[chainName].id);
|
|
1501
|
-
if (!publicClient) {
|
|
1502
|
-
const offers2 = Array.from(loanTokens.values()).flat();
|
|
1503
|
-
for (const offer of offers2) {
|
|
1504
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1505
|
-
message: `Public client for chain "${chainName}" is not available`
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
return;
|
|
1509
|
-
}
|
|
1510
|
-
const balances = /* @__PURE__ */ new Map();
|
|
1511
|
-
const allowances = /* @__PURE__ */ new Map();
|
|
1512
|
-
for (const [loanToken2, offers2] of loanTokens) {
|
|
1513
|
-
const data = await Promise.all(
|
|
1514
|
-
offers2.flatMap((offer) => [
|
|
1515
|
-
publicClient.readContract({
|
|
1516
|
-
address: loanToken2,
|
|
1517
|
-
abi: viem.parseAbi([
|
|
1518
|
-
"function balanceOf(address owner) view returns (uint256 balance)"
|
|
1519
|
-
]),
|
|
1520
|
-
functionName: "balanceOf",
|
|
1521
|
-
args: [offer.offering]
|
|
1522
|
-
}),
|
|
1523
|
-
publicClient.readContract({
|
|
1524
|
-
address: loanToken2,
|
|
1525
|
-
abi: viem.parseAbi([
|
|
1526
|
-
"function allowance(address owner, address spender) public view returns (uint256 remaining)"
|
|
1527
|
-
]),
|
|
1528
|
-
functionName: "allowance",
|
|
1529
|
-
args: [offer.offering, morpho2]
|
|
1530
|
-
})
|
|
1531
|
-
])
|
|
1532
|
-
);
|
|
1533
|
-
for (let i = 0; i < offers2.length; i++) {
|
|
1534
|
-
const user = offers2[i].offering.toLowerCase();
|
|
1535
|
-
const balance = data[i * 2] || 0n;
|
|
1536
|
-
const allowance = data[i * 2 + 1] || 0n;
|
|
1537
|
-
const userBalances = balances.get(user) ?? /* @__PURE__ */ new Map();
|
|
1538
|
-
userBalances.set(loanToken2.toLowerCase(), balance);
|
|
1539
|
-
const userAllowances = allowances.get(user) ?? /* @__PURE__ */ new Map();
|
|
1540
|
-
userAllowances.set(loanToken2.toLowerCase(), allowance);
|
|
1541
|
-
balances.set(user, userBalances);
|
|
1542
|
-
allowances.set(user, userAllowances);
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
for (const offer of Array.from(loanTokens.values()).flat()) {
|
|
1546
|
-
const user = offer.offering.toLowerCase();
|
|
1547
|
-
const userBalances = balances.get(user);
|
|
1548
|
-
const balance = userBalances?.get(offer.loanToken.toLowerCase());
|
|
1549
|
-
if (balance < offer.assets) {
|
|
1550
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1551
|
-
message: `Insufficient balance for ${offer.loanToken} on chain ${offer.chainId} (${balance.toString()} < ${offer.assets.toString()})`
|
|
1552
|
-
});
|
|
1553
|
-
continue;
|
|
1554
|
-
}
|
|
1555
|
-
const userAllowances = allowances.get(user);
|
|
1556
|
-
const allowance = userAllowances?.get(offer.loanToken.toLowerCase());
|
|
1557
|
-
if (allowance < offer.assets) {
|
|
1558
|
-
issues.set(hashToIndex.get(offer.hash), {
|
|
1559
|
-
message: `Insufficient allowance for ${offer.loanToken} on chain ${offer.chainId} (${allowance.toString()} < ${offer.assets.toString()})`
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
})
|
|
1564
|
-
);
|
|
1565
|
-
return issues;
|
|
1566
|
-
}
|
|
1567
|
-
);
|
|
1568
1622
|
return [
|
|
1569
1623
|
chainId,
|
|
1570
1624
|
loanToken,
|
|
1571
1625
|
expiry,
|
|
1572
|
-
// note: callback
|
|
1573
|
-
// integrators should be able to
|
|
1574
|
-
|
|
1575
|
-
sellOffersEmptyCallback,
|
|
1576
|
-
buyOffersEmptyCallback
|
|
1626
|
+
// note: callback rule should be the last one, since it does not mean that the offer is forever invalid
|
|
1627
|
+
// integrators should be able to choose if they want to keep the offer or not
|
|
1628
|
+
callback
|
|
1577
1629
|
];
|
|
1578
1630
|
}
|
|
1579
1631
|
|
|
1632
|
+
exports.Callback = Callback_exports;
|
|
1633
|
+
exports.Cursor = Cursor_exports;
|
|
1634
|
+
exports.Liquidity = Liquidity_exports;
|
|
1580
1635
|
exports.OfferStore = OfferStore_exports;
|
|
1581
1636
|
exports.Router = Client_exports;
|
|
1582
|
-
exports.RouterEvent = RouterEvent_exports;
|
|
1583
1637
|
exports.RouterOffer = RouterOffer_exports;
|
|
1584
|
-
exports.Utils = utils_exports;
|
|
1585
1638
|
exports.Validation = Validation_exports;
|
|
1586
1639
|
exports.ValidationRule = ValidationRule_exports;
|
|
1587
1640
|
Object.keys(mempool).forEach(function (k) {
|