@morpho-dev/router 0.1.9 → 0.1.11

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.
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var mempool = require('@morpho-dev/mempool');
4
- var jsBase64 = require('js-base64');
5
4
  var viem = require('viem');
5
+ var jsBase64 = require('js-base64');
6
6
  var v4 = require('zod/v4');
7
7
  var zodOpenapi = require('zod-openapi');
8
8
 
@@ -18,60 +18,153 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
18
18
  var Callback_exports = {};
19
19
  __export(Callback_exports, {
20
20
  CallbackType: () => CallbackType,
21
+ WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
21
22
  buildLiquidity: () => buildLiquidity,
23
+ decode: () => decode,
24
+ encode: () => encode,
22
25
  getCallbackIdForOffer: () => getCallbackIdForOffer
23
26
  });
24
27
  var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
25
28
  CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
29
+ CallbackType2["SellWithdrawFromWallet"] = "sell_withdraw_from_wallet";
26
30
  return CallbackType2;
27
31
  })(CallbackType || {});
32
+ var WhitelistedCallbackAddresses = {
33
+ ["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
34
+ ["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */]: [
35
+ "0x1111111111111111111111111111111111111111",
36
+ "0x2222222222222222222222222222222222222222"
37
+ // @TODO: update once deployed and add mapping per chain if needed
38
+ ].map((address) => address.toLowerCase())
39
+ };
28
40
  function buildLiquidity(parameters) {
29
- const { type, user, contract, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
30
- if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
31
- throw new Error(`CallbackType not implemented: ${type}`);
32
- const amountStr = amount.toString();
33
- const id = `${user}-${chainId.toString()}-${type}-${contract}`.toLowerCase();
34
- return {
35
- userPosition: {
36
- id,
37
- availableLiquidityQueueId: id,
38
- user: user.toLowerCase(),
39
- chainId,
40
- amount: amountStr,
41
- updatedAt
42
- },
43
- queues: [
44
- {
45
- queue: {
46
- queueId: id,
47
- availableLiquidityPoolId: id,
48
- index,
41
+ switch (parameters.type) {
42
+ case "buy_with_empty_callback" /* BuyWithEmptyCallback */: {
43
+ const { user, loanToken, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
44
+ const amountStr = amount.toString();
45
+ const id = `${user}-${chainId.toString()}-${parameters.type}-${loanToken}`.toLowerCase();
46
+ const poolId = `${user}-${chainId.toString()}-${loanToken}`.toLowerCase();
47
+ return {
48
+ userPosition: {
49
+ id,
50
+ availableLiquidityQueueId: id,
51
+ user: user.toLowerCase(),
52
+ chainId,
53
+ amount: amountStr,
49
54
  updatedAt
50
55
  },
51
- pool: {
56
+ queues: [
57
+ {
58
+ queue: {
59
+ queueId: id,
60
+ availableLiquidityPoolId: poolId,
61
+ index,
62
+ callbackAmount: "0",
63
+ updatedAt
64
+ },
65
+ pool: {
66
+ id: poolId,
67
+ amount: amountStr,
68
+ updatedAt
69
+ }
70
+ }
71
+ ]
72
+ };
73
+ }
74
+ case "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */: {
75
+ const {
76
+ user,
77
+ termId,
78
+ chainId,
79
+ amount,
80
+ collaterals,
81
+ index = 0,
82
+ updatedAt = /* @__PURE__ */ new Date()
83
+ } = parameters;
84
+ const amountStr = amount.toString();
85
+ const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}`.toLowerCase();
86
+ return {
87
+ userPosition: {
52
88
  id,
89
+ availableLiquidityQueueId: id,
90
+ user: user.toLowerCase(),
91
+ chainId,
53
92
  amount: amountStr,
54
93
  updatedAt
55
- }
56
- }
57
- ]
58
- };
94
+ },
95
+ queues: collaterals.map((collateral) => {
96
+ const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
97
+ return {
98
+ queue: {
99
+ queueId: id,
100
+ availableLiquidityPoolId: poolId,
101
+ index,
102
+ callbackAmount: collateral.callbackAmount.toString(),
103
+ updatedAt
104
+ },
105
+ pool: {
106
+ id: poolId,
107
+ amount: collateral.balance.toString(),
108
+ updatedAt
109
+ }
110
+ };
111
+ })
112
+ };
113
+ }
114
+ default: {
115
+ throw new Error(`CallbackType not implemented`);
116
+ }
117
+ }
59
118
  }
60
119
  function getCallbackIdForOffer(offer) {
61
120
  if (offer.buy && offer.callback.data === "0x") {
62
- const type = "buy_with_empty_callback" /* BuyWithEmptyCallback */;
63
- const user = offer.offering;
64
- const loanToken = offer.loanToken;
65
- return `${user}-${offer.chainId.toString()}-${type}-${loanToken}`.toLowerCase();
121
+ return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
122
+ }
123
+ if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].includes(
124
+ offer.callback.address.toLowerCase()
125
+ )) {
126
+ return `${offer.offering}-${offer.chainId.toString()}-${"sell_withdraw_from_wallet" /* SellWithdrawFromWallet */}-${mempool.Offer.termId(offer)}`.toLowerCase();
66
127
  }
67
128
  return null;
68
129
  }
130
+ function decodeSellWithdrawFromWalletData(data) {
131
+ if (!data || data === "0x") throw new Error("Empty callback data");
132
+ try {
133
+ const [collaterals, amounts] = viem.decodeAbiParameters(
134
+ [{ type: "address[]" }, { type: "uint256[]" }],
135
+ data
136
+ );
137
+ if (collaterals.length !== amounts.length) {
138
+ throw new Error("Mismatched array lengths");
139
+ }
140
+ return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
141
+ } catch (_) {
142
+ throw new Error("Invalid SellWithdrawFromWallet callback data");
143
+ }
144
+ }
145
+ function decode(parameters) {
146
+ const { type, data } = parameters;
147
+ if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
148
+ return decodeSellWithdrawFromWalletData(data);
149
+ }
150
+ throw new Error(`CallbackType not implemented: ${type}`);
151
+ }
152
+ function encode(parameters) {
153
+ const { type, data } = parameters;
154
+ if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
155
+ return viem.encodeAbiParameters(
156
+ [{ type: "address[]" }, { type: "uint256[]" }],
157
+ [data.collaterals, data.amounts]
158
+ );
159
+ }
160
+ throw new Error(`CallbackType not implemented: ${type}`);
161
+ }
69
162
 
70
163
  // src/core/Cursor.ts
71
164
  var Cursor_exports = {};
72
165
  __export(Cursor_exports, {
73
- decode: () => decode,
74
- encode: () => encode,
166
+ decode: () => decode2,
167
+ encode: () => encode2,
75
168
  validate: () => validate
76
169
  });
77
170
  function validate(cursor) {
@@ -143,10 +236,10 @@ function validate(cursor) {
143
236
  }
144
237
  return true;
145
238
  }
146
- function encode(c) {
239
+ function encode2(c) {
147
240
  return jsBase64.Base64.encodeURL(JSON.stringify(c));
148
241
  }
149
- function decode(token) {
242
+ function decode2(token) {
150
243
  if (!token) return null;
151
244
  const decoded = JSON.parse(jsBase64.Base64.decode(token));
152
245
  validate(decoded);
@@ -226,20 +319,20 @@ async function fetch2(parameters) {
226
319
  const map = await fetchBalancesAndAllowances({
227
320
  client,
228
321
  spender,
229
- pairs: pairs.map(({ user, contract }) => ({ user, token: contract })),
322
+ pairs: pairs.map(({ user, loanToken }) => ({ user, token: loanToken })),
230
323
  options
231
324
  });
232
325
  const out = [];
233
- for (const [user, perContract] of map) {
234
- for (const [contract, { balance, allowance }] of perContract) {
326
+ for (const [user, perLoanToken] of map) {
327
+ for (const [loanToken, { balance, allowance }] of perLoanToken) {
235
328
  const amount = balance < allowance ? balance : allowance;
236
329
  out.push(
237
330
  buildLiquidity({
238
331
  type,
239
332
  user,
240
- contract,
333
+ loanToken,
241
334
  chainId,
242
- amount: amount.toString(),
335
+ amount,
243
336
  index: 0
244
337
  })
245
338
  );
@@ -555,7 +648,7 @@ var GetOffersQueryParams = v4.z.object({
555
648
  (val) => {
556
649
  if (!val) return true;
557
650
  try {
558
- const decoded = decode(val);
651
+ const decoded = decode2(val);
559
652
  return decoded !== null;
560
653
  } catch (_error) {
561
654
  return false;
@@ -727,7 +820,7 @@ var MatchOffersQueryParams = v4.z.object({
727
820
  (val) => {
728
821
  if (!val) return true;
729
822
  try {
730
- const decoded = decode(val);
823
+ const decoded = decode2(val);
731
824
  return decoded !== null;
732
825
  } catch (_error) {
733
826
  return false;
@@ -1203,18 +1296,89 @@ function morpho() {
1203
1296
  return { message: "Expiry mismatch" };
1204
1297
  }
1205
1298
  });
1206
- const callback = single("empty_callback", (offer, _) => {
1207
- if (!offer.buy || offer.callback.data !== "0x") {
1208
- return { message: "Callback not supported yet." };
1299
+ const sellEmptyCallback = single(
1300
+ "sell_offers_empty_callback",
1301
+ (offer, _) => {
1302
+ if (!offer.buy && offer.callback.data === "0x") {
1303
+ return { message: "Sell offers require a non-empty callback." };
1304
+ }
1209
1305
  }
1210
- });
1306
+ );
1307
+ const buyNonEmptyCallback = single(
1308
+ "buy_offers_non_empty_callback",
1309
+ (offer, _) => {
1310
+ if (offer.buy && offer.callback.data !== "0x") {
1311
+ return { message: "Buy offers must use an empty callback." };
1312
+ }
1313
+ }
1314
+ );
1315
+ const sellNonWhitelistedCallback = single(
1316
+ "sell_offers_non_whitelisted_callback",
1317
+ (offer, _) => {
1318
+ if (!offer.buy && offer.callback.data !== "0x") {
1319
+ const allowed = new Set(
1320
+ WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].map(
1321
+ (a) => a.toLowerCase()
1322
+ )
1323
+ );
1324
+ const callbackAddress = offer.callback.address?.toLowerCase();
1325
+ if (!callbackAddress || !allowed.has(callbackAddress)) {
1326
+ return { message: "Sell offer callback address is not whitelisted." };
1327
+ }
1328
+ }
1329
+ }
1330
+ );
1331
+ const sellCallbackDataInvalid = single(
1332
+ "sell_offers_callback_data_invalid",
1333
+ (offer, _) => {
1334
+ if (!offer.buy && offer.callback.data !== "0x") {
1335
+ try {
1336
+ const decoded = decode({
1337
+ type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
1338
+ data: offer.callback.data
1339
+ });
1340
+ if (decoded.length === 0) {
1341
+ return { message: "Sell offer callback data must include at least one collateral." };
1342
+ }
1343
+ } catch (_2) {
1344
+ return { message: "Sell offer callback data cannot be decoded." };
1345
+ }
1346
+ }
1347
+ }
1348
+ );
1349
+ const sellCallbackCollateralInvalid = single(
1350
+ "sell_offers_callback_collateral_invalid",
1351
+ (offer, _) => {
1352
+ if (!offer.buy && offer.callback.data !== "0x") {
1353
+ try {
1354
+ const decoded = decode({
1355
+ type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
1356
+ data: offer.callback.data
1357
+ });
1358
+ const offerCollaterals = new Set(
1359
+ offer.collaterals.map((c) => c.asset.toLowerCase())
1360
+ );
1361
+ for (const { collateral } of decoded) {
1362
+ if (!offerCollaterals.has(collateral.toLowerCase())) {
1363
+ return { message: "Sell callback collateral is not part of offer collaterals." };
1364
+ }
1365
+ }
1366
+ } catch (_2) {
1367
+ }
1368
+ }
1369
+ }
1370
+ );
1211
1371
  return [
1212
1372
  chainId,
1213
1373
  loanToken,
1214
1374
  expiry,
1215
- // note: callback rule should be the last one, since it does not mean that the offer is forever invalid
1375
+ // note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
1216
1376
  // integrators should be able to choose if they want to keep the offer or not
1217
- callback
1377
+ sellEmptyCallback,
1378
+ buyNonEmptyCallback,
1379
+ sellNonWhitelistedCallback,
1380
+ sellCallbackDataInvalid,
1381
+ sellCallbackCollateralInvalid
1218
1382
  ];
1219
1383
  }
1220
1384