@morpho-dev/router 0.7.2 → 0.9.0
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/cli.js +834 -334
- package/dist/drizzle/migrations/0000_setup_single_migration_folder.sql +64 -64
- package/dist/drizzle/migrations/0001_add-trigger-for-consumed-events.sql +5 -5
- package/dist/drizzle/migrations/0002_insert-status-code.sql +1 -1
- package/dist/drizzle/migrations/0003_update-triggers-for-consumed-events.sql +1 -1
- package/dist/drizzle/migrations/0004_drop-status-offers-foreign-key-constraint.sql +1 -1
- package/dist/drizzle/migrations/0005_add-index-to-boost-group-query-and-offer-hash.sql +1 -1
- package/dist/drizzle/migrations/0006_add-callbacks-and-positions-relations.sql +11 -11
- package/dist/drizzle/migrations/0008_validation.sql +10 -10
- package/dist/drizzle/migrations/0009_add-transfers-table.sql +4 -4
- package/dist/drizzle/migrations/0010_add-price.sql +1 -1
- package/dist/drizzle/migrations/0011_nullable-callback-amount.sql +1 -1
- package/dist/drizzle/migrations/0012_add-position-asset.sql +1 -1
- package/dist/drizzle/migrations/0013_remove-depecrated-domains.sql +13 -13
- package/dist/drizzle/migrations/0014_rename-offers-v2-into-offers.sql +19 -19
- package/dist/drizzle/migrations/0015_add-lots-table.sql +3 -3
- package/dist/drizzle/migrations/0016_merkle-metadata.sql +7 -7
- package/dist/drizzle/migrations/0017_dusty_the_hunter.sql +1 -1
- package/dist/drizzle/migrations/0018_add_chain_collector_constraints.sql +3 -3
- package/dist/drizzle/migrations/0019_add-obligation-units-shares.sql +2 -2
- package/dist/drizzle/migrations/0020_add-session.sql +1 -1
- package/dist/drizzle/migrations/0021_drop_chain_collector_epoch_indexes.sql +2 -2
- package/dist/drizzle/migrations/0021_migrate-rate-to-price.sql +6 -6
- package/dist/drizzle/migrations/0022_consolidate-price.sql +5 -5
- package/dist/drizzle/migrations/0023_remove-block-number-for-collaterals.sql +1 -1
- package/dist/drizzle/migrations/0024_add-obligation-id-to-lots.sql +8 -0
- package/dist/drizzle/migrations/0025_rename-price-to-tick.sql +202 -0
- package/dist/drizzle/migrations/0026_add-receiver-if-maker-is-seller.sql +1 -0
- package/dist/drizzle/migrations/meta/0000_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0001_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0002_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0003_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0004_snapshot.json +47 -47
- package/dist/drizzle/migrations/meta/0005_snapshot.json +47 -47
- package/dist/drizzle/migrations/meta/0006_snapshot.json +61 -61
- package/dist/drizzle/migrations/meta/0008_snapshot.json +62 -62
- package/dist/drizzle/migrations/meta/0009_snapshot.json +66 -66
- package/dist/drizzle/migrations/meta/0010_snapshot.json +66 -66
- package/dist/drizzle/migrations/meta/0013_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0014_snapshot.json +48 -48
- package/dist/drizzle/migrations/meta/0015_snapshot.json +52 -52
- package/dist/drizzle/migrations/meta/0016_snapshot.json +61 -61
- package/dist/drizzle/migrations/meta/0017_snapshot.json +61 -61
- package/dist/drizzle/migrations/meta/0018_snapshot.json +62 -62
- package/dist/drizzle/migrations/meta/0019_snapshot.json +62 -62
- package/dist/drizzle/migrations/meta/0023_snapshot.json +62 -62
- package/dist/drizzle/migrations/meta/0024_snapshot.json +1448 -0
- package/dist/drizzle/migrations/meta/0025_snapshot.json +1448 -0
- package/dist/drizzle/migrations/meta/0026_snapshot.json +1454 -0
- package/dist/drizzle/migrations/meta/_journal.json +21 -0
- package/dist/evm/bytecode/morpho.txt +1 -1
- package/dist/index.browser.d.mts +206 -77
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +206 -77
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +445 -197
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +440 -198
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +347 -119
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +347 -119
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +865 -312
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +861 -314
- package/dist/index.node.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
|
39
39
|
import { drizzle as drizzle$1 } from "drizzle-orm/pglite";
|
|
40
40
|
import { migrate as migrate$1 } from "drizzle-orm/pglite/migrator";
|
|
41
41
|
import { Pool } from "pg";
|
|
42
|
-
import { and, asc, eq, gt, gte, inArray, lte, ne, sql } from "drizzle-orm";
|
|
42
|
+
import { and, asc, desc, eq, gt, gte, inArray, lte, ne, sql } from "drizzle-orm";
|
|
43
43
|
import { bigint, boolean, foreignKey, index, integer, numeric, pgSchema, primaryKey, serial, text, timestamp, uniqueIndex, varchar } from "drizzle-orm/pg-core";
|
|
44
44
|
import { parse } from "smol-toml";
|
|
45
45
|
import { cors } from "hono/cors";
|
|
@@ -152,7 +152,7 @@ function startActiveSpan(tracer, name, fn) {
|
|
|
152
152
|
//#endregion
|
|
153
153
|
//#region package.json
|
|
154
154
|
var name = "@morpho-dev/router";
|
|
155
|
-
var version = "0.
|
|
155
|
+
var version = "0.9.0";
|
|
156
156
|
var description = "Router package for Morpho protocol";
|
|
157
157
|
|
|
158
158
|
//#endregion
|
|
@@ -331,8 +331,8 @@ const chains$2 = {
|
|
|
331
331
|
name: "ethereum-virtual-testnet",
|
|
332
332
|
custom: {
|
|
333
333
|
morpho: {
|
|
334
|
-
address: "
|
|
335
|
-
blockCreated:
|
|
334
|
+
address: "0xc9f3c65996fc46b9500608b2c9a9152c01c540f7",
|
|
335
|
+
blockCreated: 23226871
|
|
336
336
|
},
|
|
337
337
|
morphoBlue: {
|
|
338
338
|
address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
|
|
@@ -1469,7 +1469,7 @@ async function run(parameters) {
|
|
|
1469
1469
|
* @param parameters - Gatekeeper parameters. {@link GatekeeperParameters}
|
|
1470
1470
|
* @returns Gatekeeper instance. {@link Gatekeeper}
|
|
1471
1471
|
*/
|
|
1472
|
-
function create$
|
|
1472
|
+
function create$21(parameters) {
|
|
1473
1473
|
const { rules } = parameters;
|
|
1474
1474
|
return { isAllowed: async (offers) => {
|
|
1475
1475
|
return await run({
|
|
@@ -1640,6 +1640,33 @@ const assets = {
|
|
|
1640
1640
|
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
|
|
1641
1641
|
]
|
|
1642
1642
|
};
|
|
1643
|
+
const collateralAssets = {
|
|
1644
|
+
[ChainId.ETHEREUM.toString()]: [
|
|
1645
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
1646
|
+
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
1647
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
1648
|
+
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
1649
|
+
],
|
|
1650
|
+
[ChainId.BASE.toString()]: [
|
|
1651
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
1652
|
+
"0x4200000000000000000000000000000000000006",
|
|
1653
|
+
"0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
|
|
1654
|
+
"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
|
|
1655
|
+
"0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
|
|
1656
|
+
],
|
|
1657
|
+
[ChainId["ETHEREUM-VIRTUAL-TESTNET"].toString()]: [
|
|
1658
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
1659
|
+
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
1660
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
1661
|
+
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
1662
|
+
],
|
|
1663
|
+
[ChainId.ANVIL.toString()]: [
|
|
1664
|
+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
1665
|
+
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
1666
|
+
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
|
|
1667
|
+
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
1668
|
+
]
|
|
1669
|
+
};
|
|
1643
1670
|
const oracles$1 = {
|
|
1644
1671
|
[ChainId.ETHEREUM.toString()]: [
|
|
1645
1672
|
"0xDddd770BADd886dF3864029e4B377B5F6a2B6b83",
|
|
@@ -1680,19 +1707,19 @@ const oracles$1 = {
|
|
|
1680
1707
|
const configs = {
|
|
1681
1708
|
ethereum: {
|
|
1682
1709
|
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
1683
|
-
maturities: [MaturityType.
|
|
1710
|
+
maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
|
|
1684
1711
|
},
|
|
1685
1712
|
base: {
|
|
1686
1713
|
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
1687
|
-
maturities: [MaturityType.
|
|
1714
|
+
maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
|
|
1688
1715
|
},
|
|
1689
1716
|
"ethereum-virtual-testnet": {
|
|
1690
1717
|
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
1691
|
-
maturities: [MaturityType.
|
|
1718
|
+
maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
|
|
1692
1719
|
},
|
|
1693
1720
|
anvil: {
|
|
1694
1721
|
callbacks: [{ type: Type$1.BuyWithEmptyCallback }, { type: Type$1.SellWithEmptyCallback }],
|
|
1695
|
-
maturities: [MaturityType.
|
|
1722
|
+
maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek]
|
|
1696
1723
|
}
|
|
1697
1724
|
};
|
|
1698
1725
|
|
|
@@ -1738,18 +1765,18 @@ const MorphoV2 = parseAbi([
|
|
|
1738
1765
|
"function setFeeSetter(address newFeeSetter)",
|
|
1739
1766
|
"function setObligationTradingFee(bytes32 id, uint256 index, uint256 newTradingFee)",
|
|
1740
1767
|
"function setOwner(address newOwner)",
|
|
1741
|
-
"function setTradingFeeRecipient(address
|
|
1768
|
+
"function setTradingFeeRecipient(address feeRecipient)",
|
|
1742
1769
|
"function sharesOf(bytes32 id, address user) view returns (uint256)",
|
|
1743
1770
|
"function shuffleSession()",
|
|
1744
1771
|
"function supplyCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
|
|
1745
|
-
"function take(uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, address taker, ((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, bool buy, address maker, uint256 assets, uint256 obligationUnits, uint256 obligationShares, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof
|
|
1772
|
+
"function take(uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, address taker, address takerCallback, bytes takerCallbackData, address receiverIfTakerIsSeller, ((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, bool buy, address maker, uint256 assets, uint256 obligationUnits, uint256 obligationShares, uint256 start, uint256 expiry, uint256 tick, bytes32 group, bytes32 session, address callback, bytes callbackData, address receiverIfMakerIsSeller) offer, (uint8 v, bytes32 r, bytes32 s) sig, bytes32 root, bytes32[] proof) returns (uint256, uint256, uint256, uint256)",
|
|
1746
1773
|
"function totalShares(bytes32 id) view returns (uint256)",
|
|
1747
1774
|
"function totalUnits(bytes32 id) view returns (uint256)",
|
|
1748
1775
|
"function touchObligation((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation) returns (bytes32)",
|
|
1749
1776
|
"function tradingFee(bytes32 id, uint256 timeToMaturity) view returns (uint256)",
|
|
1750
1777
|
"function tradingFeeRecipient() view returns (address)",
|
|
1751
|
-
"function withdraw((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, uint256 obligationUnits, uint256 shares, address onBehalf) returns (uint256, uint256)",
|
|
1752
|
-
"function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf)",
|
|
1778
|
+
"function withdraw((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, uint256 obligationUnits, uint256 shares, address onBehalf, address receiver) returns (uint256, uint256)",
|
|
1779
|
+
"function withdrawCollateral((address loanToken, (address token, uint256 lltv, address oracle)[] collaterals, uint256 maturity) obligation, address collateral, uint256 assets, address onBehalf, address receiver)",
|
|
1753
1780
|
"function withdrawable(bytes32 id) view returns (uint256)",
|
|
1754
1781
|
"event Constructor(address indexed owner)",
|
|
1755
1782
|
"event Consume(address indexed user, bytes32 indexed group, uint256 amount)",
|
|
@@ -1761,12 +1788,12 @@ const MorphoV2 = parseAbi([
|
|
|
1761
1788
|
"event SetFeeSetter(address indexed feeSetter)",
|
|
1762
1789
|
"event SetObligationTradingFee(bytes32 indexed id, uint256 indexed index, uint256 newTradingFee)",
|
|
1763
1790
|
"event SetOwner(address indexed owner)",
|
|
1764
|
-
"event SetTradingFeeRecipient(address indexed
|
|
1791
|
+
"event SetTradingFeeRecipient(address indexed feeRecipient)",
|
|
1765
1792
|
"event ShuffleSession(address indexed user, bytes32 session)",
|
|
1766
1793
|
"event SupplyCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)",
|
|
1767
|
-
"event Take(address caller, bytes32 indexed id, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, bool buyerIsLender, bool sellerIsBorrower, bytes32 group, uint256 consumed)",
|
|
1768
|
-
"event Withdraw(address
|
|
1769
|
-
"event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf)"
|
|
1794
|
+
"event Take(address caller, bytes32 indexed id, address indexed maker, address indexed taker, bool offerIsBuy, uint256 buyerAssets, uint256 sellerAssets, uint256 obligationUnits, uint256 obligationShares, bool buyerIsLender, bool sellerIsBorrower, address sellerReceiver, bytes32 group, uint256 consumed)",
|
|
1795
|
+
"event Withdraw(address caller, bytes32 indexed id, uint256 obligationUnits, uint256 shares, address indexed onBehalf, address indexed receiver)",
|
|
1796
|
+
"event WithdrawCollateral(address caller, bytes32 indexed id, address indexed collateral, uint256 assets, address indexed onBehalf, address receiver)"
|
|
1770
1797
|
]);
|
|
1771
1798
|
|
|
1772
1799
|
//#endregion
|
|
@@ -1789,7 +1816,7 @@ const Oracle = [{
|
|
|
1789
1816
|
* @param chains - Array of chain objects to register.
|
|
1790
1817
|
* @returns A registry for looking up chains by ID. {@link ChainRegistry}
|
|
1791
1818
|
*/
|
|
1792
|
-
function create$
|
|
1819
|
+
function create$20(chains) {
|
|
1793
1820
|
const byId = /* @__PURE__ */ new Map();
|
|
1794
1821
|
for (const chain of chains) byId.set(chain.id, chain);
|
|
1795
1822
|
return {
|
|
@@ -2126,7 +2153,7 @@ const OfferSchema = () => {
|
|
|
2126
2153
|
assets: z$2.bigint({ coerce: true }).min(0n).max(maxUint256),
|
|
2127
2154
|
obligationUnits: z$2.bigint({ coerce: true }).min(0n).max(maxUint256).optional().default(0n),
|
|
2128
2155
|
obligationShares: z$2.bigint({ coerce: true }).min(0n).max(maxUint256).optional().default(0n),
|
|
2129
|
-
|
|
2156
|
+
tick: z$2.coerce.number().int().min(0).max(990),
|
|
2130
2157
|
maturity: MaturitySchema,
|
|
2131
2158
|
expiry: z$2.number().int().max(Number.MAX_SAFE_INTEGER),
|
|
2132
2159
|
start: z$2.number().int().max(Number.MAX_SAFE_INTEGER),
|
|
@@ -2147,7 +2174,8 @@ const OfferSchema = () => {
|
|
|
2147
2174
|
callback: z$2.object({
|
|
2148
2175
|
address: z$2.string().transform(transformAddress),
|
|
2149
2176
|
data: z$2.string().transform(transformHex)
|
|
2150
|
-
})
|
|
2177
|
+
}),
|
|
2178
|
+
receiverIfMakerIsSeller: z$2.string().transform(transformAddress)
|
|
2151
2179
|
}).refine((data) => data.start < data.expiry, {
|
|
2152
2180
|
message: "start must be before expiry",
|
|
2153
2181
|
path: ["start"]
|
|
@@ -2163,8 +2191,12 @@ const OfferSchema = () => {
|
|
|
2163
2191
|
* @returns The created offer.
|
|
2164
2192
|
*/
|
|
2165
2193
|
function from$12(input) {
|
|
2194
|
+
const normalizedInput = {
|
|
2195
|
+
...input,
|
|
2196
|
+
receiverIfMakerIsSeller: input.receiverIfMakerIsSeller ?? input.maker
|
|
2197
|
+
};
|
|
2166
2198
|
try {
|
|
2167
|
-
return OfferSchema().parse(
|
|
2199
|
+
return OfferSchema().parse(normalizedInput);
|
|
2168
2200
|
} catch (error) {
|
|
2169
2201
|
throw new InvalidOfferError(error);
|
|
2170
2202
|
}
|
|
@@ -2198,7 +2230,7 @@ const serialize = (offer) => ({
|
|
|
2198
2230
|
assets: offer.assets.toString(),
|
|
2199
2231
|
obligationUnits: offer.obligationUnits.toString(),
|
|
2200
2232
|
obligationShares: offer.obligationShares.toString(),
|
|
2201
|
-
|
|
2233
|
+
tick: offer.tick,
|
|
2202
2234
|
maturity: Number(offer.maturity),
|
|
2203
2235
|
expiry: Number(offer.expiry),
|
|
2204
2236
|
start: Number(offer.start),
|
|
@@ -2216,6 +2248,7 @@ const serialize = (offer) => ({
|
|
|
2216
2248
|
address: offer.callback.address,
|
|
2217
2249
|
data: offer.callback.data
|
|
2218
2250
|
},
|
|
2251
|
+
receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller,
|
|
2219
2252
|
hash: hash(offer)
|
|
2220
2253
|
});
|
|
2221
2254
|
/**
|
|
@@ -2243,14 +2276,13 @@ function random(config) {
|
|
|
2243
2276
|
[.98, 2]
|
|
2244
2277
|
]));
|
|
2245
2278
|
const buy = config?.buy !== void 0 ? config.buy : bool();
|
|
2246
|
-
const
|
|
2247
|
-
const
|
|
2248
|
-
const
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
return [BigInt(q) * (ONE / 4n), buy ? 1 + idx : 1 + (len - 1 - idx)];
|
|
2279
|
+
const tickMin = buy ? 0 : 495;
|
|
2280
|
+
const len = (buy ? 495 : 990) - tickMin + 1;
|
|
2281
|
+
const tickPairs = Array.from({ length: len }, (_, idx) => {
|
|
2282
|
+
const weight = buy ? 1 + idx : 1 + (len - 1 - idx);
|
|
2283
|
+
return [tickMin + idx, weight];
|
|
2252
2284
|
});
|
|
2253
|
-
const
|
|
2285
|
+
const tick = config?.tick ?? weightedChoice(tickPairs);
|
|
2254
2286
|
const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
|
|
2255
2287
|
const unit = BigInt(10) ** BigInt(loanTokenDecimals);
|
|
2256
2288
|
const amountBase = BigInt(100 + int(999901));
|
|
@@ -2259,12 +2291,13 @@ function random(config) {
|
|
|
2259
2291
|
address: zeroAddress,
|
|
2260
2292
|
data: "0x"
|
|
2261
2293
|
};
|
|
2294
|
+
const maker = config?.maker ?? address();
|
|
2262
2295
|
return from$12({
|
|
2263
|
-
maker
|
|
2296
|
+
maker,
|
|
2264
2297
|
assets: assetsScaled,
|
|
2265
2298
|
obligationUnits: config?.obligationUnits ?? 0n,
|
|
2266
2299
|
obligationShares: config?.obligationShares ?? 0n,
|
|
2267
|
-
|
|
2300
|
+
tick,
|
|
2268
2301
|
maturity,
|
|
2269
2302
|
expiry: config?.expiry ?? maturity - 1,
|
|
2270
2303
|
start: config?.start ?? maturity - 10,
|
|
@@ -2277,7 +2310,8 @@ function random(config) {
|
|
|
2277
2310
|
...random$1(),
|
|
2278
2311
|
lltv
|
|
2279
2312
|
})).sort((a, b) => a.asset.localeCompare(b.asset)),
|
|
2280
|
-
callback: config?.callback ?? emptyCallback
|
|
2313
|
+
callback: config?.callback ?? emptyCallback,
|
|
2314
|
+
receiverIfMakerIsSeller: config?.receiverIfMakerIsSeller ?? maker
|
|
2281
2315
|
});
|
|
2282
2316
|
}
|
|
2283
2317
|
const weightedChoice = (pairs) => {
|
|
@@ -2329,7 +2363,7 @@ const types = {
|
|
|
2329
2363
|
type: "uint256"
|
|
2330
2364
|
},
|
|
2331
2365
|
{
|
|
2332
|
-
name: "
|
|
2366
|
+
name: "tick",
|
|
2333
2367
|
type: "uint256"
|
|
2334
2368
|
},
|
|
2335
2369
|
{
|
|
@@ -2363,6 +2397,10 @@ const types = {
|
|
|
2363
2397
|
{
|
|
2364
2398
|
name: "callback",
|
|
2365
2399
|
type: "Callback"
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
name: "receiverIfMakerIsSeller",
|
|
2403
|
+
type: "address"
|
|
2366
2404
|
}
|
|
2367
2405
|
],
|
|
2368
2406
|
Collateral: [
|
|
@@ -2397,7 +2435,7 @@ function hash(offer) {
|
|
|
2397
2435
|
assets: offer.assets,
|
|
2398
2436
|
obligationUnits: offer.obligationUnits,
|
|
2399
2437
|
obligationShares: offer.obligationShares,
|
|
2400
|
-
|
|
2438
|
+
tick: BigInt(offer.tick),
|
|
2401
2439
|
maturity: BigInt(offer.maturity),
|
|
2402
2440
|
expiry: BigInt(offer.expiry),
|
|
2403
2441
|
group: offer.group,
|
|
@@ -2408,7 +2446,8 @@ function hash(offer) {
|
|
|
2408
2446
|
callback: {
|
|
2409
2447
|
address: offer.callback.address.toLowerCase(),
|
|
2410
2448
|
data: offer.callback.data
|
|
2411
|
-
}
|
|
2449
|
+
},
|
|
2450
|
+
receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase()
|
|
2412
2451
|
},
|
|
2413
2452
|
primaryType: "Offer",
|
|
2414
2453
|
types
|
|
@@ -2503,6 +2542,12 @@ const takeEvent = {
|
|
|
2503
2542
|
indexed: false,
|
|
2504
2543
|
internalType: "bool"
|
|
2505
2544
|
},
|
|
2545
|
+
{
|
|
2546
|
+
name: "sellerReceiver",
|
|
2547
|
+
type: "address",
|
|
2548
|
+
indexed: false,
|
|
2549
|
+
internalType: "address"
|
|
2550
|
+
},
|
|
2506
2551
|
{
|
|
2507
2552
|
name: "group",
|
|
2508
2553
|
type: "bytes32",
|
|
@@ -2624,13 +2669,57 @@ function from$10(parameters) {
|
|
|
2624
2669
|
};
|
|
2625
2670
|
}
|
|
2626
2671
|
|
|
2672
|
+
//#endregion
|
|
2673
|
+
//#region src/core/Tick.ts
|
|
2674
|
+
/** ln(1 + 0.025), scaled by 1e18. Matches TickLib onchain constant. */
|
|
2675
|
+
const LN_ONE_PLUS_DELTA = 24692612590371501n;
|
|
2676
|
+
/** ln(2), scaled by 1e18. Matches TickLib onchain constant. */
|
|
2677
|
+
const LN2 = 693147180559945309n;
|
|
2678
|
+
const WAD$1 = 10n ** 18n;
|
|
2679
|
+
const WAD_SQUARED = 10n ** 36n;
|
|
2680
|
+
const PRICE_STEP = 10n ** 13n;
|
|
2681
|
+
const HALF_TICK_RANGE = 495n;
|
|
2682
|
+
/** Tick domain supported by Morpho V2. */
|
|
2683
|
+
const TICK_RANGE = 990;
|
|
2684
|
+
/**
|
|
2685
|
+
* Converts a tick to a wad price using the same approximation and rounding as TickLib.
|
|
2686
|
+
* @param tick - Tick value in the inclusive range [0, 990].
|
|
2687
|
+
* @returns The price in wad units.
|
|
2688
|
+
* @throws {@link InvalidTickError} If tick is not an integer in range [0, 990].
|
|
2689
|
+
*/
|
|
2690
|
+
function tickToPrice(tick) {
|
|
2691
|
+
assertTick(tick);
|
|
2692
|
+
return divHalfDownUnchecked(divHalfDownUnchecked(WAD_SQUARED, WAD$1 + wExp(LN_ONE_PLUS_DELTA * (HALF_TICK_RANGE - BigInt(tick)))), PRICE_STEP) * PRICE_STEP;
|
|
2693
|
+
}
|
|
2694
|
+
function divHalfDownUnchecked(x, d) {
|
|
2695
|
+
return (x + (d - 1n) / 2n) / d;
|
|
2696
|
+
}
|
|
2697
|
+
function wExp(x) {
|
|
2698
|
+
if (x < 0n) return WAD_SQUARED / wExp(-x);
|
|
2699
|
+
const q = (x + LN2 / 2n) / LN2;
|
|
2700
|
+
const r = x - q * LN2;
|
|
2701
|
+
const secondTerm = r * r / (2n * WAD$1);
|
|
2702
|
+
const thirdTerm = secondTerm * r / (3n * WAD$1);
|
|
2703
|
+
return WAD$1 + r + secondTerm + thirdTerm << q;
|
|
2704
|
+
}
|
|
2705
|
+
function assertTick(tick) {
|
|
2706
|
+
if (!Number.isInteger(tick) || tick < 0 || tick > TICK_RANGE) throw new InvalidTickError(tick);
|
|
2707
|
+
}
|
|
2708
|
+
var InvalidTickError = class extends BaseError {
|
|
2709
|
+
name = "Tick.InvalidTickError";
|
|
2710
|
+
constructor(tick) {
|
|
2711
|
+
super(`Invalid tick: ${tick}. Tick must be an integer between 0 and ${TICK_RANGE}.`);
|
|
2712
|
+
}
|
|
2713
|
+
};
|
|
2714
|
+
|
|
2627
2715
|
//#endregion
|
|
2628
2716
|
//#region src/core/Quote.ts
|
|
2629
|
-
const
|
|
2717
|
+
const SideInputSchema = z$2.object({ tick: z$2.number().int().min(0).max(TICK_RANGE).nullable() }).strict();
|
|
2718
|
+
const QuoteInputSchema = z$2.object({
|
|
2630
2719
|
obligationId: z$2.string().transform(transformHex),
|
|
2631
|
-
ask:
|
|
2632
|
-
bid:
|
|
2633
|
-
});
|
|
2720
|
+
ask: SideInputSchema,
|
|
2721
|
+
bid: SideInputSchema
|
|
2722
|
+
}).strict();
|
|
2634
2723
|
/**
|
|
2635
2724
|
* Creates a quote for a given obligation.
|
|
2636
2725
|
* @constructor
|
|
@@ -2640,16 +2729,16 @@ const QuoteSchema = z$2.object({
|
|
|
2640
2729
|
*
|
|
2641
2730
|
* @example
|
|
2642
2731
|
* ```ts
|
|
2643
|
-
* const quote = Quote.from({ obligationId: "0x123", ask: {
|
|
2732
|
+
* const quote = Quote.from({ obligationId: "0x123", ask: { tick: 500 }, bid: { tick: 510 } });
|
|
2644
2733
|
* ```
|
|
2645
2734
|
*/
|
|
2646
2735
|
function from$9(parameters) {
|
|
2647
2736
|
try {
|
|
2648
|
-
const parsedQuote =
|
|
2737
|
+
const parsedQuote = QuoteInputSchema.parse(parameters);
|
|
2649
2738
|
return {
|
|
2650
2739
|
obligationId: parsedQuote.obligationId,
|
|
2651
|
-
ask: parsedQuote.ask,
|
|
2652
|
-
bid: parsedQuote.bid
|
|
2740
|
+
ask: sideFromTick(parsedQuote.ask),
|
|
2741
|
+
bid: sideFromTick(parsedQuote.bid)
|
|
2653
2742
|
};
|
|
2654
2743
|
} catch (error) {
|
|
2655
2744
|
throw new InvalidQuoteError(error);
|
|
@@ -2661,6 +2750,12 @@ var InvalidQuoteError = class extends BaseError {
|
|
|
2661
2750
|
super("Invalid quote.", { cause: error });
|
|
2662
2751
|
}
|
|
2663
2752
|
};
|
|
2753
|
+
function sideFromTick(side) {
|
|
2754
|
+
return {
|
|
2755
|
+
tick: side.tick,
|
|
2756
|
+
price: side.tick === null ? 0n : tickToPrice(side.tick)
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2664
2759
|
|
|
2665
2760
|
//#endregion
|
|
2666
2761
|
//#region src/core/TradingFee.ts
|
|
@@ -2942,7 +3037,7 @@ const chains$1 = ({ chains }) => single("chain_ids", `Validates that offer chain
|
|
|
2942
3037
|
});
|
|
2943
3038
|
const maturity = ({ maturities }) => single("maturity", `Validates that offer maturity is one of: [${maturities.join(", ")}]`, (offer) => {
|
|
2944
3039
|
const allowedMaturities = maturities.map((m) => from$16(m));
|
|
2945
|
-
if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be
|
|
3040
|
+
if (!allowedMaturities.includes(offer.maturity)) return { message: `Maturity must be one of (${allowedMaturities.join(", ")}). Got: ${offer.maturity}` };
|
|
2946
3041
|
});
|
|
2947
3042
|
const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy empty callback is ${callbacks.includes(Type$1.BuyWithEmptyCallback) ? "allowed" : "not allowed"}; sell empty callback is ${callbacks.includes(Type$1.SellWithEmptyCallback) ? "allowed" : "not allowed"}; non-empty callbacks are rejected`, (offer) => {
|
|
2948
3043
|
if (!isEmptyCallback(offer)) return { message: "Non-empty callbacks are not supported." };
|
|
@@ -2950,15 +3045,25 @@ const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy
|
|
|
2950
3045
|
if (isEmptyCallback(offer) && !offer.buy && !callbacks.includes(Type$1.SellWithEmptyCallback)) return { message: "Sell offers with empty callback not allowed." };
|
|
2951
3046
|
});
|
|
2952
3047
|
/**
|
|
2953
|
-
* A validation rule that checks if the offer's
|
|
2954
|
-
* @param assetsByChainId - Allowed
|
|
3048
|
+
* A validation rule that checks if the offer's loan token is allowed for its chain.
|
|
3049
|
+
* @param assetsByChainId - Allowed loan tokens indexed by chain id.
|
|
3050
|
+
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
3051
|
+
*/
|
|
3052
|
+
const loanToken = ({ assetsByChainId }) => single("loan_token", "Validates that offer loan token is in the allowed token list for the offer chain", (offer) => {
|
|
3053
|
+
const allowedLoanTokens = assetsByChainId[offer.chainId]?.map((asset) => asset.toLowerCase());
|
|
3054
|
+
if (!allowedLoanTokens || allowedLoanTokens.length === 0) return { message: `No allowed loan tokens for chain ${offer.chainId}` };
|
|
3055
|
+
if (!allowedLoanTokens.includes(offer.loanToken.toLowerCase())) return { message: "Loan token is not allowed" };
|
|
3056
|
+
});
|
|
3057
|
+
/**
|
|
3058
|
+
* A validation rule that checks if the offer's collateral tokens are allowed for its chain.
|
|
3059
|
+
* @param collateralAssetsByChainId - Allowed collateral tokens indexed by chain id.
|
|
2955
3060
|
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
2956
3061
|
*/
|
|
2957
|
-
const
|
|
2958
|
-
const
|
|
2959
|
-
if (
|
|
2960
|
-
if (
|
|
2961
|
-
if (offer.collaterals.some((collateral) => !
|
|
3062
|
+
const collateralToken = ({ collateralAssetsByChainId }) => single("collateral_token", "Validates that offer collateral tokens are in the allowed token list for the offer chain", (offer) => {
|
|
3063
|
+
const allowedCollateralTokens = collateralAssetsByChainId[offer.chainId]?.map((asset) => asset.toLowerCase()) ?? [];
|
|
3064
|
+
if (allowedCollateralTokens.length === 0) return { message: `No allowed collateral tokens for chain ${offer.chainId}` };
|
|
3065
|
+
if (offer.collaterals.length === 0) return { message: "At least one collateral token is required" };
|
|
3066
|
+
if (offer.collaterals.some((collateral) => !allowedCollateralTokens.includes(collateral.asset.toLowerCase()))) return { message: "Collateral token is not allowed" };
|
|
2962
3067
|
});
|
|
2963
3068
|
/**
|
|
2964
3069
|
* A validation rule that checks if the offer's oracle addresses are allowed for its chain.
|
|
@@ -3001,21 +3106,24 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
|
|
|
3001
3106
|
//#region src/gatekeeper/morphoRules.ts
|
|
3002
3107
|
const morphoRules = (chains) => {
|
|
3003
3108
|
const assetsByChainId = {};
|
|
3109
|
+
const collateralAssetsByChainId = {};
|
|
3004
3110
|
const oraclesByChainId = {};
|
|
3005
3111
|
for (const chain of chains) {
|
|
3006
3112
|
assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
|
|
3113
|
+
collateralAssetsByChainId[chain.id] = collateralAssets[chain.id.toString()] ?? [];
|
|
3007
3114
|
oraclesByChainId[chain.id] = oracles$1[chain.id.toString()] ?? [];
|
|
3008
3115
|
}
|
|
3009
3116
|
return [
|
|
3010
3117
|
sameMaker(),
|
|
3011
3118
|
amountMutualExclusivity(),
|
|
3012
3119
|
chains$1({ chains }),
|
|
3013
|
-
maturity({ maturities: [MaturityType.
|
|
3120
|
+
maturity({ maturities: [MaturityType.EndOfWeek, MaturityType.EndOfNextWeek] }),
|
|
3014
3121
|
callback({
|
|
3015
3122
|
callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
|
|
3016
3123
|
allowedAddresses: []
|
|
3017
3124
|
}),
|
|
3018
|
-
|
|
3125
|
+
loanToken({ assetsByChainId }),
|
|
3126
|
+
collateralToken({ collateralAssetsByChainId }),
|
|
3019
3127
|
oracle({ oraclesByChainId })
|
|
3020
3128
|
];
|
|
3021
3129
|
};
|
|
@@ -3023,7 +3131,7 @@ const morphoRules = (chains) => {
|
|
|
3023
3131
|
//#endregion
|
|
3024
3132
|
//#region src/gatekeeper/ConfigRules.ts
|
|
3025
3133
|
/**
|
|
3026
|
-
* Build the configured rules (maturities + callback addresses + loan tokens + oracles) for the provided chains.
|
|
3134
|
+
* Build the configured rules (maturities + callback addresses + loan tokens + collateral tokens + oracles) for the provided chains.
|
|
3027
3135
|
* @param chains - Chains to include in the configured rules.
|
|
3028
3136
|
* @returns Sorted list of config rules.
|
|
3029
3137
|
*/
|
|
@@ -3043,6 +3151,12 @@ function buildConfigRules(chains) {
|
|
|
3043
3151
|
chain_id: chain.id,
|
|
3044
3152
|
address: normalizeAddress(address)
|
|
3045
3153
|
});
|
|
3154
|
+
const collateralTokens = collateralAssets[chain.id.toString()] ?? [];
|
|
3155
|
+
for (const address of collateralTokens) rules.push({
|
|
3156
|
+
type: "collateral_token",
|
|
3157
|
+
chain_id: chain.id,
|
|
3158
|
+
address: normalizeAddress(address)
|
|
3159
|
+
});
|
|
3046
3160
|
const oracles = oracles$1[chain.id.toString()] ?? [];
|
|
3047
3161
|
for (const address of oracles) rules.push({
|
|
3048
3162
|
type: "oracle",
|
|
@@ -3070,6 +3184,10 @@ function buildConfigRulesChecksum(rules) {
|
|
|
3070
3184
|
hash.update(`callback:${rule.chain_id}:${rule.callback_type}:${rule.address}\n`);
|
|
3071
3185
|
continue;
|
|
3072
3186
|
}
|
|
3187
|
+
if (rule.type === "collateral_token") {
|
|
3188
|
+
hash.update(`collateral_token:${rule.chain_id}:${rule.address}\n`);
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3073
3191
|
if (rule.type === "oracle") {
|
|
3074
3192
|
hash.update(`oracle:${rule.chain_id}:${rule.address}\n`);
|
|
3075
3193
|
continue;
|
|
@@ -3090,6 +3208,7 @@ function compareConfigRules(left, right) {
|
|
|
3090
3208
|
return left.address.localeCompare(right.address);
|
|
3091
3209
|
}
|
|
3092
3210
|
if (left.type === "loan_token" && right.type === "loan_token") return left.address.localeCompare(right.address);
|
|
3211
|
+
if (left.type === "collateral_token" && right.type === "collateral_token") return left.address.localeCompare(right.address);
|
|
3093
3212
|
if (left.type === "oracle" && right.type === "oracle") return left.address.localeCompare(right.address);
|
|
3094
3213
|
return 0;
|
|
3095
3214
|
}
|
|
@@ -3097,8 +3216,10 @@ function compareConfigRules(left, right) {
|
|
|
3097
3216
|
//#endregion
|
|
3098
3217
|
//#region src/api/Schema/BookResponse.ts
|
|
3099
3218
|
function from$6(level) {
|
|
3219
|
+
const price = tickToPrice(level.tick);
|
|
3100
3220
|
return {
|
|
3101
|
-
|
|
3221
|
+
tick: level.tick,
|
|
3222
|
+
price: price.toString(),
|
|
3102
3223
|
assets: level.assets.toString(),
|
|
3103
3224
|
count: level.count
|
|
3104
3225
|
};
|
|
@@ -3144,6 +3265,7 @@ const RouterStatusResponse = z$1.object({
|
|
|
3144
3265
|
* Creates an `ObligationResponse` from a `Obligation`.
|
|
3145
3266
|
* @constructor
|
|
3146
3267
|
* @param obligation - {@link Obligation}
|
|
3268
|
+
* @param quote - {@link Quote}
|
|
3147
3269
|
* @returns The created `ObligationResponse`. {@link ObligationResponse}
|
|
3148
3270
|
*/
|
|
3149
3271
|
function from$5(obligation, quote) {
|
|
@@ -3157,8 +3279,14 @@ function from$5(obligation, quote) {
|
|
|
3157
3279
|
oracle: c.oracle
|
|
3158
3280
|
})),
|
|
3159
3281
|
maturity: obligation.maturity,
|
|
3160
|
-
ask: {
|
|
3161
|
-
|
|
3282
|
+
ask: {
|
|
3283
|
+
tick: quote.ask.tick,
|
|
3284
|
+
price: quote.ask.price.toString()
|
|
3285
|
+
},
|
|
3286
|
+
bid: {
|
|
3287
|
+
tick: quote.bid.tick,
|
|
3288
|
+
price: quote.bid.price.toString()
|
|
3289
|
+
}
|
|
3162
3290
|
};
|
|
3163
3291
|
}
|
|
3164
3292
|
|
|
@@ -3201,11 +3329,12 @@ function from$4(input) {
|
|
|
3201
3329
|
obligation_shares: input.obligationShares.toString(),
|
|
3202
3330
|
start: input.start,
|
|
3203
3331
|
expiry: input.expiry,
|
|
3204
|
-
|
|
3332
|
+
tick: input.tick,
|
|
3205
3333
|
group: input.group,
|
|
3206
3334
|
session: input.session,
|
|
3207
3335
|
callback: input.callback.address,
|
|
3208
|
-
callback_data: input.callback.data
|
|
3336
|
+
callback_data: input.callback.data,
|
|
3337
|
+
receiver_if_maker_is_seller: input.receiverIfMakerIsSeller
|
|
3209
3338
|
},
|
|
3210
3339
|
offer_hash: input.hash,
|
|
3211
3340
|
obligation_id: id({
|
|
@@ -3272,7 +3401,7 @@ var InternalServerError = class extends APIError {
|
|
|
3272
3401
|
super(STATUS_CODE.INTERNAL_SERVER_ERROR, message, "INTERNAL_SERVER_ERROR");
|
|
3273
3402
|
}
|
|
3274
3403
|
};
|
|
3275
|
-
var BadRequestError = class extends APIError {
|
|
3404
|
+
var BadRequestError$1 = class extends APIError {
|
|
3276
3405
|
constructor(message = "Invalid JSON format", details) {
|
|
3277
3406
|
super(STATUS_CODE.BAD_REQUEST, message, "BAD_REQUEST", details);
|
|
3278
3407
|
}
|
|
@@ -3294,7 +3423,7 @@ function success(args) {
|
|
|
3294
3423
|
*/
|
|
3295
3424
|
function failure(err) {
|
|
3296
3425
|
if (err instanceof APIError) return handleAPIError(err);
|
|
3297
|
-
if (err instanceof SyntaxError) return handleAPIError(new BadRequestError(err.message));
|
|
3426
|
+
if (err instanceof SyntaxError) return handleAPIError(new BadRequestError$1(err.message));
|
|
3298
3427
|
if (err instanceof z$2.ZodError) return handleAPIError(handleZodError(err));
|
|
3299
3428
|
return handleAPIError(new InternalServerError());
|
|
3300
3429
|
}
|
|
@@ -3344,7 +3473,7 @@ function __decorate(decorators, target, key, desc) {
|
|
|
3344
3473
|
//#region src/api/Schema/openapi.ts
|
|
3345
3474
|
const timestampExample = "2024-01-01T12:00:00.000Z";
|
|
3346
3475
|
const offerCursorExample = "eyJvZmZzZXQiOjEwMH0";
|
|
3347
|
-
const obligationCursorExample = "
|
|
3476
|
+
const obligationCursorExample = "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ";
|
|
3348
3477
|
const offerExample = {
|
|
3349
3478
|
offer: {
|
|
3350
3479
|
obligation: {
|
|
@@ -3363,11 +3492,12 @@ const offerExample = {
|
|
|
3363
3492
|
obligation_shares: "0",
|
|
3364
3493
|
start: 1761922790,
|
|
3365
3494
|
expiry: 1761922799,
|
|
3366
|
-
|
|
3495
|
+
tick: 495,
|
|
3367
3496
|
group: "0x000000000000000000000000000000000000000000000000000000000008b8f4",
|
|
3368
3497
|
session: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3369
3498
|
callback: "0x0000000000000000000000000000000000000000",
|
|
3370
|
-
callback_data: "0x"
|
|
3499
|
+
callback_data: "0x",
|
|
3500
|
+
receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
3371
3501
|
},
|
|
3372
3502
|
offer_hash: "0xac4bd8318ec914f89f8af913f162230575b0ac0696a19256bc12138c5cfe1427",
|
|
3373
3503
|
obligation_id: "0x25690ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9abc",
|
|
@@ -3404,7 +3534,7 @@ const validateOfferExample = {
|
|
|
3404
3534
|
assets: "369216000000000000000000",
|
|
3405
3535
|
obligation_units: "0",
|
|
3406
3536
|
obligation_shares: "0",
|
|
3407
|
-
|
|
3537
|
+
tick: 495,
|
|
3408
3538
|
maturity: 1761922799,
|
|
3409
3539
|
expiry: 1761922799,
|
|
3410
3540
|
start: 1761922790,
|
|
@@ -3421,7 +3551,8 @@ const validateOfferExample = {
|
|
|
3421
3551
|
callback: {
|
|
3422
3552
|
address: "0x0000000000000000000000000000000000000000",
|
|
3423
3553
|
data: "0x"
|
|
3424
|
-
}
|
|
3554
|
+
},
|
|
3555
|
+
receiver_if_maker_is_seller: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401"
|
|
3425
3556
|
};
|
|
3426
3557
|
const routerStatusExample = {
|
|
3427
3558
|
status: "live",
|
|
@@ -3492,11 +3623,23 @@ __decorate([ApiProperty({
|
|
|
3492
3623
|
example: validateOfferExample.callback.data
|
|
3493
3624
|
})], ValidateCallbackRequest.prototype, "data", void 0);
|
|
3494
3625
|
var AskResponse = class {};
|
|
3626
|
+
__decorate([ApiProperty({
|
|
3627
|
+
type: "number",
|
|
3628
|
+
nullable: true,
|
|
3629
|
+
example: 500,
|
|
3630
|
+
description: "Best ask tick. Null when there is no active ask quote."
|
|
3631
|
+
})], AskResponse.prototype, "tick", void 0);
|
|
3495
3632
|
__decorate([ApiProperty({
|
|
3496
3633
|
type: "string",
|
|
3497
3634
|
example: "1000000000000000000"
|
|
3498
3635
|
})], AskResponse.prototype, "price", void 0);
|
|
3499
3636
|
var BidResponse = class {};
|
|
3637
|
+
__decorate([ApiProperty({
|
|
3638
|
+
type: "number",
|
|
3639
|
+
nullable: true,
|
|
3640
|
+
example: 500,
|
|
3641
|
+
description: "Best bid tick. Null when there is no active bid quote."
|
|
3642
|
+
})], BidResponse.prototype, "tick", void 0);
|
|
3500
3643
|
__decorate([ApiProperty({
|
|
3501
3644
|
type: "string",
|
|
3502
3645
|
example: "1000000000000000000"
|
|
@@ -3548,9 +3691,11 @@ __decorate([ApiProperty({
|
|
|
3548
3691
|
example: offerExample.offer.expiry
|
|
3549
3692
|
})], OfferDataResponse.prototype, "expiry", void 0);
|
|
3550
3693
|
__decorate([ApiProperty({
|
|
3551
|
-
type: "
|
|
3552
|
-
example: offerExample.offer.
|
|
3553
|
-
|
|
3694
|
+
type: "number",
|
|
3695
|
+
example: offerExample.offer.tick,
|
|
3696
|
+
minimum: 0,
|
|
3697
|
+
maximum: 990
|
|
3698
|
+
})], OfferDataResponse.prototype, "tick", void 0);
|
|
3554
3699
|
__decorate([ApiProperty({
|
|
3555
3700
|
type: "string",
|
|
3556
3701
|
example: offerExample.offer.group
|
|
@@ -3567,6 +3712,10 @@ __decorate([ApiProperty({
|
|
|
3567
3712
|
type: "string",
|
|
3568
3713
|
example: offerExample.offer.callback_data
|
|
3569
3714
|
})], OfferDataResponse.prototype, "callback_data", void 0);
|
|
3715
|
+
__decorate([ApiProperty({
|
|
3716
|
+
type: "string",
|
|
3717
|
+
example: offerExample.offer.receiver_if_maker_is_seller
|
|
3718
|
+
})], OfferDataResponse.prototype, "receiver_if_maker_is_seller", void 0);
|
|
3570
3719
|
var OfferListItemResponse = class {};
|
|
3571
3720
|
__decorate([ApiProperty({
|
|
3572
3721
|
type: () => OfferDataResponse,
|
|
@@ -3791,9 +3940,11 @@ __decorate([ApiProperty({
|
|
|
3791
3940
|
required: false
|
|
3792
3941
|
})], ValidateOfferRequest.prototype, "obligation_shares", void 0);
|
|
3793
3942
|
__decorate([ApiProperty({
|
|
3794
|
-
type: "
|
|
3795
|
-
example: validateOfferExample.
|
|
3796
|
-
|
|
3943
|
+
type: "number",
|
|
3944
|
+
example: validateOfferExample.tick,
|
|
3945
|
+
minimum: 0,
|
|
3946
|
+
maximum: 990
|
|
3947
|
+
})], ValidateOfferRequest.prototype, "tick", void 0);
|
|
3797
3948
|
__decorate([ApiProperty({
|
|
3798
3949
|
type: "number",
|
|
3799
3950
|
example: validateOfferExample.maturity
|
|
@@ -3834,6 +3985,10 @@ __decorate([ApiProperty({
|
|
|
3834
3985
|
type: () => ValidateCallbackRequest,
|
|
3835
3986
|
example: validateOfferExample.callback
|
|
3836
3987
|
})], ValidateOfferRequest.prototype, "callback", void 0);
|
|
3988
|
+
__decorate([ApiProperty({
|
|
3989
|
+
type: "string",
|
|
3990
|
+
example: validateOfferExample.receiver_if_maker_is_seller
|
|
3991
|
+
})], ValidateOfferRequest.prototype, "receiver_if_maker_is_seller", void 0);
|
|
3837
3992
|
var ValidateOffersRequest = class {};
|
|
3838
3993
|
__decorate([ApiProperty({
|
|
3839
3994
|
type: () => [ValidateOfferRequest],
|
|
@@ -3893,9 +4048,16 @@ __decorate([ApiProperty({
|
|
|
3893
4048
|
description: "List of validation issues. Returned when any offer fails validation."
|
|
3894
4049
|
})], ValidationFailureResponse.prototype, "data", void 0);
|
|
3895
4050
|
var BookLevelResponse = class {};
|
|
4051
|
+
__decorate([ApiProperty({
|
|
4052
|
+
type: "number",
|
|
4053
|
+
example: 495,
|
|
4054
|
+
minimum: 0,
|
|
4055
|
+
maximum: 990
|
|
4056
|
+
})], BookLevelResponse.prototype, "tick", void 0);
|
|
3896
4057
|
__decorate([ApiProperty({
|
|
3897
4058
|
type: "string",
|
|
3898
|
-
example: "
|
|
4059
|
+
example: "500000000000000000",
|
|
4060
|
+
description: "Price derived from tick, scaled by 1e18."
|
|
3899
4061
|
})], BookLevelResponse.prototype, "price", void 0);
|
|
3900
4062
|
__decorate([ApiProperty({
|
|
3901
4063
|
type: "string",
|
|
@@ -3909,6 +4071,7 @@ const positionExample = {
|
|
|
3909
4071
|
chain_id: 1,
|
|
3910
4072
|
contract: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078",
|
|
3911
4073
|
user: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
|
|
4074
|
+
obligation_id: "0x12590ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9f67",
|
|
3912
4075
|
reserved: "200000000000000000000",
|
|
3913
4076
|
block_number: 21345678
|
|
3914
4077
|
};
|
|
@@ -3925,6 +4088,12 @@ __decorate([ApiProperty({
|
|
|
3925
4088
|
type: "string",
|
|
3926
4089
|
example: positionExample.user
|
|
3927
4090
|
})], PositionListItemResponse.prototype, "user", void 0);
|
|
4091
|
+
__decorate([ApiProperty({
|
|
4092
|
+
type: "string",
|
|
4093
|
+
nullable: true,
|
|
4094
|
+
example: positionExample.obligation_id,
|
|
4095
|
+
description: "Obligation id this reserved amount belongs to, or null if no lots exist."
|
|
4096
|
+
})], PositionListItemResponse.prototype, "obligation_id", void 0);
|
|
3928
4097
|
__decorate([ApiProperty({
|
|
3929
4098
|
type: "string",
|
|
3930
4099
|
example: positionExample.reserved
|
|
@@ -3952,7 +4121,7 @@ __decorate([ApiProperty({
|
|
|
3952
4121
|
})], BookListResponse.prototype, "cursor", void 0);
|
|
3953
4122
|
__decorate([ApiProperty({
|
|
3954
4123
|
type: () => [BookLevelResponse],
|
|
3955
|
-
description: "Aggregated book levels grouped by
|
|
4124
|
+
description: "Aggregated book levels grouped by offer tick."
|
|
3956
4125
|
})], BookListResponse.prototype, "data", void 0);
|
|
3957
4126
|
let BooksController = class BooksController {
|
|
3958
4127
|
async getBook() {}
|
|
@@ -3962,7 +4131,7 @@ __decorate([
|
|
|
3962
4131
|
methods: ["get"],
|
|
3963
4132
|
path: "/v1/books/{obligationId}/{side}",
|
|
3964
4133
|
summary: "Get aggregated book",
|
|
3965
|
-
description: "Returns aggregated book data for a given obligation and side. Offers are grouped by
|
|
4134
|
+
description: "Returns aggregated book data for a given obligation and side. Offers are grouped by tick with summed takeable amounts, and each level includes the corresponding wad-scaled price. Book levels are sorted by tick (ascending for sell side, descending for buy side)."
|
|
3966
4135
|
}),
|
|
3967
4136
|
ApiParam({
|
|
3968
4137
|
name: "obligationId",
|
|
@@ -3987,7 +4156,7 @@ __decorate([
|
|
|
3987
4156
|
name: "limit",
|
|
3988
4157
|
type: "number",
|
|
3989
4158
|
example: 10,
|
|
3990
|
-
description: "Maximum number of
|
|
4159
|
+
description: "Maximum number of tick levels to return."
|
|
3991
4160
|
}),
|
|
3992
4161
|
ApiResponse({
|
|
3993
4162
|
status: 200,
|
|
@@ -4181,6 +4350,11 @@ const configRulesLoanTokenExample = {
|
|
|
4181
4350
|
chain_id: 1,
|
|
4182
4351
|
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
|
4183
4352
|
};
|
|
4353
|
+
const configRulesCollateralTokenExample = {
|
|
4354
|
+
type: "collateral_token",
|
|
4355
|
+
chain_id: 1,
|
|
4356
|
+
address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
4357
|
+
};
|
|
4184
4358
|
const configRulesOracleExample = {
|
|
4185
4359
|
type: "oracle",
|
|
4186
4360
|
chain_id: 1,
|
|
@@ -4190,6 +4364,7 @@ const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
|
|
|
4190
4364
|
const configRulesPayloadExample = [
|
|
4191
4365
|
configRulesMaturityExample,
|
|
4192
4366
|
configRulesLoanTokenExample,
|
|
4367
|
+
configRulesCollateralTokenExample,
|
|
4193
4368
|
configRulesOracleExample
|
|
4194
4369
|
];
|
|
4195
4370
|
const configContractNames = [
|
|
@@ -4316,7 +4491,7 @@ __decorate([
|
|
|
4316
4491
|
methods: ["get"],
|
|
4317
4492
|
path: "/v1/config/rules",
|
|
4318
4493
|
summary: "Get config rules",
|
|
4319
|
-
description: "Returns configured rules (maturities, loan tokens, oracles) for supported chains."
|
|
4494
|
+
description: "Returns configured rules (maturities, loan tokens, collateral tokens, oracles) for supported chains."
|
|
4320
4495
|
}),
|
|
4321
4496
|
ApiQuery({
|
|
4322
4497
|
name: "cursor",
|
|
@@ -4336,7 +4511,7 @@ __decorate([
|
|
|
4336
4511
|
name: "types",
|
|
4337
4512
|
type: ["string"],
|
|
4338
4513
|
required: false,
|
|
4339
|
-
example: "maturity,loan_token,oracle",
|
|
4514
|
+
example: "maturity,loan_token,collateral_token,oracle",
|
|
4340
4515
|
description: "Filter by rule types (comma-separated).",
|
|
4341
4516
|
style: "form",
|
|
4342
4517
|
explode: false
|
|
@@ -4366,13 +4541,13 @@ __decorate([
|
|
|
4366
4541
|
methods: ["get"],
|
|
4367
4542
|
path: "/v1/obligations",
|
|
4368
4543
|
summary: "List all obligations",
|
|
4369
|
-
description: "Returns a list of obligations with their current best ask and bid.
|
|
4544
|
+
description: "Returns a list of obligations with their current best ask and bid. Sorting is customizable with the sort parameter and defaults to id ascending."
|
|
4370
4545
|
}),
|
|
4371
4546
|
ApiQuery({
|
|
4372
4547
|
name: "cursor",
|
|
4373
4548
|
type: "string",
|
|
4374
4549
|
example: obligationCursorExample,
|
|
4375
|
-
description: "
|
|
4550
|
+
description: "Pagination cursor in base64url-encoded format."
|
|
4376
4551
|
}),
|
|
4377
4552
|
ApiQuery({
|
|
4378
4553
|
name: "limit",
|
|
@@ -4416,6 +4591,15 @@ __decorate([
|
|
|
4416
4591
|
style: "form",
|
|
4417
4592
|
explode: false
|
|
4418
4593
|
}),
|
|
4594
|
+
ApiQuery({
|
|
4595
|
+
name: "sort",
|
|
4596
|
+
type: "string",
|
|
4597
|
+
required: false,
|
|
4598
|
+
example: "-ask,bid,maturity",
|
|
4599
|
+
description: "Sort order as comma-separated fields (`id`, `ask`, `bid`, `maturity`). Prefix with `-` for descending order. Max 3 fields.",
|
|
4600
|
+
style: "form",
|
|
4601
|
+
explode: false
|
|
4602
|
+
}),
|
|
4419
4603
|
ApiResponse({
|
|
4420
4604
|
status: 200,
|
|
4421
4605
|
description: "Success",
|
|
@@ -4454,7 +4638,7 @@ __decorate([
|
|
|
4454
4638
|
methods: ["get"],
|
|
4455
4639
|
path: "/v1/users/{userAddress}/positions",
|
|
4456
4640
|
summary: "Get user positions",
|
|
4457
|
-
description: "Returns positions for a user with reserved balance.
|
|
4641
|
+
description: "Returns positions for a user with reserved balance per obligation. Each (position, obligation) pair is returned as a separate row. Positions with no lots return a single row with obligation_id = null and reserved = 0."
|
|
4458
4642
|
}),
|
|
4459
4643
|
ApiParam({
|
|
4460
4644
|
name: "userAddress",
|
|
@@ -4541,6 +4725,7 @@ function from$3(position) {
|
|
|
4541
4725
|
chain_id: position.chainId,
|
|
4542
4726
|
contract: position.contract,
|
|
4543
4727
|
user: position.user,
|
|
4728
|
+
obligation_id: position.obligationId,
|
|
4544
4729
|
reserved: position.reserved.toString(),
|
|
4545
4730
|
block_number: position.blockNumber
|
|
4546
4731
|
};
|
|
@@ -4549,11 +4734,13 @@ function from$3(position) {
|
|
|
4549
4734
|
//#endregion
|
|
4550
4735
|
//#region src/api/Schema/requests.ts
|
|
4551
4736
|
const MAX_LIMIT = 100;
|
|
4552
|
-
const DEFAULT_LIMIT$
|
|
4737
|
+
const DEFAULT_LIMIT$5 = 20;
|
|
4738
|
+
const MAX_OBLIGATION_SORT_FIELDS = 3;
|
|
4553
4739
|
const CONFIG_RULES_MAX_LIMIT = 1e3;
|
|
4554
4740
|
const CONFIG_RULES_DEFAULT_LIMIT = 100;
|
|
4555
4741
|
const CONFIG_CONTRACTS_MAX_LIMIT = 1e3;
|
|
4556
4742
|
const CONFIG_CONTRACTS_DEFAULT_LIMIT = 1e3;
|
|
4743
|
+
const OBLIGATION_SORT_ENTRY_REGEX = /^-?(id|ask|bid|maturity)$/;
|
|
4557
4744
|
/** Validate cursor is a valid base64url-encoded JSON object.
|
|
4558
4745
|
* Domain layer handles semantic validation of cursor fields. */
|
|
4559
4746
|
function isValidBase64urlJson(val) {
|
|
@@ -4585,8 +4772,8 @@ const PaginationQueryParams = z$2.object({
|
|
|
4585
4772
|
description: "Pagination cursor in base64url-encoded format",
|
|
4586
4773
|
example: "eyJzaWRlIjoic2VsbCIsImN1cnJlbnRQcmljZSI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJibG9ja051bWJlciI6MSwiYXNzZXRzIjoiMTAwMDAwMDAwMDAwMDAwMDAwMCIsImhhc2giOiIweGRmZDY4NTllM2UwODJkMTkzODlhMWFlYzFiZGFkN2U4ZDkyZDk2YjFhYTc5NDBkYTkxYTMxMjVkMzFlM2JlNWIiLCJ0b3RhbFJldHVybmVkIjoxMCwibm93IjoxNjAwMDAwMDAwfQ"
|
|
4587
4774
|
}),
|
|
4588
|
-
limit: z$2.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$2.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$
|
|
4589
|
-
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$
|
|
4775
|
+
limit: z$2.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$2.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
|
|
4776
|
+
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
|
|
4590
4777
|
example: 10
|
|
4591
4778
|
})
|
|
4592
4779
|
});
|
|
@@ -4594,10 +4781,11 @@ const ConfigRuleTypes = z$2.enum([
|
|
|
4594
4781
|
"maturity",
|
|
4595
4782
|
"callback",
|
|
4596
4783
|
"loan_token",
|
|
4784
|
+
"collateral_token",
|
|
4597
4785
|
"oracle"
|
|
4598
4786
|
]);
|
|
4599
4787
|
const GetConfigRulesQueryParams = z$2.object({
|
|
4600
|
-
cursor: z$2.string().regex(/^(maturity|callback|loan_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
|
|
4788
|
+
cursor: z$2.string().regex(/^(maturity|callback|loan_token|collateral_token|oracle):[1-9]\d*:.+$/, { message: "Cursor must be in the format type:chain_id:<value>" }).optional().meta({
|
|
4601
4789
|
description: "Pagination cursor in type:chain_id:<value> format",
|
|
4602
4790
|
example: "maturity:1:1730415600:end_of_next_month"
|
|
4603
4791
|
}),
|
|
@@ -4607,7 +4795,7 @@ const GetConfigRulesQueryParams = z$2.object({
|
|
|
4607
4795
|
}),
|
|
4608
4796
|
types: csvArray(ConfigRuleTypes).meta({
|
|
4609
4797
|
description: "Filter by rule types (comma-separated).",
|
|
4610
|
-
example: "maturity,loan_token,oracle"
|
|
4798
|
+
example: "maturity,loan_token,collateral_token,oracle"
|
|
4611
4799
|
}),
|
|
4612
4800
|
chains: csvArray(z$2.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
4613
4801
|
description: "Filter by chain IDs (comma-separated).",
|
|
@@ -4682,9 +4870,12 @@ const GetOffersQueryParams = PaginationQueryParams.omit({ cursor: true }).extend
|
|
|
4682
4870
|
});
|
|
4683
4871
|
const GetObligationsQueryParams = z$2.object({
|
|
4684
4872
|
...PaginationQueryParams.shape,
|
|
4685
|
-
cursor: z$2.string().optional().
|
|
4686
|
-
|
|
4687
|
-
|
|
4873
|
+
cursor: z$2.string().optional().refine((val) => {
|
|
4874
|
+
if (!val) return true;
|
|
4875
|
+
return isValidBase64urlJson(val);
|
|
4876
|
+
}, { message: "Invalid cursor format. Must be a valid base64url-encoded cursor object" }).meta({
|
|
4877
|
+
description: "Pagination cursor in base64url-encoded format.",
|
|
4878
|
+
example: "eyJzb3J0IjpbImlkIl0sImlkIjoiMHgxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAiLCJhc2siOiIwIiwiYmlkIjoiMCIsIm1hdHVyaXR5IjoxNzYxOTIyODAwfQ"
|
|
4688
4879
|
}),
|
|
4689
4880
|
chains: csvArray(z$2.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
4690
4881
|
description: "Filter by chain IDs (comma-separated).",
|
|
@@ -4701,18 +4892,35 @@ const GetObligationsQueryParams = z$2.object({
|
|
|
4701
4892
|
maturities: csvArray(z$2.string().regex(/^[1-9]\d*$/, { message: "Maturity must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
4702
4893
|
description: "Filter by exact maturity timestamps (comma-separated, unix seconds).",
|
|
4703
4894
|
example: "1761922800,1764524800"
|
|
4895
|
+
}),
|
|
4896
|
+
sort: csvArray(z$2.string().regex(OBLIGATION_SORT_ENTRY_REGEX, { message: "Sort entries must be one of: id, ask, bid, maturity (optionally prefixed with '-')" })).refine((entries) => entries === void 0 || entries.length <= MAX_OBLIGATION_SORT_FIELDS, { message: `Sort cannot include more than ${MAX_OBLIGATION_SORT_FIELDS} fields` }).superRefine((entries, ctx) => {
|
|
4897
|
+
if (!entries) return;
|
|
4898
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4899
|
+
for (const entry of entries) {
|
|
4900
|
+
const field = entry.startsWith("-") ? entry.slice(1) : entry;
|
|
4901
|
+
if (seen.has(field)) {
|
|
4902
|
+
ctx.addIssue({
|
|
4903
|
+
code: "custom",
|
|
4904
|
+
message: `Duplicate sort field: ${field}`
|
|
4905
|
+
});
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
seen.add(field);
|
|
4909
|
+
}
|
|
4910
|
+
}).meta({
|
|
4911
|
+
description: "Sort order as comma-separated fields. Prefix a field with '-' for descending order. Max 3 fields.",
|
|
4912
|
+
example: "-ask,bid,maturity"
|
|
4704
4913
|
})
|
|
4705
4914
|
});
|
|
4706
4915
|
const GetObligationParams = z$2.object({ obligation_id: z$2.string({ error: "Obligation id is required and must be a valid 32-byte hex string" }).regex(/^0x[a-fA-F0-9]{64}$/, { error: "Obligation id must be a valid 32-byte hex string" }).transform((val) => val.toLowerCase()).meta({
|
|
4707
4916
|
description: "Obligation id",
|
|
4708
4917
|
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
4709
4918
|
}) });
|
|
4710
|
-
/** Validate a book cursor format: {side,
|
|
4919
|
+
/** Validate a book cursor format: {side, lastTick, offersCursor} */
|
|
4711
4920
|
function isValidBookCursor(cursorString) {
|
|
4712
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
4713
4921
|
try {
|
|
4714
4922
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
4715
|
-
return (v?.side === "buy" || v?.side === "sell") &&
|
|
4923
|
+
return (v?.side === "buy" || v?.side === "sell") && typeof v?.lastTick === "number" && Number.isInteger(v.lastTick) && (v?.offersCursor === null || typeof v?.offersCursor === "string");
|
|
4716
4924
|
} catch {
|
|
4717
4925
|
return false;
|
|
4718
4926
|
}
|
|
@@ -4725,8 +4933,8 @@ const BookPaginationQueryParams = z$2.object({
|
|
|
4725
4933
|
description: "Pagination cursor in base64url-encoded format for book levels",
|
|
4726
4934
|
example: "eyJzaWRlIjoiYnV5IiwibGFzdFJhdGUiOiIxMDAwMDAwMDAwMDAwMDAwMDAwIiwib2ZmZXJzQ3Vyc29yIjpudWxsfQ"
|
|
4727
4935
|
}),
|
|
4728
|
-
limit: z$2.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$2.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$
|
|
4729
|
-
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$
|
|
4936
|
+
limit: z$2.string().regex(/^[1-9]\d*$/, { message: "Limit must be a positive integer" }).transform((val) => Number.parseInt(val, 10)).pipe(z$2.number().max(MAX_LIMIT, { message: `Limit cannot exceed ${MAX_LIMIT}` })).optional().default(DEFAULT_LIMIT$5).meta({
|
|
4937
|
+
description: `Limit maximum: ${MAX_LIMIT}. Default: ${DEFAULT_LIMIT$5}`,
|
|
4730
4938
|
example: 10
|
|
4731
4939
|
})
|
|
4732
4940
|
});
|
|
@@ -4801,8 +5009,8 @@ async function getConfigRules(query, chains) {
|
|
|
4801
5009
|
} catch (err) {
|
|
4802
5010
|
return failure(err);
|
|
4803
5011
|
}
|
|
4804
|
-
if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError("Cursor type must match requested rule types"));
|
|
4805
|
-
if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError("Cursor chain_id must match requested chains"));
|
|
5012
|
+
if (cursorRule && typeFilter && !typeFilter.has(cursorRule.type)) return failure(new BadRequestError$1("Cursor type must match requested rule types"));
|
|
5013
|
+
if (cursorRule && chainFilter && !chainFilter.has(cursorRule.chain_id)) return failure(new BadRequestError$1("Cursor chain_id must match requested chains"));
|
|
4806
5014
|
const startIndex = cursorRule ? findStartIndex$1(filteredRules, cursorRule) : 0;
|
|
4807
5015
|
const page = filteredRules.slice(startIndex, startIndex + limit);
|
|
4808
5016
|
const nextCursor = startIndex + limit < filteredRules.length && page.length > 0 ? formatCursor$1(page.at(-1)) : null;
|
|
@@ -4817,19 +5025,20 @@ function formatCursor$1(rule) {
|
|
|
4817
5025
|
if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
|
|
4818
5026
|
if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
|
|
4819
5027
|
if (rule.type === "oracle") return `oracle:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
5028
|
+
if (rule.type === "collateral_token") return `collateral_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
4820
5029
|
return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
4821
5030
|
}
|
|
4822
5031
|
function parseCursor$1(cursor) {
|
|
4823
5032
|
const [type, chain, ...rest] = cursor.split(":");
|
|
4824
|
-
if (!type || !chain || rest.length === 0) throw new BadRequestError("Cursor must be in the format type:chain_id:<value>");
|
|
4825
|
-
if (!isConfigRuleType(type)) throw new BadRequestError("Cursor has an invalid rule type");
|
|
5033
|
+
if (!type || !chain || rest.length === 0) throw new BadRequestError$1("Cursor must be in the format type:chain_id:<value>");
|
|
5034
|
+
if (!isConfigRuleType(type)) throw new BadRequestError$1("Cursor has an invalid rule type");
|
|
4826
5035
|
const chain_id = Number.parseInt(chain, 10);
|
|
4827
|
-
if (!Number.isFinite(chain_id)) throw new BadRequestError("Cursor has an invalid chain_id");
|
|
5036
|
+
if (!Number.isFinite(chain_id)) throw new BadRequestError$1("Cursor has an invalid chain_id");
|
|
4828
5037
|
if (type === "maturity") {
|
|
4829
5038
|
const timestampValue = Number.parseInt(rest[0] ?? "", 10);
|
|
4830
5039
|
const nameValue = rest.slice(1).join(":");
|
|
4831
|
-
if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError("Cursor must be in the format maturity:chain_id:timestamp:name");
|
|
4832
|
-
if (!isMaturityType(nameValue)) throw new BadRequestError("Cursor has an invalid maturity name");
|
|
5040
|
+
if (!Number.isFinite(timestampValue) || nameValue.length === 0) throw new BadRequestError$1("Cursor must be in the format maturity:chain_id:timestamp:name");
|
|
5041
|
+
if (!isMaturityType(nameValue)) throw new BadRequestError$1("Cursor has an invalid maturity name");
|
|
4833
5042
|
return {
|
|
4834
5043
|
type,
|
|
4835
5044
|
chain_id,
|
|
@@ -4840,8 +5049,8 @@ function parseCursor$1(cursor) {
|
|
|
4840
5049
|
if (type === "callback") {
|
|
4841
5050
|
const callbackTypeValue = rest[0] ?? "";
|
|
4842
5051
|
const addressValue = rest.slice(1).join(":");
|
|
4843
|
-
if (!callbackTypeValue || !addressValue) throw new BadRequestError("Cursor must be in the format callback:chain_id:callback_type:address");
|
|
4844
|
-
if (!isCallbackType(callbackTypeValue)) throw new BadRequestError("Cursor has an invalid callback type");
|
|
5052
|
+
if (!callbackTypeValue || !addressValue) throw new BadRequestError$1("Cursor must be in the format callback:chain_id:callback_type:address");
|
|
5053
|
+
if (!isCallbackType(callbackTypeValue)) throw new BadRequestError$1("Cursor has an invalid callback type");
|
|
4845
5054
|
return {
|
|
4846
5055
|
type,
|
|
4847
5056
|
chain_id,
|
|
@@ -4849,16 +5058,16 @@ function parseCursor$1(cursor) {
|
|
|
4849
5058
|
address: parseAddress(addressValue, "Cursor address")
|
|
4850
5059
|
};
|
|
4851
5060
|
}
|
|
4852
|
-
if (type === "loan_token" || type === "oracle") {
|
|
5061
|
+
if (type === "loan_token" || type === "collateral_token" || type === "oracle") {
|
|
4853
5062
|
const addressValue = rest.join(":");
|
|
4854
|
-
if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
|
|
5063
|
+
if (!addressValue) throw new BadRequestError$1(`Cursor must be in the format ${type}:chain_id:address`);
|
|
4855
5064
|
return {
|
|
4856
5065
|
type,
|
|
4857
5066
|
chain_id,
|
|
4858
5067
|
address: parseAddress(addressValue, "Cursor address")
|
|
4859
5068
|
};
|
|
4860
5069
|
}
|
|
4861
|
-
throw new BadRequestError("Cursor has an invalid rule type");
|
|
5070
|
+
throw new BadRequestError$1("Cursor has an invalid rule type");
|
|
4862
5071
|
}
|
|
4863
5072
|
function findStartIndex$1(rules, cursor) {
|
|
4864
5073
|
let low = 0;
|
|
@@ -4872,11 +5081,11 @@ function findStartIndex$1(rules, cursor) {
|
|
|
4872
5081
|
return low;
|
|
4873
5082
|
}
|
|
4874
5083
|
function parseAddress(address, label) {
|
|
4875
|
-
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError(`${label} must be a valid 20-byte address`);
|
|
5084
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) throw new BadRequestError$1(`${label} must be a valid 20-byte address`);
|
|
4876
5085
|
return address.toLowerCase();
|
|
4877
5086
|
}
|
|
4878
5087
|
function isConfigRuleType(value) {
|
|
4879
|
-
return value === "maturity" || value === "callback" || value === "loan_token" || value === "oracle";
|
|
5088
|
+
return value === "maturity" || value === "callback" || value === "loan_token" || value === "collateral_token" || value === "oracle";
|
|
4880
5089
|
}
|
|
4881
5090
|
function isMaturityType(value) {
|
|
4882
5091
|
return Object.values(MaturityType).includes(value);
|
|
@@ -4885,7 +5094,7 @@ function parseMaturity(value) {
|
|
|
4885
5094
|
try {
|
|
4886
5095
|
return from$16(value);
|
|
4887
5096
|
} catch (err) {
|
|
4888
|
-
throw new BadRequestError(err instanceof Error ? err.message : "Invalid maturity timestamp");
|
|
5097
|
+
throw new BadRequestError$1(err instanceof Error ? err.message : "Invalid maturity timestamp");
|
|
4889
5098
|
}
|
|
4890
5099
|
}
|
|
4891
5100
|
function isCallbackType(value) {
|
|
@@ -4968,7 +5177,7 @@ function formatValue(value) {
|
|
|
4968
5177
|
async function validateOffers(body, gatekeeper) {
|
|
4969
5178
|
const logger = getLogger();
|
|
4970
5179
|
const result = safeParse("validate_offers", body, (issue) => issue.message);
|
|
4971
|
-
if (!result.success) return failure(new BadRequestError(result.error.issues[0]?.message ?? "Invalid request body"));
|
|
5180
|
+
if (!result.success) return failure(new BadRequestError$1(result.error.issues[0]?.message ?? "Invalid request body"));
|
|
4972
5181
|
const { offers: rawOffers } = result.data;
|
|
4973
5182
|
const parsedOffers = [];
|
|
4974
5183
|
const offerIndexByHash = /* @__PURE__ */ new Map();
|
|
@@ -4984,7 +5193,7 @@ async function validateOffers(body, gatekeeper) {
|
|
|
4984
5193
|
} catch (err) {
|
|
4985
5194
|
let message = err instanceof Error ? err.message : String(err);
|
|
4986
5195
|
if (err instanceof InvalidOfferError) message = err.formattedMessage;
|
|
4987
|
-
return failure(new BadRequestError(`Offer at index ${i} failed to parse: ${message}`));
|
|
5196
|
+
return failure(new BadRequestError$1(`Offer at index ${i} failed to parse: ${message}`));
|
|
4988
5197
|
}
|
|
4989
5198
|
}
|
|
4990
5199
|
try {
|
|
@@ -5043,7 +5252,7 @@ function createApp(parameters) {
|
|
|
5043
5252
|
return c.json(failure$4.body, failure$4.statusCode);
|
|
5044
5253
|
}
|
|
5045
5254
|
if (body === null || typeof body !== "object") {
|
|
5046
|
-
const failure$3 = failure(new BadRequestError("Request body must be a JSON object"));
|
|
5255
|
+
const failure$3 = failure(new BadRequestError$1("Request body must be a JSON object"));
|
|
5047
5256
|
return c.json(failure$3.body, failure$3.statusCode);
|
|
5048
5257
|
}
|
|
5049
5258
|
const { statusCode, body: payload } = await validateOffers(body, gatekeeper);
|
|
@@ -5149,7 +5358,7 @@ function now() {
|
|
|
5149
5358
|
|
|
5150
5359
|
//#endregion
|
|
5151
5360
|
//#region src/database/drizzle/VERSION.ts
|
|
5152
|
-
const VERSION = "router_v1.
|
|
5361
|
+
const VERSION = "router_v1.8";
|
|
5153
5362
|
|
|
5154
5363
|
//#endregion
|
|
5155
5364
|
//#region src/database/drizzle/schema.ts
|
|
@@ -5301,10 +5510,7 @@ const offers = s.table(EnumTableName.OFFERS, {
|
|
|
5301
5510
|
precision: 78,
|
|
5302
5511
|
scale: 0
|
|
5303
5512
|
}).notNull().default("0"),
|
|
5304
|
-
|
|
5305
|
-
precision: 78,
|
|
5306
|
-
scale: 0
|
|
5307
|
-
}).notNull(),
|
|
5513
|
+
tick: integer("tick").notNull(),
|
|
5308
5514
|
maturity: integer("maturity").notNull(),
|
|
5309
5515
|
expiry: integer("expiry").notNull(),
|
|
5310
5516
|
start: integer("start").notNull(),
|
|
@@ -5315,6 +5521,7 @@ const offers = s.table(EnumTableName.OFFERS, {
|
|
|
5315
5521
|
buy: boolean("buy").notNull(),
|
|
5316
5522
|
callbackAddress: varchar("callback_address", { length: 42 }).notNull(),
|
|
5317
5523
|
callbackData: text("callback_data").notNull(),
|
|
5524
|
+
receiverIfMakerIsSeller: varchar("receiver_if_maker_is_seller", { length: 42 }),
|
|
5318
5525
|
blockNumber: bigint("block_number", { mode: "number" }).notNull(),
|
|
5319
5526
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
5320
5527
|
}, (table) => [
|
|
@@ -5369,6 +5576,7 @@ const lots = s.table(EnumTableName.LOTS, {
|
|
|
5369
5576
|
user: varchar("user", { length: 42 }).notNull(),
|
|
5370
5577
|
contract: varchar("contract", { length: 42 }).notNull(),
|
|
5371
5578
|
group: varchar("group", { length: 66 }).notNull(),
|
|
5579
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5372
5580
|
lower: numeric("lower", {
|
|
5373
5581
|
precision: 78,
|
|
5374
5582
|
scale: 0
|
|
@@ -5383,7 +5591,8 @@ const lots = s.table(EnumTableName.LOTS, {
|
|
|
5383
5591
|
table.chainId,
|
|
5384
5592
|
table.user,
|
|
5385
5593
|
table.contract,
|
|
5386
|
-
table.group
|
|
5594
|
+
table.group,
|
|
5595
|
+
table.obligationId
|
|
5387
5596
|
],
|
|
5388
5597
|
name: "lots_pk"
|
|
5389
5598
|
}),
|
|
@@ -5419,6 +5628,7 @@ const offsets = s.table(EnumTableName.OFFSETS, {
|
|
|
5419
5628
|
user: varchar("user", { length: 42 }).notNull(),
|
|
5420
5629
|
contract: varchar("contract", { length: 42 }).notNull(),
|
|
5421
5630
|
group: varchar("group", { length: 66 }).notNull(),
|
|
5631
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5422
5632
|
value: numeric("value", {
|
|
5423
5633
|
precision: 78,
|
|
5424
5634
|
scale: 0
|
|
@@ -5428,7 +5638,8 @@ const offsets = s.table(EnumTableName.OFFSETS, {
|
|
|
5428
5638
|
table.chainId,
|
|
5429
5639
|
table.user,
|
|
5430
5640
|
table.contract,
|
|
5431
|
-
table.group
|
|
5641
|
+
table.group,
|
|
5642
|
+
table.obligationId
|
|
5432
5643
|
],
|
|
5433
5644
|
name: "offsets_pk"
|
|
5434
5645
|
}), foreignKey({
|
|
@@ -6019,6 +6230,7 @@ function decodeCallbacks(parameters) {
|
|
|
6019
6230
|
positionContract: loanToken,
|
|
6020
6231
|
positionUser: offer.maker,
|
|
6021
6232
|
group: offer.group,
|
|
6233
|
+
obligationId: obligationId(offer),
|
|
6022
6234
|
size: offer.assets
|
|
6023
6235
|
});
|
|
6024
6236
|
callbacks.push({
|
|
@@ -6691,7 +6903,7 @@ async function* collectPrices(parameters) {
|
|
|
6691
6903
|
//#region src/indexer/collectors/CollectorBuilder.ts
|
|
6692
6904
|
function createBuilder(parameters) {
|
|
6693
6905
|
const { client, db, gatekeeper, options: { maxBlockNumber, blockWindow, interval } = {} } = parameters;
|
|
6694
|
-
const createCollector = (name, collect) => create$
|
|
6906
|
+
const createCollector = (name, collect) => create$17({
|
|
6695
6907
|
name,
|
|
6696
6908
|
collect,
|
|
6697
6909
|
client,
|
|
@@ -6795,7 +7007,7 @@ function from$1(config) {
|
|
|
6795
7007
|
retryAttempts,
|
|
6796
7008
|
retryDelayMs
|
|
6797
7009
|
});
|
|
6798
|
-
return create$
|
|
7010
|
+
return create$19({
|
|
6799
7011
|
client,
|
|
6800
7012
|
collectors: [
|
|
6801
7013
|
offersCollector,
|
|
@@ -6805,7 +7017,7 @@ function from$1(config) {
|
|
|
6805
7017
|
]
|
|
6806
7018
|
});
|
|
6807
7019
|
}
|
|
6808
|
-
function create$
|
|
7020
|
+
function create$19(params) {
|
|
6809
7021
|
const { collectors, client } = params;
|
|
6810
7022
|
const indexerId = `${client.chain.id.toString()}.indexer`;
|
|
6811
7023
|
const tracer = getTracer(`router.${indexerId}`);
|
|
@@ -6834,7 +7046,7 @@ function create$18(params) {
|
|
|
6834
7046
|
|
|
6835
7047
|
//#endregion
|
|
6836
7048
|
//#region src/indexer/collectors/Admin.ts
|
|
6837
|
-
function create$
|
|
7049
|
+
function create$18(parameters) {
|
|
6838
7050
|
const collector = "admin";
|
|
6839
7051
|
const { client, db, options: { maxBatchSize = 25, maxBlockNumber } = {} } = parameters;
|
|
6840
7052
|
const maxBlockNumberBI = maxBlockNumber !== void 0 ? BigInt(maxBlockNumber) : void 0;
|
|
@@ -7074,8 +7286,8 @@ const names = [
|
|
|
7074
7286
|
"positions",
|
|
7075
7287
|
"prices"
|
|
7076
7288
|
];
|
|
7077
|
-
function create$
|
|
7078
|
-
const admin = create$
|
|
7289
|
+
function create$17({ name, collect, client, db, options }) {
|
|
7290
|
+
const admin = create$18({
|
|
7079
7291
|
client,
|
|
7080
7292
|
db,
|
|
7081
7293
|
options
|
|
@@ -7202,7 +7414,7 @@ function start(collector) {
|
|
|
7202
7414
|
//#endregion
|
|
7203
7415
|
//#region src/database/domains/Blocks.ts
|
|
7204
7416
|
/** Postgres implementation. */
|
|
7205
|
-
const create$
|
|
7417
|
+
const create$16 = (config) => {
|
|
7206
7418
|
const { db, chainRegistry } = config;
|
|
7207
7419
|
const getChain = async (chainId) => {
|
|
7208
7420
|
const rows = await db.select({
|
|
@@ -7381,14 +7593,14 @@ const create$15 = (config) => {
|
|
|
7381
7593
|
|
|
7382
7594
|
//#endregion
|
|
7383
7595
|
//#region src/database/domains/Book.ts
|
|
7384
|
-
const DEFAULT_LIMIT$
|
|
7596
|
+
const DEFAULT_LIMIT$4 = 100;
|
|
7385
7597
|
const MAX_TOTAL_OFFERS = 500;
|
|
7386
|
-
function create$
|
|
7598
|
+
function create$15(config) {
|
|
7387
7599
|
const db = config.db;
|
|
7388
7600
|
const logger = getLogger();
|
|
7389
7601
|
const getOffers = async (parameters) => {
|
|
7390
7602
|
const { side, obligationId, cursor: cursorString } = parameters;
|
|
7391
|
-
const requestedLimit = parameters.limit ?? DEFAULT_LIMIT$
|
|
7603
|
+
const requestedLimit = parameters.limit ?? DEFAULT_LIMIT$4;
|
|
7392
7604
|
const priceSortDirection = side === "sell" ? "asc" : "desc";
|
|
7393
7605
|
const inputCursor = Cursor.decode(cursorString, logger);
|
|
7394
7606
|
if (cursorString != null && inputCursor === null) return {
|
|
@@ -7420,7 +7632,8 @@ function create$14(config) {
|
|
|
7420
7632
|
};
|
|
7421
7633
|
return {
|
|
7422
7634
|
get: async (parameters) => {
|
|
7423
|
-
const { side, obligationId, cursor: cursorString, limit = DEFAULT_LIMIT$
|
|
7635
|
+
const { side, obligationId, cursor: cursorString, limit = DEFAULT_LIMIT$4 } = parameters;
|
|
7636
|
+
const tickSortDirection = side === "sell" ? "asc" : "desc";
|
|
7424
7637
|
const inputCursor = LevelCursor.decode(cursorString, logger);
|
|
7425
7638
|
if (cursorString != null && inputCursor === null) return {
|
|
7426
7639
|
levels: [],
|
|
@@ -7435,23 +7648,23 @@ function create$14(config) {
|
|
|
7435
7648
|
cursor: inputCursor?.offersCursor ?? void 0,
|
|
7436
7649
|
limit: fetchLimit
|
|
7437
7650
|
});
|
|
7438
|
-
const
|
|
7651
|
+
const tickMap = /* @__PURE__ */ new Map();
|
|
7439
7652
|
for (const row of rows) {
|
|
7440
|
-
const
|
|
7441
|
-
const existing = priceMap.get(priceKey);
|
|
7653
|
+
const existing = tickMap.get(row.tick);
|
|
7442
7654
|
if (existing) {
|
|
7443
7655
|
existing.assets += row.takeable;
|
|
7444
7656
|
existing.count += 1;
|
|
7445
|
-
} else
|
|
7657
|
+
} else tickMap.set(row.tick, {
|
|
7446
7658
|
assets: row.takeable,
|
|
7447
7659
|
count: 1
|
|
7448
7660
|
});
|
|
7449
7661
|
}
|
|
7450
|
-
const levels = Array.from(
|
|
7451
|
-
|
|
7452
|
-
assets:
|
|
7453
|
-
count:
|
|
7662
|
+
const levels = Array.from(tickMap.entries()).map(([tick, level]) => ({
|
|
7663
|
+
tick,
|
|
7664
|
+
assets: level.assets,
|
|
7665
|
+
count: level.count
|
|
7454
7666
|
}));
|
|
7667
|
+
levels.sort((a, b) => tickSortDirection === "asc" ? a.tick - b.tick : b.tick - a.tick);
|
|
7455
7668
|
const paginatedLevels = levels.slice(0, limit);
|
|
7456
7669
|
const hasMore = levels.length > limit || offersNextCursor !== null;
|
|
7457
7670
|
const lastLevel = paginatedLevels[paginatedLevels.length - 1];
|
|
@@ -7497,14 +7710,14 @@ async function _getOffers(db, params) {
|
|
|
7497
7710
|
AND (s.code IS NULL OR s.code = ${Status.VALID})
|
|
7498
7711
|
ORDER BY
|
|
7499
7712
|
o.group_chain_id, o.group_maker, o."group_group",
|
|
7500
|
-
o.
|
|
7713
|
+
o.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, o.block_number ASC, o.assets DESC, o.hash ASC
|
|
7501
7714
|
),
|
|
7502
7715
|
enriched AS (
|
|
7503
7716
|
SELECT
|
|
7504
7717
|
w.*,
|
|
7505
7718
|
g.consumed, g.chain_id, obl.loan_token,
|
|
7506
7719
|
CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
|
|
7507
|
-
THEN w.
|
|
7720
|
+
THEN w.tick ELSE -w.tick END AS tick_norm,
|
|
7508
7721
|
w.block_number AS block_norm,
|
|
7509
7722
|
-w.assets AS assets_norm,
|
|
7510
7723
|
w.hash AS hash_norm
|
|
@@ -7521,33 +7734,35 @@ async function _getOffers(db, params) {
|
|
|
7521
7734
|
FROM enriched e
|
|
7522
7735
|
${cursor != null ? sql`
|
|
7523
7736
|
WHERE
|
|
7524
|
-
(e.
|
|
7737
|
+
(e.tick_norm, e.block_norm, e.assets_norm, e.hash_norm)
|
|
7525
7738
|
> (
|
|
7526
7739
|
CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
|
|
7527
|
-
THEN ${cursor.
|
|
7740
|
+
THEN ${cursor.tick}::integer ELSE -${cursor.tick}::integer END,
|
|
7528
7741
|
${cursor.blockNumber},
|
|
7529
7742
|
-${cursor.assets}::numeric,
|
|
7530
7743
|
${cursor.hash}
|
|
7531
7744
|
)` : sql``}
|
|
7532
|
-
ORDER BY e.
|
|
7745
|
+
ORDER BY e.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, e.block_number ASC, e.assets DESC, e.hash ASC
|
|
7533
7746
|
LIMIT ${limit}
|
|
7534
7747
|
),
|
|
7535
|
-
-- Compute sum of offsets per position
|
|
7748
|
+
-- Compute sum of offsets per position and obligation
|
|
7536
7749
|
position_offsets AS (
|
|
7537
7750
|
SELECT
|
|
7538
7751
|
chain_id,
|
|
7539
7752
|
"user",
|
|
7540
7753
|
contract,
|
|
7754
|
+
obligation_id,
|
|
7541
7755
|
SUM(value::numeric) AS total_offset
|
|
7542
7756
|
FROM ${offsets}
|
|
7543
|
-
GROUP BY chain_id, "user", contract
|
|
7757
|
+
GROUP BY chain_id, "user", contract, obligation_id
|
|
7544
7758
|
),
|
|
7545
|
-
-- Compute position_consumed: sum of consumed from all groups with lots on each position (converted to lot terms)
|
|
7759
|
+
-- Compute position_consumed: sum of consumed from all groups with lots on each position+obligation (converted to lot terms)
|
|
7546
7760
|
position_consumed AS (
|
|
7547
7761
|
SELECT
|
|
7548
7762
|
l.chain_id,
|
|
7549
7763
|
l.contract,
|
|
7550
7764
|
l."user",
|
|
7765
|
+
l.obligation_id,
|
|
7551
7766
|
SUM(
|
|
7552
7767
|
CASE
|
|
7553
7768
|
WHEN wo.assets::numeric > 0
|
|
@@ -7564,7 +7779,7 @@ async function _getOffers(db, params) {
|
|
|
7564
7779
|
ON wo.group_chain_id = g.chain_id
|
|
7565
7780
|
AND LOWER(wo.group_maker) = LOWER(g.maker)
|
|
7566
7781
|
AND wo.group_group = g."group"
|
|
7567
|
-
GROUP BY l.chain_id, l.contract, l."user"
|
|
7782
|
+
GROUP BY l.chain_id, l.contract, l."user", l.obligation_id
|
|
7568
7783
|
),
|
|
7569
7784
|
-- Compute callback contributions with lot balance
|
|
7570
7785
|
callback_contributions AS (
|
|
@@ -7572,7 +7787,7 @@ async function _getOffers(db, params) {
|
|
|
7572
7787
|
p.hash,
|
|
7573
7788
|
p.obligation_id,
|
|
7574
7789
|
p.assets,
|
|
7575
|
-
p.
|
|
7790
|
+
p.tick,
|
|
7576
7791
|
p.obligation_units,
|
|
7577
7792
|
p.obligation_shares,
|
|
7578
7793
|
p.maturity,
|
|
@@ -7582,6 +7797,7 @@ async function _getOffers(db, params) {
|
|
|
7582
7797
|
p.buy,
|
|
7583
7798
|
p.callback_address,
|
|
7584
7799
|
p.callback_data,
|
|
7800
|
+
p.receiver_if_maker_is_seller,
|
|
7585
7801
|
p.block_number,
|
|
7586
7802
|
p.group_chain_id,
|
|
7587
7803
|
p.group_maker,
|
|
@@ -7617,6 +7833,7 @@ async function _getOffers(db, params) {
|
|
|
7617
7833
|
AND LOWER(l.contract) = LOWER(c.position_contract)
|
|
7618
7834
|
AND LOWER(l."user") = LOWER(c.position_user)
|
|
7619
7835
|
AND l."group" = p.group_group
|
|
7836
|
+
AND l.obligation_id = p.obligation_id
|
|
7620
7837
|
LEFT JOIN ${positions} pos
|
|
7621
7838
|
ON pos.chain_id = c.position_chain_id
|
|
7622
7839
|
AND LOWER(pos.contract) = LOWER(c.position_contract)
|
|
@@ -7625,10 +7842,12 @@ async function _getOffers(db, params) {
|
|
|
7625
7842
|
ON pos_offsets.chain_id = c.position_chain_id
|
|
7626
7843
|
AND LOWER(pos_offsets.contract) = LOWER(c.position_contract)
|
|
7627
7844
|
AND LOWER(pos_offsets."user") = LOWER(c.position_user)
|
|
7845
|
+
AND pos_offsets.obligation_id = p.obligation_id
|
|
7628
7846
|
LEFT JOIN position_consumed pc
|
|
7629
7847
|
ON pc.chain_id = c.position_chain_id
|
|
7630
7848
|
AND LOWER(pc.contract) = LOWER(c.position_contract)
|
|
7631
7849
|
AND LOWER(pc."user") = LOWER(c.position_user)
|
|
7850
|
+
AND pc.obligation_id = p.obligation_id
|
|
7632
7851
|
),
|
|
7633
7852
|
-- Compute contribution per callback in loan terms (loan token only — collateral positions are not indexed)
|
|
7634
7853
|
callback_loan_contribution AS (
|
|
@@ -7646,7 +7865,7 @@ async function _getOffers(db, params) {
|
|
|
7646
7865
|
hash,
|
|
7647
7866
|
obligation_id,
|
|
7648
7867
|
assets,
|
|
7649
|
-
|
|
7868
|
+
tick,
|
|
7650
7869
|
obligation_units,
|
|
7651
7870
|
obligation_shares,
|
|
7652
7871
|
maturity,
|
|
@@ -7656,6 +7875,7 @@ async function _getOffers(db, params) {
|
|
|
7656
7875
|
buy,
|
|
7657
7876
|
callback_address,
|
|
7658
7877
|
callback_data,
|
|
7878
|
+
receiver_if_maker_is_seller,
|
|
7659
7879
|
block_number,
|
|
7660
7880
|
group_chain_id,
|
|
7661
7881
|
group_maker,
|
|
@@ -7672,16 +7892,17 @@ async function _getOffers(db, params) {
|
|
|
7672
7892
|
WHERE clc.callback_id IS NOT NULL
|
|
7673
7893
|
ORDER BY clc.hash, clc.position_chain_id, clc.position_contract, clc.position_user, clc.contribution_in_loan DESC
|
|
7674
7894
|
) deduped
|
|
7675
|
-
GROUP BY hash, obligation_id, assets,
|
|
7676
|
-
|
|
7677
|
-
consumed, chain_id, loan_token, session
|
|
7895
|
+
GROUP BY hash, obligation_id, assets, tick, obligation_units, obligation_shares, maturity, expiry, start, group_group, buy,
|
|
7896
|
+
callback_address, callback_data, block_number, group_chain_id, group_maker,
|
|
7897
|
+
consumed, chain_id, loan_token, session, receiver_if_maker_is_seller
|
|
7678
7898
|
UNION ALL
|
|
7679
7899
|
-- Sell offers without callbacks: collateral positions not indexed, takeable = assets - consumed
|
|
7680
7900
|
SELECT
|
|
7681
|
-
p.hash, p.obligation_id, p.assets, p.
|
|
7901
|
+
p.hash, p.obligation_id, p.assets, p.tick,
|
|
7682
7902
|
p.obligation_units, p.obligation_shares,
|
|
7683
7903
|
p.maturity, p.expiry, p.start, p.group_group,
|
|
7684
7904
|
p.buy, p.callback_address, p.callback_data,
|
|
7905
|
+
p.receiver_if_maker_is_seller,
|
|
7685
7906
|
p.block_number, p.group_chain_id, p.group_maker,
|
|
7686
7907
|
p.consumed, p.chain_id, p.loan_token, p.session,
|
|
7687
7908
|
0 AS total_available
|
|
@@ -7700,7 +7921,7 @@ async function _getOffers(db, params) {
|
|
|
7700
7921
|
oc.obligation_units,
|
|
7701
7922
|
oc.obligation_shares,
|
|
7702
7923
|
oc.consumed,
|
|
7703
|
-
oc.
|
|
7924
|
+
oc.tick,
|
|
7704
7925
|
oc.maturity,
|
|
7705
7926
|
oc.expiry,
|
|
7706
7927
|
oc.start,
|
|
@@ -7710,6 +7931,7 @@ async function _getOffers(db, params) {
|
|
|
7710
7931
|
oc.loan_token,
|
|
7711
7932
|
oc.callback_address,
|
|
7712
7933
|
oc.callback_data,
|
|
7934
|
+
oc.receiver_if_maker_is_seller,
|
|
7713
7935
|
oc.block_number,
|
|
7714
7936
|
oc.session,
|
|
7715
7937
|
COALESCE(oc.total_available, 0) AS available,
|
|
@@ -7732,20 +7954,21 @@ async function _getOffers(db, params) {
|
|
|
7732
7954
|
))
|
|
7733
7955
|
END > 0
|
|
7734
7956
|
ORDER BY
|
|
7735
|
-
oc.
|
|
7957
|
+
oc.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`},
|
|
7736
7958
|
oc.block_number ASC,
|
|
7737
7959
|
oc.assets DESC,
|
|
7738
7960
|
oc.hash ASC;
|
|
7739
7961
|
`);
|
|
7740
7962
|
return {
|
|
7741
7963
|
rows: raw.rows.map((row) => {
|
|
7964
|
+
const receiverIfMakerIsSeller = (row.receiver_if_maker_is_seller ?? row.group_maker).toLowerCase();
|
|
7742
7965
|
return {
|
|
7743
7966
|
hash: row.hash,
|
|
7744
7967
|
maker: row.group_maker,
|
|
7745
7968
|
assets: BigInt(row.assets),
|
|
7746
7969
|
obligationUnits: BigInt(row.obligation_units ?? 0),
|
|
7747
7970
|
obligationShares: BigInt(row.obligation_shares ?? 0),
|
|
7748
|
-
|
|
7971
|
+
tick: row.tick,
|
|
7749
7972
|
maturity: row.maturity,
|
|
7750
7973
|
expiry: row.expiry,
|
|
7751
7974
|
start: row.start,
|
|
@@ -7763,6 +7986,7 @@ async function _getOffers(db, params) {
|
|
|
7763
7986
|
address: row.callback_address,
|
|
7764
7987
|
data: row.callback_data
|
|
7765
7988
|
},
|
|
7989
|
+
receiverIfMakerIsSeller,
|
|
7766
7990
|
blockNumber: row.block_number,
|
|
7767
7991
|
consumed: BigInt(row.consumed ?? 0),
|
|
7768
7992
|
available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
|
|
@@ -7777,7 +8001,7 @@ let Cursor;
|
|
|
7777
8001
|
function encode(row, totalReturned, now, side) {
|
|
7778
8002
|
return Buffer.from(JSON.stringify({
|
|
7779
8003
|
side,
|
|
7780
|
-
|
|
8004
|
+
tick: row.tick,
|
|
7781
8005
|
blockNumber: row.blockNumber,
|
|
7782
8006
|
assets: row.assets.toString(),
|
|
7783
8007
|
hash: row.hash,
|
|
@@ -7788,10 +8012,9 @@ let Cursor;
|
|
|
7788
8012
|
_Cursor.encode = encode;
|
|
7789
8013
|
function decode(cursorString, logger) {
|
|
7790
8014
|
if (cursorString == null) return null;
|
|
7791
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
7792
8015
|
try {
|
|
7793
8016
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
7794
|
-
if ((v?.side === "buy" || v?.side === "sell") &&
|
|
8017
|
+
if ((v?.side === "buy" || v?.side === "sell") && typeof v?.tick === "number" && Number.isInteger(v.tick) && typeof v?.blockNumber === "number" && Number.isInteger(v.blockNumber) && typeof v?.assets === "string" && /^-?\d+$/.test(v.assets) && isHex(v?.hash) && typeof v?.totalReturned === "number" && Number.isInteger(v.totalReturned) && typeof v?.now === "number" && Number.isInteger(v.now)) return v;
|
|
7795
8018
|
throw new Error("Invalid cursor");
|
|
7796
8019
|
} catch {
|
|
7797
8020
|
logger.error({
|
|
@@ -7809,7 +8032,7 @@ let LevelCursor;
|
|
|
7809
8032
|
function encode(lastLevel, offersCursor, side, now) {
|
|
7810
8033
|
return Buffer.from(JSON.stringify({
|
|
7811
8034
|
side,
|
|
7812
|
-
|
|
8035
|
+
lastTick: lastLevel.tick,
|
|
7813
8036
|
now,
|
|
7814
8037
|
offersCursor
|
|
7815
8038
|
})).toString("base64url");
|
|
@@ -7817,10 +8040,9 @@ let LevelCursor;
|
|
|
7817
8040
|
_LevelCursor.encode = encode;
|
|
7818
8041
|
function decode(cursorString, logger) {
|
|
7819
8042
|
if (cursorString == null) return null;
|
|
7820
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
7821
8043
|
try {
|
|
7822
8044
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
7823
|
-
if ((v?.side === "buy" || v?.side === "sell") &&
|
|
8045
|
+
if ((v?.side === "buy" || v?.side === "sell") && typeof v?.lastTick === "number" && Number.isInteger(v.lastTick) && typeof v?.now === "number" && Number.isInteger(v.now) && (v?.offersCursor === null || typeof v?.offersCursor === "string")) return v;
|
|
7824
8046
|
throw new Error("Invalid book cursor");
|
|
7825
8047
|
} catch {
|
|
7826
8048
|
logger.error({
|
|
@@ -7856,7 +8078,7 @@ const DEFAULT_BATCH_SIZE = 4e3;
|
|
|
7856
8078
|
* @param db - Database core instance.
|
|
7857
8079
|
* @returns Callbacks domain. {@link CallbacksDomain}
|
|
7858
8080
|
*/
|
|
7859
|
-
function create$
|
|
8081
|
+
function create$14(db) {
|
|
7860
8082
|
return {
|
|
7861
8083
|
upsert: async (inputs) => {
|
|
7862
8084
|
if (inputs.length === 0) return;
|
|
@@ -7911,7 +8133,7 @@ function create$13(db) {
|
|
|
7911
8133
|
|
|
7912
8134
|
//#endregion
|
|
7913
8135
|
//#region src/database/domains/Consumed.ts
|
|
7914
|
-
function create$
|
|
8136
|
+
function create$13(db) {
|
|
7915
8137
|
return {
|
|
7916
8138
|
create: async (events) => {
|
|
7917
8139
|
if (events.length === 0) return;
|
|
@@ -7959,7 +8181,7 @@ function create$12(db) {
|
|
|
7959
8181
|
* @param db - Database core instance.
|
|
7960
8182
|
* @returns Groups domain. {@link GroupsDomain}
|
|
7961
8183
|
*/
|
|
7962
|
-
function create$
|
|
8184
|
+
function create$12(db) {
|
|
7963
8185
|
return { create: async (groups$1) => {
|
|
7964
8186
|
if (groups$1.length === 0) return;
|
|
7965
8187
|
const rows = groups$1.map((group) => ({
|
|
@@ -7975,34 +8197,36 @@ function create$11(db) {
|
|
|
7975
8197
|
|
|
7976
8198
|
//#endregion
|
|
7977
8199
|
//#region src/database/domains/Lots.ts
|
|
7978
|
-
function create$
|
|
8200
|
+
function create$11(db) {
|
|
7979
8201
|
return {
|
|
7980
8202
|
get: async (parameters) => {
|
|
7981
|
-
const { chainId, user, contract, group } = parameters ?? {};
|
|
8203
|
+
const { chainId, user, contract, group, obligationId } = parameters ?? {};
|
|
7982
8204
|
const conditions = [];
|
|
7983
8205
|
if (chainId !== void 0) conditions.push(eq(lots.chainId, chainId));
|
|
7984
8206
|
if (user !== void 0) conditions.push(eq(lots.user, user.toLowerCase()));
|
|
7985
8207
|
if (contract !== void 0) conditions.push(eq(lots.contract, contract.toLowerCase()));
|
|
7986
8208
|
if (group !== void 0) conditions.push(eq(lots.group, group));
|
|
8209
|
+
if (obligationId !== void 0) conditions.push(eq(lots.obligationId, obligationId));
|
|
7987
8210
|
return (await db.select().from(lots).where(conditions.length > 0 ? and(...conditions) : void 0)).map((row) => ({
|
|
7988
8211
|
chainId: row.chainId,
|
|
7989
8212
|
user: row.user,
|
|
7990
8213
|
contract: row.contract,
|
|
7991
8214
|
group: row.group,
|
|
8215
|
+
obligationId: row.obligationId,
|
|
7992
8216
|
lower: BigInt(row.lower),
|
|
7993
8217
|
upper: BigInt(row.upper)
|
|
7994
8218
|
}));
|
|
7995
8219
|
},
|
|
7996
8220
|
create: async (parameters) => {
|
|
7997
8221
|
if (parameters.length === 0) return;
|
|
7998
|
-
const
|
|
8222
|
+
const lotsByKey = /* @__PURE__ */ new Map();
|
|
7999
8223
|
for (const offer of parameters) {
|
|
8000
|
-
const key = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}-${offer.group}`.toLowerCase();
|
|
8001
|
-
const existing =
|
|
8002
|
-
if (!existing || offer.size > existing.size)
|
|
8224
|
+
const key = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}-${offer.group}-${offer.obligationId}`.toLowerCase();
|
|
8225
|
+
const existing = lotsByKey.get(key);
|
|
8226
|
+
if (!existing || offer.size > existing.size) lotsByKey.set(key, offer);
|
|
8003
8227
|
}
|
|
8004
|
-
for (const offer of
|
|
8005
|
-
const maxUpperResult = await db.select({ maxUpper: sql`COALESCE(MAX(${lots.upper}::numeric), 0)` }).from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase())));
|
|
8228
|
+
for (const offer of lotsByKey.values()) if ((await db.select().from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.group, offer.group.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase()))).limit(1)).length === 0) {
|
|
8229
|
+
const maxUpperResult = await db.select({ maxUpper: sql`COALESCE(MAX(${lots.upper}::numeric), 0)` }).from(lots).where(and(eq(lots.chainId, offer.positionChainId), eq(lots.contract, offer.positionContract.toLowerCase()), eq(lots.user, offer.positionUser.toLowerCase()), eq(lots.obligationId, offer.obligationId.toLowerCase())));
|
|
8006
8230
|
const newLower = BigInt(maxUpperResult[0]?.maxUpper ?? "0");
|
|
8007
8231
|
const newUpper = newLower + offer.size;
|
|
8008
8232
|
await db.insert(lots).values({
|
|
@@ -8010,6 +8234,7 @@ function create$10(db) {
|
|
|
8010
8234
|
user: offer.positionUser.toLowerCase(),
|
|
8011
8235
|
contract: offer.positionContract.toLowerCase(),
|
|
8012
8236
|
group: offer.group.toLowerCase(),
|
|
8237
|
+
obligationId: offer.obligationId.toLowerCase(),
|
|
8013
8238
|
lower: newLower.toString(),
|
|
8014
8239
|
upper: newUpper.toString()
|
|
8015
8240
|
});
|
|
@@ -8025,45 +8250,67 @@ function create$10(db) {
|
|
|
8025
8250
|
* @param db - Database core instance.
|
|
8026
8251
|
* @returns Obligations domain. {@link ObligationsDomain}
|
|
8027
8252
|
*/
|
|
8028
|
-
function create$
|
|
8029
|
-
return {
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8253
|
+
function create$10(db) {
|
|
8254
|
+
return {
|
|
8255
|
+
get: async (parameters) => {
|
|
8256
|
+
const chainIds = parameters?.chainId;
|
|
8257
|
+
const now$3 = now();
|
|
8258
|
+
return (await db.select({
|
|
8259
|
+
chainId: obligations.chainId,
|
|
8260
|
+
loanToken: obligations.loanToken,
|
|
8261
|
+
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
8262
|
+
maturity: obligations.maturity
|
|
8263
|
+
}).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles, sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
8264
|
+
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).groupBy(obligations.obligationId).where(and(chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, gte(obligations.maturity, now$3))).orderBy(asc(obligations.obligationId))).map((row) => from$13({
|
|
8265
|
+
chainId: row.chainId,
|
|
8266
|
+
loanToken: row.loanToken,
|
|
8267
|
+
collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$14({
|
|
8268
|
+
asset: collateral.asset,
|
|
8269
|
+
oracle: collateral.oracle,
|
|
8270
|
+
lltv: from$15(BigInt(collateral.lltv))
|
|
8271
|
+
})),
|
|
8272
|
+
maturity: row.maturity
|
|
8273
|
+
}));
|
|
8274
|
+
},
|
|
8275
|
+
create: async (obligations$1) => {
|
|
8276
|
+
if (obligations$1.length === 0) return;
|
|
8277
|
+
const obligationsById = /* @__PURE__ */ new Map();
|
|
8278
|
+
for (const obligation of obligations$1) {
|
|
8279
|
+
const id$1 = id(obligation).toLowerCase();
|
|
8280
|
+
if (!obligationsById.get(id$1)) obligationsById.set(id$1, obligation);
|
|
8281
|
+
}
|
|
8282
|
+
try {
|
|
8283
|
+
await db.transaction(async (dbTx) => {
|
|
8284
|
+
const obligationRows = obligations$1.map((obligation) => ({
|
|
8047
8285
|
obligationId: id(obligation),
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
lltv: collateral.lltv
|
|
8286
|
+
chainId: obligation.chainId,
|
|
8287
|
+
loanToken: obligation.loanToken.toLowerCase(),
|
|
8288
|
+
maturity: obligation.maturity
|
|
8052
8289
|
}));
|
|
8290
|
+
for (const batch of batch$1(obligationRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligations).values(batch).onConflictDoNothing();
|
|
8291
|
+
const collateralRows = obligations$1.flatMap((obligation) => {
|
|
8292
|
+
return obligation.collaterals.map((collateral) => ({
|
|
8293
|
+
obligationId: id(obligation),
|
|
8294
|
+
asset: collateral.asset.toLowerCase(),
|
|
8295
|
+
oracleChainId: obligation.chainId,
|
|
8296
|
+
oracleAddress: collateral.oracle.toLowerCase(),
|
|
8297
|
+
lltv: collateral.lltv
|
|
8298
|
+
}));
|
|
8299
|
+
});
|
|
8300
|
+
for (const batch of batch$1(collateralRows, DEFAULT_BATCH_SIZE)) await dbTx.insert(obligationCollateralsV2).values(batch).onConflictDoNothing();
|
|
8053
8301
|
});
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
|
|
8302
|
+
} catch (err) {
|
|
8303
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
8304
|
+
throw new Error("Obligations.create failed. Ensure oracles exist before inserting obligations.", { cause: error });
|
|
8305
|
+
}
|
|
8059
8306
|
}
|
|
8060
|
-
}
|
|
8307
|
+
};
|
|
8061
8308
|
}
|
|
8062
8309
|
|
|
8063
8310
|
//#endregion
|
|
8064
8311
|
//#region src/database/domains/Offers.ts
|
|
8065
|
-
const DEFAULT_LIMIT$
|
|
8066
|
-
function create$
|
|
8312
|
+
const DEFAULT_LIMIT$3 = 100;
|
|
8313
|
+
function create$9(config) {
|
|
8067
8314
|
const { db } = config;
|
|
8068
8315
|
return {
|
|
8069
8316
|
create: async (batches) => {
|
|
@@ -8075,6 +8322,7 @@ function create$8(config) {
|
|
|
8075
8322
|
groupMaker: offer.maker.toLowerCase(),
|
|
8076
8323
|
callbackAddress: offer.callback.address.toLowerCase(),
|
|
8077
8324
|
callbackData: offer.callback.data,
|
|
8325
|
+
receiverIfMakerIsSeller: offer.receiverIfMakerIsSeller.toLowerCase(),
|
|
8078
8326
|
blockNumber
|
|
8079
8327
|
})));
|
|
8080
8328
|
if (offersRows.length === 0) return [];
|
|
@@ -8103,7 +8351,7 @@ function create$8(config) {
|
|
|
8103
8351
|
}
|
|
8104
8352
|
},
|
|
8105
8353
|
get: async (parameters) => {
|
|
8106
|
-
const limit = parameters?.limit ?? DEFAULT_LIMIT$
|
|
8354
|
+
const limit = parameters?.limit ?? DEFAULT_LIMIT$3;
|
|
8107
8355
|
const cursor = parameters?.cursor;
|
|
8108
8356
|
const maker = parameters?.maker;
|
|
8109
8357
|
if (cursor !== null && cursor !== void 0) {
|
|
@@ -8126,7 +8374,7 @@ function create$8(config) {
|
|
|
8126
8374
|
assets: offers.assets,
|
|
8127
8375
|
obligationUnits: offers.obligationUnits,
|
|
8128
8376
|
obligationShares: offers.obligationShares,
|
|
8129
|
-
|
|
8377
|
+
tick: offers.tick,
|
|
8130
8378
|
maturity: offers.maturity,
|
|
8131
8379
|
expiry: offers.expiry,
|
|
8132
8380
|
start: offers.start,
|
|
@@ -8137,16 +8385,18 @@ function create$8(config) {
|
|
|
8137
8385
|
loanToken: obligations.loanToken,
|
|
8138
8386
|
callbackAddress: offers.callbackAddress,
|
|
8139
8387
|
callbackData: offers.callbackData,
|
|
8388
|
+
receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
|
|
8140
8389
|
collaterals: collateralsLateral.collaterals,
|
|
8141
8390
|
blockNumber: offers.blockNumber
|
|
8142
8391
|
}).from(offers).innerJoin(obligations, eq(offers.obligationId, obligations.obligationId)).innerJoinLateral(collateralsLateral, sql`true`).where(and(cursor !== null && cursor !== void 0 ? gt(offers.hash, cursor) : void 0, maker !== void 0 ? eq(offers.groupMaker, maker.toLowerCase()) : void 0)).orderBy(asc(offers.hash)).limit(limit)).map((row) => {
|
|
8392
|
+
const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
|
|
8143
8393
|
return {
|
|
8144
8394
|
hash: row.hash,
|
|
8145
8395
|
maker: row.maker,
|
|
8146
8396
|
assets: BigInt(row.assets),
|
|
8147
8397
|
obligationUnits: BigInt(row.obligationUnits),
|
|
8148
8398
|
obligationShares: BigInt(row.obligationShares),
|
|
8149
|
-
|
|
8399
|
+
tick: row.tick,
|
|
8150
8400
|
maturity: from$16(row.maturity),
|
|
8151
8401
|
expiry: row.expiry,
|
|
8152
8402
|
start: row.start,
|
|
@@ -8164,6 +8414,7 @@ function create$8(config) {
|
|
|
8164
8414
|
address: row.callbackAddress,
|
|
8165
8415
|
data: row.callbackData
|
|
8166
8416
|
},
|
|
8417
|
+
receiverIfMakerIsSeller,
|
|
8167
8418
|
consumed: 0n,
|
|
8168
8419
|
available: 0n,
|
|
8169
8420
|
takeable: 0n,
|
|
@@ -8188,64 +8439,30 @@ function create$8(config) {
|
|
|
8188
8439
|
}
|
|
8189
8440
|
throw new Error("Invalid parameters");
|
|
8190
8441
|
},
|
|
8191
|
-
getObligations: async (parameters) => {
|
|
8192
|
-
const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, cursor, limit = DEFAULT_LIMIT$2 } = parameters ?? {};
|
|
8193
|
-
const now$1 = now();
|
|
8194
|
-
const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? sql`(${sql.join(loanTokens.map((token) => sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), sql` OR `)})` : void 0;
|
|
8195
|
-
const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? sql`EXISTS (
|
|
8196
|
-
SELECT 1 FROM ${obligationCollateralsV2} oc
|
|
8197
|
-
WHERE oc.obligation_id = ${obligations.obligationId}
|
|
8198
|
-
AND (${sql.join(collateralTokens.map((token) => sql`LOWER(oc.asset) = ${token.toLowerCase()}`), sql` OR `)})
|
|
8199
|
-
)` : void 0;
|
|
8200
|
-
const result = await db.select({
|
|
8201
|
-
obligationId: obligations.obligationId,
|
|
8202
|
-
chainId: obligations.chainId,
|
|
8203
|
-
loanToken: obligations.loanToken,
|
|
8204
|
-
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
8205
|
-
maturity: obligations.maturity
|
|
8206
|
-
}).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles, sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
8207
|
-
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).groupBy(obligations.obligationId).where(and(cursor !== null && cursor !== void 0 ? gt(obligations.obligationId, cursor) : sql`true`, ids !== void 0 && ids.length > 0 ? inArray(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$1), collateralFilter)).orderBy(asc(obligations.obligationId)).limit(limit);
|
|
8208
|
-
const items = [];
|
|
8209
|
-
for (const row of result) items.push(from$13({
|
|
8210
|
-
chainId: row.chainId,
|
|
8211
|
-
loanToken: row.loanToken,
|
|
8212
|
-
collaterals: row.collaterals.sort((a, b) => a.asset.localeCompare(b.asset)).map((c) => from$14({
|
|
8213
|
-
asset: c.asset,
|
|
8214
|
-
oracle: c.oracle,
|
|
8215
|
-
lltv: from$15(BigInt(c.lltv))
|
|
8216
|
-
})),
|
|
8217
|
-
maturity: row.maturity
|
|
8218
|
-
}));
|
|
8219
|
-
const returnedItems = Array.from(items.values());
|
|
8220
|
-
return {
|
|
8221
|
-
obligations: returnedItems,
|
|
8222
|
-
nextCursor: returnedItems.length === limit && returnedItems.length > 0 ? result[result.length - 1].obligationId : null
|
|
8223
|
-
};
|
|
8224
|
-
},
|
|
8225
8442
|
getQuotes: async (parameters) => {
|
|
8226
8443
|
const { obligationIds } = parameters;
|
|
8227
8444
|
if (obligationIds.length === 0) return [];
|
|
8228
8445
|
const now$2 = now();
|
|
8229
8446
|
const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
|
|
8230
8447
|
obligationId: offers.obligationId,
|
|
8231
|
-
|
|
8232
|
-
}).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(inArray(offers.obligationId, obligationIds), eq(offers.buy, side === "buy"), gte(offers.expiry, now$2), gte(offers.maturity, now$2), lte(offers.start, now$2), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? sql`${offers.
|
|
8448
|
+
tick: offers.tick
|
|
8449
|
+
}).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(inArray(offers.obligationId, obligationIds), eq(offers.buy, side === "buy"), gte(offers.expiry, now$2), gte(offers.maturity, now$2), lte(offers.start, now$2), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(offers.obligationId, side === "buy" ? sql`${offers.tick} ASC` : sql`${offers.tick} DESC`);
|
|
8233
8450
|
const [bestBuys, bestSells] = await Promise.all([query({ side: "buy" }), query({ side: "sell" })]);
|
|
8234
8451
|
const quotes = /* @__PURE__ */ new Map();
|
|
8235
8452
|
for (const row of bestSells) quotes.set(row.obligationId, {
|
|
8236
|
-
ask: {
|
|
8237
|
-
bid: {
|
|
8453
|
+
ask: { tick: row.tick },
|
|
8454
|
+
bid: { tick: null }
|
|
8238
8455
|
});
|
|
8239
8456
|
for (const row of bestBuys) {
|
|
8240
8457
|
const quote = quotes.get(row.obligationId);
|
|
8241
8458
|
if (!quote) {
|
|
8242
8459
|
quotes.set(row.obligationId, {
|
|
8243
|
-
ask: {
|
|
8244
|
-
bid: {
|
|
8460
|
+
ask: { tick: null },
|
|
8461
|
+
bid: { tick: row.tick }
|
|
8245
8462
|
});
|
|
8246
8463
|
continue;
|
|
8247
8464
|
}
|
|
8248
|
-
quote.bid = {
|
|
8465
|
+
quote.bid = { tick: row.tick };
|
|
8249
8466
|
}
|
|
8250
8467
|
return Array.from(quotes.entries()).map(([id, quote]) => {
|
|
8251
8468
|
return from$9({
|
|
@@ -8262,19 +8479,21 @@ function create$8(config) {
|
|
|
8262
8479
|
|
|
8263
8480
|
//#endregion
|
|
8264
8481
|
//#region src/database/domains/Offsets.ts
|
|
8265
|
-
function create$
|
|
8482
|
+
function create$8(db) {
|
|
8266
8483
|
return { get: async (parameters) => {
|
|
8267
|
-
const { chainId, user, contract, group } = parameters ?? {};
|
|
8484
|
+
const { chainId, user, contract, group, obligationId } = parameters ?? {};
|
|
8268
8485
|
const conditions = [];
|
|
8269
8486
|
if (chainId !== void 0) conditions.push(eq(offsets.chainId, chainId));
|
|
8270
8487
|
if (user !== void 0) conditions.push(eq(offsets.user, user.toLowerCase()));
|
|
8271
8488
|
if (contract !== void 0) conditions.push(eq(offsets.contract, contract.toLowerCase()));
|
|
8272
8489
|
if (group !== void 0) conditions.push(eq(offsets.group, group));
|
|
8490
|
+
if (obligationId !== void 0) conditions.push(eq(offsets.obligationId, obligationId));
|
|
8273
8491
|
return (await db.select().from(offsets).where(conditions.length > 0 ? and(...conditions) : void 0)).map((row) => ({
|
|
8274
8492
|
chainId: row.chainId,
|
|
8275
8493
|
user: row.user,
|
|
8276
8494
|
contract: row.contract,
|
|
8277
8495
|
group: row.group,
|
|
8496
|
+
obligationId: row.obligationId,
|
|
8278
8497
|
value: BigInt(row.value)
|
|
8279
8498
|
}));
|
|
8280
8499
|
} };
|
|
@@ -8282,7 +8501,7 @@ function create$7(db) {
|
|
|
8282
8501
|
|
|
8283
8502
|
//#endregion
|
|
8284
8503
|
//#region src/database/domains/Oracles.ts
|
|
8285
|
-
function create$
|
|
8504
|
+
function create$7(db) {
|
|
8286
8505
|
return {
|
|
8287
8506
|
get: async ({ chainId }) => {
|
|
8288
8507
|
return (await db.select({
|
|
@@ -8324,8 +8543,8 @@ function create$6(db) {
|
|
|
8324
8543
|
|
|
8325
8544
|
//#endregion
|
|
8326
8545
|
//#region src/database/domains/Positions.ts
|
|
8327
|
-
const DEFAULT_LIMIT$
|
|
8328
|
-
const create$
|
|
8546
|
+
const DEFAULT_LIMIT$2 = 100;
|
|
8547
|
+
const create$6 = (db) => {
|
|
8329
8548
|
return {
|
|
8330
8549
|
upsert: async (positions$1) => {
|
|
8331
8550
|
const positionsMap = /* @__PURE__ */ new Map();
|
|
@@ -8380,7 +8599,7 @@ const create$5 = (db) => {
|
|
|
8380
8599
|
return totalUpdated;
|
|
8381
8600
|
},
|
|
8382
8601
|
get: async (parameters) => {
|
|
8383
|
-
const { limit = DEFAULT_LIMIT$
|
|
8602
|
+
const { limit = DEFAULT_LIMIT$2, cursor: encodedCursor, chainId, type, filled } = parameters ?? {};
|
|
8384
8603
|
let cursor = null;
|
|
8385
8604
|
if (encodedCursor !== null && encodedCursor !== void 0) {
|
|
8386
8605
|
const parsed = JSON.parse(Buffer.from(encodedCursor, "base64url").toString("utf8"));
|
|
@@ -8417,14 +8636,15 @@ const create$5 = (db) => {
|
|
|
8417
8636
|
};
|
|
8418
8637
|
},
|
|
8419
8638
|
getByUser: async (parameters) => {
|
|
8420
|
-
const { user, limit = DEFAULT_LIMIT$
|
|
8639
|
+
const { user, limit = DEFAULT_LIMIT$2, cursor: encodedCursor } = parameters;
|
|
8421
8640
|
let cursor = null;
|
|
8422
8641
|
if (encodedCursor !== null && encodedCursor !== void 0) {
|
|
8423
8642
|
const parsed = JSON.parse(Buffer.from(encodedCursor, "base64url").toString("utf8"));
|
|
8424
8643
|
if (!parsed.chainId || !parsed.contract) throw new Error("Invalid cursor format");
|
|
8425
8644
|
cursor = {
|
|
8426
8645
|
chainId: parsed.chainId,
|
|
8427
|
-
contract: parsed.contract
|
|
8646
|
+
contract: parsed.contract,
|
|
8647
|
+
obligationId: parsed.obligationId ?? null
|
|
8428
8648
|
};
|
|
8429
8649
|
}
|
|
8430
8650
|
const raw = await db.execute(sql`
|
|
@@ -8433,16 +8653,18 @@ const create$5 = (db) => {
|
|
|
8433
8653
|
chain_id,
|
|
8434
8654
|
"user",
|
|
8435
8655
|
contract,
|
|
8656
|
+
obligation_id,
|
|
8436
8657
|
SUM(value::numeric) AS total_offset
|
|
8437
8658
|
FROM ${offsets}
|
|
8438
8659
|
WHERE LOWER("user") = LOWER(${user})
|
|
8439
|
-
GROUP BY chain_id, "user", contract
|
|
8660
|
+
GROUP BY chain_id, "user", contract, obligation_id
|
|
8440
8661
|
),
|
|
8441
8662
|
position_consumed AS (
|
|
8442
8663
|
SELECT
|
|
8443
8664
|
l.chain_id,
|
|
8444
8665
|
l.contract,
|
|
8445
8666
|
l."user",
|
|
8667
|
+
l.obligation_id,
|
|
8446
8668
|
SUM(
|
|
8447
8669
|
CASE
|
|
8448
8670
|
WHEN offer_agg.assets > 0
|
|
@@ -8468,50 +8690,64 @@ const create$5 = (db) => {
|
|
|
8468
8690
|
AND LOWER(offer_agg.group_maker) = LOWER(g.maker)
|
|
8469
8691
|
AND offer_agg."group_group" = g."group"
|
|
8470
8692
|
WHERE LOWER(l."user") = LOWER(${user})
|
|
8471
|
-
GROUP BY l.chain_id, l.contract, l."user"
|
|
8693
|
+
GROUP BY l.chain_id, l.contract, l."user", l.obligation_id
|
|
8472
8694
|
),
|
|
8473
8695
|
position_max_lot AS (
|
|
8474
8696
|
SELECT
|
|
8475
8697
|
chain_id,
|
|
8476
8698
|
contract,
|
|
8477
8699
|
"user",
|
|
8700
|
+
obligation_id,
|
|
8478
8701
|
MAX(upper::numeric) AS max_upper
|
|
8479
8702
|
FROM ${lots}
|
|
8480
8703
|
WHERE LOWER("user") = LOWER(${user})
|
|
8481
|
-
GROUP BY chain_id, contract, "user"
|
|
8704
|
+
GROUP BY chain_id, contract, "user", obligation_id
|
|
8705
|
+
),
|
|
8706
|
+
per_obligation AS (
|
|
8707
|
+
SELECT
|
|
8708
|
+
pml.chain_id,
|
|
8709
|
+
pml.contract,
|
|
8710
|
+
pml."user",
|
|
8711
|
+
pml.obligation_id,
|
|
8712
|
+
GREATEST(0,
|
|
8713
|
+
COALESCE(pml.max_upper, 0)
|
|
8714
|
+
- COALESCE(po.total_offset, 0)
|
|
8715
|
+
- COALESCE(pc.consumed, 0)
|
|
8716
|
+
)::text AS reserved_balance
|
|
8717
|
+
FROM position_max_lot pml
|
|
8718
|
+
LEFT JOIN position_offsets po
|
|
8719
|
+
ON po.chain_id = pml.chain_id
|
|
8720
|
+
AND LOWER(po.contract) = LOWER(pml.contract)
|
|
8721
|
+
AND LOWER(po."user") = LOWER(pml."user")
|
|
8722
|
+
AND po.obligation_id = pml.obligation_id
|
|
8723
|
+
LEFT JOIN position_consumed pc
|
|
8724
|
+
ON pc.chain_id = pml.chain_id
|
|
8725
|
+
AND LOWER(pc.contract) = LOWER(pml.contract)
|
|
8726
|
+
AND LOWER(pc."user") = LOWER(pml."user")
|
|
8727
|
+
AND pc.obligation_id = pml.obligation_id
|
|
8482
8728
|
)
|
|
8483
8729
|
SELECT
|
|
8484
8730
|
p.chain_id,
|
|
8485
8731
|
p.contract,
|
|
8486
8732
|
p."user",
|
|
8487
8733
|
p.block_number,
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
- COALESCE(po.total_offset, 0)
|
|
8491
|
-
- COALESCE(pc.consumed, 0)
|
|
8492
|
-
)::text AS reserved_balance
|
|
8734
|
+
po.obligation_id,
|
|
8735
|
+
COALESCE(po.reserved_balance, '0') AS reserved_balance
|
|
8493
8736
|
FROM ${positions} p
|
|
8494
|
-
LEFT JOIN
|
|
8737
|
+
LEFT JOIN per_obligation po
|
|
8495
8738
|
ON po.chain_id = p.chain_id
|
|
8496
8739
|
AND LOWER(po.contract) = LOWER(p.contract)
|
|
8497
8740
|
AND LOWER(po."user") = LOWER(p."user")
|
|
8498
|
-
LEFT JOIN position_consumed pc
|
|
8499
|
-
ON pc.chain_id = p.chain_id
|
|
8500
|
-
AND LOWER(pc.contract) = LOWER(p.contract)
|
|
8501
|
-
AND LOWER(pc."user") = LOWER(p."user")
|
|
8502
|
-
LEFT JOIN position_max_lot pml
|
|
8503
|
-
ON pml.chain_id = p.chain_id
|
|
8504
|
-
AND LOWER(pml.contract) = LOWER(p.contract)
|
|
8505
|
-
AND LOWER(pml."user") = LOWER(p."user")
|
|
8506
8741
|
WHERE LOWER(p."user") = LOWER(${user})
|
|
8507
8742
|
AND p."user" != ${zeroAddress}
|
|
8508
|
-
${cursor !== null ? sql`AND (p.chain_id, p.contract) > (${cursor.chainId}, ${cursor.contract})` : sql``}
|
|
8509
|
-
ORDER BY p.chain_id ASC, p.contract ASC
|
|
8743
|
+
${cursor !== null ? sql`AND (p.chain_id, p.contract, COALESCE(po.obligation_id, '')) > (${cursor.chainId}, ${cursor.contract}, ${cursor.obligationId ?? ""})` : sql``}
|
|
8744
|
+
ORDER BY p.chain_id ASC, p.contract ASC, po.obligation_id ASC NULLS FIRST
|
|
8510
8745
|
LIMIT ${limit}
|
|
8511
8746
|
`);
|
|
8512
8747
|
const nextCursor = raw.rows.length === limit ? Buffer.from(JSON.stringify({
|
|
8513
8748
|
chainId: raw.rows[raw.rows.length - 1].chain_id.toString(),
|
|
8514
|
-
contract: raw.rows[raw.rows.length - 1].contract
|
|
8749
|
+
contract: raw.rows[raw.rows.length - 1].contract,
|
|
8750
|
+
obligationId: raw.rows[raw.rows.length - 1].obligation_id
|
|
8515
8751
|
})).toString("base64url") : null;
|
|
8516
8752
|
return {
|
|
8517
8753
|
positions: raw.rows.map((row) => ({
|
|
@@ -8519,6 +8755,7 @@ const create$5 = (db) => {
|
|
|
8519
8755
|
contract: row.contract,
|
|
8520
8756
|
user: row.user,
|
|
8521
8757
|
blockNumber: row.block_number,
|
|
8758
|
+
obligationId: row.obligation_id,
|
|
8522
8759
|
reserved: BigInt(row.reserved_balance.split(".")[0] ?? "0")
|
|
8523
8760
|
})),
|
|
8524
8761
|
nextCursor
|
|
@@ -8549,7 +8786,7 @@ const create$5 = (db) => {
|
|
|
8549
8786
|
|
|
8550
8787
|
//#endregion
|
|
8551
8788
|
//#region src/database/domains/Transfers.ts
|
|
8552
|
-
const create$
|
|
8789
|
+
const create$5 = (db) => ({ create: async (transfers$1) => {
|
|
8553
8790
|
if (transfers$1.length === 0) return 0;
|
|
8554
8791
|
return await db.transaction(async (dbTx) => {
|
|
8555
8792
|
let totalInserted = 0;
|
|
@@ -8654,7 +8891,7 @@ const create$4 = (db) => ({ create: async (transfers$1) => {
|
|
|
8654
8891
|
* @param config - Configuration with database instance
|
|
8655
8892
|
* @returns TreesDomain instance
|
|
8656
8893
|
*/
|
|
8657
|
-
function create$
|
|
8894
|
+
function create$4(config) {
|
|
8658
8895
|
const db = config.db;
|
|
8659
8896
|
return {
|
|
8660
8897
|
create: async (trees$1) => {
|
|
@@ -8737,11 +8974,11 @@ function splitProofs(concatenated) {
|
|
|
8737
8974
|
|
|
8738
8975
|
//#endregion
|
|
8739
8976
|
//#region src/database/domains/Validations.ts
|
|
8740
|
-
const DEFAULT_LIMIT = 100;
|
|
8741
|
-
function create$
|
|
8977
|
+
const DEFAULT_LIMIT$1 = 100;
|
|
8978
|
+
function create$3(db) {
|
|
8742
8979
|
return {
|
|
8743
8980
|
get: async (params) => {
|
|
8744
|
-
const { status: status$2, cursor, limit = DEFAULT_LIMIT } = params ?? {};
|
|
8981
|
+
const { status: status$2, cursor, limit = DEFAULT_LIMIT$1 } = params ?? {};
|
|
8745
8982
|
if (cursor !== null && cursor !== void 0) {
|
|
8746
8983
|
if (!cursor.startsWith("0x") || cursor.length !== 66) throw new Error("Invalid cursor format");
|
|
8747
8984
|
}
|
|
@@ -8795,29 +9032,260 @@ function create$2(db) {
|
|
|
8795
9032
|
};
|
|
8796
9033
|
}
|
|
8797
9034
|
|
|
9035
|
+
//#endregion
|
|
9036
|
+
//#region src/database/readers/ObligationsListing.ts
|
|
9037
|
+
const SORT_FIELDS = [
|
|
9038
|
+
"id",
|
|
9039
|
+
"ask",
|
|
9040
|
+
"bid",
|
|
9041
|
+
"maturity"
|
|
9042
|
+
];
|
|
9043
|
+
const CURSOR_ID_REGEX = /^0x[a-f0-9]{64}$/i;
|
|
9044
|
+
const INT32_MIN = -2147483648;
|
|
9045
|
+
const INT32_MAX = 2147483647;
|
|
9046
|
+
const INT32_MAX_BIGINT = BigInt(INT32_MAX);
|
|
9047
|
+
const MAX_CURSOR_SORT_FIELDS = 4;
|
|
9048
|
+
const DEFAULT_LIMIT = 20;
|
|
9049
|
+
var BadRequestError = class extends Error {
|
|
9050
|
+
constructor(message) {
|
|
9051
|
+
super(message);
|
|
9052
|
+
this.name = "ObligationsListingBadRequestError";
|
|
9053
|
+
}
|
|
9054
|
+
};
|
|
9055
|
+
/**
|
|
9056
|
+
* Creates the obligations listing reader facade.
|
|
9057
|
+
* @param parameters - Reader dependencies.
|
|
9058
|
+
* @returns Obligations listing reader.
|
|
9059
|
+
*/
|
|
9060
|
+
function create$2(parameters) {
|
|
9061
|
+
const { db } = parameters;
|
|
9062
|
+
return { list: async (queryParameters) => {
|
|
9063
|
+
const { ids, chainId: chainIds, loanToken: loanTokens, collateralToken: collateralTokens, maturity: maturities, sort: sortTokens, cursor: encodedCursor, limit: requestedLimit } = queryParameters ?? {};
|
|
9064
|
+
const limit = requestedLimit ?? DEFAULT_LIMIT;
|
|
9065
|
+
if (!Number.isInteger(limit) || limit <= 0) throw new BadRequestError("Limit must be a positive integer");
|
|
9066
|
+
const cursorPayload = encodedCursor ? decodeCursorPayload(encodedCursor) : void 0;
|
|
9067
|
+
const requestedSort = normalizeSort(sortTokens);
|
|
9068
|
+
const cursorSort = cursorPayload ? normalizeSort(cursorPayload.sort) : void 0;
|
|
9069
|
+
if (cursorSort !== void 0 && sortTokens !== void 0 && !hasSameSort(requestedSort, cursorSort)) throw new BadRequestError("Cursor sort does not match requested sort");
|
|
9070
|
+
const sort = sortTokens !== void 0 ? requestedSort : cursorSort ?? requestedSort;
|
|
9071
|
+
const cursorValues = cursorPayload ? cursorValuesFromPayload(cursorPayload) : void 0;
|
|
9072
|
+
const now$1 = now();
|
|
9073
|
+
const loanTokenFilter = loanTokens !== void 0 && loanTokens.length > 0 ? sql`(${sql.join(loanTokens.map((token) => sql`LOWER(${obligations.loanToken}) = ${token.toLowerCase()}`), sql` OR `)})` : void 0;
|
|
9074
|
+
const collateralFilter = collateralTokens !== void 0 && collateralTokens.length > 0 ? sql`EXISTS (
|
|
9075
|
+
SELECT 1 FROM ${obligationCollateralsV2} oc
|
|
9076
|
+
WHERE oc.obligation_id = ${obligations.obligationId}
|
|
9077
|
+
AND (${sql.join(collateralTokens.map((token) => sql`LOWER(oc.asset) = ${token.toLowerCase()}`), sql` OR `)})
|
|
9078
|
+
)` : void 0;
|
|
9079
|
+
const bestAskTick = db.select({ askTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligations.obligationId), eq(offers.buy, false), gte(offers.expiry, now$1), gte(offers.maturity, now$1), lte(offers.start, now$1), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(desc(offers.tick)).limit(1).as("best_ask_tick");
|
|
9080
|
+
const bestBidTick = db.select({ bidTick: offers.tick }).from(offers).innerJoin(groups, and(eq(offers.groupChainId, groups.chainId), eq(offers.groupMaker, groups.maker), eq(offers.group, groups.group))).leftJoin(validations, eq(offers.hash, validations.offerHash)).leftJoin(status, eq(validations.statusId, status.id)).where(and(eq(offers.obligationId, obligations.obligationId), eq(offers.buy, true), gte(offers.expiry, now$1), gte(offers.maturity, now$1), lte(offers.start, now$1), sql`(${status.code} IS NULL OR ${status.code} = ${Status.VALID})`)).orderBy(asc(offers.tick)).limit(1).as("best_bid_tick");
|
|
9081
|
+
const obligationsWithQuotes = db.select({
|
|
9082
|
+
obligationId: obligations.obligationId,
|
|
9083
|
+
chainId: obligations.chainId,
|
|
9084
|
+
loanToken: obligations.loanToken,
|
|
9085
|
+
collaterals: sql`ARRAY_AGG(jsonb_build_object('asset', ${obligationCollateralsV2.asset}, 'oracle', ${oracles.address}, 'lltv', ${obligationCollateralsV2.lltv}))`.as("collaterals"),
|
|
9086
|
+
maturity: obligations.maturity,
|
|
9087
|
+
askTick: sql`MAX(${bestAskTick.askTick})`.as("ask_tick"),
|
|
9088
|
+
bidTick: sql`MAX(${bestBidTick.bidTick})`.as("bid_tick"),
|
|
9089
|
+
ask: sql`COALESCE(MAX(${bestAskTick.askTick}) + 1, 0)`.as("ask"),
|
|
9090
|
+
bid: sql`COALESCE(MAX(${bestBidTick.bidTick}) + 1, 0)`.as("bid")
|
|
9091
|
+
}).from(obligations).innerJoin(obligationCollateralsV2, eq(obligations.obligationId, obligationCollateralsV2.obligationId)).innerJoin(oracles, sql`${obligationCollateralsV2.oracleChainId} = ${oracles.chainId}
|
|
9092
|
+
AND ${obligationCollateralsV2.oracleAddress} = ${oracles.address}`).leftJoinLateral(bestAskTick, sql`true`).leftJoinLateral(bestBidTick, sql`true`).groupBy(obligations.obligationId).where(and(ids !== void 0 && ids.length > 0 ? inArray(obligations.obligationId, ids) : void 0, chainIds !== void 0 && chainIds.length > 0 ? inArray(obligations.chainId, chainIds) : void 0, loanTokenFilter, maturities !== void 0 && maturities.length > 0 ? inArray(obligations.maturity, maturities) : gte(obligations.maturity, now$1), collateralFilter)).as("obligations_with_quotes");
|
|
9093
|
+
const sortColumns = {
|
|
9094
|
+
id: obligationsWithQuotes.obligationId,
|
|
9095
|
+
ask: obligationsWithQuotes.ask,
|
|
9096
|
+
bid: obligationsWithQuotes.bid,
|
|
9097
|
+
maturity: obligationsWithQuotes.maturity
|
|
9098
|
+
};
|
|
9099
|
+
const rows = await db.select({
|
|
9100
|
+
obligationId: obligationsWithQuotes.obligationId,
|
|
9101
|
+
chainId: obligationsWithQuotes.chainId,
|
|
9102
|
+
loanToken: obligationsWithQuotes.loanToken,
|
|
9103
|
+
collaterals: obligationsWithQuotes.collaterals,
|
|
9104
|
+
maturity: obligationsWithQuotes.maturity,
|
|
9105
|
+
askTick: obligationsWithQuotes.askTick,
|
|
9106
|
+
bidTick: obligationsWithQuotes.bidTick,
|
|
9107
|
+
ask: obligationsWithQuotes.ask,
|
|
9108
|
+
bid: obligationsWithQuotes.bid
|
|
9109
|
+
}).from(obligationsWithQuotes).where(buildCursorFilter(sortColumns, sort, cursorValues)).orderBy(...buildOrderBy(sortColumns, sort)).limit(limit + 1);
|
|
9110
|
+
const hasMore = rows.length > limit;
|
|
9111
|
+
const listedRows = (hasMore ? rows.slice(0, limit) : rows).map((row) => {
|
|
9112
|
+
return {
|
|
9113
|
+
obligation: from$13({
|
|
9114
|
+
chainId: row.chainId,
|
|
9115
|
+
loanToken: row.loanToken,
|
|
9116
|
+
collaterals: row.collaterals.sort((left, right) => left.asset.localeCompare(right.asset)).map((collateral) => from$14({
|
|
9117
|
+
asset: collateral.asset,
|
|
9118
|
+
oracle: collateral.oracle,
|
|
9119
|
+
lltv: from$15(BigInt(collateral.lltv))
|
|
9120
|
+
})),
|
|
9121
|
+
maturity: row.maturity
|
|
9122
|
+
}),
|
|
9123
|
+
quote: from$9({
|
|
9124
|
+
obligationId: row.obligationId,
|
|
9125
|
+
ask: { tick: row.askTick },
|
|
9126
|
+
bid: { tick: row.bidTick }
|
|
9127
|
+
}),
|
|
9128
|
+
cursorValues: {
|
|
9129
|
+
id: row.obligationId,
|
|
9130
|
+
ask: toBigInt(row.ask),
|
|
9131
|
+
bid: toBigInt(row.bid),
|
|
9132
|
+
maturity: row.maturity
|
|
9133
|
+
}
|
|
9134
|
+
};
|
|
9135
|
+
});
|
|
9136
|
+
const nextCursor = hasMore && listedRows.length > 0 ? encodeCursorPayload({
|
|
9137
|
+
sort: sortToTokens(sort),
|
|
9138
|
+
id: listedRows[listedRows.length - 1].cursorValues.id,
|
|
9139
|
+
ask: listedRows[listedRows.length - 1].cursorValues.ask.toString(),
|
|
9140
|
+
bid: listedRows[listedRows.length - 1].cursorValues.bid.toString(),
|
|
9141
|
+
maturity: listedRows[listedRows.length - 1].cursorValues.maturity
|
|
9142
|
+
}) : null;
|
|
9143
|
+
return {
|
|
9144
|
+
obligations: listedRows.map((row) => ({
|
|
9145
|
+
obligation: row.obligation,
|
|
9146
|
+
quote: row.quote
|
|
9147
|
+
})),
|
|
9148
|
+
nextCursor
|
|
9149
|
+
};
|
|
9150
|
+
} };
|
|
9151
|
+
}
|
|
9152
|
+
function isSortField(value) {
|
|
9153
|
+
return SORT_FIELDS.includes(value);
|
|
9154
|
+
}
|
|
9155
|
+
function parseSortToken(token) {
|
|
9156
|
+
const direction = token.startsWith("-") ? "desc" : "asc";
|
|
9157
|
+
const rawField = token.startsWith("-") ? token.slice(1) : token;
|
|
9158
|
+
if (!isSortField(rawField)) throw new BadRequestError(`Invalid sort field: ${rawField}`);
|
|
9159
|
+
return {
|
|
9160
|
+
field: rawField,
|
|
9161
|
+
direction
|
|
9162
|
+
};
|
|
9163
|
+
}
|
|
9164
|
+
function normalizeSort(sortTokens) {
|
|
9165
|
+
const parsed = sortTokens?.length ? sortTokens.map(parseSortToken) : [{
|
|
9166
|
+
field: "id",
|
|
9167
|
+
direction: "asc"
|
|
9168
|
+
}];
|
|
9169
|
+
return parsed.some((entry) => entry.field === "id") ? parsed : [...parsed, {
|
|
9170
|
+
field: "id",
|
|
9171
|
+
direction: "asc"
|
|
9172
|
+
}];
|
|
9173
|
+
}
|
|
9174
|
+
function sortToTokens(sortEntries) {
|
|
9175
|
+
return sortEntries.map((entry) => entry.direction === "desc" ? `-${entry.field}` : entry.field);
|
|
9176
|
+
}
|
|
9177
|
+
function hasSameSort(left, right) {
|
|
9178
|
+
if (left.length !== right.length) return false;
|
|
9179
|
+
return left.every((sortEntry, index) => sortEntry.field === right[index]?.field && sortEntry.direction === right[index]?.direction);
|
|
9180
|
+
}
|
|
9181
|
+
function decodeCursorPayload(cursor) {
|
|
9182
|
+
let decoded;
|
|
9183
|
+
try {
|
|
9184
|
+
decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
9185
|
+
} catch {
|
|
9186
|
+
throw new BadRequestError("Invalid cursor format");
|
|
9187
|
+
}
|
|
9188
|
+
if (decoded === null || typeof decoded !== "object") throw new BadRequestError("Invalid cursor payload");
|
|
9189
|
+
const payload = decoded;
|
|
9190
|
+
const sortTokens = parseCursorSortTokens(payload.sort);
|
|
9191
|
+
if (typeof payload.id !== "string" || !CURSOR_ID_REGEX.test(payload.id)) throw new BadRequestError("Invalid cursor obligation id");
|
|
9192
|
+
const ask = parseCursorNonNegativeInt32(payload.ask, "ask");
|
|
9193
|
+
const bid = parseCursorNonNegativeInt32(payload.bid, "bid");
|
|
9194
|
+
if (!isInt32(payload.maturity)) throw new BadRequestError("Invalid cursor maturity value");
|
|
9195
|
+
return {
|
|
9196
|
+
sort: sortTokens,
|
|
9197
|
+
id: payload.id,
|
|
9198
|
+
ask,
|
|
9199
|
+
bid,
|
|
9200
|
+
maturity: payload.maturity
|
|
9201
|
+
};
|
|
9202
|
+
}
|
|
9203
|
+
function parseCursorSortTokens(value) {
|
|
9204
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_CURSOR_SORT_FIELDS) throw new BadRequestError("Invalid cursor sort");
|
|
9205
|
+
const sortEntries = value.map((token) => {
|
|
9206
|
+
if (typeof token !== "string") throw new BadRequestError("Invalid cursor sort");
|
|
9207
|
+
try {
|
|
9208
|
+
return parseSortToken(token);
|
|
9209
|
+
} catch {
|
|
9210
|
+
throw new BadRequestError("Invalid cursor sort");
|
|
9211
|
+
}
|
|
9212
|
+
});
|
|
9213
|
+
if (new Set(sortEntries.map((entry) => entry.field)).size !== sortEntries.length) throw new BadRequestError("Invalid cursor sort");
|
|
9214
|
+
return sortToTokens(sortEntries);
|
|
9215
|
+
}
|
|
9216
|
+
function parseCursorNonNegativeInt32(value, field) {
|
|
9217
|
+
if (typeof value !== "string" || !/^\d+$/.test(value)) throw new BadRequestError(`Invalid cursor ${field} value`);
|
|
9218
|
+
if (BigInt(value) > INT32_MAX_BIGINT) throw new BadRequestError(`Invalid cursor ${field} value`);
|
|
9219
|
+
return value;
|
|
9220
|
+
}
|
|
9221
|
+
function isInt32(value) {
|
|
9222
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= INT32_MIN && value <= INT32_MAX;
|
|
9223
|
+
}
|
|
9224
|
+
function encodeCursorPayload(payload) {
|
|
9225
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
9226
|
+
}
|
|
9227
|
+
function cursorValuesFromPayload(payload) {
|
|
9228
|
+
return {
|
|
9229
|
+
id: payload.id,
|
|
9230
|
+
ask: BigInt(payload.ask),
|
|
9231
|
+
bid: BigInt(payload.bid),
|
|
9232
|
+
maturity: payload.maturity
|
|
9233
|
+
};
|
|
9234
|
+
}
|
|
9235
|
+
function cursorComparisonValue(cursorValues, field) {
|
|
9236
|
+
switch (field) {
|
|
9237
|
+
case "id": return cursorValues.id;
|
|
9238
|
+
case "maturity": return cursorValues.maturity;
|
|
9239
|
+
case "ask": return cursorValues.ask.toString();
|
|
9240
|
+
case "bid": return cursorValues.bid.toString();
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
function buildCursorFilter(columns, sortEntries, cursorValues) {
|
|
9244
|
+
if (cursorValues === void 0) return void 0;
|
|
9245
|
+
const comparisons = sortEntries.map((sortEntry, index) => {
|
|
9246
|
+
const equals = sortEntries.slice(0, index).map((previous) => {
|
|
9247
|
+
return sql`${columns[previous.field]} = ${cursorComparisonValue(cursorValues, previous.field)}`;
|
|
9248
|
+
});
|
|
9249
|
+
const comparison = sortEntry.direction === "asc" ? sql`${columns[sortEntry.field]} > ${cursorComparisonValue(cursorValues, sortEntry.field)}` : sql`${columns[sortEntry.field]} < ${cursorComparisonValue(cursorValues, sortEntry.field)}`;
|
|
9250
|
+
return equals.length > 0 ? sql`(${sql.join([...equals, comparison], sql` AND `)})` : sql`(${comparison})`;
|
|
9251
|
+
});
|
|
9252
|
+
return comparisons.length > 0 ? sql`(${sql.join(comparisons, sql` OR `)})` : void 0;
|
|
9253
|
+
}
|
|
9254
|
+
function buildOrderBy(columns, sortEntries) {
|
|
9255
|
+
return sortEntries.map((sortEntry) => sortEntry.direction === "asc" ? asc(columns[sortEntry.field]) : desc(columns[sortEntry.field]));
|
|
9256
|
+
}
|
|
9257
|
+
function toBigInt(value) {
|
|
9258
|
+
if (typeof value === "bigint") return value;
|
|
9259
|
+
if (typeof value === "number") return BigInt(value);
|
|
9260
|
+
return BigInt(value.split(".")[0] ?? "0");
|
|
9261
|
+
}
|
|
9262
|
+
|
|
8798
9263
|
//#endregion
|
|
8799
9264
|
//#region src/database/Database.ts
|
|
8800
9265
|
function createDomains(core, chainRegistry) {
|
|
8801
9266
|
return {
|
|
8802
|
-
book: create$
|
|
8803
|
-
blocks: create$
|
|
9267
|
+
book: create$15({ db: core }),
|
|
9268
|
+
blocks: create$16({
|
|
8804
9269
|
db: core,
|
|
8805
9270
|
chainRegistry
|
|
8806
9271
|
}),
|
|
8807
|
-
callbacks: create$
|
|
8808
|
-
offers: create$
|
|
8809
|
-
consumed: create$
|
|
8810
|
-
groups: create$
|
|
8811
|
-
lots: create$
|
|
8812
|
-
obligations: create$
|
|
8813
|
-
offsets: create$
|
|
8814
|
-
oracles: create$
|
|
8815
|
-
trees: create$
|
|
8816
|
-
validations: create$
|
|
8817
|
-
positions: create$
|
|
8818
|
-
transfers: create$
|
|
9272
|
+
callbacks: create$14(core),
|
|
9273
|
+
offers: create$9({ db: core }),
|
|
9274
|
+
consumed: create$13(core),
|
|
9275
|
+
groups: create$12(core),
|
|
9276
|
+
lots: create$11(core),
|
|
9277
|
+
obligations: create$10(core),
|
|
9278
|
+
offsets: create$8(core),
|
|
9279
|
+
oracles: create$7(core),
|
|
9280
|
+
trees: create$4({ db: core }),
|
|
9281
|
+
validations: create$3(core),
|
|
9282
|
+
positions: create$6(core),
|
|
9283
|
+
transfers: create$5(core)
|
|
8819
9284
|
};
|
|
8820
9285
|
}
|
|
9286
|
+
function createReaders(core) {
|
|
9287
|
+
return { obligations: create$2({ db: core }) };
|
|
9288
|
+
}
|
|
8821
9289
|
const AUGMENT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
8822
9290
|
function augmentWithDomains(base, chainRegistry) {
|
|
8823
9291
|
const cached = AUGMENT_CACHE.get(base)?.get(chainRegistry);
|
|
@@ -8829,6 +9297,7 @@ function augmentWithDomains(base, chainRegistry) {
|
|
|
8829
9297
|
});
|
|
8830
9298
|
};
|
|
8831
9299
|
const dms = createDomains(wrapped, chainRegistry);
|
|
9300
|
+
const readers = createReaders(wrapped);
|
|
8832
9301
|
Object.defineProperties(wrapped, {
|
|
8833
9302
|
book: {
|
|
8834
9303
|
value: dms.book,
|
|
@@ -8885,6 +9354,10 @@ function augmentWithDomains(base, chainRegistry) {
|
|
|
8885
9354
|
transfers: {
|
|
8886
9355
|
value: dms.transfers,
|
|
8887
9356
|
enumerable: true
|
|
9357
|
+
},
|
|
9358
|
+
readers: {
|
|
9359
|
+
value: readers,
|
|
9360
|
+
enumerable: true
|
|
8888
9361
|
}
|
|
8889
9362
|
});
|
|
8890
9363
|
const chainRegistryMap = AUGMENT_CACHE.get(base);
|
|
@@ -8893,6 +9366,7 @@ function augmentWithDomains(base, chainRegistry) {
|
|
|
8893
9366
|
return wrapped;
|
|
8894
9367
|
}
|
|
8895
9368
|
const InMemoryDbMap = /* @__PURE__ */ new Map();
|
|
9369
|
+
const LEGACY_SCHEMA_START_MINOR = 7;
|
|
8896
9370
|
/**
|
|
8897
9371
|
* Connect to the database.
|
|
8898
9372
|
* @notice If no connection string is provided, an in-process PGLite database is created.
|
|
@@ -8947,9 +9421,26 @@ function applyMigrations(kind, driver) {
|
|
|
8947
9421
|
async function preMigrate(driver) {
|
|
8948
9422
|
const tracer = getTracer("db.preMigrate");
|
|
8949
9423
|
await startActiveSpan(tracer, "db.preMigrate", async () => {
|
|
8950
|
-
|
|
9424
|
+
const schemaNames = getSchemaNamesForMigration(VERSION);
|
|
9425
|
+
for (const schemaName of schemaNames) await driver.execute(`create schema if not exists "${schemaName}"`);
|
|
8951
9426
|
});
|
|
8952
9427
|
}
|
|
9428
|
+
/**
|
|
9429
|
+
* Build the list of router schemas that should exist before running migrations.
|
|
9430
|
+
* @param version - Current schema version (e.g. `router_v1.8`).
|
|
9431
|
+
* @returns Ordered schema names from `router_v1.7` to current, or just current if parsing fails.
|
|
9432
|
+
*/
|
|
9433
|
+
function getSchemaNamesForMigration(version) {
|
|
9434
|
+
const parsed = /^router_v(?<major>\d+)\.(?<minor>\d+)$/.exec(version);
|
|
9435
|
+
if (!parsed?.groups?.major || !parsed.groups.minor) return [version];
|
|
9436
|
+
const major = Number.parseInt(parsed.groups.major, 10);
|
|
9437
|
+
const currentMinor = Number.parseInt(parsed.groups.minor, 10);
|
|
9438
|
+
if (!Number.isInteger(major) || !Number.isInteger(currentMinor)) return [version];
|
|
9439
|
+
if (currentMinor < LEGACY_SCHEMA_START_MINOR) return [version];
|
|
9440
|
+
const schemaNames = [];
|
|
9441
|
+
for (let minor = LEGACY_SCHEMA_START_MINOR; minor <= currentMinor; minor += 1) schemaNames.push(`router_v${major}.${minor}`);
|
|
9442
|
+
return schemaNames;
|
|
9443
|
+
}
|
|
8953
9444
|
async function postMigrate(driver) {
|
|
8954
9445
|
const tracer = getTracer("db.postMigrate");
|
|
8955
9446
|
await startActiveSpan(tracer, "db.postMigrate", async () => {
|
|
@@ -9219,15 +9710,16 @@ async function postMigrate(driver) {
|
|
|
9219
9710
|
RETURNS trigger
|
|
9220
9711
|
LANGUAGE plpgsql AS $$
|
|
9221
9712
|
BEGIN
|
|
9222
|
-
INSERT INTO "${VERSION}"."offsets" (chain_id, "user", contract, "group", value)
|
|
9713
|
+
INSERT INTO "${VERSION}"."offsets" (chain_id, "user", contract, "group", obligation_id, value)
|
|
9223
9714
|
VALUES (
|
|
9224
9715
|
OLD.chain_id,
|
|
9225
9716
|
OLD."user",
|
|
9226
9717
|
OLD.contract,
|
|
9227
9718
|
OLD."group",
|
|
9719
|
+
OLD.obligation_id,
|
|
9228
9720
|
OLD.upper::numeric - OLD.lower::numeric
|
|
9229
9721
|
)
|
|
9230
|
-
ON CONFLICT (chain_id, "user", contract, "group") DO NOTHING;
|
|
9722
|
+
ON CONFLICT (chain_id, "user", contract, "group", obligation_id) DO NOTHING;
|
|
9231
9723
|
RETURN OLD;
|
|
9232
9724
|
END;
|
|
9233
9725
|
$$;
|
|
@@ -9527,7 +10019,7 @@ var RouterCmd = class RouterCmd extends Command {
|
|
|
9527
10019
|
const configPath = resolveConfigPath(options.configFile);
|
|
9528
10020
|
const config = configPath !== null ? loadRouterConfig(configPath) : createDefaultConfig();
|
|
9529
10021
|
const logger = defaultLogger(config.logging.level, config.logging.pretty);
|
|
9530
|
-
const chainRegistry = create$
|
|
10022
|
+
const chainRegistry = create$20(Object.values(config.chains).map((entry) => entry.chain));
|
|
9531
10023
|
const clients = (config.indexer?.chains ?? []).map((name) => {
|
|
9532
10024
|
const chainConfig = config.chains[name];
|
|
9533
10025
|
if (!chainConfig) throw new Error(`Indexer chain ${name} is not defined under [chains].`);
|
|
@@ -9575,7 +10067,7 @@ const gatekeeperCmd = new RouterCmd("gatekeeper");
|
|
|
9575
10067
|
gatekeeperCmd.description("Start Gatekeeper validation service.").action(async (opts) => {
|
|
9576
10068
|
const { gatekeeper: gatekeeperConfig, chainRegistry, logger } = opts;
|
|
9577
10069
|
await runWithLogger(logger, async () => {
|
|
9578
|
-
const gatekeeperCore = create$
|
|
10070
|
+
const gatekeeperCore = create$21({ rules: morphoRules(chainRegistry.list()) });
|
|
9579
10071
|
const handle = await start$1({
|
|
9580
10072
|
gatekeeper: gatekeeperCore,
|
|
9581
10073
|
chainRegistry,
|
|
@@ -9633,7 +10125,7 @@ async function getBook(params, db) {
|
|
|
9633
10125
|
side: query.side,
|
|
9634
10126
|
levels_count: levels.length,
|
|
9635
10127
|
has_next_cursor: nextCursor != null,
|
|
9636
|
-
|
|
10128
|
+
first_level_tick: firstLevel?.tick ?? null,
|
|
9637
10129
|
first_level_assets: firstLevel?.assets.toString() ?? null,
|
|
9638
10130
|
first_level_count: firstLevel?.count ?? null
|
|
9639
10131
|
});
|
|
@@ -9721,7 +10213,7 @@ async function getConfigContracts(query, chainRegistry) {
|
|
|
9721
10213
|
}
|
|
9722
10214
|
function parseCursor(cursor) {
|
|
9723
10215
|
const [chain, address] = cursor.split(":", 2);
|
|
9724
|
-
if (!chain || !address) throw new BadRequestError("Cursor must be in the format chain_id:0x...");
|
|
10216
|
+
if (!chain || !address) throw new BadRequestError$1("Cursor must be in the format chain_id:0x...");
|
|
9725
10217
|
return {
|
|
9726
10218
|
chain_id: Number.parseInt(chain, 10),
|
|
9727
10219
|
address: address.toLowerCase()
|
|
@@ -10042,37 +10534,44 @@ async function getHealthCollectors(query, db, chainRegistry) {
|
|
|
10042
10534
|
|
|
10043
10535
|
//#endregion
|
|
10044
10536
|
//#region src/api/Controllers/getObligation.ts
|
|
10537
|
+
function toPayloadError$1(err) {
|
|
10538
|
+
if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
|
|
10539
|
+
return err;
|
|
10540
|
+
}
|
|
10045
10541
|
async function getObligation(params, db) {
|
|
10046
10542
|
const logger = getLogger();
|
|
10047
10543
|
const result = safeParse("get_obligation", params, (issue) => issue.message);
|
|
10048
10544
|
if (!result.success) return failure(result.error);
|
|
10049
10545
|
const query = result.data;
|
|
10050
10546
|
try {
|
|
10051
|
-
const
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10547
|
+
const listing = await db.readers.obligations.list({
|
|
10548
|
+
ids: [query.obligation_id],
|
|
10549
|
+
limit: 1
|
|
10550
|
+
});
|
|
10551
|
+
if (listing.obligations.length === 0) return failure(new NotFoundError("Obligation not found"));
|
|
10552
|
+
const obligation = listing.obligations[0];
|
|
10055
10553
|
return success({
|
|
10056
|
-
data: from$5(obligation, quote
|
|
10057
|
-
obligationId: id(obligation),
|
|
10058
|
-
ask: { price: 0n },
|
|
10059
|
-
bid: { price: 0n }
|
|
10060
|
-
}),
|
|
10554
|
+
data: from$5(obligation.obligation, obligation.quote),
|
|
10061
10555
|
cursor: null
|
|
10062
10556
|
});
|
|
10063
10557
|
} catch (err) {
|
|
10558
|
+
const payloadError = toPayloadError$1(err);
|
|
10064
10559
|
logger.error({
|
|
10065
|
-
err,
|
|
10560
|
+
err: payloadError,
|
|
10066
10561
|
msg: "Error get obligation",
|
|
10067
|
-
errorMessage:
|
|
10068
|
-
errorStack:
|
|
10562
|
+
errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
|
|
10563
|
+
errorStack: payloadError instanceof Error ? payloadError.stack : void 0
|
|
10069
10564
|
});
|
|
10070
|
-
return failure(
|
|
10565
|
+
return failure(payloadError);
|
|
10071
10566
|
}
|
|
10072
10567
|
}
|
|
10073
10568
|
|
|
10074
10569
|
//#endregion
|
|
10075
10570
|
//#region src/api/Controllers/getObligations.ts
|
|
10571
|
+
function toPayloadError(err) {
|
|
10572
|
+
if (err instanceof BadRequestError) return new BadRequestError$1(err.message);
|
|
10573
|
+
return err;
|
|
10574
|
+
}
|
|
10076
10575
|
async function getObligations(queryParameters, db) {
|
|
10077
10576
|
const logger = getLogger();
|
|
10078
10577
|
const result = safeParse("get_obligations", queryParameters, (issue) => issue.message);
|
|
@@ -10083,31 +10582,28 @@ async function getObligations(queryParameters, db) {
|
|
|
10083
10582
|
const loanTokens = query.loan_tokens?.length ? query.loan_tokens : void 0;
|
|
10084
10583
|
const collateralTokens = query.collateral_tokens?.length ? query.collateral_tokens : void 0;
|
|
10085
10584
|
const maturities = query.maturities?.length ? query.maturities : void 0;
|
|
10086
|
-
const
|
|
10087
|
-
cursor: query.cursor,
|
|
10088
|
-
limit: query.limit,
|
|
10585
|
+
const listing = await db.readers.obligations.list({
|
|
10089
10586
|
chainId: chainIds,
|
|
10090
10587
|
loanToken: loanTokens,
|
|
10091
10588
|
collateralToken: collateralTokens,
|
|
10092
|
-
maturity: maturities
|
|
10589
|
+
maturity: maturities,
|
|
10590
|
+
sort: query.sort,
|
|
10591
|
+
cursor: query.cursor,
|
|
10592
|
+
limit: query.limit
|
|
10093
10593
|
});
|
|
10094
|
-
const quotes = await db.offers.getQuotes({ obligationIds: obligations.map((o) => id(o)) });
|
|
10095
10594
|
return success({
|
|
10096
|
-
data: obligations.map((
|
|
10097
|
-
|
|
10098
|
-
ask: { price: 0n },
|
|
10099
|
-
bid: { price: 0n }
|
|
10100
|
-
})),
|
|
10101
|
-
cursor: nextCursor ?? null
|
|
10595
|
+
data: listing.obligations.map((item) => from$5(item.obligation, item.quote)),
|
|
10596
|
+
cursor: listing.nextCursor
|
|
10102
10597
|
});
|
|
10103
10598
|
} catch (err) {
|
|
10599
|
+
const payloadError = toPayloadError(err);
|
|
10104
10600
|
logger.error({
|
|
10105
|
-
err,
|
|
10601
|
+
err: payloadError,
|
|
10106
10602
|
msg: "Error get obligations",
|
|
10107
|
-
errorMessage:
|
|
10108
|
-
errorStack:
|
|
10603
|
+
errorMessage: payloadError instanceof Error ? payloadError.message : String(payloadError),
|
|
10604
|
+
errorStack: payloadError instanceof Error ? payloadError.stack : void 0
|
|
10109
10605
|
});
|
|
10110
|
-
return failure(
|
|
10606
|
+
return failure(payloadError);
|
|
10111
10607
|
}
|
|
10112
10608
|
}
|
|
10113
10609
|
|
|
@@ -10120,7 +10616,7 @@ async function getObligations(queryParameters, db) {
|
|
|
10120
10616
|
* @returns The offers with pagination cursor.
|
|
10121
10617
|
*/
|
|
10122
10618
|
async function getOffersQuery(db, parameters) {
|
|
10123
|
-
const limit = parameters?.limit ?? DEFAULT_LIMIT$
|
|
10619
|
+
const limit = parameters?.limit ?? DEFAULT_LIMIT$3;
|
|
10124
10620
|
const cursor = parameters?.cursor;
|
|
10125
10621
|
const maker = parameters?.maker;
|
|
10126
10622
|
if (cursor !== null && cursor !== void 0) {
|
|
@@ -10196,7 +10692,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10196
10692
|
obligationUnits: offers.obligationUnits,
|
|
10197
10693
|
obligationShares: offers.obligationShares,
|
|
10198
10694
|
consumed: groups.consumed,
|
|
10199
|
-
|
|
10695
|
+
tick: offers.tick,
|
|
10200
10696
|
maturity: offers.maturity,
|
|
10201
10697
|
expiry: offers.expiry,
|
|
10202
10698
|
start: offers.start,
|
|
@@ -10207,6 +10703,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10207
10703
|
loanToken: obligations.loanToken,
|
|
10208
10704
|
callbackAddress: offers.callbackAddress,
|
|
10209
10705
|
callbackData: offers.callbackData,
|
|
10706
|
+
receiverIfMakerIsSeller: offers.receiverIfMakerIsSeller,
|
|
10210
10707
|
collaterals: collateralsLateral.collaterals,
|
|
10211
10708
|
blockNumber: offers.blockNumber,
|
|
10212
10709
|
available: sql`${availableExpr}::numeric`.as("available"),
|
|
@@ -10228,13 +10725,14 @@ async function getOffersQuery(db, parameters) {
|
|
|
10228
10725
|
)
|
|
10229
10726
|
END
|
|
10230
10727
|
) > 0` : void 0)).orderBy(asc(offers.hash)).limit(limit)).map((row) => {
|
|
10728
|
+
const receiverIfMakerIsSeller = (row.receiverIfMakerIsSeller ?? row.maker).toLowerCase();
|
|
10231
10729
|
return {
|
|
10232
10730
|
hash: row.hash,
|
|
10233
10731
|
maker: row.maker,
|
|
10234
10732
|
assets: BigInt(row.assets),
|
|
10235
10733
|
obligationUnits: BigInt(row.obligationUnits),
|
|
10236
10734
|
obligationShares: BigInt(row.obligationShares),
|
|
10237
|
-
|
|
10735
|
+
tick: row.tick,
|
|
10238
10736
|
maturity: from$16(row.maturity),
|
|
10239
10737
|
expiry: row.expiry,
|
|
10240
10738
|
start: row.start,
|
|
@@ -10252,6 +10750,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10252
10750
|
address: row.callbackAddress,
|
|
10253
10751
|
data: row.callbackData
|
|
10254
10752
|
},
|
|
10753
|
+
receiverIfMakerIsSeller,
|
|
10255
10754
|
consumed: BigInt(row.consumed),
|
|
10256
10755
|
available: BigInt(String(row.available ?? "0").split(".")[0] ?? "0"),
|
|
10257
10756
|
takeable: BigInt(String(row.takeable ?? "0").split(".")[0] ?? "0"),
|
|
@@ -10565,7 +11064,7 @@ startCmd.description("Start Router services.").addOption(new Option("--mock <n>"
|
|
|
10565
11064
|
let gatekeeperUrl = gatekeeperConfig?.url;
|
|
10566
11065
|
let gatekeeperHandle = null;
|
|
10567
11066
|
if (!gatekeeperUrl) {
|
|
10568
|
-
const gatekeeperCore = create$
|
|
11067
|
+
const gatekeeperCore = create$21({ rules: morphoRules(chainRegistry.list()) });
|
|
10569
11068
|
gatekeeperHandle = await start$1({
|
|
10570
11069
|
gatekeeper: gatekeeperCore,
|
|
10571
11070
|
chainRegistry,
|
|
@@ -10891,6 +11390,7 @@ function buildOfferAssociationsFromOffers(parameters) {
|
|
|
10891
11390
|
positionContract: loanToken,
|
|
10892
11391
|
positionUser: offer.maker,
|
|
10893
11392
|
group: offer.group,
|
|
11393
|
+
obligationId: obligationId(offer),
|
|
10894
11394
|
size: offer.assets
|
|
10895
11395
|
});
|
|
10896
11396
|
}
|