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