@morpho-dev/router 0.1.6 → 0.1.8
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/0000_init.sql +95 -0
- package/dist/drizzle/offers_v1.1/0001_new_table_for_collectors_block_numbers.sql +5 -0
- package/dist/drizzle/offers_v1.1/0002_update-liquidity-tables.sql +8 -0
- package/dist/drizzle/offers_v1.1/0003_add-not-null-for-queue-id.sql +1 -0
- package/dist/drizzle/offers_v1.1/0004_add-callback-id-to-offer.sql +1 -0
- package/dist/drizzle/offers_v1.1/0005_add-missing-indices-to-liquidity-tables.sql +2 -0
- package/dist/drizzle/offers_v1.1/meta/0000_snapshot.json +827 -0
- package/dist/drizzle/offers_v1.1/meta/0001_snapshot.json +827 -0
- package/dist/drizzle/offers_v1.1/meta/0002_snapshot.json +833 -0
- package/dist/drizzle/offers_v1.1/meta/0003_snapshot.json +833 -0
- package/dist/drizzle/offers_v1.1/meta/0004_snapshot.json +839 -0
- package/dist/drizzle/offers_v1.1/meta/0005_snapshot.json +877 -0
- package/dist/drizzle/offers_v1.1/meta/_journal.json +48 -0
- package/dist/index.browser.d.cts +1 -114
- package/dist/index.browser.d.ts +1 -114
- package/dist/index.browser.js +149 -561
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +151 -562
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.cts +1446 -306
- package/dist/index.node.d.ts +1446 -306
- package/dist/index.node.js +2387 -1120
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +2321 -1064
- package/dist/index.node.mjs.map +1 -1
- package/package.json +12 -3
package/dist/index.node.js
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var mempool = require('@morpho-dev/mempool');
|
|
4
|
-
var jsBase64 = require('js-base64');
|
|
5
4
|
var v4 = require('zod/v4');
|
|
6
5
|
var zodOpenapi = require('zod-openapi');
|
|
7
6
|
var viem = require('viem');
|
|
7
|
+
var jsBase64 = require('js-base64');
|
|
8
|
+
var actions = require('viem/actions');
|
|
9
|
+
var async_hooks = require('async_hooks');
|
|
10
|
+
var drizzleOrm = require('drizzle-orm');
|
|
11
|
+
var pgCore = require('drizzle-orm/pg-core');
|
|
12
|
+
var path = require('path');
|
|
13
|
+
var pglite = require('@electric-sql/pglite');
|
|
14
|
+
var nodePostgres = require('drizzle-orm/node-postgres');
|
|
15
|
+
var migrator = require('drizzle-orm/node-postgres/migrator');
|
|
16
|
+
var pglite$1 = require('drizzle-orm/pglite');
|
|
17
|
+
var migrator$1 = require('drizzle-orm/pglite/migrator');
|
|
18
|
+
var pg = require('pg');
|
|
8
19
|
var nodeServer = require('@hono/node-server');
|
|
9
20
|
var hono = require('hono');
|
|
10
|
-
|
|
21
|
+
|
|
22
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
|
+
|
|
24
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
11
25
|
|
|
12
26
|
var __defProp = Object.defineProperty;
|
|
13
27
|
var __export = (target, all) => {
|
|
@@ -15,60 +29,17 @@ var __export = (target, all) => {
|
|
|
15
29
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
30
|
};
|
|
17
31
|
|
|
18
|
-
// src/
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
// src/core/apiSchema/index.ts
|
|
33
|
+
var apiSchema_exports = {};
|
|
34
|
+
__export(apiSchema_exports, {
|
|
35
|
+
OpenApi: () => OpenApi,
|
|
36
|
+
fromResponse: () => fromResponse,
|
|
37
|
+
parse: () => parse,
|
|
38
|
+
safeParse: () => safeParse,
|
|
39
|
+
toResponse: () => toResponse
|
|
24
40
|
});
|
|
25
|
-
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
26
|
-
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
27
|
-
return CallbackType2;
|
|
28
|
-
})(CallbackType || {});
|
|
29
|
-
function buildLiquidity(parameters) {
|
|
30
|
-
const { type, user, contract, chainId, amount, index = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
31
|
-
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
32
|
-
throw new Error(`CallbackType not implemented: ${type}`);
|
|
33
|
-
const amountStr = amount.toString();
|
|
34
|
-
const id = `${user}-${chainId.toString()}-${type}-${contract}`.toLowerCase();
|
|
35
|
-
return {
|
|
36
|
-
userPosition: {
|
|
37
|
-
id,
|
|
38
|
-
availableLiquidityQueueId: id,
|
|
39
|
-
user: user.toLowerCase(),
|
|
40
|
-
chainId,
|
|
41
|
-
amount: amountStr,
|
|
42
|
-
updatedAt
|
|
43
|
-
},
|
|
44
|
-
queues: [
|
|
45
|
-
{
|
|
46
|
-
queue: {
|
|
47
|
-
queueId: id,
|
|
48
|
-
availableLiquidityPoolId: id,
|
|
49
|
-
index,
|
|
50
|
-
updatedAt
|
|
51
|
-
},
|
|
52
|
-
pool: {
|
|
53
|
-
id,
|
|
54
|
-
amount: amountStr,
|
|
55
|
-
updatedAt
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
]
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function getCallbackIdForOffer(offer) {
|
|
62
|
-
if (offer.buy && offer.callback.data === "0x") {
|
|
63
|
-
const type = "buy_with_empty_callback" /* BuyWithEmptyCallback */;
|
|
64
|
-
const user = offer.offering;
|
|
65
|
-
const loanToken = offer.loanToken;
|
|
66
|
-
return `${user}-${offer.chainId.toString()}-${type}-${loanToken}`.toLowerCase();
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
41
|
|
|
71
|
-
// src/Cursor.ts
|
|
42
|
+
// src/core/Cursor.ts
|
|
72
43
|
var Cursor_exports = {};
|
|
73
44
|
__export(Cursor_exports, {
|
|
74
45
|
decode: () => decode,
|
|
@@ -154,17 +125,7 @@ function decode(token) {
|
|
|
154
125
|
return decoded;
|
|
155
126
|
}
|
|
156
127
|
|
|
157
|
-
// src/core/
|
|
158
|
-
var apiSchema_exports = {};
|
|
159
|
-
__export(apiSchema_exports, {
|
|
160
|
-
OpenApi: () => OpenApi,
|
|
161
|
-
fromResponse: () => fromResponse,
|
|
162
|
-
parse: () => parse,
|
|
163
|
-
safeParse: () => safeParse,
|
|
164
|
-
toResponse: () => toResponse
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
// src/RouterOffer.ts
|
|
128
|
+
// src/core/RouterOffer.ts
|
|
168
129
|
var RouterOffer_exports = {};
|
|
169
130
|
__export(RouterOffer_exports, {
|
|
170
131
|
InvalidRouterOfferError: () => InvalidRouterOfferError,
|
|
@@ -741,1171 +702,2477 @@ var OpenApi = zodOpenapi.createDocument({
|
|
|
741
702
|
|
|
742
703
|
// src/core/apiSchema/utils.ts
|
|
743
704
|
function toResponse(routerOffer) {
|
|
744
|
-
const { consumed, status, metadata, ...offer } = routerOffer;
|
|
705
|
+
const { consumed: consumed2, status, metadata, ...offer } = routerOffer;
|
|
745
706
|
return {
|
|
746
707
|
offer,
|
|
747
|
-
consumed,
|
|
708
|
+
consumed: consumed2,
|
|
748
709
|
status,
|
|
749
710
|
metadata
|
|
750
711
|
};
|
|
751
712
|
}
|
|
752
713
|
function fromResponse(offerResponse) {
|
|
753
|
-
const { offer, consumed, status, metadata } = offerResponse;
|
|
714
|
+
const { offer, consumed: consumed2, status, metadata } = offerResponse;
|
|
754
715
|
return {
|
|
755
716
|
...offer,
|
|
756
|
-
consumed,
|
|
717
|
+
consumed: consumed2,
|
|
757
718
|
status,
|
|
758
719
|
metadata
|
|
759
720
|
};
|
|
760
721
|
}
|
|
761
722
|
|
|
762
|
-
// src/core/
|
|
763
|
-
var
|
|
764
|
-
__export(
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
HttpGetOffersFailedError: () => HttpGetOffersFailedError,
|
|
769
|
-
HttpRateLimitError: () => HttpRateLimitError,
|
|
770
|
-
HttpUnauthorizedError: () => HttpUnauthorizedError,
|
|
771
|
-
InternalServerError: () => InternalServerError,
|
|
772
|
-
InvalidUrlError: () => InvalidUrlError,
|
|
773
|
-
NotFoundError: () => NotFoundError,
|
|
774
|
-
ValidationError: () => ValidationError,
|
|
775
|
-
connect: () => connect,
|
|
776
|
-
error: () => error,
|
|
777
|
-
get: () => get,
|
|
778
|
-
handleZodError: () => handleZodError,
|
|
779
|
-
match: () => match,
|
|
780
|
-
serve: () => serve,
|
|
781
|
-
success: () => success
|
|
723
|
+
// src/core/Callback.ts
|
|
724
|
+
var Callback_exports = {};
|
|
725
|
+
__export(Callback_exports, {
|
|
726
|
+
CallbackType: () => CallbackType,
|
|
727
|
+
buildLiquidity: () => buildLiquidity,
|
|
728
|
+
getCallbackIdForOffer: () => getCallbackIdForOffer
|
|
782
729
|
});
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
headers
|
|
794
|
-
};
|
|
730
|
+
var CallbackType = /* @__PURE__ */ ((CallbackType2) => {
|
|
731
|
+
CallbackType2["BuyWithEmptyCallback"] = "buy_with_empty_callback";
|
|
732
|
+
return CallbackType2;
|
|
733
|
+
})(CallbackType || {});
|
|
734
|
+
function buildLiquidity(parameters) {
|
|
735
|
+
const { type, user, contract, chainId, amount, index: index2 = 0, updatedAt = /* @__PURE__ */ new Date() } = parameters;
|
|
736
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
737
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
738
|
+
const amountStr = amount.toString();
|
|
739
|
+
const id = `${user}-${chainId.toString()}-${type}-${contract}`.toLowerCase();
|
|
795
740
|
return {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
741
|
+
userPosition: {
|
|
742
|
+
id,
|
|
743
|
+
availableLiquidityQueueId: id,
|
|
744
|
+
user: user.toLowerCase(),
|
|
745
|
+
chainId,
|
|
746
|
+
amount: amountStr,
|
|
747
|
+
updatedAt
|
|
748
|
+
},
|
|
749
|
+
queues: [
|
|
750
|
+
{
|
|
751
|
+
queue: {
|
|
752
|
+
queueId: id,
|
|
753
|
+
availableLiquidityPoolId: id,
|
|
754
|
+
index: index2,
|
|
755
|
+
updatedAt
|
|
756
|
+
},
|
|
757
|
+
pool: {
|
|
758
|
+
id,
|
|
759
|
+
amount: amountStr,
|
|
760
|
+
updatedAt
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
]
|
|
799
764
|
};
|
|
800
765
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
url.searchParams.set("side", parameters.side);
|
|
808
|
-
}
|
|
809
|
-
if (parameters.chains?.length) {
|
|
810
|
-
url.searchParams.set("chains", parameters.chains.join(","));
|
|
766
|
+
function getCallbackIdForOffer(offer) {
|
|
767
|
+
if (offer.buy && offer.callback.data === "0x") {
|
|
768
|
+
const type = "buy_with_empty_callback" /* BuyWithEmptyCallback */;
|
|
769
|
+
const user = offer.offering;
|
|
770
|
+
const loanToken = offer.loanToken;
|
|
771
|
+
return `${user}-${offer.chainId.toString()}-${type}-${loanToken}`.toLowerCase();
|
|
811
772
|
}
|
|
812
|
-
|
|
813
|
-
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/core/Collector/index.ts
|
|
777
|
+
var Collector_exports = {};
|
|
778
|
+
__export(Collector_exports, {
|
|
779
|
+
createBuyWithEmptyCallbackLiquidityCollector: () => createBuyWithEmptyCallbackLiquidityCollector,
|
|
780
|
+
createChainReorgsCollector: () => createChainReorgsCollector,
|
|
781
|
+
createConsumedEventsCollector: () => createConsumedEventsCollector,
|
|
782
|
+
createMempoolCollector: () => createMempoolCollector,
|
|
783
|
+
start: () => start
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// src/core/Liquidity.ts
|
|
787
|
+
var Liquidity_exports = {};
|
|
788
|
+
__export(Liquidity_exports, {
|
|
789
|
+
fetch: () => fetch2,
|
|
790
|
+
fetchBalancesAndAllowances: () => fetchBalancesAndAllowances,
|
|
791
|
+
serialize: () => serialize
|
|
792
|
+
});
|
|
793
|
+
async function fetchBalancesAndAllowances(parameters) {
|
|
794
|
+
const { client, spender, pairs, options } = parameters;
|
|
795
|
+
if (pairs.length === 0) return /* @__PURE__ */ new Map();
|
|
796
|
+
const batchSize = Math.max(1, options?.batchSize ?? 5e3);
|
|
797
|
+
const retryAttempts = Math.max(1, options?.retryAttempts ?? 3);
|
|
798
|
+
const retryDelayMs = Math.max(0, options?.retryDelayMs ?? 50);
|
|
799
|
+
const blockNumber = options?.blockNumber ? BigInt(options.blockNumber) : void 0;
|
|
800
|
+
const out = /* @__PURE__ */ new Map();
|
|
801
|
+
for (const pairsBatch of mempool.Utils.batch(pairs, batchSize)) {
|
|
802
|
+
const balanceContracts = [];
|
|
803
|
+
const allowanceContracts = [];
|
|
804
|
+
for (const { user, token } of pairsBatch) {
|
|
805
|
+
balanceContracts.push({
|
|
806
|
+
address: token,
|
|
807
|
+
abi: viem.erc20Abi,
|
|
808
|
+
functionName: "balanceOf",
|
|
809
|
+
args: [user]
|
|
810
|
+
});
|
|
811
|
+
allowanceContracts.push({
|
|
812
|
+
address: token,
|
|
813
|
+
abi: viem.erc20Abi,
|
|
814
|
+
functionName: "allowance",
|
|
815
|
+
args: [user, spender]
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
const [balances, allowances] = await Promise.all([
|
|
819
|
+
mempool.Utils.retry(
|
|
820
|
+
() => client.multicall({
|
|
821
|
+
allowFailure: false,
|
|
822
|
+
contracts: balanceContracts,
|
|
823
|
+
...blockNumber ? { blockNumber } : {}
|
|
824
|
+
}),
|
|
825
|
+
retryAttempts,
|
|
826
|
+
retryDelayMs
|
|
827
|
+
),
|
|
828
|
+
mempool.Utils.retry(
|
|
829
|
+
() => client.multicall({
|
|
830
|
+
allowFailure: false,
|
|
831
|
+
contracts: allowanceContracts,
|
|
832
|
+
...blockNumber ? { blockNumber } : {}
|
|
833
|
+
}),
|
|
834
|
+
retryAttempts,
|
|
835
|
+
retryDelayMs
|
|
836
|
+
)
|
|
837
|
+
]);
|
|
838
|
+
for (let i = 0; i < pairsBatch.length; i++) {
|
|
839
|
+
const { user, token } = pairsBatch[i];
|
|
840
|
+
const balance = balances[i];
|
|
841
|
+
const allowance = allowances[i];
|
|
842
|
+
let perUser = out.get(user);
|
|
843
|
+
if (!perUser) {
|
|
844
|
+
perUser = /* @__PURE__ */ new Map();
|
|
845
|
+
out.set(user, perUser);
|
|
846
|
+
}
|
|
847
|
+
perUser.set(token, { balance, allowance });
|
|
848
|
+
}
|
|
814
849
|
}
|
|
815
|
-
|
|
816
|
-
|
|
850
|
+
return out;
|
|
851
|
+
}
|
|
852
|
+
async function fetch2(parameters) {
|
|
853
|
+
const { client, chainId, spender, type, pairs, options } = parameters;
|
|
854
|
+
if (type !== "buy_with_empty_callback" /* BuyWithEmptyCallback */)
|
|
855
|
+
throw new Error(`CallbackType not implemented: ${type}`);
|
|
856
|
+
const map = await fetchBalancesAndAllowances({
|
|
857
|
+
client,
|
|
858
|
+
spender,
|
|
859
|
+
pairs: pairs.map(({ user, contract }) => ({ user, token: contract })),
|
|
860
|
+
options
|
|
861
|
+
});
|
|
862
|
+
const out = [];
|
|
863
|
+
for (const [user, perContract] of map) {
|
|
864
|
+
for (const [contract, { balance, allowance }] of perContract) {
|
|
865
|
+
const amount = balance < allowance ? balance : allowance;
|
|
866
|
+
out.push(
|
|
867
|
+
buildLiquidity({
|
|
868
|
+
type,
|
|
869
|
+
user,
|
|
870
|
+
contract,
|
|
871
|
+
chainId,
|
|
872
|
+
amount: amount.toString(),
|
|
873
|
+
index: 0
|
|
874
|
+
})
|
|
875
|
+
);
|
|
876
|
+
}
|
|
817
877
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
if (parameters.minExpiry !== void 0) {
|
|
840
|
-
url.searchParams.set("min_expiry", parameters.minExpiry.toString());
|
|
841
|
-
}
|
|
842
|
-
if (parameters.maxExpiry !== void 0) {
|
|
843
|
-
url.searchParams.set("max_expiry", parameters.maxExpiry.toString());
|
|
844
|
-
}
|
|
845
|
-
if (parameters.collateralAssets?.length) {
|
|
846
|
-
url.searchParams.set("collateral_assets", parameters.collateralAssets.join(","));
|
|
847
|
-
}
|
|
848
|
-
if (parameters.collateralOracles?.length) {
|
|
849
|
-
url.searchParams.set("collateral_oracles", parameters.collateralOracles.join(","));
|
|
850
|
-
}
|
|
851
|
-
if (parameters.collateralTuple?.length) {
|
|
852
|
-
const tupleStr = parameters.collateralTuple.map(({ asset, oracle, lltv }) => {
|
|
853
|
-
let result = asset;
|
|
854
|
-
if (oracle) {
|
|
855
|
-
result += `:${oracle}`;
|
|
856
|
-
} else if (lltv !== void 0) {
|
|
857
|
-
result += `:`;
|
|
878
|
+
return out;
|
|
879
|
+
}
|
|
880
|
+
function serialize(liquidity) {
|
|
881
|
+
const normalized = {
|
|
882
|
+
userPosition: {
|
|
883
|
+
id: liquidity.userPosition.id,
|
|
884
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
885
|
+
user: liquidity.userPosition.user,
|
|
886
|
+
chainId: String(liquidity.userPosition.chainId),
|
|
887
|
+
amount: String(liquidity.userPosition.amount)
|
|
888
|
+
},
|
|
889
|
+
queues: liquidity.queues.map((queueWithPool) => ({
|
|
890
|
+
queue: {
|
|
891
|
+
queueId: queueWithPool.queue.queueId,
|
|
892
|
+
availableLiquidityPoolId: queueWithPool.queue.availableLiquidityPoolId,
|
|
893
|
+
index: queueWithPool.queue.index
|
|
894
|
+
},
|
|
895
|
+
pool: {
|
|
896
|
+
id: queueWithPool.pool.id,
|
|
897
|
+
amount: String(queueWithPool.pool.amount)
|
|
858
898
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (parameters.cursor) {
|
|
877
|
-
url.searchParams.set("cursor", parameters.cursor);
|
|
878
|
-
}
|
|
879
|
-
if (parameters.limit !== void 0) {
|
|
880
|
-
url.searchParams.set("limit", parameters.limit.toString());
|
|
881
|
-
}
|
|
882
|
-
const { cursor: returnedCursor, data: offers } = await getApi(config, url);
|
|
883
|
-
const routerOffers = offers.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
884
|
-
return {
|
|
885
|
-
cursor: returnedCursor,
|
|
886
|
-
offers: routerOffers.map(from).map(toResponse)
|
|
899
|
+
})).sort(
|
|
900
|
+
(left, right) => {
|
|
901
|
+
const leftQueueId = left.queue.queueId || "";
|
|
902
|
+
const rightQueueId = right.queue.queueId || "";
|
|
903
|
+
if (leftQueueId < rightQueueId) return -1;
|
|
904
|
+
if (leftQueueId > rightQueueId) return 1;
|
|
905
|
+
const leftPoolId = left.pool.id;
|
|
906
|
+
const rightPoolId = right.pool.id;
|
|
907
|
+
if (leftPoolId < rightPoolId) return -1;
|
|
908
|
+
if (leftPoolId > rightPoolId) return 1;
|
|
909
|
+
const leftIndex = left.queue.index;
|
|
910
|
+
const rightIndex = right.queue.index;
|
|
911
|
+
if (leftIndex < rightIndex) return -1;
|
|
912
|
+
if (leftIndex > rightIndex) return 1;
|
|
913
|
+
return 0;
|
|
914
|
+
}
|
|
915
|
+
)
|
|
887
916
|
};
|
|
917
|
+
return JSON.stringify(normalized);
|
|
888
918
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
url.searchParams.set("limit", parameters.limit.toString());
|
|
923
|
-
}
|
|
924
|
-
const { cursor: returnedCursor, data: offers } = await getApi(config, url);
|
|
925
|
-
const routerOffers = offers.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
919
|
+
|
|
920
|
+
// src/core/Logger.ts
|
|
921
|
+
var Logger_exports = {};
|
|
922
|
+
__export(Logger_exports, {
|
|
923
|
+
LogLevelValues: () => LogLevelValues,
|
|
924
|
+
defaultLogger: () => defaultLogger,
|
|
925
|
+
getLogger: () => getLogger,
|
|
926
|
+
runWithLogger: () => runWithLogger,
|
|
927
|
+
silentLogger: () => silentLogger
|
|
928
|
+
});
|
|
929
|
+
var LogLevelValues = [
|
|
930
|
+
"silent",
|
|
931
|
+
"trace",
|
|
932
|
+
"debug",
|
|
933
|
+
"info",
|
|
934
|
+
"warn",
|
|
935
|
+
"error",
|
|
936
|
+
"fatal"
|
|
937
|
+
];
|
|
938
|
+
function defaultLogger(minLevel) {
|
|
939
|
+
const threshold = minLevel ?? "info";
|
|
940
|
+
const levelIndexByName = LogLevelValues.reduce(
|
|
941
|
+
(acc, lvl, idx) => {
|
|
942
|
+
acc[lvl] = idx;
|
|
943
|
+
return acc;
|
|
944
|
+
},
|
|
945
|
+
{}
|
|
946
|
+
);
|
|
947
|
+
const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
|
|
948
|
+
const wrap = (consoleMethod, methodLevel) => isEnabled(methodLevel) ? (entry) => {
|
|
949
|
+
console[consoleMethod](viem.stringify({ level: methodLevel, ...entry }));
|
|
950
|
+
} : () => {
|
|
951
|
+
};
|
|
926
952
|
return {
|
|
927
|
-
|
|
928
|
-
|
|
953
|
+
trace: wrap("trace", "trace"),
|
|
954
|
+
debug: wrap("debug", "debug"),
|
|
955
|
+
info: wrap("info", "info"),
|
|
956
|
+
warn: wrap("warn", "warn"),
|
|
957
|
+
error: wrap("error", "error"),
|
|
958
|
+
fatal: wrap("error", "fatal")
|
|
929
959
|
};
|
|
930
960
|
}
|
|
931
|
-
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
961
|
+
function silentLogger() {
|
|
962
|
+
const noop = () => {
|
|
963
|
+
};
|
|
964
|
+
return { trace: noop, debug: noop, info: noop, warn: noop, error: noop, fatal: noop };
|
|
965
|
+
}
|
|
966
|
+
var loggerContext = new async_hooks.AsyncLocalStorage();
|
|
967
|
+
function runWithLogger(logger, fn) {
|
|
968
|
+
return loggerContext.run(logger, fn);
|
|
969
|
+
}
|
|
970
|
+
function getLogger() {
|
|
971
|
+
return loggerContext.getStore() ?? defaultLogger();
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// src/core/Collector/BuyWithEmptyCallbackLiquidityCollector.ts
|
|
975
|
+
function createBuyWithEmptyCallbackLiquidityCollector(params) {
|
|
976
|
+
const {
|
|
977
|
+
client,
|
|
978
|
+
collectorBlockStore,
|
|
979
|
+
liquidityStore,
|
|
980
|
+
offerStore,
|
|
981
|
+
chain,
|
|
982
|
+
options: { maxBatchSize = 1e3, interval = 3e4 } = {}
|
|
983
|
+
} = params;
|
|
984
|
+
const collector = "buy_with_empty_callback_liquidity";
|
|
985
|
+
async function getUniqueUserTokenPairs() {
|
|
986
|
+
const unique = /* @__PURE__ */ new Set();
|
|
987
|
+
const pairs = [];
|
|
988
|
+
let cursor;
|
|
989
|
+
do {
|
|
990
|
+
const { offers: offers2, nextCursor } = await offerStore.getAll({
|
|
991
|
+
query: {
|
|
992
|
+
side: "buy",
|
|
993
|
+
chains: [Number(chain.id)],
|
|
994
|
+
cursor,
|
|
995
|
+
limit: 1e3
|
|
996
|
+
}
|
|
944
997
|
});
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
998
|
+
cursor = nextCursor ?? void 0;
|
|
999
|
+
for (const offer of offers2) {
|
|
1000
|
+
const creator = offer.offering;
|
|
1001
|
+
const loanToken = offer.loanToken;
|
|
1002
|
+
const key = `${offer.offering}-${offer.loanToken}`.toLowerCase();
|
|
1003
|
+
if (unique.has(key)) continue;
|
|
1004
|
+
unique.add(key);
|
|
1005
|
+
pairs.push({ user: creator, token: loanToken });
|
|
1006
|
+
}
|
|
1007
|
+
} while (cursor);
|
|
1008
|
+
return pairs;
|
|
1009
|
+
}
|
|
1010
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
1011
|
+
const logger = getLogger();
|
|
1012
|
+
logger.info({
|
|
1013
|
+
collector,
|
|
1014
|
+
chainId: chain.id,
|
|
1015
|
+
msg: "start",
|
|
1016
|
+
interval,
|
|
1017
|
+
maxBatchSize
|
|
950
1018
|
});
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1019
|
+
return mempool.Utils.poll(
|
|
1020
|
+
async () => {
|
|
1021
|
+
const publicClient = client.extend(viem.publicActions);
|
|
1022
|
+
const pairs = await getUniqueUserTokenPairs();
|
|
1023
|
+
const latestBlockNumber = Number(await actions.getBlockNumber(publicClient));
|
|
1024
|
+
logger.debug({
|
|
1025
|
+
collector,
|
|
1026
|
+
chainId: chain.id,
|
|
1027
|
+
msg: "snapshot started",
|
|
1028
|
+
blockNumber: latestBlockNumber
|
|
1029
|
+
});
|
|
1030
|
+
const liquidityEntries = await fetch2({
|
|
1031
|
+
client: publicClient,
|
|
1032
|
+
chainId: chain.id,
|
|
1033
|
+
spender: chain.morpho,
|
|
1034
|
+
type: "buy_with_empty_callback" /* BuyWithEmptyCallback */,
|
|
1035
|
+
pairs: pairs.map(({ user, token }) => ({ user, contract: token })),
|
|
1036
|
+
options: {
|
|
1037
|
+
blockNumber: latestBlockNumber,
|
|
1038
|
+
batchSize: maxBatchSize
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
logger.debug({
|
|
1042
|
+
collector,
|
|
1043
|
+
chainId: chain.id,
|
|
1044
|
+
msg: `found ${liquidityEntries.length} liquidity entries`,
|
|
1045
|
+
blockNumber: latestBlockNumber
|
|
1046
|
+
});
|
|
1047
|
+
const existingLiquidityEntries = await liquidityStore.getAll();
|
|
1048
|
+
const existingById = /* @__PURE__ */ new Map();
|
|
1049
|
+
for (const liquidityEntry of existingLiquidityEntries) {
|
|
1050
|
+
const liquidityId = liquidityEntry.userPosition.id;
|
|
1051
|
+
const serializedEntry = serialize(liquidityEntry);
|
|
1052
|
+
existingById.set(liquidityId, serializedEntry);
|
|
1053
|
+
}
|
|
1054
|
+
for (const liquidity of liquidityEntries) {
|
|
1055
|
+
const serialized = serialize(liquidity);
|
|
1056
|
+
const prevSerialized = existingById.get(liquidity.userPosition.id);
|
|
1057
|
+
if (prevSerialized === serialized) continue;
|
|
1058
|
+
await liquidityStore.save({ liquidity });
|
|
1059
|
+
}
|
|
1060
|
+
await collectorBlockStore.saveBlockNumber({
|
|
1061
|
+
collectorName: collector,
|
|
1062
|
+
chainId: chain.id,
|
|
1063
|
+
blockNumber: latestBlockNumber
|
|
1064
|
+
});
|
|
1065
|
+
logger.info({
|
|
1066
|
+
collector,
|
|
1067
|
+
chainId: chain.id,
|
|
1068
|
+
msg: "snapshot done",
|
|
1069
|
+
blockNumber: latestBlockNumber
|
|
1070
|
+
});
|
|
1071
|
+
emit(latestBlockNumber);
|
|
1072
|
+
},
|
|
1073
|
+
{ interval }
|
|
1074
|
+
);
|
|
955
1075
|
});
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1076
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
1077
|
+
};
|
|
1078
|
+
return {
|
|
1079
|
+
name: collector,
|
|
1080
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName: collector, chainId: chain.id }),
|
|
1081
|
+
collect,
|
|
1082
|
+
onReorg
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
function createChainReorgsCollector(parameters) {
|
|
1086
|
+
const collectorName = "chain_reorgs";
|
|
1087
|
+
const {
|
|
1088
|
+
client,
|
|
1089
|
+
subscribers,
|
|
1090
|
+
collectorStore,
|
|
1091
|
+
chain,
|
|
1092
|
+
options: { maxBatchSize = 25, interval } = {}
|
|
1093
|
+
} = parameters;
|
|
1094
|
+
let finalizedBlock = null;
|
|
1095
|
+
let unfinalizedBlocks = [];
|
|
1096
|
+
const commonAncestor = (block) => {
|
|
1097
|
+
const parent = unfinalizedBlocks.find((b) => b.hash === block.parentHash);
|
|
1098
|
+
if (parent) return parent;
|
|
1099
|
+
if (finalizedBlock == null) throw new Error("Failed to get common ancestor");
|
|
1100
|
+
return finalizedBlock;
|
|
1101
|
+
};
|
|
1102
|
+
const reconcile = async (block) => {
|
|
1103
|
+
if (block.hash === null || block.number === null || block.parentHash === null)
|
|
1104
|
+
throw new Error("Failed to get block");
|
|
1105
|
+
const latestBlock = unfinalizedBlocks[unfinalizedBlocks.length - 1];
|
|
1106
|
+
if (latestBlock === void 0) {
|
|
1107
|
+
unfinalizedBlocks.push({
|
|
1108
|
+
hash: block.hash,
|
|
1109
|
+
number: block.number,
|
|
1110
|
+
parentHash: block.parentHash
|
|
1111
|
+
});
|
|
1112
|
+
return;
|
|
964
1113
|
}
|
|
965
|
-
|
|
966
|
-
|
|
1114
|
+
if (latestBlock.hash === block.hash) return;
|
|
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
|
|
967
1160
|
});
|
|
968
|
-
}
|
|
969
|
-
|
|
1161
|
+
};
|
|
1162
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
1163
|
+
let tick = 0;
|
|
1164
|
+
const fetchFinalizedBlock = async () => {
|
|
1165
|
+
if (tick % 20 === 0 || finalizedBlock === null) {
|
|
1166
|
+
finalizedBlock = await client.getBlock({
|
|
1167
|
+
blockTag: "finalized",
|
|
1168
|
+
includeTransactions: false
|
|
1169
|
+
});
|
|
1170
|
+
if (finalizedBlock === null) throw new Error("Failed to get finalized block");
|
|
1171
|
+
}
|
|
1172
|
+
tick++;
|
|
1173
|
+
};
|
|
1174
|
+
return mempool.Utils.poll(
|
|
1175
|
+
async () => {
|
|
1176
|
+
await fetchFinalizedBlock();
|
|
1177
|
+
const latestBlock = await client.getBlock({
|
|
1178
|
+
blockTag: "latest",
|
|
1179
|
+
includeTransactions: false
|
|
1180
|
+
});
|
|
1181
|
+
await reconcile(latestBlock);
|
|
1182
|
+
const lastBlockNumber = Number(latestBlock.number);
|
|
1183
|
+
await collectorStore.saveBlockNumber({
|
|
1184
|
+
collectorName: "chain_reorgs",
|
|
1185
|
+
chainId: chain.id,
|
|
1186
|
+
blockNumber: lastBlockNumber
|
|
1187
|
+
});
|
|
1188
|
+
emit(lastBlockNumber);
|
|
1189
|
+
},
|
|
1190
|
+
{ interval: interval ?? 3e4 }
|
|
1191
|
+
);
|
|
1192
|
+
});
|
|
1193
|
+
return {
|
|
1194
|
+
name: collectorName,
|
|
1195
|
+
lastSyncedBlock: async () => await collectorStore.getBlockNumber({ collectorName, chainId: chain.id }),
|
|
1196
|
+
collect,
|
|
1197
|
+
onReorg: (_) => {
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
970
1200
|
}
|
|
971
|
-
var InvalidUrlError = class extends mempool.Errors.BaseError {
|
|
972
|
-
name = "Router.InvalidUrlError";
|
|
973
|
-
constructor(url) {
|
|
974
|
-
super(`URL "${url}" is not http/https.`);
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
var HttpUnauthorizedError = class extends mempool.Errors.BaseError {
|
|
978
|
-
name = "Router.HttpUnauthorizedError";
|
|
979
|
-
constructor() {
|
|
980
|
-
super("Unauthorized.", {
|
|
981
|
-
metaMessages: ["Ensure that an API key is provided."]
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
var HttpForbiddenError = class extends mempool.Errors.BaseError {
|
|
986
|
-
name = "Router.HttpForbiddenError";
|
|
987
|
-
constructor() {
|
|
988
|
-
super("Forbidden.", {
|
|
989
|
-
metaMessages: ["Ensure that the API key is valid."]
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
};
|
|
993
|
-
var HttpRateLimitError = class extends mempool.Errors.BaseError {
|
|
994
|
-
name = "Router.HttpRateLimitError";
|
|
995
|
-
constructor() {
|
|
996
|
-
super("Rate limit exceeded.", {
|
|
997
|
-
metaMessages: [
|
|
998
|
-
"The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
|
|
999
|
-
]
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
};
|
|
1003
|
-
var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
|
|
1004
|
-
name = "Router.HttpGetOffersFailedError";
|
|
1005
|
-
constructor(message, { details } = {}) {
|
|
1006
|
-
super(message, {
|
|
1007
|
-
metaMessages: [details]
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
1201
|
|
|
1012
|
-
// src/
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (map.has(parameters2.offer.hash.toLowerCase())) return parameters2.offer.hash;
|
|
1023
|
-
const callbackId = getCallbackIdForOffer(parameters2.offer);
|
|
1024
|
-
map.set(parameters2.offer.hash.toLowerCase(), {
|
|
1025
|
-
...parameters2.offer,
|
|
1026
|
-
...callbackId ? { callbackId } : {},
|
|
1027
|
-
status: parameters2.status,
|
|
1028
|
-
metadata: parameters2.metadata
|
|
1029
|
-
});
|
|
1030
|
-
const chainId = parameters2.offer.chainId;
|
|
1031
|
-
const address = parameters2.offer.offering.toLowerCase();
|
|
1032
|
-
const nonce = parameters2.offer.nonce;
|
|
1033
|
-
const filledForChain = filled.get(chainId) || /* @__PURE__ */ new Map();
|
|
1034
|
-
const filledForOffering = filledForChain.get(address) || /* @__PURE__ */ new Map();
|
|
1035
|
-
if (!filledForOffering.has(nonce)) filledForOffering.set(nonce, 0n);
|
|
1036
|
-
filledForChain.set(address, filledForOffering);
|
|
1037
|
-
filled.set(chainId, filledForChain);
|
|
1038
|
-
return Promise.resolve(parameters2.offer.hash);
|
|
1202
|
+
// src/core/Collector/Collector.ts
|
|
1203
|
+
function start(collector) {
|
|
1204
|
+
let stopped = false;
|
|
1205
|
+
const it = collector.collect();
|
|
1206
|
+
(async () => {
|
|
1207
|
+
while (!stopped) await it.next();
|
|
1208
|
+
await it.return();
|
|
1209
|
+
})();
|
|
1210
|
+
return () => {
|
|
1211
|
+
stopped = true;
|
|
1039
1212
|
};
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1213
|
+
}
|
|
1214
|
+
function createConsumedEventsCollector(parameters) {
|
|
1215
|
+
const {
|
|
1216
|
+
client,
|
|
1217
|
+
contractAddress,
|
|
1218
|
+
offerStore,
|
|
1219
|
+
collectorBlockStore,
|
|
1220
|
+
options: { maxBatchSize = 1e3, interval = 3e4 } = {},
|
|
1221
|
+
chainId
|
|
1222
|
+
} = parameters;
|
|
1223
|
+
const collector = "consumed_events";
|
|
1224
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
1225
|
+
const logger = getLogger();
|
|
1226
|
+
logger.info({
|
|
1227
|
+
collector,
|
|
1228
|
+
chainId: chainId.toString(),
|
|
1229
|
+
msg: "start",
|
|
1230
|
+
interval,
|
|
1231
|
+
maxBatchSize
|
|
1232
|
+
});
|
|
1233
|
+
return mempool.Utils.poll(
|
|
1234
|
+
async () => {
|
|
1235
|
+
const lastSyncedBlock = await collectorBlockStore.getBlockNumber({
|
|
1236
|
+
collectorName: collector,
|
|
1237
|
+
chainId
|
|
1238
|
+
});
|
|
1239
|
+
logger.debug({ collector, chainId, msg: "stream started", blockNumber: lastSyncedBlock });
|
|
1240
|
+
const stream = mempool.Chain.streamLogs({
|
|
1241
|
+
client,
|
|
1242
|
+
contractAddress,
|
|
1243
|
+
event: consumedEvent,
|
|
1244
|
+
blockNumberGte: lastSyncedBlock,
|
|
1245
|
+
order: "asc",
|
|
1246
|
+
options: { maxBatchSize }
|
|
1247
|
+
});
|
|
1248
|
+
let blockNumber = lastSyncedBlock;
|
|
1249
|
+
for await (const { logs, blockNumber: lastStreamBlockNumber } of stream) {
|
|
1250
|
+
try {
|
|
1251
|
+
const parsedLogs = viem.parseEventLogs({ abi: [consumedEvent], logs });
|
|
1252
|
+
logger.debug({ collector, chainId, msg: `found ${parsedLogs.length} consumed events` });
|
|
1253
|
+
for (const log of parsedLogs) {
|
|
1254
|
+
if (log.blockNumber === null || log.logIndex === null || log.transactionHash === null) {
|
|
1255
|
+
logger.debug({
|
|
1256
|
+
collector,
|
|
1257
|
+
chainId,
|
|
1258
|
+
msg: "skipping log because it is missing required fields"
|
|
1259
|
+
});
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1262
|
+
const offer = fromConsumedLog({
|
|
1263
|
+
blockNumber: log.blockNumber,
|
|
1264
|
+
logIndex: log.logIndex,
|
|
1265
|
+
chainId: Number(chainId),
|
|
1266
|
+
transactionHash: log.transactionHash,
|
|
1267
|
+
user: log.args.user,
|
|
1268
|
+
nonce: log.args.nonce,
|
|
1269
|
+
amount: log.args.amount
|
|
1270
|
+
});
|
|
1271
|
+
await offerStore.updateConsumedAmount({
|
|
1272
|
+
id: offer.id,
|
|
1273
|
+
chainId: offer.chainId,
|
|
1274
|
+
offering: offer.offering,
|
|
1275
|
+
nonce: offer.nonce,
|
|
1276
|
+
consumed: offer.amount
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
blockNumber = lastStreamBlockNumber;
|
|
1280
|
+
await collectorBlockStore.saveBlockNumber({
|
|
1281
|
+
collectorName: collector,
|
|
1282
|
+
chainId,
|
|
1283
|
+
blockNumber
|
|
1284
|
+
});
|
|
1285
|
+
emit(blockNumber);
|
|
1286
|
+
} catch (err) {
|
|
1287
|
+
logger.error({ err, msg: "Failed to process offer_consumed events" });
|
|
1288
|
+
}
|
|
1077
1289
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1290
|
+
logger.info({
|
|
1291
|
+
collector,
|
|
1292
|
+
chainId,
|
|
1293
|
+
msg: "stream finished",
|
|
1294
|
+
startBlock: lastSyncedBlock,
|
|
1295
|
+
endBlock: blockNumber
|
|
1296
|
+
});
|
|
1297
|
+
},
|
|
1298
|
+
{ interval }
|
|
1299
|
+
);
|
|
1300
|
+
});
|
|
1301
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
1080
1302
|
};
|
|
1081
1303
|
return {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
minLltv,
|
|
1109
|
-
maxLltv,
|
|
1110
|
-
sortBy = "expiry",
|
|
1111
|
-
sortOrder = "desc",
|
|
1112
|
-
cursor: queryCursor,
|
|
1113
|
-
limit = 20
|
|
1114
|
-
} = query || {};
|
|
1115
|
-
const now = mempool.Time.now();
|
|
1116
|
-
const buy = side === "buy";
|
|
1117
|
-
let offers = Array.from(map.values()).map((o) => ({
|
|
1118
|
-
...o,
|
|
1119
|
-
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1120
|
-
})).filter((o) => o.consumed < o.assets);
|
|
1121
|
-
const cursor = decode(queryCursor);
|
|
1122
|
-
if (cursor) {
|
|
1123
|
-
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
1124
|
-
throw new Error("Cursor does not match the current sort parameters");
|
|
1125
|
-
}
|
|
1126
|
-
switch (cursor.sort) {
|
|
1127
|
-
case "rate":
|
|
1128
|
-
offers = offers.filter(
|
|
1129
|
-
(o) => (sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)) && (o.rate !== BigInt(cursor.rate) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1130
|
-
);
|
|
1131
|
-
break;
|
|
1132
|
-
case "maturity":
|
|
1133
|
-
offers = offers.filter(
|
|
1134
|
-
(o) => (sortOrder === "asc" ? o.maturity >= BigInt(cursor.maturity) : o.maturity <= BigInt(cursor.maturity)) && (o.maturity !== mempool.Maturity.from(cursor.maturity) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1135
|
-
);
|
|
1136
|
-
break;
|
|
1137
|
-
case "expiry":
|
|
1138
|
-
offers = offers.filter(
|
|
1139
|
-
(o) => (sortOrder === "asc" ? o.expiry >= cursor.expiry : o.expiry <= cursor.expiry) && (o.expiry !== cursor.expiry || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1140
|
-
);
|
|
1141
|
-
break;
|
|
1142
|
-
case "amount":
|
|
1143
|
-
offers = offers.filter(
|
|
1144
|
-
(o) => (sortOrder === "asc" ? o.assets >= BigInt(cursor.assets) : o.assets <= BigInt(cursor.assets)) && (o.assets !== BigInt(cursor.assets) || (sortOrder === "asc" ? o.hash > cursor.hash : o.hash < cursor.hash))
|
|
1145
|
-
);
|
|
1146
|
-
break;
|
|
1147
|
-
default:
|
|
1148
|
-
throw new Error("Invalid sort parameter");
|
|
1149
|
-
}
|
|
1150
|
-
offers = offers.filter((o) => o.hash !== cursor.hash);
|
|
1151
|
-
}
|
|
1152
|
-
creators && (creators = creators.map((c) => c.toLowerCase()));
|
|
1153
|
-
loanTokens && (loanTokens = loanTokens.map((lt) => lt.toLowerCase()));
|
|
1154
|
-
callbackAddresses && (callbackAddresses = callbackAddresses.map((ca) => ca.toLowerCase()));
|
|
1155
|
-
collateralAssets && (collateralAssets = collateralAssets.map((ca) => ca.toLowerCase()));
|
|
1156
|
-
collateralOracles && (collateralOracles = collateralOracles.map((co) => co.toLowerCase()));
|
|
1157
|
-
collateralTuple && (collateralTuple = collateralTuple.map((ct) => ({
|
|
1158
|
-
asset: ct.asset.toLowerCase(),
|
|
1159
|
-
oracle: ct.oracle?.toLowerCase()
|
|
1160
|
-
})));
|
|
1161
|
-
offers = offers.filter((o) => o.expiry >= now);
|
|
1162
|
-
creators && (offers = offers.filter((o) => creators.includes(o.offering.toLowerCase())));
|
|
1163
|
-
side && (offers = offers.filter((o) => o.buy === buy));
|
|
1164
|
-
chains && (offers = offers.filter((o) => chains.includes(Number(o.chainId))));
|
|
1165
|
-
loanTokens && (offers = offers.filter((o) => loanTokens.includes(o.loanToken.toLowerCase())));
|
|
1166
|
-
status && (offers = offers.filter((o) => status.includes(o.status)));
|
|
1167
|
-
callbackAddresses && (offers = offers.filter(
|
|
1168
|
-
(o) => callbackAddresses.includes(o.callback.address.toLowerCase())
|
|
1169
|
-
));
|
|
1170
|
-
minAmount && (offers = offers.filter((o) => o.assets >= minAmount));
|
|
1171
|
-
maxAmount && (offers = offers.filter((o) => o.assets <= maxAmount));
|
|
1172
|
-
minRate && (offers = offers.filter((o) => o.rate >= minRate));
|
|
1173
|
-
maxRate && (offers = offers.filter((o) => o.rate <= maxRate));
|
|
1174
|
-
minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
|
|
1175
|
-
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1176
|
-
minExpiry && (offers = offers.filter((o) => o.expiry >= minExpiry));
|
|
1177
|
-
maxExpiry && (offers = offers.filter((o) => o.expiry <= maxExpiry));
|
|
1178
|
-
collateralAssets && (offers = offers.filter(
|
|
1179
|
-
(o) => o.collaterals.some((c) => collateralAssets.includes(c.asset.toLowerCase()))
|
|
1180
|
-
));
|
|
1181
|
-
collateralOracles && (offers = offers.filter(
|
|
1182
|
-
(o) => o.collaterals.some((c) => collateralOracles.includes(c.oracle.toLowerCase()))
|
|
1183
|
-
));
|
|
1184
|
-
collateralTuple && (offers = offers.filter(
|
|
1185
|
-
(o) => o.collaterals.some(
|
|
1186
|
-
(c) => collateralTuple.some(
|
|
1187
|
-
(ct) => c.asset.toLowerCase() === ct.asset.toLowerCase() && (ct.oracle ? c.oracle.toLowerCase() === ct.oracle.toLowerCase() : true) && (ct.lltv ? c.lltv === mempool.LLTV.from(BigInt(ct.lltv)) : true)
|
|
1188
|
-
)
|
|
1189
|
-
)
|
|
1190
|
-
));
|
|
1191
|
-
minLltv && (offers = offers.filter(
|
|
1192
|
-
(o) => o.collaterals.every((c) => c.lltv >= viem.parseUnits(minLltv.toString(), 16))
|
|
1193
|
-
));
|
|
1194
|
-
maxLltv && (offers = offers.filter(
|
|
1195
|
-
(o) => o.collaterals.every((c) => c.lltv <= viem.parseUnits(maxLltv.toString(), 16))
|
|
1196
|
-
));
|
|
1197
|
-
offers = offers.sort((a, b) => sort(sortBy, sortOrder, a, b));
|
|
1198
|
-
let nextCursor = null;
|
|
1199
|
-
if (offers.length > limit) {
|
|
1200
|
-
const last = offers[limit - 1];
|
|
1201
|
-
const base = {
|
|
1202
|
-
sort: sortBy,
|
|
1203
|
-
dir: sortOrder,
|
|
1204
|
-
hash: last.hash
|
|
1205
|
-
};
|
|
1206
|
-
switch (sortBy) {
|
|
1207
|
-
case "rate":
|
|
1208
|
-
base.rate = last.rate.toString();
|
|
1209
|
-
break;
|
|
1210
|
-
case "amount":
|
|
1211
|
-
base.assets = last.assets.toString();
|
|
1212
|
-
break;
|
|
1213
|
-
case "maturity":
|
|
1214
|
-
base.maturity = last.maturity;
|
|
1215
|
-
break;
|
|
1216
|
-
default:
|
|
1217
|
-
base.expiry = last.expiry;
|
|
1218
|
-
}
|
|
1219
|
-
nextCursor = encode(base);
|
|
1220
|
-
}
|
|
1221
|
-
offers = offers.slice(0, limit);
|
|
1222
|
-
const data = offers.map((o) => ({
|
|
1223
|
-
...mempool.Offer.from({
|
|
1224
|
-
offering: o.offering,
|
|
1225
|
-
assets: o.assets,
|
|
1226
|
-
rate: o.rate,
|
|
1227
|
-
maturity: mempool.Maturity.from(o.maturity),
|
|
1228
|
-
expiry: o.expiry,
|
|
1229
|
-
start: o.start,
|
|
1230
|
-
nonce: o.nonce,
|
|
1231
|
-
buy: o.buy,
|
|
1232
|
-
chainId: o.chainId,
|
|
1233
|
-
loanToken: o.loanToken,
|
|
1234
|
-
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1235
|
-
callback: {
|
|
1236
|
-
address: o.callback.address,
|
|
1237
|
-
data: o.callback.data,
|
|
1238
|
-
gasLimit: o.callback.gasLimit
|
|
1239
|
-
},
|
|
1240
|
-
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1241
|
-
}),
|
|
1242
|
-
consumed: o.consumed,
|
|
1243
|
-
status: o.status,
|
|
1244
|
-
...o.metadata ? { metadata: o.metadata } : {}
|
|
1245
|
-
}));
|
|
1246
|
-
return {
|
|
1247
|
-
offers: data,
|
|
1248
|
-
nextCursor
|
|
1249
|
-
};
|
|
1250
|
-
},
|
|
1251
|
-
findMatchingOffers: async (params) => {
|
|
1252
|
-
const {
|
|
1253
|
-
side,
|
|
1254
|
-
chainId,
|
|
1255
|
-
rate,
|
|
1256
|
-
collaterals = [],
|
|
1257
|
-
maturity,
|
|
1258
|
-
minMaturity,
|
|
1259
|
-
maxMaturity,
|
|
1260
|
-
loanToken,
|
|
1261
|
-
creator,
|
|
1262
|
-
cursor: queryCursor,
|
|
1263
|
-
limit = 20
|
|
1264
|
-
} = params;
|
|
1265
|
-
const now = mempool.Time.now();
|
|
1266
|
-
const isBuying = side === "buy";
|
|
1267
|
-
const sortOrder = isBuying ? "desc" : "asc";
|
|
1268
|
-
let offers = Array.from(map.values()).map((o) => ({
|
|
1269
|
-
...o,
|
|
1270
|
-
consumed: filled.get(o.chainId)?.get(o.offering.toLowerCase())?.get(o.nonce) || 0n
|
|
1271
|
-
})).filter((o) => o.consumed < o.assets);
|
|
1272
|
-
const cursor = decode(queryCursor);
|
|
1273
|
-
if (cursor) {
|
|
1274
|
-
if (cursor.sort !== "rate" || cursor.dir !== sortOrder) {
|
|
1275
|
-
throw new Error("Cursor does not match the current sort parameters");
|
|
1304
|
+
name: collector,
|
|
1305
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName: collector, chainId }),
|
|
1306
|
+
onReorg,
|
|
1307
|
+
collect
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/core/Validation.ts
|
|
1312
|
+
var Validation_exports = {};
|
|
1313
|
+
__export(Validation_exports, {
|
|
1314
|
+
run: () => run
|
|
1315
|
+
});
|
|
1316
|
+
async function run(parameters) {
|
|
1317
|
+
const { items, rules, ctx = {}, chunkSize } = parameters;
|
|
1318
|
+
const issues = [];
|
|
1319
|
+
let validItems = items.slice();
|
|
1320
|
+
for (const rule of rules) {
|
|
1321
|
+
if (validItems.length === 0) return { valid: [], issues };
|
|
1322
|
+
const indicesToRemove = /* @__PURE__ */ new Set();
|
|
1323
|
+
if (rule.kind === "single") {
|
|
1324
|
+
for (let i = 0; i < validItems.length; i++) {
|
|
1325
|
+
const item = validItems[i];
|
|
1326
|
+
const issue = await rule.run(item, ctx);
|
|
1327
|
+
if (issue) {
|
|
1328
|
+
issues.push({ ...issue, ruleName: rule.name, item });
|
|
1329
|
+
indicesToRemove.add(i);
|
|
1276
1330
|
}
|
|
1277
|
-
offers = offers.filter(
|
|
1278
|
-
(o) => sortOrder === "asc" ? o.rate >= BigInt(cursor.rate) : o.rate <= BigInt(cursor.rate)
|
|
1279
|
-
);
|
|
1280
1331
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
(c) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
|
|
1291
|
-
);
|
|
1292
|
-
})
|
|
1293
|
-
) : (
|
|
1294
|
-
// when wanting to sell, user sell collaterals ⊆ buy offer collaterals
|
|
1295
|
-
collaterals.every((c) => {
|
|
1296
|
-
return o.collaterals.some(
|
|
1297
|
-
(oc) => oc.asset.toLowerCase() === c.asset.toLowerCase() && oc.oracle.toLowerCase() === c.oracle.toLowerCase() && oc.lltv === c.lltv
|
|
1298
|
-
);
|
|
1299
|
-
})
|
|
1300
|
-
)
|
|
1301
|
-
));
|
|
1302
|
-
maturity && (offers = offers.filter((o) => o.maturity === maturity));
|
|
1303
|
-
minMaturity && (offers = offers.filter((o) => o.maturity >= minMaturity));
|
|
1304
|
-
maxMaturity && (offers = offers.filter((o) => o.maturity <= maxMaturity));
|
|
1305
|
-
loanToken && (offers = offers.filter((o) => o.loanToken.toLowerCase() === loanToken.toLowerCase()));
|
|
1306
|
-
creator && (offers = offers.filter((o) => o.offering.toLowerCase() === creator.toLowerCase()));
|
|
1307
|
-
offers = offers.filter((o) => ["valid"].includes(o.status));
|
|
1308
|
-
const byGroup = /* @__PURE__ */ new Map();
|
|
1309
|
-
for (const offer of offers) {
|
|
1310
|
-
const groupKey = `${offer.chainId}-${offer.offering.toLowerCase()}-${offer.nonce}-${offer.buy}`;
|
|
1311
|
-
const current = byGroup.get(groupKey);
|
|
1312
|
-
if (!current) {
|
|
1313
|
-
byGroup.set(groupKey, offer);
|
|
1314
|
-
continue;
|
|
1315
|
-
}
|
|
1316
|
-
const remainingCandidate = offer.assets - offer.consumed;
|
|
1317
|
-
const remainingCurrent = current.assets - current.consumed;
|
|
1318
|
-
let candidateIsBetter = false;
|
|
1319
|
-
if (offer.buy) {
|
|
1320
|
-
if (offer.rate !== current.rate) candidateIsBetter = offer.rate < current.rate;
|
|
1321
|
-
else if (remainingCandidate !== remainingCurrent)
|
|
1322
|
-
candidateIsBetter = remainingCandidate > remainingCurrent;
|
|
1323
|
-
else if (offer.maturity !== current.maturity)
|
|
1324
|
-
candidateIsBetter = offer.maturity > current.maturity;
|
|
1325
|
-
else candidateIsBetter = offer.hash < current.hash;
|
|
1326
|
-
} else {
|
|
1327
|
-
if (offer.rate !== current.rate) candidateIsBetter = offer.rate > current.rate;
|
|
1328
|
-
else if (remainingCandidate !== remainingCurrent)
|
|
1329
|
-
candidateIsBetter = remainingCandidate > remainingCurrent;
|
|
1330
|
-
else if (offer.maturity !== current.maturity)
|
|
1331
|
-
candidateIsBetter = offer.maturity > current.maturity;
|
|
1332
|
-
else candidateIsBetter = offer.hash < current.hash;
|
|
1332
|
+
} else if (rule.kind === "batch") {
|
|
1333
|
+
const exec = async (slice, offset) => {
|
|
1334
|
+
const map = await rule.run(slice, ctx);
|
|
1335
|
+
for (let i = 0; i < slice.length; i++) {
|
|
1336
|
+
const issue = map.get(i);
|
|
1337
|
+
if (issue !== void 0) {
|
|
1338
|
+
issues.push({ ...issue, ruleName: rule.name, item: slice[i] });
|
|
1339
|
+
indicesToRemove.add(offset + i);
|
|
1340
|
+
}
|
|
1333
1341
|
}
|
|
1334
|
-
if (candidateIsBetter) byGroup.set(groupKey, offer);
|
|
1335
|
-
}
|
|
1336
|
-
offers = Array.from(byGroup.values());
|
|
1337
|
-
offers = offers.sort((a, b) => sort("rate", sortOrder, a, b));
|
|
1338
|
-
cursor && (offers = offers.filter((o) => o.hash !== cursor.hash));
|
|
1339
|
-
let nextCursor = null;
|
|
1340
|
-
if (offers.length > limit) {
|
|
1341
|
-
const last = offers[limit - 1];
|
|
1342
|
-
nextCursor = encode({
|
|
1343
|
-
sort: "rate",
|
|
1344
|
-
dir: sortOrder,
|
|
1345
|
-
hash: last.hash,
|
|
1346
|
-
rate: last.rate.toString()
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
1349
|
-
offers = offers.slice(0, limit);
|
|
1350
|
-
const data = offers.map((o) => ({
|
|
1351
|
-
...mempool.Offer.from({
|
|
1352
|
-
offering: o.offering,
|
|
1353
|
-
assets: o.assets,
|
|
1354
|
-
rate: o.rate,
|
|
1355
|
-
maturity: mempool.Maturity.from(o.maturity),
|
|
1356
|
-
expiry: o.expiry,
|
|
1357
|
-
start: o.start,
|
|
1358
|
-
nonce: o.nonce,
|
|
1359
|
-
buy: o.buy,
|
|
1360
|
-
chainId: o.chainId,
|
|
1361
|
-
loanToken: o.loanToken,
|
|
1362
|
-
collaterals: o.collaterals.map((c) => ({ asset: c.asset, oracle: c.oracle, lltv: c.lltv })).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
1363
|
-
callback: {
|
|
1364
|
-
address: o.callback.address,
|
|
1365
|
-
data: o.callback.data,
|
|
1366
|
-
gasLimit: o.callback.gasLimit
|
|
1367
|
-
},
|
|
1368
|
-
...o.signature !== null && o.signature !== void 0 ? { signature: o.signature } : {}
|
|
1369
|
-
}),
|
|
1370
|
-
consumed: o.consumed,
|
|
1371
|
-
status: o.status,
|
|
1372
|
-
...o.metadata ? { metadata: o.metadata } : {}
|
|
1373
|
-
}));
|
|
1374
|
-
return {
|
|
1375
|
-
offers: data,
|
|
1376
|
-
nextCursor
|
|
1377
1342
|
};
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
return true;
|
|
1383
|
-
},
|
|
1384
|
-
deleteMany: async (hashes) => {
|
|
1385
|
-
let deleted = 0;
|
|
1386
|
-
for (const hash of hashes) {
|
|
1387
|
-
if (map.has(hash.toLowerCase())) {
|
|
1388
|
-
map.delete(hash.toLowerCase());
|
|
1389
|
-
deleted++;
|
|
1343
|
+
if (!chunkSize) await exec(validItems, 0);
|
|
1344
|
+
else {
|
|
1345
|
+
for (let i = 0; i < validItems.length; i += chunkSize) {
|
|
1346
|
+
await exec(validItems.slice(i, i + chunkSize), i);
|
|
1390
1347
|
}
|
|
1391
1348
|
}
|
|
1392
|
-
return deleted;
|
|
1393
|
-
},
|
|
1394
|
-
updateStatus: async (parameters2) => {
|
|
1395
|
-
const key = parameters2.offerHash.toLowerCase();
|
|
1396
|
-
const existing = map.get(key);
|
|
1397
|
-
if (!existing) return;
|
|
1398
|
-
if (existing.status === parameters2.status) return;
|
|
1399
|
-
map.set(key, {
|
|
1400
|
-
...existing,
|
|
1401
|
-
status: parameters2.status,
|
|
1402
|
-
metadata: parameters2.metadata
|
|
1403
|
-
});
|
|
1404
|
-
},
|
|
1405
|
-
updateConsumedAmount: async (parameters2) => {
|
|
1406
|
-
if (consumedIds.has(parameters2.id)) return;
|
|
1407
|
-
consumedIds.add(parameters2.id);
|
|
1408
|
-
const chainId = parameters2.chainId;
|
|
1409
|
-
const address = parameters2.offering.toLowerCase();
|
|
1410
|
-
const nonce = parameters2.nonce;
|
|
1411
|
-
const filledForChain = filled.get(chainId) || /* @__PURE__ */ new Map();
|
|
1412
|
-
const filledForOffering = filledForChain.get(address) || /* @__PURE__ */ new Map();
|
|
1413
|
-
const current = filledForOffering.get(nonce) || 0n;
|
|
1414
|
-
filledForOffering.set(nonce, current + parameters2.consumed);
|
|
1415
|
-
filledForChain.set(address, filledForOffering);
|
|
1416
|
-
filled.set(chainId, filledForChain);
|
|
1417
1349
|
}
|
|
1350
|
+
validItems = validItems.filter((_, i) => !indicesToRemove.has(i));
|
|
1351
|
+
}
|
|
1352
|
+
return {
|
|
1353
|
+
valid: validItems,
|
|
1354
|
+
issues
|
|
1418
1355
|
};
|
|
1419
1356
|
}
|
|
1420
1357
|
|
|
1421
|
-
// src/core/
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1358
|
+
// src/core/ValidationRule.ts
|
|
1359
|
+
var ValidationRule_exports = {};
|
|
1360
|
+
__export(ValidationRule_exports, {
|
|
1361
|
+
batch: () => batch,
|
|
1362
|
+
morpho: () => morpho,
|
|
1363
|
+
single: () => single
|
|
1364
|
+
});
|
|
1365
|
+
function single(name, run2) {
|
|
1366
|
+
return { kind: "single", name, run: run2 };
|
|
1367
|
+
}
|
|
1368
|
+
function batch(name, run2) {
|
|
1369
|
+
return { kind: "batch", name, run: run2 };
|
|
1370
|
+
}
|
|
1371
|
+
function morpho() {
|
|
1372
|
+
const chainId = single("chain_id", (offer, { chain }) => {
|
|
1373
|
+
if (chain.id !== offer.chainId) {
|
|
1374
|
+
return {
|
|
1375
|
+
message: `Chain ID ${offer.chainId} is not the same as the chain ID in the context (${chain.id})`
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1427
1378
|
});
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
loanTokens: params.loan_tokens,
|
|
1437
|
-
status: params.status,
|
|
1438
|
-
callbackAddresses: params.callback_addresses,
|
|
1439
|
-
minAmount: params.min_amount,
|
|
1440
|
-
maxAmount: params.max_amount,
|
|
1441
|
-
minRate: params.min_rate,
|
|
1442
|
-
maxRate: params.max_rate,
|
|
1443
|
-
minMaturity: params.min_maturity,
|
|
1444
|
-
maxMaturity: params.max_maturity,
|
|
1445
|
-
minExpiry: params.min_expiry,
|
|
1446
|
-
maxExpiry: params.max_expiry,
|
|
1447
|
-
collateralAssets: params.collateral_assets,
|
|
1448
|
-
collateralOracles: params.collateral_oracles,
|
|
1449
|
-
collateralTuple: params.collateral_tuple,
|
|
1450
|
-
minLltv: params.min_lltv,
|
|
1451
|
-
maxLltv: params.max_lltv,
|
|
1452
|
-
sortBy: params.sort_by,
|
|
1453
|
-
sortOrder: params.sort_order,
|
|
1454
|
-
cursor: params.cursor,
|
|
1455
|
-
limit: params.limit
|
|
1456
|
-
}
|
|
1457
|
-
});
|
|
1458
|
-
return success(c, {
|
|
1459
|
-
data: offers.offers.map(
|
|
1460
|
-
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
1461
|
-
),
|
|
1462
|
-
cursor: offers.nextCursor ?? null
|
|
1463
|
-
});
|
|
1464
|
-
} catch (err) {
|
|
1465
|
-
console.error(err);
|
|
1466
|
-
return error(err, c);
|
|
1379
|
+
const loanToken = single("loan_token", (offer, { chain }) => {
|
|
1380
|
+
const tokens = new Set(
|
|
1381
|
+
Array.from(chain.whitelistedAssets.values()).map((a) => a.toLowerCase())
|
|
1382
|
+
);
|
|
1383
|
+
if (!tokens.has(offer.loanToken.toLowerCase())) {
|
|
1384
|
+
return {
|
|
1385
|
+
message: `Loan token ${offer.loanToken} is not whitelisted on chain ${offer.chainId}`
|
|
1386
|
+
};
|
|
1467
1387
|
}
|
|
1468
1388
|
});
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
const offers = await store.findMatchingOffers({
|
|
1473
|
-
side: params.side,
|
|
1474
|
-
chainId: params.chain_id,
|
|
1475
|
-
rate: params.rate,
|
|
1476
|
-
collaterals: params.collaterals,
|
|
1477
|
-
maturity: params.maturity,
|
|
1478
|
-
minMaturity: params.min_maturity,
|
|
1479
|
-
maxMaturity: params.max_maturity,
|
|
1480
|
-
loanToken: params.loan_token,
|
|
1481
|
-
creator: params.creator,
|
|
1482
|
-
cursor: params.cursor,
|
|
1483
|
-
limit: params.limit
|
|
1484
|
-
});
|
|
1485
|
-
return success(c, {
|
|
1486
|
-
data: offers.offers.map(
|
|
1487
|
-
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
1488
|
-
),
|
|
1489
|
-
cursor: offers.nextCursor ?? null
|
|
1490
|
-
});
|
|
1491
|
-
} catch (err) {
|
|
1492
|
-
console.error(err);
|
|
1493
|
-
return error(err, c);
|
|
1494
|
-
}
|
|
1495
|
-
});
|
|
1496
|
-
nodeServer.serve(
|
|
1497
|
-
{
|
|
1498
|
-
fetch: app.fetch,
|
|
1499
|
-
port: parameters.port
|
|
1500
|
-
},
|
|
1501
|
-
(info) => {
|
|
1502
|
-
console.log(`Started local router @ http://localhost:${info.port}`);
|
|
1503
|
-
}
|
|
1504
|
-
);
|
|
1505
|
-
}
|
|
1506
|
-
function error(error2, c) {
|
|
1507
|
-
if (error2 instanceof APIError) {
|
|
1508
|
-
return handleAPIError(error2, c);
|
|
1509
|
-
}
|
|
1510
|
-
if (error2 instanceof SyntaxError) {
|
|
1511
|
-
return handleAPIError(new BadRequestError(error2.message), c);
|
|
1512
|
-
}
|
|
1513
|
-
if (error2 instanceof v4.z.ZodError) {
|
|
1514
|
-
return handleAPIError(handleZodError(error2), c);
|
|
1515
|
-
}
|
|
1516
|
-
return handleAPIError(new InternalServerError(), c);
|
|
1517
|
-
}
|
|
1518
|
-
function success(c, {
|
|
1519
|
-
data,
|
|
1520
|
-
cursor
|
|
1521
|
-
}) {
|
|
1522
|
-
return c.json({
|
|
1523
|
-
status: "success",
|
|
1524
|
-
cursor,
|
|
1525
|
-
data,
|
|
1526
|
-
meta: {
|
|
1527
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1389
|
+
const expiry = single("expiry", (offer, _) => {
|
|
1390
|
+
if (offer.expiry < Math.floor(Date.now() / 1e3)) {
|
|
1391
|
+
return { message: "Expiry mismatch" };
|
|
1528
1392
|
}
|
|
1529
1393
|
});
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
super(message);
|
|
1534
|
-
this.statusCode = statusCode;
|
|
1535
|
-
this.code = code;
|
|
1536
|
-
this.details = details;
|
|
1537
|
-
this.name = "APIError";
|
|
1538
|
-
}
|
|
1539
|
-
};
|
|
1540
|
-
var ValidationError = class extends APIError {
|
|
1541
|
-
constructor(message, details) {
|
|
1542
|
-
super(400, message, "VALIDATION_ERROR", details);
|
|
1543
|
-
}
|
|
1544
|
-
};
|
|
1545
|
-
var NotFoundError = class extends APIError {
|
|
1546
|
-
constructor(message) {
|
|
1547
|
-
super(404, message, "NOT_FOUND");
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
var InternalServerError = class extends APIError {
|
|
1551
|
-
constructor(message = "Internal server error") {
|
|
1552
|
-
super(500, message, "INTERNAL_SERVER_ERROR");
|
|
1553
|
-
}
|
|
1554
|
-
};
|
|
1555
|
-
var BadRequestError = class extends APIError {
|
|
1556
|
-
constructor(message = "Invalid JSON format", details) {
|
|
1557
|
-
super(400, message, "BAD_REQUEST", details);
|
|
1558
|
-
}
|
|
1559
|
-
};
|
|
1560
|
-
function handleZodError(error2) {
|
|
1561
|
-
const formattedErrors = error2.issues.map((err) => {
|
|
1562
|
-
const field = err.path.join(".");
|
|
1563
|
-
let issue = err.message;
|
|
1564
|
-
if (err.code === "invalid_type") {
|
|
1565
|
-
if (err.message.includes("received undefined")) {
|
|
1566
|
-
issue = `${field} is required`;
|
|
1567
|
-
} else {
|
|
1568
|
-
issue = err.message;
|
|
1569
|
-
}
|
|
1570
|
-
} else if (err.code === "invalid_format") {
|
|
1571
|
-
issue = `${field} has an invalid format`;
|
|
1394
|
+
const callback = single("empty_callback", (offer, _) => {
|
|
1395
|
+
if (!offer.buy || offer.callback.data !== "0x") {
|
|
1396
|
+
return { message: "Callback not supported yet." };
|
|
1572
1397
|
}
|
|
1573
|
-
return {
|
|
1574
|
-
field,
|
|
1575
|
-
issue
|
|
1576
|
-
};
|
|
1577
1398
|
});
|
|
1578
|
-
return
|
|
1399
|
+
return [
|
|
1400
|
+
chainId,
|
|
1401
|
+
loanToken,
|
|
1402
|
+
expiry,
|
|
1403
|
+
// note: callback rule should be the last one, since it does not mean that the offer is forever invalid
|
|
1404
|
+
// integrators should be able to choose if they want to keep the offer or not
|
|
1405
|
+
callback
|
|
1406
|
+
];
|
|
1579
1407
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1408
|
+
|
|
1409
|
+
// src/core/Collector/MempoolCollector.ts
|
|
1410
|
+
function createMempoolCollector(parameters) {
|
|
1411
|
+
const {
|
|
1412
|
+
mempool: mempool$1,
|
|
1413
|
+
offerStore,
|
|
1414
|
+
collectorBlockStore,
|
|
1415
|
+
chain,
|
|
1416
|
+
options: { maxBatchSize = 1e3, interval = 3e4 } = {}
|
|
1417
|
+
} = parameters;
|
|
1418
|
+
const collector = "mempool_offers";
|
|
1419
|
+
const collect = mempool.Utils.lazy((emit) => {
|
|
1420
|
+
const logger = getLogger();
|
|
1421
|
+
logger.info({
|
|
1422
|
+
collector,
|
|
1423
|
+
chainId: chain.id,
|
|
1424
|
+
msg: "start",
|
|
1425
|
+
interval,
|
|
1426
|
+
maxBatchSize
|
|
1427
|
+
});
|
|
1428
|
+
let lastSyncedBlock = null;
|
|
1429
|
+
return mempool$1.watch({
|
|
1430
|
+
lastSyncedBlock: async () => {
|
|
1431
|
+
lastSyncedBlock = await collectorBlockStore.getBlockNumber({
|
|
1432
|
+
collectorName: collector,
|
|
1433
|
+
chainId: chain.id
|
|
1434
|
+
});
|
|
1435
|
+
logger.debug({
|
|
1436
|
+
collector,
|
|
1437
|
+
chainId: chain.id,
|
|
1438
|
+
msg: "stream started",
|
|
1439
|
+
blockNumber: lastSyncedBlock
|
|
1440
|
+
});
|
|
1441
|
+
return lastSyncedBlock;
|
|
1589
1442
|
},
|
|
1590
|
-
|
|
1591
|
-
|
|
1443
|
+
polling: { maxBatchSize, interval },
|
|
1444
|
+
onOffers: async (offers2, blockNumber) => {
|
|
1445
|
+
logger.debug({ collector, chainId: chain.id, msg: `found ${offers2.length} offers` });
|
|
1446
|
+
try {
|
|
1447
|
+
const { valid: validOffers, issues } = await run({
|
|
1448
|
+
items: offers2,
|
|
1449
|
+
rules: morpho(),
|
|
1450
|
+
ctx: { chain }
|
|
1451
|
+
});
|
|
1452
|
+
const invalidOffersToSave = [];
|
|
1453
|
+
const issueToStatus = {
|
|
1454
|
+
empty_callback: "callback_not_supported",
|
|
1455
|
+
sell_offers_empty_callback: "callback_not_supported",
|
|
1456
|
+
buy_offers_empty_callback: "callback_error"
|
|
1457
|
+
};
|
|
1458
|
+
for (const issue of issues) {
|
|
1459
|
+
const status = issueToStatus[issue.ruleName];
|
|
1460
|
+
if (status) {
|
|
1461
|
+
invalidOffersToSave.push({
|
|
1462
|
+
offer: issue.item,
|
|
1463
|
+
status,
|
|
1464
|
+
metadata: { issue: issue.ruleName }
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
await offerStore.createMany([
|
|
1469
|
+
...validOffers.map((offer) => ({
|
|
1470
|
+
offer,
|
|
1471
|
+
status: "valid"
|
|
1472
|
+
})),
|
|
1473
|
+
...invalidOffersToSave
|
|
1474
|
+
]);
|
|
1475
|
+
} catch (err) {
|
|
1476
|
+
logger.error({
|
|
1477
|
+
err,
|
|
1478
|
+
msg: "Failed to process offer_created events, falling back to unverified status"
|
|
1479
|
+
});
|
|
1480
|
+
await offerStore.createMany(
|
|
1481
|
+
offers2.map((offer) => ({
|
|
1482
|
+
offer,
|
|
1483
|
+
status: "unverified",
|
|
1484
|
+
metadata: { issue: "Offer processing failed" }
|
|
1485
|
+
}))
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
await collectorBlockStore.saveBlockNumber({
|
|
1489
|
+
collectorName: collector,
|
|
1490
|
+
chainId: chain.id,
|
|
1491
|
+
blockNumber
|
|
1492
|
+
});
|
|
1493
|
+
emit(blockNumber);
|
|
1494
|
+
if (lastSyncedBlock === null) return;
|
|
1495
|
+
logger.info({
|
|
1496
|
+
collector,
|
|
1497
|
+
chainId: chain.id,
|
|
1498
|
+
msg: "stream finished",
|
|
1499
|
+
startBlock: lastSyncedBlock,
|
|
1500
|
+
endBlock: blockNumber
|
|
1501
|
+
});
|
|
1592
1502
|
}
|
|
1593
|
-
})
|
|
1503
|
+
});
|
|
1594
1504
|
});
|
|
1505
|
+
const onReorg = (_lastFinalizedBlockNumber) => {
|
|
1506
|
+
};
|
|
1507
|
+
return {
|
|
1508
|
+
name: collector,
|
|
1509
|
+
lastSyncedBlock: async () => await collectorBlockStore.getBlockNumber({ collectorName: collector, chainId: chain.id }),
|
|
1510
|
+
collect,
|
|
1511
|
+
onReorg
|
|
1512
|
+
};
|
|
1595
1513
|
}
|
|
1596
1514
|
|
|
1597
|
-
// src/
|
|
1598
|
-
var
|
|
1599
|
-
__export(
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
serialize: () => serialize
|
|
1515
|
+
// src/core/CollectorBlockStore.ts
|
|
1516
|
+
var CollectorBlockStore_exports = {};
|
|
1517
|
+
__export(CollectorBlockStore_exports, {
|
|
1518
|
+
create: () => create,
|
|
1519
|
+
memory: () => memory
|
|
1603
1520
|
});
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1521
|
+
|
|
1522
|
+
// src/core/OfferStore/drizzle/schema.ts
|
|
1523
|
+
var schema_exports = {};
|
|
1524
|
+
__export(schema_exports, {
|
|
1525
|
+
VERSION: () => VERSION,
|
|
1526
|
+
availableLiquidityPools: () => availableLiquidityPools,
|
|
1527
|
+
availableLiquidityQueues: () => availableLiquidityQueues,
|
|
1528
|
+
collectorBlockNumbers: () => collectorBlockNumbers,
|
|
1529
|
+
consumed: () => consumed,
|
|
1530
|
+
offerCollaterals: () => offerCollaterals,
|
|
1531
|
+
offerStatus: () => offerStatus,
|
|
1532
|
+
offers: () => offers,
|
|
1533
|
+
userPositions: () => userPositions
|
|
1534
|
+
});
|
|
1535
|
+
var VERSION = "offers_v1.1";
|
|
1536
|
+
var s = pgCore.pgSchema(VERSION);
|
|
1537
|
+
var offers = s.table(
|
|
1538
|
+
"offers",
|
|
1539
|
+
{
|
|
1540
|
+
hash: pgCore.varchar("hash", { length: 66 }).primaryKey(),
|
|
1541
|
+
offering: pgCore.varchar("offering", { length: 42 }).notNull(),
|
|
1542
|
+
assets: pgCore.numeric("assets", { precision: 78, scale: 0 }).notNull(),
|
|
1543
|
+
rate: pgCore.bigint("rate", { mode: "bigint" }).notNull(),
|
|
1544
|
+
maturity: pgCore.integer("maturity").notNull(),
|
|
1545
|
+
expiry: pgCore.integer("expiry").notNull(),
|
|
1546
|
+
start: pgCore.integer("start").notNull(),
|
|
1547
|
+
nonce: pgCore.bigint("nonce", { mode: "bigint" }).notNull(),
|
|
1548
|
+
buy: pgCore.boolean("buy").notNull(),
|
|
1549
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
1550
|
+
loanToken: pgCore.varchar("loan_token", { length: 42 }).notNull(),
|
|
1551
|
+
callbackAddress: pgCore.varchar("callback_address", { length: 42 }).notNull(),
|
|
1552
|
+
callbackData: pgCore.text("callback_data").notNull(),
|
|
1553
|
+
callbackGasLimit: pgCore.bigint("callback_gas_limit", { mode: "bigint" }).notNull(),
|
|
1554
|
+
signature: pgCore.varchar("signature", { length: 132 }),
|
|
1555
|
+
callbackId: pgCore.varchar("callback_id", { length: 256 }),
|
|
1556
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
1557
|
+
},
|
|
1558
|
+
(table) => [
|
|
1559
|
+
pgCore.index("offers_offering_idx").on(table.offering),
|
|
1560
|
+
pgCore.index("offers_buy_idx").on(table.buy),
|
|
1561
|
+
pgCore.index("offers_chain_id_idx").on(table.chainId),
|
|
1562
|
+
pgCore.index("offers_loan_token_idx").on(table.loanToken),
|
|
1563
|
+
pgCore.index("offers_maturity_idx").on(table.maturity),
|
|
1564
|
+
pgCore.index("offers_expiry_idx").on(table.expiry),
|
|
1565
|
+
pgCore.index("offers_rate_idx").on(table.rate),
|
|
1566
|
+
pgCore.index("offers_assets_idx").on(table.assets),
|
|
1567
|
+
// Compound indices for cursor pagination with hash
|
|
1568
|
+
pgCore.index("offers_rate_hash_idx").on(table.rate, table.hash),
|
|
1569
|
+
pgCore.index("offers_maturity_hash_idx").on(table.maturity, table.hash),
|
|
1570
|
+
pgCore.index("offers_expiry_hash_idx").on(table.expiry, table.hash),
|
|
1571
|
+
pgCore.index("offers_assets_hash_idx").on(table.assets, table.hash)
|
|
1572
|
+
]
|
|
1573
|
+
);
|
|
1574
|
+
var offerCollaterals = s.table(
|
|
1575
|
+
"offer_collaterals",
|
|
1576
|
+
{
|
|
1577
|
+
id: pgCore.serial("id").primaryKey(),
|
|
1578
|
+
offerHash: pgCore.varchar("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
1579
|
+
asset: pgCore.varchar("asset", { length: 42 }).notNull(),
|
|
1580
|
+
oracle: pgCore.varchar("oracle", { length: 42 }).notNull(),
|
|
1581
|
+
lltv: pgCore.bigint("lltv", { mode: "bigint" }).notNull()
|
|
1582
|
+
},
|
|
1583
|
+
(table) => [
|
|
1584
|
+
pgCore.index("offer_collaterals_offer_hash_idx").on(table.offerHash),
|
|
1585
|
+
pgCore.index("offer_collaterals_asset_idx").on(table.asset),
|
|
1586
|
+
pgCore.index("offer_collaterals_oracle_idx").on(table.oracle),
|
|
1587
|
+
// Composite index
|
|
1588
|
+
pgCore.index("offer_collaterals_tuple_idx").on(table.asset, table.oracle, table.lltv)
|
|
1589
|
+
]
|
|
1590
|
+
);
|
|
1591
|
+
var offerStatus = s.table(
|
|
1592
|
+
"offer_status",
|
|
1593
|
+
{
|
|
1594
|
+
id: pgCore.serial("id").primaryKey(),
|
|
1595
|
+
offerHash: pgCore.varchar("offer_hash", { length: 66 }).notNull().references(() => offers.hash, { onDelete: "cascade" }),
|
|
1596
|
+
status: pgCore.text("status").$type().notNull(),
|
|
1597
|
+
metadata: pgCore.jsonb("metadata"),
|
|
1598
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
1599
|
+
},
|
|
1600
|
+
(table) => [
|
|
1601
|
+
pgCore.index("offer_status_offer_hash_created_at_idx").on(table.offerHash, drizzleOrm.desc(table.createdAt)),
|
|
1602
|
+
pgCore.index("offer_status_status_idx").on(table.status)
|
|
1603
|
+
]
|
|
1604
|
+
);
|
|
1605
|
+
var consumed = s.table(
|
|
1606
|
+
"consumed_per_user_and_nonce",
|
|
1607
|
+
{
|
|
1608
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
1609
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
1610
|
+
offering: pgCore.varchar("offering", { length: 42 }).notNull(),
|
|
1611
|
+
nonce: pgCore.bigint("nonce", { mode: "bigint" }).notNull(),
|
|
1612
|
+
consumed: pgCore.numeric("consumed", { precision: 78, scale: 0 }).notNull(),
|
|
1613
|
+
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
1614
|
+
},
|
|
1615
|
+
(table) => [
|
|
1616
|
+
pgCore.index("consumed_per_user_and_nonce_chain_id_offering_nonce_created_at_idx").on(
|
|
1617
|
+
table.chainId,
|
|
1618
|
+
table.offering,
|
|
1619
|
+
table.nonce,
|
|
1620
|
+
drizzleOrm.desc(table.createdAt)
|
|
1621
|
+
)
|
|
1622
|
+
]
|
|
1623
|
+
);
|
|
1624
|
+
var collectorBlockNumbers = s.table(
|
|
1625
|
+
"collector_block_numbers",
|
|
1626
|
+
{
|
|
1627
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
1628
|
+
name: pgCore.text("name").notNull(),
|
|
1629
|
+
blockNumber: pgCore.bigint("block_number", { mode: "number" }).notNull(),
|
|
1630
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1631
|
+
},
|
|
1632
|
+
(table) => [pgCore.uniqueIndex("collector_block_numbers_chain_name_idx").on(table.chainId, table.name)]
|
|
1633
|
+
);
|
|
1634
|
+
var availableLiquidityPools = s.table("available_liquidity_pools", {
|
|
1635
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
1636
|
+
amount: pgCore.numeric("amount", { precision: 78, scale: 0 }).notNull(),
|
|
1637
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1638
|
+
});
|
|
1639
|
+
var availableLiquidityQueues = s.table(
|
|
1640
|
+
"available_liquidity_queues",
|
|
1641
|
+
{
|
|
1642
|
+
queueId: pgCore.varchar("queue_id", { length: 255 }).notNull(),
|
|
1643
|
+
availableLiquidityPoolId: pgCore.varchar("available_liquidity_pool_id", { length: 255 }).notNull().references(() => availableLiquidityPools.id, { onDelete: "cascade" }),
|
|
1644
|
+
index: pgCore.integer("index").notNull(),
|
|
1645
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1646
|
+
},
|
|
1647
|
+
(table) => [
|
|
1648
|
+
pgCore.primaryKey({
|
|
1649
|
+
columns: [table.queueId, table.availableLiquidityPoolId],
|
|
1650
|
+
name: "available_liquidity_queues_pk"
|
|
1651
|
+
}),
|
|
1652
|
+
pgCore.index("available_liquidity_queues_queue_index_idx").on(table.queueId, table.index)
|
|
1653
|
+
]
|
|
1654
|
+
);
|
|
1655
|
+
var userPositions = s.table(
|
|
1656
|
+
"user_positions",
|
|
1657
|
+
{
|
|
1658
|
+
id: pgCore.varchar("id", { length: 255 }).primaryKey(),
|
|
1659
|
+
availableLiquidityQueueId: pgCore.varchar("available_liquidity_queue_id", { length: 255 }).notNull(),
|
|
1660
|
+
user: pgCore.varchar("user", { length: 255 }).notNull(),
|
|
1661
|
+
chainId: pgCore.bigint("chain_id", { mode: "bigint" }).notNull(),
|
|
1662
|
+
amount: pgCore.numeric("amount", { precision: 78, scale: 0 }).notNull(),
|
|
1663
|
+
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
1664
|
+
},
|
|
1665
|
+
(table) => [
|
|
1666
|
+
pgCore.index("user_positions_available_liquidity_queue_id_idx").on(table.availableLiquidityQueueId)
|
|
1667
|
+
]
|
|
1668
|
+
);
|
|
1669
|
+
|
|
1670
|
+
// src/core/CollectorBlockStore.ts
|
|
1671
|
+
var create = (config) => {
|
|
1672
|
+
const db = config.db;
|
|
1673
|
+
return {
|
|
1674
|
+
getBlockNumber: async (parameters) => {
|
|
1675
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1676
|
+
const result = await db.select({ blockNumber: collectorBlockNumbers.blockNumber }).from(collectorBlockNumbers).where(
|
|
1677
|
+
drizzleOrm.and(
|
|
1678
|
+
drizzleOrm.eq(collectorBlockNumbers.name, name),
|
|
1679
|
+
drizzleOrm.eq(collectorBlockNumbers.chainId, parameters.chainId)
|
|
1680
|
+
)
|
|
1681
|
+
).limit(1);
|
|
1682
|
+
if (result.length === 0)
|
|
1683
|
+
return mempool.Chain.getChain(parameters.chainId)?.mempool.deploymentBlock || 0;
|
|
1684
|
+
return Number(result[0].blockNumber);
|
|
1685
|
+
},
|
|
1686
|
+
saveBlockNumber: async (parameters) => {
|
|
1687
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1688
|
+
await db.insert(collectorBlockNumbers).values({
|
|
1689
|
+
chainId: parameters.chainId,
|
|
1690
|
+
name,
|
|
1691
|
+
blockNumber: parameters.blockNumber,
|
|
1692
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1693
|
+
}).onConflictDoUpdate({
|
|
1694
|
+
target: [collectorBlockNumbers.chainId, collectorBlockNumbers.name],
|
|
1695
|
+
set: {
|
|
1696
|
+
blockNumber: parameters.blockNumber,
|
|
1697
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1698
|
+
}
|
|
1627
1699
|
});
|
|
1628
1700
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
contracts: allowanceContracts,
|
|
1643
|
-
...blockNumber ? { blockNumber } : {}
|
|
1644
|
-
}),
|
|
1645
|
-
retryAttempts,
|
|
1646
|
-
retryDelayMs
|
|
1647
|
-
)
|
|
1648
|
-
]);
|
|
1649
|
-
for (let i = 0; i < pairsBatch.length; i++) {
|
|
1650
|
-
const { user, token } = pairsBatch[i];
|
|
1651
|
-
const balance = balances[i];
|
|
1652
|
-
const allowance = allowances[i];
|
|
1653
|
-
let perUser = out.get(user);
|
|
1654
|
-
if (!perUser) {
|
|
1655
|
-
perUser = /* @__PURE__ */ new Map();
|
|
1656
|
-
out.set(user, perUser);
|
|
1701
|
+
};
|
|
1702
|
+
};
|
|
1703
|
+
function memory() {
|
|
1704
|
+
const blockNumbers = /* @__PURE__ */ new Map();
|
|
1705
|
+
return {
|
|
1706
|
+
getBlockNumber: async (parameters) => {
|
|
1707
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1708
|
+
return blockNumbers.get(name)?.get(parameters.chainId) || mempool.Chain.getChain(parameters.chainId)?.mempool.deploymentBlock || 0;
|
|
1709
|
+
},
|
|
1710
|
+
saveBlockNumber: async (parameters) => {
|
|
1711
|
+
const name = parameters.collectorName.toLowerCase();
|
|
1712
|
+
if (!blockNumbers.has(name)) {
|
|
1713
|
+
blockNumbers.set(name, /* @__PURE__ */ new Map());
|
|
1657
1714
|
}
|
|
1658
|
-
|
|
1715
|
+
if (!blockNumbers.get(name)?.has(parameters.chainId)) {
|
|
1716
|
+
blockNumbers.get(name).set(parameters.chainId, parameters.blockNumber);
|
|
1717
|
+
}
|
|
1718
|
+
blockNumbers.get(name).set(parameters.chainId, parameters.blockNumber);
|
|
1659
1719
|
}
|
|
1660
|
-
}
|
|
1661
|
-
return out;
|
|
1720
|
+
};
|
|
1662
1721
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
const
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1722
|
+
|
|
1723
|
+
// src/core/LiquidityStore.ts
|
|
1724
|
+
var LiquidityStore_exports = {};
|
|
1725
|
+
__export(LiquidityStore_exports, {
|
|
1726
|
+
create: () => create2,
|
|
1727
|
+
memory: () => memory2
|
|
1728
|
+
});
|
|
1729
|
+
var create2 = (config) => {
|
|
1730
|
+
const db = config.db;
|
|
1731
|
+
return {
|
|
1732
|
+
getByUserPositionId: async (parameters) => {
|
|
1733
|
+
const up = await db.select().from(userPositions).where(drizzleOrm.eq(userPositions.id, parameters.userPositionId)).limit(1);
|
|
1734
|
+
if (up.length === 0) return null;
|
|
1735
|
+
const userPositionRow = up[0];
|
|
1736
|
+
const rows = await db.select({ queue: availableLiquidityQueues, pool: availableLiquidityPools }).from(availableLiquidityQueues).innerJoin(
|
|
1737
|
+
availableLiquidityPools,
|
|
1738
|
+
drizzleOrm.eq(availableLiquidityPools.id, availableLiquidityQueues.availableLiquidityPoolId)
|
|
1739
|
+
).where(drizzleOrm.eq(availableLiquidityQueues.queueId, userPositionRow.availableLiquidityQueueId));
|
|
1740
|
+
const queues = rows.map((row) => ({
|
|
1741
|
+
queue: row.queue,
|
|
1742
|
+
pool: row.pool
|
|
1743
|
+
}));
|
|
1744
|
+
return { userPosition: userPositionRow, queues };
|
|
1745
|
+
},
|
|
1746
|
+
getAll: async () => {
|
|
1747
|
+
const rows = await db.select({
|
|
1748
|
+
userPosition: userPositions,
|
|
1749
|
+
queue: availableLiquidityQueues,
|
|
1750
|
+
pool: availableLiquidityPools
|
|
1751
|
+
}).from(userPositions).innerJoin(
|
|
1752
|
+
availableLiquidityQueues,
|
|
1753
|
+
drizzleOrm.eq(availableLiquidityQueues.queueId, userPositions.availableLiquidityQueueId)
|
|
1754
|
+
).innerJoin(
|
|
1755
|
+
availableLiquidityPools,
|
|
1756
|
+
drizzleOrm.eq(availableLiquidityPools.id, availableLiquidityQueues.availableLiquidityPoolId)
|
|
1686
1757
|
);
|
|
1758
|
+
const byUserPosition = /* @__PURE__ */ new Map();
|
|
1759
|
+
for (const row of rows) {
|
|
1760
|
+
const id = row.userPosition.id;
|
|
1761
|
+
if (!byUserPosition.has(id)) {
|
|
1762
|
+
byUserPosition.set(id, { userPosition: row.userPosition, queues: [] });
|
|
1763
|
+
}
|
|
1764
|
+
byUserPosition.get(id).queues.push({ queue: row.queue, pool: row.pool });
|
|
1765
|
+
}
|
|
1766
|
+
return Array.from(byUserPosition.values());
|
|
1767
|
+
},
|
|
1768
|
+
save: async (parameters) => {
|
|
1769
|
+
const { liquidity } = parameters;
|
|
1770
|
+
await db.transaction(async (tx) => {
|
|
1771
|
+
for (const qp of liquidity.queues) {
|
|
1772
|
+
await tx.insert(availableLiquidityPools).values({
|
|
1773
|
+
id: qp.pool.id,
|
|
1774
|
+
amount: qp.pool.amount,
|
|
1775
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1776
|
+
}).onConflictDoUpdate({
|
|
1777
|
+
target: availableLiquidityPools.id,
|
|
1778
|
+
set: {
|
|
1779
|
+
amount: qp.pool.amount,
|
|
1780
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1781
|
+
}
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
for (const qp of liquidity.queues) {
|
|
1785
|
+
await tx.insert(availableLiquidityQueues).values({
|
|
1786
|
+
queueId: qp.queue.queueId,
|
|
1787
|
+
availableLiquidityPoolId: qp.pool.id,
|
|
1788
|
+
index: qp.queue.index,
|
|
1789
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1790
|
+
}).onConflictDoUpdate({
|
|
1791
|
+
target: [
|
|
1792
|
+
availableLiquidityQueues.queueId,
|
|
1793
|
+
availableLiquidityQueues.availableLiquidityPoolId
|
|
1794
|
+
],
|
|
1795
|
+
set: {
|
|
1796
|
+
index: qp.queue.index,
|
|
1797
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
await tx.insert(userPositions).values({
|
|
1802
|
+
id: liquidity.userPosition.id,
|
|
1803
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
1804
|
+
user: liquidity.userPosition.user,
|
|
1805
|
+
chainId: liquidity.userPosition.chainId,
|
|
1806
|
+
amount: liquidity.userPosition.amount,
|
|
1807
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1808
|
+
}).onConflictDoUpdate({
|
|
1809
|
+
target: userPositions.id,
|
|
1810
|
+
set: {
|
|
1811
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
1812
|
+
user: liquidity.userPosition.user,
|
|
1813
|
+
chainId: liquidity.userPosition.chainId,
|
|
1814
|
+
amount: liquidity.userPosition.amount,
|
|
1815
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
});
|
|
1687
1819
|
}
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1820
|
+
};
|
|
1821
|
+
};
|
|
1822
|
+
function memory2() {
|
|
1823
|
+
const poolsById = /* @__PURE__ */ new Map();
|
|
1824
|
+
const queuesByComposite = /* @__PURE__ */ new Map();
|
|
1825
|
+
const queueIndexByQueueId = /* @__PURE__ */ new Map();
|
|
1826
|
+
const userPositionsById = /* @__PURE__ */ new Map();
|
|
1827
|
+
return {
|
|
1828
|
+
getByUserPositionId: async (parameters) => {
|
|
1829
|
+
const up = userPositionsById.get(parameters.userPositionId);
|
|
1830
|
+
if (!up) return null;
|
|
1831
|
+
const compositeKeys = queueIndexByQueueId.get(up.availableLiquidityQueueId) || /* @__PURE__ */ new Set();
|
|
1832
|
+
const queues = [];
|
|
1833
|
+
for (const key of compositeKeys) {
|
|
1834
|
+
const q = queuesByComposite.get(key);
|
|
1835
|
+
if (!q) continue;
|
|
1836
|
+
const p = poolsById.get(q.availableLiquidityPoolId);
|
|
1837
|
+
if (!p) continue;
|
|
1838
|
+
queues.push({ queue: q, pool: p });
|
|
1839
|
+
}
|
|
1840
|
+
return { userPosition: up, queues };
|
|
1699
1841
|
},
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1842
|
+
getAll: async () => {
|
|
1843
|
+
const results = [];
|
|
1844
|
+
for (const up of userPositionsById.values()) {
|
|
1845
|
+
const compositeKeys = queueIndexByQueueId.get(up.availableLiquidityQueueId) || /* @__PURE__ */ new Set();
|
|
1846
|
+
const queues = [];
|
|
1847
|
+
for (const key of compositeKeys) {
|
|
1848
|
+
const q = queuesByComposite.get(key);
|
|
1849
|
+
if (!q) continue;
|
|
1850
|
+
const p = poolsById.get(q.availableLiquidityPoolId);
|
|
1851
|
+
if (!p) continue;
|
|
1852
|
+
queues.push({ queue: q, pool: p });
|
|
1853
|
+
}
|
|
1854
|
+
results.push({ userPosition: up, queues });
|
|
1709
1855
|
}
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
const leftIndex = left.queue.index;
|
|
1721
|
-
const rightIndex = right.queue.index;
|
|
1722
|
-
if (leftIndex < rightIndex) return -1;
|
|
1723
|
-
if (leftIndex > rightIndex) return 1;
|
|
1724
|
-
return 0;
|
|
1856
|
+
return results;
|
|
1857
|
+
},
|
|
1858
|
+
save: async (parameters) => {
|
|
1859
|
+
const { liquidity } = parameters;
|
|
1860
|
+
for (const qp of liquidity.queues) {
|
|
1861
|
+
poolsById.set(qp.pool.id, {
|
|
1862
|
+
id: qp.pool.id,
|
|
1863
|
+
amount: qp.pool.amount,
|
|
1864
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1865
|
+
});
|
|
1725
1866
|
}
|
|
1726
|
-
|
|
1867
|
+
for (const qp of liquidity.queues) {
|
|
1868
|
+
const qid = qp.queue.queueId;
|
|
1869
|
+
if (!qid) continue;
|
|
1870
|
+
const composite = `${qid}::${qp.pool.id}`;
|
|
1871
|
+
queuesByComposite.set(composite, {
|
|
1872
|
+
queueId: qid,
|
|
1873
|
+
availableLiquidityPoolId: qp.pool.id,
|
|
1874
|
+
index: qp.queue.index,
|
|
1875
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1876
|
+
});
|
|
1877
|
+
if (!queueIndexByQueueId.has(qid)) queueIndexByQueueId.set(qid, /* @__PURE__ */ new Set());
|
|
1878
|
+
queueIndexByQueueId.get(qid).add(composite);
|
|
1879
|
+
}
|
|
1880
|
+
userPositionsById.set(liquidity.userPosition.id, {
|
|
1881
|
+
id: liquidity.userPosition.id,
|
|
1882
|
+
availableLiquidityQueueId: liquidity.userPosition.availableLiquidityQueueId,
|
|
1883
|
+
user: liquidity.userPosition.user,
|
|
1884
|
+
chainId: liquidity.userPosition.chainId,
|
|
1885
|
+
amount: liquidity.userPosition.amount,
|
|
1886
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1727
1889
|
};
|
|
1728
|
-
return JSON.stringify(normalized);
|
|
1729
1890
|
}
|
|
1730
1891
|
|
|
1731
|
-
// src/
|
|
1732
|
-
var
|
|
1733
|
-
__export(
|
|
1734
|
-
|
|
1735
|
-
defaultLogger: () => defaultLogger,
|
|
1736
|
-
getLogger: () => getLogger,
|
|
1737
|
-
runWithLogger: () => runWithLogger,
|
|
1738
|
-
silentLogger: () => silentLogger
|
|
1892
|
+
// src/core/OfferStore/index.ts
|
|
1893
|
+
var OfferStore_exports = {};
|
|
1894
|
+
__export(OfferStore_exports, {
|
|
1895
|
+
create: () => create3
|
|
1739
1896
|
});
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
"trace",
|
|
1743
|
-
"debug",
|
|
1744
|
-
"info",
|
|
1745
|
-
"warn",
|
|
1746
|
-
"error",
|
|
1747
|
-
"fatal"
|
|
1748
|
-
];
|
|
1749
|
-
function defaultLogger(minLevel) {
|
|
1750
|
-
const threshold = minLevel ?? "trace";
|
|
1751
|
-
const levelIndexByName = LogLevelValues.reduce(
|
|
1752
|
-
(acc, lvl, idx) => {
|
|
1753
|
-
acc[lvl] = idx;
|
|
1754
|
-
return acc;
|
|
1755
|
-
},
|
|
1756
|
-
{}
|
|
1757
|
-
);
|
|
1758
|
-
const isEnabled = (methodLevel) => levelIndexByName[methodLevel] >= levelIndexByName[threshold];
|
|
1897
|
+
function create3(config) {
|
|
1898
|
+
const db = config.db;
|
|
1759
1899
|
return {
|
|
1760
|
-
|
|
1761
|
-
|
|
1900
|
+
create: async (parameters) => {
|
|
1901
|
+
const { offer, status, metadata } = parameters;
|
|
1902
|
+
return await db.transaction(async (tx) => {
|
|
1903
|
+
const callbackId = getCallbackIdForOffer(offer);
|
|
1904
|
+
const result = await tx.insert(offers).values({
|
|
1905
|
+
hash: offer.hash.toLowerCase(),
|
|
1906
|
+
offering: offer.offering.toLowerCase(),
|
|
1907
|
+
assets: offer.assets.toString(),
|
|
1908
|
+
rate: offer.rate,
|
|
1909
|
+
maturity: offer.maturity,
|
|
1910
|
+
expiry: offer.expiry,
|
|
1911
|
+
start: offer.start,
|
|
1912
|
+
nonce: offer.nonce,
|
|
1913
|
+
buy: offer.buy,
|
|
1914
|
+
chainId: offer.chainId,
|
|
1915
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
1916
|
+
callbackAddress: offer.callback.address.toLowerCase(),
|
|
1917
|
+
callbackData: offer.callback.data,
|
|
1918
|
+
callbackGasLimit: offer.callback.gasLimit,
|
|
1919
|
+
signature: offer.signature,
|
|
1920
|
+
callbackId: callbackId ?? null
|
|
1921
|
+
}).onConflictDoNothing().returning();
|
|
1922
|
+
if (result.length === 0) {
|
|
1923
|
+
return offer.hash;
|
|
1924
|
+
}
|
|
1925
|
+
for (const collateral of offer.collaterals) {
|
|
1926
|
+
await tx.insert(offerCollaterals).values({
|
|
1927
|
+
offerHash: offer.hash.toLowerCase(),
|
|
1928
|
+
asset: collateral.asset.toLowerCase(),
|
|
1929
|
+
oracle: collateral.oracle.toLowerCase(),
|
|
1930
|
+
lltv: collateral.lltv
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
await tx.insert(offerStatus).values({
|
|
1934
|
+
offerHash: offer.hash.toLowerCase(),
|
|
1935
|
+
status,
|
|
1936
|
+
metadata
|
|
1937
|
+
});
|
|
1938
|
+
return offer.hash;
|
|
1939
|
+
});
|
|
1762
1940
|
},
|
|
1763
|
-
|
|
1764
|
-
|
|
1941
|
+
createMany: async (parameters) => {
|
|
1942
|
+
return await db.transaction(async (tx) => {
|
|
1943
|
+
const hashes = [];
|
|
1944
|
+
for (const { offer, status, metadata } of parameters) {
|
|
1945
|
+
const callbackId = getCallbackIdForOffer(offer);
|
|
1946
|
+
const result = await tx.insert(offers).values({
|
|
1947
|
+
hash: offer.hash.toLowerCase(),
|
|
1948
|
+
offering: offer.offering.toLowerCase(),
|
|
1949
|
+
assets: offer.assets.toString(),
|
|
1950
|
+
rate: offer.rate,
|
|
1951
|
+
maturity: offer.maturity,
|
|
1952
|
+
expiry: offer.expiry,
|
|
1953
|
+
start: offer.start,
|
|
1954
|
+
nonce: offer.nonce,
|
|
1955
|
+
buy: offer.buy,
|
|
1956
|
+
chainId: offer.chainId,
|
|
1957
|
+
loanToken: offer.loanToken.toLowerCase(),
|
|
1958
|
+
callbackAddress: offer.callback.address.toLowerCase(),
|
|
1959
|
+
callbackData: offer.callback.data,
|
|
1960
|
+
callbackGasLimit: offer.callback.gasLimit,
|
|
1961
|
+
signature: offer.signature,
|
|
1962
|
+
callbackId: callbackId ?? null
|
|
1963
|
+
}).onConflictDoNothing().returning();
|
|
1964
|
+
if (result.length === 0) continue;
|
|
1965
|
+
for (const collateral of offer.collaterals) {
|
|
1966
|
+
await tx.insert(offerCollaterals).values({
|
|
1967
|
+
offerHash: offer.hash.toLowerCase(),
|
|
1968
|
+
asset: collateral.asset.toLowerCase(),
|
|
1969
|
+
oracle: collateral.oracle.toLowerCase(),
|
|
1970
|
+
lltv: collateral.lltv
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
await tx.insert(offerStatus).values({
|
|
1974
|
+
offerHash: offer.hash.toLowerCase(),
|
|
1975
|
+
status,
|
|
1976
|
+
metadata
|
|
1977
|
+
});
|
|
1978
|
+
hashes.push(offer.hash);
|
|
1979
|
+
}
|
|
1980
|
+
return hashes;
|
|
1981
|
+
});
|
|
1765
1982
|
},
|
|
1766
|
-
|
|
1767
|
-
|
|
1983
|
+
getAll: async (params) => {
|
|
1984
|
+
const { query } = params ?? {};
|
|
1985
|
+
const conditions = [];
|
|
1986
|
+
const now = mempool.Time.now();
|
|
1987
|
+
if (query?.creators && query.creators.length > 0) {
|
|
1988
|
+
conditions.push(
|
|
1989
|
+
drizzleOrm.inArray(
|
|
1990
|
+
offers.offering,
|
|
1991
|
+
query.creators.map((c) => c.toLowerCase())
|
|
1992
|
+
)
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
if (query?.side) {
|
|
1996
|
+
conditions.push(drizzleOrm.eq(offers.buy, query.side === "buy"));
|
|
1997
|
+
}
|
|
1998
|
+
if (query?.chains && query.chains.length > 0) {
|
|
1999
|
+
conditions.push(
|
|
2000
|
+
drizzleOrm.inArray(
|
|
2001
|
+
offers.chainId,
|
|
2002
|
+
query.chains.map((chain) => BigInt(chain))
|
|
2003
|
+
)
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
if (query?.loanTokens && query.loanTokens.length > 0) {
|
|
2007
|
+
conditions.push(
|
|
2008
|
+
drizzleOrm.inArray(
|
|
2009
|
+
offers.loanToken,
|
|
2010
|
+
query.loanTokens.map((t) => t.toLowerCase())
|
|
2011
|
+
)
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
if (query?.callbackAddresses && query.callbackAddresses.length > 0) {
|
|
2015
|
+
conditions.push(
|
|
2016
|
+
drizzleOrm.inArray(
|
|
2017
|
+
offers.callbackAddress,
|
|
2018
|
+
query.callbackAddresses.map((c) => c.toLowerCase())
|
|
2019
|
+
)
|
|
2020
|
+
);
|
|
2021
|
+
}
|
|
2022
|
+
conditions.push(drizzleOrm.gte(offers.expiry, now));
|
|
2023
|
+
if (query?.minAmount !== void 0) {
|
|
2024
|
+
conditions.push(drizzleOrm.gte(offers.assets, query.minAmount.toString()));
|
|
2025
|
+
}
|
|
2026
|
+
if (query?.maxAmount !== void 0) {
|
|
2027
|
+
conditions.push(drizzleOrm.lte(offers.assets, query.maxAmount.toString()));
|
|
2028
|
+
}
|
|
2029
|
+
if (query?.minRate !== void 0) {
|
|
2030
|
+
conditions.push(drizzleOrm.gte(offers.rate, query.minRate));
|
|
2031
|
+
}
|
|
2032
|
+
if (query?.maxRate !== void 0) {
|
|
2033
|
+
conditions.push(drizzleOrm.lte(offers.rate, query.maxRate));
|
|
2034
|
+
}
|
|
2035
|
+
if (query?.minMaturity !== void 0) {
|
|
2036
|
+
conditions.push(drizzleOrm.gte(offers.maturity, query.minMaturity));
|
|
2037
|
+
}
|
|
2038
|
+
if (query?.maxMaturity !== void 0) {
|
|
2039
|
+
conditions.push(drizzleOrm.lte(offers.maturity, query.maxMaturity));
|
|
2040
|
+
}
|
|
2041
|
+
if (query?.minExpiry !== void 0) {
|
|
2042
|
+
conditions.push(drizzleOrm.gte(offers.expiry, query.minExpiry));
|
|
2043
|
+
}
|
|
2044
|
+
if (query?.maxExpiry !== void 0) {
|
|
2045
|
+
conditions.push(drizzleOrm.lte(offers.expiry, query.maxExpiry));
|
|
2046
|
+
}
|
|
2047
|
+
if (query?.collateralAssets && query.collateralAssets.length > 0) {
|
|
2048
|
+
conditions.push(
|
|
2049
|
+
drizzleOrm.inArray(
|
|
2050
|
+
offerCollaterals.asset,
|
|
2051
|
+
query.collateralAssets.map((a) => a.toLowerCase())
|
|
2052
|
+
)
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
if (query?.collateralOracles && query.collateralOracles.length > 0) {
|
|
2056
|
+
conditions.push(
|
|
2057
|
+
drizzleOrm.inArray(
|
|
2058
|
+
offerCollaterals.oracle,
|
|
2059
|
+
query.collateralOracles.map((o) => o.toLowerCase())
|
|
2060
|
+
)
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2063
|
+
if (query?.collateralTuple && query.collateralTuple.length > 0) {
|
|
2064
|
+
const tupleClauses = query.collateralTuple.map((tuple) => {
|
|
2065
|
+
const parts = [
|
|
2066
|
+
drizzleOrm.sql`${offerCollaterals.asset} = ${tuple.asset.toLowerCase()}`
|
|
2067
|
+
];
|
|
2068
|
+
if (tuple.oracle) {
|
|
2069
|
+
parts.push(drizzleOrm.sql`${offerCollaterals.oracle} = ${tuple.oracle.toLowerCase()}`);
|
|
2070
|
+
}
|
|
2071
|
+
if (tuple.lltv !== void 0) {
|
|
2072
|
+
parts.push(drizzleOrm.sql`${offerCollaterals.lltv} = ${tuple.lltv}`);
|
|
2073
|
+
}
|
|
2074
|
+
let clause = parts[0];
|
|
2075
|
+
for (let i = 1; i < parts.length; i++) {
|
|
2076
|
+
clause = drizzleOrm.sql`${clause} AND ${parts[i]}`;
|
|
2077
|
+
}
|
|
2078
|
+
return drizzleOrm.sql`(${clause})`;
|
|
2079
|
+
}).filter((c) => c !== void 0);
|
|
2080
|
+
if (tupleClauses.length > 0) {
|
|
2081
|
+
let combined = tupleClauses[0];
|
|
2082
|
+
for (let i = 1; i < tupleClauses.length; i++) {
|
|
2083
|
+
combined = drizzleOrm.sql`${combined} OR ${tupleClauses[i]}`;
|
|
2084
|
+
}
|
|
2085
|
+
conditions.push(drizzleOrm.sql`(${combined})`);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (query?.minLltv !== void 0) {
|
|
2089
|
+
conditions.push(drizzleOrm.gte(offerCollaterals.lltv, viem.parseUnits(query.minLltv.toString(), 16)));
|
|
2090
|
+
}
|
|
2091
|
+
if (query?.maxLltv !== void 0) {
|
|
2092
|
+
conditions.push(drizzleOrm.lte(offerCollaterals.lltv, viem.parseUnits(query.maxLltv.toString(), 16)));
|
|
2093
|
+
}
|
|
2094
|
+
const sortBy = query?.sortBy ?? "expiry";
|
|
2095
|
+
const sortOrder = query?.sortOrder ?? "desc";
|
|
2096
|
+
const sortColumn = (() => {
|
|
2097
|
+
switch (sortBy) {
|
|
2098
|
+
case "rate":
|
|
2099
|
+
return offers.rate;
|
|
2100
|
+
case "maturity":
|
|
2101
|
+
return offers.maturity;
|
|
2102
|
+
case "expiry":
|
|
2103
|
+
return offers.expiry;
|
|
2104
|
+
case "amount":
|
|
2105
|
+
return offers.assets;
|
|
2106
|
+
default:
|
|
2107
|
+
return offers.expiry;
|
|
2108
|
+
}
|
|
2109
|
+
})();
|
|
2110
|
+
const cursor = decode(query?.cursor);
|
|
2111
|
+
if (cursor) {
|
|
2112
|
+
if (cursor.sort !== sortBy || cursor.dir !== sortOrder) {
|
|
2113
|
+
throw new Error("Cursor does not match the current sort parameters");
|
|
2114
|
+
}
|
|
2115
|
+
const op = sortOrder === "asc" ? drizzleOrm.sql`>` : drizzleOrm.sql`<`;
|
|
2116
|
+
const cursorVal = (() => {
|
|
2117
|
+
switch (sortBy) {
|
|
2118
|
+
case "rate":
|
|
2119
|
+
return BigInt(cursor.rate);
|
|
2120
|
+
case "amount":
|
|
2121
|
+
return BigInt(cursor.assets);
|
|
2122
|
+
case "maturity":
|
|
2123
|
+
return cursor.maturity;
|
|
2124
|
+
case "expiry":
|
|
2125
|
+
return cursor.expiry;
|
|
2126
|
+
default:
|
|
2127
|
+
return cursor.expiry;
|
|
2128
|
+
}
|
|
2129
|
+
})();
|
|
2130
|
+
conditions.push(drizzleOrm.sql`(${sortColumn}, ${offers.hash}) ${op} (${cursorVal}, ${cursor.hash})`);
|
|
2131
|
+
}
|
|
2132
|
+
const limit = query?.limit ?? 20;
|
|
2133
|
+
const latestStatus = db.select({
|
|
2134
|
+
status: offerStatus.status,
|
|
2135
|
+
metadata: offerStatus.metadata
|
|
2136
|
+
}).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, offers.hash)).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1).as("latest_status");
|
|
2137
|
+
const sumConsumed = db.select({
|
|
2138
|
+
consumed: drizzleOrm.sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
|
|
2139
|
+
}).from(consumed).where(
|
|
2140
|
+
drizzleOrm.and(
|
|
2141
|
+
drizzleOrm.eq(consumed.offering, offers.offering),
|
|
2142
|
+
drizzleOrm.eq(consumed.nonce, offers.nonce),
|
|
2143
|
+
drizzleOrm.eq(consumed.chainId, offers.chainId)
|
|
2144
|
+
)
|
|
2145
|
+
).as("sum_consumed");
|
|
2146
|
+
const results = await db.select({
|
|
2147
|
+
hash: offers.hash,
|
|
2148
|
+
offering: offers.offering,
|
|
2149
|
+
assets: offers.assets,
|
|
2150
|
+
consumed: sumConsumed.consumed,
|
|
2151
|
+
rate: offers.rate,
|
|
2152
|
+
maturity: offers.maturity,
|
|
2153
|
+
expiry: offers.expiry,
|
|
2154
|
+
start: offers.start,
|
|
2155
|
+
nonce: offers.nonce,
|
|
2156
|
+
buy: offers.buy,
|
|
2157
|
+
chainId: offers.chainId,
|
|
2158
|
+
loanToken: offers.loanToken,
|
|
2159
|
+
callbackAddress: offers.callbackAddress,
|
|
2160
|
+
callbackData: offers.callbackData,
|
|
2161
|
+
callbackGasLimit: offers.callbackGasLimit,
|
|
2162
|
+
signature: offers.signature,
|
|
2163
|
+
createdAt: offers.createdAt,
|
|
2164
|
+
collateralAsset: offerCollaterals.asset,
|
|
2165
|
+
collateralOracle: offerCollaterals.oracle,
|
|
2166
|
+
collateralLltv: offerCollaterals.lltv,
|
|
2167
|
+
status: latestStatus.status,
|
|
2168
|
+
metadata: latestStatus.metadata
|
|
2169
|
+
}).from(offers).leftJoin(offerCollaterals, drizzleOrm.eq(offers.hash, offerCollaterals.offerHash)).leftJoinLateral(latestStatus, drizzleOrm.sql`true`).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
|
|
2170
|
+
drizzleOrm.and(
|
|
2171
|
+
conditions.length > 0 ? drizzleOrm.and(...conditions) : drizzleOrm.sql`true`,
|
|
2172
|
+
query?.status && query.status.length > 0 ? drizzleOrm.inArray(latestStatus.status, query.status) : drizzleOrm.eq(latestStatus.status, "valid"),
|
|
2173
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) > 0`
|
|
2174
|
+
)
|
|
2175
|
+
).orderBy(
|
|
2176
|
+
...sortOrder === "asc" ? [drizzleOrm.asc(sortColumn), drizzleOrm.asc(offers.hash)] : [drizzleOrm.desc(sortColumn), drizzleOrm.desc(offers.hash)]
|
|
2177
|
+
).limit(limit);
|
|
2178
|
+
const offerMap = /* @__PURE__ */ new Map();
|
|
2179
|
+
for (const row of results) {
|
|
2180
|
+
const offer = offerMap.get(row.hash) || {
|
|
2181
|
+
hash: row.hash,
|
|
2182
|
+
offering: row.offering,
|
|
2183
|
+
assets: BigInt(row.assets),
|
|
2184
|
+
consumed: BigInt(row.consumed),
|
|
2185
|
+
rate: row.rate,
|
|
2186
|
+
maturity: row.maturity,
|
|
2187
|
+
expiry: row.expiry,
|
|
2188
|
+
start: row.start,
|
|
2189
|
+
nonce: row.nonce,
|
|
2190
|
+
buy: row.buy,
|
|
2191
|
+
chainId: row.chainId,
|
|
2192
|
+
loanToken: row.loanToken,
|
|
2193
|
+
callbackAddress: row.callbackAddress,
|
|
2194
|
+
callbackData: row.callbackData,
|
|
2195
|
+
callbackGasLimit: row.callbackGasLimit,
|
|
2196
|
+
signature: row.signature,
|
|
2197
|
+
createdAt: row.createdAt,
|
|
2198
|
+
collaterals: [],
|
|
2199
|
+
status: row.status,
|
|
2200
|
+
metadata: row.metadata
|
|
2201
|
+
};
|
|
2202
|
+
if (row.collateralAsset && row.collateralOracle && row.collateralLltv) {
|
|
2203
|
+
offer.collaterals.push({
|
|
2204
|
+
asset: row.collateralAsset,
|
|
2205
|
+
oracle: row.collateralOracle,
|
|
2206
|
+
lltv: mempool.LLTV.from(row.collateralLltv)
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
offerMap.set(row.hash, offer);
|
|
2210
|
+
}
|
|
2211
|
+
let nextCursor = null;
|
|
2212
|
+
if (results.length === limit && results.length > 0) {
|
|
2213
|
+
const lastRow = results[results.length - 1];
|
|
2214
|
+
const base = {
|
|
2215
|
+
sort: sortBy,
|
|
2216
|
+
dir: sortOrder,
|
|
2217
|
+
hash: lastRow.hash
|
|
2218
|
+
};
|
|
2219
|
+
switch (sortBy) {
|
|
2220
|
+
case "rate":
|
|
2221
|
+
base.rate = lastRow.rate.toString();
|
|
2222
|
+
break;
|
|
2223
|
+
case "amount":
|
|
2224
|
+
base.assets = lastRow.assets.toString();
|
|
2225
|
+
break;
|
|
2226
|
+
case "maturity":
|
|
2227
|
+
base.maturity = lastRow.maturity;
|
|
2228
|
+
break;
|
|
2229
|
+
default:
|
|
2230
|
+
base.expiry = lastRow.expiry;
|
|
2231
|
+
}
|
|
2232
|
+
nextCursor = encode(base);
|
|
2233
|
+
}
|
|
2234
|
+
const transformedResults = [];
|
|
2235
|
+
for (const offerData of offerMap.values()) {
|
|
2236
|
+
const offer = mempool.Offer.from({
|
|
2237
|
+
offering: offerData.offering,
|
|
2238
|
+
assets: offerData.assets,
|
|
2239
|
+
rate: offerData.rate,
|
|
2240
|
+
maturity: mempool.Maturity.from(offerData.maturity),
|
|
2241
|
+
expiry: offerData.expiry,
|
|
2242
|
+
start: offerData.start,
|
|
2243
|
+
nonce: offerData.nonce,
|
|
2244
|
+
buy: offerData.buy,
|
|
2245
|
+
chainId: offerData.chainId,
|
|
2246
|
+
loanToken: offerData.loanToken,
|
|
2247
|
+
collaterals: offerData.collaterals.map((c) => ({
|
|
2248
|
+
asset: c.asset,
|
|
2249
|
+
oracle: c.oracle,
|
|
2250
|
+
lltv: mempool.LLTV.from(c.lltv)
|
|
2251
|
+
})).sort((a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())),
|
|
2252
|
+
callback: {
|
|
2253
|
+
address: offerData.callbackAddress,
|
|
2254
|
+
data: offerData.callbackData,
|
|
2255
|
+
gasLimit: offerData.callbackGasLimit
|
|
2256
|
+
},
|
|
2257
|
+
...offerData.signature !== null ? { signature: offerData.signature } : void 0
|
|
2258
|
+
});
|
|
2259
|
+
transformedResults.push({
|
|
2260
|
+
...offer,
|
|
2261
|
+
consumed: offerData.consumed,
|
|
2262
|
+
status: offerData.status,
|
|
2263
|
+
...offerData.metadata !== null ? { metadata: offerData.metadata } : void 0
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
return { offers: transformedResults, nextCursor };
|
|
2267
|
+
},
|
|
2268
|
+
/**
|
|
2269
|
+
* Returns offers that match specified offer based on the provided parameters.
|
|
2270
|
+
*
|
|
2271
|
+
* Rules for filtering:
|
|
2272
|
+
* - If offer expiry is in the past compared to now, it should not be included
|
|
2273
|
+
* - If offer maturity is in the past compared to now, it should not be included
|
|
2274
|
+
* - It should return if buyOffer.rate >= sellOffer.rate
|
|
2275
|
+
* - It should return if buyOffer.userAddress != sellOffer.userAddress
|
|
2276
|
+
* - It should return if maturity matches
|
|
2277
|
+
* - It should return if loanToken matches
|
|
2278
|
+
* - If the incoming intent is a buy / lend offer, only consider existing sell / borrow offers.
|
|
2279
|
+
* - If the incoming intent is a sell / borrow offer, only consider existing buy / lend offers.
|
|
2280
|
+
* - When the intent is lend: require offer_collaterals ⊆ intent_collaterals
|
|
2281
|
+
* - When the intent is borrow: require intent_collaterals ⊆ offer_collaterals
|
|
2282
|
+
* - It should sort by increasing rate if the input offer is a buy/lend offer
|
|
2283
|
+
* - It should sort by decreasing rate if the input offer is a sell/borrow offer
|
|
2284
|
+
*/
|
|
2285
|
+
findMatchingOffers: async (params) => {
|
|
2286
|
+
const {
|
|
2287
|
+
side,
|
|
2288
|
+
chainId,
|
|
2289
|
+
rate,
|
|
2290
|
+
collaterals = [],
|
|
2291
|
+
maturity,
|
|
2292
|
+
minMaturity,
|
|
2293
|
+
maxMaturity,
|
|
2294
|
+
loanToken,
|
|
2295
|
+
creator,
|
|
2296
|
+
cursor,
|
|
2297
|
+
limit = 20
|
|
2298
|
+
} = params;
|
|
2299
|
+
const isIncomingBuy = side === "buy";
|
|
2300
|
+
const nowEpochSeconds = Math.floor(Date.now() / 1e3);
|
|
2301
|
+
const rateSortDirection = isIncomingBuy ? "desc" : "asc";
|
|
2302
|
+
const baseWhereClauses = [];
|
|
2303
|
+
baseWhereClauses.push(drizzleOrm.eq(offers.buy, !isIncomingBuy));
|
|
2304
|
+
baseWhereClauses.push(drizzleOrm.eq(offers.chainId, BigInt(chainId)));
|
|
2305
|
+
baseWhereClauses.push(drizzleOrm.gte(offers.expiry, nowEpochSeconds));
|
|
2306
|
+
baseWhereClauses.push(drizzleOrm.gte(offers.maturity, nowEpochSeconds));
|
|
2307
|
+
if (maturity) baseWhereClauses.push(drizzleOrm.eq(offers.maturity, maturity));
|
|
2308
|
+
if (minMaturity) baseWhereClauses.push(drizzleOrm.gte(offers.maturity, minMaturity));
|
|
2309
|
+
if (maxMaturity) baseWhereClauses.push(drizzleOrm.lte(offers.maturity, maxMaturity));
|
|
2310
|
+
if (loanToken) baseWhereClauses.push(drizzleOrm.eq(offers.loanToken, loanToken.toLowerCase()));
|
|
2311
|
+
if (creator) baseWhereClauses.push(drizzleOrm.eq(offers.offering, creator.toLowerCase()));
|
|
2312
|
+
if (rate)
|
|
2313
|
+
baseWhereClauses.push(isIncomingBuy ? drizzleOrm.gte(offers.rate, rate) : drizzleOrm.lte(offers.rate, rate));
|
|
2314
|
+
const parsedCursor = decode(cursor);
|
|
2315
|
+
if (parsedCursor) {
|
|
2316
|
+
if (parsedCursor.sort !== "rate" || parsedCursor.dir !== rateSortDirection) {
|
|
2317
|
+
throw new Error("Cursor does not match the current sort parameters");
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
if (collaterals.length > 0) {
|
|
2321
|
+
const collateralValues = collaterals.map(
|
|
2322
|
+
(c) => `('${c.asset.toLowerCase()}', '${c.oracle.toLowerCase()}', ${c.lltv.toString()})`
|
|
2323
|
+
).join(", ");
|
|
2324
|
+
const userCollaterals = drizzleOrm.sql`(VALUES ${drizzleOrm.sql.raw(collateralValues)}) AS user_collaterals(asset, oracle, lltv)`;
|
|
2325
|
+
const sellPredicate = drizzleOrm.sql`
|
|
2326
|
+
NOT EXISTS (
|
|
2327
|
+
SELECT 1 FROM ${userCollaterals}
|
|
2328
|
+
WHERE NOT EXISTS (
|
|
2329
|
+
SELECT 1 FROM ${offerCollaterals} offer_collaterals
|
|
2330
|
+
WHERE offer_collaterals.offer_hash = ${offers.hash}
|
|
2331
|
+
AND offer_collaterals.asset = user_collaterals.asset
|
|
2332
|
+
AND offer_collaterals.oracle = user_collaterals.oracle
|
|
2333
|
+
AND offer_collaterals.lltv = user_collaterals.lltv
|
|
2334
|
+
)
|
|
2335
|
+
)
|
|
2336
|
+
`;
|
|
2337
|
+
const buyPredicate = drizzleOrm.sql`
|
|
2338
|
+
NOT EXISTS (
|
|
2339
|
+
SELECT 1 FROM ${offerCollaterals} offer_collaterals
|
|
2340
|
+
WHERE offer_collaterals.offer_hash = ${offers.hash}
|
|
2341
|
+
AND NOT EXISTS (
|
|
2342
|
+
SELECT 1 FROM ${userCollaterals}
|
|
2343
|
+
WHERE user_collaterals.asset = offer_collaterals.asset
|
|
2344
|
+
AND user_collaterals.oracle = offer_collaterals.oracle
|
|
2345
|
+
AND user_collaterals.lltv = offer_collaterals.lltv
|
|
2346
|
+
)
|
|
2347
|
+
)
|
|
2348
|
+
`;
|
|
2349
|
+
baseWhereClauses.push(isIncomingBuy ? buyPredicate : sellPredicate);
|
|
2350
|
+
}
|
|
2351
|
+
const latestStatus = db.select({
|
|
2352
|
+
status: offerStatus.status,
|
|
2353
|
+
metadata: offerStatus.metadata
|
|
2354
|
+
}).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, offers.hash)).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1).as("latest_status");
|
|
2355
|
+
const sumConsumed = db.select({
|
|
2356
|
+
consumed: drizzleOrm.sql`COALESCE(SUM(${consumed.consumed}), 0)`.as("consumed")
|
|
2357
|
+
}).from(consumed).where(
|
|
2358
|
+
drizzleOrm.and(
|
|
2359
|
+
drizzleOrm.eq(consumed.offering, offers.offering),
|
|
2360
|
+
drizzleOrm.eq(consumed.nonce, offers.nonce),
|
|
2361
|
+
drizzleOrm.eq(consumed.chainId, offers.chainId)
|
|
2362
|
+
)
|
|
2363
|
+
).as("sum_consumed");
|
|
2364
|
+
const statusCondition = drizzleOrm.eq(latestStatus.status, "valid");
|
|
2365
|
+
const bestOffers = db.selectDistinctOn(
|
|
2366
|
+
// group key
|
|
2367
|
+
[offers.offering, offers.nonce, offers.buy],
|
|
2368
|
+
{
|
|
2369
|
+
hash: offers.hash,
|
|
2370
|
+
offering: offers.offering,
|
|
2371
|
+
assets: offers.assets,
|
|
2372
|
+
consumed: sumConsumed.consumed,
|
|
2373
|
+
remaining: drizzleOrm.sql`${offers.assets} - COALESCE(${sumConsumed.consumed}, 0)`.as("remaining"),
|
|
2374
|
+
rate: offers.rate,
|
|
2375
|
+
maturity: offers.maturity,
|
|
2376
|
+
expiry: offers.expiry,
|
|
2377
|
+
start: offers.start,
|
|
2378
|
+
nonce: offers.nonce,
|
|
2379
|
+
buy: offers.buy,
|
|
2380
|
+
chainId: offers.chainId,
|
|
2381
|
+
loanToken: offers.loanToken,
|
|
2382
|
+
callbackAddress: offers.callbackAddress,
|
|
2383
|
+
callbackData: offers.callbackData,
|
|
2384
|
+
callbackGasLimit: offers.callbackGasLimit,
|
|
2385
|
+
signature: offers.signature,
|
|
2386
|
+
callbackId: offers.callbackId,
|
|
2387
|
+
status: latestStatus.status,
|
|
2388
|
+
metadata: latestStatus.metadata
|
|
2389
|
+
}
|
|
2390
|
+
).from(offers).leftJoinLateral(latestStatus, drizzleOrm.sql`true`).leftJoinLateral(sumConsumed, drizzleOrm.sql`true`).where(
|
|
2391
|
+
drizzleOrm.and(
|
|
2392
|
+
drizzleOrm.and(...baseWhereClauses),
|
|
2393
|
+
statusCondition,
|
|
2394
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) > 0`
|
|
2395
|
+
)
|
|
2396
|
+
).orderBy(
|
|
2397
|
+
offers.offering,
|
|
2398
|
+
offers.nonce,
|
|
2399
|
+
offers.buy,
|
|
2400
|
+
// 1 price (direction depends on side)
|
|
2401
|
+
drizzleOrm.sql`CASE WHEN ${offers.buy} THEN ${offers.rate} ELSE -${offers.rate} END`,
|
|
2402
|
+
// 2 size (remaining)
|
|
2403
|
+
drizzleOrm.sql`( ${offers.assets} - COALESCE(${sumConsumed.consumed}, 0) ) DESC`,
|
|
2404
|
+
// 3 term (longer maturity)
|
|
2405
|
+
drizzleOrm.desc(offers.maturity)
|
|
2406
|
+
).as("best_offers");
|
|
2407
|
+
const queueLiquidity = db.select({
|
|
2408
|
+
callbackId: userPositions.id,
|
|
2409
|
+
userAmount: userPositions.amount,
|
|
2410
|
+
// user's per-callback cap
|
|
2411
|
+
queueLiquidity: drizzleOrm.sql`COALESCE(SUM(${availableLiquidityPools.amount}), 0)`.as(
|
|
2412
|
+
"queue_liquidity"
|
|
2413
|
+
)
|
|
2414
|
+
}).from(userPositions).leftJoin(
|
|
2415
|
+
availableLiquidityQueues,
|
|
2416
|
+
drizzleOrm.eq(userPositions.availableLiquidityQueueId, availableLiquidityQueues.queueId)
|
|
2417
|
+
).leftJoin(
|
|
2418
|
+
availableLiquidityPools,
|
|
2419
|
+
drizzleOrm.eq(availableLiquidityQueues.availableLiquidityPoolId, availableLiquidityPools.id)
|
|
2420
|
+
).groupBy(userPositions.id).as("queue_liquidity");
|
|
2421
|
+
const sortExpr = drizzleOrm.sql`CASE WHEN ${bestOffers.buy} THEN ${bestOffers.rate} ELSE -${bestOffers.rate} END`;
|
|
2422
|
+
const offersWithEligibility = db.select({
|
|
2423
|
+
hash: bestOffers.hash,
|
|
2424
|
+
offering: bestOffers.offering,
|
|
2425
|
+
assets: bestOffers.assets,
|
|
2426
|
+
consumed: bestOffers.consumed,
|
|
2427
|
+
remaining: bestOffers.remaining,
|
|
2428
|
+
rate: bestOffers.rate,
|
|
2429
|
+
maturity: bestOffers.maturity,
|
|
2430
|
+
expiry: bestOffers.expiry,
|
|
2431
|
+
start: bestOffers.start,
|
|
2432
|
+
nonce: bestOffers.nonce,
|
|
2433
|
+
buy: bestOffers.buy,
|
|
2434
|
+
chainId: bestOffers.chainId,
|
|
2435
|
+
loanToken: bestOffers.loanToken,
|
|
2436
|
+
callbackAddress: bestOffers.callbackAddress,
|
|
2437
|
+
callbackData: bestOffers.callbackData,
|
|
2438
|
+
callbackGasLimit: bestOffers.callbackGasLimit,
|
|
2439
|
+
signature: bestOffers.signature,
|
|
2440
|
+
callbackId: bestOffers.callbackId,
|
|
2441
|
+
status: bestOffers.status,
|
|
2442
|
+
metadata: bestOffers.metadata,
|
|
2443
|
+
// liquidity caps
|
|
2444
|
+
userAmount: drizzleOrm.sql`COALESCE(${queueLiquidity.userAmount}, 0)`.as("user_amount"),
|
|
2445
|
+
queueLiquidity: drizzleOrm.sql`COALESCE(${queueLiquidity.queueLiquidity}, 0)`.as(
|
|
2446
|
+
"queue_liquidity"
|
|
2447
|
+
),
|
|
2448
|
+
// running total of remaining per callback, ordered by the *same* priority as selection
|
|
2449
|
+
cumulativeRemaining: drizzleOrm.sql`
|
|
2450
|
+
SUM(${bestOffers.remaining}) OVER (
|
|
2451
|
+
PARTITION BY ${bestOffers.callbackId}
|
|
2452
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2453
|
+
ROWS UNBOUNDED PRECEDING
|
|
2454
|
+
)
|
|
2455
|
+
`.as("cumulative_remaining"),
|
|
2456
|
+
eligible: drizzleOrm.sql`
|
|
2457
|
+
CASE
|
|
2458
|
+
WHEN ${bestOffers.buy} = false THEN true
|
|
2459
|
+
WHEN ${bestOffers.remaining} <= 0 THEN false
|
|
2460
|
+
ELSE ( SUM(${bestOffers.remaining}) OVER (
|
|
2461
|
+
PARTITION BY ${bestOffers.callbackId}
|
|
2462
|
+
ORDER BY ${sortExpr}, ${drizzleOrm.asc(bestOffers.hash)}
|
|
2463
|
+
ROWS UNBOUNDED PRECEDING
|
|
2464
|
+
)
|
|
2465
|
+
<= LEAST(
|
|
2466
|
+
COALESCE(${queueLiquidity.userAmount}, 0),
|
|
2467
|
+
COALESCE(${queueLiquidity.queueLiquidity}, 0)
|
|
2468
|
+
)
|
|
2469
|
+
)
|
|
2470
|
+
END
|
|
2471
|
+
`.as("eligible")
|
|
2472
|
+
}).from(bestOffers).leftJoin(queueLiquidity, drizzleOrm.eq(bestOffers.callbackId, queueLiquidity.callbackId)).as("offers_with_eligibility");
|
|
2473
|
+
const hardCap = limit * 5 + 1 + (parsedCursor ? 1 : 0);
|
|
2474
|
+
const validOffers = db.select({
|
|
2475
|
+
// pass-through all fields + a global row_number for hard cap
|
|
2476
|
+
hash: offersWithEligibility.hash,
|
|
2477
|
+
offering: offersWithEligibility.offering,
|
|
2478
|
+
assets: offersWithEligibility.assets,
|
|
2479
|
+
consumed: offersWithEligibility.consumed,
|
|
2480
|
+
remaining: offersWithEligibility.remaining,
|
|
2481
|
+
rate: offersWithEligibility.rate,
|
|
2482
|
+
maturity: offersWithEligibility.maturity,
|
|
2483
|
+
expiry: offersWithEligibility.expiry,
|
|
2484
|
+
start: offersWithEligibility.start,
|
|
2485
|
+
nonce: offersWithEligibility.nonce,
|
|
2486
|
+
buy: offersWithEligibility.buy,
|
|
2487
|
+
chainId: offersWithEligibility.chainId,
|
|
2488
|
+
loanToken: offersWithEligibility.loanToken,
|
|
2489
|
+
callbackAddress: offersWithEligibility.callbackAddress,
|
|
2490
|
+
callbackData: offersWithEligibility.callbackData,
|
|
2491
|
+
callbackGasLimit: offersWithEligibility.callbackGasLimit,
|
|
2492
|
+
signature: offersWithEligibility.signature,
|
|
2493
|
+
callbackId: offersWithEligibility.callbackId,
|
|
2494
|
+
status: offersWithEligibility.status,
|
|
2495
|
+
metadata: offersWithEligibility.metadata,
|
|
2496
|
+
userAmount: offersWithEligibility.userAmount,
|
|
2497
|
+
queueLiquidity: offersWithEligibility.queueLiquidity,
|
|
2498
|
+
cumulativeRemaining: offersWithEligibility.cumulativeRemaining,
|
|
2499
|
+
eligible: offersWithEligibility.eligible,
|
|
2500
|
+
// sort expression is the same again as in offersWithEligibility
|
|
2501
|
+
rowNumber: drizzleOrm.sql`
|
|
2502
|
+
ROW_NUMBER() OVER (
|
|
2503
|
+
ORDER BY
|
|
2504
|
+
CASE WHEN ${offersWithEligibility.buy} THEN ${offersWithEligibility.rate} ELSE -${offersWithEligibility.rate} END,
|
|
2505
|
+
${drizzleOrm.asc(offersWithEligibility.hash)}
|
|
2506
|
+
)
|
|
2507
|
+
`.as("row_number")
|
|
2508
|
+
}).from(offersWithEligibility).where(drizzleOrm.sql`${offersWithEligibility.eligible} = true`).limit(hardCap).as("valid_offers");
|
|
2509
|
+
const cursorTuple = parsedCursor ? {
|
|
2510
|
+
rate: BigInt(parsedCursor.rate),
|
|
2511
|
+
hash: parsedCursor.hash
|
|
2512
|
+
} : null;
|
|
2513
|
+
const withCollats = await db.select({
|
|
2514
|
+
// base fields
|
|
2515
|
+
hash: drizzleOrm.sql`${validOffers.hash}`,
|
|
2516
|
+
offering: drizzleOrm.sql`${validOffers.offering}`,
|
|
2517
|
+
assets: validOffers.assets,
|
|
2518
|
+
consumed: validOffers.consumed,
|
|
2519
|
+
rate: validOffers.rate,
|
|
2520
|
+
maturity: validOffers.maturity,
|
|
2521
|
+
expiry: validOffers.expiry,
|
|
2522
|
+
start: validOffers.start,
|
|
2523
|
+
nonce: validOffers.nonce,
|
|
2524
|
+
buy: validOffers.buy,
|
|
2525
|
+
chainId: validOffers.chainId,
|
|
2526
|
+
loanToken: drizzleOrm.sql`${validOffers.loanToken}`,
|
|
2527
|
+
callbackAddress: drizzleOrm.sql`${validOffers.callbackAddress}`,
|
|
2528
|
+
callbackData: drizzleOrm.sql`${validOffers.callbackData}`,
|
|
2529
|
+
callbackGasLimit: validOffers.callbackGasLimit,
|
|
2530
|
+
signature: drizzleOrm.sql`${validOffers.signature}`,
|
|
2531
|
+
callbackId: validOffers.callbackId,
|
|
2532
|
+
status: drizzleOrm.sql`${validOffers.status}`,
|
|
2533
|
+
metadata: validOffers.metadata,
|
|
2534
|
+
// collateral fields
|
|
2535
|
+
collateralAsset: drizzleOrm.sql`${offerCollaterals.asset}`,
|
|
2536
|
+
collateralOracle: drizzleOrm.sql`${offerCollaterals.oracle}`,
|
|
2537
|
+
collateralLltv: offerCollaterals.lltv
|
|
2538
|
+
}).from(validOffers).leftJoin(offerCollaterals, drizzleOrm.eq(validOffers.hash, offerCollaterals.offerHash)).where(
|
|
2539
|
+
drizzleOrm.and(
|
|
2540
|
+
...cursorTuple ? [
|
|
2541
|
+
drizzleOrm.sql`(${validOffers.rate}, ${validOffers.hash}) ${rateSortDirection === "asc" ? drizzleOrm.sql`>` : drizzleOrm.sql`<`} (${cursorTuple.rate}, ${cursorTuple.hash})`
|
|
2542
|
+
] : []
|
|
2543
|
+
)
|
|
2544
|
+
).orderBy(
|
|
2545
|
+
rateSortDirection === "asc" ? drizzleOrm.asc(validOffers.rate) : drizzleOrm.desc(validOffers.rate),
|
|
2546
|
+
drizzleOrm.asc(validOffers.hash)
|
|
2547
|
+
);
|
|
2548
|
+
const buildOffersMap = (rows, skipHash) => {
|
|
2549
|
+
const map = /* @__PURE__ */ new Map();
|
|
2550
|
+
for (const row of rows) {
|
|
2551
|
+
const entry = map.get(row.hash) ?? { base: row, collaterals: [] };
|
|
2552
|
+
if (row.collateralAsset && row.collateralOracle && row.collateralLltv) {
|
|
2553
|
+
entry.collaterals.push({
|
|
2554
|
+
asset: row.collateralAsset,
|
|
2555
|
+
oracle: row.collateralOracle,
|
|
2556
|
+
lltv: mempool.LLTV.from(row.collateralLltv)
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
map.set(row.hash, entry);
|
|
2560
|
+
}
|
|
2561
|
+
return map;
|
|
2562
|
+
};
|
|
2563
|
+
const allEntries = Array.from(buildOffersMap(withCollats).values());
|
|
2564
|
+
const pageEntries = allEntries.slice(0, limit);
|
|
2565
|
+
let nextCursor = null;
|
|
2566
|
+
if (allEntries.length > limit) {
|
|
2567
|
+
const last = pageEntries[pageEntries.length - 1].base;
|
|
2568
|
+
nextCursor = encode({
|
|
2569
|
+
sort: "rate",
|
|
2570
|
+
dir: rateSortDirection,
|
|
2571
|
+
hash: last.hash,
|
|
2572
|
+
rate: last.rate.toString()
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
const data = pageEntries.map(({ base, collaterals: cols }) => ({
|
|
2576
|
+
...mempool.Offer.from({
|
|
2577
|
+
offering: base.offering,
|
|
2578
|
+
assets: BigInt(base.assets),
|
|
2579
|
+
rate: base.rate,
|
|
2580
|
+
maturity: mempool.Maturity.from(base.maturity),
|
|
2581
|
+
expiry: base.expiry,
|
|
2582
|
+
start: base.start,
|
|
2583
|
+
nonce: base.nonce,
|
|
2584
|
+
buy: base.buy,
|
|
2585
|
+
chainId: base.chainId,
|
|
2586
|
+
loanToken: base.loanToken,
|
|
2587
|
+
collaterals: cols.sort(
|
|
2588
|
+
(a, b) => a.asset.toLowerCase().localeCompare(b.asset.toLowerCase())
|
|
2589
|
+
),
|
|
2590
|
+
callback: {
|
|
2591
|
+
address: base.callbackAddress,
|
|
2592
|
+
data: base.callbackData,
|
|
2593
|
+
gasLimit: base.callbackGasLimit
|
|
2594
|
+
},
|
|
2595
|
+
...base.signature !== null ? { signature: base.signature } : void 0
|
|
2596
|
+
}),
|
|
2597
|
+
consumed: BigInt(base.consumed),
|
|
2598
|
+
status: base.status,
|
|
2599
|
+
...base.metadata ? { metadata: base.metadata } : {}
|
|
2600
|
+
}));
|
|
2601
|
+
return { offers: data, nextCursor };
|
|
1768
2602
|
},
|
|
1769
|
-
|
|
1770
|
-
|
|
2603
|
+
delete: async (hash) => {
|
|
2604
|
+
const result = await db.delete(offers).where(drizzleOrm.eq(offers.hash, hash.toLowerCase()));
|
|
2605
|
+
return result.affectedRows > 0;
|
|
2606
|
+
},
|
|
2607
|
+
deleteMany: async (hashes) => {
|
|
2608
|
+
if (hashes.length === 0) {
|
|
2609
|
+
return 0;
|
|
2610
|
+
}
|
|
2611
|
+
return await db.transaction(async (tx) => {
|
|
2612
|
+
const normalizedHashes = hashes.map((hash) => hash.toLowerCase());
|
|
2613
|
+
const result = await tx.delete(offers).where(
|
|
2614
|
+
drizzleOrm.sql`${offers.hash} = ANY(${drizzleOrm.sql.raw(`ARRAY[${normalizedHashes.map((h) => `'${h}'`).join(", ")}]`)})`
|
|
2615
|
+
);
|
|
2616
|
+
return result.affectedRows;
|
|
2617
|
+
});
|
|
1771
2618
|
},
|
|
1772
|
-
|
|
1773
|
-
|
|
2619
|
+
updateStatus: async (parameters) => {
|
|
2620
|
+
const latest = await db.select({ status: offerStatus.status }).from(offerStatus).where(drizzleOrm.eq(offerStatus.offerHash, parameters.offerHash.toLowerCase())).orderBy(drizzleOrm.desc(offerStatus.createdAt)).limit(1);
|
|
2621
|
+
if (latest.length > 0 && latest[0].status === parameters.status) return;
|
|
2622
|
+
await db.insert(offerStatus).values({
|
|
2623
|
+
offerHash: parameters.offerHash.toLowerCase(),
|
|
2624
|
+
status: parameters.status,
|
|
2625
|
+
metadata: parameters.metadata
|
|
2626
|
+
});
|
|
1774
2627
|
},
|
|
1775
|
-
|
|
2628
|
+
updateConsumedAmount: async (parameters) => {
|
|
2629
|
+
await db.insert(consumed).values({
|
|
2630
|
+
id: parameters.id,
|
|
2631
|
+
chainId: parameters.chainId,
|
|
2632
|
+
offering: parameters.offering.toLowerCase(),
|
|
2633
|
+
nonce: parameters.nonce,
|
|
2634
|
+
consumed: parameters.consumed.toString()
|
|
2635
|
+
}).onConflictDoNothing();
|
|
2636
|
+
}
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// src/core/OfferStore/PG.ts
|
|
2641
|
+
var PG_exports = {};
|
|
2642
|
+
__export(PG_exports, {
|
|
2643
|
+
applyMigrations: () => applyMigrations,
|
|
2644
|
+
clean: () => clean,
|
|
2645
|
+
connect: () => connect
|
|
2646
|
+
});
|
|
2647
|
+
function connect(parameters) {
|
|
2648
|
+
if (parameters.type === "pg") {
|
|
2649
|
+
const pool2 = new pg.Pool({ connectionString: parameters.endpoint });
|
|
2650
|
+
const client2 = nodePostgres.drizzle(pool2, { schema: schema_exports });
|
|
2651
|
+
return Object.assign(client2, { name: "pg", pool: pool2 });
|
|
2652
|
+
}
|
|
2653
|
+
const pool = new pglite.PGlite();
|
|
2654
|
+
const client = pglite$1.drizzle(pool, { schema: schema_exports });
|
|
2655
|
+
return Object.assign(client, { name: "pglite", pool });
|
|
2656
|
+
}
|
|
2657
|
+
async function applyMigrations(pg) {
|
|
2658
|
+
const migrationsFolder = process.env.AWS_LAMBDA_FUNCTION_NAME ? path__default.default.join(process.cwd(), "drizzle", VERSION) : path__default.default.join(__dirname, "drizzle", VERSION);
|
|
2659
|
+
await pg.execute(`create schema if not exists "${VERSION}"`);
|
|
2660
|
+
if (pg.name === "pg") {
|
|
2661
|
+
await migrator.migrate(pg, { migrationsFolder });
|
|
2662
|
+
return;
|
|
2663
|
+
}
|
|
2664
|
+
await migrator$1.migrate(pg, { migrationsFolder });
|
|
2665
|
+
}
|
|
2666
|
+
async function clean(pg) {
|
|
2667
|
+
await pg.execute(`drop schema if exists "${VERSION}" cascade`);
|
|
2668
|
+
await pg.execute(`create schema "${VERSION}"`);
|
|
2669
|
+
await pg.execute("drop schema if exists drizzle cascade");
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/core/router/index.ts
|
|
2673
|
+
var router_exports = {};
|
|
2674
|
+
__export(router_exports, {
|
|
2675
|
+
APIError: () => APIError,
|
|
2676
|
+
BadRequestError: () => BadRequestError,
|
|
2677
|
+
HttpForbiddenError: () => HttpForbiddenError,
|
|
2678
|
+
HttpGetOffersFailedError: () => HttpGetOffersFailedError,
|
|
2679
|
+
HttpRateLimitError: () => HttpRateLimitError,
|
|
2680
|
+
HttpUnauthorizedError: () => HttpUnauthorizedError,
|
|
2681
|
+
InternalServerError: () => InternalServerError,
|
|
2682
|
+
InvalidUrlError: () => InvalidUrlError,
|
|
2683
|
+
NotFoundError: () => NotFoundError,
|
|
2684
|
+
ValidationError: () => ValidationError,
|
|
2685
|
+
connect: () => connect2,
|
|
2686
|
+
error: () => error,
|
|
2687
|
+
get: () => get,
|
|
2688
|
+
handleZodError: () => handleZodError,
|
|
2689
|
+
match: () => match,
|
|
2690
|
+
serve: () => serve,
|
|
2691
|
+
success: () => success
|
|
2692
|
+
});
|
|
2693
|
+
function connect2(opts) {
|
|
2694
|
+
const u = new URL(opts?.url || "https://router.morpho.dev");
|
|
2695
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
2696
|
+
throw new InvalidUrlError(u.toString());
|
|
2697
|
+
}
|
|
2698
|
+
const headers = opts?.headers ?? new Headers();
|
|
2699
|
+
headers.set("Content-Type", "application/json");
|
|
2700
|
+
opts?.apiKey !== void 0 ? headers.set("X-API-Key", opts.apiKey) : null;
|
|
2701
|
+
const config = {
|
|
2702
|
+
url: u,
|
|
2703
|
+
headers
|
|
2704
|
+
};
|
|
2705
|
+
return {
|
|
2706
|
+
...config,
|
|
2707
|
+
get: (parameters) => get(config, parameters),
|
|
2708
|
+
match: (parameters) => match(config, parameters)
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
async function get(config, parameters) {
|
|
2712
|
+
const url = new URL(`${config.url.toString()}v1/offers`);
|
|
2713
|
+
if (parameters.creators?.length) {
|
|
2714
|
+
url.searchParams.set("creators", parameters.creators.join(","));
|
|
2715
|
+
}
|
|
2716
|
+
if (parameters.side) {
|
|
2717
|
+
url.searchParams.set("side", parameters.side);
|
|
2718
|
+
}
|
|
2719
|
+
if (parameters.chains?.length) {
|
|
2720
|
+
url.searchParams.set("chains", parameters.chains.join(","));
|
|
2721
|
+
}
|
|
2722
|
+
if (parameters.loanTokens?.length) {
|
|
2723
|
+
url.searchParams.set("loan_tokens", parameters.loanTokens.join(","));
|
|
2724
|
+
}
|
|
2725
|
+
if (parameters.status?.length) {
|
|
2726
|
+
url.searchParams.set("status", parameters.status.join(","));
|
|
2727
|
+
}
|
|
2728
|
+
if (parameters.callbackAddresses?.length) {
|
|
2729
|
+
url.searchParams.set("callback_addresses", parameters.callbackAddresses.join(","));
|
|
2730
|
+
}
|
|
2731
|
+
if (parameters.minAmount !== void 0) {
|
|
2732
|
+
url.searchParams.set("min_amount", parameters.minAmount.toString());
|
|
2733
|
+
}
|
|
2734
|
+
if (parameters.maxAmount !== void 0) {
|
|
2735
|
+
url.searchParams.set("max_amount", parameters.maxAmount.toString());
|
|
2736
|
+
}
|
|
2737
|
+
if (parameters.minRate !== void 0) {
|
|
2738
|
+
url.searchParams.set("min_rate", parameters.minRate.toString());
|
|
2739
|
+
}
|
|
2740
|
+
if (parameters.maxRate !== void 0) {
|
|
2741
|
+
url.searchParams.set("max_rate", parameters.maxRate.toString());
|
|
2742
|
+
}
|
|
2743
|
+
if (parameters.minMaturity !== void 0) {
|
|
2744
|
+
url.searchParams.set("min_maturity", parameters.minMaturity.toString());
|
|
2745
|
+
}
|
|
2746
|
+
if (parameters.maxMaturity !== void 0) {
|
|
2747
|
+
url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
|
|
2748
|
+
}
|
|
2749
|
+
if (parameters.minExpiry !== void 0) {
|
|
2750
|
+
url.searchParams.set("min_expiry", parameters.minExpiry.toString());
|
|
2751
|
+
}
|
|
2752
|
+
if (parameters.maxExpiry !== void 0) {
|
|
2753
|
+
url.searchParams.set("max_expiry", parameters.maxExpiry.toString());
|
|
2754
|
+
}
|
|
2755
|
+
if (parameters.collateralAssets?.length) {
|
|
2756
|
+
url.searchParams.set("collateral_assets", parameters.collateralAssets.join(","));
|
|
2757
|
+
}
|
|
2758
|
+
if (parameters.collateralOracles?.length) {
|
|
2759
|
+
url.searchParams.set("collateral_oracles", parameters.collateralOracles.join(","));
|
|
2760
|
+
}
|
|
2761
|
+
if (parameters.collateralTuple?.length) {
|
|
2762
|
+
const tupleStr = parameters.collateralTuple.map(({ asset, oracle, lltv }) => {
|
|
2763
|
+
let result = asset;
|
|
2764
|
+
if (oracle) {
|
|
2765
|
+
result += `:${oracle}`;
|
|
2766
|
+
} else if (lltv !== void 0) {
|
|
2767
|
+
result += `:`;
|
|
2768
|
+
}
|
|
2769
|
+
if (lltv !== void 0) result += `:${viem.formatUnits(lltv, 16)}`;
|
|
2770
|
+
return result;
|
|
2771
|
+
}).join("#");
|
|
2772
|
+
url.searchParams.set("collateral_tuple", tupleStr);
|
|
2773
|
+
}
|
|
2774
|
+
if (parameters.minLltv !== void 0) {
|
|
2775
|
+
url.searchParams.set("min_lltv", viem.formatUnits(parameters.minLltv, 16));
|
|
2776
|
+
}
|
|
2777
|
+
if (parameters.maxLltv !== void 0) {
|
|
2778
|
+
url.searchParams.set("max_lltv", viem.formatUnits(parameters.maxLltv, 16));
|
|
2779
|
+
}
|
|
2780
|
+
if (parameters.sortBy) {
|
|
2781
|
+
url.searchParams.set("sort_by", parameters.sortBy);
|
|
2782
|
+
}
|
|
2783
|
+
if (parameters.sortOrder) {
|
|
2784
|
+
url.searchParams.set("sort_order", parameters.sortOrder);
|
|
2785
|
+
}
|
|
2786
|
+
if (parameters.cursor) {
|
|
2787
|
+
url.searchParams.set("cursor", parameters.cursor);
|
|
2788
|
+
}
|
|
2789
|
+
if (parameters.limit !== void 0) {
|
|
2790
|
+
url.searchParams.set("limit", parameters.limit.toString());
|
|
2791
|
+
}
|
|
2792
|
+
const { cursor: returnedCursor, data: offers2 } = await getApi(config, url);
|
|
2793
|
+
const routerOffers = offers2.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
2794
|
+
return {
|
|
2795
|
+
cursor: returnedCursor,
|
|
2796
|
+
offers: routerOffers.map(from).map(toResponse)
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
async function match(config, parameters) {
|
|
2800
|
+
const url = new URL(`${config.url.toString()}v1/offers/match`);
|
|
2801
|
+
url.searchParams.set("side", parameters.side);
|
|
2802
|
+
url.searchParams.set("chain_id", parameters.chainId.toString());
|
|
2803
|
+
if (parameters.rate !== void 0) {
|
|
2804
|
+
url.searchParams.set("rate", parameters.rate.toString());
|
|
2805
|
+
}
|
|
2806
|
+
if (parameters.collaterals?.length) {
|
|
2807
|
+
const collateralsStr = parameters.collaterals.map(({ asset, oracle, lltv }) => `${asset}:${oracle}:${viem.formatUnits(lltv, 16)}`).join("#");
|
|
2808
|
+
url.searchParams.set("collaterals", collateralsStr);
|
|
2809
|
+
}
|
|
2810
|
+
if (parameters.maturity !== void 0) {
|
|
2811
|
+
url.searchParams.set("maturity", parameters.maturity.toString());
|
|
2812
|
+
}
|
|
2813
|
+
if (parameters.minMaturity !== void 0) {
|
|
2814
|
+
url.searchParams.set("min_maturity", parameters.minMaturity.toString());
|
|
2815
|
+
}
|
|
2816
|
+
if (parameters.maxMaturity !== void 0) {
|
|
2817
|
+
url.searchParams.set("max_maturity", parameters.maxMaturity.toString());
|
|
2818
|
+
}
|
|
2819
|
+
if (parameters.loanToken) {
|
|
2820
|
+
url.searchParams.set("loan_token", parameters.loanToken);
|
|
2821
|
+
}
|
|
2822
|
+
if (parameters.creator) {
|
|
2823
|
+
url.searchParams.set("creator", parameters.creator);
|
|
2824
|
+
}
|
|
2825
|
+
if (parameters.status?.length) {
|
|
2826
|
+
url.searchParams.set("status", parameters.status.join(","));
|
|
2827
|
+
}
|
|
2828
|
+
if (parameters.cursor) {
|
|
2829
|
+
url.searchParams.set("cursor", parameters.cursor);
|
|
2830
|
+
}
|
|
2831
|
+
if (parameters.limit !== void 0) {
|
|
2832
|
+
url.searchParams.set("limit", parameters.limit.toString());
|
|
2833
|
+
}
|
|
2834
|
+
const { cursor: returnedCursor, data: offers2 } = await getApi(config, url);
|
|
2835
|
+
const routerOffers = offers2.map(mempool.Format.fromSnakeCase).map(fromResponse);
|
|
2836
|
+
return {
|
|
2837
|
+
cursor: returnedCursor,
|
|
2838
|
+
offers: routerOffers.map(from).map(toResponse)
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
async function getApi(config, url) {
|
|
2842
|
+
const pathname = url.pathname;
|
|
2843
|
+
let action;
|
|
2844
|
+
switch (true) {
|
|
2845
|
+
case pathname.includes("/v1/offers/match"):
|
|
2846
|
+
action = "match_offers";
|
|
2847
|
+
break;
|
|
2848
|
+
case pathname.includes("/v1/offers"):
|
|
2849
|
+
action = "get_offers";
|
|
2850
|
+
break;
|
|
2851
|
+
default:
|
|
2852
|
+
throw new HttpGetOffersFailedError("Unknown endpoint", {
|
|
2853
|
+
details: `Unsupported path: ${pathname}`
|
|
2854
|
+
});
|
|
2855
|
+
}
|
|
2856
|
+
const schemaParseResult = safeParse(action, Object.fromEntries(url.searchParams));
|
|
2857
|
+
if (!schemaParseResult.success) {
|
|
2858
|
+
throw new HttpGetOffersFailedError(`Invalid URL parameters`, {
|
|
2859
|
+
details: schemaParseResult.error.issues[0]?.message
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
const response = await fetch(url.toString(), {
|
|
2863
|
+
method: "GET",
|
|
2864
|
+
headers: config.headers
|
|
2865
|
+
});
|
|
2866
|
+
if (!response.ok) {
|
|
2867
|
+
switch (response.status) {
|
|
2868
|
+
case 401:
|
|
2869
|
+
throw new HttpUnauthorizedError();
|
|
2870
|
+
case 403:
|
|
2871
|
+
throw new HttpForbiddenError();
|
|
2872
|
+
case 429:
|
|
2873
|
+
throw new HttpRateLimitError();
|
|
2874
|
+
}
|
|
2875
|
+
throw new HttpGetOffersFailedError(`GET request returned ${response.status}`, {
|
|
2876
|
+
details: await response.text()
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2879
|
+
return response.json();
|
|
2880
|
+
}
|
|
2881
|
+
var InvalidUrlError = class extends mempool.Errors.BaseError {
|
|
2882
|
+
name = "Router.InvalidUrlError";
|
|
2883
|
+
constructor(url) {
|
|
2884
|
+
super(`URL "${url}" is not http/https.`);
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
var HttpUnauthorizedError = class extends mempool.Errors.BaseError {
|
|
2888
|
+
name = "Router.HttpUnauthorizedError";
|
|
2889
|
+
constructor() {
|
|
2890
|
+
super("Unauthorized.", {
|
|
2891
|
+
metaMessages: ["Ensure that an API key is provided."]
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
var HttpForbiddenError = class extends mempool.Errors.BaseError {
|
|
2896
|
+
name = "Router.HttpForbiddenError";
|
|
2897
|
+
constructor() {
|
|
2898
|
+
super("Forbidden.", {
|
|
2899
|
+
metaMessages: ["Ensure that the API key is valid."]
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2902
|
+
};
|
|
2903
|
+
var HttpRateLimitError = class extends mempool.Errors.BaseError {
|
|
2904
|
+
name = "Router.HttpRateLimitError";
|
|
2905
|
+
constructor() {
|
|
2906
|
+
super("Rate limit exceeded.", {
|
|
2907
|
+
metaMessages: [
|
|
2908
|
+
"The number of allowed requests has been exceeded. You must wait for the rate limit to reset."
|
|
2909
|
+
]
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
var HttpGetOffersFailedError = class extends mempool.Errors.BaseError {
|
|
2914
|
+
name = "Router.HttpGetOffersFailedError";
|
|
2915
|
+
constructor(message, { details } = {}) {
|
|
2916
|
+
super(message, {
|
|
2917
|
+
metaMessages: [details]
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
async function serve(parameters) {
|
|
2922
|
+
const app = new hono.Hono();
|
|
2923
|
+
const store = parameters.store;
|
|
2924
|
+
const logger = getLogger();
|
|
2925
|
+
app.get("/v1/offers", async (c) => {
|
|
2926
|
+
try {
|
|
2927
|
+
const params = parse("get_offers", c.req.query());
|
|
2928
|
+
const offers2 = await store.getAll({
|
|
2929
|
+
query: {
|
|
2930
|
+
creators: params.creators,
|
|
2931
|
+
side: params.side,
|
|
2932
|
+
chains: params.chains,
|
|
2933
|
+
loanTokens: params.loan_tokens,
|
|
2934
|
+
status: params.status,
|
|
2935
|
+
callbackAddresses: params.callback_addresses,
|
|
2936
|
+
minAmount: params.min_amount,
|
|
2937
|
+
maxAmount: params.max_amount,
|
|
2938
|
+
minRate: params.min_rate,
|
|
2939
|
+
maxRate: params.max_rate,
|
|
2940
|
+
minMaturity: params.min_maturity,
|
|
2941
|
+
maxMaturity: params.max_maturity,
|
|
2942
|
+
minExpiry: params.min_expiry,
|
|
2943
|
+
maxExpiry: params.max_expiry,
|
|
2944
|
+
collateralAssets: params.collateral_assets,
|
|
2945
|
+
collateralOracles: params.collateral_oracles,
|
|
2946
|
+
collateralTuple: params.collateral_tuple,
|
|
2947
|
+
minLltv: params.min_lltv,
|
|
2948
|
+
maxLltv: params.max_lltv,
|
|
2949
|
+
sortBy: params.sort_by,
|
|
2950
|
+
sortOrder: params.sort_order,
|
|
2951
|
+
cursor: params.cursor,
|
|
2952
|
+
limit: params.limit
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
return success(c, {
|
|
2956
|
+
data: offers2.offers.map(
|
|
2957
|
+
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
2958
|
+
),
|
|
2959
|
+
cursor: offers2.nextCursor ?? null
|
|
2960
|
+
});
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
console.error(err);
|
|
2963
|
+
return error(err, c);
|
|
1776
2964
|
}
|
|
1777
|
-
};
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
2965
|
+
});
|
|
2966
|
+
app.get("/v1/offers/match", async (c) => {
|
|
2967
|
+
try {
|
|
2968
|
+
const params = parse("match_offers", c.req.query());
|
|
2969
|
+
const offers2 = await store.findMatchingOffers({
|
|
2970
|
+
side: params.side,
|
|
2971
|
+
chainId: params.chain_id,
|
|
2972
|
+
rate: params.rate,
|
|
2973
|
+
collaterals: params.collaterals,
|
|
2974
|
+
maturity: params.maturity,
|
|
2975
|
+
minMaturity: params.min_maturity,
|
|
2976
|
+
maxMaturity: params.max_maturity,
|
|
2977
|
+
loanToken: params.loan_token,
|
|
2978
|
+
creator: params.creator,
|
|
2979
|
+
cursor: params.cursor,
|
|
2980
|
+
limit: params.limit
|
|
2981
|
+
});
|
|
2982
|
+
return success(c, {
|
|
2983
|
+
data: offers2.offers.map(
|
|
2984
|
+
(offer) => mempool.Format.stringifyBigint(mempool.Format.toSnakeCase(toResponse(offer)))
|
|
2985
|
+
),
|
|
2986
|
+
cursor: offers2.nextCursor ?? null
|
|
2987
|
+
});
|
|
2988
|
+
} catch (err) {
|
|
2989
|
+
console.error(err);
|
|
2990
|
+
return error(err, c);
|
|
2991
|
+
}
|
|
2992
|
+
});
|
|
2993
|
+
nodeServer.serve(
|
|
2994
|
+
{
|
|
2995
|
+
fetch: app.fetch,
|
|
2996
|
+
port: parameters.port
|
|
1790
2997
|
},
|
|
1791
|
-
|
|
2998
|
+
(info) => {
|
|
2999
|
+
logger.info({ service: "api", msg: "Router server started", port: info.port });
|
|
1792
3000
|
}
|
|
1793
|
-
|
|
3001
|
+
);
|
|
1794
3002
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
3003
|
+
function error(error2, c) {
|
|
3004
|
+
if (error2 instanceof APIError) {
|
|
3005
|
+
return handleAPIError(error2, c);
|
|
3006
|
+
}
|
|
3007
|
+
if (error2 instanceof SyntaxError) {
|
|
3008
|
+
return handleAPIError(new BadRequestError(error2.message), c);
|
|
3009
|
+
}
|
|
3010
|
+
if (error2 instanceof v4.z.ZodError) {
|
|
3011
|
+
return handleAPIError(handleZodError(error2), c);
|
|
3012
|
+
}
|
|
3013
|
+
return handleAPIError(new InternalServerError(), c);
|
|
1798
3014
|
}
|
|
1799
|
-
function
|
|
1800
|
-
|
|
3015
|
+
function success(c, {
|
|
3016
|
+
data,
|
|
3017
|
+
cursor
|
|
3018
|
+
}) {
|
|
3019
|
+
return c.json({
|
|
3020
|
+
status: "success",
|
|
3021
|
+
cursor,
|
|
3022
|
+
data,
|
|
3023
|
+
meta: {
|
|
3024
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3025
|
+
}
|
|
3026
|
+
});
|
|
1801
3027
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
3028
|
+
var APIError = class extends Error {
|
|
3029
|
+
constructor(statusCode, message, code, details) {
|
|
3030
|
+
super(message);
|
|
3031
|
+
this.statusCode = statusCode;
|
|
3032
|
+
this.code = code;
|
|
3033
|
+
this.details = details;
|
|
3034
|
+
this.name = "APIError";
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
var ValidationError = class extends APIError {
|
|
3038
|
+
constructor(message, details) {
|
|
3039
|
+
super(400, message, "VALIDATION_ERROR", details);
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
var NotFoundError = class extends APIError {
|
|
3043
|
+
constructor(message) {
|
|
3044
|
+
super(404, message, "NOT_FOUND");
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
var InternalServerError = class extends APIError {
|
|
3048
|
+
constructor(message = "Internal server error") {
|
|
3049
|
+
super(500, message, "INTERNAL_SERVER_ERROR");
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
var BadRequestError = class extends APIError {
|
|
3053
|
+
constructor(message = "Invalid JSON format", details) {
|
|
3054
|
+
super(400, message, "BAD_REQUEST", details);
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
function handleZodError(error2) {
|
|
3058
|
+
const formattedErrors = error2.issues.map((err) => {
|
|
3059
|
+
const field = err.path.join(".");
|
|
3060
|
+
let issue = err.message;
|
|
3061
|
+
if (err.code === "invalid_type") {
|
|
3062
|
+
if (err.message.includes("received undefined")) {
|
|
3063
|
+
issue = `${field} is required`;
|
|
3064
|
+
} else {
|
|
3065
|
+
issue = err.message;
|
|
1840
3066
|
}
|
|
3067
|
+
} else if (err.code === "invalid_format") {
|
|
3068
|
+
issue = `${field} has an invalid format`;
|
|
1841
3069
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
3070
|
+
return {
|
|
3071
|
+
field,
|
|
3072
|
+
issue
|
|
3073
|
+
};
|
|
3074
|
+
});
|
|
3075
|
+
return new ValidationError("Validation failed", formattedErrors);
|
|
3076
|
+
}
|
|
3077
|
+
function handleAPIError(error2, c) {
|
|
3078
|
+
return c.json({
|
|
3079
|
+
statusCode: error2.statusCode,
|
|
3080
|
+
body: JSON.stringify({
|
|
3081
|
+
status: "error",
|
|
3082
|
+
error: {
|
|
3083
|
+
code: error2.code,
|
|
3084
|
+
message: error2.message,
|
|
3085
|
+
...error2.details && typeof error2.details === "object" ? { details: error2.details } : {}
|
|
3086
|
+
},
|
|
3087
|
+
meta: {
|
|
3088
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3089
|
+
}
|
|
3090
|
+
})
|
|
3091
|
+
});
|
|
1848
3092
|
}
|
|
1849
3093
|
|
|
1850
|
-
// src/
|
|
1851
|
-
var
|
|
1852
|
-
__export(
|
|
1853
|
-
|
|
1854
|
-
morpho: () => morpho,
|
|
1855
|
-
single: () => single
|
|
3094
|
+
// src/core/Services.ts
|
|
3095
|
+
var Services_exports = {};
|
|
3096
|
+
__export(Services_exports, {
|
|
3097
|
+
from: () => from2
|
|
1856
3098
|
});
|
|
1857
|
-
function
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
3099
|
+
function from2(config) {
|
|
3100
|
+
const { chain, rpcUrl, dbConfig } = config;
|
|
3101
|
+
const walletClient = viem.createWalletClient({
|
|
3102
|
+
chain,
|
|
3103
|
+
transport: viem.http(rpcUrl)
|
|
3104
|
+
}).extend(viem.publicActions);
|
|
3105
|
+
if (dbConfig.type === "pg" && !dbConfig.endpoint) {
|
|
3106
|
+
throw new Error("dbOffer.endpoint is required when dbOffer.type is pg");
|
|
3107
|
+
}
|
|
3108
|
+
const DB = dbConfig.type === "pg" ? connect({ type: "pg", endpoint: dbConfig.endpoint }) : connect({ type: "pglite" });
|
|
3109
|
+
const offerStore = create3({ db: DB });
|
|
3110
|
+
const collectorBlockStore = create({
|
|
3111
|
+
db: DB
|
|
1870
3112
|
});
|
|
1871
|
-
const
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
3113
|
+
const liquidityStore = create2({ db: DB });
|
|
3114
|
+
const interval = 1e4;
|
|
3115
|
+
const maxBatchSize = 1e3;
|
|
3116
|
+
const mempoolOffersCollector = createMempoolCollector({
|
|
3117
|
+
mempool: mempool.Mempool.connect({
|
|
3118
|
+
client: walletClient,
|
|
3119
|
+
mempoolAddress: chain.mempool.address
|
|
3120
|
+
}),
|
|
3121
|
+
offerStore,
|
|
3122
|
+
collectorBlockStore,
|
|
3123
|
+
chain,
|
|
3124
|
+
options: {
|
|
3125
|
+
interval,
|
|
3126
|
+
maxBatchSize
|
|
1879
3127
|
}
|
|
1880
3128
|
});
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
3129
|
+
const consumedEventsCollector = createConsumedEventsCollector({
|
|
3130
|
+
client: walletClient,
|
|
3131
|
+
contractAddress: chain.morpho,
|
|
3132
|
+
offerStore,
|
|
3133
|
+
collectorBlockStore,
|
|
3134
|
+
chainId: chain.id,
|
|
3135
|
+
options: {
|
|
3136
|
+
interval,
|
|
3137
|
+
maxBatchSize
|
|
1884
3138
|
}
|
|
1885
3139
|
});
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
3140
|
+
const buyWithEmptyCallbackLiquidityCollector = createBuyWithEmptyCallbackLiquidityCollector({
|
|
3141
|
+
client: walletClient,
|
|
3142
|
+
offerStore,
|
|
3143
|
+
collectorBlockStore,
|
|
3144
|
+
liquidityStore,
|
|
3145
|
+
chain,
|
|
3146
|
+
options: {
|
|
3147
|
+
interval,
|
|
3148
|
+
maxBatchSize
|
|
1889
3149
|
}
|
|
1890
3150
|
});
|
|
1891
|
-
return
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
3151
|
+
return {
|
|
3152
|
+
offerStore,
|
|
3153
|
+
collectorBlockStore,
|
|
3154
|
+
liquidityStore,
|
|
3155
|
+
mempoolOffersCollector,
|
|
3156
|
+
consumedEventsCollector,
|
|
3157
|
+
buyWithEmptyCallbackLiquidityCollector,
|
|
3158
|
+
DB
|
|
3159
|
+
};
|
|
1899
3160
|
}
|
|
1900
3161
|
|
|
1901
3162
|
exports.ApiSchema = apiSchema_exports;
|
|
1902
3163
|
exports.Callback = Callback_exports;
|
|
3164
|
+
exports.Collector = Collector_exports;
|
|
3165
|
+
exports.CollectorBlockStore = CollectorBlockStore_exports;
|
|
1903
3166
|
exports.Cursor = Cursor_exports;
|
|
1904
3167
|
exports.Liquidity = Liquidity_exports;
|
|
3168
|
+
exports.LiquidityStore = LiquidityStore_exports;
|
|
1905
3169
|
exports.Logger = Logger_exports;
|
|
1906
3170
|
exports.OfferStore = OfferStore_exports;
|
|
3171
|
+
exports.OffersSchema = schema_exports;
|
|
3172
|
+
exports.PG = PG_exports;
|
|
1907
3173
|
exports.Router = router_exports;
|
|
1908
3174
|
exports.RouterOffer = RouterOffer_exports;
|
|
3175
|
+
exports.Services = Services_exports;
|
|
1909
3176
|
exports.Validation = Validation_exports;
|
|
1910
3177
|
exports.ValidationRule = ValidationRule_exports;
|
|
1911
3178
|
Object.keys(mempool).forEach(function (k) {
|