@morpho-dev/router 0.1.11 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -64
- package/dist/cli.js +6043 -0
- package/dist/cli.js.map +1 -0
- package/dist/drizzle/router_v1.4/0000_add_obligation_id.sql +112 -0
- package/dist/drizzle/router_v1.4/0001_update-primary-key-on-link.sql +3 -0
- package/dist/drizzle/{offers_v1.1/meta/0006_snapshot.json → router_v1.4/meta/0000_snapshot.json} +344 -256
- package/dist/drizzle/{offers_v1.1/meta/0007_snapshot.json → router_v1.4/meta/0001_snapshot.json} +306 -266
- package/dist/drizzle/router_v1.4/meta/_journal.json +20 -0
- package/dist/index.browser.d.cts +1367 -559
- package/dist/index.browser.d.ts +1367 -559
- package/dist/index.browser.js +1911 -835
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +1878 -830
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +2827 -1562
- package/dist/index.node.d.ts +2827 -1562
- package/dist/index.node.js +5964 -2718
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +5915 -2710
- package/dist/index.node.mjs.map +1 -1
- package/package.json +16 -10
- package/dist/drizzle/offers_v1.1/0000_init.sql +0 -95
- package/dist/drizzle/offers_v1.1/0001_new_table_for_collectors_block_numbers.sql +0 -5
- package/dist/drizzle/offers_v1.1/0002_update-liquidity-tables.sql +0 -8
- package/dist/drizzle/offers_v1.1/0003_add-not-null-for-queue-id.sql +0 -1
- package/dist/drizzle/offers_v1.1/0004_add-callback-id-to-offer.sql +0 -1
- package/dist/drizzle/offers_v1.1/0005_add-missing-indices-to-liquidity-tables.sql +0 -2
- package/dist/drizzle/offers_v1.1/0006_add-callback-amount-to-queues-table.sql +0 -1
- package/dist/drizzle/offers_v1.1/0007_add-index-to-created-at.sql +0 -2
- package/dist/drizzle/offers_v1.1/meta/0000_snapshot.json +0 -827
- package/dist/drizzle/offers_v1.1/meta/0001_snapshot.json +0 -827
- package/dist/drizzle/offers_v1.1/meta/0002_snapshot.json +0 -833
- package/dist/drizzle/offers_v1.1/meta/0003_snapshot.json +0 -833
- package/dist/drizzle/offers_v1.1/meta/0004_snapshot.json +0 -839
- package/dist/drizzle/offers_v1.1/meta/0005_snapshot.json +0 -877
- package/dist/drizzle/offers_v1.1/meta/_journal.json +0 -62
package/dist/index.browser.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { Errors, LLTV, Offer, Utils, Format } from '@morpho-dev/mempool';
|
|
2
|
-
export * from '@morpho-dev/mempool';
|
|
3
|
-
import { parseUnits, encodeAbiParameters, maxUint256, formatUnits, decodeAbiParameters, erc20Abi } from 'viem';
|
|
4
|
-
import { Base64 } from 'js-base64';
|
|
5
1
|
import { z } from 'zod/v4';
|
|
2
|
+
import { maxUint256, isAddress, isHex, decodeAbiParameters, encodeAbiParameters, keccak256, zeroAddress, hashTypedData, getAddress, publicActions } from 'viem';
|
|
3
|
+
import { getBlock, getLogs } from 'viem/actions';
|
|
4
|
+
import { base, mainnet, anvil } from 'viem/chains';
|
|
5
|
+
import * as z7 from 'zod';
|
|
6
|
+
import { Base64 } from 'js-base64';
|
|
7
|
+
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
|
|
6
8
|
import { createDocument } from 'zod-openapi';
|
|
7
9
|
|
|
8
10
|
var __defProp = Object.defineProperty;
|
|
@@ -11,122 +13,289 @@ var __export = (target, all) => {
|
|
|
11
13
|
for (var name in all)
|
|
12
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
15
|
};
|
|
14
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
|
|
16
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
17
|
+
|
|
18
|
+
// src/api/Api/Schema/index.ts
|
|
19
|
+
var Schema_exports = {};
|
|
20
|
+
__export(Schema_exports, {
|
|
21
|
+
ChainHealth: () => ChainHealth,
|
|
22
|
+
ChainsHealthResponse: () => ChainsHealthResponse,
|
|
23
|
+
CollectorHealth: () => CollectorHealth,
|
|
24
|
+
CollectorsHealthResponse: () => CollectorsHealthResponse,
|
|
25
|
+
ObligationResponse: () => ObligationResponse_exports,
|
|
26
|
+
OfferResponse: () => OfferResponse_exports,
|
|
27
|
+
OpenApi: () => OpenApi,
|
|
28
|
+
RouterStatusResponse: () => RouterStatusResponse,
|
|
29
|
+
parse: () => parse,
|
|
30
|
+
safeParse: () => safeParse
|
|
31
|
+
});
|
|
32
|
+
var CollectorHealth = z.object({
|
|
33
|
+
name: z.string(),
|
|
34
|
+
chain_id: z.number(),
|
|
35
|
+
block_number: z.number().nullable(),
|
|
36
|
+
updated_at: z.string().nullable(),
|
|
37
|
+
lag: z.number().nullable(),
|
|
38
|
+
status: z.enum(["live", "lagging", "unknown"])
|
|
39
|
+
});
|
|
40
|
+
var CollectorsHealthResponse = z.object({
|
|
41
|
+
collectors: z.array(CollectorHealth)
|
|
42
|
+
});
|
|
43
|
+
var ChainHealth = z.object({
|
|
44
|
+
chain_id: z.number(),
|
|
45
|
+
block_number: z.number(),
|
|
46
|
+
updated_at: z.string()
|
|
47
|
+
});
|
|
48
|
+
var ChainsHealthResponse = z.object({
|
|
49
|
+
chains: z.array(ChainHealth)
|
|
50
|
+
});
|
|
51
|
+
var RouterStatusResponse = z.object({
|
|
52
|
+
status: z.enum(["live", "syncing"])
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// src/api/Api/Schema/ObligationResponse.ts
|
|
56
|
+
var ObligationResponse_exports = {};
|
|
57
|
+
__export(ObligationResponse_exports, {
|
|
58
|
+
from: () => from6
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// src/core/Abi.ts
|
|
62
|
+
var Abi_exports = {};
|
|
63
|
+
__export(Abi_exports, {
|
|
64
|
+
ERC4626: () => ERC4626,
|
|
65
|
+
MetaMorpho: () => MetaMorpho,
|
|
66
|
+
MetaMorphoFactory: () => MetaMorphoFactory,
|
|
67
|
+
Morpho: () => Morpho,
|
|
68
|
+
Oracle: () => Oracle
|
|
69
|
+
});
|
|
70
|
+
var Oracle = [
|
|
71
|
+
{
|
|
72
|
+
type: "function",
|
|
73
|
+
name: "price",
|
|
74
|
+
inputs: [],
|
|
75
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
76
|
+
stateMutability: "view"
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
var ERC4626 = [
|
|
80
|
+
{
|
|
81
|
+
type: "function",
|
|
82
|
+
name: "asset",
|
|
83
|
+
inputs: [],
|
|
84
|
+
outputs: [{ name: "", type: "address" }],
|
|
85
|
+
stateMutability: "view"
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
var MetaMorphoFactory = [
|
|
89
|
+
{
|
|
90
|
+
type: "function",
|
|
91
|
+
name: "isMetaMorpho",
|
|
92
|
+
inputs: [{ name: "target", type: "address" }],
|
|
93
|
+
outputs: [{ name: "", type: "bool" }],
|
|
94
|
+
stateMutability: "view"
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
var MetaMorpho = [
|
|
98
|
+
{
|
|
99
|
+
type: "function",
|
|
100
|
+
name: "withdrawQueue",
|
|
101
|
+
inputs: [{ name: "index", type: "uint256" }],
|
|
102
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
103
|
+
stateMutability: "view"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "function",
|
|
107
|
+
name: "withdrawQueueLength",
|
|
108
|
+
inputs: [],
|
|
109
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
110
|
+
stateMutability: "view"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: "function",
|
|
114
|
+
name: "maxWithdraw",
|
|
115
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
116
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
117
|
+
stateMutability: "view"
|
|
118
|
+
}
|
|
119
|
+
];
|
|
120
|
+
var Morpho = [
|
|
121
|
+
{
|
|
122
|
+
type: "function",
|
|
123
|
+
name: "collateralOf",
|
|
124
|
+
inputs: [
|
|
125
|
+
{
|
|
126
|
+
name: "",
|
|
127
|
+
type: "address",
|
|
128
|
+
internalType: "address"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "",
|
|
132
|
+
type: "bytes32",
|
|
133
|
+
internalType: "bytes32"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "",
|
|
137
|
+
type: "address",
|
|
138
|
+
internalType: "address"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
outputs: [
|
|
142
|
+
{
|
|
143
|
+
name: "",
|
|
144
|
+
type: "uint256",
|
|
145
|
+
internalType: "uint256"
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
stateMutability: "view"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: "function",
|
|
152
|
+
name: "debtOf",
|
|
153
|
+
inputs: [
|
|
154
|
+
{
|
|
155
|
+
name: "",
|
|
156
|
+
type: "address",
|
|
157
|
+
internalType: "address"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "",
|
|
161
|
+
type: "bytes32",
|
|
162
|
+
internalType: "bytes32"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
outputs: [
|
|
166
|
+
{
|
|
167
|
+
name: "",
|
|
168
|
+
type: "uint256",
|
|
169
|
+
internalType: "uint256"
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
stateMutability: "view"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: "function",
|
|
176
|
+
name: "market",
|
|
177
|
+
inputs: [
|
|
178
|
+
{
|
|
179
|
+
name: "id",
|
|
180
|
+
type: "bytes32",
|
|
181
|
+
internalType: "Id"
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
outputs: [
|
|
185
|
+
{
|
|
186
|
+
name: "totalSupplyAssets",
|
|
187
|
+
type: "uint128",
|
|
188
|
+
internalType: "uint128"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "totalSupplyShares",
|
|
192
|
+
type: "uint128",
|
|
193
|
+
internalType: "uint128"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "totalBorrowAssets",
|
|
197
|
+
type: "uint128",
|
|
198
|
+
internalType: "uint128"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "totalBorrowShares",
|
|
202
|
+
type: "uint128",
|
|
203
|
+
internalType: "uint128"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: "lastUpdate",
|
|
207
|
+
type: "uint128",
|
|
208
|
+
internalType: "uint128"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
name: "fee",
|
|
212
|
+
type: "uint128",
|
|
213
|
+
internalType: "uint128"
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
stateMutability: "view"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: "function",
|
|
220
|
+
name: "position",
|
|
221
|
+
inputs: [
|
|
222
|
+
{
|
|
223
|
+
name: "id",
|
|
224
|
+
type: "bytes32",
|
|
225
|
+
internalType: "Id"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "user",
|
|
229
|
+
type: "address",
|
|
230
|
+
internalType: "address"
|
|
231
|
+
}
|
|
232
|
+
],
|
|
233
|
+
outputs: [
|
|
234
|
+
{
|
|
235
|
+
name: "supplyShares",
|
|
236
|
+
type: "uint256",
|
|
237
|
+
internalType: "uint256"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "borrowShares",
|
|
241
|
+
type: "uint128",
|
|
242
|
+
internalType: "uint128"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "collateral",
|
|
246
|
+
type: "uint128",
|
|
247
|
+
internalType: "uint128"
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
stateMutability: "view"
|
|
251
|
+
}
|
|
252
|
+
];
|
|
15
253
|
|
|
16
254
|
// src/core/Callback.ts
|
|
17
255
|
var Callback_exports = {};
|
|
18
256
|
__export(Callback_exports, {
|
|
19
257
|
CallbackType: () => CallbackType,
|
|
20
258
|
WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
259
|
+
decodeBuyVaultV1Callback: () => decodeBuyVaultV1Callback,
|
|
260
|
+
decodeSellERC20Callback: () => decodeSellERC20Callback,
|
|
261
|
+
encodeBuyVaultV1Callback: () => encodeBuyVaultV1Callback,
|
|
262
|
+
encodeSellERC20Callback: () => encodeSellERC20Callback
|
|
25
263
|
});
|
|
26
264
|
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
27
265
|
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
28
|
-
CallbackType2["
|
|
266
|
+
CallbackType2["BuyVaultV1Callback"] = "buy_vault_v1_callback";
|
|
267
|
+
CallbackType2["SellERC20Callback"] = "sell_erc20_callback";
|
|
29
268
|
return CallbackType2;
|
|
30
269
|
})(CallbackType || {});
|
|
31
270
|
var WhitelistedCallbackAddresses = {
|
|
32
271
|
["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
|
|
33
|
-
["
|
|
272
|
+
["buy_vault_v1_callback" /* BuyVaultV1Callback */]: [
|
|
273
|
+
"0x3333333333333333333333333333333333333333",
|
|
274
|
+
"0x4444444444444444444444444444444444444444"
|
|
275
|
+
// @TODO: update once deployed and add mapping per chain if needed
|
|
276
|
+
].map((address) => address.toLowerCase()),
|
|
277
|
+
["sell_erc20_callback" /* SellERC20Callback */]: [
|
|
34
278
|
"0x1111111111111111111111111111111111111111",
|
|
35
279
|
"0x2222222222222222222222222222222222222222"
|
|
36
280
|
// @TODO: update once deployed and add mapping per chain if needed
|
|
37
281
|
].map((address) => address.toLowerCase())
|
|
38
282
|
};
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
id,
|
|
49
|
-
availableLiquidityQueueId: id,
|
|
50
|
-
user: user.toLowerCase(),
|
|
51
|
-
chainId,
|
|
52
|
-
amount: amountStr,
|
|
53
|
-
updatedAt
|
|
54
|
-
},
|
|
55
|
-
queues: [
|
|
56
|
-
{
|
|
57
|
-
queue: {
|
|
58
|
-
queueId: id,
|
|
59
|
-
availableLiquidityPoolId: poolId,
|
|
60
|
-
index,
|
|
61
|
-
callbackAmount: "0",
|
|
62
|
-
updatedAt
|
|
63
|
-
},
|
|
64
|
-
pool: {
|
|
65
|
-
id: poolId,
|
|
66
|
-
amount: amountStr,
|
|
67
|
-
updatedAt
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
case "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */: {
|
|
74
|
-
const {
|
|
75
|
-
user,
|
|
76
|
-
termId,
|
|
77
|
-
chainId,
|
|
78
|
-
amount,
|
|
79
|
-
collaterals,
|
|
80
|
-
index = 0,
|
|
81
|
-
updatedAt = /* @__PURE__ */ new Date()
|
|
82
|
-
} = parameters;
|
|
83
|
-
const amountStr = amount.toString();
|
|
84
|
-
const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}`.toLowerCase();
|
|
85
|
-
return {
|
|
86
|
-
userPosition: {
|
|
87
|
-
id,
|
|
88
|
-
availableLiquidityQueueId: id,
|
|
89
|
-
user: user.toLowerCase(),
|
|
90
|
-
chainId,
|
|
91
|
-
amount: amountStr,
|
|
92
|
-
updatedAt
|
|
93
|
-
},
|
|
94
|
-
queues: collaterals.map((collateral) => {
|
|
95
|
-
const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
|
|
96
|
-
return {
|
|
97
|
-
queue: {
|
|
98
|
-
queueId: id,
|
|
99
|
-
availableLiquidityPoolId: poolId,
|
|
100
|
-
index,
|
|
101
|
-
callbackAmount: collateral.callbackAmount.toString(),
|
|
102
|
-
updatedAt
|
|
103
|
-
},
|
|
104
|
-
pool: {
|
|
105
|
-
id: poolId,
|
|
106
|
-
amount: collateral.balance.toString(),
|
|
107
|
-
updatedAt
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
})
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
default: {
|
|
114
|
-
throw new Error(`CallbackType not implemented`);
|
|
283
|
+
function decodeBuyVaultV1Callback(data) {
|
|
284
|
+
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
285
|
+
try {
|
|
286
|
+
const [vaults, amounts] = decodeAbiParameters(
|
|
287
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
288
|
+
data
|
|
289
|
+
);
|
|
290
|
+
if (vaults.length !== amounts.length) {
|
|
291
|
+
throw new Error("Mismatched array lengths");
|
|
115
292
|
}
|
|
293
|
+
return vaults.map((v, i) => ({ vault: v, amount: amounts[i] }));
|
|
294
|
+
} catch (_) {
|
|
295
|
+
throw new Error("Invalid BuyVaultV1Callback callback data");
|
|
116
296
|
}
|
|
117
297
|
}
|
|
118
|
-
function
|
|
119
|
-
if (offer.buy && offer.callback.data === "0x") {
|
|
120
|
-
return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
|
|
121
|
-
}
|
|
122
|
-
if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].includes(
|
|
123
|
-
offer.callback.address.toLowerCase()
|
|
124
|
-
)) {
|
|
125
|
-
return `${offer.offering}-${offer.chainId.toString()}-${"sell_withdraw_from_wallet" /* SellWithdrawFromWallet */}-${Offer.termId(offer)}`.toLowerCase();
|
|
126
|
-
}
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
function decodeSellWithdrawFromWalletData(data) {
|
|
298
|
+
function decodeSellERC20Callback(data) {
|
|
130
299
|
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
131
300
|
try {
|
|
132
301
|
const [collaterals, amounts] = decodeAbiParameters(
|
|
@@ -138,32 +307,449 @@ function decodeSellWithdrawFromWalletData(data) {
|
|
|
138
307
|
}
|
|
139
308
|
return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
|
|
140
309
|
} catch (_) {
|
|
141
|
-
throw new Error("Invalid
|
|
310
|
+
throw new Error("Invalid SellERC20Callback callback data");
|
|
142
311
|
}
|
|
143
312
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
313
|
+
function encodeBuyVaultV1Callback(parameters) {
|
|
314
|
+
return encodeAbiParameters(
|
|
315
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
316
|
+
[parameters.vaults, parameters.amounts]
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
function encodeSellERC20Callback(parameters) {
|
|
320
|
+
return encodeAbiParameters(
|
|
321
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
322
|
+
[parameters.collaterals, parameters.amounts]
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/core/Chain.ts
|
|
327
|
+
var Chain_exports = {};
|
|
328
|
+
__export(Chain_exports, {
|
|
329
|
+
ChainId: () => ChainId,
|
|
330
|
+
InvalidBatchSizeError: () => InvalidBatchSizeError,
|
|
331
|
+
InvalidBlockRangeError: () => InvalidBlockRangeError,
|
|
332
|
+
InvalidBlockWindowError: () => InvalidBlockWindowError,
|
|
333
|
+
MissingBlockNumberError: () => MissingBlockNumberError,
|
|
334
|
+
chainIds: () => chainIds,
|
|
335
|
+
chainNames: () => chainNames,
|
|
336
|
+
chains: () => chains,
|
|
337
|
+
getChain: () => getChain,
|
|
338
|
+
getWhitelistedChains: () => getWhitelistedChains,
|
|
339
|
+
streamLogs: () => streamLogs
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// src/utils/BigMath.ts
|
|
343
|
+
function max(a, b) {
|
|
344
|
+
return a > b ? a : b;
|
|
345
|
+
}
|
|
346
|
+
function min(a, b) {
|
|
347
|
+
return a < b ? a : b;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/utils/batch.ts
|
|
351
|
+
function* batch(array2, batchSize) {
|
|
352
|
+
for (let i = 0; i < array2.length; i += batchSize) {
|
|
353
|
+
yield array2.slice(i, i + batchSize);
|
|
148
354
|
}
|
|
149
|
-
throw new Error(`CallbackType not implemented: ${type}`);
|
|
150
355
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
356
|
+
|
|
357
|
+
// src/utils/Errors.ts
|
|
358
|
+
var Errors_exports = {};
|
|
359
|
+
__export(Errors_exports, {
|
|
360
|
+
BaseError: () => BaseError
|
|
361
|
+
});
|
|
362
|
+
var BaseError = class _BaseError extends Error {
|
|
363
|
+
constructor(shortMessage, options = {}) {
|
|
364
|
+
const details = (() => {
|
|
365
|
+
if (options.cause instanceof _BaseError) {
|
|
366
|
+
if (options.cause.details) return options.cause.details;
|
|
367
|
+
if (options.cause.shortMessage) return options.cause.shortMessage;
|
|
368
|
+
}
|
|
369
|
+
if (options.cause && "details" in options.cause && typeof options.cause.details === "string")
|
|
370
|
+
return options.cause.details;
|
|
371
|
+
if (options.cause?.message) return options.cause.message;
|
|
372
|
+
return options.details;
|
|
373
|
+
})();
|
|
374
|
+
const message = [
|
|
375
|
+
shortMessage || "An error occurred.",
|
|
376
|
+
...options.metaMessages ? ["", ...options.metaMessages] : [],
|
|
377
|
+
...details ? ["", details ? `Details: ${details}` : void 0] : []
|
|
378
|
+
].filter((x) => typeof x === "string").join("\n");
|
|
379
|
+
super(message, options.cause ? { cause: options.cause } : void 0);
|
|
380
|
+
__publicField(this, "details");
|
|
381
|
+
__publicField(this, "shortMessage");
|
|
382
|
+
__publicField(this, "cause");
|
|
383
|
+
__publicField(this, "name", "BaseError");
|
|
384
|
+
this.cause = options.cause;
|
|
385
|
+
this.details = details;
|
|
386
|
+
this.shortMessage = shortMessage;
|
|
387
|
+
}
|
|
388
|
+
walk(fn) {
|
|
389
|
+
return walk(this, fn);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
function walk(err, fn) {
|
|
393
|
+
if (fn?.(err)) return err;
|
|
394
|
+
if (err && typeof err === "object" && "cause" in err && err.cause) return walk(err.cause, fn);
|
|
395
|
+
return fn ? null : err;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/core/Chain.ts
|
|
399
|
+
var chainNames = ["ethereum", "base", "ethereum-virtual-testnet", "anvil"];
|
|
400
|
+
var ChainId = {
|
|
401
|
+
ETHEREUM: BigInt(mainnet.id),
|
|
402
|
+
BASE: BigInt(base.id),
|
|
403
|
+
"ETHEREUM-VIRTUAL-TESTNET": 109111114n,
|
|
404
|
+
ANVIL: 505050505n
|
|
405
|
+
// random id to not clash with other chains
|
|
406
|
+
};
|
|
407
|
+
var chainIds = new Set(Object.values(ChainId));
|
|
408
|
+
var chainNameLookup = new Map(Object.entries(ChainId).map(([key, value]) => [value, key]));
|
|
409
|
+
function getChain(chainId) {
|
|
410
|
+
const chainName = chainNameLookup.get(chainId)?.toLowerCase();
|
|
411
|
+
if (!chainName) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
return chains[chainName];
|
|
415
|
+
}
|
|
416
|
+
var getWhitelistedChains = () => {
|
|
417
|
+
return [chains.ethereum, chains.base, chains["ethereum-virtual-testnet"], chains.anvil];
|
|
418
|
+
};
|
|
419
|
+
var chains = {
|
|
420
|
+
ethereum: {
|
|
421
|
+
...mainnet,
|
|
422
|
+
id: ChainId.ETHEREUM,
|
|
423
|
+
name: "ethereum",
|
|
424
|
+
whitelistedAssets: new Set(
|
|
425
|
+
[
|
|
426
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
427
|
+
// USDC
|
|
428
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
429
|
+
// DAI
|
|
430
|
+
].map((address) => address.toLowerCase())
|
|
431
|
+
),
|
|
432
|
+
morpho: "0x0000000000000000000000000000000000000000",
|
|
433
|
+
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
|
|
434
|
+
mempool: {
|
|
435
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
436
|
+
deploymentBlock: 23347674,
|
|
437
|
+
reindexBuffer: 10
|
|
438
|
+
},
|
|
439
|
+
vaultV1Factory: {
|
|
440
|
+
"v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
|
|
441
|
+
"v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
base: {
|
|
445
|
+
...base,
|
|
446
|
+
id: ChainId.BASE,
|
|
447
|
+
name: "base",
|
|
448
|
+
whitelistedAssets: new Set(
|
|
449
|
+
[
|
|
450
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
451
|
+
// USDC
|
|
452
|
+
"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb"
|
|
453
|
+
// DAI
|
|
454
|
+
].map((address) => address.toLowerCase())
|
|
455
|
+
),
|
|
456
|
+
morpho: "0x0000000000000000000000000000000000000000",
|
|
457
|
+
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
|
|
458
|
+
mempool: {
|
|
459
|
+
address: "0x0000000000000000000000000000000000000000",
|
|
460
|
+
deploymentBlock: 35449942,
|
|
461
|
+
reindexBuffer: 10
|
|
462
|
+
},
|
|
463
|
+
vaultV1Factory: {
|
|
464
|
+
"v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
|
|
465
|
+
"v1.1": "0xFf62A7c278C62eD665133147129245053Bbf5918"
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
"ethereum-virtual-testnet": {
|
|
469
|
+
...mainnet,
|
|
470
|
+
id: ChainId["ETHEREUM-VIRTUAL-TESTNET"],
|
|
471
|
+
name: "ethereum-virtual-testnet",
|
|
472
|
+
whitelistedAssets: new Set(
|
|
473
|
+
[
|
|
474
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
475
|
+
// USDC
|
|
476
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
477
|
+
// DAI
|
|
478
|
+
].map((address) => address.toLowerCase())
|
|
479
|
+
),
|
|
480
|
+
morpho: "0x11a002d45db720ed47a80d2f3489cba5b833eaf5",
|
|
481
|
+
// @TODO: This is mock Consumed contract, update with Terms once stable
|
|
482
|
+
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
|
|
483
|
+
mempool: {
|
|
484
|
+
address: "0x5b06224f736a57635b5bcb50b8ef178b189107cb",
|
|
485
|
+
deploymentBlock: 23224302,
|
|
486
|
+
reindexBuffer: 10
|
|
487
|
+
},
|
|
488
|
+
vaultV1Factory: {
|
|
489
|
+
"v1.0": "0xA9c3D3a366466Fa809d1Ae982Fb2c46E5fC41101",
|
|
490
|
+
"v1.1": "0x1897A8997241C1cD4bD0698647e4EB7213535c24"
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
anvil: {
|
|
494
|
+
...anvil,
|
|
495
|
+
id: ChainId.ANVIL,
|
|
496
|
+
name: "anvil",
|
|
497
|
+
whitelistedAssets: new Set(
|
|
498
|
+
[
|
|
499
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
500
|
+
// USDC
|
|
501
|
+
"0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
|
502
|
+
// DAI
|
|
503
|
+
].map((address) => address.toLowerCase())
|
|
504
|
+
),
|
|
505
|
+
morpho: "0x23DFBc4B8B80C14CC5e25011B8491f268395BAd6",
|
|
506
|
+
morphoBlue: "0x0000000000000000000000000000000000000000",
|
|
507
|
+
// Set dynamically in tests
|
|
508
|
+
mempool: {
|
|
509
|
+
address: "0xD946246695A9259F3B33a78629026F61B3Ab40aF",
|
|
510
|
+
deploymentBlock: 23223727,
|
|
511
|
+
reindexBuffer: 10
|
|
512
|
+
},
|
|
513
|
+
vaultV1Factory: {
|
|
514
|
+
"v1.0": "0x0000000000000000000000000000000000000000",
|
|
515
|
+
"v1.1": "0x0000000000000000000000000000000000000000"
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var MAX_BATCH_SIZE = 1e4;
|
|
520
|
+
var DEFAULT_BATCH_SIZE = 2500;
|
|
521
|
+
var MAX_BLOCK_WINDOW = 2e3;
|
|
522
|
+
var DEFAULT_BLOCK_WINDOW = 500;
|
|
523
|
+
async function* streamLogs(parameters) {
|
|
524
|
+
const {
|
|
525
|
+
client,
|
|
526
|
+
contractAddress,
|
|
527
|
+
event,
|
|
528
|
+
blockNumberGte,
|
|
529
|
+
blockNumberLte,
|
|
530
|
+
order = "desc",
|
|
531
|
+
options: { maxBatchSize = DEFAULT_BATCH_SIZE, blockWindow = DEFAULT_BLOCK_WINDOW } = {}
|
|
532
|
+
} = parameters;
|
|
533
|
+
if (maxBatchSize > MAX_BATCH_SIZE) throw new InvalidBatchSizeError(maxBatchSize);
|
|
534
|
+
if (blockWindow > MAX_BLOCK_WINDOW) throw new InvalidBlockWindowError(blockWindow);
|
|
535
|
+
if (order === "asc" && blockNumberGte === void 0) throw new MissingBlockNumberError();
|
|
536
|
+
const latestBlock = (await getBlock(client, { blockTag: "latest", includeTransactions: false })).number;
|
|
537
|
+
let toBlock = 0n;
|
|
538
|
+
if (order === "asc")
|
|
539
|
+
toBlock = min(BigInt(blockNumberGte) + BigInt(blockWindow), latestBlock);
|
|
540
|
+
if (order === "desc")
|
|
541
|
+
toBlock = blockNumberLte === void 0 ? latestBlock : min(BigInt(blockNumberLte), latestBlock);
|
|
542
|
+
let fromBlock = 0n;
|
|
543
|
+
if (order === "asc") fromBlock = min(BigInt(blockNumberGte), latestBlock);
|
|
544
|
+
if (order === "desc")
|
|
545
|
+
fromBlock = max(BigInt(blockNumberGte || toBlock - BigInt(blockWindow)), 0n);
|
|
546
|
+
if (order === "asc") toBlock = min(toBlock, fromBlock + BigInt(blockWindow));
|
|
547
|
+
if (order === "desc") fromBlock = max(fromBlock, toBlock - BigInt(blockWindow));
|
|
548
|
+
if (fromBlock > toBlock) throw new InvalidBlockRangeError(fromBlock, toBlock);
|
|
549
|
+
let streaming = true;
|
|
550
|
+
while (streaming) {
|
|
551
|
+
const logs = await getLogs(client, {
|
|
552
|
+
address: contractAddress,
|
|
553
|
+
event,
|
|
554
|
+
fromBlock,
|
|
555
|
+
toBlock
|
|
556
|
+
});
|
|
557
|
+
logs.sort((a, b) => {
|
|
558
|
+
if (a.blockNumber !== b.blockNumber)
|
|
559
|
+
return order === "asc" ? Number(a.blockNumber - b.blockNumber) : Number(b.blockNumber - a.blockNumber);
|
|
560
|
+
if (a.transactionIndex !== b.transactionIndex)
|
|
561
|
+
return order === "asc" ? a.transactionIndex - b.transactionIndex : b.transactionIndex - a.transactionIndex;
|
|
562
|
+
return order === "asc" ? a.logIndex - b.logIndex : b.logIndex - a.logIndex;
|
|
563
|
+
});
|
|
564
|
+
for (const logBatch of batch(logs, maxBatchSize)) {
|
|
565
|
+
if (logBatch.length === 0) break;
|
|
566
|
+
yield {
|
|
567
|
+
logs: logBatch,
|
|
568
|
+
blockNumber: logBatch.length === maxBatchSize ? (
|
|
569
|
+
// if the batch is full, return the last block number, block numbers are always sorted
|
|
570
|
+
Number(logBatch[logBatch.length - 1]?.blockNumber)
|
|
571
|
+
) : (
|
|
572
|
+
// if the batch is not full, return `toBlock` or `fromBlock` to indicate until which block the logs were fetched
|
|
573
|
+
order === "asc" ? Number(toBlock) : Number(fromBlock)
|
|
574
|
+
)
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
streaming = order === "asc" ? toBlock < (blockNumberLte || latestBlock) : fromBlock > (blockNumberGte || 0n);
|
|
578
|
+
if (order === "asc") {
|
|
579
|
+
fromBlock = min(BigInt(toBlock) + 1n, latestBlock);
|
|
580
|
+
toBlock = min(fromBlock + BigInt(blockWindow), latestBlock);
|
|
581
|
+
}
|
|
582
|
+
if (order === "desc") {
|
|
583
|
+
const lowerBound = BigInt(blockNumberGte || 0);
|
|
584
|
+
const windowSize = BigInt(blockWindow);
|
|
585
|
+
const nextToBlock = max(fromBlock - 1n, lowerBound);
|
|
586
|
+
const nextFromBlock = max(nextToBlock - windowSize, lowerBound);
|
|
587
|
+
toBlock = nextToBlock;
|
|
588
|
+
fromBlock = nextFromBlock;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
yield { logs: [], blockNumber: order === "asc" ? Number(toBlock) : Number(fromBlock) };
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
var InvalidBlockRangeError = class extends BaseError {
|
|
595
|
+
constructor(fromBlock, toBlock) {
|
|
596
|
+
super(
|
|
597
|
+
`Invalid block range while streaming data from chain. From block ${fromBlock} to block ${toBlock}.`
|
|
598
|
+
);
|
|
599
|
+
__publicField(this, "name", "Chain.InvalidBlockRangeError");
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
var InvalidBlockWindowError = class extends BaseError {
|
|
603
|
+
constructor(blockWindow) {
|
|
604
|
+
super(
|
|
605
|
+
`Invalid block window while streaming data from chain. Maximum is ${MAX_BLOCK_WINDOW}. Got ${blockWindow}.`
|
|
157
606
|
);
|
|
607
|
+
__publicField(this, "name", "Chain.InvalidBlockWindowError");
|
|
158
608
|
}
|
|
159
|
-
|
|
609
|
+
};
|
|
610
|
+
var InvalidBatchSizeError = class extends BaseError {
|
|
611
|
+
constructor(maxBatchSize) {
|
|
612
|
+
super(
|
|
613
|
+
`Invalid batch size while streaming data from chain. Maximum is ${MAX_BATCH_SIZE}. Got ${maxBatchSize}.`
|
|
614
|
+
);
|
|
615
|
+
__publicField(this, "name", "Chain.InvalidBatchSizeError");
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
var MissingBlockNumberError = class extends BaseError {
|
|
619
|
+
constructor() {
|
|
620
|
+
super("Missing block number when streaming data from chain in ascending order.");
|
|
621
|
+
__publicField(this, "name", "Chain.MissingBlockNumberError");
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// src/core/Collateral.ts
|
|
626
|
+
var Collateral_exports = {};
|
|
627
|
+
__export(Collateral_exports, {
|
|
628
|
+
CollateralSchema: () => CollateralSchema,
|
|
629
|
+
CollateralsSchema: () => CollateralsSchema,
|
|
630
|
+
from: () => from2
|
|
631
|
+
});
|
|
632
|
+
var transformHex = (val, ctx) => {
|
|
633
|
+
if (isHex(val)) return val;
|
|
634
|
+
ctx.addIssue({
|
|
635
|
+
code: "invalid_format",
|
|
636
|
+
input: val,
|
|
637
|
+
format: "hex",
|
|
638
|
+
error: "not a hex"
|
|
639
|
+
});
|
|
640
|
+
return z7.NEVER;
|
|
641
|
+
};
|
|
642
|
+
var transformAddress = (val, ctx) => {
|
|
643
|
+
if (isAddress(val.toLowerCase())) return val.toLowerCase();
|
|
644
|
+
ctx.addIssue({
|
|
645
|
+
code: "invalid_format",
|
|
646
|
+
input: val,
|
|
647
|
+
format: "address",
|
|
648
|
+
error: "not a valid address"
|
|
649
|
+
});
|
|
650
|
+
return z7.NEVER;
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/core/LLTV.ts
|
|
654
|
+
var LLTV_exports = {};
|
|
655
|
+
__export(LLTV_exports, {
|
|
656
|
+
InvalidLLTVError: () => InvalidLLTVError,
|
|
657
|
+
InvalidOptionError: () => InvalidOptionError,
|
|
658
|
+
LLTVSchema: () => LLTVSchema,
|
|
659
|
+
Options: () => Options,
|
|
660
|
+
from: () => from
|
|
661
|
+
});
|
|
662
|
+
var Options = [0.385, 0.5, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965, 0.98];
|
|
663
|
+
var LLTV_SCALED = Options.map((lltv) => BigInt(lltv * 10 ** 18));
|
|
664
|
+
function from(lltv) {
|
|
665
|
+
if (typeof lltv === "bigint" && !LLTV_SCALED.includes(lltv)) throw new InvalidLLTVError(lltv);
|
|
666
|
+
if (typeof lltv === "bigint") return lltv;
|
|
667
|
+
if (typeof lltv === "number" && !Options.includes(lltv)) throw new InvalidOptionError(lltv);
|
|
668
|
+
return BigInt(lltv * 10 ** 18);
|
|
160
669
|
}
|
|
670
|
+
var InvalidOptionError = class extends BaseError {
|
|
671
|
+
constructor(input) {
|
|
672
|
+
super(
|
|
673
|
+
`Invalid LLTV option. Input: "${input}". Accepted values are: ${Options.map(
|
|
674
|
+
(option) => `"${option}"`
|
|
675
|
+
).join(", ")}.`
|
|
676
|
+
);
|
|
677
|
+
__publicField(this, "name", "LLTV.InvalidOptionError");
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
var InvalidLLTVError = class extends BaseError {
|
|
681
|
+
constructor(input) {
|
|
682
|
+
super(
|
|
683
|
+
`Invalid LLTV. Input: "${input}". Accepted values are: ${LLTV_SCALED.map(
|
|
684
|
+
(option) => `"${option}"`
|
|
685
|
+
).join(", ")}.`
|
|
686
|
+
);
|
|
687
|
+
__publicField(this, "name", "LLTV.InvalidLLTVError");
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
var LLTVSchema = z7.bigint({ coerce: true }).refine(
|
|
691
|
+
(lltv) => {
|
|
692
|
+
try {
|
|
693
|
+
from(lltv);
|
|
694
|
+
return true;
|
|
695
|
+
} catch (_) {
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
error: () => {
|
|
701
|
+
return "Invalid LLTV: must be one of 0.385, 0.625, 0.77, 0.86, 0.915, 0.945, 0.965 or 0.98 (scaled by 1e18)";
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
).transform((lltv) => from(lltv));
|
|
705
|
+
|
|
706
|
+
// src/core/Collateral.ts
|
|
707
|
+
var CollateralSchema = z7.object({
|
|
708
|
+
asset: z7.string().transform(transformAddress),
|
|
709
|
+
oracle: z7.string().transform(transformAddress),
|
|
710
|
+
lltv: LLTVSchema
|
|
711
|
+
});
|
|
712
|
+
var CollateralsSchema = z7.array(CollateralSchema).min(1, { message: "At least one collateral is required" }).refine(
|
|
713
|
+
(collaterals) => {
|
|
714
|
+
for (let i = 1; i < collaterals.length; i++) {
|
|
715
|
+
if (collaterals[i - 1].asset.toLowerCase() > collaterals[i].asset.toLowerCase()) {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return true;
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
message: "Collaterals must be sorted alphabetically by address"
|
|
723
|
+
}
|
|
724
|
+
).refine(
|
|
725
|
+
(collaterals) => {
|
|
726
|
+
const uniqueAssets = /* @__PURE__ */ new Set();
|
|
727
|
+
for (const collateral of collaterals) {
|
|
728
|
+
const assetAddress = collateral.asset.toLowerCase();
|
|
729
|
+
if (uniqueAssets.has(assetAddress)) {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
uniqueAssets.add(assetAddress);
|
|
733
|
+
}
|
|
734
|
+
return true;
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
message: "Collaterals must not contain duplicate assets"
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
var from2 = (parameters) => {
|
|
741
|
+
return {
|
|
742
|
+
asset: parameters.asset.toLowerCase(),
|
|
743
|
+
lltv: from(parameters.lltv),
|
|
744
|
+
oracle: parameters.oracle.toLowerCase()
|
|
745
|
+
};
|
|
746
|
+
};
|
|
161
747
|
|
|
162
748
|
// src/core/Cursor.ts
|
|
163
749
|
var Cursor_exports = {};
|
|
164
750
|
__export(Cursor_exports, {
|
|
165
|
-
decode: () =>
|
|
166
|
-
encode: () =>
|
|
751
|
+
decode: () => decode,
|
|
752
|
+
encode: () => encode,
|
|
167
753
|
validate: () => validate
|
|
168
754
|
});
|
|
169
755
|
function validate(cursor) {
|
|
@@ -233,12 +819,17 @@ function validate(cursor) {
|
|
|
233
819
|
`Invalid ${validation.field} value: ${fieldValue}. Must be a ${validation.error}`
|
|
234
820
|
);
|
|
235
821
|
}
|
|
822
|
+
if (c.page !== void 0) {
|
|
823
|
+
if (typeof c.page !== "number" || !Number.isInteger(c.page) || c.page < 1) {
|
|
824
|
+
throw new Error("Invalid page: must be a positive integer");
|
|
825
|
+
}
|
|
826
|
+
}
|
|
236
827
|
return true;
|
|
237
828
|
}
|
|
238
|
-
function
|
|
829
|
+
function encode(c) {
|
|
239
830
|
return Base64.encodeURL(JSON.stringify(c));
|
|
240
831
|
}
|
|
241
|
-
function
|
|
832
|
+
function decode(token) {
|
|
242
833
|
if (!token) return null;
|
|
243
834
|
const decoded = JSON.parse(Base64.decode(token));
|
|
244
835
|
validate(decoded);
|
|
@@ -248,578 +839,780 @@ function decode2(token) {
|
|
|
248
839
|
// src/core/Liquidity.ts
|
|
249
840
|
var Liquidity_exports = {};
|
|
250
841
|
__export(Liquidity_exports, {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
842
|
+
calculateMaxDebt: () => calculateMaxDebt,
|
|
843
|
+
generateAllowancePoolId: () => generateAllowancePoolId,
|
|
844
|
+
generateBalancePoolId: () => generateBalancePoolId,
|
|
845
|
+
generateBuyVaultCallbackPoolId: () => generateBuyVaultCallbackPoolId,
|
|
846
|
+
generateDebtPoolId: () => generateDebtPoolId,
|
|
847
|
+
generateMarketLiquidityPoolId: () => generateMarketLiquidityPoolId,
|
|
848
|
+
generateObligationCollateralPoolId: () => generateObligationCollateralPoolId,
|
|
849
|
+
generateSellERC20CallbackPoolId: () => generateSellERC20CallbackPoolId,
|
|
850
|
+
generateUserVaultPositionPoolId: () => generateUserVaultPositionPoolId,
|
|
851
|
+
generateVaultPositionPoolId: () => generateVaultPositionPoolId
|
|
254
852
|
});
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
853
|
+
function calculateMaxDebt(amount, oraclePrice, lltv) {
|
|
854
|
+
const ORACLE_PRICE_SCALE = 10n ** 36n;
|
|
855
|
+
const PRECISION = 10n ** 18n;
|
|
856
|
+
const collateralQuoted = amount * oraclePrice / ORACLE_PRICE_SCALE;
|
|
857
|
+
const maxDebt = collateralQuoted * lltv / PRECISION;
|
|
858
|
+
return maxDebt;
|
|
859
|
+
}
|
|
860
|
+
function generateBalancePoolId(parameters) {
|
|
861
|
+
const { user, chainId, token } = parameters;
|
|
862
|
+
return `${user}-${chainId.toString()}-${token}-balance`.toLowerCase();
|
|
863
|
+
}
|
|
864
|
+
function generateAllowancePoolId(parameters) {
|
|
865
|
+
const { user, chainId, token } = parameters;
|
|
866
|
+
return `${user}-${chainId.toString()}-${token}-allowance`.toLowerCase();
|
|
867
|
+
}
|
|
868
|
+
function generateSellERC20CallbackPoolId(parameters) {
|
|
869
|
+
const { user, chainId, obligationId: obligationId2, token, offerHash } = parameters;
|
|
870
|
+
return `${user}-${chainId.toString()}-${obligationId2}-${token}-${offerHash}-sell_erc20_callback`.toLowerCase();
|
|
871
|
+
}
|
|
872
|
+
function generateObligationCollateralPoolId(parameters) {
|
|
873
|
+
const { user, chainId, obligationId: obligationId2, token } = parameters;
|
|
874
|
+
return `${user}-${chainId.toString()}-${obligationId2}-${token}-obligation-collateral`.toLowerCase();
|
|
875
|
+
}
|
|
876
|
+
function generateBuyVaultCallbackPoolId(parameters) {
|
|
877
|
+
const { user, chainId, vault, offerHash } = parameters;
|
|
878
|
+
return `${user}-${chainId.toString()}-${vault}-${offerHash}-${"buy_vault_v1_callback" /* BuyVaultV1Callback */}`.toLowerCase();
|
|
879
|
+
}
|
|
880
|
+
function generateDebtPoolId(parameters) {
|
|
881
|
+
const { user, chainId, obligationId: obligationId2 } = parameters;
|
|
882
|
+
return `${user}-${chainId.toString()}-${obligationId2}-debt`.toLowerCase();
|
|
883
|
+
}
|
|
884
|
+
function generateUserVaultPositionPoolId(parameters) {
|
|
885
|
+
const { user, chainId, vault } = parameters;
|
|
886
|
+
return `${user}-${chainId.toString()}-${vault}-user-vault-position`.toLowerCase();
|
|
887
|
+
}
|
|
888
|
+
function generateVaultPositionPoolId(parameters) {
|
|
889
|
+
const { vault, chainId, marketId } = parameters;
|
|
890
|
+
return `${vault}-${chainId.toString()}-${marketId}-vault-position`.toLowerCase();
|
|
891
|
+
}
|
|
892
|
+
function generateMarketLiquidityPoolId(parameters) {
|
|
893
|
+
const { chainId, marketId } = parameters;
|
|
894
|
+
return `${chainId.toString()}-${marketId}-market-liquidity`.toLowerCase();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// src/core/Maturity.ts
|
|
898
|
+
var Maturity_exports = {};
|
|
899
|
+
__export(Maturity_exports, {
|
|
900
|
+
InvalidDateError: () => InvalidDateError,
|
|
901
|
+
InvalidFormatError: () => InvalidFormatError,
|
|
902
|
+
InvalidOptionError: () => InvalidOptionError2,
|
|
903
|
+
MaturitySchema: () => MaturitySchema,
|
|
904
|
+
from: () => from3
|
|
905
|
+
});
|
|
906
|
+
var MaturitySchema = z7.number().int().refine(
|
|
907
|
+
(maturity) => {
|
|
908
|
+
try {
|
|
909
|
+
from3(maturity);
|
|
910
|
+
return true;
|
|
911
|
+
} catch (_e) {
|
|
912
|
+
return false;
|
|
279
913
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
retryDelayMs
|
|
289
|
-
),
|
|
290
|
-
Utils.retry(
|
|
291
|
-
() => client.multicall({
|
|
292
|
-
allowFailure: false,
|
|
293
|
-
contracts: allowanceContracts,
|
|
294
|
-
...blockNumber ? { blockNumber } : {}
|
|
295
|
-
}),
|
|
296
|
-
retryAttempts,
|
|
297
|
-
retryDelayMs
|
|
298
|
-
)
|
|
299
|
-
]);
|
|
300
|
-
for (let i = 0; i < pairsBatch.length; i++) {
|
|
301
|
-
const { user, token } = pairsBatch[i];
|
|
302
|
-
const balance = balances[i];
|
|
303
|
-
const allowance = allowances[i];
|
|
304
|
-
let perUser = out.get(user);
|
|
305
|
-
if (!perUser) {
|
|
306
|
-
perUser = /* @__PURE__ */ new Map();
|
|
307
|
-
out.set(user, perUser);
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
error: (issue) => {
|
|
917
|
+
try {
|
|
918
|
+
const maturityDate = new Date(issue.input * 1e3);
|
|
919
|
+
return `The maturity is set to ${maturityDate}. It must fall on the allowed settlement cycles (Friday 15:00 UTC at the end of week/month/quarter).`;
|
|
920
|
+
} catch (_) {
|
|
921
|
+
return `The maturity is set to ${issue.input}. It must fall on the allowed settlement cycles (Friday 15:00 UTC at the end of week/month/quarter).`;
|
|
308
922
|
}
|
|
309
|
-
perUser.set(token, { balance, allowance });
|
|
310
923
|
}
|
|
311
924
|
}
|
|
312
|
-
|
|
925
|
+
).transform((maturity) => maturity);
|
|
926
|
+
var MaturityOptions = {
|
|
927
|
+
end_of_week: () => endOfWeek(),
|
|
928
|
+
end_of_next_week: () => endOfNextWeek(),
|
|
929
|
+
end_of_month: () => endOfMonth(),
|
|
930
|
+
end_of_next_month: () => endOfNextMonth(),
|
|
931
|
+
end_of_quarter: () => endOfQuarter(),
|
|
932
|
+
end_of_next_quarter: () => endOfNextQuarter()
|
|
933
|
+
};
|
|
934
|
+
function from3(ts) {
|
|
935
|
+
if (typeof ts === "string") {
|
|
936
|
+
if (ts in MaturityOptions) return MaturityOptions[ts]();
|
|
937
|
+
throw new InvalidOptionError2(ts);
|
|
938
|
+
}
|
|
939
|
+
if (typeof ts === "number" && ts > 1e12) throw new InvalidFormatError();
|
|
940
|
+
if (!Object.values(MaturityOptions).some((option) => option() === ts))
|
|
941
|
+
throw new InvalidDateError(ts);
|
|
942
|
+
return ts;
|
|
313
943
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
944
|
+
var endOfWeek = () => fridayOfWeek(0);
|
|
945
|
+
var endOfNextWeek = () => fridayOfWeek(1);
|
|
946
|
+
var endOfMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth());
|
|
947
|
+
var endOfNextMonth = () => lastFridayOfMonth((/* @__PURE__ */ new Date()).getUTCFullYear(), (/* @__PURE__ */ new Date()).getUTCMonth() + 1);
|
|
948
|
+
var endOfQuarter = () => lastFridayOfQuarter(0);
|
|
949
|
+
var endOfNextQuarter = () => lastFridayOfQuarter(1);
|
|
950
|
+
var fridayOfWeek = (weeksAhead = 0) => {
|
|
951
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
952
|
+
const today15H = new Date(
|
|
953
|
+
Date.UTC(now2.getUTCFullYear(), now2.getUTCMonth(), now2.getUTCDate(), 15)
|
|
954
|
+
);
|
|
955
|
+
let daysUntilFriday = (5 - today15H.getUTCDay() + 7) % 7;
|
|
956
|
+
if (daysUntilFriday === 0 && now2.getTime() >= today15H.getTime()) {
|
|
957
|
+
daysUntilFriday = 7;
|
|
958
|
+
}
|
|
959
|
+
const friday = new Date(today15H);
|
|
960
|
+
friday.setUTCDate(friday.getUTCDate() + daysUntilFriday + weeksAhead * 7);
|
|
961
|
+
return friday.getTime() / 1e3;
|
|
962
|
+
};
|
|
963
|
+
var lastFridayOfMonth = (year, month) => {
|
|
964
|
+
const lastDayOfMonth15H = new Date(Date.UTC(year, month + 1, 0, 15));
|
|
965
|
+
while (lastDayOfMonth15H.getUTCDay() !== 5) {
|
|
966
|
+
lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate() - 1);
|
|
967
|
+
}
|
|
968
|
+
const maturity = lastDayOfMonth15H.setUTCDate(lastDayOfMonth15H.getUTCDate()) / 1e3;
|
|
969
|
+
return maturity;
|
|
970
|
+
};
|
|
971
|
+
var lastFridayOfQuarter = (quartersAhead = 0) => {
|
|
972
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
973
|
+
const quarterIndex = Math.floor(now2.getUTCMonth() / 3) + quartersAhead;
|
|
974
|
+
const year = now2.getUTCFullYear() + Math.floor(quarterIndex / 4);
|
|
975
|
+
const quarter = quarterIndex % 4;
|
|
976
|
+
const lastMonth = quarter * 3 + 2;
|
|
977
|
+
return lastFridayOfMonth(year, lastMonth);
|
|
978
|
+
};
|
|
979
|
+
var InvalidFormatError = class extends BaseError {
|
|
980
|
+
constructor() {
|
|
981
|
+
super("Invalid maturity format. Maturity should be expressed in seconds.");
|
|
982
|
+
__publicField(this, "name", "Maturity.InvalidFormatError");
|
|
339
983
|
}
|
|
340
|
-
|
|
984
|
+
};
|
|
985
|
+
var InvalidDateError = class extends BaseError {
|
|
986
|
+
constructor(input) {
|
|
987
|
+
super(
|
|
988
|
+
`Invalid maturity date. Input: "${input}". Accepted values are: ${Object.values(
|
|
989
|
+
MaturityOptions
|
|
990
|
+
).map((option) => `"${option()}"`).join(", ")}.`
|
|
991
|
+
);
|
|
992
|
+
__publicField(this, "name", "Maturity.InvalidDateError");
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
var InvalidOptionError2 = class extends BaseError {
|
|
996
|
+
constructor(input) {
|
|
997
|
+
super(
|
|
998
|
+
`Invalid maturity option. Input: "${input}". Accepted values are: ${Object.keys(
|
|
999
|
+
MaturityOptions
|
|
1000
|
+
).map((option) => `"${option}"`).join(", ")}.`
|
|
1001
|
+
);
|
|
1002
|
+
__publicField(this, "name", "Maturity.InvalidOptionError");
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
// src/core/Obligation.ts
|
|
1007
|
+
var Obligation_exports = {};
|
|
1008
|
+
__export(Obligation_exports, {
|
|
1009
|
+
CollateralsAreNotSortedError: () => CollateralsAreNotSortedError,
|
|
1010
|
+
InvalidObligationError: () => InvalidObligationError,
|
|
1011
|
+
ObligationSchema: () => ObligationSchema,
|
|
1012
|
+
from: () => from4,
|
|
1013
|
+
fromSnakeCase: () => fromSnakeCase2,
|
|
1014
|
+
id: () => id,
|
|
1015
|
+
random: () => random
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
// src/utils/Format.ts
|
|
1019
|
+
var Format_exports = {};
|
|
1020
|
+
__export(Format_exports, {
|
|
1021
|
+
fromSnakeCase: () => fromSnakeCase,
|
|
1022
|
+
toSnakeCase: () => toSnakeCase
|
|
1023
|
+
});
|
|
1024
|
+
function toSnakeCase(obj) {
|
|
1025
|
+
return stringifyBigint(
|
|
1026
|
+
processObject(
|
|
1027
|
+
obj,
|
|
1028
|
+
(s) => s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),
|
|
1029
|
+
(value) => typeof value === "string" && isAddress(value.toLowerCase()) ? getAddress(value.toLowerCase()) : value
|
|
1030
|
+
)
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
function fromSnakeCase(obj) {
|
|
1034
|
+
return processObject(
|
|
1035
|
+
obj,
|
|
1036
|
+
(s) => isAddress(s.toLowerCase()) ? s : s.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
|
|
1037
|
+
(value) => typeof value === "string" && isAddress(value.toLowerCase()) ? value.toLowerCase() : value
|
|
1038
|
+
);
|
|
341
1039
|
}
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
1040
|
+
function processObject(obj, fnKey, fnValue) {
|
|
1041
|
+
if (typeof obj !== "object" || obj === null) return obj;
|
|
1042
|
+
if (Array.isArray(obj)) return obj.map((item) => processObject(item, fnKey, fnValue));
|
|
1043
|
+
return Object.entries(obj).reduce(
|
|
1044
|
+
(acc, [key, value]) => {
|
|
1045
|
+
const newKey = fnKey(key);
|
|
1046
|
+
acc[newKey] = typeof value === "object" && value !== null ? processObject(value, fnKey, fnValue) : fnValue(value);
|
|
1047
|
+
return acc;
|
|
350
1048
|
},
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
1049
|
+
{}
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
function stringifyBigint(value) {
|
|
1053
|
+
if (typeof value === "bigint") return value.toString();
|
|
1054
|
+
if (Array.isArray(value)) return value.map(stringifyBigint);
|
|
1055
|
+
if (value && typeof value === "object") {
|
|
1056
|
+
const out = {};
|
|
1057
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1058
|
+
out[k] = stringifyBigint(v);
|
|
1059
|
+
}
|
|
1060
|
+
return out;
|
|
1061
|
+
}
|
|
1062
|
+
return value;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// src/core/Obligation.ts
|
|
1066
|
+
var ObligationSchema = z7.object({
|
|
1067
|
+
chainId: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1068
|
+
loanToken: z7.string().transform(transformAddress),
|
|
1069
|
+
collaterals: CollateralsSchema,
|
|
1070
|
+
maturity: MaturitySchema
|
|
1071
|
+
});
|
|
1072
|
+
function from4(parameters) {
|
|
1073
|
+
try {
|
|
1074
|
+
const parsedObligation = ObligationSchema.parse({
|
|
1075
|
+
...parameters,
|
|
1076
|
+
maturity: from3(parameters.maturity)
|
|
1077
|
+
});
|
|
1078
|
+
return {
|
|
1079
|
+
chainId: parsedObligation.chainId,
|
|
1080
|
+
loanToken: parsedObligation.loanToken.toLowerCase(),
|
|
1081
|
+
collaterals: parsedObligation.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)),
|
|
1082
|
+
maturity: parsedObligation.maturity
|
|
1083
|
+
};
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
throw new InvalidObligationError(error);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function fromSnakeCase2(input) {
|
|
1089
|
+
return from4(fromSnakeCase(input));
|
|
1090
|
+
}
|
|
1091
|
+
function id(obligation) {
|
|
1092
|
+
let lastAsset = "";
|
|
1093
|
+
for (const collateral of obligation.collaterals) {
|
|
1094
|
+
const newAsset = collateral.asset.toLowerCase();
|
|
1095
|
+
if (newAsset.localeCompare(lastAsset) < 0) throw new CollateralsAreNotSortedError();
|
|
1096
|
+
lastAsset = newAsset;
|
|
1097
|
+
}
|
|
1098
|
+
return keccak256(
|
|
1099
|
+
encodeAbiParameters(
|
|
1100
|
+
[
|
|
1101
|
+
{ type: "uint256" },
|
|
1102
|
+
{ type: "address" },
|
|
1103
|
+
{
|
|
1104
|
+
type: "tuple[]",
|
|
1105
|
+
components: [
|
|
1106
|
+
{ type: "address", name: "token" },
|
|
1107
|
+
{ type: "uint256", name: "lltv" },
|
|
1108
|
+
{ type: "address", name: "oracle" }
|
|
1109
|
+
]
|
|
1110
|
+
},
|
|
1111
|
+
{ type: "uint256" }
|
|
1112
|
+
],
|
|
1113
|
+
[
|
|
1114
|
+
obligation.chainId,
|
|
1115
|
+
obligation.loanToken.toLowerCase(),
|
|
1116
|
+
obligation.collaterals.map((c) => ({
|
|
1117
|
+
token: c.asset.toLowerCase(),
|
|
1118
|
+
lltv: c.lltv,
|
|
1119
|
+
oracle: c.oracle.toLowerCase()
|
|
1120
|
+
})),
|
|
1121
|
+
BigInt(obligation.maturity)
|
|
1122
|
+
]
|
|
377
1123
|
)
|
|
378
|
-
|
|
379
|
-
return JSON.stringify(normalized);
|
|
1124
|
+
);
|
|
380
1125
|
}
|
|
1126
|
+
function random() {
|
|
1127
|
+
return from4({
|
|
1128
|
+
chainId: 1n,
|
|
1129
|
+
loanToken: privateKeyToAccount(generatePrivateKey()).address,
|
|
1130
|
+
collaterals: [
|
|
1131
|
+
from2({
|
|
1132
|
+
asset: privateKeyToAccount(generatePrivateKey()).address,
|
|
1133
|
+
oracle: privateKeyToAccount(generatePrivateKey()).address,
|
|
1134
|
+
lltv: 0.965
|
|
1135
|
+
})
|
|
1136
|
+
],
|
|
1137
|
+
maturity: from3("end_of_next_quarter")
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
var InvalidObligationError = class extends BaseError {
|
|
1141
|
+
constructor(error) {
|
|
1142
|
+
super("Invalid obligation.", { cause: error });
|
|
1143
|
+
__publicField(this, "name", "Obligation.InvalidObligationError");
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
var CollateralsAreNotSortedError = class extends BaseError {
|
|
1147
|
+
constructor() {
|
|
1148
|
+
super("Collaterals are not sorted alphabetically by address.");
|
|
1149
|
+
__publicField(this, "name", "Obligation.CollateralsAreNotSortedError");
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
381
1152
|
|
|
382
|
-
// src/core/
|
|
383
|
-
var
|
|
384
|
-
__export(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
1153
|
+
// src/core/Offer.ts
|
|
1154
|
+
var Offer_exports = {};
|
|
1155
|
+
__export(Offer_exports, {
|
|
1156
|
+
AccountNotSetError: () => AccountNotSetError,
|
|
1157
|
+
InvalidOfferError: () => InvalidOfferError,
|
|
1158
|
+
OfferHashSchema: () => OfferHashSchema,
|
|
1159
|
+
OfferSchema: () => OfferSchema,
|
|
388
1160
|
consumedEvent: () => consumedEvent,
|
|
389
|
-
|
|
1161
|
+
decode: () => decode2,
|
|
1162
|
+
domain: () => domain,
|
|
1163
|
+
encode: () => encode2,
|
|
1164
|
+
from: () => from5,
|
|
390
1165
|
fromConsumedLog: () => fromConsumedLog,
|
|
1166
|
+
fromSnakeCase: () => fromSnakeCase3,
|
|
1167
|
+
hash: () => hash,
|
|
1168
|
+
obligationId: () => obligationId,
|
|
1169
|
+
random: () => random2,
|
|
1170
|
+
sign: () => sign,
|
|
1171
|
+
toSnakeCase: () => toSnakeCase2,
|
|
1172
|
+
types: () => types
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
// src/utils/index.ts
|
|
1176
|
+
var utils_exports = {};
|
|
1177
|
+
__export(utils_exports, {
|
|
1178
|
+
BaseError: () => BaseError,
|
|
1179
|
+
Time: () => time_exports,
|
|
1180
|
+
batch: () => batch,
|
|
1181
|
+
batchMulticall: () => batchMulticall,
|
|
391
1182
|
fromSnakeCase: () => fromSnakeCase,
|
|
392
|
-
|
|
393
|
-
|
|
1183
|
+
lazy: () => lazy,
|
|
1184
|
+
max: () => max,
|
|
1185
|
+
min: () => min,
|
|
1186
|
+
poll: () => poll,
|
|
1187
|
+
retry: () => retry,
|
|
1188
|
+
toSnakeCase: () => toSnakeCase,
|
|
1189
|
+
wait: () => wait
|
|
394
1190
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
1191
|
+
|
|
1192
|
+
// src/utils/retry.ts
|
|
1193
|
+
var retry = async (fn, attempts = 3, delayMs = 50) => {
|
|
1194
|
+
let lastErr;
|
|
1195
|
+
for (let i = 0; i < attempts; i++) {
|
|
1196
|
+
try {
|
|
1197
|
+
return await fn();
|
|
1198
|
+
} catch (err) {
|
|
1199
|
+
lastErr = err;
|
|
1200
|
+
if (i < attempts - 1) await new Promise((r) => setTimeout(r, delayMs));
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
throw lastErr;
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// src/utils/batchMulticall.ts
|
|
1207
|
+
async function batchMulticall(parameters) {
|
|
1208
|
+
const { client, calls, batchSize, retryAttempts, retryDelayMs, blockNumber } = parameters;
|
|
1209
|
+
const results = [];
|
|
1210
|
+
for (const callsBatch of batch(calls, batchSize)) {
|
|
1211
|
+
const batchResults = await retry(
|
|
1212
|
+
() => client.multicall({
|
|
1213
|
+
allowFailure: false,
|
|
1214
|
+
contracts: callsBatch,
|
|
1215
|
+
...blockNumber ? { blockNumber } : {}
|
|
1216
|
+
}),
|
|
1217
|
+
retryAttempts,
|
|
1218
|
+
retryDelayMs
|
|
1219
|
+
);
|
|
1220
|
+
results.push(...batchResults);
|
|
1221
|
+
}
|
|
1222
|
+
return results;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/utils/lazy.ts
|
|
1226
|
+
function lazy(pollFn) {
|
|
1227
|
+
return () => async function* () {
|
|
1228
|
+
let active = true;
|
|
1229
|
+
let resolveNext = null;
|
|
1230
|
+
const queue = [];
|
|
1231
|
+
const wait2 = () => new Promise((resolve) => {
|
|
1232
|
+
resolveNext = resolve;
|
|
1233
|
+
});
|
|
1234
|
+
const emit = (item) => {
|
|
1235
|
+
queue.push(item);
|
|
1236
|
+
resolveNext?.();
|
|
1237
|
+
resolveNext = null;
|
|
1238
|
+
};
|
|
1239
|
+
let unpoll = null;
|
|
1240
|
+
const stop = () => {
|
|
1241
|
+
active = false;
|
|
1242
|
+
unpoll?.();
|
|
1243
|
+
resolveNext?.();
|
|
1244
|
+
resolveNext = null;
|
|
1245
|
+
};
|
|
1246
|
+
unpoll = pollFn(emit, { stop });
|
|
1247
|
+
try {
|
|
1248
|
+
while (active) {
|
|
1249
|
+
if (queue.length === 0) await wait2();
|
|
1250
|
+
while (queue.length > 0 && active) yield queue.shift();
|
|
1251
|
+
}
|
|
1252
|
+
} finally {
|
|
1253
|
+
stop();
|
|
1254
|
+
}
|
|
1255
|
+
}();
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/utils/wait.ts
|
|
1259
|
+
async function wait(time) {
|
|
1260
|
+
return new Promise((res) => setTimeout(res, time));
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/utils/poll.ts
|
|
1264
|
+
function poll(fn, { interval }) {
|
|
1265
|
+
let active = true;
|
|
1266
|
+
const unwatch = () => active = false;
|
|
1267
|
+
const watch2 = async () => {
|
|
1268
|
+
await wait(interval);
|
|
1269
|
+
const poll2 = async () => {
|
|
1270
|
+
if (!active) return;
|
|
1271
|
+
await fn({ unpoll: unwatch });
|
|
1272
|
+
await wait(interval);
|
|
1273
|
+
poll2();
|
|
1274
|
+
};
|
|
1275
|
+
poll2();
|
|
1276
|
+
};
|
|
1277
|
+
watch2();
|
|
1278
|
+
return unwatch;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// src/utils/time.ts
|
|
1282
|
+
var time_exports = {};
|
|
1283
|
+
__export(time_exports, {
|
|
1284
|
+
max: () => max2,
|
|
1285
|
+
now: () => now
|
|
407
1286
|
});
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
1287
|
+
function now() {
|
|
1288
|
+
return Math.floor(Date.now() / 1e3);
|
|
1289
|
+
}
|
|
1290
|
+
function max2() {
|
|
1291
|
+
return 864e16;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// src/core/Offer.ts
|
|
1295
|
+
var OfferHashSchema = z7.string().regex(/^0x[0-9a-fA-F]{64}$/, {
|
|
1296
|
+
message: "Hash must be a valid 32-byte hex string"
|
|
1297
|
+
}).transform(transformHex);
|
|
1298
|
+
var OfferSchema = (parameters) => {
|
|
1299
|
+
const { omitHash = false } = parameters || {};
|
|
1300
|
+
const now2 = time_exports.now();
|
|
1301
|
+
let base = z7.object({
|
|
1302
|
+
offering: z7.string().transform(transformAddress),
|
|
1303
|
+
assets: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1304
|
+
rate: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1305
|
+
maturity: MaturitySchema,
|
|
1306
|
+
expiry: z7.number().int().min(now2, { message: "Expiry must be set to a future date" }).max(Number.MAX_SAFE_INTEGER),
|
|
1307
|
+
start: z7.number().int().max(Number.MAX_SAFE_INTEGER),
|
|
1308
|
+
nonce: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1309
|
+
buy: z7.boolean(),
|
|
1310
|
+
chainId: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1311
|
+
loanToken: z7.string().transform(transformAddress),
|
|
1312
|
+
collaterals: CollateralsSchema,
|
|
1313
|
+
callback: z7.object({
|
|
1314
|
+
address: z7.string().transform(transformAddress),
|
|
1315
|
+
data: z7.string().transform(transformHex),
|
|
1316
|
+
gasLimit: z7.bigint({ coerce: true }).min(0n).max(maxUint256)
|
|
1317
|
+
}),
|
|
1318
|
+
consumed: z7.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
1319
|
+
blockNumber: z7.number().int().max(Number.MAX_SAFE_INTEGER),
|
|
1320
|
+
signature: z7.string().regex(/^0x[0-9a-fA-F]{130}$/, {
|
|
1321
|
+
message: "Signature must be a valid 65-byte hex string"
|
|
1322
|
+
}).transform(transformHex).optional()
|
|
1323
|
+
});
|
|
1324
|
+
if (!omitHash) base = base.extend({ hash: OfferHashSchema });
|
|
1325
|
+
return base.refine((data) => data.start < data.expiry, {
|
|
1326
|
+
message: "Start must be before expiry",
|
|
1327
|
+
path: ["start"]
|
|
1328
|
+
}).refine((data) => data.expiry <= data.maturity, {
|
|
1329
|
+
message: "Expiry cannot be after maturity",
|
|
1330
|
+
path: ["expiry"]
|
|
1331
|
+
});
|
|
417
1332
|
};
|
|
418
|
-
function
|
|
1333
|
+
function from5(input) {
|
|
419
1334
|
try {
|
|
420
|
-
const parsedOffer =
|
|
421
|
-
const parsedHash =
|
|
1335
|
+
const parsedOffer = OfferSchema({ omitHash: true }).parse(input);
|
|
1336
|
+
const parsedHash = OfferHashSchema.parse(hash(parsedOffer));
|
|
422
1337
|
return {
|
|
423
1338
|
...parsedOffer,
|
|
424
1339
|
hash: parsedHash
|
|
425
1340
|
};
|
|
426
1341
|
} catch (error) {
|
|
427
|
-
throw new
|
|
1342
|
+
throw new InvalidOfferError(error);
|
|
428
1343
|
}
|
|
429
1344
|
}
|
|
430
|
-
function
|
|
431
|
-
return
|
|
1345
|
+
function fromSnakeCase3(input) {
|
|
1346
|
+
return from5(fromSnakeCase(input));
|
|
432
1347
|
}
|
|
433
|
-
function
|
|
434
|
-
return
|
|
1348
|
+
function toSnakeCase2(offer) {
|
|
1349
|
+
return toSnakeCase(offer);
|
|
435
1350
|
}
|
|
436
|
-
function
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
1351
|
+
function random2() {
|
|
1352
|
+
const loanToken = privateKeyToAccount(generatePrivateKey()).address;
|
|
1353
|
+
const maturity = from3("end_of_month");
|
|
1354
|
+
const expiry = from3("end_of_week") - 1;
|
|
1355
|
+
const lltv = from(0.965);
|
|
1356
|
+
const offer = from5({
|
|
1357
|
+
offering: privateKeyToAccount(generatePrivateKey()).address,
|
|
1358
|
+
assets: BigInt(Math.floor(Math.random() * 1e6)),
|
|
1359
|
+
rate: BigInt(Math.floor(Math.random() * 1e6)),
|
|
1360
|
+
maturity,
|
|
1361
|
+
expiry,
|
|
1362
|
+
start: expiry - 10,
|
|
1363
|
+
nonce: BigInt(Math.floor(Math.random() * 1e6)),
|
|
1364
|
+
buy: Math.random() > 0.5,
|
|
1365
|
+
chainId: 1n,
|
|
1366
|
+
loanToken,
|
|
1367
|
+
collaterals: [
|
|
1368
|
+
from2({
|
|
1369
|
+
asset: zeroAddress,
|
|
1370
|
+
oracle: zeroAddress,
|
|
1371
|
+
lltv
|
|
1372
|
+
})
|
|
1373
|
+
],
|
|
1374
|
+
callback: {
|
|
1375
|
+
address: zeroAddress,
|
|
1376
|
+
data: "0x",
|
|
1377
|
+
gasLimit: 0n
|
|
1378
|
+
},
|
|
1379
|
+
consumed: 0n,
|
|
1380
|
+
blockNumber: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)
|
|
443
1381
|
});
|
|
1382
|
+
return offer;
|
|
444
1383
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
|
|
449
|
-
chainId: BigInt(chainId),
|
|
450
|
-
offering: user,
|
|
451
|
-
nonce,
|
|
452
|
-
amount
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
var InvalidRouterOfferError = class extends Errors.BaseError {
|
|
456
|
-
constructor(error) {
|
|
457
|
-
super("Invalid router offer.", { cause: error });
|
|
458
|
-
__publicField(this, "name", "RouterOffer.InvalidRouterOfferError");
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
// src/core/router/Client.ts
|
|
463
|
-
var Client_exports = {};
|
|
464
|
-
__export(Client_exports, {
|
|
465
|
-
HttpForbiddenError: () => HttpForbiddenError,
|
|
466
|
-
HttpGetOffersFailedError: () => HttpGetOffersFailedError,
|
|
467
|
-
HttpRateLimitError: () => HttpRateLimitError,
|
|
468
|
-
HttpUnauthorizedError: () => HttpUnauthorizedError,
|
|
469
|
-
InvalidUrlError: () => InvalidUrlError,
|
|
470
|
-
connect: () => connect,
|
|
471
|
-
get: () => get,
|
|
472
|
-
match: () => match
|
|
1384
|
+
var domain = (chainId) => ({
|
|
1385
|
+
chainId,
|
|
1386
|
+
verifyingContract: zeroAddress
|
|
473
1387
|
});
|
|
474
|
-
var
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
max_amount: z.bigint({ coerce: true }).positive({
|
|
524
|
-
message: "Max amount must be a positive number"
|
|
525
|
-
}).optional().meta({
|
|
526
|
-
description: "Maximum amount of assets in the offer",
|
|
527
|
-
example: "10000"
|
|
528
|
-
}),
|
|
529
|
-
// Rate range
|
|
530
|
-
min_rate: z.bigint({ coerce: true }).positive({
|
|
531
|
-
message: "Min rate must be a positive number"
|
|
532
|
-
}).optional().meta({
|
|
533
|
-
description: "Minimum rate per asset (in wei)",
|
|
534
|
-
example: "500000000000000000"
|
|
535
|
-
}),
|
|
536
|
-
max_rate: z.bigint({ coerce: true }).positive({
|
|
537
|
-
message: "Max rate must be a positive number"
|
|
538
|
-
}).optional().meta({
|
|
539
|
-
description: "Maximum rate per asset (in wei)",
|
|
540
|
-
example: "1500000000000000000"
|
|
541
|
-
}),
|
|
542
|
-
// Time range
|
|
543
|
-
min_maturity: z.coerce.number().int().min(0).optional().meta({
|
|
544
|
-
description: "Minimum maturity timestamp (Unix timestamp in seconds)",
|
|
545
|
-
example: "1700000000"
|
|
546
|
-
}),
|
|
547
|
-
max_maturity: z.coerce.number().int().min(0).optional().meta({
|
|
548
|
-
description: "Maximum maturity timestamp (Unix timestamp in seconds)",
|
|
549
|
-
example: "1800000000"
|
|
550
|
-
}),
|
|
551
|
-
min_expiry: z.coerce.number().int().optional().meta({
|
|
552
|
-
description: "Minimum expiry timestamp (Unix timestamp in seconds)",
|
|
553
|
-
example: "1700000000"
|
|
554
|
-
}),
|
|
555
|
-
max_expiry: z.coerce.number().int().optional().meta({
|
|
556
|
-
description: "Maximum expiry timestamp (Unix timestamp in seconds)",
|
|
557
|
-
example: "1800000000"
|
|
558
|
-
}),
|
|
559
|
-
// Collateral filtering
|
|
560
|
-
collateral_assets: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
561
|
-
message: "Collateral assets must be comma-separated Ethereum addresses"
|
|
562
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
563
|
-
description: "Filter by multiple collateral assets (comma-separated)",
|
|
564
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
565
|
-
}),
|
|
566
|
-
collateral_oracles: z.string().regex(/^0x[a-fA-F0-9]{40}(,0x[a-fA-F0-9]{40})*$/, {
|
|
567
|
-
message: "Collateral oracles must be comma-separated Ethereum addresses"
|
|
568
|
-
}).transform((val) => val.split(",").map((addr) => addr.toLowerCase())).optional().meta({
|
|
569
|
-
description: "Filter by multiple rate oracles (comma-separated)",
|
|
570
|
-
example: "0x1234567890123456789012345678901234567890,0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
|
|
571
|
-
}),
|
|
572
|
-
collateral_tuple: z.string().transform((val, ctx) => {
|
|
573
|
-
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]+)?)?)*$/;
|
|
574
|
-
if (!pattern.test(val)) {
|
|
575
|
-
ctx.addIssue({
|
|
576
|
-
code: "custom",
|
|
577
|
-
message: "collateral_tuple has an invalid format",
|
|
578
|
-
input: val
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
return val.split("#").map((tuple) => {
|
|
582
|
-
const parts = tuple.split(":");
|
|
583
|
-
if (parts.length === 0 || !parts[0]) {
|
|
584
|
-
ctx.addIssue({
|
|
585
|
-
code: "custom",
|
|
586
|
-
message: "Asset address is required for each collateral tuple",
|
|
587
|
-
path: ["asset"],
|
|
588
|
-
input: val
|
|
589
|
-
});
|
|
590
|
-
return z.NEVER;
|
|
591
|
-
}
|
|
592
|
-
const asset = parts[0]?.toLowerCase();
|
|
593
|
-
const oracle = parts[1]?.toLowerCase();
|
|
594
|
-
const lltv = parts[2] ? parseFloat(parts[2]) : void 0;
|
|
595
|
-
if (lltv !== void 0 && (lltv < MIN_LLTV || lltv > MAX_LLTV)) {
|
|
596
|
-
ctx.addIssue({
|
|
597
|
-
code: "custom",
|
|
598
|
-
message: `LLTV must be between ${MIN_LLTV} and ${MAX_LLTV} (0-100%)`,
|
|
599
|
-
path: ["lltv"],
|
|
600
|
-
input: val
|
|
601
|
-
});
|
|
602
|
-
return z.NEVER;
|
|
603
|
-
}
|
|
604
|
-
let lltvValue;
|
|
605
|
-
if (lltv !== void 0) {
|
|
606
|
-
try {
|
|
607
|
-
lltvValue = LLTV.from(parseUnits(lltv.toString(), 16));
|
|
608
|
-
} catch (e) {
|
|
609
|
-
ctx.issues.push({
|
|
610
|
-
code: "custom",
|
|
611
|
-
message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
|
|
612
|
-
input: lltv,
|
|
613
|
-
path: ["lltv"]
|
|
614
|
-
});
|
|
615
|
-
return z.NEVER;
|
|
616
|
-
}
|
|
1388
|
+
var types = {
|
|
1389
|
+
EIP712Domain: [
|
|
1390
|
+
{ name: "chainId", type: "uint256" },
|
|
1391
|
+
{ name: "verifyingContract", type: "address" }
|
|
1392
|
+
],
|
|
1393
|
+
Offer: [
|
|
1394
|
+
{ name: "offering", type: "address" },
|
|
1395
|
+
{ name: "assets", type: "uint256" },
|
|
1396
|
+
{ name: "rate", type: "uint256" },
|
|
1397
|
+
{ name: "maturity", type: "uint256" },
|
|
1398
|
+
{ name: "expiry", type: "uint256" },
|
|
1399
|
+
{ name: "nonce", type: "uint256" },
|
|
1400
|
+
{ name: "buy", type: "bool" },
|
|
1401
|
+
{ name: "loanToken", type: "address" },
|
|
1402
|
+
{ name: "collaterals", type: "Collateral[]" },
|
|
1403
|
+
{ name: "callback", type: "Callback" }
|
|
1404
|
+
],
|
|
1405
|
+
Collateral: [
|
|
1406
|
+
{ name: "asset", type: "address" },
|
|
1407
|
+
{ name: "oracle", type: "address" },
|
|
1408
|
+
{ name: "lltv", type: "uint256" }
|
|
1409
|
+
],
|
|
1410
|
+
Callback: [
|
|
1411
|
+
{ name: "address", type: "address" },
|
|
1412
|
+
{ name: "data", type: "bytes" },
|
|
1413
|
+
{ name: "gasLimit", type: "uint256" }
|
|
1414
|
+
]
|
|
1415
|
+
};
|
|
1416
|
+
function sign(offer, wallet) {
|
|
1417
|
+
if (!wallet.account) throw new AccountNotSetError();
|
|
1418
|
+
return wallet.signTypedData({
|
|
1419
|
+
account: wallet.account,
|
|
1420
|
+
domain: domain(offer.chainId),
|
|
1421
|
+
types,
|
|
1422
|
+
primaryType: "Offer",
|
|
1423
|
+
message: {
|
|
1424
|
+
offering: offer.offering.toLowerCase(),
|
|
1425
|
+
assets: offer.assets,
|
|
1426
|
+
rate: offer.rate,
|
|
1427
|
+
maturity: BigInt(offer.maturity),
|
|
1428
|
+
expiry: BigInt(offer.expiry),
|
|
1429
|
+
nonce: offer.nonce,
|
|
1430
|
+
buy: offer.buy,
|
|
1431
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
1432
|
+
collaterals: offer.collaterals,
|
|
1433
|
+
callback: {
|
|
1434
|
+
address: offer.callback.address.toLowerCase(),
|
|
1435
|
+
data: offer.callback.data,
|
|
1436
|
+
gasLimit: offer.callback.gasLimit
|
|
617
1437
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
description: "Field to sort results by",
|
|
639
|
-
example: "rate"
|
|
640
|
-
}),
|
|
641
|
-
sort_order: z.enum(["asc", "desc"]).optional().default("desc").meta({
|
|
642
|
-
description: "Sort direction: asc (ascending) or desc (descending, default)",
|
|
643
|
-
example: "desc"
|
|
644
|
-
}),
|
|
645
|
-
// Pagination
|
|
646
|
-
cursor: z.string().optional().refine(
|
|
647
|
-
(val) => {
|
|
648
|
-
if (!val) return true;
|
|
649
|
-
try {
|
|
650
|
-
const decoded = decode2(val);
|
|
651
|
-
return decoded !== null;
|
|
652
|
-
} catch (_error) {
|
|
653
|
-
return false;
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
function hash(offer) {
|
|
1442
|
+
return hashTypedData({
|
|
1443
|
+
domain: domain(offer.chainId),
|
|
1444
|
+
message: {
|
|
1445
|
+
offering: offer.offering.toLowerCase(),
|
|
1446
|
+
assets: offer.assets,
|
|
1447
|
+
rate: offer.rate,
|
|
1448
|
+
maturity: BigInt(offer.maturity),
|
|
1449
|
+
expiry: BigInt(offer.expiry),
|
|
1450
|
+
nonce: offer.nonce,
|
|
1451
|
+
buy: offer.buy,
|
|
1452
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
1453
|
+
collaterals: offer.collaterals,
|
|
1454
|
+
callback: {
|
|
1455
|
+
address: offer.callback.address.toLowerCase(),
|
|
1456
|
+
data: offer.callback.data,
|
|
1457
|
+
gasLimit: offer.callback.gasLimit
|
|
654
1458
|
}
|
|
655
1459
|
},
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
message: `Limit cannot exceed ${MAX_LIMIT}`
|
|
1460
|
+
primaryType: "Offer",
|
|
1461
|
+
types
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
function obligationId(offer) {
|
|
1465
|
+
return id(
|
|
1466
|
+
from4({
|
|
1467
|
+
chainId: offer.chainId,
|
|
1468
|
+
loanToken: offer.loanToken,
|
|
1469
|
+
collaterals: offer.collaterals,
|
|
1470
|
+
maturity: offer.maturity
|
|
668
1471
|
})
|
|
669
|
-
)
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
{
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
{
|
|
682
|
-
message: "max_expiry must be greater than or equal to min_expiry",
|
|
683
|
-
path: ["max_expiry"]
|
|
684
|
-
}
|
|
685
|
-
).refine(
|
|
686
|
-
(data) => data.min_amount === void 0 || data.max_amount === void 0 || data.max_amount >= data.min_amount,
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
var OfferAbi = [
|
|
1475
|
+
{ name: "offering", type: "address" },
|
|
1476
|
+
{ name: "assets", type: "uint256" },
|
|
1477
|
+
{ name: "rate", type: "uint256" },
|
|
1478
|
+
{ name: "maturity", type: "uint256" },
|
|
1479
|
+
{ name: "expiry", type: "uint256" },
|
|
1480
|
+
{ name: "nonce", type: "uint256" },
|
|
1481
|
+
{ name: "buy", type: "bool" },
|
|
1482
|
+
{ name: "chainId", type: "uint256" },
|
|
1483
|
+
{ name: "loanToken", type: "address" },
|
|
1484
|
+
{ name: "start", type: "uint256" },
|
|
687
1485
|
{
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1486
|
+
name: "collaterals",
|
|
1487
|
+
type: "tuple[]",
|
|
1488
|
+
components: [
|
|
1489
|
+
{ name: "asset", type: "address" },
|
|
1490
|
+
{ name: "oracle", type: "address" },
|
|
1491
|
+
{ name: "lltv", type: "uint256" }
|
|
1492
|
+
]
|
|
1493
|
+
},
|
|
693
1494
|
{
|
|
694
|
-
|
|
695
|
-
|
|
1495
|
+
name: "callback",
|
|
1496
|
+
type: "tuple",
|
|
1497
|
+
components: [
|
|
1498
|
+
{ name: "address", type: "address" },
|
|
1499
|
+
{ name: "data", type: "bytes" },
|
|
1500
|
+
{ name: "gasLimit", type: "uint256" }
|
|
1501
|
+
]
|
|
1502
|
+
},
|
|
1503
|
+
{ name: "signature", type: "bytes" }
|
|
1504
|
+
];
|
|
1505
|
+
function encode2(offer) {
|
|
1506
|
+
return encodeAbiParameters(OfferAbi, [
|
|
1507
|
+
offer.offering,
|
|
1508
|
+
offer.assets,
|
|
1509
|
+
offer.rate,
|
|
1510
|
+
BigInt(offer.maturity),
|
|
1511
|
+
BigInt(offer.expiry),
|
|
1512
|
+
offer.nonce,
|
|
1513
|
+
offer.buy,
|
|
1514
|
+
offer.chainId,
|
|
1515
|
+
offer.loanToken,
|
|
1516
|
+
BigInt(offer.start),
|
|
1517
|
+
offer.collaterals,
|
|
1518
|
+
offer.callback,
|
|
1519
|
+
offer.signature ?? "0x"
|
|
1520
|
+
]);
|
|
1521
|
+
}
|
|
1522
|
+
function decode2(data, blockNumber) {
|
|
1523
|
+
let decoded;
|
|
1524
|
+
try {
|
|
1525
|
+
decoded = decodeAbiParameters(OfferAbi, data);
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
throw new InvalidOfferError(error);
|
|
1528
|
+
}
|
|
1529
|
+
const offer = from5({
|
|
1530
|
+
offering: decoded[0],
|
|
1531
|
+
assets: decoded[1],
|
|
1532
|
+
rate: decoded[2],
|
|
1533
|
+
maturity: from3(Number(decoded[3])),
|
|
1534
|
+
expiry: Number(decoded[4]),
|
|
1535
|
+
nonce: decoded[5],
|
|
1536
|
+
buy: decoded[6],
|
|
1537
|
+
chainId: decoded[7],
|
|
1538
|
+
loanToken: decoded[8],
|
|
1539
|
+
start: Number(decoded[9]),
|
|
1540
|
+
collaterals: decoded[10].map((c) => {
|
|
1541
|
+
return from2({
|
|
1542
|
+
asset: c.asset,
|
|
1543
|
+
oracle: c.oracle,
|
|
1544
|
+
lltv: c.lltv
|
|
1545
|
+
});
|
|
1546
|
+
}),
|
|
1547
|
+
callback: {
|
|
1548
|
+
address: decoded[11].address,
|
|
1549
|
+
data: decoded[11].data,
|
|
1550
|
+
gasLimit: decoded[11].gasLimit
|
|
1551
|
+
},
|
|
1552
|
+
consumed: 0n,
|
|
1553
|
+
blockNumber: Number(blockNumber),
|
|
1554
|
+
...decoded[12] === "0x" ? {} : { signature: decoded[12] }
|
|
1555
|
+
});
|
|
1556
|
+
return offer;
|
|
1557
|
+
}
|
|
1558
|
+
var consumedEvent = {
|
|
1559
|
+
type: "event",
|
|
1560
|
+
name: "Consumed",
|
|
1561
|
+
inputs: [
|
|
1562
|
+
{ name: "user", type: "address", indexed: true, internalType: "address" },
|
|
1563
|
+
{ name: "nonce", type: "uint256", indexed: true, internalType: "uint256" },
|
|
1564
|
+
{ name: "amount", type: "uint256", indexed: false, internalType: "uint256" }
|
|
1565
|
+
],
|
|
1566
|
+
anonymous: false
|
|
1567
|
+
};
|
|
1568
|
+
function fromConsumedLog(parameters) {
|
|
1569
|
+
const { blockNumber, logIndex, chainId, transactionHash, user, nonce, amount } = parameters;
|
|
1570
|
+
return {
|
|
1571
|
+
id: `${blockNumber.toString()}-${logIndex.toString()}-${chainId}-${transactionHash}`,
|
|
1572
|
+
chainId: BigInt(chainId),
|
|
1573
|
+
offering: user,
|
|
1574
|
+
nonce,
|
|
1575
|
+
amount,
|
|
1576
|
+
blockNumber: Number(blockNumber)
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
var InvalidOfferError = class extends BaseError {
|
|
1580
|
+
constructor(error) {
|
|
1581
|
+
super("Invalid offer.", { cause: error });
|
|
1582
|
+
__publicField(this, "name", "Offer.InvalidOfferError");
|
|
696
1583
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
{
|
|
700
|
-
|
|
701
|
-
|
|
1584
|
+
};
|
|
1585
|
+
var AccountNotSetError = class extends BaseError {
|
|
1586
|
+
constructor() {
|
|
1587
|
+
super("Account not set.");
|
|
1588
|
+
__publicField(this, "name", "Offer.AccountNotSetError");
|
|
702
1589
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
return Number.parseInt(val, 10);
|
|
726
|
-
}).pipe(z.number().positive()).meta({
|
|
727
|
-
description: "The blockchain network chain ID",
|
|
728
|
-
example: "1"
|
|
729
|
-
}),
|
|
730
|
-
// Optional parameters
|
|
731
|
-
rate: z.bigint({ coerce: true }).positive({
|
|
732
|
-
message: "Rate must be a positive number"
|
|
733
|
-
}).optional().meta({
|
|
734
|
-
description: "Rate per asset (in wei) for matching offers",
|
|
735
|
-
example: "1000000000000000000"
|
|
736
|
-
}),
|
|
737
|
-
// Collateral filtering
|
|
738
|
-
collaterals: z.string().transform((val, ctx) => {
|
|
739
|
-
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]+)?)*$/;
|
|
740
|
-
if (!pattern.test(val)) {
|
|
741
|
-
ctx.addIssue({
|
|
742
|
-
code: "custom",
|
|
743
|
-
message: "Collaterals must be in format: asset:oracle:lltv#asset2:oracle2:lltv2. All fields are required for each collateral.",
|
|
744
|
-
input: val
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
return val.split("#").map((collateral) => {
|
|
748
|
-
const parts = collateral.split(":");
|
|
749
|
-
if (parts.length !== 3) {
|
|
750
|
-
ctx.addIssue({
|
|
751
|
-
code: "custom",
|
|
752
|
-
message: "Each collateral must have exactly 3 parts: asset:oracle:lltv",
|
|
753
|
-
path: ["collaterals"],
|
|
754
|
-
input: val
|
|
755
|
-
});
|
|
756
|
-
return z.NEVER;
|
|
757
|
-
}
|
|
758
|
-
const [asset, oracle, lltvStr] = parts;
|
|
759
|
-
if (!asset || !oracle || !lltvStr) {
|
|
760
|
-
ctx.addIssue({
|
|
761
|
-
code: "custom",
|
|
762
|
-
message: "Asset, oracle, and lltv are all required for each collateral",
|
|
763
|
-
path: ["collaterals"],
|
|
764
|
-
input: val
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
let lltvValue;
|
|
768
|
-
if (lltvStr !== void 0) {
|
|
769
|
-
try {
|
|
770
|
-
lltvValue = LLTV.from(parseUnits(lltvStr, 16));
|
|
771
|
-
} catch (e) {
|
|
772
|
-
ctx.issues.push({
|
|
773
|
-
code: "custom",
|
|
774
|
-
message: e instanceof LLTV.InvalidLLTVError || e instanceof LLTV.InvalidOptionError ? e.message : "Invalid LLTV.",
|
|
775
|
-
input: lltvStr,
|
|
776
|
-
path: ["lltv"]
|
|
777
|
-
});
|
|
778
|
-
return z.NEVER;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
return {
|
|
782
|
-
asset: asset.toLowerCase(),
|
|
783
|
-
oracle: oracle.toLowerCase(),
|
|
784
|
-
lltv: lltvValue
|
|
785
|
-
};
|
|
786
|
-
});
|
|
787
|
-
}).optional().meta({
|
|
788
|
-
description: "Collateral requirements in format: asset:oracle:lltv#asset2:oracle2:lltv2. Use # to separate multiple collaterals.",
|
|
789
|
-
example: "0x1234567890123456789012345678901234567890:0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:86#0x9876543210987654321098765432109876543210:0xfedcbafedcbafedcbafedcbafedcbafedcbafedc:94.5"
|
|
790
|
-
}),
|
|
791
|
-
// Maturity filtering
|
|
792
|
-
maturity: z.coerce.number().int().min(0).optional().meta({
|
|
793
|
-
description: "Maturity timestamp (Unix timestamp in seconds)",
|
|
794
|
-
example: "1700000000"
|
|
795
|
-
}),
|
|
796
|
-
min_maturity: z.coerce.number().int().min(0).optional().meta({
|
|
797
|
-
description: "Minimum maturity timestamp (Unix timestamp in seconds)",
|
|
798
|
-
example: "1700000000"
|
|
799
|
-
}),
|
|
800
|
-
max_maturity: z.coerce.number().int().min(0).optional().meta({
|
|
801
|
-
description: "Maximum maturity timestamp (Unix timestamp in seconds)",
|
|
802
|
-
example: "1800000000"
|
|
803
|
-
}),
|
|
804
|
-
// Asset and creator filtering
|
|
805
|
-
loan_token: z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
|
|
806
|
-
message: "Loan asset must be a valid Ethereum address"
|
|
807
|
-
}).transform((val) => val.toLowerCase()).optional().meta({
|
|
808
|
-
description: "The loan asset address to match against",
|
|
809
|
-
example: "0x1234567890123456789012345678901234567890"
|
|
810
|
-
}),
|
|
811
|
-
creator: z.string().regex(/^0x[a-fA-F0-9]{40}$/, {
|
|
812
|
-
message: "Creator must be a valid Ethereum address"
|
|
813
|
-
}).transform((val) => val.toLowerCase()).optional().meta({
|
|
814
|
-
description: "Filter by a specific offer creator address",
|
|
815
|
-
example: "0x1234567890123456789012345678901234567890"
|
|
816
|
-
}),
|
|
817
|
-
// Pagination
|
|
818
|
-
cursor: z.string().optional().refine(
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
// src/core/types.ts
|
|
1593
|
+
var BrandTypeId = Symbol.for("mempool/Brand");
|
|
1594
|
+
|
|
1595
|
+
// src/api/Api/Schema/ObligationResponse.ts
|
|
1596
|
+
function from6(obligation) {
|
|
1597
|
+
return toSnakeCase({ id: Obligation_exports.id(obligation), ...obligation });
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// src/api/Api/Schema/OfferResponse.ts
|
|
1601
|
+
var OfferResponse_exports = {};
|
|
1602
|
+
__export(OfferResponse_exports, {
|
|
1603
|
+
from: () => from7
|
|
1604
|
+
});
|
|
1605
|
+
function from7(offer) {
|
|
1606
|
+
return toSnakeCase(offer);
|
|
1607
|
+
}
|
|
1608
|
+
var MAX_LIMIT = 100;
|
|
1609
|
+
var DEFAULT_LIMIT = 20;
|
|
1610
|
+
var PaginationQueryParams = z7.object({
|
|
1611
|
+
cursor: z7.string().optional().refine(
|
|
819
1612
|
(val) => {
|
|
820
1613
|
if (!val) return true;
|
|
821
1614
|
try {
|
|
822
|
-
const decoded =
|
|
1615
|
+
const decoded = Cursor_exports.decode(val);
|
|
823
1616
|
return decoded !== null;
|
|
824
1617
|
} catch (_error) {
|
|
825
1618
|
return false;
|
|
@@ -832,34 +1625,49 @@ var MatchOffersQueryParams = z.object({
|
|
|
832
1625
|
description: "Pagination cursor in base64url-encoded format",
|
|
833
1626
|
example: "eyJzb3J0IjoicHJpY2UiLCJkaXIiOiJkZXNjIiwicHJpY2UiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwiaGFzaCI6IjB4ZGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIifQ"
|
|
834
1627
|
}),
|
|
835
|
-
limit:
|
|
1628
|
+
limit: z7.string().regex(/^[1-9]\d*$/, {
|
|
836
1629
|
message: "Limit must be a positive integer"
|
|
837
1630
|
}).transform((val) => Number.parseInt(val, 10)).pipe(
|
|
838
|
-
|
|
1631
|
+
z7.number().max(MAX_LIMIT, {
|
|
839
1632
|
message: `Limit cannot exceed ${MAX_LIMIT}`
|
|
840
1633
|
})
|
|
841
1634
|
).optional().default(DEFAULT_LIMIT).meta({
|
|
842
1635
|
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT}`,
|
|
843
1636
|
example: 10
|
|
844
1637
|
})
|
|
845
|
-
})
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
)
|
|
1638
|
+
});
|
|
1639
|
+
var GetOffersQueryParams = z7.object({
|
|
1640
|
+
...PaginationQueryParams.shape,
|
|
1641
|
+
side: z7.enum(["buy", "sell"]).meta({
|
|
1642
|
+
description: "Side of the offer.",
|
|
1643
|
+
example: "buy"
|
|
1644
|
+
}),
|
|
1645
|
+
obligation_id: z7.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
|
|
1646
|
+
description: "Offers obligation id",
|
|
1647
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
1648
|
+
})
|
|
1649
|
+
});
|
|
1650
|
+
var GetObligationsQueryParams = z7.object({
|
|
1651
|
+
...PaginationQueryParams.shape,
|
|
1652
|
+
cursor: z7.string().optional().meta({
|
|
1653
|
+
description: "Obligation id cursor",
|
|
1654
|
+
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
1655
|
+
})
|
|
1656
|
+
});
|
|
852
1657
|
var schemas = {
|
|
853
1658
|
get_offers: GetOffersQueryParams,
|
|
854
|
-
|
|
1659
|
+
get_obligations: GetObligationsQueryParams
|
|
855
1660
|
};
|
|
1661
|
+
function parse(action, query) {
|
|
1662
|
+
return schemas[action].parse(query);
|
|
1663
|
+
}
|
|
856
1664
|
function safeParse(action, query, error) {
|
|
857
1665
|
return schemas[action].safeParse(query, {
|
|
858
1666
|
error
|
|
859
1667
|
});
|
|
860
1668
|
}
|
|
861
1669
|
|
|
862
|
-
// src/
|
|
1670
|
+
// src/api/Api/Schema/openapi.ts
|
|
863
1671
|
var successResponseSchema = z.object({
|
|
864
1672
|
status: z.literal("success"),
|
|
865
1673
|
cursor: z.string().nullable(),
|
|
@@ -882,8 +1690,8 @@ var errorResponseSchema = z.object({
|
|
|
882
1690
|
var paths = {
|
|
883
1691
|
"/v1/offers": {
|
|
884
1692
|
get: {
|
|
885
|
-
summary: "
|
|
886
|
-
description: "
|
|
1693
|
+
summary: "Offers",
|
|
1694
|
+
description: "Find offers that match specific criteria",
|
|
887
1695
|
tags: ["Offers"],
|
|
888
1696
|
requestParams: {
|
|
889
1697
|
query: GetOffersQueryParams
|
|
@@ -908,13 +1716,13 @@ var paths = {
|
|
|
908
1716
|
}
|
|
909
1717
|
}
|
|
910
1718
|
},
|
|
911
|
-
"/v1/
|
|
1719
|
+
"/v1/obligations": {
|
|
912
1720
|
get: {
|
|
913
|
-
summary: "
|
|
914
|
-
description: "
|
|
915
|
-
tags: ["
|
|
1721
|
+
summary: "Obligations",
|
|
1722
|
+
description: "List obligations with pagination support",
|
|
1723
|
+
tags: ["Obligations"],
|
|
916
1724
|
requestParams: {
|
|
917
|
-
query:
|
|
1725
|
+
query: GetObligationsQueryParams
|
|
918
1726
|
},
|
|
919
1727
|
responses: {
|
|
920
1728
|
200: {
|
|
@@ -935,9 +1743,60 @@ var paths = {
|
|
|
935
1743
|
}
|
|
936
1744
|
}
|
|
937
1745
|
}
|
|
1746
|
+
},
|
|
1747
|
+
"/v1/health": {
|
|
1748
|
+
get: {
|
|
1749
|
+
summary: "Router status",
|
|
1750
|
+
description: "Retrieve the aggregated status of the router.",
|
|
1751
|
+
tags: ["Health"],
|
|
1752
|
+
responses: {
|
|
1753
|
+
200: {
|
|
1754
|
+
description: "Success",
|
|
1755
|
+
content: {
|
|
1756
|
+
"application/json": {
|
|
1757
|
+
schema: RouterStatusResponse
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
},
|
|
1764
|
+
"/v1/health/collectors": {
|
|
1765
|
+
get: {
|
|
1766
|
+
summary: "Collectors health",
|
|
1767
|
+
description: "Retrieve the block numbers processed by collectors and their sync status.",
|
|
1768
|
+
tags: ["Health"],
|
|
1769
|
+
responses: {
|
|
1770
|
+
200: {
|
|
1771
|
+
description: "Success",
|
|
1772
|
+
content: {
|
|
1773
|
+
"application/json": {
|
|
1774
|
+
schema: CollectorsHealthResponse
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
"/v1/health/chains": {
|
|
1782
|
+
get: {
|
|
1783
|
+
summary: "Chains health",
|
|
1784
|
+
description: "Retrieve the latest block processed for each chain.",
|
|
1785
|
+
tags: ["Health"],
|
|
1786
|
+
responses: {
|
|
1787
|
+
200: {
|
|
1788
|
+
description: "Success",
|
|
1789
|
+
content: {
|
|
1790
|
+
"application/json": {
|
|
1791
|
+
schema: ChainsHealthResponse
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
938
1797
|
}
|
|
939
1798
|
};
|
|
940
|
-
createDocument({
|
|
1799
|
+
var OpenApi = createDocument({
|
|
941
1800
|
openapi: "3.1.0",
|
|
942
1801
|
info: {
|
|
943
1802
|
title: "Router API",
|
|
@@ -947,6 +1806,12 @@ createDocument({
|
|
|
947
1806
|
tags: [
|
|
948
1807
|
{
|
|
949
1808
|
name: "Offers"
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
name: "Obligations"
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
name: "Health"
|
|
950
1815
|
}
|
|
951
1816
|
],
|
|
952
1817
|
servers: [
|
|
@@ -962,120 +1827,40 @@ createDocument({
|
|
|
962
1827
|
paths
|
|
963
1828
|
});
|
|
964
1829
|
|
|
965
|
-
// src/
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
consumed,
|
|
980
|
-
status,
|
|
981
|
-
metadata
|
|
982
|
-
};
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// src/core/router/Client.ts
|
|
986
|
-
function connect(opts) {
|
|
987
|
-
const u = new URL(opts?.url || "https://router.morpho.dev");
|
|
1830
|
+
// src/api/Client.ts
|
|
1831
|
+
var Client_exports = {};
|
|
1832
|
+
__export(Client_exports, {
|
|
1833
|
+
HttpForbiddenError: () => HttpForbiddenError,
|
|
1834
|
+
HttpGetApiFailedError: () => HttpGetApiFailedError,
|
|
1835
|
+
HttpRateLimitError: () => HttpRateLimitError,
|
|
1836
|
+
HttpUnauthorizedError: () => HttpUnauthorizedError,
|
|
1837
|
+
InvalidUrlError: () => InvalidUrlError,
|
|
1838
|
+
connect: () => connect,
|
|
1839
|
+
getObligations: () => getObligations,
|
|
1840
|
+
getOffers: () => getOffers
|
|
1841
|
+
});
|
|
1842
|
+
function connect(options) {
|
|
1843
|
+
const u = new URL(options?.url || "https://router.morpho.dev");
|
|
988
1844
|
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
989
1845
|
throw new InvalidUrlError(u.toString());
|
|
990
1846
|
}
|
|
991
|
-
const headers =
|
|
1847
|
+
const headers = options?.headers ?? new Headers();
|
|
992
1848
|
headers.set("Content-Type", "application/json");
|
|
993
|
-
|
|
1849
|
+
options?.apiKey !== void 0 ? headers.set("X-API-Key", options.apiKey) : null;
|
|
994
1850
|
const config = {
|
|
995
1851
|
url: u,
|
|
996
1852
|
headers
|
|
997
1853
|
};
|
|
998
1854
|
return {
|
|
999
1855
|
...config,
|
|
1000
|
-
|
|
1001
|
-
|
|
1856
|
+
getOffers: (parameters) => getOffers(config, parameters),
|
|
1857
|
+
getObligations: (parameters) => getObligations(config, parameters)
|
|
1002
1858
|
};
|
|
1003
1859
|
}
|
|
1004
|
-
async function
|
|
1860
|
+
async function getOffers(config, parameters) {
|
|
1005
1861
|
const url = new URL(`${config.url.toString()}v1/offers`);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
if (parameters.side) {
|
|
1010
|
-
url.searchParams.set("side", parameters.side);
|
|
1011
|
-
}
|
|
1012
|
-
if (parameters.chains?.length) {
|
|
1013
|
-
url.searchParams.set("chains", parameters.chains.join(","));
|
|
1014
|
-
}
|
|
1015
|
-
if (parameters.loanTokens?.length) {
|
|
1016
|
-
url.searchParams.set("loan_tokens", parameters.loanTokens.join(","));
|
|
1017
|
-
}
|
|
1018
|
-
if (parameters.status?.length) {
|
|
1019
|
-
url.searchParams.set("status", parameters.status.join(","));
|
|
1020
|
-
}
|
|
1021
|
-
if (parameters.callbackAddresses?.length) {
|
|
1022
|
-
url.searchParams.set("callback_addresses", parameters.callbackAddresses.join(","));
|
|
1023
|
-
}
|
|
1024
|
-
if (parameters.minAmount !== void 0) {
|
|
1025
|
-
url.searchParams.set("min_amount", parameters.minAmount.toString());
|
|
1026
|
-
}
|
|
1027
|
-
if (parameters.maxAmount !== void 0) {
|
|
1028
|
-
url.searchParams.set("max_amount", parameters.maxAmount.toString());
|
|
1029
|
-
}
|
|
1030
|
-
if (parameters.minRate !== void 0) {
|
|
1031
|
-
url.searchParams.set("min_rate", parameters.minRate.toString());
|
|
1032
|
-
}
|
|
1033
|
-
if (parameters.maxRate !== void 0) {
|
|
1034
|
-
url.searchParams.set("max_rate", parameters.maxRate.toString());
|
|
1035
|
-
}
|
|
1036
|
-
if (parameters.minMaturity !== void 0) {
|
|
1037
|
-
url.searchParams.set("min_maturity", parameters.minMaturity.toString());
|
|
1038
|
-
}
|
|
1039
|
-
if (parameters.maxMaturity !== void 0) {
|
|
1040
|
-
url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
|
|
1041
|
-
}
|
|
1042
|
-
if (parameters.minExpiry !== void 0) {
|
|
1043
|
-
url.searchParams.set("min_expiry", parameters.minExpiry.toString());
|
|
1044
|
-
}
|
|
1045
|
-
if (parameters.maxExpiry !== void 0) {
|
|
1046
|
-
url.searchParams.set("max_expiry", parameters.maxExpiry.toString());
|
|
1047
|
-
}
|
|
1048
|
-
if (parameters.collateralAssets?.length) {
|
|
1049
|
-
url.searchParams.set("collateral_assets", parameters.collateralAssets.join(","));
|
|
1050
|
-
}
|
|
1051
|
-
if (parameters.collateralOracles?.length) {
|
|
1052
|
-
url.searchParams.set("collateral_oracles", parameters.collateralOracles.join(","));
|
|
1053
|
-
}
|
|
1054
|
-
if (parameters.collateralTuple?.length) {
|
|
1055
|
-
const tupleStr = parameters.collateralTuple.map(({ asset, oracle, lltv }) => {
|
|
1056
|
-
let result = asset;
|
|
1057
|
-
if (oracle) {
|
|
1058
|
-
result += `:${oracle}`;
|
|
1059
|
-
} else if (lltv !== void 0) {
|
|
1060
|
-
result += `:`;
|
|
1061
|
-
}
|
|
1062
|
-
if (lltv !== void 0) result += `:${formatUnits(lltv, 16)}`;
|
|
1063
|
-
return result;
|
|
1064
|
-
}).join("#");
|
|
1065
|
-
url.searchParams.set("collateral_tuple", tupleStr);
|
|
1066
|
-
}
|
|
1067
|
-
if (parameters.minLltv !== void 0) {
|
|
1068
|
-
url.searchParams.set("min_lltv", formatUnits(parameters.minLltv, 16));
|
|
1069
|
-
}
|
|
1070
|
-
if (parameters.maxLltv !== void 0) {
|
|
1071
|
-
url.searchParams.set("max_lltv", formatUnits(parameters.maxLltv, 16));
|
|
1072
|
-
}
|
|
1073
|
-
if (parameters.sortBy) {
|
|
1074
|
-
url.searchParams.set("sort_by", parameters.sortBy);
|
|
1075
|
-
}
|
|
1076
|
-
if (parameters.sortOrder) {
|
|
1077
|
-
url.searchParams.set("sort_order", parameters.sortOrder);
|
|
1078
|
-
}
|
|
1862
|
+
url.searchParams.set("side", parameters.side);
|
|
1863
|
+
url.searchParams.set("obligation_id", parameters.obligationId.toString());
|
|
1079
1864
|
if (parameters.cursor) {
|
|
1080
1865
|
url.searchParams.set("cursor", parameters.cursor);
|
|
1081
1866
|
}
|
|
@@ -1083,72 +1868,45 @@ async function get(config, parameters) {
|
|
|
1083
1868
|
url.searchParams.set("limit", parameters.limit.toString());
|
|
1084
1869
|
}
|
|
1085
1870
|
const { cursor: returnedCursor, data: offers } = await getApi(config, url);
|
|
1086
|
-
const routerOffers = offers.map(
|
|
1871
|
+
const routerOffers = offers.map(Offer_exports.fromSnakeCase);
|
|
1087
1872
|
return {
|
|
1088
1873
|
cursor: returnedCursor,
|
|
1089
|
-
offers: routerOffers
|
|
1874
|
+
offers: routerOffers
|
|
1090
1875
|
};
|
|
1091
1876
|
}
|
|
1092
|
-
async function
|
|
1093
|
-
const url = new URL(`${config.url.toString()}v1/
|
|
1094
|
-
|
|
1095
|
-
url.searchParams.set("chain_id", parameters.chainId.toString());
|
|
1096
|
-
if (parameters.rate !== void 0) {
|
|
1097
|
-
url.searchParams.set("rate", parameters.rate.toString());
|
|
1098
|
-
}
|
|
1099
|
-
if (parameters.collaterals?.length) {
|
|
1100
|
-
const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${formatUnits(lltv, 16)}`).join("#");
|
|
1101
|
-
url.searchParams.set("collaterals", collateralsStr);
|
|
1102
|
-
}
|
|
1103
|
-
if (parameters.maturity !== void 0) {
|
|
1104
|
-
url.searchParams.set("maturity", parameters.maturity.toString());
|
|
1105
|
-
}
|
|
1106
|
-
if (parameters.minMaturity !== void 0) {
|
|
1107
|
-
url.searchParams.set("min_maturity", parameters.minMaturity.toString());
|
|
1108
|
-
}
|
|
1109
|
-
if (parameters.maxMaturity !== void 0) {
|
|
1110
|
-
url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
|
|
1111
|
-
}
|
|
1112
|
-
if (parameters.loanToken) {
|
|
1113
|
-
url.searchParams.set("loan_token", parameters.loanToken);
|
|
1114
|
-
}
|
|
1115
|
-
if (parameters.creator) {
|
|
1116
|
-
url.searchParams.set("creator", parameters.creator);
|
|
1117
|
-
}
|
|
1118
|
-
if (parameters.status?.length) {
|
|
1119
|
-
url.searchParams.set("status", parameters.status.join(","));
|
|
1120
|
-
}
|
|
1121
|
-
if (parameters.cursor) {
|
|
1877
|
+
async function getObligations(config, parameters) {
|
|
1878
|
+
const url = new URL(`${config.url.toString()}v1/obligations`);
|
|
1879
|
+
if (parameters?.cursor !== void 0) {
|
|
1122
1880
|
url.searchParams.set("cursor", parameters.cursor);
|
|
1123
1881
|
}
|
|
1124
|
-
if (parameters
|
|
1882
|
+
if (parameters?.limit !== void 0) {
|
|
1125
1883
|
url.searchParams.set("limit", parameters.limit.toString());
|
|
1126
1884
|
}
|
|
1127
|
-
const { cursor: returnedCursor, data:
|
|
1128
|
-
const
|
|
1885
|
+
const { cursor: returnedCursor, data: obligationsSnake } = await getApi(config, url);
|
|
1886
|
+
const obligations = obligationsSnake.map(Obligation_exports.fromSnakeCase);
|
|
1129
1887
|
return {
|
|
1130
1888
|
cursor: returnedCursor,
|
|
1131
|
-
|
|
1889
|
+
obligations
|
|
1132
1890
|
};
|
|
1133
1891
|
}
|
|
1134
1892
|
async function getApi(config, url) {
|
|
1135
1893
|
const pathname = url.pathname;
|
|
1136
1894
|
let action;
|
|
1137
1895
|
switch (true) {
|
|
1138
|
-
case pathname.includes("/v1/offers/match"):
|
|
1139
|
-
action = "match_offers";
|
|
1140
|
-
break;
|
|
1141
1896
|
case pathname.includes("/v1/offers"):
|
|
1142
1897
|
action = "get_offers";
|
|
1143
1898
|
break;
|
|
1899
|
+
case pathname.includes("/v1/obligations"):
|
|
1900
|
+
action = "get_obligations";
|
|
1901
|
+
break;
|
|
1144
1902
|
default:
|
|
1145
|
-
throw new
|
|
1903
|
+
throw new HttpGetApiFailedError("Unknown endpoint", {
|
|
1146
1904
|
details: `Unsupported path: ${pathname}`
|
|
1147
1905
|
});
|
|
1148
1906
|
}
|
|
1149
1907
|
const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
|
|
1150
1908
|
if (!schemaParseResult.success) {
|
|
1151
|
-
throw new
|
|
1909
|
+
throw new HttpGetApiFailedError(`Invalid URL parameters`, {
|
|
1152
1910
|
details: schemaParseResult.error.issues[0]?.message
|
|
1153
1911
|
});
|
|
1154
1912
|
}
|
|
@@ -1165,19 +1923,19 @@ async function getApi(config, url) {
|
|
|
1165
1923
|
case 429:
|
|
1166
1924
|
throw new HttpRateLimitError();
|
|
1167
1925
|
}
|
|
1168
|
-
throw new
|
|
1926
|
+
throw new HttpGetApiFailedError(`GET request returned ${response.status}`, {
|
|
1169
1927
|
details: await response.text()
|
|
1170
1928
|
});
|
|
1171
1929
|
}
|
|
1172
1930
|
return response.json();
|
|
1173
1931
|
}
|
|
1174
|
-
var InvalidUrlError = class extends
|
|
1932
|
+
var InvalidUrlError = class extends BaseError {
|
|
1175
1933
|
constructor(url) {
|
|
1176
1934
|
super(`URL "${url}" is not http/https.`);
|
|
1177
1935
|
__publicField(this, "name", "Router.InvalidUrlError");
|
|
1178
1936
|
}
|
|
1179
1937
|
};
|
|
1180
|
-
var HttpUnauthorizedError = class extends
|
|
1938
|
+
var HttpUnauthorizedError = class extends BaseError {
|
|
1181
1939
|
constructor() {
|
|
1182
1940
|
super("Unauthorized.", {
|
|
1183
1941
|
metaMessages: ["Ensure that an API key is provided."]
|
|
@@ -1185,7 +1943,7 @@ var HttpUnauthorizedError = class extends Errors.BaseError {
|
|
|
1185
1943
|
__publicField(this, "name", "Router.HttpUnauthorizedError");
|
|
1186
1944
|
}
|
|
1187
1945
|
};
|
|
1188
|
-
var HttpForbiddenError = class extends
|
|
1946
|
+
var HttpForbiddenError = class extends BaseError {
|
|
1189
1947
|
constructor() {
|
|
1190
1948
|
super("Forbidden.", {
|
|
1191
1949
|
metaMessages: ["Ensure that the API key is valid."]
|
|
@@ -1193,7 +1951,7 @@ var HttpForbiddenError = class extends Errors.BaseError {
|
|
|
1193
1951
|
__publicField(this, "name", "Router.HttpForbiddenError");
|
|
1194
1952
|
}
|
|
1195
1953
|
};
|
|
1196
|
-
var HttpRateLimitError = class extends
|
|
1954
|
+
var HttpRateLimitError = class extends BaseError {
|
|
1197
1955
|
constructor() {
|
|
1198
1956
|
super("Rate limit exceeded.", {
|
|
1199
1957
|
metaMessages: [
|
|
@@ -1203,16 +1961,16 @@ var HttpRateLimitError = class extends Errors.BaseError {
|
|
|
1203
1961
|
__publicField(this, "name", "Router.HttpRateLimitError");
|
|
1204
1962
|
}
|
|
1205
1963
|
};
|
|
1206
|
-
var
|
|
1964
|
+
var HttpGetApiFailedError = class extends BaseError {
|
|
1207
1965
|
constructor(message, { details } = {}) {
|
|
1208
1966
|
super(message, {
|
|
1209
1967
|
metaMessages: [details]
|
|
1210
1968
|
});
|
|
1211
|
-
__publicField(this, "name", "Router.
|
|
1969
|
+
__publicField(this, "name", "Router.HttpGetApiFailedError");
|
|
1212
1970
|
}
|
|
1213
1971
|
};
|
|
1214
1972
|
|
|
1215
|
-
// src/
|
|
1973
|
+
// src/collectors/validations/Validation.ts
|
|
1216
1974
|
var Validation_exports = {};
|
|
1217
1975
|
__export(Validation_exports, {
|
|
1218
1976
|
run: () => run
|
|
@@ -1259,17 +2017,17 @@ async function run(parameters) {
|
|
|
1259
2017
|
};
|
|
1260
2018
|
}
|
|
1261
2019
|
|
|
1262
|
-
// src/
|
|
2020
|
+
// src/collectors/validations/ValidationRule.ts
|
|
1263
2021
|
var ValidationRule_exports = {};
|
|
1264
2022
|
__export(ValidationRule_exports, {
|
|
1265
|
-
batch: () =>
|
|
2023
|
+
batch: () => batch2,
|
|
1266
2024
|
morpho: () => morpho,
|
|
1267
2025
|
single: () => single
|
|
1268
2026
|
});
|
|
1269
2027
|
function single(name, run2) {
|
|
1270
2028
|
return { kind: "single", name, run: run2 };
|
|
1271
2029
|
}
|
|
1272
|
-
function
|
|
2030
|
+
function batch2(name, run2) {
|
|
1273
2031
|
return { kind: "batch", name, run: run2 };
|
|
1274
2032
|
}
|
|
1275
2033
|
function morpho() {
|
|
@@ -1307,7 +2065,17 @@ function morpho() {
|
|
|
1307
2065
|
"buy_offers_non_empty_callback",
|
|
1308
2066
|
(offer, _) => {
|
|
1309
2067
|
if (offer.buy && offer.callback.data !== "0x") {
|
|
1310
|
-
|
|
2068
|
+
const allowed = new Set(
|
|
2069
|
+
Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.BuyVaultV1Callback].map(
|
|
2070
|
+
(a) => a.toLowerCase()
|
|
2071
|
+
)
|
|
2072
|
+
);
|
|
2073
|
+
const callbackAddress = offer.callback.address?.toLowerCase();
|
|
2074
|
+
if (!callbackAddress || !allowed.has(callbackAddress)) {
|
|
2075
|
+
return {
|
|
2076
|
+
message: "Buy offers with non-empty callback must use a whitelisted BuyVaultV1Callback."
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
1311
2079
|
}
|
|
1312
2080
|
}
|
|
1313
2081
|
);
|
|
@@ -1316,7 +2084,7 @@ function morpho() {
|
|
|
1316
2084
|
(offer, _) => {
|
|
1317
2085
|
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1318
2086
|
const allowed = new Set(
|
|
1319
|
-
WhitelistedCallbackAddresses[
|
|
2087
|
+
Callback_exports.WhitelistedCallbackAddresses[Callback_exports.CallbackType.SellERC20Callback].map(
|
|
1320
2088
|
(a) => a.toLowerCase()
|
|
1321
2089
|
)
|
|
1322
2090
|
);
|
|
@@ -1332,10 +2100,7 @@ function morpho() {
|
|
|
1332
2100
|
(offer, _) => {
|
|
1333
2101
|
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1334
2102
|
try {
|
|
1335
|
-
const decoded =
|
|
1336
|
-
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1337
|
-
data: offer.callback.data
|
|
1338
|
-
});
|
|
2103
|
+
const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
|
|
1339
2104
|
if (decoded.length === 0) {
|
|
1340
2105
|
return { message: "Sell offer callback data must include at least one collateral." };
|
|
1341
2106
|
}
|
|
@@ -1350,10 +2115,7 @@ function morpho() {
|
|
|
1350
2115
|
(offer, _) => {
|
|
1351
2116
|
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1352
2117
|
try {
|
|
1353
|
-
const decoded =
|
|
1354
|
-
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1355
|
-
data: offer.callback.data
|
|
1356
|
-
});
|
|
2118
|
+
const decoded = Callback_exports.decodeSellERC20Callback(offer.callback.data);
|
|
1357
2119
|
const offerCollaterals = new Set(
|
|
1358
2120
|
offer.collaterals.map((c) => c.asset.toLowerCase())
|
|
1359
2121
|
);
|
|
@@ -1367,20 +2129,306 @@ function morpho() {
|
|
|
1367
2129
|
}
|
|
1368
2130
|
}
|
|
1369
2131
|
);
|
|
2132
|
+
const buyCallbackDataInvalid = single(
|
|
2133
|
+
"buy_offers_callback_data_invalid",
|
|
2134
|
+
(offer, _) => {
|
|
2135
|
+
if (offer.buy && offer.callback.data !== "0x") {
|
|
2136
|
+
try {
|
|
2137
|
+
const decoded = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
|
|
2138
|
+
if (decoded.length === 0) {
|
|
2139
|
+
return { message: "Buy offer callback data must include at least one vault." };
|
|
2140
|
+
}
|
|
2141
|
+
} catch (_2) {
|
|
2142
|
+
return { message: "Buy offer callback data cannot be decoded." };
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
);
|
|
2147
|
+
const buyCallbackVaultInvalid = batch2(
|
|
2148
|
+
"buy_offers_callback_vault_invalid",
|
|
2149
|
+
async (offers, { client, chain }) => {
|
|
2150
|
+
const validationIssues = /* @__PURE__ */ new Map();
|
|
2151
|
+
const offersByVaultAddress = /* @__PURE__ */ new Map();
|
|
2152
|
+
for (let i = 0; i < offers.length; i++) {
|
|
2153
|
+
const offer = offers[i];
|
|
2154
|
+
if (offer.buy && offer.callback.data !== "0x") {
|
|
2155
|
+
try {
|
|
2156
|
+
const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
|
|
2157
|
+
for (const { vault } of callbackVaults) {
|
|
2158
|
+
const normalizedVaultAddress = vault.toLowerCase();
|
|
2159
|
+
if (!offersByVaultAddress.has(normalizedVaultAddress)) {
|
|
2160
|
+
offersByVaultAddress.set(normalizedVaultAddress, []);
|
|
2161
|
+
}
|
|
2162
|
+
offersByVaultAddress.get(normalizedVaultAddress).push({ index: i, offer });
|
|
2163
|
+
}
|
|
2164
|
+
} catch (_) {
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
const uniqueVaultAddresses = Array.from(offersByVaultAddress.keys());
|
|
2169
|
+
if (uniqueVaultAddresses.length === 0) return validationIssues;
|
|
2170
|
+
const whitelistedFactories = Object.values(chain.vaultV1Factory);
|
|
2171
|
+
const multicallContracts = [];
|
|
2172
|
+
for (const vaultAddress of uniqueVaultAddresses) {
|
|
2173
|
+
multicallContracts.push({
|
|
2174
|
+
address: vaultAddress,
|
|
2175
|
+
abi: Abi_exports.ERC4626,
|
|
2176
|
+
functionName: "asset"
|
|
2177
|
+
});
|
|
2178
|
+
for (const factoryAddress of whitelistedFactories) {
|
|
2179
|
+
multicallContracts.push({
|
|
2180
|
+
address: factoryAddress,
|
|
2181
|
+
abi: Abi_exports.MetaMorphoFactory,
|
|
2182
|
+
functionName: "isMetaMorpho",
|
|
2183
|
+
args: [vaultAddress]
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
const multicallResults = await client.multicall({
|
|
2188
|
+
contracts: multicallContracts,
|
|
2189
|
+
allowFailure: true
|
|
2190
|
+
});
|
|
2191
|
+
const vaultAssetByAddress = /* @__PURE__ */ new Map();
|
|
2192
|
+
const registeredVaults = /* @__PURE__ */ new Set();
|
|
2193
|
+
const numberOfFactories = whitelistedFactories.length;
|
|
2194
|
+
let resultIndex = 0;
|
|
2195
|
+
for (const vaultAddress of uniqueVaultAddresses) {
|
|
2196
|
+
const assetCallResult = multicallResults[resultIndex++];
|
|
2197
|
+
const assetAddress = assetCallResult.status === "success" ? assetCallResult.result : null;
|
|
2198
|
+
vaultAssetByAddress.set(vaultAddress, assetAddress);
|
|
2199
|
+
let isRegisteredInFactory = false;
|
|
2200
|
+
for (let factoryIndex = 0; factoryIndex < numberOfFactories; factoryIndex++) {
|
|
2201
|
+
const factoryCallResult = multicallResults[resultIndex++];
|
|
2202
|
+
if (factoryCallResult.status === "success" && factoryCallResult.result === true) {
|
|
2203
|
+
isRegisteredInFactory = true;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
if (isRegisteredInFactory) {
|
|
2207
|
+
registeredVaults.add(vaultAddress);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
const uniqueOffers = /* @__PURE__ */ new Map();
|
|
2211
|
+
for (const offersArray of offersByVaultAddress.values()) {
|
|
2212
|
+
for (const { index, offer } of offersArray) {
|
|
2213
|
+
uniqueOffers.set(index, offer);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
for (const [index, offer] of uniqueOffers) {
|
|
2217
|
+
try {
|
|
2218
|
+
const callbackVaults = Callback_exports.decodeBuyVaultV1Callback(offer.callback.data);
|
|
2219
|
+
const vaultsWithIssues = [];
|
|
2220
|
+
for (const { vault } of callbackVaults) {
|
|
2221
|
+
const normalizedVaultAddress = vault.toLowerCase();
|
|
2222
|
+
const assetAddress = vaultAssetByAddress.get(normalizedVaultAddress);
|
|
2223
|
+
const isRegistered = registeredVaults.has(normalizedVaultAddress);
|
|
2224
|
+
const failureReasons = [];
|
|
2225
|
+
if (assetAddress === null) {
|
|
2226
|
+
failureReasons.push("asset call failed");
|
|
2227
|
+
} else if (assetAddress && assetAddress.toLowerCase() !== offer.loanToken.toLowerCase()) {
|
|
2228
|
+
failureReasons.push("asset mismatch");
|
|
2229
|
+
}
|
|
2230
|
+
if (!isRegistered) {
|
|
2231
|
+
failureReasons.push("not registered in factory");
|
|
2232
|
+
}
|
|
2233
|
+
if (failureReasons.length > 0) {
|
|
2234
|
+
vaultsWithIssues.push({
|
|
2235
|
+
vaultAddress: vault,
|
|
2236
|
+
failureReasons: failureReasons.join(", ")
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
if (vaultsWithIssues.length > 0) {
|
|
2241
|
+
const failureDetails = vaultsWithIssues.map((v) => `${v.vaultAddress} (${v.failureReasons})`).join("; ");
|
|
2242
|
+
validationIssues.set(index, {
|
|
2243
|
+
message: `Buy offer callback vaults are invalid: ${failureDetails}`
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
} catch (_) {
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
return validationIssues;
|
|
2250
|
+
}
|
|
2251
|
+
);
|
|
2252
|
+
const maturity = single("maturity", (offer, _) => {
|
|
2253
|
+
const allowedMaturities = [Maturity_exports.from("end_of_month"), Maturity_exports.from("end_of_next_month")];
|
|
2254
|
+
if (!allowedMaturities.includes(offer.maturity)) {
|
|
2255
|
+
return {
|
|
2256
|
+
message: `Maturity must be end of current month (${allowedMaturities[0]}) or end of next month (${allowedMaturities[1]}). Got: ${offer.maturity}`
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
1370
2260
|
return [
|
|
1371
2261
|
chainId,
|
|
1372
2262
|
loanToken,
|
|
1373
2263
|
expiry,
|
|
2264
|
+
maturity,
|
|
1374
2265
|
// note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
|
|
1375
2266
|
// integrators should be able to choose if they want to keep the offer or not
|
|
1376
2267
|
sellEmptyCallback,
|
|
1377
2268
|
buyNonEmptyCallback,
|
|
1378
2269
|
sellNonWhitelistedCallback,
|
|
1379
2270
|
sellCallbackDataInvalid,
|
|
1380
|
-
sellCallbackCollateralInvalid
|
|
2271
|
+
sellCallbackCollateralInvalid,
|
|
2272
|
+
buyCallbackDataInvalid,
|
|
2273
|
+
buyCallbackVaultInvalid
|
|
1381
2274
|
];
|
|
1382
2275
|
}
|
|
1383
2276
|
|
|
1384
|
-
|
|
2277
|
+
// src/mempool/MempoolClient.ts
|
|
2278
|
+
var MempoolClient_exports = {};
|
|
2279
|
+
__export(MempoolClient_exports, {
|
|
2280
|
+
connect: () => connect2
|
|
2281
|
+
});
|
|
2282
|
+
var DEFAULT_BATCH_SIZE2 = 100;
|
|
2283
|
+
var DEFAULT_BLOCK_WINDOW2 = 100;
|
|
2284
|
+
function from8(parameters) {
|
|
2285
|
+
const config = {
|
|
2286
|
+
client: parameters.client,
|
|
2287
|
+
mempoolAddress: parameters.mempoolAddress,
|
|
2288
|
+
blockWindow: parameters.blockWindow || DEFAULT_BLOCK_WINDOW2
|
|
2289
|
+
};
|
|
2290
|
+
return {
|
|
2291
|
+
add: (parameters2) => add(config, parameters2),
|
|
2292
|
+
get: (parameters2) => get(config, parameters2),
|
|
2293
|
+
watch: (parameters2) => watch(config, parameters2),
|
|
2294
|
+
stream: (parameters2) => streamOffers(config, parameters2)
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
async function add(config, parameters) {
|
|
2298
|
+
const offer = Offer_exports.from(parameters.offer);
|
|
2299
|
+
if (!config.client.account) throw new WalletAccountNotSetError();
|
|
2300
|
+
const chainId = await getChainId(config.client);
|
|
2301
|
+
if (BigInt(chainId) !== offer.chainId)
|
|
2302
|
+
throw new ChainIdMismatchError(offer.chainId, BigInt(chainId));
|
|
2303
|
+
try {
|
|
2304
|
+
const tx = await config.client.sendTransaction({
|
|
2305
|
+
chain: config.client.chain,
|
|
2306
|
+
account: config.client.account,
|
|
2307
|
+
to: config.mempoolAddress,
|
|
2308
|
+
data: Offer_exports.encode(offer)
|
|
2309
|
+
});
|
|
2310
|
+
return { offer, txHash: tx };
|
|
2311
|
+
} catch (error) {
|
|
2312
|
+
throw new ViemClientError(error instanceof Error ? error.message : "Unknown error");
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
async function* get(config, parameters) {
|
|
2316
|
+
const {
|
|
2317
|
+
loanToken,
|
|
2318
|
+
blockNumberGte,
|
|
2319
|
+
blockNumberLte,
|
|
2320
|
+
order = "desc",
|
|
2321
|
+
options: { maxBatchSize = DEFAULT_BATCH_SIZE2 } = {}
|
|
2322
|
+
} = parameters || {};
|
|
2323
|
+
yield* streamOffers(config, {
|
|
2324
|
+
loanToken,
|
|
2325
|
+
order,
|
|
2326
|
+
blockNumberGte,
|
|
2327
|
+
blockNumberLte,
|
|
2328
|
+
options: { maxBatchSize, blockWindow: config.blockWindow }
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
function watch(config, parameters) {
|
|
2332
|
+
const {
|
|
2333
|
+
loanToken,
|
|
2334
|
+
lastSyncedBlock,
|
|
2335
|
+
polling: { interval = 3e4, maxBatchSize = DEFAULT_BATCH_SIZE2 } = {},
|
|
2336
|
+
onOffers
|
|
2337
|
+
} = parameters;
|
|
2338
|
+
return poll(
|
|
2339
|
+
async () => {
|
|
2340
|
+
const blockNumberGte = await lastSyncedBlock();
|
|
2341
|
+
const stream = streamOffers(config, {
|
|
2342
|
+
loanToken,
|
|
2343
|
+
order: "asc",
|
|
2344
|
+
blockNumberGte,
|
|
2345
|
+
options: { maxBatchSize, blockWindow: config.blockWindow }
|
|
2346
|
+
});
|
|
2347
|
+
for await (const { offers, blockNumber } of stream) {
|
|
2348
|
+
await onOffers(offers, blockNumber);
|
|
2349
|
+
}
|
|
2350
|
+
},
|
|
2351
|
+
{ interval }
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
var chainIdCache = /* @__PURE__ */ new Map();
|
|
2355
|
+
var getChainId = async (client) => {
|
|
2356
|
+
if (chainIdCache.has(client.uid)) return chainIdCache.get(client.uid);
|
|
2357
|
+
const chainId = await client.getChainId();
|
|
2358
|
+
chainIdCache.set(client.uid, chainId);
|
|
2359
|
+
return chainId;
|
|
2360
|
+
};
|
|
2361
|
+
async function* streamOffers(config, parameters) {
|
|
2362
|
+
const {
|
|
2363
|
+
loanToken,
|
|
2364
|
+
blockNumberGte,
|
|
2365
|
+
blockNumberLte,
|
|
2366
|
+
order = "desc",
|
|
2367
|
+
options: { maxBatchSize = DEFAULT_BATCH_SIZE2, blockWindow = config.blockWindow } = {}
|
|
2368
|
+
} = parameters;
|
|
2369
|
+
const stream = Chain_exports.streamLogs({
|
|
2370
|
+
client: config.client.extend(publicActions),
|
|
2371
|
+
contractAddress: config.mempoolAddress,
|
|
2372
|
+
event: {
|
|
2373
|
+
type: "event",
|
|
2374
|
+
name: "Event",
|
|
2375
|
+
inputs: [{ name: "data", type: "bytes", indexed: false, internalType: "bytes" }],
|
|
2376
|
+
anonymous: false
|
|
2377
|
+
},
|
|
2378
|
+
blockNumberGte,
|
|
2379
|
+
blockNumberLte,
|
|
2380
|
+
order,
|
|
2381
|
+
options: { maxBatchSize, blockWindow }
|
|
2382
|
+
});
|
|
2383
|
+
let blockNumber = order === "asc" ? blockNumberGte : blockNumberLte;
|
|
2384
|
+
for await (const { logs, blockNumber: newBlockNumber } of stream) {
|
|
2385
|
+
blockNumber = newBlockNumber;
|
|
2386
|
+
if (logs.length === 0) break;
|
|
2387
|
+
let offersAndBlockNumbers = logs.map((log) => {
|
|
2388
|
+
const [payload] = decodeAbiParameters([{ type: "bytes" }], log.data);
|
|
2389
|
+
try {
|
|
2390
|
+
return { offer: Offer_exports.decode(payload, log.blockNumber), blockNumber: log.blockNumber };
|
|
2391
|
+
} catch (_) {
|
|
2392
|
+
return null;
|
|
2393
|
+
}
|
|
2394
|
+
}).filter((item) => item !== null);
|
|
2395
|
+
if (loanToken)
|
|
2396
|
+
offersAndBlockNumbers = offersAndBlockNumbers.filter(
|
|
2397
|
+
(o) => o.offer.loanToken.toLowerCase() === loanToken.toLowerCase()
|
|
2398
|
+
);
|
|
2399
|
+
if (offersAndBlockNumbers.length === 0) continue;
|
|
2400
|
+
yield {
|
|
2401
|
+
offers: offersAndBlockNumbers.map((item) => item.offer),
|
|
2402
|
+
blockNumber
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
yield { offers: [], blockNumber };
|
|
2406
|
+
return;
|
|
2407
|
+
}
|
|
2408
|
+
var WalletAccountNotSetError = class extends BaseError {
|
|
2409
|
+
constructor() {
|
|
2410
|
+
super("Wallet account is not set.");
|
|
2411
|
+
__publicField(this, "name", "Mempool.WalletAccountNotSetError");
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
var ViemClientError = class extends BaseError {
|
|
2415
|
+
constructor() {
|
|
2416
|
+
super(...arguments);
|
|
2417
|
+
__publicField(this, "name", "Mempool.ViemClientError");
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
var ChainIdMismatchError = class extends BaseError {
|
|
2421
|
+
constructor(expected, actual) {
|
|
2422
|
+
super(`Chain ID mismatch. Offer chain ID is ${expected}, network chain ID is ${actual}.`);
|
|
2423
|
+
__publicField(this, "name", "Mempool.ChainIdMismatchError");
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2427
|
+
// src/mempool/MempoolClient.ts
|
|
2428
|
+
function connect2(parameters) {
|
|
2429
|
+
return from8(parameters);
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
export { Abi_exports as Abi, BrandTypeId, Callback_exports as Callback, Chain_exports as Chain, Collateral_exports as Collateral, Cursor_exports as Cursor, Errors_exports as Errors, Format_exports as Format, LLTV_exports as LLTV, Liquidity_exports as Liquidity, Maturity_exports as Maturity, MempoolClient_exports as Mempool, Obligation_exports as Obligation, Offer_exports as Offer, Schema_exports as RouterApi, Client_exports as RouterClient, time_exports as Time, utils_exports as Utils, Validation_exports as Validation, ValidationRule_exports as ValidationRule };
|
|
1385
2433
|
//# sourceMappingURL=index.browser.mjs.map
|
|
1386
2434
|
//# sourceMappingURL=index.browser.mjs.map
|