From 8dc95d78fd291653cbcc3e02e9cc39ca0b2ed125 Mon Sep 17 00:00:00 2001 From: Miljan Milidrag Date: Fri, 21 Jul 2023 09:19:44 +0200 Subject: [PATCH] Add usageEngine and usage routes --- src/index.js | 2 ++ src/lib/usageEngine.js | 35 +++++++++++++++++++++++++++++++++++ src/routes/usage.js | 41 +++++++++++++++++++++++++++++++++++++++++ src/store/reducer.js | 15 +++++++++++---- src/store/selectors.js | 10 ++++++++++ 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 src/lib/usageEngine.js create mode 100644 src/routes/usage.js diff --git a/src/index.js b/src/index.js index 5f7e6fb6..39f69ae0 100644 --- a/src/index.js +++ b/src/index.js @@ -13,6 +13,7 @@ const createEventsApi = require('./lib/eventsApi'); const createSynchronizer = require('./lib/synchronizer'); const capacityRouter = require('./routes/capacity'); +const usageRouter = require('./routes/usage'); const quoteRouter = require('./routes/quote'); const main = async () => { @@ -48,6 +49,7 @@ const main = async () => { // initiate routes app.use('/v2', capacityRouter); + app.use('/v2', usageRouter); app.use('/v2', quoteRouter); const port = config.get('port'); diff --git a/src/lib/usageEngine.js b/src/lib/usageEngine.js new file mode 100644 index 00000000..08646af8 --- /dev/null +++ b/src/lib/usageEngine.js @@ -0,0 +1,35 @@ +const { ethers } = require('ethers'); + +const { selectPoolProducts } = require('../store/selectors'); +const { NXM_PER_ALLOCATION_UNIT } = require('./constants'); + +const { WeiPerEther, Zero } = ethers.constants; + +function usageEngine(store, poolIds) { + const { assets, assetRates } = store.getState(); + const usage = []; + const ids = poolIds.length === 0 ? Object.keys(store.getState().poolProductIds) : [...poolIds]; + + for (const poolId of ids) { + const poolProducts = selectPoolProducts(store, poolId); + + const poolCapacities = poolProducts.map(pool => { + const { productId, allocations } = pool; + const used = allocations.reduce((total, allocation) => total.add(allocation), Zero); + const capacityUsedNXM = used.mul(NXM_PER_ALLOCATION_UNIT); + return { + productId, + capacityUsed: Object.values(assets).map(assetId => ({ + assetId, + amount: capacityUsedNXM.mul(assetRates[assetId]).div(WeiPerEther), + })), + }; + }); + + usage.push({ poolId, products: poolCapacities }); + } + + return usage; +} + +module.exports = usageEngine; diff --git a/src/routes/usage.js b/src/routes/usage.js new file mode 100644 index 00000000..3405eff9 --- /dev/null +++ b/src/routes/usage.js @@ -0,0 +1,41 @@ +const express = require('express'); +const usageEngine = require('../lib/usageEngine'); +const { asyncRoute } = require('../lib/helpers'); + +const router = express.Router(); + +const formatUsageResult = ({ poolId, products }) => ({ + poolId: poolId.toString(), + products: products.map(({ productId, capacityUsed }) => ({ + productId, + capacityUsed: capacityUsed.map(({ assetId, amount }) => ({ assetId, amount: amount.toString() })), + })), +}); + +router.get( + '/usage', + asyncRoute(async (req, res) => { + const store = req.app.get('store'); + + const response = usageEngine(store, []); + res.json(response.map(usage => formatUsageResult(usage))); + }), +); + +router.get( + '/usage/:poolId', + asyncRoute(async (req, res) => { + const poolId = Number(req.params.poolId); + const store = req.app.get('store'); + + const [usage] = usageEngine(store, [poolId]); + + if (!usage) { + return res.status(400).send({ error: 'Invalid Pool Id', response: null }); + } + + res.json(formatUsageResult(usage)); + }), +); + +module.exports = router; diff --git a/src/store/reducer.js b/src/store/reducer.js index ce6ca1da..734da1e0 100644 --- a/src/store/reducer.js +++ b/src/store/reducer.js @@ -12,6 +12,7 @@ const initialState = { globalCapacityRatio: 0, poolProducts: {}, // {productId}_{poolId} -> { allocations, trancheCapacities } productPoolIds: {}, // productId -> [ poolIds ] + poolProductIds: {}, // productId -> [ poolIds ] products: {}, // productId -> { product } trancheId: 0, }; @@ -29,10 +30,16 @@ function reducer(state = initialState, { type, payload }) { const key = `${productId}_${poolId}`; const newPoolProduct = { productId, poolId, ...poolProduct }; const poolProducts = { ...state.poolProducts, [key]: newPoolProduct }; - const previousIds = state.productPoolIds[productId] || []; - const newIds = [...new Set([...previousIds, poolId])]; - const productPoolIds = { ...state.productPoolIds, [productId]: newIds }; - return { ...state, poolProducts, productPoolIds }; + + const previousPoolIds = state.productPoolIds[productId] || []; + const newPoolIds = [...new Set([...previousPoolIds, poolId])]; + const productPoolIds = { ...state.productPoolIds, [productId]: newPoolIds }; + + const previousProductIds = state.poolProductIds[poolId] || []; + const newProductIds = [...new Set([...previousProductIds, productId])]; + const poolProductIds = { ...state.poolProductIds, [poolId]: newProductIds }; + + return { ...state, poolProducts, productPoolIds, poolProductIds }; } if (type === SET_ASSET_RATE) { diff --git a/src/store/selectors.js b/src/store/selectors.js index ecad7837..f4b0332f 100644 --- a/src/store/selectors.js +++ b/src/store/selectors.js @@ -17,6 +17,15 @@ const selectProductPools = (store, productId) => { }); }; +const selectPoolProducts = (store, poolId) => { + const { poolProducts, poolProductIds } = store.getState(); + const productIds = poolProductIds[poolId] || []; + return productIds.map(productId => { + const key = `${productId}_${poolId}`; + return poolProducts[key]; + }); +}; + const selectAssetSymbol = (store, assetId) => { const { assets } = store.getState(); return Object.keys(assets).find(key => assets[key] === assetId); @@ -27,4 +36,5 @@ module.exports = { selectAssetSymbol, selectProduct, selectProductPools, + selectPoolProducts, };