@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.
- package/dist/drizzle/offers_v1.1/0006_add-callback-amount-to-queues-table.sql +1 -0
- package/dist/drizzle/offers_v1.1/0007_add-index-to-created-at.sql +2 -0
- package/dist/drizzle/offers_v1.1/meta/0006_snapshot.json +884 -0
- package/dist/drizzle/offers_v1.1/meta/0007_snapshot.json +932 -0
- package/dist/drizzle/offers_v1.1/meta/_journal.json +14 -0
- package/dist/index.browser.d.cts +140 -12
- package/dist/index.browser.d.ts +140 -12
- package/dist/index.browser.js +211 -47
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +212 -48
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +158 -12
- package/dist/index.node.d.ts +158 -12
- package/dist/index.node.js +347 -121
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +349 -123
- package/dist/index.node.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.node.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { Errors, LLTV, Offer, Format, Utils, Chain, Maturity, Time, Mempool } fr
|
|
|
2
2
|
export * from '@morpho-dev/mempool';
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
4
|
import { createDocument } from 'zod-openapi';
|
|
5
|
-
import { parseUnits, maxUint256, publicActions, parseEventLogs, formatUnits, createWalletClient, http, erc20Abi, stringify } from 'viem';
|
|
5
|
+
import { parseUnits, maxUint256, encodeAbiParameters, publicActions, parseEventLogs, formatUnits, createWalletClient, http, decodeAbiParameters, erc20Abi, stringify } from 'viem';
|
|
6
6
|
import { Base64 } from 'js-base64';
|
|
7
7
|
import { getBlockNumber } from 'viem/actions';
|
|
8
8
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
9
|
-
import { desc, and, eq, sql, gte, lte,
|
|
9
|
+
import { asc, desc, and, eq, sql, gte, lte, inArray } from 'drizzle-orm';
|
|
10
10
|
import { pgSchema, timestamp, varchar, bigint, text, boolean, integer, numeric, index, serial, jsonb, uniqueIndex, primaryKey } from 'drizzle-orm/pg-core';
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import { PGlite } from '@electric-sql/pglite';
|
|
@@ -719,54 +719,147 @@ function fromResponse(offerResponse) {
|
|
|
719
719
|
var Callback_exports = {};
|
|
720
720
|
__export(Callback_exports, {
|
|
721
721
|
CallbackType: () => CallbackType,
|
|
722
|
+
WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
|
|
722
723
|
buildLiquidity: () => buildLiquidity,
|
|
724
|
+
decode: () => decode2,
|
|
725
|
+
encode: () => encode2,
|
|
723
726
|
getCallbackIdForOffer: () => getCallbackIdForOffer
|
|
724
727
|
});
|
|
725
728
|
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
726
729
|
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
730
|
+
CallbackType2["SellWithdrawFromWallet"] = "sell_withdraw_from_wallet";
|
|
727
731
|
return CallbackType2;
|
|
728
732
|
})(CallbackType || {});
|
|
733
|
+
var WhitelistedCallbackAddresses = {
|
|
734
|
+
["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
|
|
735
|
+
["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */]: [
|
|
736
|
+
"0x1111111111111111111111111111111111111111",
|
|
737
|
+
"0x2222222222222222222222222222222222222222"
|
|
738
|
+
// @TODO: update once deployed and add mapping per chain if needed
|
|
739
|
+
].map((address) => address.toLowerCase())
|
|
740
|
+
};
|
|
729
741
|
function buildLiquidity(parameters) {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
},
|
|
744
|
-
queues: [
|
|
745
|
-
{
|
|
746
|
-
queue: {
|
|
747
|
-
queueId: id,
|
|
748
|
-
availableLiquidityPoolId: id,
|
|
749
|
-
index: index2,
|
|
742
|
+
switch (parameters.type) {
|
|
743
|
+
case "buy_with_empty_callback" /* BuyWithEmptyCallback */: {
|
|
744
|
+
const { user, loanToken, chainId, amount, index: index2 = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
745
|
+
const amountStr = amount.toString();
|
|
746
|
+
const id = `${user}-${chainId.toString()}-${parameters.type}-${loanToken}`.toLowerCase();
|
|
747
|
+
const poolId = `${user}-${chainId.toString()}-${loanToken}`.toLowerCase();
|
|
748
|
+
return {
|
|
749
|
+
userPosition: {
|
|
750
|
+
id,
|
|
751
|
+
availableLiquidityQueueId: id,
|
|
752
|
+
user: user.toLowerCase(),
|
|
753
|
+
chainId,
|
|
754
|
+
amount: amountStr,
|
|
750
755
|
updatedAt
|
|
751
756
|
},
|
|
752
|
-
|
|
757
|
+
queues: [
|
|
758
|
+
{
|
|
759
|
+
queue: {
|
|
760
|
+
queueId: id,
|
|
761
|
+
availableLiquidityPoolId: poolId,
|
|
762
|
+
index: index2,
|
|
763
|
+
callbackAmount: "0",
|
|
764
|
+
updatedAt
|
|
765
|
+
},
|
|
766
|
+
pool: {
|
|
767
|
+
id: poolId,
|
|
768
|
+
amount: amountStr,
|
|
769
|
+
updatedAt
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
]
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
case "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */: {
|
|
776
|
+
const {
|
|
777
|
+
user,
|
|
778
|
+
termId,
|
|
779
|
+
chainId,
|
|
780
|
+
amount,
|
|
781
|
+
collaterals,
|
|
782
|
+
index: index2 = 0,
|
|
783
|
+
updatedAt = /* @__PURE__ */ new Date()
|
|
784
|
+
} = parameters;
|
|
785
|
+
const amountStr = amount.toString();
|
|
786
|
+
const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}`.toLowerCase();
|
|
787
|
+
return {
|
|
788
|
+
userPosition: {
|
|
753
789
|
id,
|
|
790
|
+
availableLiquidityQueueId: id,
|
|
791
|
+
user: user.toLowerCase(),
|
|
792
|
+
chainId,
|
|
754
793
|
amount: amountStr,
|
|
755
794
|
updatedAt
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
795
|
+
},
|
|
796
|
+
queues: collaterals.map((collateral) => {
|
|
797
|
+
const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
|
|
798
|
+
return {
|
|
799
|
+
queue: {
|
|
800
|
+
queueId: id,
|
|
801
|
+
availableLiquidityPoolId: poolId,
|
|
802
|
+
index: index2,
|
|
803
|
+
callbackAmount: collateral.callbackAmount.toString(),
|
|
804
|
+
updatedAt
|
|
805
|
+
},
|
|
806
|
+
pool: {
|
|
807
|
+
id: poolId,
|
|
808
|
+
amount: collateral.balance.toString(),
|
|
809
|
+
updatedAt
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
})
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
default: {
|
|
816
|
+
throw new Error(`CallbackType not implemented`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
760
819
|
}
|
|
761
820
|
function getCallbackIdForOffer(offer) {
|
|
762
821
|
if (offer.buy && offer.callback.data === "0x") {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
822
|
+
return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
|
|
823
|
+
}
|
|
824
|
+
if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].includes(
|
|
825
|
+
offer.callback.address.toLowerCase()
|
|
826
|
+
)) {
|
|
827
|
+
return `${offer.offering}-${offer.chainId.toString()}-${"sell_withdraw_from_wallet" /* SellWithdrawFromWallet */}-${Offer.termId(offer)}`.toLowerCase();
|
|
767
828
|
}
|
|
768
829
|
return null;
|
|
769
830
|
}
|
|
831
|
+
function decodeSellWithdrawFromWalletData(data) {
|
|
832
|
+
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
833
|
+
try {
|
|
834
|
+
const [collaterals, amounts] = decodeAbiParameters(
|
|
835
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
836
|
+
data
|
|
837
|
+
);
|
|
838
|
+
if (collaterals.length !== amounts.length) {
|
|
839
|
+
throw new Error("Mismatched array lengths");
|
|
840
|
+
}
|
|
841
|
+
return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
|
|
842
|
+
} catch (_) {
|
|
843
|
+
throw new Error("Invalid SellWithdrawFromWallet callback data");
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
function decode2(parameters) {
|
|
847
|
+
const { type, data } = parameters;
|
|
848
|
+
if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
|
|
849
|
+
return decodeSellWithdrawFromWalletData(data);
|
|
850
|
+
}
|
|
851
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
852
|
+
}
|
|
853
|
+
function encode2(parameters) {
|
|
854
|
+
const { type, data } = parameters;
|
|
855
|
+
if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
|
|
856
|
+
return encodeAbiParameters(
|
|
857
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
858
|
+
[data.collaterals, data.amounts]
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
862
|
+
}
|
|
770
863
|
|
|
771
864
|
// src/core/Collector/index.ts
|
|
772
865
|
var Collector_exports = {};
|
|
@@ -851,20 +944,20 @@ async function fetch2(parameters) {
|
|
|
851
944
|
const map = await fetchBalancesAndAllowances({
|
|
852
945
|
client,
|
|
853
946
|
spender,
|
|
854
|
-
pairs: pairs.map(({ user,
|
|
947
|
+
pairs: pairs.map(({ user, loanToken }) => ({ user, token: loanToken })),
|
|
855
948
|
options
|
|
856
949
|
});
|
|
857
950
|
const out = [];
|
|
858
|
-
for (const [user,
|
|
859
|
-
for (const [
|
|
951
|
+
for (const [user, perLoanToken] of map) {
|
|
952
|
+
for (const [loanToken, { balance, allowance }] of perLoanToken) {
|
|
860
953
|
const amount = balance < allowance ? balance : allowance;
|
|
861
954
|
out.push(
|
|
862
955
|
buildLiquidity({
|
|
863
956
|
type,
|
|
864
957
|
user,
|
|
865
|
-
|
|
958
|
+
loanToken,
|
|
866
959
|
chainId,
|
|
867
|
-
amount
|
|
960
|
+
amount,
|
|
868
961
|
index: 0
|
|
869
962
|
})
|
|
870
963
|
);
|
|
@@ -922,16 +1015,16 @@ __export(Logger_exports, {
|
|
|
922
1015
|
silentLogger: () => silentLogger
|
|
923
1016
|
});
|
|
924
1017
|
var LogLevelValues = [
|
|
925
|
-
"silent",
|
|
926
1018
|
"trace",
|
|
927
1019
|
"debug",
|
|
928
1020
|
"info",
|
|
929
1021
|
"warn",
|
|
930
1022
|
"error",
|
|
931
|
-
"fatal"
|
|
1023
|
+
"fatal",
|
|
1024
|
+
"silent"
|
|
932
1025
|
];
|
|
933
1026
|
function defaultLogger(minLevel) {
|
|
934
|
-
const threshold = minLevel ?? "info";
|
|
1027
|
+
const threshold = minLevel ?? process.env.ROUTER_LOG_LEVEL ?? "info";
|
|
935
1028
|
const levelIndexByName = LogLevelValues.reduce(
|
|
936
1029
|
(acc, lvl, idx) => {
|
|
937
1030
|
acc[lvl] = idx;
|
|
@@ -1027,7 +1120,7 @@ function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
|
1027
1120
|
chainId: chain.id,
|
|
1028
1121
|
spender: chain.morpho,
|
|
1029
1122
|
type: "buy_with_empty_callback" /* BuyWithEmptyCallback */,
|
|
1030
|
-
pairs: pairs.map(({ user, token }) => ({ user,
|
|
1123
|
+
pairs: pairs.map(({ user, token }) => ({ user, loanToken: token })),
|
|
1031
1124
|
options: {
|
|
1032
1125
|
blockNumber: latestBlockNumber,
|
|
1033
1126
|
batchSize: maxBatchSize
|
|
@@ -1078,14 +1171,15 @@ function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
|
1078
1171
|
};
|
|
1079
1172
|
}
|
|
1080
1173
|
function createChainReorgsCollector(parameters) {
|
|
1081
|
-
const
|
|
1174
|
+
const collector = "chain_reorgs";
|
|
1082
1175
|
const {
|
|
1083
1176
|
client,
|
|
1084
1177
|
subscribers,
|
|
1085
1178
|
collectorStore,
|
|
1086
1179
|
chain,
|
|
1087
|
-
options: { maxBatchSize = 25, interval } = {}
|
|
1180
|
+
options: { maxBatchSize = 25, interval = 3e4, maxBlockNumber } = {}
|
|
1088
1181
|
} = parameters;
|
|
1182
|
+
const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
|
|
1089
1183
|
let finalizedBlock = null;
|
|
1090
1184
|
let unfinalizedBlocks = [];
|
|
1091
1185
|
const commonAncestor = (block) => {
|
|
@@ -1094,67 +1188,79 @@ function createChainReorgsCollector(parameters) {
|
|
|
1094
1188
|
if (finalizedBlock == null) throw new Error("Failed to get common ancestor");
|
|
1095
1189
|
return finalizedBlock;
|
|
1096
1190
|
};
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
unfinalizedBlocks.
|
|
1191
|
+
const collect = Utils.lazy((emit, { stop }) => {
|
|
1192
|
+
const logger = getLogger();
|
|
1193
|
+
const reconcile = async (block) => {
|
|
1194
|
+
if (block.hash === null || block.number === null || block.parentHash === null)
|
|
1195
|
+
throw new Error("Failed to get block");
|
|
1196
|
+
const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
|
|
1197
|
+
if (latestBlock === void 0) {
|
|
1198
|
+
const newBlock2 = {
|
|
1199
|
+
hash: block.hash,
|
|
1200
|
+
number: block.number,
|
|
1201
|
+
parentHash: block.parentHash
|
|
1202
|
+
};
|
|
1203
|
+
unfinalizedBlocks.push(newBlock2);
|
|
1204
|
+
return newBlock2;
|
|
1205
|
+
}
|
|
1206
|
+
if (latestBlock.hash === block.hash) return latestBlock;
|
|
1207
|
+
if (latestBlock.number >= block.number) {
|
|
1208
|
+
const ancestor = commonAncestor(block);
|
|
1209
|
+
logger.info({
|
|
1210
|
+
collector,
|
|
1211
|
+
chainId: chain.id,
|
|
1212
|
+
msg: `reorg detected, latestBlock.number: ${latestBlock.number} >= block.number: ${block.number} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1213
|
+
});
|
|
1214
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1215
|
+
unfinalizedBlocks = [];
|
|
1216
|
+
return ancestor;
|
|
1217
|
+
}
|
|
1218
|
+
if (latestBlock.number + 1n < block.number) {
|
|
1219
|
+
logger.debug({
|
|
1220
|
+
collector,
|
|
1221
|
+
chainId: chain.id,
|
|
1222
|
+
msg: `missing blocks between block ${latestBlock.number} and block ${block.number} on chain ${chain.id}`
|
|
1223
|
+
});
|
|
1224
|
+
const missingBlockNumbers = (() => {
|
|
1225
|
+
const missingBlockNumbers2 = [];
|
|
1226
|
+
let start2 = latestBlock.number + 1n;
|
|
1227
|
+
const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
|
|
1228
|
+
while (start2 < threshold) {
|
|
1229
|
+
missingBlockNumbers2.push(start2);
|
|
1230
|
+
start2 = start2 + 1n;
|
|
1231
|
+
}
|
|
1232
|
+
return missingBlockNumbers2;
|
|
1233
|
+
})();
|
|
1234
|
+
const missingBlocks = await Promise.all(
|
|
1235
|
+
missingBlockNumbers.map(
|
|
1236
|
+
(blockNumber) => client.getBlock({ blockNumber, includeTransactions: false })
|
|
1237
|
+
)
|
|
1238
|
+
);
|
|
1239
|
+
for (const missingBlock of missingBlocks) {
|
|
1240
|
+
const returnedBlock = await reconcile(missingBlock);
|
|
1241
|
+
if (returnedBlock.number !== missingBlock.number) return returnedBlock;
|
|
1242
|
+
}
|
|
1243
|
+
return reconcile(block);
|
|
1244
|
+
}
|
|
1245
|
+
if (block.parentHash !== latestBlock.hash) {
|
|
1246
|
+
const ancestor = commonAncestor(block);
|
|
1247
|
+
logger.info({
|
|
1248
|
+
collector,
|
|
1249
|
+
chainId: chain.id,
|
|
1250
|
+
msg: `reorg detected, block.parentHash: ${block.parentHash} !== latestBlock.hash: ${latestBlock.hash} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1251
|
+
});
|
|
1252
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1253
|
+
unfinalizedBlocks = [];
|
|
1254
|
+
return ancestor;
|
|
1255
|
+
}
|
|
1256
|
+
const newBlock = {
|
|
1103
1257
|
hash: block.hash,
|
|
1104
1258
|
number: block.number,
|
|
1105
1259
|
parentHash: block.parentHash
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
if (latestBlock.number >= block.number) {
|
|
1111
|
-
const ancestor = commonAncestor(block);
|
|
1112
|
-
console.log(
|
|
1113
|
-
`reorg detected, latestBlock.number: ${latestBlock.number} > block.number: ${block.number} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1114
|
-
);
|
|
1115
|
-
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1116
|
-
unfinalizedBlocks = [];
|
|
1117
|
-
return;
|
|
1118
|
-
}
|
|
1119
|
-
if (latestBlock.number + 1n < block.number) {
|
|
1120
|
-
console.log(
|
|
1121
|
-
`missing blocks between block ${latestBlock.number} and block ${block.number} on chain ${chain.id}`
|
|
1122
|
-
);
|
|
1123
|
-
const missingBlockNumbers = (() => {
|
|
1124
|
-
const missingBlockNumbers2 = [];
|
|
1125
|
-
let start2 = latestBlock.number + 1n;
|
|
1126
|
-
const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
|
|
1127
|
-
while (start2 < threshold) {
|
|
1128
|
-
missingBlockNumbers2.push(start2);
|
|
1129
|
-
start2 = start2 + 1n;
|
|
1130
|
-
}
|
|
1131
|
-
return missingBlockNumbers2;
|
|
1132
|
-
})();
|
|
1133
|
-
const missingBlocks = await Promise.all(
|
|
1134
|
-
missingBlockNumbers.map(
|
|
1135
|
-
(blockNumber) => client.getBlock({ blockNumber, includeTransactions: false })
|
|
1136
|
-
)
|
|
1137
|
-
);
|
|
1138
|
-
for (const missingBlock of missingBlocks) await reconcile(missingBlock);
|
|
1139
|
-
await reconcile(block);
|
|
1140
|
-
return;
|
|
1141
|
-
}
|
|
1142
|
-
if (block.parentHash !== latestBlock.hash) {
|
|
1143
|
-
const ancestor = commonAncestor(block);
|
|
1144
|
-
console.log(
|
|
1145
|
-
`reorg detected, block.parentHash: ${block.parentHash} !== latestBlock.hash: ${latestBlock.hash} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1146
|
-
);
|
|
1147
|
-
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1148
|
-
unfinalizedBlocks = [];
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
|
-
unfinalizedBlocks.push({
|
|
1152
|
-
hash: block.hash,
|
|
1153
|
-
number: block.number,
|
|
1154
|
-
parentHash: block.parentHash
|
|
1155
|
-
});
|
|
1156
|
-
};
|
|
1157
|
-
const collect = Utils.lazy((emit) => {
|
|
1260
|
+
};
|
|
1261
|
+
unfinalizedBlocks.push(newBlock);
|
|
1262
|
+
return newBlock;
|
|
1263
|
+
};
|
|
1158
1264
|
let tick = 0;
|
|
1159
1265
|
const fetchFinalizedBlock = async () => {
|
|
1160
1266
|
if (tick % 20 === 0 || finalizedBlock === null) {
|
|
@@ -1162,32 +1268,58 @@ function createChainReorgsCollector(parameters) {
|
|
|
1162
1268
|
blockTag: "finalized",
|
|
1163
1269
|
includeTransactions: false
|
|
1164
1270
|
});
|
|
1165
|
-
if (finalizedBlock === null)
|
|
1271
|
+
if (finalizedBlock === null) {
|
|
1272
|
+
const msg = "Failed to get finalized block";
|
|
1273
|
+
logger.fatal({ collector, chainId: chain.id, msg });
|
|
1274
|
+
throw new Error(msg);
|
|
1275
|
+
}
|
|
1276
|
+
unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number >= finalizedBlock.number);
|
|
1166
1277
|
}
|
|
1167
1278
|
tick++;
|
|
1168
1279
|
};
|
|
1169
|
-
|
|
1280
|
+
let isMaxBlockNumberReached = false;
|
|
1281
|
+
const unpoll = Utils.poll(
|
|
1170
1282
|
async () => {
|
|
1171
|
-
|
|
1172
|
-
|
|
1283
|
+
if (isMaxBlockNumberReached) {
|
|
1284
|
+
stop();
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const head = await client.getBlock({
|
|
1173
1288
|
blockTag: "latest",
|
|
1174
1289
|
includeTransactions: false
|
|
1175
1290
|
});
|
|
1176
|
-
|
|
1177
|
-
|
|
1291
|
+
if (maxBlockNumberBI !== void 0 && head.number >= maxBlockNumberBI) {
|
|
1292
|
+
logger.info({
|
|
1293
|
+
collector,
|
|
1294
|
+
chainId: chain.id,
|
|
1295
|
+
msg: `head is greater than max block number, head.number: ${head.number} > maxBlockNumber: ${maxBlockNumber} on chain ${chain.id}.`
|
|
1296
|
+
});
|
|
1297
|
+
isMaxBlockNumberReached = true;
|
|
1298
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(maxBlockNumber));
|
|
1299
|
+
await collectorStore.saveBlockNumber({
|
|
1300
|
+
collectorName: collector,
|
|
1301
|
+
chainId: chain.id,
|
|
1302
|
+
blockNumber: maxBlockNumber
|
|
1303
|
+
});
|
|
1304
|
+
emit(maxBlockNumber);
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
await fetchFinalizedBlock();
|
|
1308
|
+
const blockNumber = Number((await reconcile(head)).number);
|
|
1178
1309
|
await collectorStore.saveBlockNumber({
|
|
1179
|
-
collectorName:
|
|
1310
|
+
collectorName: collector,
|
|
1180
1311
|
chainId: chain.id,
|
|
1181
|
-
blockNumber
|
|
1312
|
+
blockNumber
|
|
1182
1313
|
});
|
|
1183
|
-
emit(
|
|
1314
|
+
emit(blockNumber);
|
|
1184
1315
|
},
|
|
1185
1316
|
{ interval: interval ?? 3e4 }
|
|
1186
1317
|
);
|
|
1318
|
+
return unpoll;
|
|
1187
1319
|
});
|
|
1188
1320
|
return {
|
|
1189
|
-
name:
|
|
1190
|
-
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
1321
|
+
name: collector,
|
|
1322
|
+
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName: collector, chainId: chain.id }),
|
|
1191
1323
|
collect,
|
|
1192
1324
|
onReorg: (_) => {
|
|
1193
1325
|
}
|
|
@@ -1386,18 +1518,89 @@ function morpho() {
|
|
|
1386
1518
|
return { message: "Expiry mismatch" };
|
|
1387
1519
|
}
|
|
1388
1520
|
});
|
|
1389
|
-
const
|
|
1390
|
-
|
|
1391
|
-
|
|
1521
|
+
const sellEmptyCallback = single(
|
|
1522
|
+
"sell_offers_empty_callback",
|
|
1523
|
+
(offer, _) => {
|
|
1524
|
+
if (!offer.buy && offer.callback.data === "0x") {
|
|
1525
|
+
return { message: "Sell offers require a non-empty callback." };
|
|
1526
|
+
}
|
|
1392
1527
|
}
|
|
1393
|
-
|
|
1528
|
+
);
|
|
1529
|
+
const buyNonEmptyCallback = single(
|
|
1530
|
+
"buy_offers_non_empty_callback",
|
|
1531
|
+
(offer, _) => {
|
|
1532
|
+
if (offer.buy && offer.callback.data !== "0x") {
|
|
1533
|
+
return { message: "Buy offers must use an empty callback." };
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
const sellNonWhitelistedCallback = single(
|
|
1538
|
+
"sell_offers_non_whitelisted_callback",
|
|
1539
|
+
(offer, _) => {
|
|
1540
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1541
|
+
const allowed = new Set(
|
|
1542
|
+
WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].map(
|
|
1543
|
+
(a) => a.toLowerCase()
|
|
1544
|
+
)
|
|
1545
|
+
);
|
|
1546
|
+
const callbackAddress = offer.callback.address?.toLowerCase();
|
|
1547
|
+
if (!callbackAddress || !allowed.has(callbackAddress)) {
|
|
1548
|
+
return { message: "Sell offer callback address is not whitelisted." };
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
);
|
|
1553
|
+
const sellCallbackDataInvalid = single(
|
|
1554
|
+
"sell_offers_callback_data_invalid",
|
|
1555
|
+
(offer, _) => {
|
|
1556
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1557
|
+
try {
|
|
1558
|
+
const decoded = decode2({
|
|
1559
|
+
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1560
|
+
data: offer.callback.data
|
|
1561
|
+
});
|
|
1562
|
+
if (decoded.length === 0) {
|
|
1563
|
+
return { message: "Sell offer callback data must include at least one collateral." };
|
|
1564
|
+
}
|
|
1565
|
+
} catch (_2) {
|
|
1566
|
+
return { message: "Sell offer callback data cannot be decoded." };
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
);
|
|
1571
|
+
const sellCallbackCollateralInvalid = single(
|
|
1572
|
+
"sell_offers_callback_collateral_invalid",
|
|
1573
|
+
(offer, _) => {
|
|
1574
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1575
|
+
try {
|
|
1576
|
+
const decoded = decode2({
|
|
1577
|
+
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1578
|
+
data: offer.callback.data
|
|
1579
|
+
});
|
|
1580
|
+
const offerCollaterals2 = new Set(
|
|
1581
|
+
offer.collaterals.map((c) => c.asset.toLowerCase())
|
|
1582
|
+
);
|
|
1583
|
+
for (const { collateral } of decoded) {
|
|
1584
|
+
if (!offerCollaterals2.has(collateral.toLowerCase())) {
|
|
1585
|
+
return { message: "Sell callback collateral is not part of offer collaterals." };
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
} catch (_2) {
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
);
|
|
1394
1593
|
return [
|
|
1395
1594
|
chainId,
|
|
1396
1595
|
loanToken,
|
|
1397
1596
|
expiry,
|
|
1398
|
-
// note: callback
|
|
1597
|
+
// note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
|
|
1399
1598
|
// integrators should be able to choose if they want to keep the offer or not
|
|
1400
|
-
|
|
1599
|
+
sellEmptyCallback,
|
|
1600
|
+
buyNonEmptyCallback,
|
|
1601
|
+
sellNonWhitelistedCallback,
|
|
1602
|
+
sellCallbackDataInvalid,
|
|
1603
|
+
sellCallbackCollateralInvalid
|
|
1401
1604
|
];
|
|
1402
1605
|
}
|
|
1403
1606
|
|
|
@@ -1446,9 +1649,11 @@ function createMempoolCollector(parameters) {
|
|
|
1446
1649
|
});
|
|
1447
1650
|
const invalidOffersToSave = [];
|
|
1448
1651
|
const issueToStatus = {
|
|
1449
|
-
empty_callback: "callback_not_supported",
|
|
1450
1652
|
sell_offers_empty_callback: "callback_not_supported",
|
|
1451
|
-
|
|
1653
|
+
buy_offers_non_empty_callback: "callback_not_supported",
|
|
1654
|
+
sell_offers_non_whitelisted_callback: "callback_not_supported",
|
|
1655
|
+
sell_offers_callback_data_invalid: "callback_error",
|
|
1656
|
+
sell_offers_callback_collateral_invalid: "callback_error"
|
|
1452
1657
|
};
|
|
1453
1658
|
for (const issue of issues) {
|
|
1454
1659
|
const status = issueToStatus[issue.ruleName];
|
|
@@ -1559,11 +1764,19 @@ var offers = s.table(
|
|
|
1559
1764
|
index("offers_expiry_idx").on(table.expiry),
|
|
1560
1765
|
index("offers_rate_idx").on(table.rate),
|
|
1561
1766
|
index("offers_assets_idx").on(table.assets),
|
|
1767
|
+
index("offers_created_at_idx").on(table.createdAt),
|
|
1562
1768
|
// Compound indices for cursor pagination with hash
|
|
1563
1769
|
index("offers_rate_hash_idx").on(table.rate, table.hash),
|
|
1564
1770
|
index("offers_maturity_hash_idx").on(table.maturity, table.hash),
|
|
1565
1771
|
index("offers_expiry_hash_idx").on(table.expiry, table.hash),
|
|
1566
|
-
index("offers_assets_hash_idx").on(table.assets, table.hash)
|
|
1772
|
+
index("offers_assets_hash_idx").on(table.assets, table.hash),
|
|
1773
|
+
// Compound index for multi-level sorting optimization (rate, createdAt, assets, hash)
|
|
1774
|
+
index("offers_rate_created_at_assets_hash_idx").on(
|
|
1775
|
+
table.rate,
|
|
1776
|
+
asc(table.createdAt),
|
|
1777
|
+
desc(table.assets),
|
|
1778
|
+
asc(table.hash)
|
|
1779
|
+
)
|
|
1567
1780
|
]
|
|
1568
1781
|
);
|
|
1569
1782
|
var offerCollaterals = s.table(
|
|
@@ -1637,6 +1850,7 @@ var availableLiquidityQueues = s.table(
|
|
|
1637
1850
|
queueId: varchar("queue_id", { length: 255 }).notNull(),
|
|
1638
1851
|
availableLiquidityPoolId: varchar("available_liquidity_pool_id", { length: 255 }).notNull().references(() => availableLiquidityPools.id, { onDelete: "cascade" }),
|
|
1639
1852
|
index: integer("index").notNull(),
|
|
1853
|
+
callbackAmount: numeric("callback_amount", { precision: 78, scale: 0 }).default("0").notNull(),
|
|
1640
1854
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
1641
1855
|
},
|
|
1642
1856
|
(table) => [
|
|
@@ -1781,6 +1995,7 @@ var create2 = (config) => {
|
|
|
1781
1995
|
queueId: qp.queue.queueId,
|
|
1782
1996
|
availableLiquidityPoolId: qp.pool.id,
|
|
1783
1997
|
index: qp.queue.index,
|
|
1998
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1784
1999
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1785
2000
|
}).onConflictDoUpdate({
|
|
1786
2001
|
target: [
|
|
@@ -1789,6 +2004,7 @@ var create2 = (config) => {
|
|
|
1789
2004
|
],
|
|
1790
2005
|
set: {
|
|
1791
2006
|
index: qp.queue.index,
|
|
2007
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1792
2008
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1793
2009
|
}
|
|
1794
2010
|
});
|
|
@@ -1867,6 +2083,7 @@ function memory2() {
|
|
|
1867
2083
|
queueId: qid,
|
|
1868
2084
|
availableLiquidityPoolId: qp.pool.id,
|
|
1869
2085
|
index: qp.queue.index,
|
|
2086
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1870
2087
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1871
2088
|
});
|
|
1872
2089
|
if (!queueIndexByQueueId.has(qid)) queueIndexByQueueId.set(qid, /* @__PURE__ */ new Set());
|
|
@@ -2380,7 +2597,8 @@ function create3(config) {
|
|
|
2380
2597
|
signature: offers.signature,
|
|
2381
2598
|
callbackId: offers.callbackId,
|
|
2382
2599
|
status: latestStatus.status,
|
|
2383
|
-
metadata: latestStatus.metadata
|
|
2600
|
+
metadata: latestStatus.metadata,
|
|
2601
|
+
createdAt: offers.createdAt
|
|
2384
2602
|
}
|
|
2385
2603
|
).from(offers).leftJoinLateral(latestStatus, sql`true`).leftJoinLateral(sumConsumed, sql`true`).where(
|
|
2386
2604
|
and(
|
|
@@ -2435,6 +2653,7 @@ function create3(config) {
|
|
|
2435
2653
|
callbackId: bestOffers.callbackId,
|
|
2436
2654
|
status: bestOffers.status,
|
|
2437
2655
|
metadata: bestOffers.metadata,
|
|
2656
|
+
createdAt: bestOffers.createdAt,
|
|
2438
2657
|
// liquidity caps
|
|
2439
2658
|
userAmount: sql`COALESCE(${queueLiquidity.userAmount}, 0)`.as("user_amount"),
|
|
2440
2659
|
queueLiquidity: sql`COALESCE(${queueLiquidity.queueLiquidity}, 0)`.as(
|
|
@@ -2444,7 +2663,7 @@ function create3(config) {
|
|
|
2444
2663
|
cumulativeRemaining: sql`
|
|
2445
2664
|
SUM(${bestOffers.remaining}) OVER (
|
|
2446
2665
|
PARTITION BY ${bestOffers.callbackId}
|
|
2447
|
-
ORDER BY ${sortExpr}, ${asc(bestOffers.hash)}
|
|
2666
|
+
ORDER BY ${sortExpr}, ${asc(bestOffers.createdAt)}, ${bestOffers.assets} DESC, ${asc(bestOffers.hash)}
|
|
2448
2667
|
ROWS UNBOUNDED PRECEDING
|
|
2449
2668
|
)
|
|
2450
2669
|
`.as("cumulative_remaining"),
|
|
@@ -2454,7 +2673,7 @@ function create3(config) {
|
|
|
2454
2673
|
WHEN ${bestOffers.remaining} <= 0 THEN false
|
|
2455
2674
|
ELSE ( SUM(${bestOffers.remaining}) OVER (
|
|
2456
2675
|
PARTITION BY ${bestOffers.callbackId}
|
|
2457
|
-
ORDER BY ${sortExpr}, ${asc(bestOffers.hash)}
|
|
2676
|
+
ORDER BY ${sortExpr}, ${asc(bestOffers.createdAt)}, ${bestOffers.assets} DESC, ${asc(bestOffers.hash)}
|
|
2458
2677
|
ROWS UNBOUNDED PRECEDING
|
|
2459
2678
|
)
|
|
2460
2679
|
<= LEAST(
|
|
@@ -2488,6 +2707,7 @@ function create3(config) {
|
|
|
2488
2707
|
callbackId: offersWithEligibility.callbackId,
|
|
2489
2708
|
status: offersWithEligibility.status,
|
|
2490
2709
|
metadata: offersWithEligibility.metadata,
|
|
2710
|
+
createdAt: offersWithEligibility.createdAt,
|
|
2491
2711
|
userAmount: offersWithEligibility.userAmount,
|
|
2492
2712
|
queueLiquidity: offersWithEligibility.queueLiquidity,
|
|
2493
2713
|
cumulativeRemaining: offersWithEligibility.cumulativeRemaining,
|
|
@@ -2497,6 +2717,8 @@ function create3(config) {
|
|
|
2497
2717
|
ROW_NUMBER() OVER (
|
|
2498
2718
|
ORDER BY
|
|
2499
2719
|
CASE WHEN ${offersWithEligibility.buy} THEN ${offersWithEligibility.rate} ELSE -${offersWithEligibility.rate} END,
|
|
2720
|
+
${offersWithEligibility.createdAt},
|
|
2721
|
+
${offersWithEligibility.assets} DESC,
|
|
2500
2722
|
${asc(offersWithEligibility.hash)}
|
|
2501
2723
|
)
|
|
2502
2724
|
`.as("row_number")
|
|
@@ -2538,6 +2760,10 @@ function create3(config) {
|
|
|
2538
2760
|
)
|
|
2539
2761
|
).orderBy(
|
|
2540
2762
|
rateSortDirection === "asc" ? asc(validOffers.rate) : desc(validOffers.rate),
|
|
2763
|
+
asc(validOffers.createdAt),
|
|
2764
|
+
// earlier createdAt first
|
|
2765
|
+
desc(validOffers.assets),
|
|
2766
|
+
// higher assets first
|
|
2541
2767
|
asc(validOffers.hash)
|
|
2542
2768
|
);
|
|
2543
2769
|
const buildOffersMap = (rows, skipHash) => {
|