@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.js
CHANGED
|
@@ -724,54 +724,147 @@ function fromResponse(offerResponse) {
|
|
|
724
724
|
var Callback_exports = {};
|
|
725
725
|
__export(Callback_exports, {
|
|
726
726
|
CallbackType: () => CallbackType,
|
|
727
|
+
WhitelistedCallbackAddresses: () => WhitelistedCallbackAddresses,
|
|
727
728
|
buildLiquidity: () => buildLiquidity,
|
|
729
|
+
decode: () => decode2,
|
|
730
|
+
encode: () => encode2,
|
|
728
731
|
getCallbackIdForOffer: () => getCallbackIdForOffer
|
|
729
732
|
});
|
|
730
733
|
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
731
734
|
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
735
|
+
CallbackType2["SellWithdrawFromWallet"] = "sell_withdraw_from_wallet";
|
|
732
736
|
return CallbackType2;
|
|
733
737
|
})(CallbackType || {});
|
|
738
|
+
var WhitelistedCallbackAddresses = {
|
|
739
|
+
["buy_with_empty_callback" /* BuyWithEmptyCallback */]: [],
|
|
740
|
+
["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */]: [
|
|
741
|
+
"0x1111111111111111111111111111111111111111",
|
|
742
|
+
"0x2222222222222222222222222222222222222222"
|
|
743
|
+
// @TODO: update once deployed and add mapping per chain if needed
|
|
744
|
+
].map((address) => address.toLowerCase())
|
|
745
|
+
};
|
|
734
746
|
function buildLiquidity(parameters) {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
},
|
|
749
|
-
queues: [
|
|
750
|
-
{
|
|
751
|
-
queue: {
|
|
752
|
-
queueId: id,
|
|
753
|
-
availableLiquidityPoolId: id,
|
|
754
|
-
index: index2,
|
|
747
|
+
switch (parameters.type) {
|
|
748
|
+
case "buy_with_empty_callback" /* BuyWithEmptyCallback */: {
|
|
749
|
+
const { user, loanToken, chainId, amount, index: index2 = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
750
|
+
const amountStr = amount.toString();
|
|
751
|
+
const id = `${user}-${chainId.toString()}-${parameters.type}-${loanToken}`.toLowerCase();
|
|
752
|
+
const poolId = `${user}-${chainId.toString()}-${loanToken}`.toLowerCase();
|
|
753
|
+
return {
|
|
754
|
+
userPosition: {
|
|
755
|
+
id,
|
|
756
|
+
availableLiquidityQueueId: id,
|
|
757
|
+
user: user.toLowerCase(),
|
|
758
|
+
chainId,
|
|
759
|
+
amount: amountStr,
|
|
755
760
|
updatedAt
|
|
756
761
|
},
|
|
757
|
-
|
|
762
|
+
queues: [
|
|
763
|
+
{
|
|
764
|
+
queue: {
|
|
765
|
+
queueId: id,
|
|
766
|
+
availableLiquidityPoolId: poolId,
|
|
767
|
+
index: index2,
|
|
768
|
+
callbackAmount: "0",
|
|
769
|
+
updatedAt
|
|
770
|
+
},
|
|
771
|
+
pool: {
|
|
772
|
+
id: poolId,
|
|
773
|
+
amount: amountStr,
|
|
774
|
+
updatedAt
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
]
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
case "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */: {
|
|
781
|
+
const {
|
|
782
|
+
user,
|
|
783
|
+
termId,
|
|
784
|
+
chainId,
|
|
785
|
+
amount,
|
|
786
|
+
collaterals,
|
|
787
|
+
index: index2 = 0,
|
|
788
|
+
updatedAt = /* @__PURE__ */ new Date()
|
|
789
|
+
} = parameters;
|
|
790
|
+
const amountStr = amount.toString();
|
|
791
|
+
const id = `${user}-${chainId.toString()}-${parameters.type}-${termId}`.toLowerCase();
|
|
792
|
+
return {
|
|
793
|
+
userPosition: {
|
|
758
794
|
id,
|
|
795
|
+
availableLiquidityQueueId: id,
|
|
796
|
+
user: user.toLowerCase(),
|
|
797
|
+
chainId,
|
|
759
798
|
amount: amountStr,
|
|
760
799
|
updatedAt
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
800
|
+
},
|
|
801
|
+
queues: collaterals.map((collateral) => {
|
|
802
|
+
const poolId = `${user}-${chainId.toString()}-${collateral.collateralAddress}`.toLowerCase();
|
|
803
|
+
return {
|
|
804
|
+
queue: {
|
|
805
|
+
queueId: id,
|
|
806
|
+
availableLiquidityPoolId: poolId,
|
|
807
|
+
index: index2,
|
|
808
|
+
callbackAmount: collateral.callbackAmount.toString(),
|
|
809
|
+
updatedAt
|
|
810
|
+
},
|
|
811
|
+
pool: {
|
|
812
|
+
id: poolId,
|
|
813
|
+
amount: collateral.balance.toString(),
|
|
814
|
+
updatedAt
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
})
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
default: {
|
|
821
|
+
throw new Error(`CallbackType not implemented`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
765
824
|
}
|
|
766
825
|
function getCallbackIdForOffer(offer) {
|
|
767
826
|
if (offer.buy && offer.callback.data === "0x") {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
827
|
+
return `${offer.offering}-${offer.chainId.toString()}-${"buy_with_empty_callback" /* BuyWithEmptyCallback */}-${offer.loanToken}`.toLowerCase();
|
|
828
|
+
}
|
|
829
|
+
if (!offer.buy && offer.callback.data !== "0x" && WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].includes(
|
|
830
|
+
offer.callback.address.toLowerCase()
|
|
831
|
+
)) {
|
|
832
|
+
return `${offer.offering}-${offer.chainId.toString()}-${"sell_withdraw_from_wallet" /* SellWithdrawFromWallet */}-${mempool.Offer.termId(offer)}`.toLowerCase();
|
|
772
833
|
}
|
|
773
834
|
return null;
|
|
774
835
|
}
|
|
836
|
+
function decodeSellWithdrawFromWalletData(data) {
|
|
837
|
+
if (!data || data === "0x") throw new Error("Empty callback data");
|
|
838
|
+
try {
|
|
839
|
+
const [collaterals, amounts] = viem.decodeAbiParameters(
|
|
840
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
841
|
+
data
|
|
842
|
+
);
|
|
843
|
+
if (collaterals.length !== amounts.length) {
|
|
844
|
+
throw new Error("Mismatched array lengths");
|
|
845
|
+
}
|
|
846
|
+
return collaterals.map((c, i) => ({ collateral: c, amount: amounts[i] }));
|
|
847
|
+
} catch (_) {
|
|
848
|
+
throw new Error("Invalid SellWithdrawFromWallet callback data");
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function decode2(parameters) {
|
|
852
|
+
const { type, data } = parameters;
|
|
853
|
+
if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
|
|
854
|
+
return decodeSellWithdrawFromWalletData(data);
|
|
855
|
+
}
|
|
856
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
857
|
+
}
|
|
858
|
+
function encode2(parameters) {
|
|
859
|
+
const { type, data } = parameters;
|
|
860
|
+
if (type === "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */) {
|
|
861
|
+
return viem.encodeAbiParameters(
|
|
862
|
+
[{ type: "address[]" }, { type: "uint256[]" }],
|
|
863
|
+
[data.collaterals, data.amounts]
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
867
|
+
}
|
|
775
868
|
|
|
776
869
|
// src/core/Collector/index.ts
|
|
777
870
|
var Collector_exports = {};
|
|
@@ -856,20 +949,20 @@ async function fetch2(parameters) {
|
|
|
856
949
|
const map = await fetchBalancesAndAllowances({
|
|
857
950
|
client,
|
|
858
951
|
spender,
|
|
859
|
-
pairs: pairs.map(({ user,
|
|
952
|
+
pairs: pairs.map(({ user, loanToken }) => ({ user, token: loanToken })),
|
|
860
953
|
options
|
|
861
954
|
});
|
|
862
955
|
const out = [];
|
|
863
|
-
for (const [user,
|
|
864
|
-
for (const [
|
|
956
|
+
for (const [user, perLoanToken] of map) {
|
|
957
|
+
for (const [loanToken, { balance, allowance }] of perLoanToken) {
|
|
865
958
|
const amount = balance < allowance ? balance : allowance;
|
|
866
959
|
out.push(
|
|
867
960
|
buildLiquidity({
|
|
868
961
|
type,
|
|
869
962
|
user,
|
|
870
|
-
|
|
963
|
+
loanToken,
|
|
871
964
|
chainId,
|
|
872
|
-
amount
|
|
965
|
+
amount,
|
|
873
966
|
index: 0
|
|
874
967
|
})
|
|
875
968
|
);
|
|
@@ -927,16 +1020,16 @@ __export(Logger_exports, {
|
|
|
927
1020
|
silentLogger: () => silentLogger
|
|
928
1021
|
});
|
|
929
1022
|
var LogLevelValues = [
|
|
930
|
-
"silent",
|
|
931
1023
|
"trace",
|
|
932
1024
|
"debug",
|
|
933
1025
|
"info",
|
|
934
1026
|
"warn",
|
|
935
1027
|
"error",
|
|
936
|
-
"fatal"
|
|
1028
|
+
"fatal",
|
|
1029
|
+
"silent"
|
|
937
1030
|
];
|
|
938
1031
|
function defaultLogger(minLevel) {
|
|
939
|
-
const threshold = minLevel ?? "info";
|
|
1032
|
+
const threshold = minLevel ?? process.env.ROUTER_LOG_LEVEL ?? "info";
|
|
940
1033
|
const levelIndexByName = LogLevelValues.reduce(
|
|
941
1034
|
(acc, lvl, idx) => {
|
|
942
1035
|
acc[lvl] = idx;
|
|
@@ -1032,7 +1125,7 @@ function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
|
1032
1125
|
chainId: chain.id,
|
|
1033
1126
|
spender: chain.morpho,
|
|
1034
1127
|
type: "buy_with_empty_callback" /* BuyWithEmptyCallback */,
|
|
1035
|
-
pairs: pairs.map(({ user, token }) => ({ user,
|
|
1128
|
+
pairs: pairs.map(({ user, token }) => ({ user, loanToken: token })),
|
|
1036
1129
|
options: {
|
|
1037
1130
|
blockNumber: latestBlockNumber,
|
|
1038
1131
|
batchSize: maxBatchSize
|
|
@@ -1083,14 +1176,15 @@ function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
|
1083
1176
|
};
|
|
1084
1177
|
}
|
|
1085
1178
|
function createChainReorgsCollector(parameters) {
|
|
1086
|
-
const
|
|
1179
|
+
const collector = "chain_reorgs";
|
|
1087
1180
|
const {
|
|
1088
1181
|
client,
|
|
1089
1182
|
subscribers,
|
|
1090
1183
|
collectorStore,
|
|
1091
1184
|
chain,
|
|
1092
|
-
options: { maxBatchSize = 25, interval } = {}
|
|
1185
|
+
options: { maxBatchSize = 25, interval = 3e4, maxBlockNumber } = {}
|
|
1093
1186
|
} = parameters;
|
|
1187
|
+
const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
|
|
1094
1188
|
let finalizedBlock = null;
|
|
1095
1189
|
let unfinalizedBlocks = [];
|
|
1096
1190
|
const commonAncestor = (block) => {
|
|
@@ -1099,67 +1193,79 @@ function createChainReorgsCollector(parameters) {
|
|
|
1099
1193
|
if (finalizedBlock == null) throw new Error("Failed to get common ancestor");
|
|
1100
1194
|
return finalizedBlock;
|
|
1101
1195
|
};
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
unfinalizedBlocks.
|
|
1196
|
+
const collect = mempool.Utils.lazy((emit, { stop }) => {
|
|
1197
|
+
const logger = getLogger();
|
|
1198
|
+
const reconcile = async (block) => {
|
|
1199
|
+
if (block.hash === null || block.number === null || block.parentHash === null)
|
|
1200
|
+
throw new Error("Failed to get block");
|
|
1201
|
+
const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
|
|
1202
|
+
if (latestBlock === void 0) {
|
|
1203
|
+
const newBlock2 = {
|
|
1204
|
+
hash: block.hash,
|
|
1205
|
+
number: block.number,
|
|
1206
|
+
parentHash: block.parentHash
|
|
1207
|
+
};
|
|
1208
|
+
unfinalizedBlocks.push(newBlock2);
|
|
1209
|
+
return newBlock2;
|
|
1210
|
+
}
|
|
1211
|
+
if (latestBlock.hash === block.hash) return latestBlock;
|
|
1212
|
+
if (latestBlock.number >= block.number) {
|
|
1213
|
+
const ancestor = commonAncestor(block);
|
|
1214
|
+
logger.info({
|
|
1215
|
+
collector,
|
|
1216
|
+
chainId: chain.id,
|
|
1217
|
+
msg: `reorg detected, latestBlock.number: ${latestBlock.number} >= block.number: ${block.number} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1218
|
+
});
|
|
1219
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1220
|
+
unfinalizedBlocks = [];
|
|
1221
|
+
return ancestor;
|
|
1222
|
+
}
|
|
1223
|
+
if (latestBlock.number + 1n < block.number) {
|
|
1224
|
+
logger.debug({
|
|
1225
|
+
collector,
|
|
1226
|
+
chainId: chain.id,
|
|
1227
|
+
msg: `missing blocks between block ${latestBlock.number} and block ${block.number} on chain ${chain.id}`
|
|
1228
|
+
});
|
|
1229
|
+
const missingBlockNumbers = (() => {
|
|
1230
|
+
const missingBlockNumbers2 = [];
|
|
1231
|
+
let start2 = latestBlock.number + 1n;
|
|
1232
|
+
const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
|
|
1233
|
+
while (start2 < threshold) {
|
|
1234
|
+
missingBlockNumbers2.push(start2);
|
|
1235
|
+
start2 = start2 + 1n;
|
|
1236
|
+
}
|
|
1237
|
+
return missingBlockNumbers2;
|
|
1238
|
+
})();
|
|
1239
|
+
const missingBlocks = await Promise.all(
|
|
1240
|
+
missingBlockNumbers.map(
|
|
1241
|
+
(blockNumber) => client.getBlock({ blockNumber, includeTransactions: false })
|
|
1242
|
+
)
|
|
1243
|
+
);
|
|
1244
|
+
for (const missingBlock of missingBlocks) {
|
|
1245
|
+
const returnedBlock = await reconcile(missingBlock);
|
|
1246
|
+
if (returnedBlock.number !== missingBlock.number) return returnedBlock;
|
|
1247
|
+
}
|
|
1248
|
+
return reconcile(block);
|
|
1249
|
+
}
|
|
1250
|
+
if (block.parentHash !== latestBlock.hash) {
|
|
1251
|
+
const ancestor = commonAncestor(block);
|
|
1252
|
+
logger.info({
|
|
1253
|
+
collector,
|
|
1254
|
+
chainId: chain.id,
|
|
1255
|
+
msg: `reorg detected, block.parentHash: ${block.parentHash} !== latestBlock.hash: ${latestBlock.hash} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1256
|
+
});
|
|
1257
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1258
|
+
unfinalizedBlocks = [];
|
|
1259
|
+
return ancestor;
|
|
1260
|
+
}
|
|
1261
|
+
const newBlock = {
|
|
1108
1262
|
hash: block.hash,
|
|
1109
1263
|
number: block.number,
|
|
1110
1264
|
parentHash: block.parentHash
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
if (latestBlock.number >= block.number) {
|
|
1116
|
-
const ancestor = commonAncestor(block);
|
|
1117
|
-
console.log(
|
|
1118
|
-
`reorg detected, latestBlock.number: ${latestBlock.number} > block.number: ${block.number} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1119
|
-
);
|
|
1120
|
-
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1121
|
-
unfinalizedBlocks = [];
|
|
1122
|
-
return;
|
|
1123
|
-
}
|
|
1124
|
-
if (latestBlock.number + 1n < block.number) {
|
|
1125
|
-
console.log(
|
|
1126
|
-
`missing blocks between block ${latestBlock.number} and block ${block.number} on chain ${chain.id}`
|
|
1127
|
-
);
|
|
1128
|
-
const missingBlockNumbers = (() => {
|
|
1129
|
-
const missingBlockNumbers2 = [];
|
|
1130
|
-
let start2 = latestBlock.number + 1n;
|
|
1131
|
-
const threshold = latestBlock.number + BigInt(maxBatchSize) > block.number ? block.number : latestBlock.number + BigInt(maxBatchSize);
|
|
1132
|
-
while (start2 < threshold) {
|
|
1133
|
-
missingBlockNumbers2.push(start2);
|
|
1134
|
-
start2 = start2 + 1n;
|
|
1135
|
-
}
|
|
1136
|
-
return missingBlockNumbers2;
|
|
1137
|
-
})();
|
|
1138
|
-
const missingBlocks = await Promise.all(
|
|
1139
|
-
missingBlockNumbers.map(
|
|
1140
|
-
(blockNumber) => client.getBlock({ blockNumber, includeTransactions: false })
|
|
1141
|
-
)
|
|
1142
|
-
);
|
|
1143
|
-
for (const missingBlock of missingBlocks) await reconcile(missingBlock);
|
|
1144
|
-
await reconcile(block);
|
|
1145
|
-
return;
|
|
1146
|
-
}
|
|
1147
|
-
if (block.parentHash !== latestBlock.hash) {
|
|
1148
|
-
const ancestor = commonAncestor(block);
|
|
1149
|
-
console.log(
|
|
1150
|
-
`reorg detected, block.parentHash: ${block.parentHash} !== latestBlock.hash: ${latestBlock.hash} on chain ${chain.id}. Ancestor: ${ancestor.number}`
|
|
1151
|
-
);
|
|
1152
|
-
subscribers.forEach((subscriber) => subscriber.onReorg(Number(ancestor.number)));
|
|
1153
|
-
unfinalizedBlocks = [];
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
unfinalizedBlocks.push({
|
|
1157
|
-
hash: block.hash,
|
|
1158
|
-
number: block.number,
|
|
1159
|
-
parentHash: block.parentHash
|
|
1160
|
-
});
|
|
1161
|
-
};
|
|
1162
|
-
const collect = mempool.Utils.lazy((emit) => {
|
|
1265
|
+
};
|
|
1266
|
+
unfinalizedBlocks.push(newBlock);
|
|
1267
|
+
return newBlock;
|
|
1268
|
+
};
|
|
1163
1269
|
let tick = 0;
|
|
1164
1270
|
const fetchFinalizedBlock = async () => {
|
|
1165
1271
|
if (tick % 20 === 0 || finalizedBlock === null) {
|
|
@@ -1167,32 +1273,58 @@ function createChainReorgsCollector(parameters) {
|
|
|
1167
1273
|
blockTag: "finalized",
|
|
1168
1274
|
includeTransactions: false
|
|
1169
1275
|
});
|
|
1170
|
-
if (finalizedBlock === null)
|
|
1276
|
+
if (finalizedBlock === null) {
|
|
1277
|
+
const msg = "Failed to get finalized block";
|
|
1278
|
+
logger.fatal({ collector, chainId: chain.id, msg });
|
|
1279
|
+
throw new Error(msg);
|
|
1280
|
+
}
|
|
1281
|
+
unfinalizedBlocks = unfinalizedBlocks.filter((b) => b.number >= finalizedBlock.number);
|
|
1171
1282
|
}
|
|
1172
1283
|
tick++;
|
|
1173
1284
|
};
|
|
1174
|
-
|
|
1285
|
+
let isMaxBlockNumberReached = false;
|
|
1286
|
+
const unpoll = mempool.Utils.poll(
|
|
1175
1287
|
async () => {
|
|
1176
|
-
|
|
1177
|
-
|
|
1288
|
+
if (isMaxBlockNumberReached) {
|
|
1289
|
+
stop();
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const head = await client.getBlock({
|
|
1178
1293
|
blockTag: "latest",
|
|
1179
1294
|
includeTransactions: false
|
|
1180
1295
|
});
|
|
1181
|
-
|
|
1182
|
-
|
|
1296
|
+
if (maxBlockNumberBI !== void 0 && head.number >= maxBlockNumberBI) {
|
|
1297
|
+
logger.info({
|
|
1298
|
+
collector,
|
|
1299
|
+
chainId: chain.id,
|
|
1300
|
+
msg: `head is greater than max block number, head.number: ${head.number} > maxBlockNumber: ${maxBlockNumber} on chain ${chain.id}.`
|
|
1301
|
+
});
|
|
1302
|
+
isMaxBlockNumberReached = true;
|
|
1303
|
+
subscribers.forEach((subscriber) => subscriber.onReorg(maxBlockNumber));
|
|
1304
|
+
await collectorStore.saveBlockNumber({
|
|
1305
|
+
collectorName: collector,
|
|
1306
|
+
chainId: chain.id,
|
|
1307
|
+
blockNumber: maxBlockNumber
|
|
1308
|
+
});
|
|
1309
|
+
emit(maxBlockNumber);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
await fetchFinalizedBlock();
|
|
1313
|
+
const blockNumber = Number((await reconcile(head)).number);
|
|
1183
1314
|
await collectorStore.saveBlockNumber({
|
|
1184
|
-
collectorName:
|
|
1315
|
+
collectorName: collector,
|
|
1185
1316
|
chainId: chain.id,
|
|
1186
|
-
blockNumber
|
|
1317
|
+
blockNumber
|
|
1187
1318
|
});
|
|
1188
|
-
emit(
|
|
1319
|
+
emit(blockNumber);
|
|
1189
1320
|
},
|
|
1190
1321
|
{ interval: interval ?? 3e4 }
|
|
1191
1322
|
);
|
|
1323
|
+
return unpoll;
|
|
1192
1324
|
});
|
|
1193
1325
|
return {
|
|
1194
|
-
name:
|
|
1195
|
-
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
1326
|
+
name: collector,
|
|
1327
|
+
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName: collector, chainId: chain.id }),
|
|
1196
1328
|
collect,
|
|
1197
1329
|
onReorg: (_) => {
|
|
1198
1330
|
}
|
|
@@ -1391,18 +1523,89 @@ function morpho() {
|
|
|
1391
1523
|
return { message: "Expiry mismatch" };
|
|
1392
1524
|
}
|
|
1393
1525
|
});
|
|
1394
|
-
const
|
|
1395
|
-
|
|
1396
|
-
|
|
1526
|
+
const sellEmptyCallback = single(
|
|
1527
|
+
"sell_offers_empty_callback",
|
|
1528
|
+
(offer, _) => {
|
|
1529
|
+
if (!offer.buy && offer.callback.data === "0x") {
|
|
1530
|
+
return { message: "Sell offers require a non-empty callback." };
|
|
1531
|
+
}
|
|
1397
1532
|
}
|
|
1398
|
-
|
|
1533
|
+
);
|
|
1534
|
+
const buyNonEmptyCallback = single(
|
|
1535
|
+
"buy_offers_non_empty_callback",
|
|
1536
|
+
(offer, _) => {
|
|
1537
|
+
if (offer.buy && offer.callback.data !== "0x") {
|
|
1538
|
+
return { message: "Buy offers must use an empty callback." };
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
);
|
|
1542
|
+
const sellNonWhitelistedCallback = single(
|
|
1543
|
+
"sell_offers_non_whitelisted_callback",
|
|
1544
|
+
(offer, _) => {
|
|
1545
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1546
|
+
const allowed = new Set(
|
|
1547
|
+
WhitelistedCallbackAddresses["sell_withdraw_from_wallet" /* SellWithdrawFromWallet */].map(
|
|
1548
|
+
(a) => a.toLowerCase()
|
|
1549
|
+
)
|
|
1550
|
+
);
|
|
1551
|
+
const callbackAddress = offer.callback.address?.toLowerCase();
|
|
1552
|
+
if (!callbackAddress || !allowed.has(callbackAddress)) {
|
|
1553
|
+
return { message: "Sell offer callback address is not whitelisted." };
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
const sellCallbackDataInvalid = single(
|
|
1559
|
+
"sell_offers_callback_data_invalid",
|
|
1560
|
+
(offer, _) => {
|
|
1561
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1562
|
+
try {
|
|
1563
|
+
const decoded = decode2({
|
|
1564
|
+
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1565
|
+
data: offer.callback.data
|
|
1566
|
+
});
|
|
1567
|
+
if (decoded.length === 0) {
|
|
1568
|
+
return { message: "Sell offer callback data must include at least one collateral." };
|
|
1569
|
+
}
|
|
1570
|
+
} catch (_2) {
|
|
1571
|
+
return { message: "Sell offer callback data cannot be decoded." };
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
);
|
|
1576
|
+
const sellCallbackCollateralInvalid = single(
|
|
1577
|
+
"sell_offers_callback_collateral_invalid",
|
|
1578
|
+
(offer, _) => {
|
|
1579
|
+
if (!offer.buy && offer.callback.data !== "0x") {
|
|
1580
|
+
try {
|
|
1581
|
+
const decoded = decode2({
|
|
1582
|
+
type: "sell_withdraw_from_wallet" /* SellWithdrawFromWallet */,
|
|
1583
|
+
data: offer.callback.data
|
|
1584
|
+
});
|
|
1585
|
+
const offerCollaterals2 = new Set(
|
|
1586
|
+
offer.collaterals.map((c) => c.asset.toLowerCase())
|
|
1587
|
+
);
|
|
1588
|
+
for (const { collateral } of decoded) {
|
|
1589
|
+
if (!offerCollaterals2.has(collateral.toLowerCase())) {
|
|
1590
|
+
return { message: "Sell callback collateral is not part of offer collaterals." };
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
} catch (_2) {
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
);
|
|
1399
1598
|
return [
|
|
1400
1599
|
chainId,
|
|
1401
1600
|
loanToken,
|
|
1402
1601
|
expiry,
|
|
1403
|
-
// note: callback
|
|
1602
|
+
// note: callback rules should be the last ones, since they do not mean that the offer is forever invalid
|
|
1404
1603
|
// integrators should be able to choose if they want to keep the offer or not
|
|
1405
|
-
|
|
1604
|
+
sellEmptyCallback,
|
|
1605
|
+
buyNonEmptyCallback,
|
|
1606
|
+
sellNonWhitelistedCallback,
|
|
1607
|
+
sellCallbackDataInvalid,
|
|
1608
|
+
sellCallbackCollateralInvalid
|
|
1406
1609
|
];
|
|
1407
1610
|
}
|
|
1408
1611
|
|
|
@@ -1451,9 +1654,11 @@ function createMempoolCollector(parameters) {
|
|
|
1451
1654
|
});
|
|
1452
1655
|
const invalidOffersToSave = [];
|
|
1453
1656
|
const issueToStatus = {
|
|
1454
|
-
empty_callback: "callback_not_supported",
|
|
1455
1657
|
sell_offers_empty_callback: "callback_not_supported",
|
|
1456
|
-
|
|
1658
|
+
buy_offers_non_empty_callback: "callback_not_supported",
|
|
1659
|
+
sell_offers_non_whitelisted_callback: "callback_not_supported",
|
|
1660
|
+
sell_offers_callback_data_invalid: "callback_error",
|
|
1661
|
+
sell_offers_callback_collateral_invalid: "callback_error"
|
|
1457
1662
|
};
|
|
1458
1663
|
for (const issue of issues) {
|
|
1459
1664
|
const status = issueToStatus[issue.ruleName];
|
|
@@ -1564,11 +1769,19 @@ var offers = s.table(
|
|
|
1564
1769
|
pgCore.index("offers_expiry_idx").on(table.expiry),
|
|
1565
1770
|
pgCore.index("offers_rate_idx").on(table.rate),
|
|
1566
1771
|
pgCore.index("offers_assets_idx").on(table.assets),
|
|
1772
|
+
pgCore.index("offers_created_at_idx").on(table.createdAt),
|
|
1567
1773
|
// Compound indices for cursor pagination with hash
|
|
1568
1774
|
pgCore.index("offers_rate_hash_idx").on(table.rate, table.hash),
|
|
1569
1775
|
pgCore.index("offers_maturity_hash_idx").on(table.maturity, table.hash),
|
|
1570
1776
|
pgCore.index("offers_expiry_hash_idx").on(table.expiry, table.hash),
|
|
1571
|
-
pgCore.index("offers_assets_hash_idx").on(table.assets, table.hash)
|
|
1777
|
+
pgCore.index("offers_assets_hash_idx").on(table.assets, table.hash),
|
|
1778
|
+
// Compound index for multi-level sorting optimization (rate, createdAt, assets, hash)
|
|
1779
|
+
pgCore.index("offers_rate_created_at_assets_hash_idx").on(
|
|
1780
|
+
table.rate,
|
|
1781
|
+
drizzleOrm.asc(table.createdAt),
|
|
1782
|
+
drizzleOrm.desc(table.assets),
|
|
1783
|
+
drizzleOrm.asc(table.hash)
|
|
1784
|
+
)
|
|
1572
1785
|
]
|
|
1573
1786
|
);
|
|
1574
1787
|
var offerCollaterals = s.table(
|
|
@@ -1642,6 +1855,7 @@ var availableLiquidityQueues = s.table(
|
|
|
1642
1855
|
queueId: pgCore.varchar("queue_id", { length: 255 }).notNull(),
|
|
1643
1856
|
availableLiquidityPoolId: pgCore.varchar("available_liquidity_pool_id", { length: 255 }).notNull().references(() => availableLiquidityPools.id, { onDelete: "cascade" }),
|
|
1644
1857
|
index: pgCore.integer("index").notNull(),
|
|
1858
|
+
callbackAmount: pgCore.numeric("callback_amount", { precision: 78, scale: 0 }).default("0").notNull(),
|
|
1645
1859
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1646
1860
|
},
|
|
1647
1861
|
(table) => [
|
|
@@ -1786,6 +2000,7 @@ var create2 = (config) => {
|
|
|
1786
2000
|
queueId: qp.queue.queueId,
|
|
1787
2001
|
availableLiquidityPoolId: qp.pool.id,
|
|
1788
2002
|
index: qp.queue.index,
|
|
2003
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1789
2004
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1790
2005
|
}).onConflictDoUpdate({
|
|
1791
2006
|
target: [
|
|
@@ -1794,6 +2009,7 @@ var create2 = (config) => {
|
|
|
1794
2009
|
],
|
|
1795
2010
|
set: {
|
|
1796
2011
|
index: qp.queue.index,
|
|
2012
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1797
2013
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1798
2014
|
}
|
|
1799
2015
|
});
|
|
@@ -1872,6 +2088,7 @@ function memory2() {
|
|
|
1872
2088
|
queueId: qid,
|
|
1873
2089
|
availableLiquidityPoolId: qp.pool.id,
|
|
1874
2090
|
index: qp.queue.index,
|
|
2091
|
+
callbackAmount: qp.queue.callbackAmount,
|
|
1875
2092
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1876
2093
|
});
|
|
1877
2094
|
if (!queueIndexByQueueId.has(qid)) queueIndexByQueueId.set(qid, /* @__PURE__ */ new Set());
|
|
@@ -2385,7 +2602,8 @@ function create3(config) {
|
|
|
2385
2602
|
signature: offers.signature,
|
|
2386
2603
|
callbackId: offers.callbackId,
|
|
2387
2604
|
status: latestStatus.status,
|
|
2388
|
-
metadata: latestStatus.metadata
|
|
2605
|
+
metadata: latestStatus.metadata,
|
|
2606
|
+
createdAt: offers.createdAt
|
|
2389
2607
|
}
|
|
2390
2608
|
).from(offers).leftJoinLateral(latestStatus, drizzleOrm.sql`true`).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
|
|
2391
2609
|
drizzleOrm.and(
|
|
@@ -2440,6 +2658,7 @@ function create3(config) {
|
|
|
2440
2658
|
callbackId: bestOffers.callbackId,
|
|
2441
2659
|
status: bestOffers.status,
|
|
2442
2660
|
metadata: bestOffers.metadata,
|
|
2661
|
+
createdAt: bestOffers.createdAt,
|
|
2443
2662
|
// liquidity caps
|
|
2444
2663
|
userAmount: drizzleOrm.sql`COALESCE(${queueLiquidity.userAmount}, 0)`.as("user_amount"),
|
|
2445
2664
|
queueLiquidity: drizzleOrm.sql`COALESCE(${queueLiquidity.queueLiquidity}, 0)`.as(
|
|
@@ -2449,7 +2668,7 @@ function create3(config) {
|
|
|
2449
2668
|
cumulativeRemaining: drizzleOrm.sql`
|
|
2450
2669
|
SUM(${bestOffers.remaining}) OVER (
|
|
2451
2670
|
PARTITION BY ${bestOffers.callbackId}
|
|
2452
|
-
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2671
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.createdAt)}, ${bestOffers.assets} DESC, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2453
2672
|
ROWS UNBOUNDED PRECEDING
|
|
2454
2673
|
)
|
|
2455
2674
|
`.as("cumulative_remaining"),
|
|
@@ -2459,7 +2678,7 @@ function create3(config) {
|
|
|
2459
2678
|
WHEN ${bestOffers.remaining} <= 0 THEN false
|
|
2460
2679
|
ELSE ( SUM(${bestOffers.remaining}) OVER (
|
|
2461
2680
|
PARTITION BY ${bestOffers.callbackId}
|
|
2462
|
-
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2681
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.createdAt)}, ${bestOffers.assets} DESC, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2463
2682
|
ROWS UNBOUNDED PRECEDING
|
|
2464
2683
|
)
|
|
2465
2684
|
<= LEAST(
|
|
@@ -2493,6 +2712,7 @@ function create3(config) {
|
|
|
2493
2712
|
callbackId: offersWithEligibility.callbackId,
|
|
2494
2713
|
status: offersWithEligibility.status,
|
|
2495
2714
|
metadata: offersWithEligibility.metadata,
|
|
2715
|
+
createdAt: offersWithEligibility.createdAt,
|
|
2496
2716
|
userAmount: offersWithEligibility.userAmount,
|
|
2497
2717
|
queueLiquidity: offersWithEligibility.queueLiquidity,
|
|
2498
2718
|
cumulativeRemaining: offersWithEligibility.cumulativeRemaining,
|
|
@@ -2502,6 +2722,8 @@ function create3(config) {
|
|
|
2502
2722
|
ROW_NUMBER() OVER (
|
|
2503
2723
|
ORDER BY
|
|
2504
2724
|
CASE WHEN ${offersWithEligibility.buy} THEN ${offersWithEligibility.rate} ELSE -${offersWithEligibility.rate} END,
|
|
2725
|
+
${offersWithEligibility.createdAt},
|
|
2726
|
+
${offersWithEligibility.assets} DESC,
|
|
2505
2727
|
${drizzleOrm.asc(offersWithEligibility.hash)}
|
|
2506
2728
|
)
|
|
2507
2729
|
`.as("row_number")
|
|
@@ -2543,6 +2765,10 @@ function create3(config) {
|
|
|
2543
2765
|
)
|
|
2544
2766
|
).orderBy(
|
|
2545
2767
|
rateSortDirection === "asc" ? drizzleOrm.asc(validOffers.rate) : drizzleOrm.desc(validOffers.rate),
|
|
2768
|
+
drizzleOrm.asc(validOffers.createdAt),
|
|
2769
|
+
// earlier createdAt first
|
|
2770
|
+
drizzleOrm.desc(validOffers.assets),
|
|
2771
|
+
// higher assets first
|
|
2546
2772
|
drizzleOrm.asc(validOffers.hash)
|
|
2547
2773
|
);
|
|
2548
2774
|
const buildOffersMap = (rows, skipHash) => {
|