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