diff --git a/.env.sample b/.env.sample index 332ee08..5b08c77 100644 --- a/.env.sample +++ b/.env.sample @@ -3,4 +3,5 @@ PROVIDER_URL= PORT=3000 PRIORITY_POOLS_ORDER_186=18,22,1 PRIORITY_POOLS_ORDER_195=1,23,22,2,5 -PRIORITY_POOLS_ORDER_196=1,23,22,2,5 \ No newline at end of file +PRIORITY_POOLS_ORDER_196=1,23,22,2,5 +LOG_LEVEL=INFO diff --git a/package-lock.json b/package-lock.json index 96f3dd8..cf8fdbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@nexusmutual/deployments": "^2.10.0", + "@nexusmutual/utils": "^0.0.1", "convict": "^6.2.4", "dotenv": "^16.0.3", "ethers": "^5.7.2", @@ -1968,6 +1969,15 @@ "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-2.10.0.tgz", "integrity": "sha512-sI61ZRLhC4i7Ibt0DX9WNIpgtt+bhPAaHuMGCGdtw7uK58SCUlFqMCSfwOF/Onx28RmEPJry0Cf9KQCKOLuhXA==" }, + "node_modules/@nexusmutual/utils": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@nexusmutual/utils/-/utils-0.0.1.tgz", + "integrity": "sha512-qTzc2fev+D6wm8LWclyFUOFjh+NiK6iLvcyMnoHxzNPPLpbgd0mAWFuBPlcGCFlemdu9liSCxpCt+aWV6KKqrQ==", + "license": "ISC", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -9408,6 +9418,11 @@ "resolved": "https://registry.npmjs.org/@nexusmutual/deployments/-/deployments-2.10.0.tgz", "integrity": "sha512-sI61ZRLhC4i7Ibt0DX9WNIpgtt+bhPAaHuMGCGdtw7uK58SCUlFqMCSfwOF/Onx28RmEPJry0Cf9KQCKOLuhXA==" }, + "@nexusmutual/utils": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@nexusmutual/utils/-/utils-0.0.1.tgz", + "integrity": "sha512-qTzc2fev+D6wm8LWclyFUOFjh+NiK6iLvcyMnoHxzNPPLpbgd0mAWFuBPlcGCFlemdu9liSCxpCt+aWV6KKqrQ==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index d1fa242..8fd76f5 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "homepage": "https://github.com/NexusMutual/cover-router#readme", "dependencies": { "@nexusmutual/deployments": "^2.10.0", + "@nexusmutual/utils": "^0.0.1", "convict": "^6.2.4", "dotenv": "^16.0.3", "ethers": "^5.7.2", diff --git a/src/config.js b/src/config.js index 4a78118..dffa072 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,11 @@ const convict = require('convict'); const config = convict({ + logLevel: { + doc: 'The logging level to set', + default: 'WARN', + env: 'LOG_LEVEL', + }, port: { doc: 'The port to bind.', format: 'port', diff --git a/src/index.js b/src/index.js index 97191c2..defa44f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ require('dotenv').config(); const { addresses } = require('@nexusmutual/deployments'); +const { setLogLevel } = require('@nexusmutual/utils'); const { ethers } = require('ethers'); const express = require('express'); const swaggerUI = require('swagger-ui-express'); @@ -15,6 +16,8 @@ const { capacityRouter, pricingRouter, quoteRouter, reindexRouter } = require('. const { createStore, initialState, load, save } = require('./store'); const main = async () => { + setLogLevel(config.get('logLevel')); + // provider const providerURL = config.get('provider'); const provider = new ethers.providers.JsonRpcProvider(providerURL); @@ -61,7 +64,7 @@ const main = async () => { app.set('synchronizer', synchronizer); if (!isFromCache) { - console.log('Missing initial state, delaying startup until the state is fully loaded'); + console.warn('Missing initial state, delaying startup until the state is fully loaded'); await synchronizer.updateAssetRates(); await synchronizer.updateAll(); } diff --git a/src/lib/chainApi.js b/src/lib/chainApi.js index dc559ac..b1ce0a2 100644 --- a/src/lib/chainApi.js +++ b/src/lib/chainApi.js @@ -51,7 +51,7 @@ const createChainApi = async contracts => { const fetchPoolProduct = async (productId, poolId, globalCapacityRatio, capacityReductionRatio) => { const stakingPool = contracts('StakingPool', poolId); - console.log('Fetching allocations for product', productId, 'in pool', poolId, 'at address', stakingPool.address); + console.info(`Fetching allocations for product ${productId} in pool ${poolId} at address ${stakingPool.address}`); // pool allocations and capacities const allocations = await stakingPool.getActiveAllocations(productId); diff --git a/src/lib/eventsApi.js b/src/lib/eventsApi.js index 259cd03..df56f91 100644 --- a/src/lib/eventsApi.js +++ b/src/lib/eventsApi.js @@ -32,7 +32,7 @@ module.exports = async (provider, contracts) => { const blockBucketId = calculateBucketId(blockTimestamp); if (blockBucketId === activeBucketId) { - console.log(`Event: Bucket ${currentBucketId} expired`); + console.info(`Event: Bucket ${currentBucketId} expired`); currentBucketId = activeBucketId; emitter.emit('bucket:change'); @@ -44,7 +44,7 @@ module.exports = async (provider, contracts) => { const blockTrancheId = calculateTrancheId(blockTimestamp); if (blockTrancheId === activeTrancheId) { - console.log(`Event: Tranche ${currentTrancheId} expired`); + console.info(`Event: Tranche ${currentTrancheId} expired`); currentTrancheId = activeTrancheId; emitter.emit('tranche:change'); @@ -62,7 +62,7 @@ module.exports = async (provider, contracts) => { const stakingPool = contracts('StakingPool', poolId); for (const eventName of events) { stakingPool.on(eventName, () => { - console.log(`Event: ${eventName} triggered for Pool ${poolId}`); + console.info(`Event: ${eventName} triggered for Pool ${poolId}`); emitter.emit('pool:change', poolId); }); } @@ -71,27 +71,27 @@ module.exports = async (provider, contracts) => { // subscribe to events on new staking pool stakingPoolFactory.on('StakingPoolCreated', async poolId => { const poolIdParsed = BigNumber.isBigNumber(poolId) ? poolId.toNumber() : poolId; - console.log(`Event: Pool ${poolIdParsed} created`); + console.info(`Event: Pool ${poolIdParsed} created`); emitter.emit('pool:change', poolIdParsed); const stakingPool = contracts('StakingPool', poolIdParsed); for (const eventName of events) { stakingPool.on(eventName, () => { - console.log(`Event: ${eventName} triggered for Pool ${poolIdParsed}`); + console.info(`Event: ${eventName} triggered for Pool ${poolIdParsed}`); emitter.emit('pool:change', poolIdParsed); }); } }); stakingProducts.on('ProductUpdated', productId => { - console.log(`Event: Product ${productId} update`); + console.info(`Event: Product ${productId} update`); emitter.emit('product:change', productId); }); coverProducts.on('ProductSet', productId => { - console.log(`Event: Product ${productId} set`); + console.info(`Event: Product ${productId} set`); emitter.emit('product:change', productId); }); cover.on('CoverEdited', (coverId, productId) => { - console.log(`Event: Cover ${coverId} for product ${productId} edited`); + console.info(`Event: Cover ${coverId} for product ${productId} edited`); emitter.emit('product:change', productId); }); diff --git a/src/lib/quoteEngine.js b/src/lib/quoteEngine.js index 3fc535b..0fd73c1 100644 --- a/src/lib/quoteEngine.js +++ b/src/lib/quoteEngine.js @@ -185,7 +185,9 @@ const customAllocationPriorityFixedPrice = (amountToAllocate, poolsData, customP const poolId = customPoolIdPriorityCopy.shift(); const pool = poolsData.find(poolData => poolData.poolId === poolId); if (!pool) { - console.info(`Unable to find pool ${poolId} in poolsData array. Skipping\n`, inspect(poolsData, { depth: null })); + console.warn(`Unable to find pool ${poolId} in poolsData array. Skipping\n`); + console.warn(`Available poolIds in poolsData: ${poolsData.map(p => p.poolId).join(', ')}`); + console.debug('poolsData: ', inspect(poolsData, { depth: null })); continue; } @@ -239,7 +241,7 @@ const quoteEngine = (store, productId, amount, period, coverAsset) => { // rounding up to nearest allocation unit const amountToAllocate = divCeil(coverAmountInNxm, NXM_PER_ALLOCATION_UNIT).mul(NXM_PER_ALLOCATION_UNIT); - console.log('Amount to allocate:', formatEther(amountToAllocate), 'nxm'); + console.info(`Amount to allocate: ${formatEther(amountToAllocate)} nxm`); const poolsData = productPools.map(pool => { const { poolId, targetPrice, bumpedPrice, bumpedPriceUpdateTime, allocations, trancheCapacities } = pool; @@ -293,7 +295,8 @@ const quoteEngine = (store, productId, amount, period, coverAsset) => { const pool = poolsData.find(data => poolId.toString() === data.poolId.toString()); if (!pool) { - console.error('poolsData: ', inspect(poolsData, { depth: null })); + console.info(`Available poolIds in poolsData: ${poolsData.map(p => p.poolId).join(', ')}`); + console.debug('poolsData: ', inspect(poolsData, { depth: null })); throw new Error(`Unable to find pool ${poolId} in poolsData`); } @@ -311,10 +314,10 @@ const quoteEngine = (store, productId, amount, period, coverAsset) => { asset: selectAsset(store, assetId), })); - console.log('Pool:', poolId); - console.log('Initially used capacity:', formatEther(pool.initialCapacityUsed), 'nxm'); - console.log('Total pool capacity :', formatEther(pool.totalCapacity), 'nxm'); - console.log('Pool capacity :', formatEther(capacityInNxm), 'nxm'); + console.info('Pool:', poolId); + console.info('Initially used capacity:', formatEther(pool.initialCapacityUsed), 'nxm'); + console.info('Total pool capacity :', formatEther(pool.totalCapacity), 'nxm'); + console.info('Pool capacity :', formatEther(capacityInNxm), 'nxm'); const coverAmountInAsset = amountToAllocate.mul(assetRate).div(WeiPerEther); diff --git a/src/lib/synchronizer.js b/src/lib/synchronizer.js index ff69a3a..f3a78c9 100644 --- a/src/lib/synchronizer.js +++ b/src/lib/synchronizer.js @@ -36,7 +36,7 @@ module.exports = async (store, chainApi, eventsApi) => { payload: { productId, poolId, poolProduct }, }); } - console.log(`Update: product data for product with id ${productId}`); + console.info(`Update: product data for product with id ${productId}`); }; async function updatePool(poolId) { @@ -54,7 +54,7 @@ module.exports = async (store, chainApi, eventsApi) => { payload: { productId, poolId, poolProduct }, }); } - console.log(`Update: Pool data for pool with id ${poolId}`); + console.info(`Update: Pool data for pool with id ${poolId}`); } const updateAll = async () => { @@ -79,7 +79,7 @@ module.exports = async (store, chainApi, eventsApi) => { const rate = await chainApi.fetchTokenPriceInAsset(assetId); store.dispatch({ type: SET_ASSET_RATE, payload: { assetId, rate } }); } - console.log('Update: Asset rates'); + console.info('Update: Asset rates'); }; eventsApi.on('pool:change', updatePool); diff --git a/src/routes/capacity.js b/src/routes/capacity.js index b237054..543ca76 100644 --- a/src/routes/capacity.js +++ b/src/routes/capacity.js @@ -1,3 +1,5 @@ +const { inspect } = require('node:util'); + const { ethers, BigNumber } = require('ethers'); const express = require('express'); @@ -71,6 +73,8 @@ router.get( asyncRoute(async (req, res) => { const periodQuery = Number(req.query.period) || 30; + console.info(`Request: periodQuery=${periodQuery}`); + if (!Number.isInteger(periodQuery) || periodQuery < 28 || periodQuery > 365) { return res.status(400).send({ error: 'Invalid period: must be an integer between 28 and 365', response: null }); } @@ -81,7 +85,7 @@ router.get( const capacities = getAllProductCapacities(store, period); const response = capacities.map(capacity => formatCapacityResult(capacity)); - console.log(JSON.stringify(capacities, null, 2)); + console.debug('Response: ', inspect(response, { depth: null })); res.json(response); } catch (error) { @@ -167,6 +171,8 @@ router.get( const productId = Number(req.params.productId); const periodQuery = Number(req.query.period) || 30; + console.info(`Request: productId=${productId}, periodQuery=${periodQuery}`); + if (!Number.isInteger(periodQuery) || periodQuery < 28 || periodQuery > 365) { return res.status(400).send({ error: 'Invalid period: must be an integer between 28 and 365', response: null }); } @@ -184,7 +190,7 @@ router.get( } const response = formatCapacityResult(capacity); - console.log(JSON.stringify(response, null, 2)); + console.info('Response: ', inspect(response, { depth: null })); res.json(response); } catch (error) { @@ -267,6 +273,8 @@ router.get( const poolId = Number(req.params.poolId); const periodQuery = Number(req.query.period) || 30; + console.info(`Request: poolId=${poolId}, periodQuery=${periodQuery}`); + if (!Number.isInteger(periodQuery) || periodQuery < 28 || periodQuery > 365) { return res.status(400).send({ error: 'Invalid period: must be an integer between 28 and 365', response: null }); } @@ -284,7 +292,7 @@ router.get( utilizationRate: poolCapacity.utilizationRate.toNumber(), productsCapacity: poolCapacity.productsCapacity.map(productCapacity => formatCapacityResult(productCapacity)), }; - console.log(JSON.stringify(response, null, 2)); + console.info('Response: ', inspect(response, { depth: null })); res.json(response); } catch (error) { @@ -343,6 +351,8 @@ router.get( const productId = Number(req.params.productId); const periodQuery = Number(req.query.period) || 30; + console.info(`Request: poolId=${poolId}, productId=${productId}, periodQuery=${periodQuery}`); + if (!Number.isInteger(periodQuery) || periodQuery < 28 || periodQuery > 365) { return res.status(400).send({ error: 'Invalid period: must be an integer between 28 and 365', response: null }); } @@ -362,7 +372,7 @@ router.get( } const response = formatCapacityResult(capacity); - console.log(JSON.stringify(response, null, 2)); + console.info('Response: ', inspect(response, { depth: null })); res.json(response); } catch (error) { diff --git a/src/routes/pricing.js b/src/routes/pricing.js index 36d0219..db6d2f2 100644 --- a/src/routes/pricing.js +++ b/src/routes/pricing.js @@ -1,3 +1,5 @@ +const { inspect } = require('node:util'); + const express = require('express'); const { asyncRoute } = require('../lib/helpers'); @@ -60,6 +62,8 @@ router.get( asyncRoute(async (req, res) => { const productId = Number(req.params.productId); + console.info(`Request: productId=${productId}`); + if (!Number.isInteger(productId) || productId < 0) { return res.status(400).send({ error: 'Invalid productId: must be an integer', response: null }); } @@ -73,7 +77,7 @@ router.get( } const response = formatPricingResult(pricingResult); - console.log(JSON.stringify(response, null, 2)); + console.info('Response: ', inspect(response, { depth: null })); res.json(response); } catch (error) { diff --git a/src/routes/quote.js b/src/routes/quote.js index 26bb88d..663849a 100644 --- a/src/routes/quote.js +++ b/src/routes/quote.js @@ -1,3 +1,5 @@ +const { inspect } = require('node:util'); + const { BigNumber, ethers } = require('ethers'); const express = require('express'); @@ -148,6 +150,9 @@ router.get( const period = BigNumber.from(req.query.period).mul(24 * 3600); const coverAsset = Number(req.query.coverAsset); + const normalizedRequestQuery = { ...req.query, amount: BigNumber.from(req.query.amount).toString() }; + console.info('Request: ', inspect(normalizedRequestQuery, { depth: null })); + const store = req.app.get('store'); const route = await quoteEngine(store, productId, amount, period, coverAsset); @@ -208,7 +213,7 @@ router.get( })), }; - console.log(JSON.stringify(response, null, 2)); + console.info('Response: ', inspect(response, { depth: null })); res.json(response); }), diff --git a/test/unit/eventApi.js b/test/unit/eventApi.js index 0170035..f6f53b2 100644 --- a/test/unit/eventApi.js +++ b/test/unit/eventApi.js @@ -16,7 +16,7 @@ function contractFactoryMock(addresses, provider) { const mockedFactory = (name, id = 0, forceNew = false) => { if (name !== 'StakingPoolFactory') { - console.log(name, id, forceNew); + console.log({ name, id, forceNew }); return factory(name, id, forceNew); } diff --git a/test/unit/quoteEngine.js b/test/unit/quoteEngine.js index f8a57a6..82f5e2d 100644 --- a/test/unit/quoteEngine.js +++ b/test/unit/quoteEngine.js @@ -69,8 +69,6 @@ describe('Quote Engine tests', () => { { const quote = quotes[1]; - console.log(quote.premiumInNxm.toString()); - expect(quote.poolId).to.be.equal(2); expect(quote.premiumInNxm.toString()).to.be.equal('468378082191780821'); expect(quote.premiumInAsset.toString()).to.be.equal('13453897657327743831');