@morpho-dev/router 0.7.2 → 0.8.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 +292 -122
- 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/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/_journal.json +14 -0
- package/dist/index.browser.d.mts +103 -33
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +103 -33
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +298 -146
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +293 -147
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +182 -63
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +182 -63
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +342 -127
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +337 -128
- package/dist/index.node.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -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.8.0";
|
|
156
156
|
var description = "Router package for Morpho protocol";
|
|
157
157
|
|
|
158
158
|
//#endregion
|
|
@@ -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",
|
|
@@ -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),
|
|
@@ -2198,7 +2225,7 @@ const serialize = (offer) => ({
|
|
|
2198
2225
|
assets: offer.assets.toString(),
|
|
2199
2226
|
obligationUnits: offer.obligationUnits.toString(),
|
|
2200
2227
|
obligationShares: offer.obligationShares.toString(),
|
|
2201
|
-
|
|
2228
|
+
tick: offer.tick,
|
|
2202
2229
|
maturity: Number(offer.maturity),
|
|
2203
2230
|
expiry: Number(offer.expiry),
|
|
2204
2231
|
start: Number(offer.start),
|
|
@@ -2243,14 +2270,13 @@ function random(config) {
|
|
|
2243
2270
|
[.98, 2]
|
|
2244
2271
|
]));
|
|
2245
2272
|
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)];
|
|
2273
|
+
const tickMin = buy ? 0 : 495;
|
|
2274
|
+
const len = (buy ? 495 : 990) - tickMin + 1;
|
|
2275
|
+
const tickPairs = Array.from({ length: len }, (_, idx) => {
|
|
2276
|
+
const weight = buy ? 1 + idx : 1 + (len - 1 - idx);
|
|
2277
|
+
return [tickMin + idx, weight];
|
|
2252
2278
|
});
|
|
2253
|
-
const
|
|
2279
|
+
const tick = config?.tick ?? weightedChoice(tickPairs);
|
|
2254
2280
|
const loanTokenDecimals = config?.assetsDecimals?.[loanToken] ?? 18;
|
|
2255
2281
|
const unit = BigInt(10) ** BigInt(loanTokenDecimals);
|
|
2256
2282
|
const amountBase = BigInt(100 + int(999901));
|
|
@@ -2264,7 +2290,7 @@ function random(config) {
|
|
|
2264
2290
|
assets: assetsScaled,
|
|
2265
2291
|
obligationUnits: config?.obligationUnits ?? 0n,
|
|
2266
2292
|
obligationShares: config?.obligationShares ?? 0n,
|
|
2267
|
-
|
|
2293
|
+
tick,
|
|
2268
2294
|
maturity,
|
|
2269
2295
|
expiry: config?.expiry ?? maturity - 1,
|
|
2270
2296
|
start: config?.start ?? maturity - 10,
|
|
@@ -2329,7 +2355,7 @@ const types = {
|
|
|
2329
2355
|
type: "uint256"
|
|
2330
2356
|
},
|
|
2331
2357
|
{
|
|
2332
|
-
name: "
|
|
2358
|
+
name: "tick",
|
|
2333
2359
|
type: "uint256"
|
|
2334
2360
|
},
|
|
2335
2361
|
{
|
|
@@ -2397,7 +2423,7 @@ function hash(offer) {
|
|
|
2397
2423
|
assets: offer.assets,
|
|
2398
2424
|
obligationUnits: offer.obligationUnits,
|
|
2399
2425
|
obligationShares: offer.obligationShares,
|
|
2400
|
-
|
|
2426
|
+
tick: BigInt(offer.tick),
|
|
2401
2427
|
maturity: BigInt(offer.maturity),
|
|
2402
2428
|
expiry: BigInt(offer.expiry),
|
|
2403
2429
|
group: offer.group,
|
|
@@ -2662,6 +2688,49 @@ var InvalidQuoteError = class extends BaseError {
|
|
|
2662
2688
|
}
|
|
2663
2689
|
};
|
|
2664
2690
|
|
|
2691
|
+
//#endregion
|
|
2692
|
+
//#region src/core/Tick.ts
|
|
2693
|
+
/** ln(1 + 0.025), scaled by 1e18. Matches TickLib onchain constant. */
|
|
2694
|
+
const LN_ONE_PLUS_DELTA = 24692612590371501n;
|
|
2695
|
+
/** ln(2), scaled by 1e18. Matches TickLib onchain constant. */
|
|
2696
|
+
const LN2 = 693147180559945309n;
|
|
2697
|
+
const WAD$1 = 10n ** 18n;
|
|
2698
|
+
const WAD_SQUARED = 10n ** 36n;
|
|
2699
|
+
const PRICE_STEP = 10n ** 13n;
|
|
2700
|
+
const HALF_TICK_RANGE = 495n;
|
|
2701
|
+
/** Tick domain supported by Morpho V2. */
|
|
2702
|
+
const TICK_RANGE = 990;
|
|
2703
|
+
/**
|
|
2704
|
+
* Converts a tick to a wad price using the same approximation and rounding as TickLib.
|
|
2705
|
+
* @param tick - Tick value in the inclusive range [0, 990].
|
|
2706
|
+
* @returns The price in wad units.
|
|
2707
|
+
* @throws {@link InvalidTickError} If tick is not an integer in range [0, 990].
|
|
2708
|
+
*/
|
|
2709
|
+
function tickToPrice(tick) {
|
|
2710
|
+
assertTick(tick);
|
|
2711
|
+
return divHalfDownUnchecked(divHalfDownUnchecked(WAD_SQUARED, WAD$1 + wExp(LN_ONE_PLUS_DELTA * (HALF_TICK_RANGE - BigInt(tick)))), PRICE_STEP) * PRICE_STEP;
|
|
2712
|
+
}
|
|
2713
|
+
function divHalfDownUnchecked(x, d) {
|
|
2714
|
+
return (x + (d - 1n) / 2n) / d;
|
|
2715
|
+
}
|
|
2716
|
+
function wExp(x) {
|
|
2717
|
+
if (x < 0n) return WAD_SQUARED / wExp(-x);
|
|
2718
|
+
const q = (x + LN2 / 2n) / LN2;
|
|
2719
|
+
const r = x - q * LN2;
|
|
2720
|
+
const secondTerm = r * r / (2n * WAD$1);
|
|
2721
|
+
const thirdTerm = secondTerm * r / (3n * WAD$1);
|
|
2722
|
+
return WAD$1 + r + secondTerm + thirdTerm << q;
|
|
2723
|
+
}
|
|
2724
|
+
function assertTick(tick) {
|
|
2725
|
+
if (!Number.isInteger(tick) || tick < 0 || tick > TICK_RANGE) throw new InvalidTickError(tick);
|
|
2726
|
+
}
|
|
2727
|
+
var InvalidTickError = class extends BaseError {
|
|
2728
|
+
name = "Tick.InvalidTickError";
|
|
2729
|
+
constructor(tick) {
|
|
2730
|
+
super(`Invalid tick: ${tick}. Tick must be an integer between 0 and ${TICK_RANGE}.`);
|
|
2731
|
+
}
|
|
2732
|
+
};
|
|
2733
|
+
|
|
2665
2734
|
//#endregion
|
|
2666
2735
|
//#region src/core/TradingFee.ts
|
|
2667
2736
|
/** WAD constant (1e18) for fee scaling. */
|
|
@@ -2950,15 +3019,25 @@ const callback = ({ callbacks }) => single("callback", `Validates callbacks: buy
|
|
|
2950
3019
|
if (isEmptyCallback(offer) && !offer.buy && !callbacks.includes(Type$1.SellWithEmptyCallback)) return { message: "Sell offers with empty callback not allowed." };
|
|
2951
3020
|
});
|
|
2952
3021
|
/**
|
|
2953
|
-
* A validation rule that checks if the offer's
|
|
2954
|
-
* @param assetsByChainId - Allowed
|
|
3022
|
+
* A validation rule that checks if the offer's loan token is allowed for its chain.
|
|
3023
|
+
* @param assetsByChainId - Allowed loan tokens indexed by chain id.
|
|
3024
|
+
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
3025
|
+
*/
|
|
3026
|
+
const loanToken = ({ assetsByChainId }) => single("loan_token", "Validates that offer loan token is in the allowed token list for the offer chain", (offer) => {
|
|
3027
|
+
const allowedLoanTokens = assetsByChainId[offer.chainId]?.map((asset) => asset.toLowerCase());
|
|
3028
|
+
if (!allowedLoanTokens || allowedLoanTokens.length === 0) return { message: `No allowed loan tokens for chain ${offer.chainId}` };
|
|
3029
|
+
if (!allowedLoanTokens.includes(offer.loanToken.toLowerCase())) return { message: "Loan token is not allowed" };
|
|
3030
|
+
});
|
|
3031
|
+
/**
|
|
3032
|
+
* A validation rule that checks if the offer's collateral tokens are allowed for its chain.
|
|
3033
|
+
* @param collateralAssetsByChainId - Allowed collateral tokens indexed by chain id.
|
|
2955
3034
|
* @returns The issue that was found. If the offer is valid, this will be undefined.
|
|
2956
3035
|
*/
|
|
2957
|
-
const
|
|
2958
|
-
const
|
|
2959
|
-
if (
|
|
2960
|
-
if (
|
|
2961
|
-
if (offer.collaterals.some((collateral) => !
|
|
3036
|
+
const collateralToken = ({ collateralAssetsByChainId }) => single("collateral_token", "Validates that offer collateral tokens are in the allowed token list for the offer chain", (offer) => {
|
|
3037
|
+
const allowedCollateralTokens = collateralAssetsByChainId[offer.chainId]?.map((asset) => asset.toLowerCase()) ?? [];
|
|
3038
|
+
if (allowedCollateralTokens.length === 0) return { message: `No allowed collateral tokens for chain ${offer.chainId}` };
|
|
3039
|
+
if (offer.collaterals.length === 0) return { message: "At least one collateral token is required" };
|
|
3040
|
+
if (offer.collaterals.some((collateral) => !allowedCollateralTokens.includes(collateral.asset.toLowerCase()))) return { message: "Collateral token is not allowed" };
|
|
2962
3041
|
});
|
|
2963
3042
|
/**
|
|
2964
3043
|
* A validation rule that checks if the offer's oracle addresses are allowed for its chain.
|
|
@@ -3001,9 +3080,11 @@ const amountMutualExclusivity = () => single("amount_mutual_exclusivity", "Valid
|
|
|
3001
3080
|
//#region src/gatekeeper/morphoRules.ts
|
|
3002
3081
|
const morphoRules = (chains) => {
|
|
3003
3082
|
const assetsByChainId = {};
|
|
3083
|
+
const collateralAssetsByChainId = {};
|
|
3004
3084
|
const oraclesByChainId = {};
|
|
3005
3085
|
for (const chain of chains) {
|
|
3006
3086
|
assetsByChainId[chain.id] = assets[chain.id.toString()] ?? [];
|
|
3087
|
+
collateralAssetsByChainId[chain.id] = collateralAssets[chain.id.toString()] ?? [];
|
|
3007
3088
|
oraclesByChainId[chain.id] = oracles$1[chain.id.toString()] ?? [];
|
|
3008
3089
|
}
|
|
3009
3090
|
return [
|
|
@@ -3015,7 +3096,8 @@ const morphoRules = (chains) => {
|
|
|
3015
3096
|
callbacks: [Type$1.BuyWithEmptyCallback, Type$1.SellWithEmptyCallback],
|
|
3016
3097
|
allowedAddresses: []
|
|
3017
3098
|
}),
|
|
3018
|
-
|
|
3099
|
+
loanToken({ assetsByChainId }),
|
|
3100
|
+
collateralToken({ collateralAssetsByChainId }),
|
|
3019
3101
|
oracle({ oraclesByChainId })
|
|
3020
3102
|
];
|
|
3021
3103
|
};
|
|
@@ -3023,7 +3105,7 @@ const morphoRules = (chains) => {
|
|
|
3023
3105
|
//#endregion
|
|
3024
3106
|
//#region src/gatekeeper/ConfigRules.ts
|
|
3025
3107
|
/**
|
|
3026
|
-
* Build the configured rules (maturities + callback addresses + loan tokens + oracles) for the provided chains.
|
|
3108
|
+
* Build the configured rules (maturities + callback addresses + loan tokens + collateral tokens + oracles) for the provided chains.
|
|
3027
3109
|
* @param chains - Chains to include in the configured rules.
|
|
3028
3110
|
* @returns Sorted list of config rules.
|
|
3029
3111
|
*/
|
|
@@ -3043,6 +3125,12 @@ function buildConfigRules(chains) {
|
|
|
3043
3125
|
chain_id: chain.id,
|
|
3044
3126
|
address: normalizeAddress(address)
|
|
3045
3127
|
});
|
|
3128
|
+
const collateralTokens = collateralAssets[chain.id.toString()] ?? [];
|
|
3129
|
+
for (const address of collateralTokens) rules.push({
|
|
3130
|
+
type: "collateral_token",
|
|
3131
|
+
chain_id: chain.id,
|
|
3132
|
+
address: normalizeAddress(address)
|
|
3133
|
+
});
|
|
3046
3134
|
const oracles = oracles$1[chain.id.toString()] ?? [];
|
|
3047
3135
|
for (const address of oracles) rules.push({
|
|
3048
3136
|
type: "oracle",
|
|
@@ -3070,6 +3158,10 @@ function buildConfigRulesChecksum(rules) {
|
|
|
3070
3158
|
hash.update(`callback:${rule.chain_id}:${rule.callback_type}:${rule.address}\n`);
|
|
3071
3159
|
continue;
|
|
3072
3160
|
}
|
|
3161
|
+
if (rule.type === "collateral_token") {
|
|
3162
|
+
hash.update(`collateral_token:${rule.chain_id}:${rule.address}\n`);
|
|
3163
|
+
continue;
|
|
3164
|
+
}
|
|
3073
3165
|
if (rule.type === "oracle") {
|
|
3074
3166
|
hash.update(`oracle:${rule.chain_id}:${rule.address}\n`);
|
|
3075
3167
|
continue;
|
|
@@ -3090,6 +3182,7 @@ function compareConfigRules(left, right) {
|
|
|
3090
3182
|
return left.address.localeCompare(right.address);
|
|
3091
3183
|
}
|
|
3092
3184
|
if (left.type === "loan_token" && right.type === "loan_token") return left.address.localeCompare(right.address);
|
|
3185
|
+
if (left.type === "collateral_token" && right.type === "collateral_token") return left.address.localeCompare(right.address);
|
|
3093
3186
|
if (left.type === "oracle" && right.type === "oracle") return left.address.localeCompare(right.address);
|
|
3094
3187
|
return 0;
|
|
3095
3188
|
}
|
|
@@ -3097,8 +3190,10 @@ function compareConfigRules(left, right) {
|
|
|
3097
3190
|
//#endregion
|
|
3098
3191
|
//#region src/api/Schema/BookResponse.ts
|
|
3099
3192
|
function from$6(level) {
|
|
3193
|
+
const price = tickToPrice(level.tick);
|
|
3100
3194
|
return {
|
|
3101
|
-
|
|
3195
|
+
tick: level.tick,
|
|
3196
|
+
price: price.toString(),
|
|
3102
3197
|
assets: level.assets.toString(),
|
|
3103
3198
|
count: level.count
|
|
3104
3199
|
};
|
|
@@ -3201,7 +3296,7 @@ function from$4(input) {
|
|
|
3201
3296
|
obligation_shares: input.obligationShares.toString(),
|
|
3202
3297
|
start: input.start,
|
|
3203
3298
|
expiry: input.expiry,
|
|
3204
|
-
|
|
3299
|
+
tick: input.tick,
|
|
3205
3300
|
group: input.group,
|
|
3206
3301
|
session: input.session,
|
|
3207
3302
|
callback: input.callback.address,
|
|
@@ -3363,7 +3458,7 @@ const offerExample = {
|
|
|
3363
3458
|
obligation_shares: "0",
|
|
3364
3459
|
start: 1761922790,
|
|
3365
3460
|
expiry: 1761922799,
|
|
3366
|
-
|
|
3461
|
+
tick: 495,
|
|
3367
3462
|
group: "0x000000000000000000000000000000000000000000000000000000000008b8f4",
|
|
3368
3463
|
session: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3369
3464
|
callback: "0x0000000000000000000000000000000000000000",
|
|
@@ -3404,7 +3499,7 @@ const validateOfferExample = {
|
|
|
3404
3499
|
assets: "369216000000000000000000",
|
|
3405
3500
|
obligation_units: "0",
|
|
3406
3501
|
obligation_shares: "0",
|
|
3407
|
-
|
|
3502
|
+
tick: 495,
|
|
3408
3503
|
maturity: 1761922799,
|
|
3409
3504
|
expiry: 1761922799,
|
|
3410
3505
|
start: 1761922790,
|
|
@@ -3548,9 +3643,11 @@ __decorate([ApiProperty({
|
|
|
3548
3643
|
example: offerExample.offer.expiry
|
|
3549
3644
|
})], OfferDataResponse.prototype, "expiry", void 0);
|
|
3550
3645
|
__decorate([ApiProperty({
|
|
3551
|
-
type: "
|
|
3552
|
-
example: offerExample.offer.
|
|
3553
|
-
|
|
3646
|
+
type: "number",
|
|
3647
|
+
example: offerExample.offer.tick,
|
|
3648
|
+
minimum: 0,
|
|
3649
|
+
maximum: 990
|
|
3650
|
+
})], OfferDataResponse.prototype, "tick", void 0);
|
|
3554
3651
|
__decorate([ApiProperty({
|
|
3555
3652
|
type: "string",
|
|
3556
3653
|
example: offerExample.offer.group
|
|
@@ -3791,9 +3888,11 @@ __decorate([ApiProperty({
|
|
|
3791
3888
|
required: false
|
|
3792
3889
|
})], ValidateOfferRequest.prototype, "obligation_shares", void 0);
|
|
3793
3890
|
__decorate([ApiProperty({
|
|
3794
|
-
type: "
|
|
3795
|
-
example: validateOfferExample.
|
|
3796
|
-
|
|
3891
|
+
type: "number",
|
|
3892
|
+
example: validateOfferExample.tick,
|
|
3893
|
+
minimum: 0,
|
|
3894
|
+
maximum: 990
|
|
3895
|
+
})], ValidateOfferRequest.prototype, "tick", void 0);
|
|
3797
3896
|
__decorate([ApiProperty({
|
|
3798
3897
|
type: "number",
|
|
3799
3898
|
example: validateOfferExample.maturity
|
|
@@ -3893,9 +3992,16 @@ __decorate([ApiProperty({
|
|
|
3893
3992
|
description: "List of validation issues. Returned when any offer fails validation."
|
|
3894
3993
|
})], ValidationFailureResponse.prototype, "data", void 0);
|
|
3895
3994
|
var BookLevelResponse = class {};
|
|
3995
|
+
__decorate([ApiProperty({
|
|
3996
|
+
type: "number",
|
|
3997
|
+
example: 495,
|
|
3998
|
+
minimum: 0,
|
|
3999
|
+
maximum: 990
|
|
4000
|
+
})], BookLevelResponse.prototype, "tick", void 0);
|
|
3896
4001
|
__decorate([ApiProperty({
|
|
3897
4002
|
type: "string",
|
|
3898
|
-
example: "
|
|
4003
|
+
example: "500000000000000000",
|
|
4004
|
+
description: "Price derived from tick, scaled by 1e18."
|
|
3899
4005
|
})], BookLevelResponse.prototype, "price", void 0);
|
|
3900
4006
|
__decorate([ApiProperty({
|
|
3901
4007
|
type: "string",
|
|
@@ -3909,6 +4015,7 @@ const positionExample = {
|
|
|
3909
4015
|
chain_id: 1,
|
|
3910
4016
|
contract: "0xC9A9C45C0eB717f8b5F193Af6bAa05A1c0Ac5078",
|
|
3911
4017
|
user: "0x7b093658BE7f90B63D7c359e8f408e503c2D9401",
|
|
4018
|
+
obligation_id: "0x12590ae1aee324a005be565f3bcdd16dbf8daf7969b26c181c8b8f467dad9f67",
|
|
3912
4019
|
reserved: "200000000000000000000",
|
|
3913
4020
|
block_number: 21345678
|
|
3914
4021
|
};
|
|
@@ -3925,6 +4032,12 @@ __decorate([ApiProperty({
|
|
|
3925
4032
|
type: "string",
|
|
3926
4033
|
example: positionExample.user
|
|
3927
4034
|
})], PositionListItemResponse.prototype, "user", void 0);
|
|
4035
|
+
__decorate([ApiProperty({
|
|
4036
|
+
type: "string",
|
|
4037
|
+
nullable: true,
|
|
4038
|
+
example: positionExample.obligation_id,
|
|
4039
|
+
description: "Obligation id this reserved amount belongs to, or null if no lots exist."
|
|
4040
|
+
})], PositionListItemResponse.prototype, "obligation_id", void 0);
|
|
3928
4041
|
__decorate([ApiProperty({
|
|
3929
4042
|
type: "string",
|
|
3930
4043
|
example: positionExample.reserved
|
|
@@ -3952,7 +4065,7 @@ __decorate([ApiProperty({
|
|
|
3952
4065
|
})], BookListResponse.prototype, "cursor", void 0);
|
|
3953
4066
|
__decorate([ApiProperty({
|
|
3954
4067
|
type: () => [BookLevelResponse],
|
|
3955
|
-
description: "Aggregated book levels grouped by
|
|
4068
|
+
description: "Aggregated book levels grouped by offer tick."
|
|
3956
4069
|
})], BookListResponse.prototype, "data", void 0);
|
|
3957
4070
|
let BooksController = class BooksController {
|
|
3958
4071
|
async getBook() {}
|
|
@@ -3962,7 +4075,7 @@ __decorate([
|
|
|
3962
4075
|
methods: ["get"],
|
|
3963
4076
|
path: "/v1/books/{obligationId}/{side}",
|
|
3964
4077
|
summary: "Get aggregated book",
|
|
3965
|
-
description: "Returns aggregated book data for a given obligation and side. Offers are grouped by
|
|
4078
|
+
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
4079
|
}),
|
|
3967
4080
|
ApiParam({
|
|
3968
4081
|
name: "obligationId",
|
|
@@ -3987,7 +4100,7 @@ __decorate([
|
|
|
3987
4100
|
name: "limit",
|
|
3988
4101
|
type: "number",
|
|
3989
4102
|
example: 10,
|
|
3990
|
-
description: "Maximum number of
|
|
4103
|
+
description: "Maximum number of tick levels to return."
|
|
3991
4104
|
}),
|
|
3992
4105
|
ApiResponse({
|
|
3993
4106
|
status: 200,
|
|
@@ -4181,6 +4294,11 @@ const configRulesLoanTokenExample = {
|
|
|
4181
4294
|
chain_id: 1,
|
|
4182
4295
|
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
|
4183
4296
|
};
|
|
4297
|
+
const configRulesCollateralTokenExample = {
|
|
4298
|
+
type: "collateral_token",
|
|
4299
|
+
chain_id: 1,
|
|
4300
|
+
address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0"
|
|
4301
|
+
};
|
|
4184
4302
|
const configRulesOracleExample = {
|
|
4185
4303
|
type: "oracle",
|
|
4186
4304
|
chain_id: 1,
|
|
@@ -4190,6 +4308,7 @@ const configRulesChecksumExample = "f1d2d2f924e986ac86fdf7b36c94bcdf";
|
|
|
4190
4308
|
const configRulesPayloadExample = [
|
|
4191
4309
|
configRulesMaturityExample,
|
|
4192
4310
|
configRulesLoanTokenExample,
|
|
4311
|
+
configRulesCollateralTokenExample,
|
|
4193
4312
|
configRulesOracleExample
|
|
4194
4313
|
];
|
|
4195
4314
|
const configContractNames = [
|
|
@@ -4316,7 +4435,7 @@ __decorate([
|
|
|
4316
4435
|
methods: ["get"],
|
|
4317
4436
|
path: "/v1/config/rules",
|
|
4318
4437
|
summary: "Get config rules",
|
|
4319
|
-
description: "Returns configured rules (maturities, loan tokens, oracles) for supported chains."
|
|
4438
|
+
description: "Returns configured rules (maturities, loan tokens, collateral tokens, oracles) for supported chains."
|
|
4320
4439
|
}),
|
|
4321
4440
|
ApiQuery({
|
|
4322
4441
|
name: "cursor",
|
|
@@ -4336,7 +4455,7 @@ __decorate([
|
|
|
4336
4455
|
name: "types",
|
|
4337
4456
|
type: ["string"],
|
|
4338
4457
|
required: false,
|
|
4339
|
-
example: "maturity,loan_token,oracle",
|
|
4458
|
+
example: "maturity,loan_token,collateral_token,oracle",
|
|
4340
4459
|
description: "Filter by rule types (comma-separated).",
|
|
4341
4460
|
style: "form",
|
|
4342
4461
|
explode: false
|
|
@@ -4454,7 +4573,7 @@ __decorate([
|
|
|
4454
4573
|
methods: ["get"],
|
|
4455
4574
|
path: "/v1/users/{userAddress}/positions",
|
|
4456
4575
|
summary: "Get user positions",
|
|
4457
|
-
description: "Returns positions for a user with reserved balance.
|
|
4576
|
+
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
4577
|
}),
|
|
4459
4578
|
ApiParam({
|
|
4460
4579
|
name: "userAddress",
|
|
@@ -4541,6 +4660,7 @@ function from$3(position) {
|
|
|
4541
4660
|
chain_id: position.chainId,
|
|
4542
4661
|
contract: position.contract,
|
|
4543
4662
|
user: position.user,
|
|
4663
|
+
obligation_id: position.obligationId,
|
|
4544
4664
|
reserved: position.reserved.toString(),
|
|
4545
4665
|
block_number: position.blockNumber
|
|
4546
4666
|
};
|
|
@@ -4594,10 +4714,11 @@ const ConfigRuleTypes = z$2.enum([
|
|
|
4594
4714
|
"maturity",
|
|
4595
4715
|
"callback",
|
|
4596
4716
|
"loan_token",
|
|
4717
|
+
"collateral_token",
|
|
4597
4718
|
"oracle"
|
|
4598
4719
|
]);
|
|
4599
4720
|
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({
|
|
4721
|
+
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
4722
|
description: "Pagination cursor in type:chain_id:<value> format",
|
|
4602
4723
|
example: "maturity:1:1730415600:end_of_next_month"
|
|
4603
4724
|
}),
|
|
@@ -4607,7 +4728,7 @@ const GetConfigRulesQueryParams = z$2.object({
|
|
|
4607
4728
|
}),
|
|
4608
4729
|
types: csvArray(ConfigRuleTypes).meta({
|
|
4609
4730
|
description: "Filter by rule types (comma-separated).",
|
|
4610
|
-
example: "maturity,loan_token,oracle"
|
|
4731
|
+
example: "maturity,loan_token,collateral_token,oracle"
|
|
4611
4732
|
}),
|
|
4612
4733
|
chains: csvArray(z$2.string().regex(/^[1-9]\d*$/, { message: "Chain must be a positive integer" }).transform((val) => Number.parseInt(val, 10))).meta({
|
|
4613
4734
|
description: "Filter by chain IDs (comma-separated).",
|
|
@@ -4707,12 +4828,11 @@ const GetObligationParams = z$2.object({ obligation_id: z$2.string({ error: "Obl
|
|
|
4707
4828
|
description: "Obligation id",
|
|
4708
4829
|
example: "0x1234567890123456789012345678901234567890123456789012345678901234"
|
|
4709
4830
|
}) });
|
|
4710
|
-
/** Validate a book cursor format: {side,
|
|
4831
|
+
/** Validate a book cursor format: {side, lastTick, offersCursor} */
|
|
4711
4832
|
function isValidBookCursor(cursorString) {
|
|
4712
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
4713
4833
|
try {
|
|
4714
4834
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
4715
|
-
return (v?.side === "buy" || v?.side === "sell") &&
|
|
4835
|
+
return (v?.side === "buy" || v?.side === "sell") && typeof v?.lastTick === "number" && Number.isInteger(v.lastTick) && (v?.offersCursor === null || typeof v?.offersCursor === "string");
|
|
4716
4836
|
} catch {
|
|
4717
4837
|
return false;
|
|
4718
4838
|
}
|
|
@@ -4817,6 +4937,7 @@ function formatCursor$1(rule) {
|
|
|
4817
4937
|
if (rule.type === "maturity") return `maturity:${rule.chain_id}:${rule.timestamp}:${rule.name}`;
|
|
4818
4938
|
if (rule.type === "callback") return `callback:${rule.chain_id}:${rule.callback_type}:${rule.address.toLowerCase()}`;
|
|
4819
4939
|
if (rule.type === "oracle") return `oracle:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
4940
|
+
if (rule.type === "collateral_token") return `collateral_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
4820
4941
|
return `loan_token:${rule.chain_id}:${rule.address.toLowerCase()}`;
|
|
4821
4942
|
}
|
|
4822
4943
|
function parseCursor$1(cursor) {
|
|
@@ -4849,7 +4970,7 @@ function parseCursor$1(cursor) {
|
|
|
4849
4970
|
address: parseAddress(addressValue, "Cursor address")
|
|
4850
4971
|
};
|
|
4851
4972
|
}
|
|
4852
|
-
if (type === "loan_token" || type === "oracle") {
|
|
4973
|
+
if (type === "loan_token" || type === "collateral_token" || type === "oracle") {
|
|
4853
4974
|
const addressValue = rest.join(":");
|
|
4854
4975
|
if (!addressValue) throw new BadRequestError(`Cursor must be in the format ${type}:chain_id:address`);
|
|
4855
4976
|
return {
|
|
@@ -4876,7 +4997,7 @@ function parseAddress(address, label) {
|
|
|
4876
4997
|
return address.toLowerCase();
|
|
4877
4998
|
}
|
|
4878
4999
|
function isConfigRuleType(value) {
|
|
4879
|
-
return value === "maturity" || value === "callback" || value === "loan_token" || value === "oracle";
|
|
5000
|
+
return value === "maturity" || value === "callback" || value === "loan_token" || value === "collateral_token" || value === "oracle";
|
|
4880
5001
|
}
|
|
4881
5002
|
function isMaturityType(value) {
|
|
4882
5003
|
return Object.values(MaturityType).includes(value);
|
|
@@ -5149,7 +5270,7 @@ function now() {
|
|
|
5149
5270
|
|
|
5150
5271
|
//#endregion
|
|
5151
5272
|
//#region src/database/drizzle/VERSION.ts
|
|
5152
|
-
const VERSION = "router_v1.
|
|
5273
|
+
const VERSION = "router_v1.8";
|
|
5153
5274
|
|
|
5154
5275
|
//#endregion
|
|
5155
5276
|
//#region src/database/drizzle/schema.ts
|
|
@@ -5301,10 +5422,7 @@ const offers = s.table(EnumTableName.OFFERS, {
|
|
|
5301
5422
|
precision: 78,
|
|
5302
5423
|
scale: 0
|
|
5303
5424
|
}).notNull().default("0"),
|
|
5304
|
-
|
|
5305
|
-
precision: 78,
|
|
5306
|
-
scale: 0
|
|
5307
|
-
}).notNull(),
|
|
5425
|
+
tick: integer("tick").notNull(),
|
|
5308
5426
|
maturity: integer("maturity").notNull(),
|
|
5309
5427
|
expiry: integer("expiry").notNull(),
|
|
5310
5428
|
start: integer("start").notNull(),
|
|
@@ -5369,6 +5487,7 @@ const lots = s.table(EnumTableName.LOTS, {
|
|
|
5369
5487
|
user: varchar("user", { length: 42 }).notNull(),
|
|
5370
5488
|
contract: varchar("contract", { length: 42 }).notNull(),
|
|
5371
5489
|
group: varchar("group", { length: 66 }).notNull(),
|
|
5490
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5372
5491
|
lower: numeric("lower", {
|
|
5373
5492
|
precision: 78,
|
|
5374
5493
|
scale: 0
|
|
@@ -5383,7 +5502,8 @@ const lots = s.table(EnumTableName.LOTS, {
|
|
|
5383
5502
|
table.chainId,
|
|
5384
5503
|
table.user,
|
|
5385
5504
|
table.contract,
|
|
5386
|
-
table.group
|
|
5505
|
+
table.group,
|
|
5506
|
+
table.obligationId
|
|
5387
5507
|
],
|
|
5388
5508
|
name: "lots_pk"
|
|
5389
5509
|
}),
|
|
@@ -5419,6 +5539,7 @@ const offsets = s.table(EnumTableName.OFFSETS, {
|
|
|
5419
5539
|
user: varchar("user", { length: 42 }).notNull(),
|
|
5420
5540
|
contract: varchar("contract", { length: 42 }).notNull(),
|
|
5421
5541
|
group: varchar("group", { length: 66 }).notNull(),
|
|
5542
|
+
obligationId: varchar("obligation_id", { length: 66 }).notNull(),
|
|
5422
5543
|
value: numeric("value", {
|
|
5423
5544
|
precision: 78,
|
|
5424
5545
|
scale: 0
|
|
@@ -5428,7 +5549,8 @@ const offsets = s.table(EnumTableName.OFFSETS, {
|
|
|
5428
5549
|
table.chainId,
|
|
5429
5550
|
table.user,
|
|
5430
5551
|
table.contract,
|
|
5431
|
-
table.group
|
|
5552
|
+
table.group,
|
|
5553
|
+
table.obligationId
|
|
5432
5554
|
],
|
|
5433
5555
|
name: "offsets_pk"
|
|
5434
5556
|
}), foreignKey({
|
|
@@ -6019,6 +6141,7 @@ function decodeCallbacks(parameters) {
|
|
|
6019
6141
|
positionContract: loanToken,
|
|
6020
6142
|
positionUser: offer.maker,
|
|
6021
6143
|
group: offer.group,
|
|
6144
|
+
obligationId: obligationId(offer),
|
|
6022
6145
|
size: offer.assets
|
|
6023
6146
|
});
|
|
6024
6147
|
callbacks.push({
|
|
@@ -7421,6 +7544,7 @@ function create$14(config) {
|
|
|
7421
7544
|
return {
|
|
7422
7545
|
get: async (parameters) => {
|
|
7423
7546
|
const { side, obligationId, cursor: cursorString, limit = DEFAULT_LIMIT$3 } = parameters;
|
|
7547
|
+
const tickSortDirection = side === "sell" ? "asc" : "desc";
|
|
7424
7548
|
const inputCursor = LevelCursor.decode(cursorString, logger);
|
|
7425
7549
|
if (cursorString != null && inputCursor === null) return {
|
|
7426
7550
|
levels: [],
|
|
@@ -7435,23 +7559,23 @@ function create$14(config) {
|
|
|
7435
7559
|
cursor: inputCursor?.offersCursor ?? void 0,
|
|
7436
7560
|
limit: fetchLimit
|
|
7437
7561
|
});
|
|
7438
|
-
const
|
|
7562
|
+
const tickMap = /* @__PURE__ */ new Map();
|
|
7439
7563
|
for (const row of rows) {
|
|
7440
|
-
const
|
|
7441
|
-
const existing = priceMap.get(priceKey);
|
|
7564
|
+
const existing = tickMap.get(row.tick);
|
|
7442
7565
|
if (existing) {
|
|
7443
7566
|
existing.assets += row.takeable;
|
|
7444
7567
|
existing.count += 1;
|
|
7445
|
-
} else
|
|
7568
|
+
} else tickMap.set(row.tick, {
|
|
7446
7569
|
assets: row.takeable,
|
|
7447
7570
|
count: 1
|
|
7448
7571
|
});
|
|
7449
7572
|
}
|
|
7450
|
-
const levels = Array.from(
|
|
7451
|
-
|
|
7452
|
-
assets:
|
|
7453
|
-
count:
|
|
7573
|
+
const levels = Array.from(tickMap.entries()).map(([tick, level]) => ({
|
|
7574
|
+
tick,
|
|
7575
|
+
assets: level.assets,
|
|
7576
|
+
count: level.count
|
|
7454
7577
|
}));
|
|
7578
|
+
levels.sort((a, b) => tickSortDirection === "asc" ? a.tick - b.tick : b.tick - a.tick);
|
|
7455
7579
|
const paginatedLevels = levels.slice(0, limit);
|
|
7456
7580
|
const hasMore = levels.length > limit || offersNextCursor !== null;
|
|
7457
7581
|
const lastLevel = paginatedLevels[paginatedLevels.length - 1];
|
|
@@ -7497,14 +7621,14 @@ async function _getOffers(db, params) {
|
|
|
7497
7621
|
AND (s.code IS NULL OR s.code = ${Status.VALID})
|
|
7498
7622
|
ORDER BY
|
|
7499
7623
|
o.group_chain_id, o.group_maker, o."group_group",
|
|
7500
|
-
o.
|
|
7624
|
+
o.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, o.block_number ASC, o.assets DESC, o.hash ASC
|
|
7501
7625
|
),
|
|
7502
7626
|
enriched AS (
|
|
7503
7627
|
SELECT
|
|
7504
7628
|
w.*,
|
|
7505
7629
|
g.consumed, g.chain_id, obl.loan_token,
|
|
7506
7630
|
CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
|
|
7507
|
-
THEN w.
|
|
7631
|
+
THEN w.tick ELSE -w.tick END AS tick_norm,
|
|
7508
7632
|
w.block_number AS block_norm,
|
|
7509
7633
|
-w.assets AS assets_norm,
|
|
7510
7634
|
w.hash AS hash_norm
|
|
@@ -7521,33 +7645,35 @@ async function _getOffers(db, params) {
|
|
|
7521
7645
|
FROM enriched e
|
|
7522
7646
|
${cursor != null ? sql`
|
|
7523
7647
|
WHERE
|
|
7524
|
-
(e.
|
|
7648
|
+
(e.tick_norm, e.block_norm, e.assets_norm, e.hash_norm)
|
|
7525
7649
|
> (
|
|
7526
7650
|
CASE WHEN ${priceSortDirection === "asc" ? sql`TRUE` : sql`FALSE`}
|
|
7527
|
-
THEN ${cursor.
|
|
7651
|
+
THEN ${cursor.tick}::integer ELSE -${cursor.tick}::integer END,
|
|
7528
7652
|
${cursor.blockNumber},
|
|
7529
7653
|
-${cursor.assets}::numeric,
|
|
7530
7654
|
${cursor.hash}
|
|
7531
7655
|
)` : sql``}
|
|
7532
|
-
ORDER BY e.
|
|
7656
|
+
ORDER BY e.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`}, e.block_number ASC, e.assets DESC, e.hash ASC
|
|
7533
7657
|
LIMIT ${limit}
|
|
7534
7658
|
),
|
|
7535
|
-
-- Compute sum of offsets per position
|
|
7659
|
+
-- Compute sum of offsets per position and obligation
|
|
7536
7660
|
position_offsets AS (
|
|
7537
7661
|
SELECT
|
|
7538
7662
|
chain_id,
|
|
7539
7663
|
"user",
|
|
7540
7664
|
contract,
|
|
7665
|
+
obligation_id,
|
|
7541
7666
|
SUM(value::numeric) AS total_offset
|
|
7542
7667
|
FROM ${offsets}
|
|
7543
|
-
GROUP BY chain_id, "user", contract
|
|
7668
|
+
GROUP BY chain_id, "user", contract, obligation_id
|
|
7544
7669
|
),
|
|
7545
|
-
-- Compute position_consumed: sum of consumed from all groups with lots on each position (converted to lot terms)
|
|
7670
|
+
-- Compute position_consumed: sum of consumed from all groups with lots on each position+obligation (converted to lot terms)
|
|
7546
7671
|
position_consumed AS (
|
|
7547
7672
|
SELECT
|
|
7548
7673
|
l.chain_id,
|
|
7549
7674
|
l.contract,
|
|
7550
7675
|
l."user",
|
|
7676
|
+
l.obligation_id,
|
|
7551
7677
|
SUM(
|
|
7552
7678
|
CASE
|
|
7553
7679
|
WHEN wo.assets::numeric > 0
|
|
@@ -7564,7 +7690,7 @@ async function _getOffers(db, params) {
|
|
|
7564
7690
|
ON wo.group_chain_id = g.chain_id
|
|
7565
7691
|
AND LOWER(wo.group_maker) = LOWER(g.maker)
|
|
7566
7692
|
AND wo.group_group = g."group"
|
|
7567
|
-
GROUP BY l.chain_id, l.contract, l."user"
|
|
7693
|
+
GROUP BY l.chain_id, l.contract, l."user", l.obligation_id
|
|
7568
7694
|
),
|
|
7569
7695
|
-- Compute callback contributions with lot balance
|
|
7570
7696
|
callback_contributions AS (
|
|
@@ -7572,7 +7698,7 @@ async function _getOffers(db, params) {
|
|
|
7572
7698
|
p.hash,
|
|
7573
7699
|
p.obligation_id,
|
|
7574
7700
|
p.assets,
|
|
7575
|
-
p.
|
|
7701
|
+
p.tick,
|
|
7576
7702
|
p.obligation_units,
|
|
7577
7703
|
p.obligation_shares,
|
|
7578
7704
|
p.maturity,
|
|
@@ -7617,6 +7743,7 @@ async function _getOffers(db, params) {
|
|
|
7617
7743
|
AND LOWER(l.contract) = LOWER(c.position_contract)
|
|
7618
7744
|
AND LOWER(l."user") = LOWER(c.position_user)
|
|
7619
7745
|
AND l."group" = p.group_group
|
|
7746
|
+
AND l.obligation_id = p.obligation_id
|
|
7620
7747
|
LEFT JOIN ${positions} pos
|
|
7621
7748
|
ON pos.chain_id = c.position_chain_id
|
|
7622
7749
|
AND LOWER(pos.contract) = LOWER(c.position_contract)
|
|
@@ -7625,10 +7752,12 @@ async function _getOffers(db, params) {
|
|
|
7625
7752
|
ON pos_offsets.chain_id = c.position_chain_id
|
|
7626
7753
|
AND LOWER(pos_offsets.contract) = LOWER(c.position_contract)
|
|
7627
7754
|
AND LOWER(pos_offsets."user") = LOWER(c.position_user)
|
|
7755
|
+
AND pos_offsets.obligation_id = p.obligation_id
|
|
7628
7756
|
LEFT JOIN position_consumed pc
|
|
7629
7757
|
ON pc.chain_id = c.position_chain_id
|
|
7630
7758
|
AND LOWER(pc.contract) = LOWER(c.position_contract)
|
|
7631
7759
|
AND LOWER(pc."user") = LOWER(c.position_user)
|
|
7760
|
+
AND pc.obligation_id = p.obligation_id
|
|
7632
7761
|
),
|
|
7633
7762
|
-- Compute contribution per callback in loan terms (loan token only — collateral positions are not indexed)
|
|
7634
7763
|
callback_loan_contribution AS (
|
|
@@ -7646,7 +7775,7 @@ async function _getOffers(db, params) {
|
|
|
7646
7775
|
hash,
|
|
7647
7776
|
obligation_id,
|
|
7648
7777
|
assets,
|
|
7649
|
-
|
|
7778
|
+
tick,
|
|
7650
7779
|
obligation_units,
|
|
7651
7780
|
obligation_shares,
|
|
7652
7781
|
maturity,
|
|
@@ -7672,13 +7801,13 @@ async function _getOffers(db, params) {
|
|
|
7672
7801
|
WHERE clc.callback_id IS NOT NULL
|
|
7673
7802
|
ORDER BY clc.hash, clc.position_chain_id, clc.position_contract, clc.position_user, clc.contribution_in_loan DESC
|
|
7674
7803
|
) deduped
|
|
7675
|
-
GROUP BY hash, obligation_id, assets,
|
|
7804
|
+
GROUP BY hash, obligation_id, assets, tick, obligation_units, obligation_shares, maturity, expiry, start, group_group, buy,
|
|
7676
7805
|
callback_address, callback_data, block_number, group_chain_id, group_maker,
|
|
7677
7806
|
consumed, chain_id, loan_token, session
|
|
7678
7807
|
UNION ALL
|
|
7679
7808
|
-- Sell offers without callbacks: collateral positions not indexed, takeable = assets - consumed
|
|
7680
7809
|
SELECT
|
|
7681
|
-
p.hash, p.obligation_id, p.assets, p.
|
|
7810
|
+
p.hash, p.obligation_id, p.assets, p.tick,
|
|
7682
7811
|
p.obligation_units, p.obligation_shares,
|
|
7683
7812
|
p.maturity, p.expiry, p.start, p.group_group,
|
|
7684
7813
|
p.buy, p.callback_address, p.callback_data,
|
|
@@ -7700,7 +7829,7 @@ async function _getOffers(db, params) {
|
|
|
7700
7829
|
oc.obligation_units,
|
|
7701
7830
|
oc.obligation_shares,
|
|
7702
7831
|
oc.consumed,
|
|
7703
|
-
oc.
|
|
7832
|
+
oc.tick,
|
|
7704
7833
|
oc.maturity,
|
|
7705
7834
|
oc.expiry,
|
|
7706
7835
|
oc.start,
|
|
@@ -7732,7 +7861,7 @@ async function _getOffers(db, params) {
|
|
|
7732
7861
|
))
|
|
7733
7862
|
END > 0
|
|
7734
7863
|
ORDER BY
|
|
7735
|
-
oc.
|
|
7864
|
+
oc.tick ${priceSortDirection === "asc" ? sql`ASC` : sql`DESC`},
|
|
7736
7865
|
oc.block_number ASC,
|
|
7737
7866
|
oc.assets DESC,
|
|
7738
7867
|
oc.hash ASC;
|
|
@@ -7745,7 +7874,7 @@ async function _getOffers(db, params) {
|
|
|
7745
7874
|
assets: BigInt(row.assets),
|
|
7746
7875
|
obligationUnits: BigInt(row.obligation_units ?? 0),
|
|
7747
7876
|
obligationShares: BigInt(row.obligation_shares ?? 0),
|
|
7748
|
-
|
|
7877
|
+
tick: row.tick,
|
|
7749
7878
|
maturity: row.maturity,
|
|
7750
7879
|
expiry: row.expiry,
|
|
7751
7880
|
start: row.start,
|
|
@@ -7777,7 +7906,7 @@ let Cursor;
|
|
|
7777
7906
|
function encode(row, totalReturned, now, side) {
|
|
7778
7907
|
return Buffer.from(JSON.stringify({
|
|
7779
7908
|
side,
|
|
7780
|
-
|
|
7909
|
+
tick: row.tick,
|
|
7781
7910
|
blockNumber: row.blockNumber,
|
|
7782
7911
|
assets: row.assets.toString(),
|
|
7783
7912
|
hash: row.hash,
|
|
@@ -7788,10 +7917,9 @@ let Cursor;
|
|
|
7788
7917
|
_Cursor.encode = encode;
|
|
7789
7918
|
function decode(cursorString, logger) {
|
|
7790
7919
|
if (cursorString == null) return null;
|
|
7791
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
7792
7920
|
try {
|
|
7793
7921
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
7794
|
-
if ((v?.side === "buy" || v?.side === "sell") &&
|
|
7922
|
+
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
7923
|
throw new Error("Invalid cursor");
|
|
7796
7924
|
} catch {
|
|
7797
7925
|
logger.error({
|
|
@@ -7809,7 +7937,7 @@ let LevelCursor;
|
|
|
7809
7937
|
function encode(lastLevel, offersCursor, side, now) {
|
|
7810
7938
|
return Buffer.from(JSON.stringify({
|
|
7811
7939
|
side,
|
|
7812
|
-
|
|
7940
|
+
lastTick: lastLevel.tick,
|
|
7813
7941
|
now,
|
|
7814
7942
|
offersCursor
|
|
7815
7943
|
})).toString("base64url");
|
|
@@ -7817,10 +7945,9 @@ let LevelCursor;
|
|
|
7817
7945
|
_LevelCursor.encode = encode;
|
|
7818
7946
|
function decode(cursorString, logger) {
|
|
7819
7947
|
if (cursorString == null) return null;
|
|
7820
|
-
const isNumericString = (value) => typeof value === "string" && /^-?\d+$/.test(value);
|
|
7821
7948
|
try {
|
|
7822
7949
|
const v = JSON.parse(Buffer.from(cursorString, "base64url").toString("utf8"));
|
|
7823
|
-
if ((v?.side === "buy" || v?.side === "sell") &&
|
|
7950
|
+
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
7951
|
throw new Error("Invalid book cursor");
|
|
7825
7952
|
} catch {
|
|
7826
7953
|
logger.error({
|
|
@@ -7978,31 +8105,33 @@ function create$11(db) {
|
|
|
7978
8105
|
function create$10(db) {
|
|
7979
8106
|
return {
|
|
7980
8107
|
get: async (parameters) => {
|
|
7981
|
-
const { chainId, user, contract, group } = parameters ?? {};
|
|
8108
|
+
const { chainId, user, contract, group, obligationId } = parameters ?? {};
|
|
7982
8109
|
const conditions = [];
|
|
7983
8110
|
if (chainId !== void 0) conditions.push(eq(lots.chainId, chainId));
|
|
7984
8111
|
if (user !== void 0) conditions.push(eq(lots.user, user.toLowerCase()));
|
|
7985
8112
|
if (contract !== void 0) conditions.push(eq(lots.contract, contract.toLowerCase()));
|
|
7986
8113
|
if (group !== void 0) conditions.push(eq(lots.group, group));
|
|
8114
|
+
if (obligationId !== void 0) conditions.push(eq(lots.obligationId, obligationId));
|
|
7987
8115
|
return (await db.select().from(lots).where(conditions.length > 0 ? and(...conditions) : void 0)).map((row) => ({
|
|
7988
8116
|
chainId: row.chainId,
|
|
7989
8117
|
user: row.user,
|
|
7990
8118
|
contract: row.contract,
|
|
7991
8119
|
group: row.group,
|
|
8120
|
+
obligationId: row.obligationId,
|
|
7992
8121
|
lower: BigInt(row.lower),
|
|
7993
8122
|
upper: BigInt(row.upper)
|
|
7994
8123
|
}));
|
|
7995
8124
|
},
|
|
7996
8125
|
create: async (parameters) => {
|
|
7997
8126
|
if (parameters.length === 0) return;
|
|
7998
|
-
const
|
|
8127
|
+
const lotsByKey = /* @__PURE__ */ new Map();
|
|
7999
8128
|
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)
|
|
8129
|
+
const key = `${offer.positionChainId}-${offer.positionContract}-${offer.positionUser}-${offer.group}-${offer.obligationId}`.toLowerCase();
|
|
8130
|
+
const existing = lotsByKey.get(key);
|
|
8131
|
+
if (!existing || offer.size > existing.size) lotsByKey.set(key, offer);
|
|
8003
8132
|
}
|
|
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())));
|
|
8133
|
+
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) {
|
|
8134
|
+
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
8135
|
const newLower = BigInt(maxUpperResult[0]?.maxUpper ?? "0");
|
|
8007
8136
|
const newUpper = newLower + offer.size;
|
|
8008
8137
|
await db.insert(lots).values({
|
|
@@ -8010,6 +8139,7 @@ function create$10(db) {
|
|
|
8010
8139
|
user: offer.positionUser.toLowerCase(),
|
|
8011
8140
|
contract: offer.positionContract.toLowerCase(),
|
|
8012
8141
|
group: offer.group.toLowerCase(),
|
|
8142
|
+
obligationId: offer.obligationId.toLowerCase(),
|
|
8013
8143
|
lower: newLower.toString(),
|
|
8014
8144
|
upper: newUpper.toString()
|
|
8015
8145
|
});
|
|
@@ -8126,7 +8256,7 @@ function create$8(config) {
|
|
|
8126
8256
|
assets: offers.assets,
|
|
8127
8257
|
obligationUnits: offers.obligationUnits,
|
|
8128
8258
|
obligationShares: offers.obligationShares,
|
|
8129
|
-
|
|
8259
|
+
tick: offers.tick,
|
|
8130
8260
|
maturity: offers.maturity,
|
|
8131
8261
|
expiry: offers.expiry,
|
|
8132
8262
|
start: offers.start,
|
|
@@ -8146,7 +8276,7 @@ function create$8(config) {
|
|
|
8146
8276
|
assets: BigInt(row.assets),
|
|
8147
8277
|
obligationUnits: BigInt(row.obligationUnits),
|
|
8148
8278
|
obligationShares: BigInt(row.obligationShares),
|
|
8149
|
-
|
|
8279
|
+
tick: row.tick,
|
|
8150
8280
|
maturity: from$16(row.maturity),
|
|
8151
8281
|
expiry: row.expiry,
|
|
8152
8282
|
start: row.start,
|
|
@@ -8228,8 +8358,8 @@ function create$8(config) {
|
|
|
8228
8358
|
const now$2 = now();
|
|
8229
8359
|
const query = ({ side }) => db.selectDistinctOn([offers.obligationId], {
|
|
8230
8360
|
obligationId: offers.obligationId,
|
|
8231
|
-
price: offers.
|
|
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.
|
|
8361
|
+
price: offers.tick
|
|
8362
|
+
}).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
8363
|
const [bestBuys, bestSells] = await Promise.all([query({ side: "buy" }), query({ side: "sell" })]);
|
|
8234
8364
|
const quotes = /* @__PURE__ */ new Map();
|
|
8235
8365
|
for (const row of bestSells) quotes.set(row.obligationId, {
|
|
@@ -8264,17 +8394,19 @@ function create$8(config) {
|
|
|
8264
8394
|
//#region src/database/domains/Offsets.ts
|
|
8265
8395
|
function create$7(db) {
|
|
8266
8396
|
return { get: async (parameters) => {
|
|
8267
|
-
const { chainId, user, contract, group } = parameters ?? {};
|
|
8397
|
+
const { chainId, user, contract, group, obligationId } = parameters ?? {};
|
|
8268
8398
|
const conditions = [];
|
|
8269
8399
|
if (chainId !== void 0) conditions.push(eq(offsets.chainId, chainId));
|
|
8270
8400
|
if (user !== void 0) conditions.push(eq(offsets.user, user.toLowerCase()));
|
|
8271
8401
|
if (contract !== void 0) conditions.push(eq(offsets.contract, contract.toLowerCase()));
|
|
8272
8402
|
if (group !== void 0) conditions.push(eq(offsets.group, group));
|
|
8403
|
+
if (obligationId !== void 0) conditions.push(eq(offsets.obligationId, obligationId));
|
|
8273
8404
|
return (await db.select().from(offsets).where(conditions.length > 0 ? and(...conditions) : void 0)).map((row) => ({
|
|
8274
8405
|
chainId: row.chainId,
|
|
8275
8406
|
user: row.user,
|
|
8276
8407
|
contract: row.contract,
|
|
8277
8408
|
group: row.group,
|
|
8409
|
+
obligationId: row.obligationId,
|
|
8278
8410
|
value: BigInt(row.value)
|
|
8279
8411
|
}));
|
|
8280
8412
|
} };
|
|
@@ -8424,7 +8556,8 @@ const create$5 = (db) => {
|
|
|
8424
8556
|
if (!parsed.chainId || !parsed.contract) throw new Error("Invalid cursor format");
|
|
8425
8557
|
cursor = {
|
|
8426
8558
|
chainId: parsed.chainId,
|
|
8427
|
-
contract: parsed.contract
|
|
8559
|
+
contract: parsed.contract,
|
|
8560
|
+
obligationId: parsed.obligationId ?? null
|
|
8428
8561
|
};
|
|
8429
8562
|
}
|
|
8430
8563
|
const raw = await db.execute(sql`
|
|
@@ -8433,16 +8566,18 @@ const create$5 = (db) => {
|
|
|
8433
8566
|
chain_id,
|
|
8434
8567
|
"user",
|
|
8435
8568
|
contract,
|
|
8569
|
+
obligation_id,
|
|
8436
8570
|
SUM(value::numeric) AS total_offset
|
|
8437
8571
|
FROM ${offsets}
|
|
8438
8572
|
WHERE LOWER("user") = LOWER(${user})
|
|
8439
|
-
GROUP BY chain_id, "user", contract
|
|
8573
|
+
GROUP BY chain_id, "user", contract, obligation_id
|
|
8440
8574
|
),
|
|
8441
8575
|
position_consumed AS (
|
|
8442
8576
|
SELECT
|
|
8443
8577
|
l.chain_id,
|
|
8444
8578
|
l.contract,
|
|
8445
8579
|
l."user",
|
|
8580
|
+
l.obligation_id,
|
|
8446
8581
|
SUM(
|
|
8447
8582
|
CASE
|
|
8448
8583
|
WHEN offer_agg.assets > 0
|
|
@@ -8468,50 +8603,64 @@ const create$5 = (db) => {
|
|
|
8468
8603
|
AND LOWER(offer_agg.group_maker) = LOWER(g.maker)
|
|
8469
8604
|
AND offer_agg."group_group" = g."group"
|
|
8470
8605
|
WHERE LOWER(l."user") = LOWER(${user})
|
|
8471
|
-
GROUP BY l.chain_id, l.contract, l."user"
|
|
8606
|
+
GROUP BY l.chain_id, l.contract, l."user", l.obligation_id
|
|
8472
8607
|
),
|
|
8473
8608
|
position_max_lot AS (
|
|
8474
8609
|
SELECT
|
|
8475
8610
|
chain_id,
|
|
8476
8611
|
contract,
|
|
8477
8612
|
"user",
|
|
8613
|
+
obligation_id,
|
|
8478
8614
|
MAX(upper::numeric) AS max_upper
|
|
8479
8615
|
FROM ${lots}
|
|
8480
8616
|
WHERE LOWER("user") = LOWER(${user})
|
|
8481
|
-
GROUP BY chain_id, contract, "user"
|
|
8617
|
+
GROUP BY chain_id, contract, "user", obligation_id
|
|
8618
|
+
),
|
|
8619
|
+
per_obligation AS (
|
|
8620
|
+
SELECT
|
|
8621
|
+
pml.chain_id,
|
|
8622
|
+
pml.contract,
|
|
8623
|
+
pml."user",
|
|
8624
|
+
pml.obligation_id,
|
|
8625
|
+
GREATEST(0,
|
|
8626
|
+
COALESCE(pml.max_upper, 0)
|
|
8627
|
+
- COALESCE(po.total_offset, 0)
|
|
8628
|
+
- COALESCE(pc.consumed, 0)
|
|
8629
|
+
)::text AS reserved_balance
|
|
8630
|
+
FROM position_max_lot pml
|
|
8631
|
+
LEFT JOIN position_offsets po
|
|
8632
|
+
ON po.chain_id = pml.chain_id
|
|
8633
|
+
AND LOWER(po.contract) = LOWER(pml.contract)
|
|
8634
|
+
AND LOWER(po."user") = LOWER(pml."user")
|
|
8635
|
+
AND po.obligation_id = pml.obligation_id
|
|
8636
|
+
LEFT JOIN position_consumed pc
|
|
8637
|
+
ON pc.chain_id = pml.chain_id
|
|
8638
|
+
AND LOWER(pc.contract) = LOWER(pml.contract)
|
|
8639
|
+
AND LOWER(pc."user") = LOWER(pml."user")
|
|
8640
|
+
AND pc.obligation_id = pml.obligation_id
|
|
8482
8641
|
)
|
|
8483
8642
|
SELECT
|
|
8484
8643
|
p.chain_id,
|
|
8485
8644
|
p.contract,
|
|
8486
8645
|
p."user",
|
|
8487
8646
|
p.block_number,
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
- COALESCE(po.total_offset, 0)
|
|
8491
|
-
- COALESCE(pc.consumed, 0)
|
|
8492
|
-
)::text AS reserved_balance
|
|
8647
|
+
po.obligation_id,
|
|
8648
|
+
COALESCE(po.reserved_balance, '0') AS reserved_balance
|
|
8493
8649
|
FROM ${positions} p
|
|
8494
|
-
LEFT JOIN
|
|
8650
|
+
LEFT JOIN per_obligation po
|
|
8495
8651
|
ON po.chain_id = p.chain_id
|
|
8496
8652
|
AND LOWER(po.contract) = LOWER(p.contract)
|
|
8497
8653
|
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
8654
|
WHERE LOWER(p."user") = LOWER(${user})
|
|
8507
8655
|
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
|
|
8656
|
+
${cursor !== null ? sql`AND (p.chain_id, p.contract, COALESCE(po.obligation_id, '')) > (${cursor.chainId}, ${cursor.contract}, ${cursor.obligationId ?? ""})` : sql``}
|
|
8657
|
+
ORDER BY p.chain_id ASC, p.contract ASC, po.obligation_id ASC NULLS FIRST
|
|
8510
8658
|
LIMIT ${limit}
|
|
8511
8659
|
`);
|
|
8512
8660
|
const nextCursor = raw.rows.length === limit ? Buffer.from(JSON.stringify({
|
|
8513
8661
|
chainId: raw.rows[raw.rows.length - 1].chain_id.toString(),
|
|
8514
|
-
contract: raw.rows[raw.rows.length - 1].contract
|
|
8662
|
+
contract: raw.rows[raw.rows.length - 1].contract,
|
|
8663
|
+
obligationId: raw.rows[raw.rows.length - 1].obligation_id
|
|
8515
8664
|
})).toString("base64url") : null;
|
|
8516
8665
|
return {
|
|
8517
8666
|
positions: raw.rows.map((row) => ({
|
|
@@ -8519,6 +8668,7 @@ const create$5 = (db) => {
|
|
|
8519
8668
|
contract: row.contract,
|
|
8520
8669
|
user: row.user,
|
|
8521
8670
|
blockNumber: row.block_number,
|
|
8671
|
+
obligationId: row.obligation_id,
|
|
8522
8672
|
reserved: BigInt(row.reserved_balance.split(".")[0] ?? "0")
|
|
8523
8673
|
})),
|
|
8524
8674
|
nextCursor
|
|
@@ -8893,6 +9043,7 @@ function augmentWithDomains(base, chainRegistry) {
|
|
|
8893
9043
|
return wrapped;
|
|
8894
9044
|
}
|
|
8895
9045
|
const InMemoryDbMap = /* @__PURE__ */ new Map();
|
|
9046
|
+
const LEGACY_SCHEMA_START_MINOR = 7;
|
|
8896
9047
|
/**
|
|
8897
9048
|
* Connect to the database.
|
|
8898
9049
|
* @notice If no connection string is provided, an in-process PGLite database is created.
|
|
@@ -8947,9 +9098,26 @@ function applyMigrations(kind, driver) {
|
|
|
8947
9098
|
async function preMigrate(driver) {
|
|
8948
9099
|
const tracer = getTracer("db.preMigrate");
|
|
8949
9100
|
await startActiveSpan(tracer, "db.preMigrate", async () => {
|
|
8950
|
-
|
|
9101
|
+
const schemaNames = getSchemaNamesForMigration(VERSION);
|
|
9102
|
+
for (const schemaName of schemaNames) await driver.execute(`create schema if not exists "${schemaName}"`);
|
|
8951
9103
|
});
|
|
8952
9104
|
}
|
|
9105
|
+
/**
|
|
9106
|
+
* Build the list of router schemas that should exist before running migrations.
|
|
9107
|
+
* @param version - Current schema version (e.g. `router_v1.8`).
|
|
9108
|
+
* @returns Ordered schema names from `router_v1.7` to current, or just current if parsing fails.
|
|
9109
|
+
*/
|
|
9110
|
+
function getSchemaNamesForMigration(version) {
|
|
9111
|
+
const parsed = /^router_v(?<major>\d+)\.(?<minor>\d+)$/.exec(version);
|
|
9112
|
+
if (!parsed?.groups?.major || !parsed.groups.minor) return [version];
|
|
9113
|
+
const major = Number.parseInt(parsed.groups.major, 10);
|
|
9114
|
+
const currentMinor = Number.parseInt(parsed.groups.minor, 10);
|
|
9115
|
+
if (!Number.isInteger(major) || !Number.isInteger(currentMinor)) return [version];
|
|
9116
|
+
if (currentMinor < LEGACY_SCHEMA_START_MINOR) return [version];
|
|
9117
|
+
const schemaNames = [];
|
|
9118
|
+
for (let minor = LEGACY_SCHEMA_START_MINOR; minor <= currentMinor; minor += 1) schemaNames.push(`router_v${major}.${minor}`);
|
|
9119
|
+
return schemaNames;
|
|
9120
|
+
}
|
|
8953
9121
|
async function postMigrate(driver) {
|
|
8954
9122
|
const tracer = getTracer("db.postMigrate");
|
|
8955
9123
|
await startActiveSpan(tracer, "db.postMigrate", async () => {
|
|
@@ -9219,15 +9387,16 @@ async function postMigrate(driver) {
|
|
|
9219
9387
|
RETURNS trigger
|
|
9220
9388
|
LANGUAGE plpgsql AS $$
|
|
9221
9389
|
BEGIN
|
|
9222
|
-
INSERT INTO "${VERSION}"."offsets" (chain_id, "user", contract, "group", value)
|
|
9390
|
+
INSERT INTO "${VERSION}"."offsets" (chain_id, "user", contract, "group", obligation_id, value)
|
|
9223
9391
|
VALUES (
|
|
9224
9392
|
OLD.chain_id,
|
|
9225
9393
|
OLD."user",
|
|
9226
9394
|
OLD.contract,
|
|
9227
9395
|
OLD."group",
|
|
9396
|
+
OLD.obligation_id,
|
|
9228
9397
|
OLD.upper::numeric - OLD.lower::numeric
|
|
9229
9398
|
)
|
|
9230
|
-
ON CONFLICT (chain_id, "user", contract, "group") DO NOTHING;
|
|
9399
|
+
ON CONFLICT (chain_id, "user", contract, "group", obligation_id) DO NOTHING;
|
|
9231
9400
|
RETURN OLD;
|
|
9232
9401
|
END;
|
|
9233
9402
|
$$;
|
|
@@ -9633,7 +9802,7 @@ async function getBook(params, db) {
|
|
|
9633
9802
|
side: query.side,
|
|
9634
9803
|
levels_count: levels.length,
|
|
9635
9804
|
has_next_cursor: nextCursor != null,
|
|
9636
|
-
|
|
9805
|
+
first_level_tick: firstLevel?.tick ?? null,
|
|
9637
9806
|
first_level_assets: firstLevel?.assets.toString() ?? null,
|
|
9638
9807
|
first_level_count: firstLevel?.count ?? null
|
|
9639
9808
|
});
|
|
@@ -10196,7 +10365,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10196
10365
|
obligationUnits: offers.obligationUnits,
|
|
10197
10366
|
obligationShares: offers.obligationShares,
|
|
10198
10367
|
consumed: groups.consumed,
|
|
10199
|
-
|
|
10368
|
+
tick: offers.tick,
|
|
10200
10369
|
maturity: offers.maturity,
|
|
10201
10370
|
expiry: offers.expiry,
|
|
10202
10371
|
start: offers.start,
|
|
@@ -10234,7 +10403,7 @@ async function getOffersQuery(db, parameters) {
|
|
|
10234
10403
|
assets: BigInt(row.assets),
|
|
10235
10404
|
obligationUnits: BigInt(row.obligationUnits),
|
|
10236
10405
|
obligationShares: BigInt(row.obligationShares),
|
|
10237
|
-
|
|
10406
|
+
tick: row.tick,
|
|
10238
10407
|
maturity: from$16(row.maturity),
|
|
10239
10408
|
expiry: row.expiry,
|
|
10240
10409
|
start: row.start,
|
|
@@ -10891,6 +11060,7 @@ function buildOfferAssociationsFromOffers(parameters) {
|
|
|
10891
11060
|
positionContract: loanToken,
|
|
10892
11061
|
positionUser: offer.maker,
|
|
10893
11062
|
group: offer.group,
|
|
11063
|
+
obligationId: obligationId(offer),
|
|
10894
11064
|
size: offer.assets
|
|
10895
11065
|
});
|
|
10896
11066
|
}
|